P7: project secrets keys and drills (forge-agent, app-skeleton) #118

Open
opened 2026-10-13 14:25:42 -04:00 by jfraeys · 1 comment
Owner

Gate: "Projects work independently; recipient isolation tested". Status 2026-10-13: convention and code merged; the keys are the remaining work and are yours to hold.

Done

  • Merged: infra #109 (ADR 0001 secrets/<name>.sops.yml convention, Secrets section in the app deployment contract, scripts/tests/test-project-key-isolation.sh, 8/8 with throwaway keys), forge-agent #3 (.sops.yaml placeholders, ADR 0011, tests pinning env-over-.env), app-skeleton #1 (.env.example, .sops.yaml stub, staging-check moved to ephemeral-large and jfraeysd/infra typo fixed).
  • Gate test 1 (builds/tests with no infra repo, age keys or tooling): forge-agent go run ./dev test passes in that state.

Open (operator machine)

  • forge-agent: generate an operator age key (private half in macOS Keychain) and a recovery age key (private half in personal Bitwarden, plus an offline copy); replace the two age1REPLACE_... placeholders in .sops.yaml.
  • forge-agent: create and encrypt secrets/dev.sops.yml from the current local .env (FORGE_TEST_URL, FORGE_TEST_REPO, FORGE_TEST_USER, FORGE_TEST_TOKEN); run sops exec-env secrets/dev.sops.yml -- go run ./dev integration once.
  • Drill with the real keys: the recovery key decrypts secrets/dev.sops.yml; neither forge-agent key decrypts infra's secrets/vault.sops.yml (and vice versa). Record fingerprints in each repo's recovery doc.
  • Gate test 2: an app deploys through the documented contract as opt-in (e.g. coaching-site), with its secrets named in its .env.example.
  • app-skeleton: fill the staging-check TODOs per app (APP_REPO, APP_CHECK_EXTRA_VARS).
  • Decide whether other projects holding secrets (fetch_ml, afk_bot, ...) adopt the convention; one per PR.
  • Set the P7 row in docs/secrets-remediation.md from "Code met, keys pending" to Met.
Gate: "Projects work independently; recipient isolation tested". **Status 2026-10-13: convention and code merged; the keys are the remaining work and are yours to hold.** **Done** - [x] Merged: infra #109 (ADR 0001 `secrets/<name>.sops.yml` convention, Secrets section in the app deployment contract, `scripts/tests/test-project-key-isolation.sh`, 8/8 with throwaway keys), forge-agent #3 (`.sops.yaml` placeholders, ADR 0011, tests pinning env-over-`.env`), app-skeleton #1 (`.env.example`, `.sops.yaml` stub, `staging-check` moved to `ephemeral-large` and `jfraeysd/infra` typo fixed). - [x] Gate test 1 (builds/tests with no infra repo, age keys or tooling): forge-agent `go run ./dev test` passes in that state. **Open (operator machine)** - [ ] forge-agent: generate an operator age key (private half in macOS Keychain) and a recovery age key (private half in personal Bitwarden, plus an offline copy); replace the two `age1REPLACE_...` placeholders in `.sops.yaml`. - [ ] forge-agent: create and encrypt `secrets/dev.sops.yml` from the current local `.env` (`FORGE_TEST_URL`, `FORGE_TEST_REPO`, `FORGE_TEST_USER`, `FORGE_TEST_TOKEN`); run `sops exec-env secrets/dev.sops.yml -- go run ./dev integration` once. - [ ] Drill with the real keys: the recovery key decrypts `secrets/dev.sops.yml`; neither forge-agent key decrypts infra's `secrets/vault.sops.yml` (and vice versa). Record fingerprints in each repo's recovery doc. - [ ] Gate test 2: an app deploys through the documented contract as opt-in (e.g. coaching-site), with its secrets named in its `.env.example`. - [ ] app-skeleton: fill the `staging-check` TODOs per app (`APP_REPO`, `APP_CHECK_EXTRA_VARS`). - [ ] Decide whether other projects holding secrets (fetch_ml, afk_bot, ...) adopt the convention; one per PR. - [ ] Set the P7 row in `docs/secrets-remediation.md` from "Code met, keys pending" to Met.
Author
Owner

P7 status 2026-10-14 (operator-confirmed): forge-agent keys done. Operator key in Keychain sops-age/forge-agent, recovery key in personal Bitwarden sops-age-recovery-forge-agent plus an offline copy; secrets/dev.sops.yml encrypted from the local .env; sops exec-env secrets/dev.sops.yml 'go run ./dev integration' passes; the isolation drill passed with the real keys both ways.

Found on the way and fixed: sops exec-env takes exactly two arguments (file, one quoted command), so the -- <command> form in ADR 0001/0011 and the contract runbook failed with "missing file to decrypt" (infra #146, forge-agent #4); the integration harness now allows 90 s per request (forge-agent #5, FORGE_TEST_TIMEOUT overrides) because the scratch forge is a swapping 1 GB host. Note: the integration test's header says it targets a disposable forge, but its repo jfraeys/forge-agent-it lives on the production Forgejo.

Still open: forge-agent: commit .sops.yaml, secrets/dev.sops.yml, docs/secrets-recovery.md on a branch; gate test 2 (an app deploys through the contract); app-skeleton staging-check TODOs and keys if it adopts the convention; decision for fetch_ml / afk_bot. The P7 row in docs/secrets-remediation.md is "Keys met; gate test 2 pending" (infra #147), not Met. The wizard is removed in #147 (migration-only).

**P7 status 2026-10-14 (operator-confirmed):** forge-agent keys done. Operator key in Keychain `sops-age/forge-agent`, recovery key in personal Bitwarden `sops-age-recovery-forge-agent` plus an offline copy; `secrets/dev.sops.yml` encrypted from the local `.env`; `sops exec-env secrets/dev.sops.yml 'go run ./dev integration'` passes; the isolation drill passed with the real keys both ways. Found on the way and fixed: `sops exec-env` takes exactly two arguments (file, one quoted command), so the `-- <command>` form in ADR 0001/0011 and the contract runbook failed with "missing file to decrypt" (infra #146, forge-agent #4); the integration harness now allows 90 s per request (forge-agent #5, `FORGE_TEST_TIMEOUT` overrides) because the scratch forge is a swapping 1 GB host. Note: the integration test's header says it targets a disposable forge, but its repo `jfraeys/forge-agent-it` lives on the production Forgejo. Still open: forge-agent: commit `.sops.yaml`, `secrets/dev.sops.yml`, `docs/secrets-recovery.md` on a branch; gate test 2 (an app deploys through the contract); app-skeleton staging-check TODOs and keys if it adopts the convention; decision for fetch_ml / afk_bot. The P7 row in docs/secrets-remediation.md is "Keys met; gate test 2 pending" (infra #147), not Met. The wizard is removed in #147 (migration-only).
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
jfraeys/infra#118
No description provided.