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:
applicability: applicable
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
subsystem: OpenBao + Forgejo
warden_executes: false
@ -594,13 +594,13 @@ entries:
delegation:
mode: native
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"
verified: unverified
# ops-warden routes this and executes nothing: it issues SSH certificates
# only and owns no secret store. ops-mason builds the structure; the forge
# 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)"
rotation:
method: rotate

View file

@ -1,7 +1,7 @@
# State Hub Forge Derivation Read Token
Date: 2026-08-26 (proposed)
Catalog: `state-hub-forge-derivation-read` (status `draft` until built)
Date: 2026-08-26 (policy and auth role built and verified 2026-08-27)
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)
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` |
| Secret field | `FORGE_READ_TOKEN` |
| 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
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)
```bash
# Positive: the AppRole identity can read the data path
bao token capabilities <approle-token> platform/data/workloads/state-hub/forge-derivation
# Positive: the lane identity can read the data path
bao token capabilities <lane-token> platform/data/workloads/state-hub/forge-derivation
# → expect: read
# 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
```
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
lane instead of reusing `forgejo-admin-api-token` is that it cannot do what that
one can.