From d8d3d5b1a0451c7b67cece38b6ba40984642686a Mon Sep 17 00:00:00 2001 From: tegwick Date: Sun, 9 Aug 2026 02:11:34 +0200 Subject: [PATCH] Route coulomb-social runtime env credentials via ops-warden catalog Add coulomb-social-runtime-env lane and playbook. Points operators at railiance-apps env-secret assembly; ops-warden never holds values. --- registry/routing/catalog.yaml | 38 +++++ wiki/playbooks/coulomb-social-runtime-env.md | 140 +++++++++++++++++++ 2 files changed, 178 insertions(+) create mode 100644 wiki/playbooks/coulomb-social-runtime-env.md diff --git a/registry/routing/catalog.yaml b/registry/routing/catalog.yaml index 070cd31..0c2570c 100644 --- a/registry/routing/catalog.yaml +++ b/registry/routing/catalog.yaml @@ -599,3 +599,41 @@ entries: - "bao write -f auth/approle/role/rein-openweights/secret-id -> new secret_id; deliver mode-0600 to REIN_OPENWEIGHTS_APPROLE_DIR; shred old secret_id file." - "Optionally re-mint the OpenRouter key itself if it rotated independently." - "Verify AppRole login + field presence (length only); confirm default/other policies still deny sibling paths." + + - id: coulomb-social-runtime-env + title: coulomb.social runtime env Secret (SECRET_KEY, DATABASE_URL, USER_ENGINE_PROXY_SECRET) + need_keywords: + - coulomb-social + - coulomb.social + - coulomb social + - csoc + - runtime-env + - coulomb-social-env + - django secret_key + - user-engine-proxy + - apps-pg coulomb + owner_repo: railiance-platform + subsystem: K8s Secrets + apps-pg (OpenBao path planned) + warden_executes: false + wiki_ref: wiki/playbooks/coulomb-social-runtime-env.md#worker-checklist + canon_ref: railiance-platform/docs/apps-pg.md + reviewed: "2026-08-09" + status: active + risk: standard + # K8s assembly is the live handoff today (same pattern as vergage-teilnahme-env). + # OpenBao KV platform/workloads/coulomb/coulomb-social/runtime-env is the + # future custody home — CCR not yet applied; resolvable via operator script. + auth_method: "kubectl as platform operator (or bao OIDC when OpenBao lane is provisioned)" + path_template: "k8s:coulomb-social/coulomb-social-env" + fetch_command: "cd ~/railiance-apps && make coulomb-social-env-secret-dry-run # then make coulomb-social-env-secret (values never printed)" + exec_capable: false + lane: secret + resolvable: true + rotation: + method: rotate + owner: railiance-apps + automatable: false + steps: + - "SECRET_KEY: make coulomb-social-env-secret COULOMB_SOCIAL_ENV_SECRET_ARGS='--rotate-secret-key' then rollout restart deploy/coulomb-social." + - "USER_ENGINE_PROXY_SECRET: rotate user-engine/user-engine-runtime, re-run make coulomb-social-env-secret, restart app." + - "DATABASE_URL: platform rotates apps-pg role password secret; re-run make coulomb-social-env-secret; restart app." diff --git a/wiki/playbooks/coulomb-social-runtime-env.md b/wiki/playbooks/coulomb-social-runtime-env.md new file mode 100644 index 0000000..987599b --- /dev/null +++ b/wiki/playbooks/coulomb-social-runtime-env.md @@ -0,0 +1,140 @@ +# coulomb-social runtime env credentials + +Date: 2026-08-09 +Catalog: `coulomb-social-runtime-env` +Owner: `railiance-platform` (apps-pg / OpenBao path when live) · consumer: `railiance-apps` / `coulomb-social` +ops-warden role: **route + assist** — never holds or prints values + +Runtime credentials for the coulomb.social Django deployment (`coulomb-social-env` +K8s Secret, mounted via Helm `envFrom`). + +--- + +## What this lane covers + +| Key | Source of truth (today) | Notes | +| --- | --- | --- | +| `SECRET_KEY` | Generated into K8s Secret | Django session/signing | +| `DATABASE_URL` | apps-pg role secret → URL-encoded into env Secret | Needs apps-pg + role `coulomb_social` | +| `USER_ENGINE_PROXY_SECRET` | `user-engine/user-engine-runtime` key `proxy-secret` | Trusted proxy for user-engine `/api/v1/me` | + +Non-secret OIDC settings (`OIDC_ISSUER`, `OIDC_CLIENT_ID`, redirect URI) live in +`railiance-apps/helm/coulomb-social-values.yaml` — not this lane. + +OIDC client is **public PKCE** (`coulomb-social` on KeyCape) — no client secret. + +--- + +## Owner-confirmed handoff (K8s assembly) + +| Field | Value | +| --- | --- | +| Namespace | `coulomb-social` | +| Env Secret | `coulomb-social-env` | +| App DB credentials Secret | `coulomb-social-app-credentials` (basic-auth; mirrors into consumer ns) | +| DB role / database | `coulomb_social` / `coulomb_social_db` on `apps-pg` | +| user-engine proxy | `user-engine/user-engine-runtime` | +| Assembly script | `railiance-apps/tools/create-coulomb-social-env-secret.sh` | +| Make targets | `make coulomb-social-env-secret` · `make coulomb-social-env-secret-dry-run` | +| Future OpenBao path | `platform/workloads/coulomb/coulomb-social/runtime-env` (CCR pending) | + +--- + +## Worker checklist + +### 1. Route (always first) + +```bash +warden route find "coulomb social env secret" +warden route show coulomb-social-runtime-env --json +warden access "coulomb social runtime env" --json +``` + +ops-warden **does not vend** these values. It points at the assembly script and owners. + +### 2. Establish / refresh the env Secret (no values printed) + +```bash +cd ~/railiance-apps +make coulomb-social-env-secret-dry-run # plan: key names only +make coulomb-social-env-secret # apply SECRET_KEY + USER_ENGINE_PROXY_SECRET (+ DATABASE_URL if DB secret exists) +``` + +From the app repo: + +```bash +cd ~/coulomb-social +./scripts/create-env-secret.sh --dry-run +./scripts/create-env-secret.sh +``` + +### 3. Database credential (platform) + +Until `apps-pg` is live and the role is present: + +1. Platform adds managed role + Database CR (see `railiance-platform/helm/apps-pg-*.yaml`). +2. Ensure `coulomb-social-app-credentials` exists in `coulomb-social` (and databases ns for CNPG). +3. Re-run `make coulomb-social-env-secret` to fill `DATABASE_URL`. + +### 4. Verify (capabilities / presence only) + +```bash +# Key names only — never kubectl get secret -o yaml in logs +kubectl -n coulomb-social get secret coulomb-social-env -o json \ + | python3 -c 'import sys,json; print(sorted((json.load(sys.stdin).get("data") or {}).keys()))' + +# Optional fingerprint via warden (no value): +# warden access coulomb-social-runtime-env --fingerprint # when fetch wired +``` + +### 5. Rotate + +```bash +# Django SECRET_KEY only +make coulomb-social-env-secret COULOMB_SOCIAL_ENV_SECRET_ARGS='--rotate-secret-key' +kubectl -n coulomb-social rollout restart deploy/coulomb-social + +# USER_ENGINE_PROXY_SECRET: rotate in user-engine-runtime, then re-run env-secret script +# DATABASE_URL: rotate apps-pg role password (platform), then re-run env-secret script +``` + +--- + +## Anti-patterns + +- Pasting `SECRET_KEY`, DSN passwords, or proxy secrets into chat, Git, or State Hub +- `kubectl get secret … -o yaml` in agent/CI logs +- Storing OIDC client secrets (client is public) +- Asking ops-warden to “give me the password” — use `warden access` for the **path**, then the assembly script as yourself + +--- + +## See also + +- `railiance-apps/docs/coulomb-social.md` +- `railiance-apps/tools/create-coulomb-social-env-secret.sh` +- `railiance-platform/docs/apps-pg.md` +- `coulomb-social/docs/deploy.md` + +--- + +## Established (2026-08-09) + +Operator session via `warden access coulomb-social-runtime-env` routing + assembly script: + +| Resource | Status | +| --- | --- | +| Namespace `coulomb-social` | present; labeled `railiance.io/postgres-client=apps-pg` | +| Secret `coulomb-social/coulomb-social-env` | keys: `SECRET_KEY`, `DATABASE_URL`, `USER_ENGINE_PROXY_SECRET` | +| Secret `coulomb-social/coulomb-social-app-credentials` | basic-auth username/password for role | +| Secret `databases/coulomb-social-app-credentials` | same password for future CNPG managed role | +| Catalog `coulomb-social-runtime-env` | active in ops-warden routing | +| apps-pg cluster | **not yet deployed** — DATABASE_URL is assembled; DB/role activate when apps-pg + Database CR are applied | + +Re-verify key names only: + +```bash +warden route show coulomb-social-runtime-env --json +kubectl -n coulomb-social get secret coulomb-social-env -o json \ + | python3 -c 'import sys,json; print(sorted((json.load(sys.stdin).get("data") or {}).keys()))' +```