65 lines
2.6 KiB
Markdown
65 lines
2.6 KiB
Markdown
# ops/ — operator + agent query tooling
|
|
|
|
Read-only query access to Thermograph data, uniform across environments.
|
|
|
|
## `dbq.sh` — Postgres, any environment
|
|
|
|
```sh
|
|
infra/ops/dbq.sh dev "select count(*) from climate_history"
|
|
infra/ops/dbq.sh prod -tA -c "select max(date) from climate_history"
|
|
infra/ops/dbq.sh beta -c '\dt'
|
|
echo "select 1" | infra/ops/dbq.sh prod -f -
|
|
```
|
|
|
|
Environments: `dev` (LAN compose stack on the dev machine), `beta`
|
|
(75.119.132.91), `prod` (169.58.46.181). Extra args pass straight through to
|
|
`psql` (`-tA`, `-x`, `--csv`, `-f`, …); stdin is forwarded.
|
|
|
|
### Why it execs into the container instead of using a connection string
|
|
|
|
None of the databases are exposed over TCP — each listens only on its private
|
|
docker network. Prod's is a Swarm **overlay** (`10.0.2.0/24`) that the host
|
|
cannot route to, so `ssh -L` works for beta but is *impossible* for prod;
|
|
publishing 5432 would mean new ufw rules plus a Swarm endpoint change on
|
|
production. Running `psql` inside the db container works identically in all
|
|
three environments with no ports, no tunnels, and no infra changes.
|
|
|
|
The prod container name is a Swarm task name that changes on every redeploy, so
|
|
it is resolved at call time via `docker ps --filter name=…`, never hardcoded.
|
|
|
|
### Safety
|
|
|
|
Queries connect as **`thermograph_ro`** — a `NOSUPERUSER` role granted only
|
|
`pg_read_all_data`. Read-only is enforced by Postgres, not by convention:
|
|
|
|
```
|
|
$ infra/ops/dbq.sh prod -c "create table t(i int)"
|
|
ERROR: permission denied for schema public
|
|
```
|
|
|
|
The app's own `thermograph` role is a superuser and is deliberately **not** used
|
|
by this tool. If the role is ever missing (fresh database), recreate it with:
|
|
|
|
```sql
|
|
CREATE ROLE thermograph_ro LOGIN;
|
|
GRANT CONNECT ON DATABASE thermograph TO thermograph_ro;
|
|
GRANT pg_read_all_data TO thermograph_ro;
|
|
```
|
|
|
|
## Iceberg (planned)
|
|
|
|
**Implementation spec: [`ICEBERG-HANDOFF.md`](./ICEBERG-HANDOFF.md)** — the
|
|
contract, environment constraints, acceptance criteria and anti-goals for the
|
|
Iceberg worker.
|
|
|
|
Iceberg lands as a sibling in this directory following the same three
|
|
conventions, so the query surface stays uniform:
|
|
|
|
1. **Env-keyed dispatch** — `iceberg.sh <dev|beta|prod> "<sql>"`, same argument
|
|
shape as `dbq.sh`.
|
|
2. **Run the engine where the data is reachable** — the catalog/warehouse
|
|
dictates it (a Trino/DuckDB/pyiceberg invocation, containerised the same way),
|
|
rather than exposing new network endpoints.
|
|
3. **A dedicated read-only identity** — the Iceberg equivalent of
|
|
`thermograph_ro`, so the same "read-only enforced by the system, not by
|
|
convention" property holds.
|