thermograph/infra/ops/README.md
emi 864d38d772
All checks were successful
secrets-guard / encrypted (push) Successful in 16s
shell-lint / shellcheck (push) Successful in 17s
ops/dbq.sh: read-only Postgres queries across all environments (#18)
2026-07-24 19:28:58 +00:00

2.6 KiB

ops/ — operator + agent query tooling

Read-only query access to Thermograph data, uniform across environments.

dbq.sh — Postgres, any environment

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:

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 — 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 dispatchiceberg.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.