feat(mason): build the state-hub forge read lane (MASON-WP-0003-T02)

Policy workload-kv-read-state-hub-forge-derivation and Kubernetes auth
role state-hub-forge-derivation, verified both directions through a
2-minute test token that was revoked after use: read on its own path;
deny on forgejo-admin, on the shared llm-connect provider secrets, and
on the parent metadata path.

Kubernetes auth replaces the approved AppRole. The plan's §2 survey was
written from disk with no OpenBao session and could not see that
kubernetes/ auth is enabled on this cluster; the plan's own phase-4
instruction to re-verify against live state is what surfaced it. Ruled
by the founder at phase 4. No static credential is created, so nothing
has to be delivered into the cluster or rotated.

The live survey also re-checked reuse: workload-kv-read-agent-harness-
forgejo grants one repository deploy key, not organisation-wide read, so
§2's rejection of reuse stands.

ops-mason handled no secret value. The KV path does not exist until
paste_once_provision writes the token.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 3377672@bnt-lap001
Assistant-Session: 15463ccf-238f-4e13-b163-93aa25c6d166
This commit is contained in:
tegwick 2026-08-27 22:49:13 +02:00
parent fb90abdfcb
commit 84c36a6c26
2 changed files with 80 additions and 2 deletions

View file

@ -3,7 +3,7 @@ id: state-hub-forge-derivation-read
demand_source: ops-mason/workplans/MASON-WP-0003
consumer_repo: state-hub
credential_type: openbao-approle-kv
status: approved
status: built
approved_by: "Bernd Worsch"
approved_at: "2026-08-26"
breadth: organisation-wide-read
@ -208,3 +208,65 @@ the OpenBao policy, the AppRole, the KV path structure, and the pointer-only
ops-warden catalog entry. It does not authorise ops-mason to mint or handle the
token value — the forge owner mints it with organisation-wide repository read
and no other scope, and `paste_once_provision` delivers it.
## 7. Build result (phase 4)
Built 2026-08-27 against a live `platform-root` session. Recorded in
`audit/build-log.jsonl`.
| # | Object | State |
|---|---|---|
| 1 | KV path `platform/workloads/state-hub/forge-derivation` | **not yet created** — comes into existence when `paste_once_provision` writes the value; the policy grants read on it regardless |
| 2 | Policy `workload-kv-read-state-hub-forge-derivation` | created, read on the data and metadata paths only |
| 3 | Kubernetes auth role `state-hub-forge-derivation` | created, bound to ServiceAccount `state-hub/state-hub`, `token_ttl=15m`, `token_max_ttl=30m` |
| 4 | Token value | **not handled by ops-mason** — forge owner mints, `paste_once_provision` delivers |
| 5 | ops-warden catalog entry | drafted on branch `mason/state-hub-forge-derivation-read`, `status: draft` |
### Deviation from the approved plan: Kubernetes auth, not AppRole
§2's survey was written from disk with no OpenBao session, and did not know that
`kubernetes/` auth is enabled on this same cluster (`https://10.43.0.1:443`)
with four `external-secrets-*` roles already using it. The plan's own phase-4
instruction — re-verify against live state before applying — is what surfaced it.
Ruled by the founder at phase 4 on 2026-08-27: **Kubernetes auth**. The pod
authenticates with its own projected ServiceAccount token, so no `role_id`
/`secret_id` pair exists to deliver into the cluster, store, or rotate. That
directly satisfies `STATE-WP-0084-T02`'s constraints — nothing baked into the
image, rotation without a redeploy — and removes a static credential of exactly
the kind whose custody gap cost a full session on the LLDAP side the previous
day.
### Verification
Positive and negative checked with `bao token capabilities` through a 2-minute
test token, revoked immediately after:
```text
platform/data/workloads/state-hub/forge-derivation read
platform/data/workloads/forgejo/forgejo-admin deny
platform/data/workloads/activity-core/llm-connect/...provider-secrets deny
platform/metadata/workloads/state-hub deny
```
The negative on `forgejo-admin` is the one that matters: not being able to do
what the admin lane can is the entire argument for building this lane rather
than reusing it.
### Reuse re-checked against live state
`bao policy list` shows one Forgejo-adjacent workload policy that §2 could not
see: `workload-kv-read-agent-harness-forgejo`. Read, and confirmed not
reusable — it grants a single agent-harness **deploy key** for one repository,
not organisation-wide read for a different consumer. §2's conclusion stands.
### Prerequisites before the lane carries traffic
1. ServiceAccount `state-hub` in namespace `state-hub` — the deployment
currently runs as `default`, and the role deliberately does not bind to
`default`, which would grant the lane to every pod in the namespace.
`STATE-WP-0084-T02`.
2. The Forgejo token, minted at organisation-wide repository read and delivered
to the KV path via `paste_once_provision`.
Until both are done the lane is correctly scoped and inert.

View file

@ -119,7 +119,7 @@ ruled on; build-phase posture stands as proposed. Reasoning in
```task
id: MASON-WP-0003-T02
status: todo
status: done
priority: high
state_hub_task_id: "68a1c8e4-c12b-550e-962c-06a90ab8c1c2"
```
@ -140,6 +140,22 @@ delivered a working token into it, confirmed by the consumer rather than by
reading the value here. Recorded in `audit/build-log.jsonl` against the
approved plan.
**Done (2026-08-27).** Policy `workload-kv-read-state-hub-forge-derivation` and
Kubernetes auth role `state-hub-forge-derivation` created and verified; see
`plans/state-hub-forge-derivation-read.md` §7 and `audit/build-log.jsonl`.
**Kubernetes auth replaces the approved AppRole**, ruled at phase 4 after the
live survey found `kubernetes/` auth enabled on this cluster — something §2's
disk-only survey could not see. No static `role_id`/`secret_id` is created, so
there is nothing to deliver or rotate.
ops-mason handled no secret value. The KV path does not exist yet and will not
until `paste_once_provision` writes the token; the policy grants read on it
either way. Two prerequisites remain before the lane carries traffic, both
outside this repo: ServiceAccount `state-hub/state-hub`
(`STATE-WP-0084-T02` — the role deliberately does not bind to `default`), and
the minted token.
## Register the lane in the routing catalog
```task