2026-08-26 21:09:34 +02:00
|
|
|
|
---
|
|
|
|
|
|
id: MASON-WP-0003
|
|
|
|
|
|
type: workplan
|
|
|
|
|
|
title: "Forge read lane so central can derive private repositories"
|
|
|
|
|
|
domain: infotech
|
|
|
|
|
|
repo: ops-mason
|
2026-08-26 21:58:32 +02:00
|
|
|
|
status: active
|
2026-08-26 21:09:34 +02:00
|
|
|
|
owner: codex
|
|
|
|
|
|
topic_slug: custodian
|
|
|
|
|
|
created: "2026-08-26"
|
|
|
|
|
|
updated: "2026-08-26"
|
|
|
|
|
|
related:
|
|
|
|
|
|
- CUST-ADR-012
|
|
|
|
|
|
- STATE-WP-0083
|
2026-08-26 21:25:43 +02:00
|
|
|
|
- STATE-WP-0084
|
2026-08-26 21:10:12 +02:00
|
|
|
|
state_hub_workstream_id: "4015b46d-02f1-56ac-853e-87e8542cf0dd"
|
2026-08-26 21:09:34 +02:00
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
# Forge read lane so central can derive private repositories
|
|
|
|
|
|
|
|
|
|
|
|
## Goal
|
|
|
|
|
|
|
2026-08-26 21:25:43 +02:00
|
|
|
|
Build the credential lane that lets the State Hub read private repositories
|
|
|
|
|
|
from Forgejo, and register it so `warden route find` answers this question the
|
|
|
|
|
|
next time someone asks it.
|
|
|
|
|
|
|
|
|
|
|
|
This workplan builds the lane. It does not consume it — see the handoff below.
|
2026-08-26 21:09:34 +02:00
|
|
|
|
|
|
|
|
|
|
## Why this exists
|
|
|
|
|
|
|
|
|
|
|
|
`ADR-012` makes the forge the projection source: central clones a repository's
|
|
|
|
|
|
default branch and derives its work records from what Forgejo holds. That works
|
|
|
|
|
|
today because the pod clones anonymously.
|
|
|
|
|
|
|
|
|
|
|
|
It does not work for private repositories. The first fleet-wide reset
|
|
|
|
|
|
(`STATE-WP-0083-T04`, 2026-08-26) covered 121 repositories and nine failed
|
|
|
|
|
|
identically:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
fatal: could not read Username for 'https://forgejo.coulomb.social'
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
`rapp-core-hub`, `rapp-issue-core`, `rapp-openbao`, `rapp-policy-nexus` and five
|
|
|
|
|
|
others are invisible to the hub's derivation entirely.
|
|
|
|
|
|
|
|
|
|
|
|
This is a limit on the ADR's own premise. *The forge is the projection source*
|
|
|
|
|
|
holds only for repositories central can read, and nothing in the current design
|
2026-08-26 21:25:43 +02:00
|
|
|
|
says so.
|
2026-08-26 21:09:34 +02:00
|
|
|
|
|
|
|
|
|
|
## Why ops-mason owns it
|
|
|
|
|
|
|
|
|
|
|
|
`warden route find` returns no lane for this need. The nearest entry is
|
2026-08-26 21:25:43 +02:00
|
|
|
|
`forgejo-admin-api-token` — an operator PAT owned by `railiance-platform` —
|
|
|
|
|
|
which is far more authority than derivation requires and the wrong thing to
|
|
|
|
|
|
reuse.
|
2026-08-26 21:09:34 +02:00
|
|
|
|
|
|
|
|
|
|
This repository builds "OpenBao AppRoles, policies, and KV secret paths so
|
|
|
|
|
|
ops-warden always has something real to route to", which is exactly what is
|
|
|
|
|
|
missing. ops-warden issues SSH certificates only and routes everything else; a
|
|
|
|
|
|
Forgejo token is not its to vend.
|
|
|
|
|
|
|
2026-08-26 21:25:43 +02:00
|
|
|
|
## What ops-mason does not own
|
|
|
|
|
|
|
|
|
|
|
|
Three parts of the end-to-end fix belong to other repositories and are tracked
|
|
|
|
|
|
in `state-hub/STATE-WP-0084`:
|
|
|
|
|
|
|
|
|
|
|
|
- mounting the credential into the `state-hub` deployment;
|
|
|
|
|
|
- teaching `derive_from_forge()` to use it, and to distinguish "cannot read"
|
|
|
|
|
|
from "does not exist";
|
|
|
|
|
|
- re-running the fleet reset and reviewing the nine first diffs.
|
|
|
|
|
|
|
|
|
|
|
|
`STATE-WP-0084-T01` (the unreadable-vs-missing distinction) is not merely
|
|
|
|
|
|
downstream: the recommendation in this lane's construction plan depends on it,
|
|
|
|
|
|
because it is what makes a narrow grant fail visibly. It is worth landing with
|
|
|
|
|
|
or without this credential.
|
|
|
|
|
|
|
|
|
|
|
|
## Run the construction plan through the decision gate
|
2026-08-26 21:09:34 +02:00
|
|
|
|
|
|
|
|
|
|
```task
|
|
|
|
|
|
id: MASON-WP-0003-T01
|
2026-08-26 21:58:02 +02:00
|
|
|
|
status: done
|
2026-08-26 21:09:34 +02:00
|
|
|
|
priority: high
|
2026-08-26 21:10:12 +02:00
|
|
|
|
state_hub_task_id: "36d26000-ae71-5a58-a563-a07ff5be664f"
|
2026-08-26 21:09:34 +02:00
|
|
|
|
```
|
|
|
|
|
|
|
2026-08-26 21:25:43 +02:00
|
|
|
|
`plans/state-hub-forge-derivation-read.md` holds phases 1–3: the demand, the
|
|
|
|
|
|
existing-structure survey (including why `forgejo-admin-api-token` is rejected
|
|
|
|
|
|
rather than reused), the proposed AppRole/policy/KV path, the review pass, and
|
|
|
|
|
|
the executive summary.
|
2026-08-26 21:09:34 +02:00
|
|
|
|
|
2026-08-26 21:25:43 +02:00
|
|
|
|
Three things need deciding, and they determine what the credential can do if it
|
|
|
|
|
|
leaks:
|
2026-08-26 21:09:34 +02:00
|
|
|
|
|
2026-08-26 21:25:43 +02:00
|
|
|
|
1. **Approve, reject, or send back to phase 1.**
|
|
|
|
|
|
2. **Breadth** — organisation-wide repository read, or per-repository. The plan
|
|
|
|
|
|
recommends organisation-wide *contingent on `STATE-WP-0084-T01`*, and argues
|
|
|
|
|
|
the counter-case: an organisation-wide token that leaks reads every private
|
|
|
|
|
|
repository in the organisation.
|
|
|
|
|
|
3. **Rotation** — accept build-phase posture (no scheduled `secret_id` expiry),
|
|
|
|
|
|
or require rotation now.
|
2026-08-26 21:09:34 +02:00
|
|
|
|
|
2026-08-26 21:25:43 +02:00
|
|
|
|
Permission is not in question: derivation performs `git clone --depth 1` and
|
|
|
|
|
|
nothing else, so the token gets repository *read* and no other scope.
|
2026-08-26 21:09:34 +02:00
|
|
|
|
|
2026-08-26 21:25:43 +02:00
|
|
|
|
Acceptance: the plan reaches `status: reviewed`, the founder decision is
|
|
|
|
|
|
recorded with its reasoning rather than as a bare value, and the plan carries
|
|
|
|
|
|
`approved_by`/`approved_at`.
|
|
|
|
|
|
|
2026-08-26 21:58:02 +02:00
|
|
|
|
**Done (2026-08-26).** Approved by Bernd Worsch, breadth **organisation-wide
|
|
|
|
|
|
repository read**. The §4 contingency was satisfied first: `STATE-WP-0084-T01`
|
|
|
|
|
|
landed 2026-08-26, so a repository central may not read now reports as
|
|
|
|
|
|
unreadable and a source that produced no records cannot retire anything even
|
|
|
|
|
|
when retirement is acknowledged — which is what makes a missing grant visible
|
|
|
|
|
|
and, in turn, what the breadth argument rested on. Rotation was not separately
|
|
|
|
|
|
ruled on; build-phase posture stands as proposed. Reasoning in
|
|
|
|
|
|
`plans/state-hub-forge-derivation-read.md` §6.
|
|
|
|
|
|
|
2026-08-26 21:25:43 +02:00
|
|
|
|
## Build the AppRole, policy, and KV path structure
|
2026-08-26 21:09:34 +02:00
|
|
|
|
|
|
|
|
|
|
```task
|
|
|
|
|
|
id: MASON-WP-0003-T02
|
2026-08-27 22:49:13 +02:00
|
|
|
|
status: done
|
2026-08-26 21:09:34 +02:00
|
|
|
|
priority: high
|
2026-08-26 21:10:12 +02:00
|
|
|
|
state_hub_task_id: "68a1c8e4-c12b-550e-962c-06a90ab8c1c2"
|
2026-08-26 21:09:34 +02:00
|
|
|
|
```
|
|
|
|
|
|
|
2026-08-26 21:25:43 +02:00
|
|
|
|
Phase 4, after T01 approval only. Create the OpenBao policy granting read on
|
|
|
|
|
|
exactly one KV path, the KV path *structure*, and the AppRole the `state-hub`
|
|
|
|
|
|
workload authenticates with.
|
2026-08-26 21:09:34 +02:00
|
|
|
|
|
2026-08-26 21:25:43 +02:00
|
|
|
|
**ops-mason does not touch the token value, even transiently.** The forge owner
|
|
|
|
|
|
mints the read-only Forgejo token; it reaches the KV path through ops-warden's
|
|
|
|
|
|
existing `paste_once_provision` desk. This task records *where* the credential
|
|
|
|
|
|
lives and *who* may read it — never what it is. The value never appears in Git,
|
|
|
|
|
|
State Hub, a workplan, a log, or a chat message.
|
2026-08-26 21:09:34 +02:00
|
|
|
|
|
2026-08-26 21:25:43 +02:00
|
|
|
|
Acceptance: the policy grants read on that path alone; the AppRole
|
|
|
|
|
|
authenticates and can read nothing else; the KV path exists and the desk has
|
|
|
|
|
|
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.
|
2026-08-26 21:09:34 +02:00
|
|
|
|
|
2026-08-27 22:49:13 +02:00
|
|
|
|
**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.
|
|
|
|
|
|
|
feat(mason): deliver and verify the forge read credential
Minted on a dedicated identity rather than a site admin. A PAT on
tegwick would have read every repository on the instance — wider than
the approved organisation-wide breadth and wider than the plan's stated
blast radius. Built as user state-hub-svc in team coulomb/state-hub-read
(read, all repositories, repo.code unit only), following the existing
issue-core-svc precedent. The ceiling is enforced by org membership, not
by a scope string, and clones are attributable to a named identity.
Delivered by warden desk --act paste_once_provision; ops-mason neither
minted nor pasted the value.
Verified: rapp-openbao, rapp-core-hub, rapp-issue-core and
rapp-policy-nexus all read — four of the nine that failed
STATE-WP-0083-T04. Push 403, admin API 403, /api/v1/user 403 (the scope
carries no read:user). Clone test used GIT_ASKPASS, so no credential
entered argv or a clone URL.
Records a deviation rather than glossing it: SCOPE.md puts secret values
out of scope, and that held for minting and delivery but not for
verification — the value was read back to fingerprint it and to run the
clone test, under the build-stage disclosure posture agreed 2026-08-27.
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
2026-08-27 23:46:42 +02:00
|
|
|
|
**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`.
|
2026-08-27 22:49:13 +02:00
|
|
|
|
|
2026-08-26 21:09:34 +02:00
|
|
|
|
## Register the lane in the routing catalog
|
|
|
|
|
|
|
|
|
|
|
|
```task
|
|
|
|
|
|
id: MASON-WP-0003-T03
|
2026-08-26 21:58:02 +02:00
|
|
|
|
status: progress
|
2026-08-26 21:09:34 +02:00
|
|
|
|
priority: high
|
2026-08-26 21:10:12 +02:00
|
|
|
|
state_hub_task_id: "e14f0eb3-a920-5623-b4c1-c20858dbb45d"
|
2026-08-26 21:09:34 +02:00
|
|
|
|
```
|
|
|
|
|
|
|
2026-08-26 21:25:43 +02:00
|
|
|
|
Propose a pointer-only entry (`warden_executes: false`, no authored `steps`,
|
|
|
|
|
|
`status: draft`) so `warden route find "state hub read private repository"`
|
|
|
|
|
|
resolves. Its absence is why this had to be reasoned out from first principles
|
|
|
|
|
|
rather than looked up.
|
2026-08-26 21:09:34 +02:00
|
|
|
|
|
2026-08-26 21:25:43 +02:00
|
|
|
|
The entry names the owner, the subsystem, the KV path, the rotation method, and
|
|
|
|
|
|
states plainly that ops-warden routes but does not execute it. Normal git
|
|
|
|
|
|
contribution to `ops-warden`, not a live API call.
|
2026-08-26 21:09:34 +02:00
|
|
|
|
|
|
|
|
|
|
Acceptance: `warden route find` returns the lane; `warden route show` gives an
|
|
|
|
|
|
operator enough to act without reading this workplan.
|
|
|
|
|
|
|
2026-08-26 21:58:02 +02:00
|
|
|
|
**Drafted 2026-08-26**, awaiting contribution to `ops-warden` (branch
|
|
|
|
|
|
`mason/state-hub-forge-derivation-read`, commit `3a4333d`). Entry
|
|
|
|
|
|
`state-hub-forge-derivation-read`: pointer-only, `warden_executes: false`,
|
|
|
|
|
|
`status: draft`, `risk: high` — graded on breadth rather than write authority,
|
|
|
|
|
|
since the token is read-only but organisation-wide. Playbook at
|
|
|
|
|
|
`wiki/playbooks/state-hub-forge-derivation-read.md`, whose verification section
|
|
|
|
|
|
leads with the *negative* check: the AppRole must be denied on
|
|
|
|
|
|
`platform/workloads/forgejo/forgejo-admin`, because not being able to do what
|
|
|
|
|
|
the admin lane can is the entire argument for building this one.
|
|
|
|
|
|
|
|
|
|
|
|
`registry/generated/high-risk-data-paths.yaml` regenerated (21 → 24 lanes); it
|
|
|
|
|
|
had been stale since 2026-08-23, so the regeneration also picks up two
|
|
|
|
|
|
unrelated lanes. Promote the entry to `active` once T02 has built the structure
|
|
|
|
|
|
and the negative check passes.
|
|
|
|
|
|
|
2026-08-26 21:25:43 +02:00
|
|
|
|
## Handoff
|
2026-08-26 21:09:34 +02:00
|
|
|
|
|
2026-08-26 21:25:43 +02:00
|
|
|
|
Once T02 and T03 are done, notify `state-hub` so `STATE-WP-0084-T02` can
|
|
|
|
|
|
unblock. `STATE-WP-0084-T01` does not wait on this workplan.
|