|
All checks were successful
PR build (required check) / changes (pull_request) Successful in 7s
secrets-guard / encrypted (pull_request) Successful in 6s
PR build (required check) / build-frontend (pull_request) Has been skipped
PR build (required check) / validate-observability (pull_request) Has been skipped
PR build (required check) / build-backend (pull_request) Successful in 48s
PR build (required check) / gate (pull_request) Successful in 3s
Uniform read-only query access to the LAN dev, beta and prod databases, plus a thermograph_ro role in each to enforce it. None of the databases are exposed over TCP: each listens only on its private docker network, and prod's is a Swarm overlay the host cannot route to -- so ssh -L works for beta but is impossible for prod, and publishing 5432 would mean new ufw rules and a Swarm endpoint change on production. Running psql inside the db container works identically everywhere with no ports, tunnels or infra changes, so dbq.sh does that: local docker exec for dev, ssh for beta/prod. The prod container is a Swarm task whose name changes each redeploy, so it is resolved at call time rather than hardcoded. Queries connect as thermograph_ro (NOSUPERUSER, granted only pg_read_all_data), so a stray write fails with "permission denied" even against prod; the app's own superuser role is deliberately unused here. Extra args pass through to psql and stdin is forwarded. ops/README.md documents usage and the conventions Iceberg will follow when it lands as a sibling (env-keyed dispatch, run the engine where the data is reachable, dedicated read-only identity). |
||
|---|---|---|
| .. | ||
| dbq.sh | ||
| README.md | ||
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)
Iceberg lands as a sibling in this directory following the same three conventions, so the query surface stays uniform:
- Env-keyed dispatch —
iceberg.sh <dev|beta|prod> "<sql>", same argument shape asdbq.sh. - 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.
- 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.