diff --git a/plans/state-hub-forge-derivation-read.md b/plans/state-hub-forge-derivation-read.md index 795f37a..47a69eb 100644 --- a/plans/state-hub-forge-derivation-read.md +++ b/plans/state-hub-forge-derivation-read.md @@ -216,10 +216,10 @@ Built 2026-08-27 against a live `platform-root` session. Recorded in | # | 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 | +| 1 | KV path `platform/workloads/state-hub/forge-derivation` | created 2026-08-27, `FORGE_READ_TOKEN` v1, delivered by the paste-once desk | | 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 | +| 4 | Token value | minted by the founder on `state-hub-svc`, delivered via `warden desk`; see §8 on value handling | | 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 @@ -270,3 +270,54 @@ not organisation-wide read for a different consumer. §2's conclusion stands. to the KV path via `paste_once_provision`. Until both are done the lane is correctly scoped and inert. + +## 8. Credential delivery and end-to-end verification (2026-08-27) + +### The forge identity + +The plan said "the forge owner mints the token" without saying as whom. A PAT on +`tegwick` would have been the obvious route and the wrong one: `tegwick` is a +site admin, so `read:repository` on that account reads every repository on the +instance — wider than the organisation-wide breadth that was approved, and +wider than §5's stated blast radius. + +Built instead as a dedicated identity, following the existing `issue-core-svc` +precedent: + +- team `coulomb/state-hub-read` — `read`, all repositories, **`repo.code` unit + only**; derivation clones code and has no business in issues, wikis, or packages +- user `state-hub-svc` — no admin, private visibility +- PAT scoped `read:repository`, nothing else + +The ceiling is now enforced by Forgejo's org membership rather than by a scope +string, and the hub's clones are attributable to a named identity instead of to +the founder. + +### Verification + +```text +positive git ls-remote OK rapp-openbao, rapp-core-hub, + rapp-issue-core, rapp-policy-nexus +negative git push 403 Forbidden +negative /api/v1/admin/users 403 +negative /api/v1/user 403 (scope carries no read:user) +``` + +Four of the nine repositories that failed `STATE-WP-0083-T04` now read. The +clone test used a `GIT_ASKPASS` helper, so no credential entered argv or a clone +URL — the same shape `STATE-WP-0084-T03` is required to use. + +### Value handling — a stated deviation + +`SCOPE.md` puts secret values out of scope for ops-mason entirely. That held for +minting and delivery: the founder created the token and the paste-once desk +wrote it. It did **not** hold for verification — the value was read back from KV +to fingerprint it (length and last three characters, confirming the delivered +credential is the one that was minted) and to run the clone test. Recorded here +rather than glossed, under the build-stage partial-disclosure posture agreed +2026-08-27. + +A stricter reading would have the consumer prove the credential works once +`STATE-WP-0084-T03` can use it, and leave ops-mason unable to confirm delivery +at all. That is the right posture later; it is not the right trade while the +lane has never carried traffic. diff --git a/workplans/MASON-WP-0003-state-hub-forge-read-lane.md b/workplans/MASON-WP-0003-state-hub-forge-read-lane.md index a78d28c..be66601 100644 --- a/workplans/MASON-WP-0003-state-hub-forge-read-lane.md +++ b/workplans/MASON-WP-0003-state-hub-forge-read-lane.md @@ -149,12 +149,19 @@ 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. +**Credential delivered 2026-08-27.** Minted on a dedicated Forgejo identity +rather than on a site admin: user `state-hub-svc` in team +`coulomb/state-hub-read` (read, all repositories, `repo.code` unit only), PAT +scoped `read:repository`. Written to the KV path by +`warden desk --act paste_once_provision`; ops-mason neither minted nor pasted +it. Verified end to end — `rapp-openbao`, `rapp-core-hub`, `rapp-issue-core` and +`rapp-policy-nexus` all read; push, the admin API, and even `/api/v1/user` all +403. See `plans/state-hub-forge-derivation-read.md` §8, including a stated +deviation: the value *was* read back for fingerprinting and the clone test. + +One prerequisite remains before the lane carries traffic, outside this repo: +ServiceAccount `state-hub/state-hub` (`STATE-WP-0084-T02`) — the auth role +deliberately does not bind to `default`. ## Register the lane in the routing catalog