// Policy for the DEV host's render identity (vps1). // // THIS FILE IS THE POINT OF THE MIGRATION. // // infra/CLAUDE.md states the rule: "vps1 must never hold prod credentials. It's the // box that runs Forgejo, its CI runner, and dev's unreviewed branch — the opposite of // an isolation boundary." // // Today that rule is enforced by a SHELL FLAG (THERMOGRAPH_SECRETS_SKIP_COMMON=1) set // by the *caller*, at three separate call sites: env-topology.sh:196 -> deploy.sh:67, // deploy-dev.sh:86, and infra-sync.yml:93-95. The renderer itself does not know that // env=dev means never-common. A fourth call site, or one by-hand // `render_thermograph_secrets /opt/thermograph-dev/infra dev`, writes both S3 // keypairs (one read-write on the bucket holding prod's database backups), the VAPID // private key that signs Web Push to real subscribers, REGISTRY_TOKEN, and the // IndexNow and metrics tokens onto the Forgejo CI-runner box — and exits 0. // // Under this policy that is no longer possible to get wrong. dev's identity cannot // read the common path. A misconfigured caller gets a 403 from OpenBao, not a silent // success with production credentials on disk. The failure class is gone, not guarded. // dev's own values. This is the ONLY secret path dev can read. path "thermograph/data/env/dev" { capabilities = ["read"] } path "thermograph/metadata/env/dev" { capabilities = ["read", "list"] } // EXPLICIT DENY on the shared production credential set. // // A deny rule is redundant with OpenBao's default-deny — anything not granted is // already refused. It is here anyway, and must stay, for two reasons: // 1. It is documentation that survives refactoring. Someone widening dev's grants // has to delete an explicit deny to break the rule, which is a much louder edit // than adding a path to an allow list. // 2. In OpenBao, a deny on a path beats any grant at that path regardless of rule // order or specificity. So if dev's identity is ever accidentally given a // broader policy alongside this one, this still wins. path "thermograph/data/common" { capabilities = ["deny"] } path "thermograph/metadata/common" { capabilities = ["deny"] } // Deny the other environments outright. Same argument: dev has no business reading // beta or prod, and being explicit means a widened grant elsewhere cannot silently // re-open it. path "thermograph/data/env/prod" { capabilities = ["deny"] } path "thermograph/data/env/beta" { capabilities = ["deny"] } path "thermograph/data/centralis/*" { capabilities = ["deny"] } // Token self-management, so the render can look up and renew its own lease without // needing a broader grant. path "auth/token/lookup-self" { capabilities = ["read"] } path "auth/token/renew-self" { capabilities = ["update"] }