From bcdfc78087e0ae187da500db74ce0a273d1b62e5 Mon Sep 17 00:00:00 2001 From: tegwick Date: Mon, 29 Jun 2026 12:55:58 +0200 Subject: [PATCH] docs: seed SECRETS-WP-0004 (warden-sign lane); complete SCOPE.md sections - SECRETS-WP-0004: scoped warden-sign OpenBao token lane for ops-warden, to unblock FLEX-WP-0007 T4 joint smoke. First auth-capability (non-KV) lane and first lane touching production OpenBao (bao.coulomb.social). - SCOPE.md: add the standard sections flagged by the repo scope review (Relevant When, Not Relevant When, How It Fits, Terminology, Related / Overlapping, Provided Capabilities with fenced capability blocks); refresh Current State to reflect the delivered MVP. Co-Authored-By: Claude Opus 4.8 --- SCOPE.md | 113 +++++++++++- .../SECRETS-WP-0004-warden-sign-token-lane.md | 164 ++++++++++++++++++ 2 files changed, 273 insertions(+), 4 deletions(-) create mode 100644 workplans/SECRETS-WP-0004-warden-sign-token-lane.md diff --git a/SCOPE.md b/SCOPE.md index ab801c9..d40803c 100644 --- a/SCOPE.md +++ b/SCOPE.md @@ -39,9 +39,114 @@ roles, safe delivery modes, and non-secret evidence. - Storing raw secret values in this repo, State Hub, chat, prompts, or logs. - Broad platform-root or platform-admin automation as a steady-state model. +## Relevant When + +- An approved decision needs to become a narrow OpenBao policy/auth-role apply + without platform-root hand-typing. +- A workload or operator command needs a secret delivered **into the command** + without printing or exporting it (e.g. `npm publish`). +- A non-SSH credential need (API key, provider token, npm token, DB password, + scoped OpenBao token) is routed here by ops-warden. +- Build/test/production need different privilege, ceremony, and delivery rules + for the same kind of secret. +- A reviewer needs non-secret evidence of who applied/provisioned/verified what. + +## Not Relevant When + +- You need an **SSH certificate** (→ ops-warden `warden sign`). +- You need an **authorization decision** (→ flex-auth). +- You need **identity / OIDC / MFA / claim lifecycle** (→ user-engine / key-cape). +- You need a **tunnel** or remote transport (→ ops-bridge). +- You want OpenBao itself replaced as the custody/lease/audit backend (it is not). +- You want a raw secret value moved through Git, State Hub, chat, prompts, or logs + (never — delivery is exec-time or out-of-band only). + ## Current State -The repo is in bootstrap. Seed intent, PRD, boundary documentation, and an MVP -workplan are present. The first worker should complete State Hub bootstrap, -validate the generated repo identity files, then begin the whynot-design npm -publish token pilot through the `SECRETS-WP-0002` workplan. +MVP delivered. The Python CLI (`src/secrets_engine/`) proves the +`whynot-design-npm-publish` lane end to end — catalog → decision check → +policy/AppRole apply → provision → positive/negative verify → exec-time npm +delivery → ops-warden routing pointer → revoke — verified live against OpenBao +(44 tests; `scripts/demo-e2e.sh`, `scripts/npm-publish-demo.sh`). The +netkingdom maturity-gated publication-scope policy is in place but dormant +(netkingdom at `maturity-build`), so lanes clamp to repo-scope / `NPM_AUTH_TOKEN`. + +Bootstrap workplans `SECRETS-WP-0001` (State Hub integration) and +`SECRETS-WP-0002` (MVP) are finished. Open: `SECRETS-WP-0003` (real pilot +close-out) and `SECRETS-WP-0004` (scoped `warden-sign` token lane for +ops-warden / FLEX-WP-0007 T4), both proposed. + +## How It Fits + +secrets-engine is the **interaction layer** in the NetKingdom security stack. It +sits between approval/identity systems and the OpenBao backend: + +- **OpenBao / railiance-platform** — enforces custody, policy, lease, audit. + secrets-engine drives it through least-privilege stage roles and validated + paths, never as root. +- **flex-auth / State Hub decisions** — authorize. secrets-engine requires and + verifies a decision before privileged action and writes non-secret evidence + back. +- **user-engine / key-cape** — own identity and claims that bind consumers. +- **ops-warden** — routes non-SSH credential needs here (conduit-not-broker) and + issues SSH certs itself; secrets-engine mints and custodies the tokens. +- **ops-bridge** — may consume scoped delivery for remote execution but stores no + secret material. +- **info-tech-canon** — source of canonical terminology and stage/policy concepts. + +Canonical cross-system boundary: `net-kingdom/docs/secrets-engine-security-infrastructure-boundary.md`. + +## Terminology + +| Term | Meaning | +| --- | --- | +| **lane / catalog id** | a non-secret entry describing one secret's OpenBao location, consumers, delivery, approval | +| **stage** | `build` / `test` / `prod` — separate OpenBao privilege contexts | +| **stage role** | `secrets-engine-{build,test,prod}` OpenBao role, confined to its prefix | +| **delivery mode** | how a value leaves OpenBao: `exec-env`, `npm-config`, `read-check`, `wrapped` | +| **org / repo** | Gitea organisation (`coulomb`) / repository (`whynot-design`) — explicit, not the overloaded "project" | +| **npm scope** | the `@`-prefixed npm name (`@whynot`) — distinct from org and repo | +| **maturity** | `maturity-build/test/prod` package tag; feeds the publication-scope policy | +| **bootstrap token** | temporary mode-0600 OpenBao token used during setup, revocable, outside repos | + +## Related / Overlapping + +- **ops-warden** — front door / SSH cert issuer; routes non-SSH needs here. +- **OpenBao (railiance-platform)** — the custody/policy/lease/audit backend. +- **flex-auth** — authorization decisions secrets-engine depends on. +- **user-engine / key-cape** — identity and claim lifecycle for consumers. +- **ops-bridge** — remote transport; may consume scoped delivery. +- **net-kingdom** — owns the cross-system security-infrastructure boundary doc. +- **info-tech-canon** — canonical terminology source. + +## Provided Capabilities + +```capability +type: security +title: Decision-aware OpenBao policy/role apply +description: Turns an approved State Hub decision into a narrow, guarded OpenBao apply — + catalog lane to dry-run plan to idempotent ACL policy + AppRole write — through + least-privilege build/test/prod stage roles. Refuses unapproved, superseded, wildcard, + out-of-stage, or broad-admin plans before any backend call. Writes non-secret evidence. +keywords: [secrets, openbao, vault, policy, approle, catalog, decision, stage, least-privilege, netkingdom] +``` + +```capability +type: security +title: Exec-time secret delivery +description: Delivers a secret into a child process only — never printing, exporting, or + persisting the value. npm-config injects a temporary mode-0600 .npmrc pointing at the + configured registry; exec-env injects a scoped env var. Temp config is cleaned up on + success, failure, and interruption; child output is redacted as a backstop. +keywords: [secrets, delivery, exec, npm, publish, token, injection, redaction, npmrc, openbao] +``` + +```capability +type: governance +title: Maturity-gated publication-scope policy +description: Binds npm publication scope to package maturity (build to gitea-wide, test to + org-wide, prod to repo-scoped) and gates the graduated table behind the netkingdom domain + reaching production grade. While dormant, every lane clamps to the safest repo-scope — + fail-safe, never fail-open. The injected token env-var name signals the effective blast radius. +keywords: [policy, maturity, publication-scope, governance, least-privilege, npm, gitea, netkingdom] +``` diff --git a/workplans/SECRETS-WP-0004-warden-sign-token-lane.md b/workplans/SECRETS-WP-0004-warden-sign-token-lane.md new file mode 100644 index 0000000..4bb4461 --- /dev/null +++ b/workplans/SECRETS-WP-0004-warden-sign-token-lane.md @@ -0,0 +1,164 @@ +--- +id: SECRETS-WP-0004 +type: workplan +title: "Provision a scoped warden-sign token lane (ops-warden / FLEX-WP-0007 T4)" +domain: infotech +repo: secrets-engine +status: proposed +owner: codex +topic_slug: custodian +created: "2026-06-29" +updated: "2026-06-29" +--- + +# SECRETS-WP-0004 - Provision a scoped warden-sign token lane + +## Goal + +Give ops-warden a scoped OpenBao credential that can run `warden sign` over the +HTTP API, minted and custodied by secrets-engine. Model it as a first-class +secrets-engine **auth-capability lane** (an OpenBao policy + AppRole granting a +narrow capability), not a KV value. Post the non-secret pointers to State Hub and +hand the token/secret_id to the operator out-of-band. + +This unblocks FLEX-WP-0007 T4 (the joint OpenBao + policy-gate production smoke), +after which `policy.enabled: true` can go live on CoulombCore. + +## Context + +ops-warden raised this on State Hub (msg 077ac90d, 2026-06-28). warden's vault +backend signs over the HTTP API (`X-Vault-Token`) and holds no standing OpenBao +token (conduit-not-broker), so token custody/minting is squarely secrets-engine's +lane. The operator cannot mint one interactively: the `bao` CLI is not installed +on CoulombCore and `bao login -method=oidc` is therefore unavailable — hence the +preference for an AppRole the operator can use non-interactively. + +Target backend is the **production** OpenBao at `https://bao.coulomb.social`, +mount `ssh`. Actors in scope: `agt-state-hub-bridge` (primary), +`agt-codex-interhub-bootstrap`, `adm-example`, `atm-backup-daily`. The signing +roles are `agt-role`, `adm-role`, `atm-role`. + +This is the first non-KV, non-npm lane and the first lane that touches production +OpenBao, so it also exercises parts of the engine the npm pilot did not. + +## Design Constraints + +- No token value or AppRole `secret_id` in Git, State Hub, chat, prompts, + workplans, or normal logs — pointers only on the hub, value out-of-band. +- The `warden-sign` policy must be narrow: `update` on `ssh/sign/` for the + allowlisted roles only. No broader `ssh/*`, no `sys/`, `auth/token/`, + `identity/`, no root/sudo. +- Production apply uses a mode-0600 bootstrap token outside any repo, revocable, + recorded in `docs/hardening-backlog.md` H0. +- ops-warden takes no custody; secrets-engine owns minting and revocation. + +## Tasks + +## T01 - Extend the catalog for auth-capability (non-KV) lanes + +```task +id: SECRETS-WP-0004-T01 +status: todo +priority: high +``` + +Add a lane kind that represents an OpenBao auth credential (AppRole + policy) +granting a capability on a mount, rather than a KV value. The current schema +assumes a KV `mount/path/fields` value and `provision --from-file`; an +auth-capability lane has no stored value — the deliverable is the minted token / +`secret_id`. + +Acceptance: + +- The schema validates a `warden-sign` auth-capability lane (kind explicit). +- KV-only assumptions (`kv_data_path`, from-file provisioning, value verification) + are bypassed for this kind without weakening the npm KV path. +- `plan`/`apply` produce a policy + AppRole for the lane. + +## T02 - Define the warden-sign policy and AppRole + +```task +id: SECRETS-WP-0004-T02 +status: todo +priority: high +``` + +Author the `warden-sign` ACL policy and AppRole. Policy: `update` on +`ssh/sign/agt-role` (plus `adm-role`, `atm-role`) on the `ssh` mount; nothing +else. AppRole `warden-sign` bound to that policy with a short token TTL, usable +non-interactively via `role_id` + `secret_id` (no `bao` CLI needed). + +Acceptance: + +- The policy lists exact allowed/denied paths; the guards in `roles.py` refuse any + broader variant (`ssh/*`, `sys/`, `auth/token/`, `identity/`, wildcards, root). +- Negative check: a token from this AppRole can `sign` only the allowlisted roles + and is denied elsewhere on the `ssh` mount. +- The AppRole issues a scoped token carrying only the sign capability. + +## T03 - Apply against production OpenBao via bootstrap token + +```task +id: SECRETS-WP-0004-T03 +status: todo +priority: high +``` + +Apply the policy + AppRole on `https://bao.coulomb.social` using a mode-0600 +bootstrap token stored outside any repo. Idempotent re-apply. + +Acceptance: + +- The `warden-sign` policy and AppRole exist on the production OpenBao. +- The bootstrap token file is recorded in `docs/hardening-backlog.md` H0 with an + explicit revocation task and TTL. +- Apply evidence is non-secret (policy/role names, mount, addr, status — no value). + +## T04 - Out-of-band handoff and non-secret pointers + +```task +id: SECRETS-WP-0004-T04 +status: todo +priority: high +``` + +Define and execute the handoff: mint a fresh `secret_id`, deliver it (with the +`role_id`) to the operator out-of-band; warden does `approle login` to obtain a +`VAULT_TOKEN`. Post the non-secret pointers on the ops-warden thread (policy name, +AppRole name, mount `ssh`, addr `https://bao.coulomb.social`, token TTL, status). + +Acceptance: + +- Pointers are posted to State Hub; no token value or `secret_id` appears there or + in Git/logs. +- The operator can run, on CoulombCore: + `FLEX_AUTH_EXTERNAL=1 SMOKE_VAULT=1 VAULT_TOKEN= ~/ops-warden/scripts/policy_gate_production_smoke.sh`. + +## T05 - Confirm the joint smoke and signal ops-warden + +```task +id: SECRETS-WP-0004-T05 +status: todo +priority: medium +``` + +Confirm the unblock end to end, then reply to ops-warden (msg 077ac90d) with the +pointers and runbook alignment. The reply is the explicit "we will signal +ops-warden when done" step. + +Acceptance: + +- Allow path resolves to `backend: vault` + a `policy_decision_id`; deny path + (ttl 999) is rejected by flex-auth before OpenBao. +- ops-warden is replied to on State Hub with the non-secret pointers. +- A revocation/rotation task exists for the minted credential. + +## Exit Criteria + +- A narrow `warden-sign` policy + AppRole exist on production OpenBao, minted and + custodied by secrets-engine, granting only `ssh/sign/` update. +- The operator can obtain a scoped `VAULT_TOKEN` non-interactively without `bao`. +- FLEX-WP-0007 T4 joint smoke is unblocked (or blocked only on an explicit + external condition). +- ops-warden has the non-secret pointers; no secret value crossed State Hub. +- The bootstrap token and the minted credential have revocation tasks.