fix(routing): state-hub lane uses Kubernetes auth, not an AppRole

The construction plan proposed an AppRole from a survey written with no
OpenBao session. The phase-4 live survey found kubernetes/ auth already
enabled on this cluster with four external-secrets-* roles using it, and
the founder ruled for it on 2026-08-27.

The pod authenticates with its own projected ServiceAccount token, so the
lane has no role_id/secret_id to deliver, store, or rotate. Bound to
state-hub/state-hub and deliberately not to default, which would grant
the lane to every pod in the namespace.

Policy and role are built and capability-verified. Entry stays draft: the
KV path holds no token until paste_once_provision delivers one, and the
ServiceAccount does not exist yet (STATE-WP-0084-T02).

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:03 +02:00
parent 6294e4b521
commit 6e66a56653
2 changed files with 21 additions and 9 deletions

View file

@ -583,7 +583,7 @@ entries:
workload_ref: workload_ref:
applicability: applicable applicability: applicable
unknown_reason: "The state-hub deployment has no authoritative workload identity declaration yet; the AppRole is the identity until it does." unknown_reason: "The state-hub deployment has no authoritative workload identity declaration yet; the AppRole is the identity until it does."
need_keywords: [state-hub, statehub, forge, forgejo, derivation, derive, projection, private, repository, clone, FORGE_READ_TOKEN, ADR-012, work-records, unreadable] need_keywords: [state-hub, statehub, forge, forgejo, derivation, derive, projection, private, repository, clone, FORGE_READ_TOKEN, ADR-012, work-records, unreadable, kubernetes-auth]
owner_repo: ops-mason owner_repo: ops-mason
subsystem: OpenBao + Forgejo subsystem: OpenBao + Forgejo
warden_executes: false warden_executes: false
@ -594,13 +594,13 @@ entries:
delegation: delegation:
mode: native mode: native
intended_owner: ops-mason intended_owner: ops-mason
blocked_on: "Entry proposed ahead of the build (MASON-WP-0003-T03). Promote to active once MASON-WP-0003-T02 has created the policy, AppRole, and KV path and the negative capability check passes." blocked_on: "Policy and Kubernetes auth role built and capability-verified 2026-08-27 (MASON-WP-0003-T02). Still draft: the KV path holds no token until paste_once_provision delivers one, and ServiceAccount state-hub/state-hub does not exist yet (STATE-WP-0084-T02). Promote to active once both land."
reviewed: "2026-08-26" reviewed: "2026-08-26"
verified: unverified verified: unverified
# ops-warden routes this and executes nothing: it issues SSH certificates # ops-warden routes this and executes nothing: it issues SSH certificates
# only and owns no secret store. ops-mason builds the structure; the forge # only and owns no secret store. ops-mason builds the structure; the forge
# owner mints the value; paste_once_provision delivers it. # owner mints the value; paste_once_provision delivers it.
auth_method: "AppRole login (state-hub-forge-derivation) from the in-cluster state-hub workload" auth_method: "Kubernetes auth (role state-hub-forge-derivation, ServiceAccount state-hub/state-hub) from the in-cluster state-hub workload"
path_template: "platform/workloads/state-hub/forge-derivation (field FORGE_READ_TOKEN)" path_template: "platform/workloads/state-hub/forge-derivation (field FORGE_READ_TOKEN)"
rotation: rotation:
method: rotate method: rotate

View file

@ -1,7 +1,7 @@
# State Hub Forge Derivation Read Token # State Hub Forge Derivation Read Token
Date: 2026-08-26 (proposed) Date: 2026-08-26 (policy and auth role built and verified 2026-08-27)
Catalog: `state-hub-forge-derivation-read` (status `draft` until built) Catalog: `state-hub-forge-derivation-read` (status `draft` until the token and ServiceAccount land)
Owner: `ops-mason` (built under `ops-mason/plans/state-hub-forge-derivation-read.md`, approved 2026-08-26) Owner: `ops-mason` (built under `ops-mason/plans/state-hub-forge-derivation-read.md`, approved 2026-08-26)
Read-only Forgejo token so the State Hub can clone **private** repositories and Read-only Forgejo token so the State Hub can clone **private** repositories and
@ -27,7 +27,7 @@ delivered through `paste_once_provision`.
| Path | `platform/workloads/state-hub/forge-derivation` | | Path | `platform/workloads/state-hub/forge-derivation` |
| Secret field | `FORGE_READ_TOKEN` | | Secret field | `FORGE_READ_TOKEN` |
| Policy | `workload-kv-read-state-hub-forge-derivation` (read on that path alone) | | Policy | `workload-kv-read-state-hub-forge-derivation` (read on that path alone) |
| AppRole | `state-hub-forge-derivation` (`token_ttl=15m`, `token_max_ttl=30m`) | | Auth | Kubernetes auth role `state-hub-forge-derivation`, bound to ServiceAccount `state-hub/state-hub` (`token_ttl=15m`, `token_max_ttl=30m`) |
**Forgejo token scope:** repository **read**, organisation-wide. No push, no **Forgejo token scope:** repository **read**, organisation-wide. No push, no
administration, no other Forgejo resource. Approved at that breadth on administration, no other Forgejo resource. Approved at that breadth on
@ -69,15 +69,27 @@ projection that only reads must not hold an identity that can write.
## Verify the lane (capabilities-safe — never read the value) ## Verify the lane (capabilities-safe — never read the value)
```bash ```bash
# Positive: the AppRole identity can read the data path # Positive: the lane identity can read the data path
bao token capabilities <approle-token> platform/data/workloads/state-hub/forge-derivation bao token capabilities <lane-token> platform/data/workloads/state-hub/forge-derivation
# → expect: read # → expect: read
# Negative: it can read nothing else, including the admin lane # Negative: it can read nothing else, including the admin lane
bao token capabilities <approle-token> platform/data/workloads/forgejo/forgejo-admin bao token capabilities <lane-token> platform/data/workloads/forgejo/forgejo-admin
# → expect: deny # → expect: deny
``` ```
Verified 2026-08-27 through a 2-minute test token, 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 (no listing).
**Why Kubernetes auth and not an AppRole.** The construction plan proposed an
AppRole; the phase-4 live survey found `kubernetes/` auth already enabled on
this cluster, and the founder ruled for it on 2026-08-27. The pod authenticates
with its own projected ServiceAccount token, so this lane has no `role_id`
/`secret_id` pair to deliver, store, or rotate. The role binds to
`state-hub/state-hub` and deliberately **not** to `default`, which would grant
the lane to every pod in the namespace.
The negative check is the one that matters. The whole argument for building this The negative check is the one that matters. The whole argument for building this
lane instead of reusing `forgejo-admin-api-token` is that it cannot do what that lane instead of reusing `forgejo-admin-api-token` is that it cannot do what that
one can. one can.