thermograph/infra/ops
Emi Griffith bb7ce43902
All checks were successful
PR build (required check) / changes (pull_request) Successful in 16s
secrets-guard / encrypted (pull_request) Successful in 15s
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 1m2s
PR build (required check) / gate (pull_request) Successful in 3s
Add ICEBERG-HANDOFF.md: implementation spec for the Iceberg query service
Handoff for the Iceberg worker: the contract iceberg.sh must satisfy (same
interface shape as dbq.sh, read-only enforced by the system, no new network
endpoints, call-time container resolution), the environment constraints that
will otherwise bite -- prod's Swarm overlay is unroutable from its own host so
tunnels are impossible, Contabo object storage requires path-style addressing,
the backups/ prefix is live, and beta's /etc/thermograph.env is unreadable by
the deploy user -- plus the decisions to report back, acceptance criteria with
a demonstrated refused write, and anti-goals.
2026-07-23 15:00:51 -07:00
..
dbq.sh Add ops/dbq.sh: read-only Postgres queries across all environments 2026-07-23 14:54:20 -07:00
ICEBERG-HANDOFF.md Add ICEBERG-HANDOFF.md: implementation spec for the Iceberg query service 2026-07-23 15:00:51 -07:00
README.md Add ICEBERG-HANDOFF.md: implementation spec for the Iceberg query service 2026-07-23 15:00:51 -07:00

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.