From 2f8387cc2ff4839c2403c852ff595c40b78e392f Mon Sep 17 00:00:00 2001 From: tegwick Date: Sat, 3 Oct 2026 12:55:08 +0200 Subject: [PATCH] CUST-WP-0075: catalog entry core-hub-forgejo-source-read (CCR-2026-0031) Co-Authored-By: Claude Opus 5.5 Assistant: claude-code Assistant-Model: opus Assistant-Process: 3775@bnt-lap001 Assistant-Session: cfacb6a5-ad5b-4f79-ac67-053e3b0d34a3 --- pep-stance.yaml | 4 +- registry/routing/catalog.yaml | 39 ++++++++++++++ tests/test_routing.py | 26 ++++++++++ tests/test_workload_join.py | 3 +- .../playbooks/core-hub-forgejo-source-read.md | 52 +++++++++++++++++++ 5 files changed, 121 insertions(+), 3 deletions(-) create mode 100644 wiki/playbooks/core-hub-forgejo-source-read.md diff --git a/pep-stance.yaml b/pep-stance.yaml index 01e1614..b2e1b39 100644 --- a/pep-stance.yaml +++ b/pep-stance.yaml @@ -94,14 +94,14 @@ on_apply: # a register that cannot detect its own staleness is only as good as the last # re-measure, and this one was stale by eight lanes before the test caught it. classification_coverage: - measured: "2026-09-28" + measured: "2026-10-03" attribution: self-measured signing_targets: resolved: 0 unknown: 3 not_applicable: 1 routing_lanes: - resolved: 3 + resolved: 4 unknown: 20 not_applicable: 15 diff --git a/registry/routing/catalog.yaml b/registry/routing/catalog.yaml index 32f10cc..3337836 100644 --- a/registry/routing/catalog.yaml +++ b/registry/routing/catalog.yaml @@ -390,6 +390,45 @@ entries: - "Use the attended railiance-platform bootstrap to update OpenBao and the coulomb/policy-nexus FORGEJO_SOURCE_TOKEN Actions secret without exposing the value." - "Pass one exact-commit candidate workflow, then revoke the predecessor PAT and record bounded non-secret evidence in CCR-2026-0014." + - id: core-hub-forgejo-source-read + title: Core Hub repo-manager publisher Forgejo private-source repository read token + # Read-only at Forgejo, but disclosure grants estate-wide private source read for + # org coulomb. ESO is the only consumer (CCR-2026-0031): no operator fetch lane, + # so exec_capable stays false and Warden never proxies a read of this value. + risk: high + workload_ref: + applicability: applicable + rapp_id: rapp-core-hub + name: core-hub + deployable: repository-classification-publisher + fields: [FORGEJO_SOURCE_TOKEN, API_USER, API_BASE_URL, TOKEN_SCOPES, GENERATED_AT] # CCR-2026-0031 + need_keywords: [core-hub, core, hub, repo-manager, publisher, forgejo, private, source, repository, read, token, eso, external-secrets, FORGEJO_SOURCE_TOKEN] + owner_repo: railiance-platform + subsystem: OpenBao + External Secrets + Forgejo + warden_executes: false + wiki_ref: wiki/playbooks/core-hub-forgejo-source-read.md#worker-checklist + canon_ref: railiance-platform/credential-change-requests/CCR-2026-0031-core-hub-forgejo-source-read.yaml + reviewed: "2026-10-03" + status: active + delegation: + mode: native + intended_owner: railiance-platform + reviewed: "2026-10-03" + verified: source-read + auth_method: "Kubernetes auth (mount kubernetes) role=external-secrets-core-hub-forgejo-source, bound to external-secrets/external-secrets; ESO delivery only (ClusterSecretStore openbao-core-hub-forgejo-source → ExternalSecret core-hub/repo-manager-forgejo-source)" + path_template: "platform/workloads/core-hub/forgejo-source-read" + policy_ref: "workload-kv-read-core-hub-forgejo-source" + exec_capable: false + lane: secret + rotation: + method: rotate + owner: railiance-platform + automatable: false + steps: + - "Every 180 days or on owner change: mint a replacement PAT for the restricted core-hub-source identity with scope exactly read:repository; retain the predecessor until verification passes." + - "Write the replacement to platform/workloads/core-hub/forgejo-source-read from stdin through the attended railiance-platform bootstrap, then let ESO refresh core-hub/repo-manager-forgejo-source (5m)." + - "Pass make verify-publisher in rapp-core-hub, then revoke the predecessor PAT and record bounded non-secret evidence in CCR-2026-0031." + - id: flex-auth-policy-check title: Authorization decision — may this actor perform this action # Returns an authorization decision; no credential flows (WARDEN-WP-0032-T05). diff --git a/tests/test_routing.py b/tests/test_routing.py index 58a3ce4..1943aba 100644 --- a/tests/test_routing.py +++ b/tests/test_routing.py @@ -197,6 +197,32 @@ def test_route_find_policy_nexus_source_read_prefers_concrete_lane(): assert matches[0].id == "policy-nexus-forgejo-source-read" +def test_core_hub_source_read_lane_is_eso_only_and_not_resolvable(): + """CCR-2026-0031: ESO is the only consumer, so no operator fetch lane.""" + catalog = load_catalog(_repo_catalog()) + entry = catalog.get("core-hub-forgejo-source-read") + assert entry is not None and entry.is_active + assert entry.exec_capable is False + assert entry.resolvable is False + assert entry.fetch_command is None + assert entry.lane == "secret" + assert entry.risk == "high" + assert entry.owner_repo == "railiance-platform" + assert entry.path_template == "platform/workloads/core-hub/forgejo-source-read" + assert "role=external-secrets-core-hub-forgejo-source" in entry.auth_method + assert entry.delegation is not None and entry.delegation.mode == "native" + assert entry.canon_ref.endswith("CCR-2026-0031-core-hub-forgejo-source-read.yaml") + + +def test_route_find_core_hub_source_read_prefers_core_hub_lane(): + catalog = load_catalog(_repo_catalog()) + matches = catalog.find( + "core hub repo-manager publisher Forgejo private source repository read token", + limit=1, + ) + assert matches[0].id == "core-hub-forgejo-source-read" + + def test_generic_and_template_lanes_not_resolvable(): catalog = load_catalog(_repo_catalog()) # generic openbao lane has /; login lane has . diff --git a/tests/test_workload_join.py b/tests/test_workload_join.py index d87b484..13d6fed 100644 --- a/tests/test_workload_join.py +++ b/tests/test_workload_join.py @@ -12,7 +12,7 @@ ROOT = Path(__file__).resolve().parents[1] def test_repo_catalog_uses_only_explicit_workload_references(): report = build(ROOT / "registry/routing/catalog.yaml", Path.home()) assert report["ok"] is True - assert len(report["resolved"]) == 3 + assert len(report["resolved"]) == 4 assert len(report["unknown"]) == 20 # 15 since 2026-09-21: the layer-declaration route is repository conformance # guidance, not a workload delivery lane, and the two NetKingdom SSO lanes @@ -25,6 +25,7 @@ def test_repo_catalog_uses_only_explicit_workload_references(): "ops-warden-warden-sign-token", "issue-core-ingestion-api-key", "rapp-qonto-keycape-client", + "core-hub-forgejo-source-read", # CCR-2026-0031, 2026-10-03 } diff --git a/wiki/playbooks/core-hub-forgejo-source-read.md b/wiki/playbooks/core-hub-forgejo-source-read.md new file mode 100644 index 0000000..af43f09 --- /dev/null +++ b/wiki/playbooks/core-hub-forgejo-source-read.md @@ -0,0 +1,52 @@ +# Core Hub Forgejo source-read token + +Date: 2026-10-03 +Catalog: `core-hub-forgejo-source-read` (status `active`, `exec_capable: false`, `resolvable: false`) +Owner: `railiance-platform` (OpenBao and credential lifecycle) + +This lane carries the dedicated Forgejo PAT that the Core Hub repo-manager +publisher (`rapp-core-hub`, deployable `repository-classification-publisher`) +uses to read private sources of the repository registry. The Forgejo identity +`core-hub-source` is restricted to organization-wide repository-code read in org +`coulomb`, and the PAT scope is exactly `read:repository`. It has no repository +write, package, organization administration, instance administration, Actions, +cluster, or deployment authority. + +The authoritative lifecycle and evidence record is +`railiance-platform/credential-change-requests/CCR-2026-0031-core-hub-forgejo-source-read.yaml` +(design: `railiance-platform/docs/credential-lane-designs/core-hub-forgejo-source-read.md`). +It is distinct from `policy-nexus-forgejo-source-read` (CCR-2026-0014); neither +identity nor token is reused across the two lanes. + +## Owner-confirmed lane + +| Field | Value | +| --- | --- | +| OpenBao path | `platform/workloads/core-hub/forgejo-source-read` | +| Fields | `FORGEJO_SOURCE_TOKEN`, `API_USER`, `API_BASE_URL`, `TOKEN_SCOPES`, `GENERATED_AT` | +| Read policy | `workload-kv-read-core-hub-forgejo-source` | +| Auth | Kubernetes auth role `external-secrets-core-hub-forgejo-source` (bound to `external-secrets/external-secrets`) | +| Delivery | ClusterSecretStore `openbao-core-hub-forgejo-source` → ExternalSecret `core-hub/repo-manager-forgejo-source` → Secret key `token` | +| Forgejo identity | `core-hub-source` | +| Forgejo team | `core-hub-source-readers` | + +## Worker checklist + +1. There is no operator or agent fetch lane. External Secrets is the only reader, + so `warden access core-hub-forgejo-source-read --fetch/--exec` is not offered + and the fetch-policy pin does not list this lane. Do not request one; if a + legitimate operator read need appears, it goes through a new CCR first. + +2. Check delivery health without reading values: ExternalSecret + `core-hub/repo-manager-forgejo-source` Ready, and `make verify-publisher` in + `rapp-core-hub`. Verify OpenBao allow/deny with `bao token capabilities`, never + `bao kv get` (see `catalog-lane-promotion.md`). + +3. For rotation (every 180 days or on owner change), deactivation, or compromise, + follow the CCR-2026-0031 lifecycle; `warden rotate-guide core-hub-forgejo-source-read` + prints the rotation steps. A replacement is not active until `make + verify-publisher` passes; revoke the predecessor afterward. + +4. Never place the value in argv, chat, Git, State Hub, logs, or a persistent + temporary file. Never substitute the Forgejo admin PAT, a personal token, or + the policy-nexus-source token.