From a9e64d05fe177e453bb1e6479660d4fa8c87512d Mon Sep 17 00:00:00 2001 From: codex Date: Sun, 1 Nov 2026 23:34:27 +0100 Subject: [PATCH] docs: preserve reviewed NetKingdom session artifacts --- .../protected-backup/.custodian-brief.md | 62 + .../protected-backup/WORK-RECORDS.md | 536 ++++ .../hosted-before-federated.json | 2154 +++++++++++++++++ .../hub-private-source-pending.json | 105 + .../owner-access-gate.json | 18 + .../publication-preflight.json | 546 +++++ .../published-source-verification.json | 1194 +++++++++ .../scoped-access-handoff.json | 67 + .../.custodian-brief.md.worktree | 62 + .../WORK-RECORDS.md.worktree | 536 ++++ .../publication-backup/originals.json | 10 + .../publication-commit.json | 45 + .../state.json | 47 + .../verify_current_r2.py | 120 + .../.custodian-brief.md.worktree | 144 ++ .../WORK-RECORDS.md.worktree | 176 ++ .../publication-backup/originals.json | 10 + .../platform-delivery-backend-r1/README.md | 50 + .../docs/keycape-issuer-eso-contract.md | 114 + .../scripts/keycape_issuer_delivery.py | 182 ++ .../tests/test_keycape_issuer_delivery.py | 196 ++ .../railiance-platform.patch | 436 ++++ .../regressions.json | 15 + .../source-pins.json | 40 + .../platform-delivery-backend-r2/README.md | 130 + .../assert_corrected_regressions.py | 47 + .../eso-hash-inputs.json | 42 + .../eso-hash-vectors.go | 39 + .../patch-runs.json | 22 + .../docs/keycape-issuer-eso-contract.md | 175 ++ .../scripts/bounded_public_command.py | 107 + .../scripts/keycape_issuer_delivery.py | 284 +++ .../scripts/keycape_issuer_eso.py | 90 + .../tests/eso-hash-vectors.json | 57 + .../tests/test_bounded_public_command.py | 95 + .../tests/test_keycape_issuer_delivery.py | 284 +++ .../r1-to-r2.patch | 1217 ++++++++++ .../railiance-platform.patch | 993 ++++++++ .../platform-delivery-backend-r2/reproduce.py | 43 + .../upstream-sources.json | 37 + ...is_externalsecrets_v1_secretstore_types.go | 348 +++ ...xternalsecret_externalsecret_controller.go | 985 ++++++++ ...eso-pkg_controllers_externalsecret_util.go | 81 + .../eso-pkg_controllers_secretstore_common.go | 141 ++ .../upstream/eso-pkg_controllers_util_util.go | 37 + 45 files changed, 12119 insertions(+) create mode 100644 docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/protected-backup/.custodian-brief.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/protected-backup/WORK-RECORDS.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/protected-backup/docs/assessments/netkingdom-2026-10-14/hosted-before-federated.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/protected-backup/docs/assessments/netkingdom-2026-10-14/hub-private-source-pending.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/protected-backup/docs/assessments/netkingdom-2026-10-14/owner-access-gate.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/protected-backup/docs/assessments/netkingdom-2026-10-14/publication-preflight.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/protected-backup/docs/assessments/netkingdom-2026-10-14/published-source-verification.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/protected-backup/docs/assessments/netkingdom-2026-10-14/scoped-access-handoff.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/publication-backup/.custodian-brief.md.worktree create mode 100644 docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/publication-backup/WORK-RECORDS.md.worktree create mode 100644 docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/publication-backup/originals.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/publication-commit.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/state.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/verify_current_r2.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/platform-current-source-integration-r1/publication-backup/.custodian-brief.md.worktree create mode 100644 docs/changes/netkingdom-target-2026-10-17/platform-current-source-integration-r1/publication-backup/WORK-RECORDS.md.worktree create mode 100644 docs/changes/netkingdom-target-2026-10-17/platform-current-source-integration-r1/publication-backup/originals.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r1/README.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r1/prepared-source/docs/keycape-issuer-eso-contract.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r1/prepared-source/scripts/keycape_issuer_delivery.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r1/prepared-source/tests/test_keycape_issuer_delivery.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r1/railiance-platform.patch create mode 100644 docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r1/regressions.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r1/source-pins.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/README.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/assert_corrected_regressions.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/eso-hash-inputs.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/eso-hash-vectors.go create mode 100644 docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/patch-runs.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/prepared-source/docs/keycape-issuer-eso-contract.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/prepared-source/scripts/bounded_public_command.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/prepared-source/scripts/keycape_issuer_delivery.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/prepared-source/scripts/keycape_issuer_eso.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/prepared-source/tests/eso-hash-vectors.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/prepared-source/tests/test_bounded_public_command.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/prepared-source/tests/test_keycape_issuer_delivery.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/r1-to-r2.patch create mode 100644 docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/railiance-platform.patch create mode 100644 docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/reproduce.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/upstream-sources.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/upstream/eso-apis_externalsecrets_v1_secretstore_types.go create mode 100644 docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/upstream/eso-pkg_controllers_externalsecret_externalsecret_controller.go create mode 100644 docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/upstream/eso-pkg_controllers_externalsecret_util.go create mode 100644 docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/upstream/eso-pkg_controllers_secretstore_common.go create mode 100644 docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/upstream/eso-pkg_controllers_util_util.go diff --git a/docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/protected-backup/.custodian-brief.md b/docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/protected-backup/.custodian-brief.md new file mode 100644 index 00000000..6aad3a57 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/protected-backup/.custodian-brief.md @@ -0,0 +1,62 @@ + +# Custodian Brief — the-custodian + +**Domain:** infotech +**Last synced:** 2026-10-15 23:45 UTC +**State Hub:** http://127.0.0.1:8000 *(adjust if running on a remote machine)* + +## Active Workstreams + +### NetKingdom target security architecture and SEC-DSM implementation +Progress: 2/6 done | workplan_id: `dfadb19b-445e-59c6-bcd6-48e87b912c52` + +**Open tasks:** +- ► Close source defects and reconcile uncovered lifecycle gaps `87637402` +- ► Coordinate self-service Playwright identity delivery `ed8b909a` +- ► Establish the target architecture and drive the restructuring `6aca534c` +- · Verify native closure against every requirement `82378b0f` + +### Separate agent credentials and establish supervised privileged execution +Progress: 2/5 done | workplan_id: `a98a9f34-83b4-5c8c-8107-e05f7806d50d` + +**Open tasks:** +- ! Build and hand out the agent identity `4b88b0b7` + *(wait: Await GLAS-WP-0012 (glas-harness) production acceptance of the local supervised profile; then railiance-platform RBAC, railiance-enablement k3s config, railiance-infra principal mapping and ops-warden certificate issuance.)* +- ! Fleet guidance and harness guards `90725511` + *(wait: Await CUST-WP-0073-T02: the verified agent identity and execution path must exist before it can be documented; Codex/Grok read-denial guard needs glas-harness or harness-owner delivery.)* +- ! Rotate what was exposed `1b41b532` + *(wait: Deferred by founder decision 2026-09-24; reopened only by the founder on one of the three triggers below, in one attended rotation window.)* + +### Size Railiance workloads from actual demand and establish a weekly allocation review +Progress: 1/5 done | workplan_id: `2249bddb-7524-5add-bd5c-c4163a6ca0f3` + +**Open tasks:** +- ! Measure the Vergabe prototype under representative pilot use `82192370` + *(wait: Await RAPPS-WP-0014-T03 (blocked, needs_human): populated two-user/document workflow run by Bernd and the company contact, giving representative load for p95 <= 2s / zero failures.)* +- ! Review ecosystem allocations and reserve operating room `6dc67558` + *(wait: Await CUST-WP-0071-T02 representative sample; Forgejo/runner demand and zero-request workload review need owner input (railiance-cluster, railiance-forge, resource-control).)* +- ! Apply accepted sizing changes and verify their effects `944f98e2` + *(wait: Await reviewed allocation proposals and applicable workload-owner authority.)* +- ! Establish the regular weekly workload-demand and allocation assessment `20ddd4d7` + *(wait: Await the repeatable observation and accepted sizing/review procedure.)* + +### Deterministic security flow: standing authorizations, flow report, and agent re-engagement +Progress: 3/7 done | workplan_id: `ab288f92-d364-5c24-9362-aee451f266c3` + +**Open tasks:** +- ! Schedule the flow report and decision sync `1be0569c` + *(wait: Activity Core accepted the schedule in ACTIVITY-WP-0047. Await claiming rein workstation/hub acceptance, owner activation and the first unassisted fire with report/checkpoint evidence.)* +- ! Encode the standing change rule as a policy-nexus guardrail `5dd52156` + *(wait: policy-nexus owns guardrail publication; handoff written as PNEX-WP-0006.)* +- ! Stop agent-authored index and sync commits `8a3dc219` + *(wait: Local guard and repeatable shared-hook installation are implemented. Fleet generator adoption and the required full week of clean history remain unverified; repo-manager owner acceptance is needed.)* +- ! Attended window #1 `e3da807f` + *(wait: Partly done 2026-10-11: list cut from 14 to 3; U05-U07 accepted, U08 waits on KEY-WP-0040-T01. Remaining founder acts: USER-WP-0026-T03 readback, RPF-WP-0044-T08 coulombcore check, the U08 re-run, and Sol's added RPF-WP-0044-T04 issuer login.)* + +--- +## MCP Orientation (when available) + +If the state-hub MCP server is reachable, call: +`get_domain_summary("infotech")` +This provides richer cross-domain context. +If the MCP call fails, use this file as your orientation source. diff --git a/docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/protected-backup/WORK-RECORDS.md b/docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/protected-backup/WORK-RECORDS.md new file mode 100644 index 00000000..6dc81634 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/protected-backup/WORK-RECORDS.md @@ -0,0 +1,536 @@ +# Work Records — the-custodian + +> Generated by `statehub fix-consistency` (CUST-WP-0061-T04, work-record +> stage 3). Do not edit by hand — edit the source file/block listed for +> each record and re-run fix-consistency to refresh this index. Archived +> workplans are omitted; closed decisions/intakes/engagements stay listed +> so recently-resolved work is still visible. [auto] + +| Kind | ID | Status | Lane | Source | +| --- | --- | --- | --- | --- | +| workplan | CUST-WP-ADHOC-2026-05-02 | done | — | workplans/ADHOC-2026-05-02.md | +| workplan | CUST-WP-ADHOC-2026-07-02 | finished | — | workplans/ADHOC-2026-07-02.md | +| workplan | CUST-WP-ADHOC-2026-08-25-RETIRED | finished | — | workplans/ADHOC-2026-08-25-retired.md | +| workplan | CUST-WP-ADHOC-2026-08-25 | finished | — | workplans/ADHOC-2026-08-25.md | +| workplan | CUST-WP-ADHOC-2026-09-06 | finished | — | workplans/ADHOC-2026-09-06.md | +| workplan | CUST-WP-0000 | finished | — | workplans/CUST-WP-0000-state-hub-v0.1-build-deploy.md | +| workplan | CUST-WP-0000b | finished | — | workplans/CUST-WP-0000b-state-hub-v0.2-decisions-dependencies.md | +| workplan | CUST-WP-0001 | done | — | workplans/CUST-WP-0001-custodian-agent-runtime.md | +| workplan | CUST-WP-0002 | finished | — | workplans/CUST-WP-0002-contribution-tracking-sbom.md | +| workplan | CUST-WP-0004 | finished | — | workplans/CUST-WP-0004-ep-td-tracking.md | +| workplan | CUST-WP-0005 | finished | — | workplans/CUST-WP-0005-dynamic-domains.md | +| workplan | CUST-WP-0006 | done | — | workplans/CUST-WP-0006-gems-state-hub.md | +| workplan | CUST-WP-0007 | finished | — | workplans/CUST-WP-0007-gems-migration.md | +| workplan | CUST-WP-0008 | done | — | workplans/CUST-WP-0008-consistency-engine.md | +| workplan | CUST-WP-0009 | finished | — | workplans/CUST-WP-0009-human-interventions.md | +| workplan | CUST-WP-0010 | finished | — | workplans/CUST-WP-0010-workstream-lifecycle-docs.md | +| workplan | CUST-WP-0010b | finished | — | workplans/CUST-WP-0010b-domain-and-repo-goals.md | +| workplan | CUST-WP-0013 | finished | — | workplans/CUST-WP-0013-sbom-infra-expansion.md | +| workplan | CUST-WP-0015 | done | — | workplans/CUST-WP-0015-agent-inbox.md | +| workplan | CUST-WP-0016 | done | — | workplans/CUST-WP-0016-kaizen-agentic-integration.md | +| workplan | CUST-WP-0017 | done | — | workplans/CUST-WP-0017-scope-md-agent-and-coverage.md | +| workplan | CUST-WP-0018 | done | — | workplans/CUST-WP-0018-api-hardening-code-quality.md | +| workplan | CUST-WP-0019 | done | — | workplans/CUST-WP-0019-dashboard-ux-polish.md | +| workplan | CUST-WP-0020 | done | — | workplans/CUST-WP-0020-test-suite.md | +| workplan | CUST-WP-0021 | done | — | workplans/CUST-WP-0021-multi-host-repo-paths.md | +| workplan | CUST-WP-0022 | done | — | workplans/CUST-WP-0022-capability-requests.md | +| workplan | CUST-WP-0023 | done | — | workplans/CUST-WP-0023-tpsc.md | +| workplan | CUST-WP-0024 | done | — | workplans/CUST-WP-0024-repo-doi-gate.md | +| workplan | CUST-WP-0026 | done | — | workplans/CUST-WP-0026-distributed-consistency.md | +| workplan | CUST-WP-0027 | done | — | workplans/CUST-WP-0027-capability-request-dispute.md | +| workplan | CUST-WP-0029 | done | — | workplans/CUST-WP-0029-token-consumption-tracking.md | +| workplan | CUST-WP-0030 | done | — | workplans/CUST-WP-0030-dashboard-entity-list-ux.md | +| workplan | CUST-WP-0031 | done | — | workplans/CUST-WP-0031-capability-registry.md | +| workplan | CUST-WP-0032 | done | — | workplans/CUST-WP-0032-haskell-build-machine.md | +| workplan | CUST-WP-0033 | done | — | workplans/CUST-WP-0033-interface-change-registry.md | +| workplan | CUST-WP-0039 | done | — | workplans/CUST-WP-0039-dashboard-poll-optimization.md | +| workplan | CUST-WP-0040 | finished | — | workplans/CUST-WP-0040-state-hub-nats-activity-core-integration.md | +| workplan | CUST-WP-0041 | done | — | workplans/CUST-WP-0041-api-performance.md | +| workplan | CUST-WP-0043 | finished | — | workplans/CUST-WP-0043-state-hub-repo-extraction.md | +| workplan | CUST-WP-0044 | finished | — | workplans/CUST-WP-0044-daily-statehub-wsjf-triage.md | +| workplan | CUST-WP-0045 | finished | — | workplans/CUST-WP-0045-activity-core-daily-triage-runner.md | +| workplan | CUST-WP-0069 | finished | — | workplans/CUST-WP-0045-cutover-runbook.md | +| workplan | CUST-WP-0046 | finished | — | workplans/CUST-WP-0046-hourly-recently-on-scope-activity-core.md | +| workplan | CUST-WP-0047 | finished | — | workplans/CUST-WP-0047-ops-hub-service-inventory-now-view.md | +| workplan | CUST-WP-0048 | finished | — | workplans/CUST-WP-0048-hub-core-state-hub-import-refactor.md | +| workplan | CUST-WP-0049 | finished | — | workplans/CUST-WP-0049-interhub-bootstrap-access-lane.md | +| workplan | CUST-WP-0050 | finished | — | workplans/CUST-WP-0050-repo-classification-registration-redesign.md | +| workplan | CUST-WP-0051 | finished | — | workplans/CUST-WP-0051-infrastructure-stabilization-metaplan.md | +| workplan | CUST-WP-0052 | finished | — | workplans/CUST-WP-0052-core-hub-fos-reset.md | +| workplan | CUST-WP-0053 | finished | — | workplans/CUST-WP-0053-coordination-hygiene-improvements.md | +| workplan | CUST-WP-0055 | finished | — | workplans/CUST-WP-0055-workplan-terminology-fleet-refactor.md | +| workplan | CUST-WP-0056 | finished | — | workplans/CUST-WP-0056-daily-todo-md-stale-review.md | +| workplan | CUST-WP-0059 | finished | — | workplans/CUST-WP-0059-forgejo-ci-publish-cutover-tail.md | +| workplan | CUST-WP-0060 | finished | — | workplans/CUST-WP-0060-work-record-canonization.md | +| workplan | CUST-WP-0061 | finished | — | workplans/CUST-WP-0061-work-record-stage3-intake-promotion-index.md | +| workplan | CUST-WP-0062 | finished | — | workplans/CUST-WP-0062-sbom-nexus-daily-catchup.md | +| workplan | CUST-WP-0063 | finished | — | workplans/CUST-WP-0063-inbox-governance-packets.md | +| workplan | CUST-WP-0064 | finished | — | workplans/CUST-WP-0064-sbom-controlled-scan-inputs.md | +| workplan | CUST-WP-0065 | finished | — | workplans/CUST-WP-0065-reclassify-repos-and-guidance-docs-to-the-new-sector-domain.md | +| workplan | CUST-WP-0066 | finished | — | workplans/CUST-WP-0066-repository-qualified-ad-hoc-identifiers.md | +| workplan | CUST-WP-0067 | finished | — | workplans/CUST-WP-0067-hub-authority-target-resolution.md | +| workplan | CUST-WP-0068 | finished | — | workplans/CUST-WP-0068-cache-only-work-record-recovery.md | +| workplan | CUST-WP-0070 | finished | — | workplans/CUST-WP-0070-publication-repo-category.md | +| workplan | CUST-WP-0071 | blocked | — | workplans/CUST-WP-0071-measured-workload-sizing-and-weekly-review.md | +| workplan | CUST-WP-0072 | finished | — | workplans/CUST-WP-0072-fleet-flavor-and-depends-on-backfill.md | +| workplan | CUST-WP-0073 | blocked | — | workplans/CUST-WP-0073-agent-credential-separation.md | +| workplan | CUST-WP-0074 | finished | — | workplans/CUST-WP-0074-qualified-wait-states.md | +| workplan | CUST-WP-0075 | finished | — | workplans/CUST-WP-0075-dependency-graph-driven-unblocking.md | +| workplan | CUST-WP-0076 | finished | — | workplans/CUST-WP-0076-provisioning-window-cadence.md | +| workplan | CUST-WP-0077 | finished | — | workplans/CUST-WP-0077-dependency-root-execution-wave.md | +| workplan | CUST-WP-0078 | finished | — | workplans/CUST-WP-0078-security-collaboration-consolidation.md | +| workplan | CUST-WP-0079 | blocked | — | workplans/CUST-WP-0079-deterministic-security-flow.md | +| workplan | CUST-WP-0080 | active | — | workplans/CUST-WP-0080-netkingdom-security-blueprint-and-gap-closure.md | +| workplan | CUST-WP-0081 | finished | — | workplans/CUST-WP-0081-netkingdom-architecture-review-and-reuse.md | +| workplan | THE-WP-0001 | finished | — | workplans/THE-WP-0001-federation-interface.md | +| task | CUST-WP-ADHOC-2026-05-02-T01 | done | — | workplans/ADHOC-2026-05-02.md | +| task | CUST-WP-ADHOC-2026-07-02-T01 | done | — | workplans/ADHOC-2026-07-02.md | +| task | ADHOC-2026-08-25-T01 | done | — | workplans/ADHOC-2026-08-25-retired.md | +| task | CUST-WP-ADHOC-2026-08-25-T01 | done | — | workplans/ADHOC-2026-08-25.md | +| task | CUST-WP-ADHOC-2026-09-06-T01 | done | — | workplans/ADHOC-2026-09-06.md | +| task | CUST-WP-0001-T01 | done | — | workplans/CUST-WP-0001-custodian-agent-runtime.md | +| task | CUST-WP-0001-T02 | done | — | workplans/CUST-WP-0001-custodian-agent-runtime.md | +| task | CUST-WP-0002-T01 | done | — | workplans/CUST-WP-0002-contribution-tracking-sbom.md | +| task | CUST-WP-0002-T02 | done | — | workplans/CUST-WP-0002-contribution-tracking-sbom.md | +| task | CUST-WP-0002-T03 | done | — | workplans/CUST-WP-0002-contribution-tracking-sbom.md | +| task | CUST-WP-0002-T04 | done | — | workplans/CUST-WP-0002-contribution-tracking-sbom.md | +| task | CUST-WP-0002-T05 | done | — | workplans/CUST-WP-0002-contribution-tracking-sbom.md | +| task | CUST-WP-0002-T06 | done | — | workplans/CUST-WP-0002-contribution-tracking-sbom.md | +| task | CUST-WP-0002-T07 | done | — | workplans/CUST-WP-0002-contribution-tracking-sbom.md | +| task | CUST-WP-0002-T08 | done | — | workplans/CUST-WP-0002-contribution-tracking-sbom.md | +| task | CUST-WP-0002-T09 | done | — | workplans/CUST-WP-0002-contribution-tracking-sbom.md | +| task | CUST-WP-0002-T10 | done | — | workplans/CUST-WP-0002-contribution-tracking-sbom.md | +| task | CUST-WP-0002-T11 | done | — | workplans/CUST-WP-0002-contribution-tracking-sbom.md | +| task | CUST-WP-0002-T12 | done | — | workplans/CUST-WP-0002-contribution-tracking-sbom.md | +| task | CUST-WP-0002-T13 | done | — | workplans/CUST-WP-0002-contribution-tracking-sbom.md | +| task | CUST-WP-0002-T14 | done | — | workplans/CUST-WP-0002-contribution-tracking-sbom.md | +| task | CUST-WP-0002-T15 | done | — | workplans/CUST-WP-0002-contribution-tracking-sbom.md | +| task | CUST-WP-0004-T01 | done | — | workplans/CUST-WP-0004-ep-td-tracking.md | +| task | CUST-WP-0004-T02 | done | — | workplans/CUST-WP-0004-ep-td-tracking.md | +| task | CUST-WP-0004-T03 | done | — | workplans/CUST-WP-0004-ep-td-tracking.md | +| task | CUST-WP-0004-T04 | done | — | workplans/CUST-WP-0004-ep-td-tracking.md | +| task | CUST-WP-0004-T05 | done | — | workplans/CUST-WP-0004-ep-td-tracking.md | +| task | CUST-WP-0004-T06 | done | — | workplans/CUST-WP-0004-ep-td-tracking.md | +| task | CUST-WP-0004-T07 | done | — | workplans/CUST-WP-0004-ep-td-tracking.md | +| task | CUST-WP-0004-T08 | done | — | workplans/CUST-WP-0004-ep-td-tracking.md | +| task | CUST-WP-0004-T09 | done | — | workplans/CUST-WP-0004-ep-td-tracking.md | +| task | CUST-WP-0004-T10 | done | — | workplans/CUST-WP-0004-ep-td-tracking.md | +| task | CUST-WP-0005-T01 | done | — | workplans/CUST-WP-0005-dynamic-domains.md | +| task | CUST-WP-0005-T02 | done | — | workplans/CUST-WP-0005-dynamic-domains.md | +| task | CUST-WP-0005-T03 | done | — | workplans/CUST-WP-0005-dynamic-domains.md | +| task | CUST-WP-0005-T04 | done | — | workplans/CUST-WP-0005-dynamic-domains.md | +| task | CUST-WP-0005-T05 | done | — | workplans/CUST-WP-0005-dynamic-domains.md | +| task | CUST-WP-0005-T06 | done | — | workplans/CUST-WP-0005-dynamic-domains.md | +| task | CUST-WP-0005-T07 | done | — | workplans/CUST-WP-0005-dynamic-domains.md | +| task | CUST-WP-0005-T08 | done | — | workplans/CUST-WP-0005-dynamic-domains.md | +| task | CUST-WP-0005-T09 | done | — | workplans/CUST-WP-0005-dynamic-domains.md | +| task | CUST-WP-0005-T10 | done | — | workplans/CUST-WP-0005-dynamic-domains.md | +| task | CUST-WP-0005-T11 | done | — | workplans/CUST-WP-0005-dynamic-domains.md | +| task | T01 | done | — | workplans/CUST-WP-0006-gems-state-hub.md | +| task | T02 | done | — | workplans/CUST-WP-0006-gems-state-hub.md | +| task | T03 | done | — | workplans/CUST-WP-0006-gems-state-hub.md | +| task | T04 | done | — | workplans/CUST-WP-0006-gems-state-hub.md | +| task | T05 | done | — | workplans/CUST-WP-0006-gems-state-hub.md | +| task | T01 | done | — | workplans/CUST-WP-0007-gems-migration.md | +| task | T02 | done | — | workplans/CUST-WP-0007-gems-migration.md | +| task | T03 | done | — | workplans/CUST-WP-0007-gems-migration.md | +| task | T04 | done | — | workplans/CUST-WP-0007-gems-migration.md | +| task | T05 | done | — | workplans/CUST-WP-0007-gems-migration.md | +| task | T06 | done | — | workplans/CUST-WP-0007-gems-migration.md | +| task | T07 | done | — | workplans/CUST-WP-0007-gems-migration.md | +| task | T08 | done | — | workplans/CUST-WP-0007-gems-migration.md | +| task | T09 | done | — | workplans/CUST-WP-0007-gems-migration.md | +| task | T10 | done | — | workplans/CUST-WP-0007-gems-migration.md | +| task | T11 | done | — | workplans/CUST-WP-0007-gems-migration.md | +| task | T12 | done | — | workplans/CUST-WP-0007-gems-migration.md | +| task | T13 | done | — | workplans/CUST-WP-0007-gems-migration.md | +| task | T14 | done | — | workplans/CUST-WP-0007-gems-migration.md | +| task | T15 | done | — | workplans/CUST-WP-0007-gems-migration.md | +| task | T16 | done | — | workplans/CUST-WP-0007-gems-migration.md | +| task | T17 | done | — | workplans/CUST-WP-0007-gems-migration.md | +| task | CUST-WP-0008-T01 | done | — | workplans/CUST-WP-0008-consistency-engine.md | +| task | CUST-WP-0008-T02 | done | — | workplans/CUST-WP-0008-consistency-engine.md | +| task | CUST-WP-0008-T03 | done | — | workplans/CUST-WP-0008-consistency-engine.md | +| task | CUST-WP-0008-T04 | done | — | workplans/CUST-WP-0008-consistency-engine.md | +| task | CUST-WP-0009-T1 | done | — | workplans/CUST-WP-0009-human-interventions.md | +| task | CUST-WP-0009-T2 | done | — | workplans/CUST-WP-0009-human-interventions.md | +| task | CUST-WP-0009-T3 | done | — | workplans/CUST-WP-0009-human-interventions.md | +| task | CUST-WP-0010-T1 | done | — | workplans/CUST-WP-0010-workstream-lifecycle-docs.md | +| task | CUST-WP-0010-T2 | done | — | workplans/CUST-WP-0010-workstream-lifecycle-docs.md | +| task | CUST-WP-0010-T3 | done | — | workplans/CUST-WP-0010-workstream-lifecycle-docs.md | +| task | CUST-WP-0010b-T01 | done | — | workplans/CUST-WP-0010b-domain-and-repo-goals.md | +| task | CUST-WP-0010b-T02 | done | — | workplans/CUST-WP-0010b-domain-and-repo-goals.md | +| task | CUST-WP-0013-T01 | done | — | workplans/CUST-WP-0013-sbom-infra-expansion.md | +| task | CUST-WP-0013-T02 | done | — | workplans/CUST-WP-0013-sbom-infra-expansion.md | +| task | CUST-WP-0013-T03 | done | — | workplans/CUST-WP-0013-sbom-infra-expansion.md | +| task | CUST-WP-0013-T04 | done | — | workplans/CUST-WP-0013-sbom-infra-expansion.md | +| task | CUST-WP-0013-T05 | done | — | workplans/CUST-WP-0013-sbom-infra-expansion.md | +| task | CUST-WP-0013-T06 | done | — | workplans/CUST-WP-0013-sbom-infra-expansion.md | +| task | CUST-WP-0013-T07 | done | — | workplans/CUST-WP-0013-sbom-infra-expansion.md | +| task | CUST-WP-0013-T08 | done | — | workplans/CUST-WP-0013-sbom-infra-expansion.md | +| task | CUST-WP-0013-T09 | done | — | workplans/CUST-WP-0013-sbom-infra-expansion.md | +| task | CUST-WP-0013-T10 | done | — | workplans/CUST-WP-0013-sbom-infra-expansion.md | +| task | CUST-WP-0013-T11 | done | — | workplans/CUST-WP-0013-sbom-infra-expansion.md | +| task | CUST-WP-0013-T12 | done | — | workplans/CUST-WP-0013-sbom-infra-expansion.md | +| task | CUST-WP-0013-T13 | done | — | workplans/CUST-WP-0013-sbom-infra-expansion.md | +| task | CUST-WP-0013-T14 | done | — | workplans/CUST-WP-0013-sbom-infra-expansion.md | +| task | CUST-WP-0015-T01 | done | — | workplans/CUST-WP-0015-agent-inbox.md | +| task | CUST-WP-0015-T02 | done | — | workplans/CUST-WP-0015-agent-inbox.md | +| task | CUST-WP-0015-T03 | done | — | workplans/CUST-WP-0015-agent-inbox.md | +| task | CUST-WP-0015-T04 | done | — | workplans/CUST-WP-0015-agent-inbox.md | +| task | CUST-WP-0015-T05 | done | — | workplans/CUST-WP-0015-agent-inbox.md | +| task | CUST-WP-0016-T01 | done | — | workplans/CUST-WP-0016-kaizen-agentic-integration.md | +| task | CUST-WP-0016-T02 | done | — | workplans/CUST-WP-0016-kaizen-agentic-integration.md | +| task | CUST-WP-0016-T03 | done | — | workplans/CUST-WP-0016-kaizen-agentic-integration.md | +| task | CUST-WP-0016-T04 | done | — | workplans/CUST-WP-0016-kaizen-agentic-integration.md | +| task | CUST-WP-0016-T05 | done | — | workplans/CUST-WP-0016-kaizen-agentic-integration.md | +| task | CUST-WP-0017-T01 | done | — | workplans/CUST-WP-0017-scope-md-agent-and-coverage.md | +| task | CUST-WP-0017-T02 | done | — | workplans/CUST-WP-0017-scope-md-agent-and-coverage.md | +| task | CUST-WP-0017-T03 | done | — | workplans/CUST-WP-0017-scope-md-agent-and-coverage.md | +| task | CUST-WP-0017-T04 | done | — | workplans/CUST-WP-0017-scope-md-agent-and-coverage.md | +| task | CUST-WP-0017-T05 | done | — | workplans/CUST-WP-0017-scope-md-agent-and-coverage.md | +| task | CUST-WP-0017-T06 | done | — | workplans/CUST-WP-0017-scope-md-agent-and-coverage.md | +| task | CUST-WP-0018-T01 | done | — | workplans/CUST-WP-0018-api-hardening-code-quality.md | +| task | CUST-WP-0018-T02 | done | — | workplans/CUST-WP-0018-api-hardening-code-quality.md | +| task | CUST-WP-0018-T03 | done | — | workplans/CUST-WP-0018-api-hardening-code-quality.md | +| task | CUST-WP-0018-T04 | done | — | workplans/CUST-WP-0018-api-hardening-code-quality.md | +| task | CUST-WP-0018-T05 | done | — | workplans/CUST-WP-0018-api-hardening-code-quality.md | +| task | CUST-WP-0018-T06 | done | — | workplans/CUST-WP-0018-api-hardening-code-quality.md | +| task | CUST-WP-0018-T07 | done | — | workplans/CUST-WP-0018-api-hardening-code-quality.md | +| task | CUST-WP-0019-T01 | done | — | workplans/CUST-WP-0019-dashboard-ux-polish.md | +| task | CUST-WP-0019-T02 | done | — | workplans/CUST-WP-0019-dashboard-ux-polish.md | +| task | CUST-WP-0019-T03 | done | — | workplans/CUST-WP-0019-dashboard-ux-polish.md | +| task | CUST-WP-0020-T01 | done | — | workplans/CUST-WP-0020-test-suite.md | +| task | CUST-WP-0020-T02 | done | — | workplans/CUST-WP-0020-test-suite.md | +| task | CUST-WP-0020-T03 | done | — | workplans/CUST-WP-0020-test-suite.md | +| task | CUST-WP-0020-T04 | done | — | workplans/CUST-WP-0020-test-suite.md | +| task | CUST-WP-0020-T05 | done | — | workplans/CUST-WP-0020-test-suite.md | +| task | CUST-WP-0021-T01 | done | — | workplans/CUST-WP-0021-multi-host-repo-paths.md | +| task | CUST-WP-0021-T02 | done | — | workplans/CUST-WP-0021-multi-host-repo-paths.md | +| task | CUST-WP-0021-T03 | done | — | workplans/CUST-WP-0021-multi-host-repo-paths.md | +| task | CUST-WP-0021-T04 | done | — | workplans/CUST-WP-0021-multi-host-repo-paths.md | +| task | CUST-WP-0021-T05 | done | — | workplans/CUST-WP-0021-multi-host-repo-paths.md | +| task | CUST-WP-0022-T01 | done | — | workplans/CUST-WP-0022-capability-requests.md | +| task | CUST-WP-0022-T02 | done | — | workplans/CUST-WP-0022-capability-requests.md | +| task | CUST-WP-0022-T03 | done | — | workplans/CUST-WP-0022-capability-requests.md | +| task | CUST-WP-0022-T04 | done | — | workplans/CUST-WP-0022-capability-requests.md | +| task | CUST-WP-0022-T05 | done | — | workplans/CUST-WP-0022-capability-requests.md | +| task | CUST-WP-0022-T06 | done | — | workplans/CUST-WP-0022-capability-requests.md | +| task | CUST-WP-0022-T07 | done | — | workplans/CUST-WP-0022-capability-requests.md | +| task | CUST-WP-0022-T08 | done | — | workplans/CUST-WP-0022-capability-requests.md | +| task | CUST-WP-0022-T09 | done | — | workplans/CUST-WP-0022-capability-requests.md | +| task | CUST-WP-0022-T10 | done | — | workplans/CUST-WP-0022-capability-requests.md | +| task | CUST-WP-0023-T01 | done | — | workplans/CUST-WP-0023-tpsc.md | +| task | CUST-WP-0023-T02 | done | — | workplans/CUST-WP-0023-tpsc.md | +| task | CUST-WP-0023-T03 | done | — | workplans/CUST-WP-0023-tpsc.md | +| task | CUST-WP-0023-T04 | done | — | workplans/CUST-WP-0023-tpsc.md | +| task | CUST-WP-0023-T05 | done | — | workplans/CUST-WP-0023-tpsc.md | +| task | CUST-WP-0023-T06 | done | — | workplans/CUST-WP-0023-tpsc.md | +| task | CUST-WP-0023-T07 | done | — | workplans/CUST-WP-0023-tpsc.md | +| task | CUST-WP-0023-T08 | done | — | workplans/CUST-WP-0023-tpsc.md | +| task | CUST-WP-0023-T09 | done | — | workplans/CUST-WP-0023-tpsc.md | +| task | CUST-WP-0023-T10 | done | — | workplans/CUST-WP-0023-tpsc.md | +| task | CUST-WP-0024-T01 | done | — | workplans/CUST-WP-0024-repo-doi-gate.md | +| task | CUST-WP-0024-T02 | done | — | workplans/CUST-WP-0024-repo-doi-gate.md | +| task | CUST-WP-0024-T03 | done | — | workplans/CUST-WP-0024-repo-doi-gate.md | +| task | CUST-WP-0024-T04 | done | — | workplans/CUST-WP-0024-repo-doi-gate.md | +| task | CUST-WP-0024-T05 | done | — | workplans/CUST-WP-0024-repo-doi-gate.md | +| task | CUST-WP-0026-T01 | done | — | workplans/CUST-WP-0026-distributed-consistency.md | +| task | CUST-WP-0026-T02 | done | — | workplans/CUST-WP-0026-distributed-consistency.md | +| task | CUST-WP-0026-T03 | done | — | workplans/CUST-WP-0026-distributed-consistency.md | +| task | CUST-WP-0026-T04 | done | — | workplans/CUST-WP-0026-distributed-consistency.md | +| task | CUST-WP-0026-T05 | done | — | workplans/CUST-WP-0026-distributed-consistency.md | +| task | CUST-WP-0027-T01 | done | — | workplans/CUST-WP-0027-capability-request-dispute.md | +| task | CUST-WP-0027-T02 | done | — | workplans/CUST-WP-0027-capability-request-dispute.md | +| task | CUST-WP-0027-T03 | done | — | workplans/CUST-WP-0027-capability-request-dispute.md | +| task | CUST-WP-0027-T04 | done | — | workplans/CUST-WP-0027-capability-request-dispute.md | +| task | CUST-WP-0027-T05 | done | — | workplans/CUST-WP-0027-capability-request-dispute.md | +| task | CUST-WP-0029-T01 | done | — | workplans/CUST-WP-0029-token-consumption-tracking.md | +| task | CUST-WP-0029-T02 | done | — | workplans/CUST-WP-0029-token-consumption-tracking.md | +| task | CUST-WP-0029-T03 | done | — | workplans/CUST-WP-0029-token-consumption-tracking.md | +| task | CUST-WP-0029-T04 | done | — | workplans/CUST-WP-0029-token-consumption-tracking.md | +| task | CUST-WP-0029-T05 | done | — | workplans/CUST-WP-0029-token-consumption-tracking.md | +| task | CUST-WP-0029-T06 | done | — | workplans/CUST-WP-0029-token-consumption-tracking.md | +| task | CUST-WP-0029-T07 | done | — | workplans/CUST-WP-0029-token-consumption-tracking.md | +| task | CUST-WP-0029-T08 | done | — | workplans/CUST-WP-0029-token-consumption-tracking.md | +| task | T01 | done | — | workplans/CUST-WP-0030-dashboard-entity-list-ux.md | +| task | T02 | done | — | workplans/CUST-WP-0030-dashboard-entity-list-ux.md | +| task | T03 | done | — | workplans/CUST-WP-0030-dashboard-entity-list-ux.md | +| task | T04 | done | — | workplans/CUST-WP-0030-dashboard-entity-list-ux.md | +| task | T05 | done | — | workplans/CUST-WP-0030-dashboard-entity-list-ux.md | +| task | T06 | done | — | workplans/CUST-WP-0030-dashboard-entity-list-ux.md | +| task | T07 | done | — | workplans/CUST-WP-0030-dashboard-entity-list-ux.md | +| task | T08 | done | — | workplans/CUST-WP-0030-dashboard-entity-list-ux.md | +| task | T09 | done | — | workplans/CUST-WP-0030-dashboard-entity-list-ux.md | +| task | T01 | done | — | workplans/CUST-WP-0031-capability-registry.md | +| task | T02 | done | — | workplans/CUST-WP-0031-capability-registry.md | +| task | T03 | done | — | workplans/CUST-WP-0031-capability-registry.md | +| task | T04 | done | — | workplans/CUST-WP-0031-capability-registry.md | +| task | T05 | done | — | workplans/CUST-WP-0031-capability-registry.md | +| task | T06 | done | — | workplans/CUST-WP-0031-capability-registry.md | +| task | T07 | done | — | workplans/CUST-WP-0031-capability-registry.md | +| task | CUST-WP-0032-T01 | done | — | workplans/CUST-WP-0032-haskell-build-machine.md | +| task | CUST-WP-0032-T02 | done | — | workplans/CUST-WP-0032-haskell-build-machine.md | +| task | CUST-WP-0032-T03 | done | — | workplans/CUST-WP-0032-haskell-build-machine.md | +| task | CUST-WP-0032-T04 | done | — | workplans/CUST-WP-0032-haskell-build-machine.md | +| task | CUST-WP-0032-T05 | done | — | workplans/CUST-WP-0032-haskell-build-machine.md | +| task | CUST-WP-0032-T06 | done | — | workplans/CUST-WP-0032-haskell-build-machine.md | +| task | CUST-WP-0032-T07 | done | — | workplans/CUST-WP-0032-haskell-build-machine.md | +| task | CUST-WP-0032-T08 | done | — | workplans/CUST-WP-0032-haskell-build-machine.md | +| task | CUST-WP-0032-T09 | done | — | workplans/CUST-WP-0032-haskell-build-machine.md | +| task | CUST-WP-0032-T10 | done | — | workplans/CUST-WP-0032-haskell-build-machine.md | +| task | CUST-WP-0032-T11 | done | — | workplans/CUST-WP-0032-haskell-build-machine.md | +| task | CUST-WP-0032-T12 | done | — | workplans/CUST-WP-0032-haskell-build-machine.md | +| task | CUST-WP-0032-T13 | done | — | workplans/CUST-WP-0032-haskell-build-machine.md | +| task | CUST-WP-0032-T14 | done | — | workplans/CUST-WP-0032-haskell-build-machine.md | +| task | CUST-WP-0032-T15 | done | — | workplans/CUST-WP-0032-haskell-build-machine.md | +| task | CUST-WP-0033-T01 | done | — | workplans/CUST-WP-0033-interface-change-registry.md | +| task | CUST-WP-0033-T02 | done | — | workplans/CUST-WP-0033-interface-change-registry.md | +| task | CUST-WP-0033-T03 | done | — | workplans/CUST-WP-0033-interface-change-registry.md | +| task | CUST-WP-0033-T04 | done | — | workplans/CUST-WP-0033-interface-change-registry.md | +| task | CUST-WP-0033-T05 | done | — | workplans/CUST-WP-0033-interface-change-registry.md | +| task | CUST-WP-0033-T06 | done | — | workplans/CUST-WP-0033-interface-change-registry.md | +| task | CUST-WP-0039-T1 | done | — | workplans/CUST-WP-0039-dashboard-poll-optimization.md | +| task | CUST-WP-0039-T2 | done | — | workplans/CUST-WP-0039-dashboard-poll-optimization.md | +| task | CUST-WP-0039-T3 | done | — | workplans/CUST-WP-0039-dashboard-poll-optimization.md | +| task | CUST-WP-0039-T4 | done | — | workplans/CUST-WP-0039-dashboard-poll-optimization.md | +| task | CUST-WP-0039-T5 | done | — | workplans/CUST-WP-0039-dashboard-poll-optimization.md | +| task | CUST-WP-0039-T6 | done | — | workplans/CUST-WP-0039-dashboard-poll-optimization.md | +| task | T01 | done | — | workplans/CUST-WP-0040-state-hub-nats-activity-core-integration.md | +| task | T02 | done | — | workplans/CUST-WP-0040-state-hub-nats-activity-core-integration.md | +| task | T03 | done | — | workplans/CUST-WP-0040-state-hub-nats-activity-core-integration.md | +| task | T04 | done | — | workplans/CUST-WP-0040-state-hub-nats-activity-core-integration.md | +| task | T05 | done | — | workplans/CUST-WP-0040-state-hub-nats-activity-core-integration.md | +| task | T06 | done | — | workplans/CUST-WP-0040-state-hub-nats-activity-core-integration.md | +| task | CUST-WP-0041-T1 | done | — | workplans/CUST-WP-0041-api-performance.md | +| task | CUST-WP-0041-T2 | done | — | workplans/CUST-WP-0041-api-performance.md | +| task | CUST-WP-0041-T3 | done | — | workplans/CUST-WP-0041-api-performance.md | +| task | CUST-WP-0041-T4 | done | — | workplans/CUST-WP-0041-api-performance.md | +| task | CUST-WP-0043-T01 | done | — | workplans/CUST-WP-0043-state-hub-repo-extraction.md | +| task | CUST-WP-0043-T02 | done | — | workplans/CUST-WP-0043-state-hub-repo-extraction.md | +| task | CUST-WP-0043-T03 | done | — | workplans/CUST-WP-0043-state-hub-repo-extraction.md | +| task | CUST-WP-0043-T04 | done | — | workplans/CUST-WP-0043-state-hub-repo-extraction.md | +| task | CUST-WP-0043-T05 | done | — | workplans/CUST-WP-0043-state-hub-repo-extraction.md | +| task | CUST-WP-0043-T06 | done | — | workplans/CUST-WP-0043-state-hub-repo-extraction.md | +| task | CUST-WP-0043-T07 | done | — | workplans/CUST-WP-0043-state-hub-repo-extraction.md | +| task | CUST-WP-0043-T08 | done | — | workplans/CUST-WP-0043-state-hub-repo-extraction.md | +| task | CUST-WP-0043-T09 | done | — | workplans/CUST-WP-0043-state-hub-repo-extraction.md | +| task | CUST-WP-0044-T01 | done | — | workplans/CUST-WP-0044-daily-statehub-wsjf-triage.md | +| task | CUST-WP-0044-T02 | done | — | workplans/CUST-WP-0044-daily-statehub-wsjf-triage.md | +| task | CUST-WP-0044-T03 | done | — | workplans/CUST-WP-0044-daily-statehub-wsjf-triage.md | +| task | CUST-WP-0044-T04 | done | — | workplans/CUST-WP-0044-daily-statehub-wsjf-triage.md | +| task | CUST-WP-0044-T05 | done | — | workplans/CUST-WP-0044-daily-statehub-wsjf-triage.md | +| task | CUST-WP-0044-T06 | done | — | workplans/CUST-WP-0044-daily-statehub-wsjf-triage.md | +| task | CUST-WP-0045-T01 | done | — | workplans/CUST-WP-0045-activity-core-daily-triage-runner.md | +| task | CUST-WP-0045-T02 | done | — | workplans/CUST-WP-0045-activity-core-daily-triage-runner.md | +| task | CUST-WP-0045-T03 | done | — | workplans/CUST-WP-0045-activity-core-daily-triage-runner.md | +| task | CUST-WP-0045-T04 | done | — | workplans/CUST-WP-0045-activity-core-daily-triage-runner.md | +| task | CUST-WP-0045-T05 | done | — | workplans/CUST-WP-0045-activity-core-daily-triage-runner.md | +| task | CUST-WP-0045-T06 | done | — | workplans/CUST-WP-0045-activity-core-daily-triage-runner.md | +| task | CUST-WP-0045-T07 | done | — | workplans/CUST-WP-0045-activity-core-daily-triage-runner.md | +| task | CUST-WP-0045-T08 | done | — | workplans/CUST-WP-0045-activity-core-daily-triage-runner.md | +| task | CUST-WP-0046-T01 | done | — | workplans/CUST-WP-0046-hourly-recently-on-scope-activity-core.md | +| task | CUST-WP-0046-T02 | done | — | workplans/CUST-WP-0046-hourly-recently-on-scope-activity-core.md | +| task | CUST-WP-0046-T03 | done | — | workplans/CUST-WP-0046-hourly-recently-on-scope-activity-core.md | +| task | CUST-WP-0046-T04 | done | — | workplans/CUST-WP-0046-hourly-recently-on-scope-activity-core.md | +| task | CUST-WP-0046-T05 | done | — | workplans/CUST-WP-0046-hourly-recently-on-scope-activity-core.md | +| task | CUST-WP-0046-T06 | done | — | workplans/CUST-WP-0046-hourly-recently-on-scope-activity-core.md | +| task | CUST-WP-0046-T07 | done | — | workplans/CUST-WP-0046-hourly-recently-on-scope-activity-core.md | +| task | CUST-WP-0047-T01 | done | — | workplans/CUST-WP-0047-ops-hub-service-inventory-now-view.md | +| task | CUST-WP-0047-T02 | done | — | workplans/CUST-WP-0047-ops-hub-service-inventory-now-view.md | +| task | CUST-WP-0047-T03 | done | — | workplans/CUST-WP-0047-ops-hub-service-inventory-now-view.md | +| task | CUST-WP-0047-T04 | done | — | workplans/CUST-WP-0047-ops-hub-service-inventory-now-view.md | +| task | CUST-WP-0047-T05 | cancel | — | workplans/CUST-WP-0047-ops-hub-service-inventory-now-view.md | +| task | CUST-WP-0047-T06 | done | — | workplans/CUST-WP-0047-ops-hub-service-inventory-now-view.md | +| task | CUST-WP-0047-T07 | done | — | workplans/CUST-WP-0047-ops-hub-service-inventory-now-view.md | +| task | CUST-WP-0048-T01 | done | — | workplans/CUST-WP-0048-hub-core-state-hub-import-refactor.md | +| task | CUST-WP-0048-T02 | done | — | workplans/CUST-WP-0048-hub-core-state-hub-import-refactor.md | +| task | CUST-WP-0048-T03 | done | — | workplans/CUST-WP-0048-hub-core-state-hub-import-refactor.md | +| task | CUST-WP-0048-T04 | done | — | workplans/CUST-WP-0048-hub-core-state-hub-import-refactor.md | +| task | CUST-WP-0048-T05 | done | — | workplans/CUST-WP-0048-hub-core-state-hub-import-refactor.md | +| task | CUST-WP-0048-T06 | done | — | workplans/CUST-WP-0048-hub-core-state-hub-import-refactor.md | +| task | CUST-WP-0048-T07 | done | — | workplans/CUST-WP-0048-hub-core-state-hub-import-refactor.md | +| task | CUST-WP-0048-T08 | done | — | workplans/CUST-WP-0048-hub-core-state-hub-import-refactor.md | +| task | CUST-WP-0049-T01 | done | — | workplans/CUST-WP-0049-interhub-bootstrap-access-lane.md | +| task | CUST-WP-0049-T02 | done | — | workplans/CUST-WP-0049-interhub-bootstrap-access-lane.md | +| task | CUST-WP-0049-T03 | done | — | workplans/CUST-WP-0049-interhub-bootstrap-access-lane.md | +| task | CUST-WP-0049-T04 | done | — | workplans/CUST-WP-0049-interhub-bootstrap-access-lane.md | +| task | CUST-WP-0049-T05 | done | — | workplans/CUST-WP-0049-interhub-bootstrap-access-lane.md | +| task | CUST-WP-0049-T06 | cancel | — | workplans/CUST-WP-0049-interhub-bootstrap-access-lane.md | +| task | CUST-WP-0050-T01 | done | — | workplans/CUST-WP-0050-repo-classification-registration-redesign.md | +| task | CUST-WP-0050-T02 | done | — | workplans/CUST-WP-0050-repo-classification-registration-redesign.md | +| task | CUST-WP-0050-T03 | cancel | — | workplans/CUST-WP-0050-repo-classification-registration-redesign.md | +| task | CUST-WP-0050-T04 | cancel | — | workplans/CUST-WP-0050-repo-classification-registration-redesign.md | +| task | CUST-WP-0050-T05 | cancel | — | workplans/CUST-WP-0050-repo-classification-registration-redesign.md | +| task | CUST-WP-0050-T06 | cancel | — | workplans/CUST-WP-0050-repo-classification-registration-redesign.md | +| task | CUST-WP-0050-T07 | cancel | — | workplans/CUST-WP-0050-repo-classification-registration-redesign.md | +| task | CUST-WP-0050-T08 | cancel | — | workplans/CUST-WP-0050-repo-classification-registration-redesign.md | +| task | CUST-WP-0050-T09 | cancel | — | workplans/CUST-WP-0050-repo-classification-registration-redesign.md | +| task | CUST-WP-0050-T10 | cancel | — | workplans/CUST-WP-0050-repo-classification-registration-redesign.md | +| task | CUST-WP-0050-T11 | done | — | workplans/CUST-WP-0050-repo-classification-registration-redesign.md | +| task | CUST-WP-0051-T01 | done | — | workplans/CUST-WP-0051-infrastructure-stabilization-metaplan.md | +| task | CUST-WP-0051-T02 | done | — | workplans/CUST-WP-0051-infrastructure-stabilization-metaplan.md | +| task | CUST-WP-0051-T03 | done | — | workplans/CUST-WP-0051-infrastructure-stabilization-metaplan.md | +| task | CUST-WP-0051-T04 | done | — | workplans/CUST-WP-0051-infrastructure-stabilization-metaplan.md | +| task | CUST-WP-0051-T05 | done | — | workplans/CUST-WP-0051-infrastructure-stabilization-metaplan.md | +| task | CUST-WP-0051-T06 | done | — | workplans/CUST-WP-0051-infrastructure-stabilization-metaplan.md | +| task | CUST-WP-0051-T07 | done | — | workplans/CUST-WP-0051-infrastructure-stabilization-metaplan.md | +| task | CUST-WP-0051-T08 | done | — | workplans/CUST-WP-0051-infrastructure-stabilization-metaplan.md | +| task | CUST-WP-0052-T01 | done | — | workplans/CUST-WP-0052-core-hub-fos-reset.md | +| task | CUST-WP-0052-T02 | done | — | workplans/CUST-WP-0052-core-hub-fos-reset.md | +| task | CUST-WP-0052-T03 | done | — | workplans/CUST-WP-0052-core-hub-fos-reset.md | +| task | CUST-WP-0052-T04 | done | — | workplans/CUST-WP-0052-core-hub-fos-reset.md | +| task | CUST-WP-0052-T05 | done | — | workplans/CUST-WP-0052-core-hub-fos-reset.md | +| task | CUST-WP-0052-T06 | done | — | workplans/CUST-WP-0052-core-hub-fos-reset.md | +| task | CUST-WP-0052-T07 | done | — | workplans/CUST-WP-0052-core-hub-fos-reset.md | +| task | CUST-WP-0053-T01 | done | — | workplans/CUST-WP-0053-coordination-hygiene-improvements.md | +| task | CUST-WP-0053-T02 | done | — | workplans/CUST-WP-0053-coordination-hygiene-improvements.md | +| task | CUST-WP-0053-T03 | done | — | workplans/CUST-WP-0053-coordination-hygiene-improvements.md | +| task | CUST-WP-0053-T04 | done | — | workplans/CUST-WP-0053-coordination-hygiene-improvements.md | +| task | CUST-WP-0053-T05 | done | — | workplans/CUST-WP-0053-coordination-hygiene-improvements.md | +| task | CUST-WP-0055-T01 | done | — | workplans/CUST-WP-0055-workplan-terminology-fleet-refactor.md | +| task | CUST-WP-0055-T02 | done | — | workplans/CUST-WP-0055-workplan-terminology-fleet-refactor.md | +| task | CUST-WP-0055-T03 | done | — | workplans/CUST-WP-0055-workplan-terminology-fleet-refactor.md | +| task | CUST-WP-0055-T04 | done | — | workplans/CUST-WP-0055-workplan-terminology-fleet-refactor.md | +| task | CUST-WP-0055-T05 | done | — | workplans/CUST-WP-0055-workplan-terminology-fleet-refactor.md | +| task | CUST-WP-0055-T06 | done | — | workplans/CUST-WP-0055-workplan-terminology-fleet-refactor.md | +| task | CUST-WP-0055-T07 | done | — | workplans/CUST-WP-0055-workplan-terminology-fleet-refactor.md | +| task | CUST-WP-0055-T08 | done | — | workplans/CUST-WP-0055-workplan-terminology-fleet-refactor.md | +| task | CUST-WP-0056-T01 | done | — | workplans/CUST-WP-0056-daily-todo-md-stale-review.md | +| task | CUST-WP-0056-T02 | done | — | workplans/CUST-WP-0056-daily-todo-md-stale-review.md | +| task | CUST-WP-0056-T03 | done | — | workplans/CUST-WP-0056-daily-todo-md-stale-review.md | +| task | CUST-WP-0059-T01 | done | — | workplans/CUST-WP-0059-forgejo-ci-publish-cutover-tail.md | +| task | CUST-WP-0059-T02 | done | — | workplans/CUST-WP-0059-forgejo-ci-publish-cutover-tail.md | +| task | CUST-WP-0059-T03 | done | — | workplans/CUST-WP-0059-forgejo-ci-publish-cutover-tail.md | +| task | CUST-WP-0059-T04 | done | — | workplans/CUST-WP-0059-forgejo-ci-publish-cutover-tail.md | +| task | CUST-WP-0059-T05 | done | — | workplans/CUST-WP-0059-forgejo-ci-publish-cutover-tail.md | +| task | CUST-WP-0059-T06 | done | — | workplans/CUST-WP-0059-forgejo-ci-publish-cutover-tail.md | +| task | CUST-WP-0060-T01 | done | — | workplans/CUST-WP-0060-work-record-canonization.md | +| task | CUST-WP-0060-T02 | done | — | workplans/CUST-WP-0060-work-record-canonization.md | +| task | CUST-WP-0060-T03 | done | — | workplans/CUST-WP-0060-work-record-canonization.md | +| task | CUST-WP-0060-T04 | done | — | workplans/CUST-WP-0060-work-record-canonization.md | +| task | CUST-WP-0060-T05 | done | — | workplans/CUST-WP-0060-work-record-canonization.md | +| task | CUST-WP-0060-T06 | done | — | workplans/CUST-WP-0060-work-record-canonization.md | +| task | CUST-WP-0060-T07 | done | — | workplans/CUST-WP-0060-work-record-canonization.md | +| task | CUST-WP-0061-T01 | done | — | workplans/CUST-WP-0061-work-record-stage3-intake-promotion-index.md | +| task | CUST-WP-0061-T02 | done | — | workplans/CUST-WP-0061-work-record-stage3-intake-promotion-index.md | +| task | CUST-WP-0061-T03 | done | — | workplans/CUST-WP-0061-work-record-stage3-intake-promotion-index.md | +| task | CUST-WP-0061-T04 | done | — | workplans/CUST-WP-0061-work-record-stage3-intake-promotion-index.md | +| task | CUST-WP-0061-T05 | done | — | workplans/CUST-WP-0061-work-record-stage3-intake-promotion-index.md | +| task | CUST-WP-0061-T06 | done | — | workplans/CUST-WP-0061-work-record-stage3-intake-promotion-index.md | +| task | CUST-WP-0062-T01 | done | — | workplans/CUST-WP-0062-sbom-nexus-daily-catchup.md | +| task | CUST-WP-0062-T02 | done | — | workplans/CUST-WP-0062-sbom-nexus-daily-catchup.md | +| task | CUST-WP-0062-T03 | done | — | workplans/CUST-WP-0062-sbom-nexus-daily-catchup.md | +| task | CUST-WP-0062-T04 | done | — | workplans/CUST-WP-0062-sbom-nexus-daily-catchup.md | +| task | CUST-WP-0062-T05 | done | — | workplans/CUST-WP-0062-sbom-nexus-daily-catchup.md | +| task | CUST-WP-0062-T06 | done | — | workplans/CUST-WP-0062-sbom-nexus-daily-catchup.md | +| task | CUST-WP-0063-T01 | done | — | workplans/CUST-WP-0063-inbox-governance-packets.md | +| task | CUST-WP-0063-T02 | done | — | workplans/CUST-WP-0063-inbox-governance-packets.md | +| task | CUST-WP-0063-T03 | done | — | workplans/CUST-WP-0063-inbox-governance-packets.md | +| task | CUST-WP-0063-T04 | done | — | workplans/CUST-WP-0063-inbox-governance-packets.md | +| task | CUST-WP-0063-T05 | done | — | workplans/CUST-WP-0063-inbox-governance-packets.md | +| task | CUST-WP-0064-T01 | done | — | workplans/CUST-WP-0064-sbom-controlled-scan-inputs.md | +| task | CUST-WP-0064-T02 | done | — | workplans/CUST-WP-0064-sbom-controlled-scan-inputs.md | +| task | CUST-WP-0064-T03 | done | — | workplans/CUST-WP-0064-sbom-controlled-scan-inputs.md | +| task | CUST-WP-0064-T04 | done | — | workplans/CUST-WP-0064-sbom-controlled-scan-inputs.md | +| task | CUST-WP-0065-T01 | done | — | workplans/CUST-WP-0065-reclassify-repos-and-guidance-docs-to-the-new-sector-domain.md | +| task | CUST-WP-0065-T02 | done | — | workplans/CUST-WP-0065-reclassify-repos-and-guidance-docs-to-the-new-sector-domain.md | +| task | CUST-WP-0065-T03 | done | — | workplans/CUST-WP-0065-reclassify-repos-and-guidance-docs-to-the-new-sector-domain.md | +| task | CUST-WP-0065-T04 | done | — | workplans/CUST-WP-0065-reclassify-repos-and-guidance-docs-to-the-new-sector-domain.md | +| task | CUST-WP-0066-T01 | done | — | workplans/CUST-WP-0066-repository-qualified-ad-hoc-identifiers.md | +| task | CUST-WP-0066-T02 | done | — | workplans/CUST-WP-0066-repository-qualified-ad-hoc-identifiers.md | +| task | CUST-WP-0066-T03 | done | — | workplans/CUST-WP-0066-repository-qualified-ad-hoc-identifiers.md | +| task | CUST-WP-0067-T01 | done | — | workplans/CUST-WP-0067-hub-authority-target-resolution.md | +| task | CUST-WP-0067-T02 | done | — | workplans/CUST-WP-0067-hub-authority-target-resolution.md | +| task | CUST-WP-0067-T03 | done | — | workplans/CUST-WP-0067-hub-authority-target-resolution.md | +| task | CUST-WP-0067-T04 | done | — | workplans/CUST-WP-0067-hub-authority-target-resolution.md | +| task | CUST-WP-0067-T05 | done | — | workplans/CUST-WP-0067-hub-authority-target-resolution.md | +| task | CUST-WP-0067-T06 | done | — | workplans/CUST-WP-0067-hub-authority-target-resolution.md | +| task | CUST-WP-0067-T07 | done | — | workplans/CUST-WP-0067-hub-authority-target-resolution.md | +| task | CUST-WP-0067-T08 | done | — | workplans/CUST-WP-0067-hub-authority-target-resolution.md | +| task | CUST-WP-0067-T09 | done | — | workplans/CUST-WP-0067-hub-authority-target-resolution.md | +| task | CUST-WP-0067-T10 | done | — | workplans/CUST-WP-0067-hub-authority-target-resolution.md | +| task | CUST-WP-0068-T01 | done | — | workplans/CUST-WP-0068-cache-only-work-record-recovery.md | +| task | CUST-WP-0068-T02 | done | — | workplans/CUST-WP-0068-cache-only-work-record-recovery.md | +| task | CUST-WP-0068-T03 | done | — | workplans/CUST-WP-0068-cache-only-work-record-recovery.md | +| task | CUST-WP-0068-T04 | done | — | workplans/CUST-WP-0068-cache-only-work-record-recovery.md | +| task | CUST-WP-0068-T05 | done | — | workplans/CUST-WP-0068-cache-only-work-record-recovery.md | +| task | CUST-WP-0068-T06 | done | — | workplans/CUST-WP-0068-cache-only-work-record-recovery.md | +| task | CUST-WP-0068-T07 | done | — | workplans/CUST-WP-0068-cache-only-work-record-recovery.md | +| task | CUST-WP-0068-T08 | done | — | workplans/CUST-WP-0068-cache-only-work-record-recovery.md | +| task | CUST-WP-0068-T09 | done | — | workplans/CUST-WP-0068-cache-only-work-record-recovery.md | +| task | CUST-WP-0070-T01 | done | — | workplans/CUST-WP-0070-publication-repo-category.md | +| task | CUST-WP-0070-T02 | done | — | workplans/CUST-WP-0070-publication-repo-category.md | +| task | CUST-WP-0070-T03 | done | — | workplans/CUST-WP-0070-publication-repo-category.md | +| task | CUST-WP-0071-T01 | done | — | workplans/CUST-WP-0071-measured-workload-sizing-and-weekly-review.md | +| task | CUST-WP-0071-T02 | wait | — | workplans/CUST-WP-0071-measured-workload-sizing-and-weekly-review.md | +| task | CUST-WP-0071-T03 | wait | — | workplans/CUST-WP-0071-measured-workload-sizing-and-weekly-review.md | +| task | CUST-WP-0071-T04 | wait | — | workplans/CUST-WP-0071-measured-workload-sizing-and-weekly-review.md | +| task | CUST-WP-0071-T05 | wait | — | workplans/CUST-WP-0071-measured-workload-sizing-and-weekly-review.md | +| task | CUST-WP-0072-T01 | done | — | workplans/CUST-WP-0072-fleet-flavor-and-depends-on-backfill.md | +| task | CUST-WP-0072-T02 | done | — | workplans/CUST-WP-0072-fleet-flavor-and-depends-on-backfill.md | +| task | CUST-WP-0072-T03 | done | — | workplans/CUST-WP-0072-fleet-flavor-and-depends-on-backfill.md | +| task | CUST-WP-0072-T04 | done | — | workplans/CUST-WP-0072-fleet-flavor-and-depends-on-backfill.md | +| task | CUST-WP-0073-T01 | done | — | workplans/CUST-WP-0073-agent-credential-separation.md | +| task | CUST-WP-0073-T02 | wait | — | workplans/CUST-WP-0073-agent-credential-separation.md | +| task | CUST-WP-0073-T03 | done | — | workplans/CUST-WP-0073-agent-credential-separation.md | +| task | CUST-WP-0073-T04 | wait | — | workplans/CUST-WP-0073-agent-credential-separation.md | +| task | CUST-WP-0073-T05 | wait | — | workplans/CUST-WP-0073-agent-credential-separation.md | +| task | CUST-WP-0074-T01 | done | — | workplans/CUST-WP-0074-qualified-wait-states.md | +| task | CUST-WP-0074-T02 | done | — | workplans/CUST-WP-0074-qualified-wait-states.md | +| task | CUST-WP-0074-T03 | done | — | workplans/CUST-WP-0074-qualified-wait-states.md | +| task | CUST-WP-0074-T04 | done | — | workplans/CUST-WP-0074-qualified-wait-states.md | +| task | CUST-WP-0074-T05 | done | — | workplans/CUST-WP-0074-qualified-wait-states.md | +| task | CUST-WP-0074-T06 | done | — | workplans/CUST-WP-0074-qualified-wait-states.md | +| task | CUST-WP-0075-T01 | done | — | workplans/CUST-WP-0075-dependency-graph-driven-unblocking.md | +| task | CUST-WP-0075-T02 | done | — | workplans/CUST-WP-0075-dependency-graph-driven-unblocking.md | +| task | CUST-WP-0075-T03 | done | — | workplans/CUST-WP-0075-dependency-graph-driven-unblocking.md | +| task | CUST-WP-0075-T04 | done | — | workplans/CUST-WP-0075-dependency-graph-driven-unblocking.md | +| task | CUST-WP-0075-T05 | done | — | workplans/CUST-WP-0075-dependency-graph-driven-unblocking.md | +| task | CUST-WP-0075-T06 | done | — | workplans/CUST-WP-0075-dependency-graph-driven-unblocking.md | +| task | CUST-WP-0076-T01 | done | — | workplans/CUST-WP-0076-provisioning-window-cadence.md | +| task | CUST-WP-0077-T01 | done | — | workplans/CUST-WP-0077-dependency-root-execution-wave.md | +| task | CUST-WP-0077-T02 | done | — | workplans/CUST-WP-0077-dependency-root-execution-wave.md | +| task | CUST-WP-0077-T03 | done | — | workplans/CUST-WP-0077-dependency-root-execution-wave.md | +| task | CUST-WP-0077-T04 | done | — | workplans/CUST-WP-0077-dependency-root-execution-wave.md | +| task | CUST-WP-0077-T05 | done | — | workplans/CUST-WP-0077-dependency-root-execution-wave.md | +| task | CUST-WP-0078-T01 | done | — | workplans/CUST-WP-0078-security-collaboration-consolidation.md | +| task | CUST-WP-0078-T02 | done | — | workplans/CUST-WP-0078-security-collaboration-consolidation.md | +| task | CUST-WP-0078-T03 | done | — | workplans/CUST-WP-0078-security-collaboration-consolidation.md | +| task | CUST-WP-0078-T04 | done | — | workplans/CUST-WP-0078-security-collaboration-consolidation.md | +| task | CUST-WP-0079-T01 | done | — | workplans/CUST-WP-0079-deterministic-security-flow.md | +| task | CUST-WP-0079-T02 | done | — | workplans/CUST-WP-0079-deterministic-security-flow.md | +| task | CUST-WP-0079-T03 | wait | — | workplans/CUST-WP-0079-deterministic-security-flow.md | +| task | CUST-WP-0079-T04 | wait | — | workplans/CUST-WP-0079-deterministic-security-flow.md | +| task | CUST-WP-0079-T05 | wait | — | workplans/CUST-WP-0079-deterministic-security-flow.md | +| task | CUST-WP-0079-T06 | wait | — | workplans/CUST-WP-0079-deterministic-security-flow.md | +| task | CUST-WP-0079-T07 | done | — | workplans/CUST-WP-0079-deterministic-security-flow.md | +| task | CUST-WP-0080-T01 | done | — | workplans/CUST-WP-0080-netkingdom-security-blueprint-and-gap-closure.md | +| task | CUST-WP-0080-T02 | done | — | workplans/CUST-WP-0080-netkingdom-security-blueprint-and-gap-closure.md | +| task | CUST-WP-0080-T03 | progress | — | workplans/CUST-WP-0080-netkingdom-security-blueprint-and-gap-closure.md | +| task | CUST-WP-0080-T04 | todo | — | workplans/CUST-WP-0080-netkingdom-security-blueprint-and-gap-closure.md | +| task | CUST-WP-0080-T05 | progress | — | workplans/CUST-WP-0080-netkingdom-security-blueprint-and-gap-closure.md | +| task | CUST-WP-0080-T06 | progress | — | workplans/CUST-WP-0080-netkingdom-security-blueprint-and-gap-closure.md | +| task | CUST-WP-0081-T01 | done | — | workplans/CUST-WP-0081-netkingdom-architecture-review-and-reuse.md | +| task | CUST-WP-0081-T02 | done | — | workplans/CUST-WP-0081-netkingdom-architecture-review-and-reuse.md | +| task | CUST-WP-0081-T03 | done | — | workplans/CUST-WP-0081-netkingdom-architecture-review-and-reuse.md | +| task | THE-WP-0001-T01 | done | — | workplans/THE-WP-0001-federation-interface.md | +| task | THE-WP-0001-T02 | done | — | workplans/THE-WP-0001-federation-interface.md | +| intake | CUST-IN-0001 | closed | green | intake-legacy-suggestions-migration.md | +| intake | CUST-IN-0002 | closed | green | intake-legacy-suggestions-migration.md | +| intake | CUST-IN-0003 | closed | green | intake-legacy-suggestions-migration.md | +| intake | CUST-IN-0004 | routed | green | intake-legacy-suggestions-migration.md | +| intake | CUST-IN-0005 | closed | green | intake-legacy-suggestions-migration.md | +| intake | CUST-IN-0006 | closed | green | intake-legacy-suggestions-migration.md | +| intake | CUST-IN-0007 | closed | green | intake-legacy-suggestions-migration.md | +| intake | CUST-IN-0008 | closed | blue | intake-legacy-suggestions-migration.md | +| intake | CUST-IN-0009 | closed | blue | intake-legacy-suggestions-migration.md | +| intake | CUST-IN-0010 | closed | green | intake-legacy-suggestions-migration.md | +| intake | CUST-IN-0011 | routed | red | intakes.md | +| intake | CUST-IN-0012 | closed | green | intakes.md | +| intake | CUST-IN-0013 | closed | blue | intakes.md | +| intake | CUST-IN-0014 | closed | blue | intakes.md | +| intake | CUST-IN-0015 | open | blue | intakes.md | +| intake | CUST-IN-0016 | open | green | intakes.md | +| intake | CUST-IN-0017 | open | green | intakes.md | +| intake | CUST-IN-0018 | open | green | intakes.md | diff --git a/docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/protected-backup/docs/assessments/netkingdom-2026-10-14/hosted-before-federated.json b/docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/protected-backup/docs/assessments/netkingdom-2026-10-14/hosted-before-federated.json new file mode 100644 index 00000000..5c084b1e --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/protected-backup/docs/assessments/netkingdom-2026-10-14/hosted-before-federated.json @@ -0,0 +1,2154 @@ +{ + "version": 1, + "updated": "2026-10-14", + "domain": "helix_forge", + "collision_policy": "warn", + "sources": [ + { + "repo": "access-engine", + "count": 1, + "url": "https://forgejo.coulomb.social/coulomb/access-engine/raw/branch/main/registry/indexes/capabilities.yaml", + "cache": "/data/cache/access-engine.yaml" + }, + { + "repo": "activity-core", + "count": 1, + "url": "https://forgejo.coulomb.social/coulomb/activity-core/raw/branch/main/registry/indexes/capabilities.yaml", + "cache": "/data/cache/activity-core.yaml" + }, + { + "repo": "agentic-resources", + "count": 1, + "url": "https://forgejo.coulomb.social/coulomb/agentic-resources/raw/branch/main/registry/indexes/capabilities.yaml", + "cache": "/data/cache/agentic-resources.yaml" + }, + { + "repo": "artifact-store", + "count": 1, + "url": "https://forgejo.coulomb.social/coulomb/artifact-store/raw/branch/main/registry/indexes/capabilities.yaml", + "cache": "/data/cache/artifact-store.yaml" + }, + { + "repo": "audit-core", + "count": 1, + "url": "https://forgejo.coulomb.social/coulomb/audit-core/raw/branch/main/registry/indexes/capabilities.yaml", + "cache": "/data/cache/audit-core.yaml" + }, + { + "repo": "can-you-assist", + "count": 1, + "url": "https://forgejo.coulomb.social/coulomb/can-you-assist/raw/branch/main/registry/indexes/capabilities.yaml", + "cache": "/data/cache/can-you-assist.yaml" + }, + { + "repo": "citation-engine", + "count": 1, + "url": "https://forgejo.coulomb.social/coulomb/citation-engine/raw/branch/main/registry/indexes/capabilities.yaml", + "cache": "/data/cache/citation-engine.yaml" + }, + { + "repo": "citation-evidence", + "count": 1, + "url": "https://forgejo.coulomb.social/coulomb/citation-evidence/raw/branch/main/registry/indexes/capabilities.yaml", + "cache": "/data/cache/citation-evidence.yaml" + }, + { + "repo": "citation-work", + "count": 0, + "url": "https://forgejo.coulomb.social/coulomb/citation-work/raw/branch/main/registry/indexes/capabilities.yaml", + "cache": "/data/cache/citation-work.yaml" + }, + { + "repo": "commerce-canon", + "count": 2, + "url": "https://forgejo.coulomb.social/coulomb/commerce-canon/raw/branch/main/registry/indexes/capabilities.yaml", + "cache": "/data/cache/commerce-canon.yaml" + }, + { + "repo": "config-atlas", + "count": 1, + "url": "https://forgejo.coulomb.social/coulomb/config-atlas/raw/branch/main/registry/indexes/capabilities.yaml", + "cache": "/data/cache/config-atlas.yaml" + }, + { + "repo": "coordination-engine", + "count": 1, + "url": "https://forgejo.coulomb.social/coulomb/coordination-engine/raw/branch/main/registry/indexes/capabilities.yaml", + "cache": "/data/cache/coordination-engine.yaml" + }, + { + "repo": "core-hub", + "count": 1, + "url": "https://forgejo.coulomb.social/coulomb/core-hub/raw/branch/main/registry/indexes/capabilities.yaml", + "cache": "/data/cache/core-hub.yaml" + }, + { + "repo": "domain-tree", + "count": 0, + "url": "https://forgejo.coulomb.social/coulomb/domain-tree/raw/branch/main/registry/indexes/capabilities.yaml", + "cache": "/data/cache/domain-tree.yaml" + }, + { + "repo": "email-connect", + "count": 1, + "url": "https://forgejo.coulomb.social/coulomb/email-connect/raw/branch/main/registry/indexes/capabilities.yaml", + "cache": "/data/cache/email-connect.yaml" + }, + { + "repo": "evidence-anchor", + "count": 0, + "url": "https://forgejo.coulomb.social/coulomb/evidence-anchor/raw/branch/main/registry/indexes/capabilities.yaml", + "cache": "/data/cache/evidence-anchor.yaml" + }, + { + "repo": "evidence-binder", + "count": 2, + "url": "https://forgejo.coulomb.social/coulomb/evidence-binder/raw/branch/main/registry/indexes/capabilities.yaml", + "cache": "/data/cache/evidence-binder.yaml" + }, + { + "repo": "evidence-source", + "count": 1, + "url": "https://forgejo.coulomb.social/coulomb/evidence-source/raw/branch/main/registry/indexes/capabilities.yaml", + "cache": "/data/cache/evidence-source.yaml" + }, + { + "repo": "feature-control", + "count": 3, + "url": "https://forgejo.coulomb.social/coulomb/feature-control/raw/branch/main/registry/indexes/capabilities.yaml", + "cache": "/data/cache/feature-control.yaml" + }, + { + "repo": "guide-board", + "count": 1, + "url": "https://forgejo.coulomb.social/coulomb/guide-board/raw/branch/main/registry/indexes/capabilities.yaml", + "cache": "/data/cache/guide-board.yaml" + }, + { + "repo": "helix-forge", + "count": 0, + "url": "https://forgejo.coulomb.social/coulomb/helix-forge/raw/branch/main/registry/indexes/capabilities.yaml", + "cache": "/data/cache/helix-forge.yaml" + }, + { + "repo": "hub-core", + "count": 1, + "url": "https://forgejo.coulomb.social/coulomb/hub-core/raw/branch/main/registry/indexes/capabilities.yaml", + "cache": "/data/cache/hub-core.yaml" + }, + { + "repo": "human-resources", + "count": 1, + "url": "https://forgejo.coulomb.social/coulomb/human-resources/raw/branch/main/registry/indexes/capabilities.yaml", + "cache": "/data/cache/human-resources.yaml" + }, + { + "repo": "ihp-railiance-probe", + "count": 0, + "url": "https://forgejo.coulomb.social/coulomb/ihp-railiance-probe/raw/branch/main/registry/indexes/capabilities.yaml", + "cache": "/data/cache/ihp-railiance-probe.yaml" + }, + { + "repo": "info-tech-canon", + "count": 1, + "url": "https://forgejo.coulomb.social/coulomb/info-tech-canon/raw/branch/main/registry/indexes/capabilities.yaml", + "cache": "/data/cache/info-tech-canon.yaml" + }, + { + "repo": "infospace-bench", + "count": 1, + "url": "https://forgejo.coulomb.social/coulomb/infospace-bench/raw/branch/main/registry/indexes/capabilities.yaml", + "cache": "/data/cache/infospace-bench.yaml" + }, + { + "repo": "issue-core", + "count": 1, + "url": "https://forgejo.coulomb.social/coulomb/issue-core/raw/branch/main/registry/indexes/capabilities.yaml", + "cache": "/data/cache/issue-core.yaml" + }, + { + "repo": "kaizen-agentic", + "count": 1, + "url": "https://forgejo.coulomb.social/coulomb/kaizen-agentic/raw/branch/main/registry/indexes/capabilities.yaml", + "cache": "/data/cache/kaizen-agentic.yaml" + }, + { + "repo": "key-cape", + "count": 1, + "url": "https://forgejo.coulomb.social/coulomb/key-cape/raw/branch/main/registry/indexes/capabilities.yaml", + "cache": "/data/cache/key-cape.yaml" + }, + { + "repo": "kontextual-engine", + "count": 1, + "url": "https://forgejo.coulomb.social/coulomb/kontextual-engine/raw/branch/main/registry/indexes/capabilities.yaml", + "cache": "/data/cache/kontextual-engine.yaml" + }, + { + "repo": "llm-connect", + "count": 1, + "url": "https://forgejo.coulomb.social/coulomb/llm-connect/raw/branch/main/registry/indexes/capabilities.yaml", + "cache": "/data/cache/llm-connect.yaml" + }, + { + "repo": "markitect-filter", + "count": 1, + "url": "https://forgejo.coulomb.social/coulomb/markitect-filter/raw/branch/main/registry/indexes/capabilities.yaml", + "cache": "/data/cache/markitect-filter.yaml" + }, + { + "repo": "markitect-main", + "count": 1, + "url": "https://forgejo.coulomb.social/coulomb/markitect-main/raw/branch/main/registry/indexes/capabilities.yaml", + "cache": "/data/cache/markitect-main.yaml" + }, + { + "repo": "markitect-quarkdown", + "count": 1, + "url": "https://forgejo.coulomb.social/coulomb/markitect-quarkdown/raw/branch/main/registry/indexes/capabilities.yaml", + "cache": "/data/cache/markitect-quarkdown.yaml" + }, + { + "repo": "markitect-tool", + "count": 1, + "url": "https://forgejo.coulomb.social/coulomb/markitect-tool/raw/branch/main/registry/indexes/capabilities.yaml", + "cache": "/data/cache/markitect-tool.yaml" + }, + { + "repo": "net-kingdom", + "count": 6, + "url": "https://forgejo.coulomb.social/coulomb/net-kingdom/raw/branch/main/registry/indexes/capabilities.yaml", + "cache": "/data/cache/net-kingdom.yaml" + }, + { + "repo": "open-cmis-tck", + "count": 1, + "url": "https://forgejo.coulomb.social/coulomb/open-cmis-tck/raw/branch/main/registry/indexes/capabilities.yaml", + "cache": "/data/cache/open-cmis-tck.yaml" + }, + { + "repo": "open-reuse", + "count": 1, + "url": "https://forgejo.coulomb.social/coulomb/open-reuse/raw/branch/main/registry/indexes/capabilities.yaml", + "cache": "/data/cache/open-reuse.yaml" + }, + { + "repo": "ops-bridge", + "count": 1, + "url": "https://forgejo.coulomb.social/coulomb/ops-bridge/raw/branch/main/registry/indexes/capabilities.yaml", + "cache": "/data/cache/ops-bridge.yaml" + }, + { + "repo": "ops-hub", + "count": 1, + "url": "https://forgejo.coulomb.social/coulomb/ops-hub/raw/branch/main/registry/indexes/capabilities.yaml", + "cache": "/data/cache/ops-hub.yaml" + }, + { + "repo": "ops-warden", + "count": 3, + "url": "https://forgejo.coulomb.social/coulomb/ops-warden/raw/branch/main/registry/indexes/capabilities.yaml", + "cache": "/data/cache/ops-warden.yaml" + }, + { + "repo": "phase-memory", + "count": 1, + "url": "https://forgejo.coulomb.social/coulomb/phase-memory/raw/branch/main/registry/indexes/capabilities.yaml", + "cache": "/data/cache/phase-memory.yaml" + }, + { + "repo": "railiance-apps", + "count": 1, + "url": "https://forgejo.coulomb.social/coulomb/railiance-apps/raw/branch/main/registry/indexes/capabilities.yaml", + "cache": "/data/cache/railiance-apps.yaml" + }, + { + "repo": "railiance-cluster", + "count": 1, + "url": "https://forgejo.coulomb.social/coulomb/railiance-cluster/raw/branch/main/registry/indexes/capabilities.yaml", + "cache": "/data/cache/railiance-cluster.yaml" + }, + { + "repo": "railiance-enablement", + "count": 1, + "url": "https://forgejo.coulomb.social/coulomb/railiance-enablement/raw/branch/main/registry/indexes/capabilities.yaml", + "cache": "/data/cache/railiance-enablement.yaml" + }, + { + "repo": "railiance-fabric", + "count": 1, + "url": "https://forgejo.coulomb.social/coulomb/railiance-fabric/raw/branch/main/registry/indexes/capabilities.yaml", + "cache": "/data/cache/railiance-fabric.yaml" + }, + { + "repo": "railiance-forge", + "count": 2, + "url": "https://forgejo.coulomb.social/coulomb/railiance-forge/raw/branch/main/registry/indexes/capabilities.yaml", + "cache": "/data/cache/railiance-forge.yaml" + }, + { + "repo": "railiance-infra", + "count": 1, + "url": "https://forgejo.coulomb.social/coulomb/railiance-infra/raw/branch/main/registry/indexes/capabilities.yaml", + "cache": "/data/cache/railiance-infra.yaml" + }, + { + "repo": "railiance-platform", + "count": 1, + "url": "https://forgejo.coulomb.social/coulomb/railiance-platform/raw/branch/main/registry/indexes/capabilities.yaml", + "cache": "/data/cache/railiance-platform.yaml" + }, + { + "repo": "repo-scoping", + "count": 1, + "url": "https://forgejo.coulomb.social/coulomb/repo-scoping/raw/branch/main/registry/indexes/capabilities.yaml", + "cache": "/data/cache/repo-scoping.yaml" + }, + { + "repo": "repo-seed", + "count": 1, + "url": "https://forgejo.coulomb.social/coulomb/repo-seed/raw/branch/main/registry/indexes/capabilities.yaml", + "cache": "/data/cache/repo-seed.yaml" + }, + { + "repo": "reuse-surface", + "count": 3, + "url": "https://forgejo.coulomb.social/coulomb/reuse-surface/raw/branch/main/registry/indexes/capabilities.yaml", + "cache": "/data/cache/reuse-surface.yaml" + }, + { + "repo": "shard-wiki", + "count": 8, + "url": "https://forgejo.coulomb.social/coulomb/shard-wiki/raw/branch/main/registry/indexes/capabilities.yaml", + "cache": "/data/cache/shard-wiki.yaml" + }, + { + "repo": "state-hub", + "count": 2, + "url": "https://forgejo.coulomb.social/coulomb/state-hub/raw/branch/main/registry/indexes/capabilities.yaml", + "cache": "/data/cache/state-hub.yaml" + }, + { + "repo": "tegwick-control", + "count": 0, + "url": "https://forgejo.coulomb.social/coulomb/tegwick-control/raw/branch/main/registry/indexes/capabilities.yaml", + "cache": "/data/cache/tegwick-control.yaml" + }, + { + "repo": "the-custodian", + "count": 1, + "url": "https://forgejo.coulomb.social/coulomb/the-custodian/raw/branch/main/registry/indexes/capabilities.yaml", + "cache": "/data/cache/the-custodian.yaml" + }, + { + "repo": "user-engine", + "count": 1, + "url": "https://forgejo.coulomb.social/coulomb/user-engine/raw/branch/main/registry/indexes/capabilities.yaml", + "cache": "/data/cache/user-engine.yaml" + }, + { + "repo": "vantage-point", + "count": 1, + "url": "https://forgejo.coulomb.social/coulomb/vantage-point/raw/branch/main/registry/indexes/capabilities.yaml", + "cache": "/data/cache/vantage-point.yaml" + }, + { + "repo": "vergabe-teilnahme", + "count": 1, + "url": "https://forgejo.coulomb.social/coulomb/vergabe-teilnahme/raw/branch/main/registry/indexes/capabilities.yaml", + "cache": "/data/cache/vergabe-teilnahme.yaml" + }, + { + "repo": "whynot-control", + "count": 0, + "url": "https://forgejo.coulomb.social/coulomb/whynot-control/raw/branch/main/registry/indexes/capabilities.yaml", + "cache": "/data/cache/whynot-control.yaml" + }, + { + "repo": "whynot-design", + "count": 1, + "url": "https://forgejo.coulomb.social/coulomb/whynot-design/raw/branch/main/registry/indexes/capabilities.yaml", + "cache": "/data/cache/whynot-design.yaml" + } + ], + "capabilities": [ + { + "id": "capability.activity.event-coordinate", + "name": "Durable automation and bounded operations", + "summary": "Temporal/NATS/webhook/manual trigger, validated definitions and evidence/runtime sinks.", + "domain": "helix_forge", + "status": "draft", + "owner": "activity-core", + "tags": [ + "activity", + "coordination", + "automation", + "security", + "netkingdom", + "framework-reuse" + ], + "vector": "D3 / A4 / C1 / R0", + "path": "registry/capabilities/capability.activity.event-coordinate.md", + "consumption_modes": [ + "HTTP API", + "source" + ], + "source_repo": "activity-core", + "source_url": "https://forgejo.coulomb.social/coulomb/activity-core/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/activity-core.yaml" + }, + { + "id": "capability.agents.cli-assistant", + "name": "Console-Native LLM Assistant (cya)", + "summary": "Console-native, backend-agnostic assistant CLI that expresses user intent in natural language and returns safe, explainable, context-aware help.", + "vector": "D2 / A2 / C1 / R0", + "domain": "agents", + "status": "draft", + "owner": "can-you-assist", + "path": "registry/capabilities/capability.agents.cli-assistant.md", + "tags": [ + "cli", + "assistant", + "llm", + "safety" + ], + "consumption_modes": [ + "cli" + ], + "source_repo": "can-you-assist", + "source_url": "https://forgejo.coulomb.social/coulomb/can-you-assist/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/can-you-assist.yaml" + }, + { + "id": "capability.agents.custodian-runtime-tooling", + "name": "Generic agent runtime and repository tooling", + "summary": "Agent runtime modules and repository classification tooling.", + "domain": "custodian", + "status": "draft", + "owner": "the-custodian", + "tags": [ + "agents", + "runtime", + "classification", + "security", + "netkingdom", + "framework-reuse" + ], + "vector": "D2 / A2 / C1 / R0", + "path": "registry/capabilities/capability.agents.custodian-runtime-tooling.md", + "consumption_modes": [ + "CLI", + "source" + ], + "source_repo": "the-custodian", + "source_url": "https://forgejo.coulomb.social/coulomb/the-custodian/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/the-custodian.yaml" + }, + { + "id": "capability.agents.kaizen-framework", + "name": "Kaizen Agentic Framework", + "summary": "AI agency framework providing 20 deployable agent instruction sets, project memory, metrics, role and engagement contracts, and scheduled preparation for governed execution.", + "vector": "D3 / A2 / C1 / R0", + "domain": "agents", + "status": "draft", + "owner": "kaizen-agentic", + "path": "registry/capabilities/capability.agents.kaizen-framework.md", + "tags": [ + "agents", + "memory", + "coordination", + "metrics", + "scheduling", + "engagements" + ], + "consumption_modes": [ + "cli", + "library import", + "file contracts" + ], + "source_repo": "kaizen-agentic", + "source_url": "https://forgejo.coulomb.social/coulomb/kaizen-agentic/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/kaizen-agentic.yaml" + }, + { + "id": "capability.agents.llm-connector", + "name": "Pluggable LLM Adapter Library (llm-connect)", + "summary": "Provider-neutral Python/CLI LLM adapter library supporting OpenRouter, Gemini, OpenAI, and the Claude Code CLI out of the box, with a clean abstract interface for adding new providers.", + "vector": "D3 / A2 / C2 / R1", + "domain": "agents", + "status": "draft", + "owner": "llm-connect", + "path": "registry/capabilities/capability.agents.llm-connector.md", + "tags": [ + "llm", + "adapter", + "provider-neutral" + ], + "consumption_modes": [ + "library import", + "cli" + ], + "source_repo": "llm-connect", + "source_url": "https://forgejo.coulomb.social/coulomb/llm-connect/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/llm-connect.yaml" + }, + { + "id": "capability.agents.performance-learning", + "name": "Agent performance learning", + "summary": "Capture sessions, curate patterns, distribute lessons and measure outcomes.", + "domain": "infotech", + "status": "draft", + "owner": "agentic-resources", + "tags": [ + "security", + "netkingdom", + "framework-reuse" + ], + "vector": "D3 / A2 / C1 / R0", + "path": "registry/capabilities/capability.agents.performance-learning.md", + "consumption_modes": [ + "CLI", + "source" + ], + "source_repo": "agentic-resources", + "source_url": "https://forgejo.coulomb.social/coulomb/agentic-resources/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/agentic-resources.yaml" + }, + { + "id": "capability.agents.repo-scoping-service", + "name": "Repository Scoping Service", + "summary": "Maps repositories from usefulness to implementation (Ability -> Capability -> Feature -> Evidence -> Code location) via a Python registry core, FastAPI HTTP API, and curator UI.", + "vector": "D2 / A2 / C1 / R0", + "domain": "agents", + "status": "draft", + "owner": "repo-scoping", + "path": "registry/capabilities/capability.agents.repo-scoping-service.md", + "tags": [ + "classification", + "scoping", + "registry" + ], + "consumption_modes": [ + "http api", + "library import", + "ui" + ], + "source_repo": "repo-scoping", + "source_url": "https://forgejo.coulomb.social/coulomb/repo-scoping/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/repo-scoping.yaml" + }, + { + "id": "capability.audit.event-retain", + "name": "Independent audit custody", + "summary": "Tenant-aware event ingestion, chain integrity, heartbeat and reconciliation.", + "domain": "infotech", + "status": "draft", + "owner": "audit-core", + "tags": [ + "security", + "netkingdom", + "framework-reuse" + ], + "vector": "D3 / A4 / C3 / R2", + "path": "registry/capabilities/capability.audit.event-retain.md", + "consumption_modes": [ + "HTTP API", + "source" + ], + "source_repo": "audit-core", + "source_url": "https://forgejo.coulomb.social/coulomb/audit-core/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/audit-core.yaml" + }, + { + "id": "capability.authorization.policy-evaluate", + "name": "Policy evaluation and decision evidence", + "summary": "Check, batch, list-allowed, explain, binding, lifetime and decision records.", + "domain": "helix_forge", + "status": "draft", + "owner": "access-engine", + "tags": [ + "authorization", + "policy", + "flex-auth", + "security", + "netkingdom", + "framework-reuse" + ], + "vector": "D4 / A4 / C2 / R1", + "path": "registry/capabilities/capability.authorization.policy-evaluate.md", + "consumption_modes": [ + "HTTP API", + "source" + ], + "source_repo": "access-engine", + "source_url": "https://forgejo.coulomb.social/coulomb/access-engine/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/access-engine.yaml" + }, + { + "id": "capability.cmis.tck", + "name": "OpenCMIS Conformance-Preparation Extension", + "summary": "CMIS conformance-preparation extension for guide-board, keeping CMIS-specific runner code, profiles, capability mappings, and workplans outside the generic compliance framework.", + "vector": "D2 / A1 / C1 / R0", + "domain": "communication", + "status": "draft", + "owner": "open-cmis-tck", + "path": "registry/capabilities/capability.cmis.tck.md", + "tags": [ + "cmis", + "compliance", + "guide-board-extension" + ], + "consumption_modes": [ + "library import (guide-board extension)" + ], + "source_repo": "open-cmis-tck", + "source_url": "https://forgejo.coulomb.social/coulomb/open-cmis-tck/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/open-cmis-tck.yaml" + }, + { + "id": "capability.communication.compliance-evidence-framework", + "name": "Certification And Compliance Evidence Framework", + "summary": "Framework that turns standards, conformance, regulatory, and repository-quality claims into structured, reviewable, repeatable, comparable evidence via a pluggable extension architecture.", + "vector": "D2 / A2 / C1 / R0", + "domain": "communication", + "status": "draft", + "owner": "guide-board", + "path": "registry/capabilities/capability.communication.compliance-evidence-framework.md", + "tags": [ + "compliance", + "certification", + "evidence", + "extensions" + ], + "consumption_modes": [ + "cli" + ], + "source_repo": "guide-board", + "source_url": "https://forgejo.coulomb.social/coulomb/guide-board/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/guide-board.yaml" + }, + { + "id": "capability.communication.context-engine", + "name": "Kontextual Engine (Knowledge Operations Runtime)", + "summary": "Headless knowledge-operations engine turning heterogeneous information assets into persistent, contextual, governed, retrievable, transformable, and agent-operable knowledge.", + "vector": "D4 / A1 / C1 / R0", + "domain": "communication", + "status": "draft", + "owner": "kontextual-engine", + "path": "registry/capabilities/capability.communication.context-engine.md", + "tags": [ + "knowledge", + "context", + "runtime" + ], + "consumption_modes": [ + "library import" + ], + "source_repo": "kontextual-engine", + "source_url": "https://forgejo.coulomb.social/coulomb/kontextual-engine/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/kontextual-engine.yaml" + }, + { + "id": "capability.communication.infospace-workspace", + "name": "Infospace Workspace And Evaluation Bench", + "summary": "Workspace and service for creating, developing, evaluating, and inspecting structured knowledge spaces (infospaces); application-layer successor to the infospace work begun in markitect-main.", + "vector": "D3 / A1 / C2 / R1", + "domain": "communication", + "status": "draft", + "owner": "infospace-bench", + "path": "registry/capabilities/capability.communication.infospace-workspace.md", + "tags": [ + "infospace", + "knowledge", + "evaluation" + ], + "consumption_modes": [ + "cli", + "library import (consumed by info-tech-canon)" + ], + "source_repo": "infospace-bench", + "source_url": "https://forgejo.coulomb.social/coulomb/infospace-bench/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/infospace-bench.yaml" + }, + { + "id": "capability.communication.markitect-legacy-platform", + "name": "MarkiTect Legacy Platform", + "summary": "Intelligent markdown engine and information-management platform treating documents as structured, queryable information spaces with schema validation, transclusion, and LLM-driven evaluation; the legacy umbrella now being split into markitect-tool, infospace-bench, and kontextual-engine.", + "vector": "D3 / A1 / C1 / R0", + "domain": "communication", + "status": "draft", + "owner": "markitect-main", + "path": "registry/capabilities/capability.communication.markitect-legacy-platform.md", + "tags": [ + "markitect", + "markdown", + "legacy" + ], + "consumption_modes": [ + "application (legacy, not recommended for new integration)" + ], + "source_repo": "markitect-main", + "source_url": "https://forgejo.coulomb.social/coulomb/markitect-main/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/markitect-main.yaml" + }, + { + "id": "capability.communication.markitect-quarkdown-adapter", + "name": "Markitect Quarkdown Render Adapter", + "summary": "Concrete Quarkdown render/export adapter for Markitect, mapping Markitect profiles to Quarkdown profiles and running controlled Quarkdown CLI execution plans without forking or reimplementing Quarkdown.", + "vector": "D3 / A1 / C1 / R0", + "domain": "communication", + "status": "draft", + "owner": "markitect-quarkdown", + "path": "registry/capabilities/capability.communication.markitect-quarkdown-adapter.md", + "tags": [ + "markitect", + "quarkdown", + "adapter" + ], + "consumption_modes": [ + "library import (plugin/entry-point)" + ], + "source_repo": "markitect-quarkdown", + "source_url": "https://forgejo.coulomb.social/coulomb/markitect-quarkdown/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/markitect-quarkdown.yaml" + }, + { + "id": "capability.communication.markitect-source-adapters", + "name": "Markitect Source-Format Adapters", + "summary": "Concrete source-format adapters (EPUB3, PDF) converting external document formats into canonical Markitect Markdown, implementing the markitect-tool source adapter contract.", + "vector": "D2 / A1 / C1 / R0", + "domain": "communication", + "status": "draft", + "owner": "markitect-filter", + "path": "registry/capabilities/capability.communication.markitect-source-adapters.md", + "tags": [ + "markitect", + "adapter", + "document-conversion" + ], + "consumption_modes": [ + "library import (plugin/entry-point)" + ], + "source_repo": "markitect-filter", + "source_url": "https://forgejo.coulomb.social/coulomb/markitect-filter/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/markitect-filter.yaml" + }, + { + "id": "capability.communication.markitect-toolkit", + "name": "Markitect Markdown Toolkit And CLI", + "summary": "Markdown-native toolkit and CLI (mkt) for turning semi-structured Markdown into structured, queryable, reusable knowledge artifacts; syntax-layer successor to markitect-main.", + "vector": "D4 / A2 / C2 / R1", + "domain": "communication", + "status": "draft", + "owner": "markitect-tool", + "path": "registry/capabilities/capability.communication.markitect-toolkit.md", + "tags": [ + "markitect", + "markdown", + "cli", + "toolkit" + ], + "consumption_modes": [ + "cli", + "library import" + ], + "source_repo": "markitect-tool", + "source_url": "https://forgejo.coulomb.social/coulomb/markitect-tool/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/markitect-tool.yaml" + }, + { + "id": "capability.coordination.cross-owner-work", + "name": "Cross-owner coordination lifecycle", + "summary": "Observation, lease, wake-up, checkpoint and receipt contracts/runtime.", + "domain": "infotech", + "status": "draft", + "owner": "coordination-engine", + "tags": [ + "security", + "netkingdom", + "framework-reuse" + ], + "vector": "D3 / A2 / C1 / R0", + "path": "registry/capabilities/capability.coordination.cross-owner-work.md", + "consumption_modes": [ + "CLI", + "source" + ], + "source_repo": "coordination-engine", + "source_url": "https://forgejo.coulomb.social/coulomb/coordination-engine/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/coordination-engine.yaml" + }, + { + "id": "capability.design.whynot-system", + "name": "Whynot Design System", + "summary": "Framework-agnostic visual language for whynot prototype/market-signal artefacts: design tokens, drop-in CSS, Lit-based web components usable from React/Django/Vue/plain HTML, and Django template adapters.", + "vector": "D3 / A2 / C2 / R1", + "domain": "consumer", + "status": "draft", + "owner": "whynot-design", + "path": "registry/capabilities/capability.design.whynot-system.md", + "tags": [ + "design-system", + "web-components", + "frontend" + ], + "consumption_modes": [ + "library import (npm)", + "cli", + "django template adapter" + ], + "source_repo": "whynot-design", + "source_url": "https://forgejo.coulomb.social/coulomb/whynot-design/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/whynot-design.yaml" + }, + { + "id": "capability.evidence.binding", + "name": "Evidence-to-Target Binding", + "summary": "Links evidence items to structured targets with relation, status, and confidence metadata, bidirectional queries, active-state coordination, and canonical engine events.", + "vector": "D3 / A2 / C3 / R3", + "domain": "infotech", + "status": "draft", + "owner": "evidence-binder", + "path": "registry/capabilities/capability.evidence.binding.md", + "tags": [ + "evidence", + "binding", + "evidence-link", + "form-field", + "citation-evidence" + ], + "consumption_modes": [ + "source module", + "React provider" + ], + "source_repo": "evidence-binder", + "source_url": "https://forgejo.coulomb.social/coulomb/evidence-binder/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/evidence-binder.yaml" + }, + { + "id": "capability.evidence.rect-registry", + "name": "Visual-Guide Rect Registry", + "summary": "Coordinates field, evidence-card, and highlight rectangles from independent renderers so an SVG overlay can draw the active visual guide without polling.", + "vector": "D3 / A2 / C3 / R3", + "domain": "infotech", + "status": "draft", + "owner": "evidence-binder", + "path": "registry/capabilities/capability.evidence.rect-registry.md", + "tags": [ + "rect-registry", + "visual-guide", + "overlay", + "active-state", + "citation-evidence" + ], + "consumption_modes": [ + "source module", + "React component" + ], + "source_repo": "evidence-binder", + "source_url": "https://forgejo.coulomb.social/coulomb/evidence-binder/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/evidence-binder.yaml" + }, + { + "id": "capability.feature-control.evaluate", + "name": "Feature Availability Evaluation", + "summary": "Evaluate whether a feature is active, hidden, disabled, or unavailable for a subject in context.", + "vector": "D5 / A4 / C3 / R3", + "domain": "helix_forge", + "status": "draft", + "owner": "feature-control", + "path": "registry/capabilities/capability.feature-control.evaluate.md", + "tags": [ + "feature-control", + "evaluation", + "sdk" + ], + "consumption_modes": [ + "SDK", + "service API" + ], + "source_repo": "feature-control", + "source_url": "https://forgejo.coulomb.social/coulomb/feature-control/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/feature-control.yaml" + }, + { + "id": "capability.feature-control.rollout", + "name": "Feature Rollout Control", + "summary": "Gradually expose features to subjects across tenants, domains, groups, or cohorts using rollout rules.", + "vector": "D4 / A2 / C2 / R1", + "domain": "helix_forge", + "status": "draft", + "owner": "feature-control", + "path": "registry/capabilities/capability.feature-control.rollout.md", + "tags": [ + "feature-control", + "rollout", + "planning" + ], + "consumption_modes": [ + "source module", + "SDK" + ], + "source_repo": "feature-control", + "source_url": "https://forgejo.coulomb.social/coulomb/feature-control/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/feature-control.yaml" + }, + { + "id": "capability.feature-control.visibility", + "name": "Feature Visibility Control", + "summary": "Control whether features are visible or hidden for subjects without changing entitlement or authorization.", + "vector": "D4 / A2 / C2 / R1", + "domain": "helix_forge", + "status": "draft", + "owner": "feature-control", + "path": "registry/capabilities/capability.feature-control.visibility.md", + "tags": [ + "feature-control", + "visibility" + ], + "consumption_modes": [ + "source module" + ], + "source_repo": "feature-control", + "source_url": "https://forgejo.coulomb.social/coulomb/feature-control/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/feature-control.yaml" + }, + { + "id": "capability.graph.nbgm-spec", + "name": "Network-Based Graph Model (NBGM) Specification", + "summary": "Generic system and versioned protocol specification for exploring dependency structures as network-based graph models (NBGM), unifying entity/relationship inspection and reasoning across arbitrary domains.", + "vector": "D2 / A0 / C1 / R0", + "domain": "infotech", + "status": "draft", + "owner": "vantage-point", + "path": "registry/capabilities/capability.graph.nbgm-spec.md", + "tags": [ + "graph", + "nbgm", + "specification" + ], + "consumption_modes": [ + "informational (specification only)" + ], + "source_repo": "vantage-point", + "source_url": "https://forgejo.coulomb.social/coulomb/vantage-point/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/vantage-point.yaml" + }, + { + "id": "capability.hr.workflow-catalog", + "name": "HR Workflow Catalog", + "summary": "Markdown-first registry of HR planning workflows with an iterative assessment loop tied to INTENT phases.", + "vector": "D3 / A1 / C1 / R0", + "domain": "consumer", + "status": "draft", + "owner": "human-resources", + "path": "registry/capabilities/capability.hr.workflow-catalog.md", + "tags": [ + "hr", + "workflows", + "assessment", + "planning" + ], + "consumption_modes": [ + "informational" + ], + "source_repo": "human-resources", + "source_url": "https://forgejo.coulomb.social/coulomb/human-resources/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/human-resources.yaml" + }, + { + "id": "capability.iam.key-cape", + "name": "OIDC identity and assurance", + "summary": "OIDC code with PKCE, service tokens, JWKS, MFA and caller CLI.", + "domain": "infotech", + "status": "draft", + "owner": "key-cape", + "tags": [ + "iam", + "oidc", + "netkingdom", + "security", + "framework-reuse" + ], + "vector": "D4 / A4 / C2 / R1", + "path": "registry/capabilities/capability.iam.key-cape.md", + "consumption_modes": [ + "HTTP API", + "source" + ], + "source_repo": "key-cape", + "source_url": "https://forgejo.coulomb.social/coulomb/key-cape/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/key-cape.yaml" + }, + { + "id": "capability.identity.subject-resolution", + "name": "Identity Subject Resolution", + "summary": "Resolve who or what is acting by mapping principals, accounts, actors, and identifiers to a stable subject model.", + "vector": "D3 / A0 / C1 / R0", + "domain": "helix_forge", + "status": "draft", + "owner": "commerce-canon", + "path": "registry/capabilities/capability.identity.subject-resolution.md", + "tags": [ + "identity", + "subject", + "architecture" + ], + "consumption_modes": [ + "informational" + ], + "source_repo": "commerce-canon", + "source_url": "https://forgejo.coulomb.social/coulomb/commerce-canon/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/commerce-canon.yaml" + }, + { + "id": "capability.identity.user-engine", + "name": "User lifecycle and account journeys", + "summary": "Accounts, identity links, membership, MFA and recovery portal, advisory catalog.", + "domain": "communication", + "status": "draft", + "owner": "user-engine", + "tags": [ + "identity", + "user-management", + "multi-tenant", + "security", + "netkingdom", + "framework-reuse" + ], + "vector": "D4 / A4 / C2 / R0", + "path": "registry/capabilities/capability.identity.user-engine.md", + "consumption_modes": [ + "HTTP API", + "source" + ], + "source_repo": "user-engine", + "source_url": "https://forgejo.coulomb.social/coulomb/user-engine/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/user-engine.yaml" + }, + { + "id": "capability.identity.vocabulary-canonicalize", + "name": "Identity Vocabulary Canonicalization", + "summary": "Define an implementation-neutral vocabulary for identity-related concepts across overlapping domains.", + "vector": "D4 / A0 / C2 / R0", + "domain": "helix_forge", + "status": "draft", + "owner": "commerce-canon", + "path": "registry/capabilities/capability.identity.vocabulary-canonicalize.md", + "tags": [ + "identity", + "terminology", + "research" + ], + "consumption_modes": [ + "informational" + ], + "source_repo": "commerce-canon", + "source_url": "https://forgejo.coulomb.social/coulomb/commerce-canon/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/commerce-canon.yaml" + }, + { + "id": "capability.infotech.artifact-store", + "name": "Artifact Registry And Storage Gateway", + "summary": "Generic artifact registry and storage gateway for generated outputs, evidence packages, reports, logs, snapshots, exports, and release artifacts.", + "vector": "D3 / A1 / C1 / R0", + "domain": "infotech", + "status": "draft", + "owner": "artifact-store", + "path": "registry/capabilities/capability.infotech.artifact-store.md", + "tags": [ + "artifact", + "storage", + "provenance", + "registry" + ], + "consumption_modes": [ + "library import", + "cli" + ], + "source_repo": "artifact-store", + "source_url": "https://forgejo.coulomb.social/coulomb/artifact-store/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/artifact-store.yaml" + }, + { + "id": "capability.infotech.canon-service", + "name": "Shared semantic contracts and validation", + "summary": "Markdown canon with library, CLI and read-only API.", + "domain": "infotech", + "status": "draft", + "owner": "info-tech-canon", + "tags": [ + "canon", + "infospace", + "service", + "security", + "netkingdom", + "framework-reuse" + ], + "vector": "D2 / A4 / C1 / R0", + "path": "registry/capabilities/capability.infotech.canon-service.md", + "consumption_modes": [ + "HTTP API", + "source" + ], + "source_repo": "info-tech-canon", + "source_url": "https://forgejo.coulomb.social/coulomb/info-tech-canon/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/info-tech-canon.yaml" + }, + { + "id": "capability.infotech.citation-engine", + "name": "Citation Evidence Domain Engine", + "summary": "Core domain model and engine services for the citation-evidence ecosystem \u2014 shared vocabulary, in-memory repositories, orchestration services, event bus, and citation card renderers.", + "vector": "D3 / A1 / C1 / R0", + "domain": "infotech", + "status": "draft", + "owner": "citation-engine", + "path": "registry/capabilities/capability.infotech.citation-engine.md", + "tags": [ + "citation", + "evidence", + "domain-model" + ], + "consumption_modes": [ + "library import (workspace link)" + ], + "source_repo": "citation-engine", + "source_url": "https://forgejo.coulomb.social/coulomb/citation-engine/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/citation-engine.yaml" + }, + { + "id": "capability.infotech.citation-evidence-workspace", + "name": "Citation Evidence Workspace", + "summary": "Document-centered evidence workspace for capturing, managing, presenting, and re-opening citations \u2014 the umbrella application over the citation-evidence six-package design.", + "vector": "D4 / A1 / C1 / R0", + "domain": "infotech", + "status": "draft", + "owner": "citation-evidence", + "path": "registry/capabilities/capability.infotech.citation-evidence-workspace.md", + "tags": [ + "citation", + "evidence", + "workspace" + ], + "consumption_modes": [ + "application (local dev server)" + ], + "source_repo": "citation-evidence", + "source_url": "https://forgejo.coulomb.social/coulomb/citation-evidence/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/citation-evidence.yaml" + }, + { + "id": "capability.infotech.config-surface-atlas", + "name": "Configuration Surface Atlas", + "summary": "Read-first, cross-kind map and evidence layer for configuration surfaces \u2014 what configures a system, who owns it, its scope, and where the source of truth lives.", + "vector": "D5 / A0 / C2 / R2", + "domain": "infotech", + "status": "draft", + "owner": "config-atlas", + "path": "registry/capabilities/capability.infotech.config-surface-atlas.md", + "tags": [ + "configuration", + "registry", + "control-plane", + "effective-config", + "evidence" + ], + "consumption_modes": [ + "informational", + "source module" + ], + "source_repo": "config-atlas", + "source_url": "https://forgejo.coulomb.social/coulomb/config-atlas/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/config-atlas.yaml" + }, + { + "id": "capability.infotech.core-hub", + "name": "Core Hub Production Interaction Framework", + "summary": "Contract-first framework for cross-domain state, agent coordination, evidence, registries, workplans, and operator interaction surfaces.", + "vector": "D3 / A1 / C1 / R1", + "domain": "infotech", + "status": "draft", + "owner": "core-hub", + "path": "registry/capabilities/capability.infotech.core-hub.md", + "tags": [ + "interaction-framework", + "state-hub", + "inter-hub-successor", + "agent-coordination", + "registry", + "api" + ], + "consumption_modes": [ + "informational", + "planning", + "cli" + ], + "source_repo": "core-hub", + "source_url": "https://forgejo.coulomb.social/coulomb/core-hub/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/core-hub.yaml" + }, + { + "id": "capability.infotech.email-connector", + "name": "Mailbox Evidence Scanner (email-connect)", + "summary": "Headless, provider-neutral email communication and evidence service; first slice scans a mailbox or fixture directory and produces timestamped CSV evidence reports.", + "vector": "D2 / A1 / C1 / R0", + "domain": "infotech", + "status": "draft", + "owner": "email-connect", + "path": "registry/capabilities/capability.infotech.email-connector.md", + "tags": [ + "email", + "evidence", + "coordination" + ], + "consumption_modes": [ + "cli" + ], + "source_repo": "email-connect", + "source_url": "https://forgejo.coulomb.social/coulomb/email-connect/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/email-connect.yaml" + }, + { + "id": "capability.infotech.headless-browser", + "name": "Headless Browser Automation", + "summary": "Headless browser automation, screenshots and keyboard trials through rapp-playwright 0.1.0 (Playwright 1.64.0, Chromium 156.0.8078.4). Published A5 local container; managed Railiance admission is pending. Download the Python 3.10+ standalone client from https://forgejo.coulomb.social/coulomb/rapp-playwright/raw/commit/8831a05aeba5ce588f3b395ade676dde2a151c5d/client/rapp_playwright.py and run `python3 rapp_playwright.py --url http://127.0.0.1:18083 preflight`, then `python3 rapp_playwright.py --url http://127.0.0.1:18083 trial --out trial-01` with a fresh output directory. This endpoint was verified locally on 2026-10-13 and is ephemeral; it installs no browser for the caller. If unreachable, follow the local container setup in the capability card or adapt its A2 source examples; this is an availability gap on the existing capability. Contract: https://forgejo.coulomb.social/coulomb/rapp-playwright/src/commit/8831a05aeba5ce588f3b395ade676dde2a151c5d/docs/consumer-contract.md.", + "vector": "D3 / A5 / C2 / R2", + "domain": "infotech", + "status": "reviewed", + "owner": "reuse-surface", + "path": "registry/capabilities/capability.infotech.headless-browser.md", + "tags": [ + "headless", + "browser", + "automation", + "playwright", + "chromium", + "testing", + "screenshots", + "extraction", + "firefox", + "webkit", + "selenium", + "puppeteer" + ], + "consumption_modes": [ + "informational", + "source adaptation", + "JavaScript library", + "Python library", + "cli", + "service API", + "container" + ], + "source_repo": "reuse-surface", + "source_url": "https://forgejo.coulomb.social/coulomb/reuse-surface/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/reuse-surface.yaml" + }, + { + "id": "capability.infotech.hub-core-library", + "name": "Reusable FOS Hub Core Primitives", + "summary": "Reusable FastAPI, SQLAlchemy, and MCP primitives extracted from the State Hub for use by other FOS (Federation of Services) hubs.", + "vector": "D2 / A1 / C1 / R1", + "domain": "infotech", + "status": "draft", + "owner": "hub-core", + "path": "registry/capabilities/capability.infotech.hub-core-library.md", + "tags": [ + "hub", + "fastapi", + "sqlalchemy", + "mcp" + ], + "consumption_modes": [ + "library import" + ], + "source_repo": "hub-core", + "source_url": "https://forgejo.coulomb.social/coulomb/hub-core/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/hub-core.yaml" + }, + { + "id": "capability.infotech.issue-tracking", + "name": "External issue tracker connector", + "summary": "CLI and Python adapters for external issue systems.", + "domain": "infotech", + "status": "draft", + "owner": "issue-core", + "tags": [ + "issue-tracking", + "connector", + "work-record", + "security", + "netkingdom", + "framework-reuse" + ], + "vector": "D4 / A2 / C2 / R1", + "path": "registry/capabilities/capability.infotech.issue-tracking.md", + "consumption_modes": [ + "CLI", + "source" + ], + "source_repo": "issue-core", + "source_url": "https://forgejo.coulomb.social/coulomb/issue-core/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/issue-core.yaml" + }, + { + "id": "capability.infotech.oss-integration-continuity", + "name": "Managed Continuity For OSS Integrations", + "summary": "Turns proven open-source integrations into structured, maintainable, continuously managed assets with clear boundaries and update loops, so they remain robust and transparent as upstream evolves.", + "vector": "D2 / A1 / C1 / R0", + "domain": "infotech", + "status": "draft", + "owner": "open-reuse", + "path": "registry/capabilities/capability.infotech.oss-integration-continuity.md", + "tags": [ + "oss", + "integration", + "maintenance" + ], + "consumption_modes": [ + "library import" + ], + "source_repo": "open-reuse", + "source_url": "https://forgejo.coulomb.social/coulomb/open-reuse/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/open-reuse.yaml" + }, + { + "id": "capability.infotech.pdf-evidence-ingest", + "name": "Headless PDF Evidence Ingest", + "summary": "Turns raw PDF bytes into an engine-shaped Document + DocumentRepresentation \u2014 SHA-256 fingerprint, canonical text, page map, and gap-free offset map \u2014 as a runtime-agnostic library with no viewer, persistence, or React coupling.", + "vector": "D2 / A1 / C1 / R1", + "domain": "infotech", + "status": "draft", + "owner": "evidence-source", + "path": "registry/capabilities/capability.infotech.pdf-evidence-ingest.md", + "tags": [ + "pdf", + "ingest", + "fingerprint", + "canonical-text", + "evidence" + ], + "consumption_modes": [ + "library import" + ], + "source_repo": "evidence-source", + "source_url": "https://forgejo.coulomb.social/coulomb/evidence-source/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/evidence-source.yaml" + }, + { + "id": "capability.infotech.repo-template", + "name": "Coulomb Repository Template", + "summary": "Bootstrap new git repositories with agent instructions, registry scaffold, and State Hub onboarding conventions.", + "vector": "D3 / A3 / C2 / R2", + "domain": "infotech", + "status": "draft", + "owner": "repo-seed", + "path": "registry/capabilities/capability.infotech.repo-template.md", + "tags": [ + "template", + "bootstrap", + "state-hub", + "onboarding" + ], + "consumption_modes": [ + "git clone", + "informational" + ], + "source_repo": "repo-seed", + "source_url": "https://forgejo.coulomb.social/coulomb/repo-seed/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/repo-seed.yaml" + }, + { + "id": "capability.memory.phase-planning", + "name": "Profile-Driven Memory Phase Planning (phase-memory)", + "summary": "Interprets Markitect memory profiles as runtime plans, modeling memory phases and producing deterministic dry-run actions for retention, refresh, compaction, stabilization, and activation.", + "vector": "D3 / A1 / C1 / R0", + "domain": "communication", + "status": "draft", + "owner": "phase-memory", + "path": "registry/capabilities/capability.memory.phase-planning.md", + "tags": [ + "memory", + "agentic", + "planning" + ], + "consumption_modes": [ + "library import" + ], + "source_repo": "phase-memory", + "source_url": "https://forgejo.coulomb.social/coulomb/phase-memory/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/phase-memory.yaml" + }, + { + "id": "capability.operations.ops-hub", + "name": "Operations Hub Extension Contract", + "summary": "Versioned hub-extension package and bootstrap client for operational truth, service-catalog records, readiness evidence, and migration gates.", + "vector": "D3 / A1 / C1 / R1", + "domain": "operations", + "status": "reviewed", + "owner": "ops-hub", + "path": "registry/capabilities/capability.operations.ops-hub.md", + "tags": [ + "operations", + "service-catalog", + "readiness", + "hub-extension" + ], + "consumption_modes": [ + "contract package", + "command-line bootstrap" + ], + "source_repo": "ops-hub", + "source_url": "https://forgejo.coulomb.social/coulomb/ops-hub/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/ops-hub.yaml" + }, + { + "id": "capability.ops.tunnel-bridge", + "name": "SSH tunnel lifecycle", + "summary": "Named tunnels, reconnect, health and TTL-aware cert_command.", + "domain": "infotech", + "status": "draft", + "owner": "ops-bridge", + "tags": [ + "ssh", + "tunnel", + "ops", + "connectivity", + "security", + "netkingdom", + "framework-reuse" + ], + "vector": "D3 / A2 / C2 / R1", + "path": "registry/capabilities/capability.ops.tunnel-bridge.md", + "consumption_modes": [ + "CLI", + "source" + ], + "source_repo": "ops-bridge", + "source_url": "https://forgejo.coulomb.social/coulomb/ops-bridge/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/ops-bridge.yaml" + }, + { + "id": "capability.procurement.vergabe-teilnahme", + "name": "Vergabe Teilnahme (Public Procurement Participation) Application", + "summary": "Django application (with a Vite/Tailwind frontend) for managing German public-procurement (Vergabe) tender participation \u2014 Ausschreibungs- und Teilnahme-Management-System.", + "vector": "D3 / A1 / C1 / R1", + "domain": "communication", + "status": "draft", + "owner": "vergabe-teilnahme", + "path": "registry/capabilities/capability.procurement.vergabe-teilnahme.md", + "tags": [ + "procurement", + "django", + "vergabe" + ], + "consumption_modes": [ + "application (Django + Vite, local dev via docker-compose)" + ], + "source_repo": "vergabe-teilnahme", + "source_url": "https://forgejo.coulomb.social/coulomb/vergabe-teilnahme/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/vergabe-teilnahme.yaml" + }, + { + "id": "capability.railiance.ci-enablement", + "name": "Developer delivery and CI templates", + "summary": "Reusable CI workflows, build and deployment patterns.", + "domain": "financials", + "status": "draft", + "owner": "railiance-enablement", + "tags": [ + "railiance", + "ci-cd", + "developer-enablement", + "forgejo", + "security", + "netkingdom", + "framework-reuse" + ], + "vector": "D3 / A2 / C2 / R1", + "path": "registry/capabilities/capability.railiance.ci-enablement.md", + "consumption_modes": [ + "CLI", + "source" + ], + "source_repo": "railiance-enablement", + "source_url": "https://forgejo.coulomb.social/coulomb/railiance-enablement/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/railiance-enablement.yaml" + }, + { + "id": "capability.railiance.cluster-bootstrap", + "name": "Kubernetes runtime and cluster controls", + "summary": "k3s, ingress, CNI, operators and kubeconfig/runtime checks.", + "domain": "financials", + "status": "draft", + "owner": "railiance-cluster", + "tags": [ + "railiance", + "kubernetes", + "iac", + "bootstrap", + "security", + "netkingdom", + "framework-reuse" + ], + "vector": "D3 / A2 / C1 / R0", + "path": "registry/capabilities/capability.railiance.cluster-bootstrap.md", + "consumption_modes": [ + "CLI", + "source" + ], + "source_repo": "railiance-cluster", + "source_url": "https://forgejo.coulomb.social/coulomb/railiance-cluster/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/railiance-cluster.yaml" + }, + { + "id": "capability.railiance.fabric-graph", + "name": "Infrastructure ownership and dependency graph", + "summary": "Schemas, validation, graph queries and State Hub exports.", + "domain": "financials", + "status": "draft", + "owner": "railiance-fabric", + "tags": [ + "railiance", + "graph", + "ownership", + "discovery", + "security", + "netkingdom", + "framework-reuse" + ], + "vector": "D3 / A2 / C1 / R0", + "path": "registry/capabilities/capability.railiance.fabric-graph.md", + "consumption_modes": [ + "CLI", + "source" + ], + "source_repo": "railiance-fabric", + "source_url": "https://forgejo.coulomb.social/coulomb/railiance-fabric/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/railiance-fabric.yaml" + }, + { + "id": "capability.railiance.forge-infrastructure", + "name": "Source forge, registries and runner substrate", + "summary": "Forgejo operation, package registries and runner lifecycle.", + "domain": "financials", + "status": "draft", + "owner": "railiance-forge", + "tags": [ + "railiance", + "forgejo", + "forge", + "ci-runner", + "security", + "netkingdom", + "framework-reuse" + ], + "vector": "D3 / A2 / C1 / R0", + "path": "registry/capabilities/capability.railiance.forge-infrastructure.md", + "consumption_modes": [ + "CLI", + "source" + ], + "source_repo": "railiance-forge", + "source_url": "https://forgejo.coulomb.social/coulomb/railiance-forge/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/railiance-forge.yaml" + }, + { + "id": "capability.railiance.infra-provisioning", + "name": "Host convergence and security baseline", + "summary": "Ansible inventory, firewall, SSH bootstrap and baseline verification.", + "domain": "financials", + "status": "draft", + "owner": "railiance-infra", + "tags": [ + "railiance", + "terraform", + "ansible", + "infrastructure", + "security", + "netkingdom", + "framework-reuse" + ], + "vector": "D4 / A2 / C2 / R1", + "path": "registry/capabilities/capability.railiance.infra-provisioning.md", + "consumption_modes": [ + "CLI", + "source" + ], + "source_repo": "railiance-infra", + "source_url": "https://forgejo.coulomb.social/coulomb/railiance-infra/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/railiance-infra.yaml" + }, + { + "id": "capability.railiance.platform-services", + "name": "Secret custody and platform data services", + "summary": "OpenBao policy/custody, consumer grants, secret delivery and database/backup contracts.", + "domain": "financials", + "status": "draft", + "owner": "railiance-platform", + "tags": [ + "railiance", + "platform", + "secrets", + "postgres", + "security", + "netkingdom", + "framework-reuse" + ], + "vector": "D3 / A2 / C1 / R0", + "path": "registry/capabilities/capability.railiance.platform-services.md", + "consumption_modes": [ + "CLI", + "source" + ], + "source_repo": "railiance-platform", + "source_url": "https://forgejo.coulomb.social/coulomb/railiance-platform/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/railiance-platform.yaml" + }, + { + "id": "capability.railiance.workload-deployment-tooling", + "name": "Workload release configuration and guards", + "summary": "Helm/manifests, exact image pins, application runbooks and rollout/rollback.", + "domain": "financials", + "status": "draft", + "owner": "railiance-apps", + "tags": [ + "railiance", + "kubernetes", + "helm", + "deployment", + "security", + "netkingdom", + "framework-reuse" + ], + "vector": "D3 / A2 / C1 / R1", + "path": "registry/capabilities/capability.railiance.workload-deployment-tooling.md", + "consumption_modes": [ + "CLI", + "source" + ], + "source_repo": "railiance-apps", + "source_url": "https://forgejo.coulomb.social/coulomb/railiance-apps/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/railiance-apps.yaml" + }, + { + "id": "capability.registry.register", + "name": "Capability registry and federation", + "summary": "Owner Markdown entries, indexes, federation hub and planning queries.", + "domain": "helix_forge", + "status": "draft", + "owner": "reuse-surface", + "tags": [ + "registry", + "governance", + "meta", + "security", + "netkingdom", + "framework-reuse" + ], + "vector": "D3 / A3 / C2 / R3", + "path": "registry/capabilities/capability.registry.register.md", + "consumption_modes": [ + "library", + "CLI" + ], + "source_repo": "reuse-surface", + "source_url": "https://forgejo.coulomb.social/coulomb/reuse-surface/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/reuse-surface.yaml" + }, + { + "id": "capability.registry.validate", + "name": "Capability registry validation", + "summary": "Schema, index consistency and relation checks.", + "domain": "helix_forge", + "status": "draft", + "owner": "reuse-surface", + "tags": [ + "registry", + "validation", + "cli", + "security", + "netkingdom", + "framework-reuse" + ], + "vector": "D4 / A3 / C3 / R3", + "path": "registry/capabilities/capability.registry.validate.md", + "consumption_modes": [ + "library", + "CLI" + ], + "source_repo": "reuse-surface", + "source_url": "https://forgejo.coulomb.social/coulomb/reuse-surface/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/reuse-surface.yaml" + }, + { + "id": "capability.security.access-routing", + "name": "Credential routing and contained access", + "summary": "warden route and access catalog; exact lane conduit and secret-exec routing.", + "domain": "infotech", + "status": "draft", + "owner": "ops-warden", + "tags": [ + "security", + "netkingdom", + "framework-reuse" + ], + "vector": "D3 / A3 / C1 / R0", + "path": "registry/capabilities/capability.security.access-routing.md", + "consumption_modes": [ + "library", + "CLI" + ], + "source_repo": "ops-warden", + "source_url": "https://forgejo.coulomb.social/coulomb/ops-warden/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/ops-warden.yaml" + }, + { + "id": "capability.security.bootstrap-recovery", + "name": "Guarded bootstrap and recovery preparation", + "summary": "Local identity, bootstrap console, encrypted custody handoff and caller tools.", + "domain": "infotech", + "status": "draft", + "owner": "net-kingdom", + "tags": [ + "security", + "netkingdom", + "framework-reuse" + ], + "vector": "D3 / A2 / C1 / R0", + "path": "registry/capabilities/capability.security.bootstrap-recovery.md", + "consumption_modes": [ + "CLI", + "source" + ], + "source_repo": "net-kingdom", + "source_url": "https://forgejo.coulomb.social/coulomb/net-kingdom/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/net-kingdom.yaml" + }, + { + "id": "capability.security.case-contracts", + "name": "Security case types and instance validation", + "summary": "Versioned types, cases and schema validation.", + "domain": "infotech", + "status": "draft", + "owner": "net-kingdom", + "tags": [ + "security", + "netkingdom", + "framework-reuse" + ], + "vector": "D3 / A2 / C1 / R0", + "path": "registry/capabilities/capability.security.case-contracts.md", + "consumption_modes": [ + "CLI", + "source" + ], + "source_repo": "net-kingdom", + "source_url": "https://forgejo.coulomb.social/coulomb/net-kingdom/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/net-kingdom.yaml" + }, + { + "id": "capability.security.case-follow-through", + "name": "Security case operational follow-through", + "summary": "KnownSecurityCase route, plan and operational receipts.", + "domain": "infotech", + "status": "draft", + "owner": "ops-warden", + "tags": [ + "security", + "netkingdom", + "framework-reuse" + ], + "vector": "D3 / A2 / C1 / R0", + "path": "registry/capabilities/capability.security.case-follow-through.md", + "consumption_modes": [ + "CLI", + "source" + ], + "source_repo": "ops-warden", + "source_url": "https://forgejo.coulomb.social/coulomb/ops-warden/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/ops-warden.yaml" + }, + { + "id": "capability.security.emission-profile", + "name": "Security emission cadence validation", + "summary": "Security profile over generic emission contracts and validator.", + "domain": "infotech", + "status": "draft", + "owner": "net-kingdom", + "tags": [ + "security", + "netkingdom", + "framework-reuse" + ], + "vector": "D3 / A2 / C1 / R0", + "path": "registry/capabilities/capability.security.emission-profile.md", + "consumption_modes": [ + "CLI", + "source" + ], + "source_repo": "net-kingdom", + "source_url": "https://forgejo.coulomb.social/coulomb/net-kingdom/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/net-kingdom.yaml" + }, + { + "id": "capability.security.forge-secret-prevention", + "name": "Independent introduced-history secret prevention", + "summary": "Pinned receive adapter, lifecycle template and fail-closed scan.", + "domain": "infotech", + "status": "draft", + "owner": "railiance-forge", + "tags": [ + "security", + "netkingdom", + "framework-reuse" + ], + "vector": "D3 / A2 / C1 / R0", + "path": "registry/capabilities/capability.security.forge-secret-prevention.md", + "consumption_modes": [ + "CLI", + "source" + ], + "source_repo": "railiance-forge", + "source_url": "https://forgejo.coulomb.social/coulomb/railiance-forge/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/railiance-forge.yaml" + }, + { + "id": "capability.security.iam-tooling-suite", + "name": "NetKingdom security architecture and conformance tooling", + "summary": "Versioned IAM, layer, tenancy and credential contracts plus validators.", + "domain": "infotech", + "status": "draft", + "owner": "net-kingdom", + "tags": [ + "security", + "iam", + "kubernetes", + "conformance", + "netkingdom", + "framework-reuse" + ], + "vector": "D3 / A2 / C1 / R1", + "path": "registry/capabilities/capability.security.iam-tooling-suite.md", + "consumption_modes": [ + "CLI", + "source" + ], + "source_repo": "net-kingdom", + "source_url": "https://forgejo.coulomb.social/coulomb/net-kingdom/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/net-kingdom.yaml" + }, + { + "id": "capability.security.posture-feedback", + "name": "Deterministic posture feedback", + "summary": "CLI evaluates evidence age and posture into owner-routed findings.", + "domain": "infotech", + "status": "draft", + "owner": "net-kingdom", + "tags": [ + "security", + "netkingdom", + "framework-reuse" + ], + "vector": "D3 / A2 / C1 / R0", + "path": "registry/capabilities/capability.security.posture-feedback.md", + "consumption_modes": [ + "CLI", + "source" + ], + "source_repo": "net-kingdom", + "source_url": "https://forgejo.coulomb.social/coulomb/net-kingdom/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/net-kingdom.yaml" + }, + { + "id": "capability.security.scenario-compose", + "name": "Deterministic security scenario composition", + "summary": "CLI selects pinned providers and orders trust transitions.", + "domain": "infotech", + "status": "draft", + "owner": "net-kingdom", + "tags": [ + "security", + "netkingdom", + "framework-reuse" + ], + "vector": "D3 / A2 / C1 / R0", + "path": "registry/capabilities/capability.security.scenario-compose.md", + "consumption_modes": [ + "CLI", + "source" + ], + "source_repo": "net-kingdom", + "source_url": "https://forgejo.coulomb.social/coulomb/net-kingdom/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/net-kingdom.yaml" + }, + { + "id": "capability.security.ssh-certificate-issuance", + "name": "Decision-bound SSH certificates", + "summary": "warden sign with local and OpenBao backends and adm/agt/atm actors.", + "domain": "helix_forge", + "status": "draft", + "owner": "ops-warden", + "tags": [ + "ssh", + "certificate", + "ca", + "ops-warden", + "openbao", + "security", + "netkingdom", + "framework-reuse" + ], + "vector": "D4 / A3 / C3 / R2", + "path": "registry/capabilities/capability.security.ssh-certificate-issuance.md", + "consumption_modes": [ + "library", + "CLI" + ], + "source_repo": "ops-warden", + "source_url": "https://forgejo.coulomb.social/coulomb/ops-warden/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/ops-warden.yaml" + }, + { + "id": "capability.statehub.progress-log", + "name": "Progress and work evidence indexing", + "summary": "REST progress and task evidence references.", + "domain": "helix_forge", + "status": "draft", + "owner": "state-hub", + "tags": [ + "state-hub", + "progress", + "coordination", + "security", + "netkingdom", + "framework-reuse" + ], + "vector": "D4 / A4 / C3 / R3", + "path": "registry/capabilities/capability.statehub.progress-log.md", + "consumption_modes": [ + "HTTP API", + "source" + ], + "source_repo": "state-hub", + "source_url": "https://forgejo.coulomb.social/coulomb/state-hub/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/state-hub.yaml" + }, + { + "id": "capability.statehub.workstream-coordinate", + "name": "Cross-repository work tracking", + "summary": "File-derived workplans, tasks, dependencies, decisions and coordination views.", + "domain": "helix_forge", + "status": "draft", + "owner": "state-hub", + "tags": [ + "state-hub", + "coordination", + "workplans", + "security", + "netkingdom", + "framework-reuse" + ], + "vector": "D4 / A4 / C3 / R2", + "path": "registry/capabilities/capability.statehub.workstream-coordinate.md", + "consumption_modes": [ + "HTTP API", + "source" + ], + "source_repo": "state-hub", + "source_url": "https://forgejo.coulomb.social/coulomb/state-hub/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/state-hub.yaml" + }, + { + "id": "capability.wiki.adapter-contract", + "name": "Capability-Aware Shard Adapter Contract", + "summary": "A versioned backend interface where each binding declares a verified capability profile, so federation ops degrade by capability.", + "vector": "D5 / A2 / C2 / R1", + "domain": "helix_forge", + "status": "draft", + "owner": "shard-wiki", + "path": "registry/capabilities/capability.wiki.adapter-contract.md", + "tags": [ + "wiki", + "adapter", + "capability", + "contract", + "conformance", + "shard-wiki" + ], + "consumption_modes": [ + "source module" + ], + "source_repo": "shard-wiki", + "source_url": "https://forgejo.coulomb.social/coulomb/shard-wiki/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/shard-wiki.yaml" + }, + { + "id": "capability.wiki.coordination-journal", + "name": "Event-Sourced Coordination Journal", + "summary": "An append-only, totally-ordered-per-space decision log whose current state is a derived fold; git-addressable history.", + "vector": "D5 / A2 / C2 / R1", + "domain": "helix_forge", + "status": "draft", + "owner": "shard-wiki", + "path": "registry/capabilities/capability.wiki.coordination-journal.md", + "tags": [ + "wiki", + "event-sourcing", + "coordination", + "git", + "journal", + "shard-wiki" + ], + "consumption_modes": [ + "source module" + ], + "source_repo": "shard-wiki", + "source_url": "https://forgejo.coulomb.social/coulomb/shard-wiki/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/shard-wiki.yaml" + }, + { + "id": "capability.wiki.derived-views", + "name": "Wiki Derived Views", + "summary": "Recomputable views over a wiki union \u2014 BackLinks, RecentChanges, AllPages, SiteMap, and (delegate-or-derive) Search \u2014 carrying provenance.", + "vector": "D3 / A0 / C0 / R0", + "domain": "helix_forge", + "status": "draft", + "owner": "shard-wiki", + "path": "registry/capabilities/capability.wiki.derived-views.md", + "tags": [ + "wiki", + "derived-views", + "backlinks", + "recentchanges", + "search", + "shard-wiki" + ], + "consumption_modes": [ + "informational" + ], + "source_repo": "shard-wiki", + "source_url": "https://forgejo.coulomb.social/coulomb/shard-wiki/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/shard-wiki.yaml" + }, + { + "id": "capability.wiki.engine-typed-extensions", + "name": "Wiki Engine with Typed Extensions", + "summary": "A small-core wiki engine realizing a typed-extension framework that addresses all wiki use cases and lets each shard activate only the features it needs.", + "vector": "D3 / A0 / C0 / R0", + "domain": "helix_forge", + "status": "draft", + "owner": "shard-wiki", + "path": "registry/capabilities/capability.wiki.engine-typed-extensions.md", + "tags": [ + "wiki", + "engine", + "typed-extensions", + "feature-activation", + "shard-wiki" + ], + "consumption_modes": [ + "informational" + ], + "source_repo": "shard-wiki", + "source_url": "https://forgejo.coulomb.social/coulomb/shard-wiki/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/shard-wiki.yaml" + }, + { + "id": "capability.wiki.federation-models", + "name": "Selectable Federation-Model Taxonomy", + "summary": "Federation as a plural, composable coordination axis (fork+journal, VCS-replication, query-join, feed, activity-streams, engine-mirror) selected per space.", + "vector": "D4 / A0 / C1 / R0", + "domain": "helix_forge", + "status": "draft", + "owner": "shard-wiki", + "path": "registry/capabilities/capability.wiki.federation-models.md", + "tags": [ + "wiki", + "federation", + "taxonomy", + "composable", + "shard-wiki" + ], + "consumption_modes": [ + "informational" + ], + "source_repo": "shard-wiki", + "source_url": "https://forgejo.coulomb.social/coulomb/shard-wiki/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/shard-wiki.yaml" + }, + { + "id": "capability.wiki.overlay", + "name": "Overlay-Before-Mutation Write Path", + "summary": "Non-destructive edits (draft -> patch -> apply-under-drift) that let read-only or limited backends be edited safely without silent remote mutation.", + "vector": "D5 / A2 / C2 / R1", + "domain": "helix_forge", + "status": "draft", + "owner": "shard-wiki", + "path": "registry/capabilities/capability.wiki.overlay.md", + "tags": [ + "wiki", + "overlay", + "patch", + "write-path", + "conflict", + "shard-wiki" + ], + "consumption_modes": [ + "source module" + ], + "source_repo": "shard-wiki", + "source_url": "https://forgejo.coulomb.social/coulomb/shard-wiki/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/shard-wiki.yaml" + }, + { + "id": "capability.wiki.page-model", + "name": "Backend-Neutral Wiki Page Model", + "summary": "A Markdown-first but stretchable page model with stable identity separate from placement and layered provenance.", + "vector": "D5 / A2 / C2 / R1", + "domain": "helix_forge", + "status": "draft", + "owner": "shard-wiki", + "path": "registry/capabilities/capability.wiki.page-model.md", + "tags": [ + "wiki", + "page-model", + "identity", + "provenance", + "markdown", + "shard-wiki" + ], + "consumption_modes": [ + "source module" + ], + "source_repo": "shard-wiki", + "source_url": "https://forgejo.coulomb.social/coulomb/shard-wiki/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/shard-wiki.yaml" + }, + { + "id": "capability.wiki.shard-orchestration", + "name": "Wiki Shard Orchestration", + "summary": "Present a union of pages across heterogeneous wiki-shaped shards while preserving each shard's provenance, capabilities, and history.", + "vector": "D5 / A2 / C2 / R1", + "domain": "helix_forge", + "status": "draft", + "owner": "shard-wiki", + "path": "registry/capabilities/capability.wiki.shard-orchestration.md", + "tags": [ + "wiki", + "federation", + "orchestration", + "union", + "shard-wiki" + ], + "consumption_modes": [ + "source module" + ], + "source_repo": "shard-wiki", + "source_url": "https://forgejo.coulomb.social/coulomb/shard-wiki/raw/branch/main/registry/indexes/capabilities.yaml", + "source_index": "/data/cache/shard-wiki.yaml" + } + ], + "composed_at": "2026-10-14T18:17:44+00:00", + "stale": false +} diff --git a/docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/protected-backup/docs/assessments/netkingdom-2026-10-14/hub-private-source-pending.json b/docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/protected-backup/docs/assessments/netkingdom-2026-10-14/hub-private-source-pending.json new file mode 100644 index 00000000..3e9db88b --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/protected-backup/docs/assessments/netkingdom-2026-10-14/hub-private-source-pending.json @@ -0,0 +1,105 @@ +{ + "reason": "Owner disclosure consent recorded for all seven exact source revisions; restricted hub-side reader, independent technical reviews and native authenticated index/entry fetch verification remain pending. Repositories stay private.", + "repos": [ + "rapp-openbao", + "rapp-policy-nexus", + "rapp-postgres", + "rapp-sbom-nexus", + "rapp-telemetry", + "rapp-tenant-engine", + "rapp-user-engine" + ], + "draft_registrations_not_ready": [ + { + "method": "POST", + "path": "/v1/repos", + "body": { + "repo": "rapp-openbao", + "url": "https://forgejo.coulomb.social/coulomb/rapp-openbao/raw/branch/main/registry/indexes/capabilities.yaml", + "domain": "infotech", + "enabled": true, + "required": false, + "registered_by": "codex-CUST-WP-0081" + } + }, + { + "method": "POST", + "path": "/v1/repos", + "body": { + "repo": "rapp-policy-nexus", + "url": "https://forgejo.coulomb.social/coulomb/rapp-policy-nexus/raw/branch/main/registry/indexes/capabilities.yaml", + "domain": "infotech", + "enabled": true, + "required": false, + "registered_by": "codex-CUST-WP-0081" + } + }, + { + "method": "POST", + "path": "/v1/repos", + "body": { + "repo": "rapp-postgres", + "url": "https://forgejo.coulomb.social/coulomb/rapp-postgres/raw/branch/main/registry/indexes/capabilities.yaml", + "domain": "infotech", + "enabled": true, + "required": false, + "registered_by": "codex-CUST-WP-0081" + } + }, + { + "method": "POST", + "path": "/v1/repos", + "body": { + "repo": "rapp-telemetry", + "url": "https://forgejo.coulomb.social/coulomb/rapp-telemetry/raw/branch/main/registry/indexes/capabilities.yaml", + "domain": "infotech", + "enabled": true, + "required": false, + "registered_by": "codex-CUST-WP-0081" + } + }, + { + "method": "POST", + "path": "/v1/repos", + "body": { + "repo": "rapp-tenant-engine", + "url": "https://forgejo.coulomb.social/coulomb/rapp-tenant-engine/raw/branch/main/registry/indexes/capabilities.yaml", + "domain": "infotech", + "enabled": true, + "required": false, + "registered_by": "codex-CUST-WP-0081" + } + }, + { + "method": "POST", + "path": "/v1/repos", + "body": { + "repo": "rapp-user-engine", + "url": "https://forgejo.coulomb.social/coulomb/rapp-user-engine/raw/branch/main/registry/indexes/capabilities.yaml", + "domain": "infotech", + "enabled": true, + "required": false, + "registered_by": "codex-CUST-WP-0081" + } + }, + { + "method": "POST", + "path": "/v1/repos", + "body": { + "repo": "rapp-sbom-nexus", + "url": "https://forgejo.coulomb.social/coulomb/rapp-sbom-nexus/raw/branch/main/registry/indexes/capabilities.yaml", + "domain": "infotech", + "enabled": true, + "required": false, + "registered_by": "codex-CUST-WP-0081" + } + } + ], + "needed_fields": [ + "auth_env name for an already admitted hub-side source-read credential", + "proof that the credential is limited to the intended repository read scope", + "successful hub-side index fetch without value disclosure" + ], + "consent_receipt": "private-source-owner-consent-r1.json", + "registration_preparation": "hub-private-registration-preparation-r1.json" +} diff --git a/docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/protected-backup/docs/assessments/netkingdom-2026-10-14/owner-access-gate.json b/docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/protected-backup/docs/assessments/netkingdom-2026-10-14/owner-access-gate.json new file mode 100644 index 00000000..fd27ebd4 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/protected-backup/docs/assessments/netkingdom-2026-10-14/owner-access-gate.json @@ -0,0 +1,18 @@ +{ + "workplan_id": "e8992815-389a-5c40-8569-a2b0ee3945f0", + "id": "5cd22087-e5d8-5d5e-a252-610a6ac13653", + "topic_id": "cee7bedf-2b48-46ef-8601-006474f2ad7a", + "title": "Complete scoped owner provisioning for reuse-surface discovery", + "description": "Execution/evidence gate for CUST-WP-0081-T02. Public batch applied and verified (63/70 hosted). Founder metadata disclosure consent for all seven private source revisions recorded separately as decision d8f8f216-ac94-4295-97f3-825fd21254ea; do not request it again. Remaining: independent technical reviews, restricted hub-only repository reader through railiance-platform reviewed custody/ESO delivery, sanitized auth_env and native fetch receipts, seven registrations and final all-70 audit. This gate stays open until verified provisioning; metadata consent is not a credential or rollout grant. See docs/assessments/netkingdom-2026-10-14/private-source-owner-consent-r1.json and scoped-access-handoff.json.", + "decision_type": "pending", + "status": "open", + "rationale": "All 70 owner records published; 63/70 verified hosted. Owner disclosure consent satisfied; seven-source reader and native fetch evidence pending.", + "decided_by": null, + "decided_at": null, + "deadline": null, + "escalation_note": null, + "superseded_by": null, + "created_at": "2026-10-14T07:51:34.426143Z", + "updated_at": "2026-10-14T22:31:45.188478Z", + "workstream_id": "e8992815-389a-5c40-8569-a2b0ee3945f0" +} diff --git a/docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/protected-backup/docs/assessments/netkingdom-2026-10-14/publication-preflight.json b/docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/protected-backup/docs/assessments/netkingdom-2026-10-14/publication-preflight.json new file mode 100644 index 00000000..b279dca2 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/protected-backup/docs/assessments/netkingdom-2026-10-14/publication-preflight.json @@ -0,0 +1,546 @@ +[ + { + "repo": "access-engine", + "branch": "main", + "local_head": "62ac2b4e99dec2005c7c407fe633612578a7907d", + "remote_head": "62ac2b4e99dec2005c7c407fe633612578a7907d", + "same_head": true, + "remote_read_exit": 0, + "staged_paths": [] + }, + { + "repo": "activity-core", + "branch": "main", + "local_head": "f9f8df039258a0d8b6fc29dc1f7c4bcc75fdc8c0", + "remote_head": "f9f8df039258a0d8b6fc29dc1f7c4bcc75fdc8c0", + "same_head": true, + "remote_read_exit": 0, + "staged_paths": [] + }, + { + "repo": "agentic-resources", + "branch": "main", + "local_head": "c4cedab945e047de068428fb943f346f093b22dc", + "remote_head": "c4cedab945e047de068428fb943f346f093b22dc", + "same_head": true, + "remote_read_exit": 0, + "staged_paths": [] + }, + { + "repo": "approval-engine", + "branch": "main", + "local_head": "db850aded5fc3b5d35938d26e8084e436a83943b", + "remote_head": "db850aded5fc3b5d35938d26e8084e436a83943b", + "same_head": true, + "remote_read_exit": 0, + "staged_paths": [] + }, + { + "repo": "audit-core", + "branch": "main", + "local_head": "8d4bde6ece39ce0df3a3cc49da38613123e8f215", + "remote_head": "8d4bde6ece39ce0df3a3cc49da38613123e8f215", + "same_head": true, + "remote_read_exit": 0, + "staged_paths": [] + }, + { + "repo": "coordination-engine", + "branch": "main", + "local_head": "16de8fc3f70020df646f1ebdca705a1bf88485a0", + "remote_head": "16de8fc3f70020df646f1ebdca705a1bf88485a0", + "same_head": true, + "remote_read_exit": 0, + "staged_paths": [] + }, + { + "repo": "emergent-security", + "branch": "main", + "local_head": "6eead2a922ae2eb81593213f54bf1f35e1c9970f", + "remote_head": "6eead2a922ae2eb81593213f54bf1f35e1c9970f", + "same_head": true, + "remote_read_exit": 0, + "staged_paths": [] + }, + { + "repo": "fin-hub", + "branch": "main", + "local_head": "cace0fda7474304cc42028287f7ba1327ab07d00", + "remote_head": "cace0fda7474304cc42028287f7ba1327ab07d00", + "same_head": true, + "remote_read_exit": 0, + "staged_paths": [] + }, + { + "repo": "gate-house", + "branch": "main", + "local_head": "ab1a299674679ccfefa8df84b877614ac44c9d38", + "remote_head": "ab1a299674679ccfefa8df84b877614ac44c9d38", + "same_head": true, + "remote_read_exit": 0, + "staged_paths": [] + }, + { + "repo": "glas-harness", + "branch": "main", + "local_head": "dc88f302044c7c2b41795958527e64be8a83e3ab", + "remote_head": "dc88f302044c7c2b41795958527e64be8a83e3ab", + "same_head": true, + "remote_read_exit": 0, + "staged_paths": [ + ".custodian-brief.md" + ] + }, + { + "repo": "info-tech-canon", + "branch": "main", + "local_head": "9b9be2ec8e846ceaa05fa644323b02d8603e39b6", + "remote_head": "9b9be2ec8e846ceaa05fa644323b02d8603e39b6", + "same_head": true, + "remote_read_exit": 0, + "staged_paths": [] + }, + { + "repo": "informed-decision", + "branch": "main", + "local_head": "faae9cb0dbaf4c8d23f838183f513c302ad3b9f0", + "remote_head": "faae9cb0dbaf4c8d23f838183f513c302ad3b9f0", + "same_head": true, + "remote_read_exit": 0, + "staged_paths": [ + ".custodian-brief.md" + ] + }, + { + "repo": "issue-core", + "branch": "main", + "local_head": "1427d268a09b9334a98a167230ec7dd1d4a82053", + "remote_head": "1427d268a09b9334a98a167230ec7dd1d4a82053", + "same_head": true, + "remote_read_exit": 0, + "staged_paths": [] + }, + { + "repo": "key-cape", + "branch": "main", + "local_head": "8c6c071ad9c73865793a075ed1b34b93e5075436", + "remote_head": "44b243410006a2df32adcd4cae2ab5e6f806da82", + "same_head": false, + "remote_read_exit": 0, + "staged_paths": [] + }, + { + "repo": "kings-guard", + "branch": "main", + "local_head": "6aea834dc556067672f31ed762f44a39bad988e3", + "remote_head": "6aea834dc556067672f31ed762f44a39bad988e3", + "same_head": true, + "remote_read_exit": 0, + "staged_paths": [] + }, + { + "repo": "maturity-engine", + "branch": "main", + "local_head": "2f1de542cc8f24a79452f906d99f95be39d8b6a9", + "remote_head": "363e8160a4910766e9380fabee68a8642dd45ccf", + "same_head": false, + "remote_read_exit": 0, + "staged_paths": [] + }, + { + "repo": "net-kingdom", + "branch": "main", + "local_head": "2cc09ceb05c6f2132c6fc3478901c4ee1085fa1b", + "remote_head": "8f0a5902004c47415846d818e400eb5d5e1e037d", + "same_head": false, + "remote_read_exit": 0, + "staged_paths": [] + }, + { + "repo": "ops-bridge", + "branch": "main", + "local_head": "2246656f43fa5eae3ae917f63ea1dfa3f53f4a00", + "remote_head": "2246656f43fa5eae3ae917f63ea1dfa3f53f4a00", + "same_head": true, + "remote_read_exit": 0, + "staged_paths": [] + }, + { + "repo": "ops-mason", + "branch": "main", + "local_head": "97d1fed9496aacb1836982096b8aecb6da13cf0d", + "remote_head": "97d1fed9496aacb1836982096b8aecb6da13cf0d", + "same_head": true, + "remote_read_exit": 0, + "staged_paths": [ + ".custodian-brief.md" + ] + }, + { + "repo": "ops-warden", + "branch": "main", + "local_head": "1baeb0821cb6304464fab78932d8c6501f8c4cc6", + "remote_head": "1baeb0821cb6304464fab78932d8c6501f8c4cc6", + "same_head": true, + "remote_read_exit": 0, + "staged_paths": [] + }, + { + "repo": "policy-nexus", + "branch": "main", + "local_head": "c14561b2e5318e9637ba93e4c96c06a2db92d0c4", + "remote_head": "c14561b2e5318e9637ba93e4c96c06a2db92d0c4", + "same_head": true, + "remote_read_exit": 0, + "staged_paths": [] + }, + { + "repo": "rail-kubernetes", + "branch": "main", + "local_head": "5fad47fdfb7d4cabc21cf10f52ac733ef59803b0", + "remote_head": "5fad47fdfb7d4cabc21cf10f52ac733ef59803b0", + "same_head": true, + "remote_read_exit": 0, + "staged_paths": [] + }, + { + "repo": "railiance-apps", + "branch": "main", + "local_head": "72bcad86d7531bcc27f0e465a9385be559ba0c9f", + "remote_head": "72bcad86d7531bcc27f0e465a9385be559ba0c9f", + "same_head": true, + "remote_read_exit": 0, + "staged_paths": [ + ".custodian-brief.md" + ] + }, + { + "repo": "railiance-bootstrap", + "branch": "main", + "local_head": "70bd14d3b941a49c78bbc51d8faab5ab1da1df76", + "remote_head": "70bd14d3b941a49c78bbc51d8faab5ab1da1df76", + "same_head": true, + "remote_read_exit": 0, + "staged_paths": [] + }, + { + "repo": "railiance-cluster", + "branch": "main", + "local_head": "750c3e5b9b4623c52bce2b8fbd758f423f81f6c1", + "remote_head": "750c3e5b9b4623c52bce2b8fbd758f423f81f6c1", + "same_head": true, + "remote_read_exit": 0, + "staged_paths": [] + }, + { + "repo": "railiance-enablement", + "branch": "main", + "local_head": "ce9f6d1be8ff73afdedd6d666a8485e6c7348069", + "remote_head": "ce9f6d1be8ff73afdedd6d666a8485e6c7348069", + "same_head": true, + "remote_read_exit": 0, + "staged_paths": [] + }, + { + "repo": "railiance-fabric", + "branch": "main", + "local_head": "095342c72ab6f02c72dd5d40e869dcba536bc5c1", + "remote_head": "095342c72ab6f02c72dd5d40e869dcba536bc5c1", + "same_head": true, + "remote_read_exit": 0, + "staged_paths": [] + }, + { + "repo": "railiance-forge", + "branch": "main", + "local_head": "6e8f657e55e96b4e9f3d9d74a27b420b68467e61", + "remote_head": "6e8f657e55e96b4e9f3d9d74a27b420b68467e61", + "same_head": true, + "remote_read_exit": 0, + "staged_paths": [ + ".custodian-brief.md" + ] + }, + { + "repo": "railiance-infra", + "branch": "main", + "local_head": "32f64b54fd94a5e6a145ef7c2c8faf051f721a71", + "remote_head": "99bc66f8c98a5404339074bf5bb11271d9f6d58d", + "same_head": false, + "remote_read_exit": 0, + "staged_paths": [] + }, + { + "repo": "railiance-master", + "branch": "main", + "local_head": "a0be0d3142b5392f1fb6271cf91325ce91a0f9d4", + "remote_head": "a0be0d3142b5392f1fb6271cf91325ce91a0f9d4", + "same_head": true, + "remote_read_exit": 0, + "staged_paths": [] + }, + { + "repo": "railiance-platform", + "branch": "main", + "local_head": "0f0282a43646edbb5743318bc923749af1c0cea2", + "remote_head": "b797594a21fb0969ec987e633521245e0277267b", + "same_head": false, + "remote_read_exit": 0, + "staged_paths": [] + }, + { + "repo": "railiance-telemetry", + "branch": "main", + "local_head": "8cdb2d11dda03bd6839750d059b37932f069e66a", + "remote_head": "8cdb2d11dda03bd6839750d059b37932f069e66a", + "same_head": true, + "remote_read_exit": 0, + "staged_paths": [] + }, + { + "repo": "rapp-openbao", + "branch": "main", + "local_head": "35672be138036c8433a5ac5de91e068c03c2b124", + "remote_head": "35672be138036c8433a5ac5de91e068c03c2b124", + "same_head": true, + "remote_read_exit": 0, + "staged_paths": [] + }, + { + "repo": "rapp-playwright", + "branch": "main", + "local_head": "4f76a87f67fdd9dab19564bb84235397e2f2470a", + "remote_head": "4f76a87f67fdd9dab19564bb84235397e2f2470a", + "same_head": true, + "remote_read_exit": 0, + "staged_paths": [] + }, + { + "repo": "rapp-policy-nexus", + "branch": "main", + "local_head": "088b78ac03b0cebeec34a69aae3569eb7ec2f9c3", + "remote_head": "088b78ac03b0cebeec34a69aae3569eb7ec2f9c3", + "same_head": true, + "remote_read_exit": 0, + "staged_paths": [] + }, + { + "repo": "rapp-postgres", + "branch": "main", + "local_head": "a9f569c96d74aa384ac8d3ac4aa960e4061550b5", + "remote_head": "a9f569c96d74aa384ac8d3ac4aa960e4061550b5", + "same_head": true, + "remote_read_exit": 0, + "staged_paths": [] + }, + { + "repo": "rapp-telemetry", + "branch": "main", + "local_head": "f158307e24b86bab88a1af4830ff6b4f85afdeb5", + "remote_head": "f158307e24b86bab88a1af4830ff6b4f85afdeb5", + "same_head": true, + "remote_read_exit": 0, + "staged_paths": [] + }, + { + "repo": "rapp-tenant-engine", + "branch": "main", + "local_head": "9bdc86572728783a5ed94266f04f7b12d414c557", + "remote_head": "9bdc86572728783a5ed94266f04f7b12d414c557", + "same_head": true, + "remote_read_exit": 0, + "staged_paths": [] + }, + { + "repo": "rapp-user-engine", + "branch": "main", + "local_head": "f35108f9e1b8702fb6c526fe42b2fec5ca032738", + "remote_head": "f35108f9e1b8702fb6c526fe42b2fec5ca032738", + "same_head": true, + "remote_read_exit": 0, + "staged_paths": [ + ".custodian-brief.md" + ] + }, + { + "repo": "reef-railiance", + "branch": "main", + "local_head": "33b840faac109f134bf1115d631d5b1242052d9a", + "remote_head": "33b840faac109f134bf1115d631d5b1242052d9a", + "same_head": true, + "remote_read_exit": 0, + "staged_paths": [] + }, + { + "repo": "rein-aharness", + "branch": "main", + "local_head": "a58d11ce8ae9ea07baf0682c4f860caa251692b3", + "remote_head": "a58d11ce8ae9ea07baf0682c4f860caa251692b3", + "same_head": true, + "remote_read_exit": 0, + "staged_paths": [ + ".custodian-brief.md" + ] + }, + { + "repo": "rein-openweights", + "branch": "main", + "local_head": "a2e428a76592d79982ea3c4eba5f137a12fb44e4", + "remote_head": "a2e428a76592d79982ea3c4eba5f137a12fb44e4", + "same_head": true, + "remote_read_exit": 0, + "staged_paths": [] + }, + { + "repo": "repo-manager", + "branch": "main", + "local_head": "97ea2489257de08911cc5da1e1b84d8e15780dc3", + "remote_head": "97ea2489257de08911cc5da1e1b84d8e15780dc3", + "same_head": true, + "remote_read_exit": 0, + "staged_paths": [] + }, + { + "repo": "resource-control", + "branch": "main", + "local_head": "ac4e1c43e6d82ecd81108f3b006454ac6aa76722", + "remote_head": "ac4e1c43e6d82ecd81108f3b006454ac6aa76722", + "same_head": true, + "remote_read_exit": 0, + "staged_paths": [] + }, + { + "repo": "reuse-surface", + "branch": "main", + "local_head": "03cd049773478e9de7e38035a49deaa82772a357", + "remote_head": "03cd049773478e9de7e38035a49deaa82772a357", + "same_head": true, + "remote_read_exit": 0, + "staged_paths": [] + }, + { + "repo": "risk-nexus", + "branch": "main", + "local_head": "0cb565e1e498002b0fe71975e7654b641a4c24d2", + "remote_head": "0cb565e1e498002b0fe71975e7654b641a4c24d2", + "same_head": true, + "remote_read_exit": 0, + "staged_paths": [] + }, + { + "repo": "role-engine", + "branch": "main", + "local_head": "c9d6d0763542f632f1d9b799d63b6b85cf8eaaab", + "remote_head": "c9d6d0763542f632f1d9b799d63b6b85cf8eaaab", + "same_head": true, + "remote_read_exit": 0, + "staged_paths": [] + }, + { + "repo": "runbook-tutorials", + "branch": "main", + "local_head": "f0ea260895e8ad8e60f24bf64a89be063b7f8e13", + "remote_head": "f0ea260895e8ad8e60f24bf64a89be063b7f8e13", + "same_head": true, + "remote_read_exit": 0, + "staged_paths": [] + }, + { + "repo": "sand-boxer", + "branch": "main", + "local_head": "7ddf92070f2fa267d79f8cad0aa07d16f0cd6410", + "remote_head": "7ddf92070f2fa267d79f8cad0aa07d16f0cd6410", + "same_head": true, + "remote_read_exit": 0, + "staged_paths": [ + ".custodian-brief.md" + ] + }, + { + "repo": "sbom-nexus", + "branch": "main", + "local_head": "db76194330b9502e9a1085ae25cdd27ce264ec7b", + "remote_head": "db76194330b9502e9a1085ae25cdd27ce264ec7b", + "same_head": true, + "remote_read_exit": 0, + "staged_paths": [] + }, + { + "repo": "secrets-engine", + "branch": "main", + "local_head": "6bd7129eac94131b4cf689e47cbdc9899418beb3", + "remote_head": "6bd7129eac94131b4cf689e47cbdc9899418beb3", + "same_head": true, + "remote_read_exit": 0, + "staged_paths": [ + ".custodian-brief.md" + ] + }, + { + "repo": "security-canon", + "branch": "main", + "local_head": "c31c3008a53bcff07be09d0507d4693d27b40a5a", + "remote_head": "c31c3008a53bcff07be09d0507d4693d27b40a5a", + "same_head": true, + "remote_read_exit": 0, + "staged_paths": [] + }, + { + "repo": "state-hub", + "branch": "main", + "local_head": "2a2ac2eb195a073633f4db0f166b927ca1877d67", + "remote_head": "2a2ac2eb195a073633f4db0f166b927ca1877d67", + "same_head": true, + "remote_read_exit": 0, + "staged_paths": [ + ".custodian-brief.md" + ] + }, + { + "repo": "tenant-engine", + "branch": "main", + "local_head": "79a61905957bd78d3f4c93da42d7034ad3e3c8de", + "remote_head": "79a61905957bd78d3f4c93da42d7034ad3e3c8de", + "same_head": true, + "remote_read_exit": 0, + "staged_paths": [] + }, + { + "repo": "the-custodian", + "branch": "main", + "local_head": "0e7bcd128bb80d3630d84c2ba1bedf88578ccde1", + "remote_head": "0e7bcd128bb80d3630d84c2ba1bedf88578ccde1", + "same_head": true, + "remote_read_exit": 0, + "staged_paths": [ + ".custodian-brief.md" + ] + }, + { + "repo": "user-engine", + "branch": "main", + "local_head": "e9534ef00f02f2b4772cd80f3b7c7aad5f9dae23", + "remote_head": "e9534ef00f02f2b4772cd80f3b7c7aad5f9dae23", + "same_head": true, + "remote_read_exit": 0, + "staged_paths": [] + }, + { + "repo": "whitehat-security", + "branch": "main", + "local_head": "42fe7a12c7d9d64142ec0e820ba4e975ffeac593", + "remote_head": "42fe7a12c7d9d64142ec0e820ba4e975ffeac593", + "same_head": true, + "remote_read_exit": 0, + "staged_paths": [] + }, + { + "repo": "zone-engine", + "branch": "main", + "local_head": "7e7e3f307ca996c373e91009ed1b9b0cbc6fcb9e", + "remote_head": "7e7e3f307ca996c373e91009ed1b9b0cbc6fcb9e", + "same_head": true, + "remote_read_exit": 0, + "staged_paths": [] + } +] diff --git a/docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/protected-backup/docs/assessments/netkingdom-2026-10-14/published-source-verification.json b/docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/protected-backup/docs/assessments/netkingdom-2026-10-14/published-source-verification.json new file mode 100644 index 00000000..61dafc03 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/protected-backup/docs/assessments/netkingdom-2026-10-14/published-source-verification.json @@ -0,0 +1,1194 @@ +{ + "checked_repositories": 60, + "checked_capabilities": 70, + "errors": [ + { + "repo": "rapp-openbao", + "errors": [ + "registry/indexes/capabilities.yaml: HTTPError", + "registry/capabilities/capability.railiance.openbao-package.md: HTTPError", + "Stable URL: HTTPError" + ] + }, + { + "repo": "rapp-policy-nexus", + "errors": [ + "registry/indexes/capabilities.yaml: HTTPError", + "registry/capabilities/capability.railiance.policy-publication-package.md: HTTPError", + "Stable URL: HTTPError" + ] + }, + { + "repo": "rapp-postgres", + "errors": [ + "registry/indexes/capabilities.yaml: HTTPError", + "registry/capabilities/capability.railiance.postgres-isolation-recovery.md: HTTPError", + "Stable URL: HTTPError" + ] + }, + { + "repo": "rapp-telemetry", + "errors": [ + "registry/indexes/capabilities.yaml: HTTPError", + "registry/capabilities/capability.railiance.telemetry-package.md: HTTPError", + "Stable URL: HTTPError" + ] + }, + { + "repo": "rapp-tenant-engine", + "errors": [ + "registry/indexes/capabilities.yaml: HTTPError", + "registry/capabilities/capability.railiance.tenant-engine-package.md: HTTPError", + "Stable URL: HTTPError" + ] + }, + { + "repo": "rapp-user-engine", + "errors": [ + "registry/indexes/capabilities.yaml: HTTPError", + "registry/capabilities/capability.railiance.user-engine-package.md: HTTPError", + "Stable URL: HTTPError" + ] + }, + { + "repo": "rapp-sbom-nexus", + "errors": [ + "registry/indexes/capabilities.yaml: HTTPError", + "registry/capabilities/capability.railiance.sbom-runtime-package.md: HTTPError", + "Stable URL: HTTPError" + ] + } + ], + "results": [ + { + "repo": "access-engine", + "files": [ + { + "path": "registry/indexes/capabilities.yaml", + "http": 200, + "sha256": "7d59022dac9da4d8dcbecab936d3cea9b154c8c51478b378932fd9ebe5ec7e02", + "matches_local": true + }, + { + "path": "registry/capabilities/capability.authorization.policy-evaluate.md", + "http": 200, + "sha256": "a209848f31635e4637b481fc4fefca2331957ebb89b0ae2198b90affeae247fc", + "matches_local": true + } + ], + "errors": [], + "stable_index_matches": true + }, + { + "repo": "activity-core", + "files": [ + { + "path": "registry/indexes/capabilities.yaml", + "http": 200, + "sha256": "536d359366ccf3db3b2b0d390635bc2c5311bd37ac7ff89dae11d809ef15b848", + "matches_local": true + }, + { + "path": "registry/capabilities/capability.activity.event-coordinate.md", + "http": 200, + "sha256": "72f5366d6eae0a26568ad62ea646b928ac96adaa7fcb864fb9934d405475f03b", + "matches_local": true + } + ], + "errors": [], + "stable_index_matches": true + }, + { + "repo": "agentic-resources", + "files": [ + { + "path": "registry/indexes/capabilities.yaml", + "http": 200, + "sha256": "42986b7a274ef510166e566a9756d594a6f88706189bc97ac889bab86b20240d", + "matches_local": true + }, + { + "path": "registry/capabilities/capability.agents.performance-learning.md", + "http": 200, + "sha256": "2d9988711c84968dc86a9149caed87f09362916d23b82cd28d3f7ad947378e8b", + "matches_local": true + } + ], + "errors": [], + "stable_index_matches": true + }, + { + "repo": "approval-engine", + "files": [ + { + "path": "registry/indexes/capabilities.yaml", + "http": 200, + "sha256": "b6812fd12dc530270fb66bae592c828f56f17f2fc86759cb04d750162fc619a4", + "matches_local": true + }, + { + "path": "registry/capabilities/capability.security.approval-lifecycle.md", + "http": 200, + "sha256": "bb1e47b94183c41d45c2dccb7ae5bc1f7bc98ad3f0ef5c4ee6d0cd1c5b5b06bb", + "matches_local": true + } + ], + "errors": [], + "stable_index_matches": true + }, + { + "repo": "audit-core", + "files": [ + { + "path": "registry/indexes/capabilities.yaml", + "http": 200, + "sha256": "6a1d7dbb442f0fd0e481d54fb0acbee37ef2e693198b6f2108e154d3c3795e0e", + "matches_local": true + }, + { + "path": "registry/capabilities/capability.audit.event-retain.md", + "http": 200, + "sha256": "88f58dc61aef8746124827f47eac5dd20ec5761e11551a864a3c5d5a7f28f172", + "matches_local": true + } + ], + "errors": [], + "stable_index_matches": true + }, + { + "repo": "coordination-engine", + "files": [ + { + "path": "registry/indexes/capabilities.yaml", + "http": 200, + "sha256": "f7a2c140ac1a7628a2106b1047b59e208879257b23ffed6dceef8ecf6c3bd420", + "matches_local": true + }, + { + "path": "registry/capabilities/capability.coordination.cross-owner-work.md", + "http": 200, + "sha256": "f4a7a4ee2bf689056eb02d647ae97a94dd1a16462180fbed6762a006983fe7ae", + "matches_local": true + } + ], + "errors": [], + "stable_index_matches": true + }, + { + "repo": "emergent-security", + "files": [ + { + "path": "registry/indexes/capabilities.yaml", + "http": 200, + "sha256": "268d9192dc9b97da462d46fe5b40ea33e678840b23944ab0cd73622c130bdef6", + "matches_local": true + }, + { + "path": "registry/capabilities/capability.security.agent-knowledge.md", + "http": 200, + "sha256": "66bdfd445f0292c0b9fec6999c3d7ca6134a2a1b1f781a6fc6cfa4bd62baa34c", + "matches_local": true + } + ], + "errors": [], + "stable_index_matches": true + }, + { + "repo": "fin-hub", + "files": [ + { + "path": "registry/indexes/capabilities.yaml", + "http": 200, + "sha256": "c6bb1aa6b770f684721fae9f1576f2c2f0128d1b758f4e33b8a277e0b4ff087a", + "matches_local": true + }, + { + "path": "registry/capabilities/capability.finance.security-budget-evidence.md", + "http": 200, + "sha256": "314525b624e290bffb3817f3fe0e4a5f6d729ac93d1be485fa06fbe8896d0576", + "matches_local": true + } + ], + "errors": [], + "stable_index_matches": true + }, + { + "repo": "gate-house", + "files": [ + { + "path": "registry/indexes/capabilities.yaml", + "http": 200, + "sha256": "2b63d94375d29649c41a7eae7cfb3f6e35210fd3438aa5013f3ddc22a662511a", + "matches_local": true + }, + { + "path": "registry/capabilities/capability.security.doctrine-review.md", + "http": 200, + "sha256": "55964589de53f6b07c184b0d8e18e511fbc870ceff0f5f28fa2b015b97568d26", + "matches_local": true + } + ], + "errors": [], + "stable_index_matches": true + }, + { + "repo": "glas-harness", + "files": [ + { + "path": "registry/indexes/capabilities.yaml", + "http": 200, + "sha256": "8f86dbdaf6535c909ff19967f7806d85c1f7a09db1e999033abf9b9007ed805b", + "matches_local": true + }, + { + "path": "registry/capabilities/capability.execution.profile-harness.md", + "http": 200, + "sha256": "6eea0a69ea80236bdd30379b189eb1c1e8b50d95e89eac8b43ec8fbf51ad659f", + "matches_local": true + } + ], + "errors": [], + "stable_index_matches": true + }, + { + "repo": "info-tech-canon", + "files": [ + { + "path": "registry/indexes/capabilities.yaml", + "http": 200, + "sha256": "6015ec78772586e6e351422741430be8c40d7c7116403ee4b92933df342c4c72", + "matches_local": true + }, + { + "path": "registry/capabilities/capability.infotech.canon-service.md", + "http": 200, + "sha256": "db9721777788773026d646462fad03df04cf3d1846a0ebf5d1cf1a809ea9f945", + "matches_local": true + } + ], + "errors": [], + "stable_index_matches": true + }, + { + "repo": "informed-decision", + "files": [ + { + "path": "registry/indexes/capabilities.yaml", + "http": 200, + "sha256": "b43ea44c725dcbd59fe957ffc307805e0aabc23b60a4a9049005c70ca4871a62", + "matches_local": true + }, + { + "path": "registry/capabilities/capability.security.human-decision-workspace.md", + "http": 200, + "sha256": "d64c27fcfe13ffcf0de9db7a916044f93d45b36a1dff26486efa3ac0f5ca4fc1", + "matches_local": true + } + ], + "errors": [], + "stable_index_matches": true + }, + { + "repo": "issue-core", + "files": [ + { + "path": "registry/indexes/capabilities.yaml", + "http": 200, + "sha256": "70eb00870ed87072aeb0f7c38de0a879f1b3910fe6f976b982a191e261e8e7be", + "matches_local": true + }, + { + "path": "registry/capabilities/capability.infotech.issue-tracking.md", + "http": 200, + "sha256": "60282cc3b4ab58a9df621b895bfd1e7b485d6b7ef47bde11f7d339907aeb262b", + "matches_local": true + } + ], + "errors": [], + "stable_index_matches": true + }, + { + "repo": "key-cape", + "files": [ + { + "path": "registry/indexes/capabilities.yaml", + "http": 200, + "sha256": "901d15c15cacd844cf19a1a7b74c890ce5e0b00a3372a9e10bd7114f328596c8", + "matches_local": true + }, + { + "path": "registry/capabilities/capability.iam.key-cape.md", + "http": 200, + "sha256": "c987ddd0155c248e35409878e9b2dc36ffd18bbd1b82fce8a90157fc4224a915", + "matches_local": true + } + ], + "errors": [], + "stable_index_matches": true + }, + { + "repo": "kings-guard", + "files": [ + { + "path": "registry/indexes/capabilities.yaml", + "http": 200, + "sha256": "8a143864a6ce9a7b6c5a0f64eb75f67cfa99c2b9b155baac4f81a261d3a5312c", + "matches_local": true + }, + { + "path": "registry/capabilities/capability.security.posture-assessment.md", + "http": 200, + "sha256": "e6bb4704a9e008dc8bbe8a2b14be880c8b8122ace4f2828b3fbee6ecf2583a89", + "matches_local": true + } + ], + "errors": [], + "stable_index_matches": true + }, + { + "repo": "maturity-engine", + "files": [ + { + "path": "registry/indexes/capabilities.yaml", + "http": 200, + "sha256": "42b4cd1f9c6fe0960acadd10a337a579a6313fa46c2ff5a2a34bbacf2749d049", + "matches_local": true + }, + { + "path": "registry/capabilities/capability.security.maturity-claims.md", + "http": 200, + "sha256": "498736f3da2eaa4e1a3f9f223820eb630a8fa72be4a9c1423dc7c1722b55c28f", + "matches_local": true + } + ], + "errors": [], + "stable_index_matches": true + }, + { + "repo": "net-kingdom", + "files": [ + { + "path": "registry/indexes/capabilities.yaml", + "http": 200, + "sha256": "6fadeb0e89729a9226d6eebd44fbb07723512a3ebe6737f5a1113b14829447ed", + "matches_local": true + }, + { + "path": "registry/capabilities/capability.security.iam-tooling-suite.md", + "http": 200, + "sha256": "235773e0913ec2f2cb4b8d5ac09839851bf9af7c7c5cb38b5207a3305e2eaf15", + "matches_local": true + }, + { + "path": "registry/capabilities/capability.security.scenario-compose.md", + "http": 200, + "sha256": "e9bab551d4ca5afac63cc70b59a783e6da96a1a361cc713fab31dbff604d351f", + "matches_local": true + }, + { + "path": "registry/capabilities/capability.security.posture-feedback.md", + "http": 200, + "sha256": "470ffa52b5da01ee6562de7654503da592c456c80eaa4a1b623d92594cfbb00f", + "matches_local": true + }, + { + "path": "registry/capabilities/capability.security.case-contracts.md", + "http": 200, + "sha256": "6880ee826febb8cb6da0c804ec59558fffbcaa22e3893065a2d9f2bb1d6b6732", + "matches_local": true + }, + { + "path": "registry/capabilities/capability.security.bootstrap-recovery.md", + "http": 200, + "sha256": "f98058ad567bc1fc8f0f8b5ae9c337a93bec87434ec3b0060488ae8335f5dd50", + "matches_local": true + }, + { + "path": "registry/capabilities/capability.security.emission-profile.md", + "http": 200, + "sha256": "575b651fe421659594deef4381bff6f20ff09acffd4f68e36dfd18060742aaed", + "matches_local": true + } + ], + "errors": [], + "stable_index_matches": true + }, + { + "repo": "ops-bridge", + "files": [ + { + "path": "registry/indexes/capabilities.yaml", + "http": 200, + "sha256": "aefd795eb16fcfdebc108450826a0f62e000bc61e9d78007bc6221bc2328e40c", + "matches_local": true + }, + { + "path": "registry/capabilities/capability.ops.tunnel-bridge.md", + "http": 200, + "sha256": "1c3a43c90650adb730e86e3aa8df885e0e87499a0228bfb88a31eac699028af9", + "matches_local": true + } + ], + "errors": [], + "stable_index_matches": true + }, + { + "repo": "ops-mason", + "files": [ + { + "path": "registry/indexes/capabilities.yaml", + "http": 200, + "sha256": "5eb499bc819c874835b8e6727ccbd5f9889ebb6f25a90018e3e574e5b6082b32", + "matches_local": true + }, + { + "path": "registry/capabilities/capability.security.structure-construction.md", + "http": 200, + "sha256": "000ceac5b4c71569aed1c9af970980a0c72a34db13fdca40671c9ddbfe97409d", + "matches_local": true + } + ], + "errors": [], + "stable_index_matches": true + }, + { + "repo": "ops-warden", + "files": [ + { + "path": "registry/indexes/capabilities.yaml", + "http": 200, + "sha256": "4d3aba70ead10c826814dc766f9d23e3e0ce52475330acf57905bf62b01c819c", + "matches_local": true + }, + { + "path": "registry/capabilities/capability.security.ssh-certificate-issuance.md", + "http": 200, + "sha256": "f48c749ec3342b57d94866c847be5f3ff10934bfa86b5953b729afde6d3e6b5f", + "matches_local": true + }, + { + "path": "registry/capabilities/capability.security.access-routing.md", + "http": 200, + "sha256": "e3d5b2737fa743174216e0b106cba8ae861b9a1fd7797331a041ffa941a3beae", + "matches_local": true + }, + { + "path": "registry/capabilities/capability.security.case-follow-through.md", + "http": 200, + "sha256": "8a69a28b2f17ee247305fae6201decb0805ff903ced37ab91d8c6c5dbe8a681d", + "matches_local": true + } + ], + "errors": [], + "stable_index_matches": true + }, + { + "repo": "policy-nexus", + "files": [ + { + "path": "registry/indexes/capabilities.yaml", + "http": 200, + "sha256": "c1eb3e23621de354a9081cbf29a2f3d772b5f8538aaceca417574cde5a04c021", + "matches_local": true + }, + { + "path": "registry/capabilities/capability.security.policy-publication.md", + "http": 200, + "sha256": "b961365f5a8a067226290e56cc31f6c254c345451d1830f032b670c013575d8a", + "matches_local": true + } + ], + "errors": [], + "stable_index_matches": true + }, + { + "repo": "rail-kubernetes", + "files": [ + { + "path": "registry/indexes/capabilities.yaml", + "http": 200, + "sha256": "699f69facea1ce4e71d90fe0790db19b68d3a546015453651d3afc808d31d09d", + "matches_local": true + }, + { + "path": "registry/capabilities/capability.railiance.kubernetes-rail.md", + "http": 200, + "sha256": "896feea967e22f276287408aaae6e34b95839e29a18c4fdc30e4c83baa30e05f", + "matches_local": true + } + ], + "errors": [], + "stable_index_matches": true + }, + { + "repo": "railiance-apps", + "files": [ + { + "path": "registry/indexes/capabilities.yaml", + "http": 200, + "sha256": "2a550acf8545e95bd81842bb1b00f4db46c4dbb42b522823f043e948667ddf2c", + "matches_local": true + }, + { + "path": "registry/capabilities/capability.railiance.workload-deployment-tooling.md", + "http": 200, + "sha256": "ad24b7656512f93698af3182a3187c2e32efd5e74ae69007cb86eb7cd889528f", + "matches_local": true + } + ], + "errors": [], + "stable_index_matches": true + }, + { + "repo": "railiance-bootstrap", + "files": [ + { + "path": "registry/indexes/capabilities.yaml", + "http": 200, + "sha256": "feefcc699e045a1a72f254d71f46460ad2362ed23c9de6e54c952711fcb33788", + "matches_local": true + }, + { + "path": "registry/capabilities/capability.railiance.bootstrap-sequencing.md", + "http": 200, + "sha256": "9bbfe970a0bca433f5e4f0e13f244d26ff97280db0123533ea403cc1bbaaa790", + "matches_local": true + } + ], + "errors": [], + "stable_index_matches": true + }, + { + "repo": "railiance-cluster", + "files": [ + { + "path": "registry/indexes/capabilities.yaml", + "http": 200, + "sha256": "0e67a2aca183ee7ef9c4c04e618fcb03647120f456a133e84a8f42c2b8d8fcde", + "matches_local": true + }, + { + "path": "registry/capabilities/capability.railiance.cluster-bootstrap.md", + "http": 200, + "sha256": "dec1d194f06a35a35d80ed5bd4f59a7a309737ae611fafd52d6aad9896346d78", + "matches_local": true + } + ], + "errors": [], + "stable_index_matches": true + }, + { + "repo": "railiance-enablement", + "files": [ + { + "path": "registry/indexes/capabilities.yaml", + "http": 200, + "sha256": "4b1f91b0da94e3098916f3e360b20b9926be4dd8683d6fb167a027aaf04ebfac", + "matches_local": true + }, + { + "path": "registry/capabilities/capability.railiance.ci-enablement.md", + "http": 200, + "sha256": "3f90be42901660ed177f2636455919c21adf382ac6118950f0ec52526ba6e147", + "matches_local": true + } + ], + "errors": [], + "stable_index_matches": true + }, + { + "repo": "railiance-fabric", + "files": [ + { + "path": "registry/indexes/capabilities.yaml", + "http": 200, + "sha256": "f473644906fe3f331702b407c3519e6a3cd0d96d362cf30d80e03aac53c3b862", + "matches_local": true + }, + { + "path": "registry/capabilities/capability.railiance.fabric-graph.md", + "http": 200, + "sha256": "25ad5efa3f874c60c4adc4f0eb46b4ce27805659062c0453a581e8d5b146443d", + "matches_local": true + } + ], + "errors": [], + "stable_index_matches": true + }, + { + "repo": "railiance-forge", + "files": [ + { + "path": "registry/indexes/capabilities.yaml", + "http": 200, + "sha256": "4df605d617b7656ffb94a43ab4633c8fc69ec723f68f5f5b8d5f24ad8efb6b98", + "matches_local": true + }, + { + "path": "registry/capabilities/capability.railiance.forge-infrastructure.md", + "http": 200, + "sha256": "a974968ea22aa2ce39f243677ae6a7997e2850f0b5630c4fd0107819a8151135", + "matches_local": true + }, + { + "path": "registry/capabilities/capability.security.forge-secret-prevention.md", + "http": 200, + "sha256": "54a1797dfcaa724be1b837bc69abc3df7e5546289c3a7faf333d7bc0b580b8f7", + "matches_local": true + } + ], + "errors": [], + "stable_index_matches": true + }, + { + "repo": "railiance-infra", + "files": [ + { + "path": "registry/indexes/capabilities.yaml", + "http": 200, + "sha256": "14a76b1f1dd317a1d426502ff5f80780e9551cd1cd600543de6fb9a84cb8ada2", + "matches_local": true + }, + { + "path": "registry/capabilities/capability.railiance.infra-provisioning.md", + "http": 200, + "sha256": "3a66a7ef7ea692f9908c054e7d689a374b816aeb0beefade6b42d697d3112f9f", + "matches_local": true + } + ], + "errors": [], + "stable_index_matches": true + }, + { + "repo": "railiance-master", + "files": [ + { + "path": "registry/indexes/capabilities.yaml", + "http": 200, + "sha256": "f36cb2765ec04064314f43866f9a87a94a4f0f728adfaad46739f232b4986c0a", + "matches_local": true + }, + { + "path": "registry/capabilities/capability.railiance.security-consumption-contracts.md", + "http": 200, + "sha256": "0477910eaabec436e7fa1cc8244bed5b1319436f6fa22e20086fb0f676f3de14", + "matches_local": true + } + ], + "errors": [], + "stable_index_matches": true + }, + { + "repo": "railiance-platform", + "files": [ + { + "path": "registry/indexes/capabilities.yaml", + "http": 200, + "sha256": "0315d526165368e0a513de6b15932778d43c3e6045c589c95060f555fca3202e", + "matches_local": true + }, + { + "path": "registry/capabilities/capability.railiance.platform-services.md", + "http": 200, + "sha256": "ac900d26de158ea990bd5f666dcaa1dc8e715e6f1e5b6594b3ab23e2c208b8b6", + "matches_local": true + } + ], + "errors": [], + "stable_index_matches": true + }, + { + "repo": "railiance-telemetry", + "files": [ + { + "path": "registry/indexes/capabilities.yaml", + "http": 200, + "sha256": "82f3f2d188e2d7ebea71b7262e106c5f48feaf39a2da881bb645ff9352884205", + "matches_local": true + }, + { + "path": "registry/capabilities/capability.observability.security-signals.md", + "http": 200, + "sha256": "5a4ccfe91855a423cce8fe4f6432a15eb78acd2f1413475d4e5ceeb415b15f3d", + "matches_local": true + } + ], + "errors": [], + "stable_index_matches": true + }, + { + "repo": "rapp-openbao", + "files": [], + "errors": [ + "registry/indexes/capabilities.yaml: HTTPError", + "registry/capabilities/capability.railiance.openbao-package.md: HTTPError", + "Stable URL: HTTPError" + ] + }, + { + "repo": "rapp-playwright", + "files": [ + { + "path": "registry/indexes/capabilities.yaml", + "http": 200, + "sha256": "79645df817858f96230f2443d66796d1078185c7afef5948f6a38632234d33bb", + "matches_local": true + }, + { + "path": "registry/capabilities/capability.railiance.browser-verification.md", + "http": 200, + "sha256": "ad1f3f14ccabec8b25a4a0125ad3ea6c065c3216b6bc6f84552f6c1435e5d042", + "matches_local": true + } + ], + "errors": [], + "stable_index_matches": true + }, + { + "repo": "rapp-policy-nexus", + "files": [], + "errors": [ + "registry/indexes/capabilities.yaml: HTTPError", + "registry/capabilities/capability.railiance.policy-publication-package.md: HTTPError", + "Stable URL: HTTPError" + ] + }, + { + "repo": "rapp-postgres", + "files": [], + "errors": [ + "registry/indexes/capabilities.yaml: HTTPError", + "registry/capabilities/capability.railiance.postgres-isolation-recovery.md: HTTPError", + "Stable URL: HTTPError" + ] + }, + { + "repo": "rapp-telemetry", + "files": [], + "errors": [ + "registry/indexes/capabilities.yaml: HTTPError", + "registry/capabilities/capability.railiance.telemetry-package.md: HTTPError", + "Stable URL: HTTPError" + ] + }, + { + "repo": "rapp-tenant-engine", + "files": [], + "errors": [ + "registry/indexes/capabilities.yaml: HTTPError", + "registry/capabilities/capability.railiance.tenant-engine-package.md: HTTPError", + "Stable URL: HTTPError" + ] + }, + { + "repo": "rapp-user-engine", + "files": [], + "errors": [ + "registry/indexes/capabilities.yaml: HTTPError", + "registry/capabilities/capability.railiance.user-engine-package.md: HTTPError", + "Stable URL: HTTPError" + ] + }, + { + "repo": "reef-railiance", + "files": [ + { + "path": "registry/indexes/capabilities.yaml", + "http": 200, + "sha256": "84a89c376021e3cb980c02c41d1a31396b874bebcc3801c2d1a9a33ae4195eb5", + "matches_local": true + }, + { + "path": "registry/capabilities/capability.railiance.substrate-bindings.md", + "http": 200, + "sha256": "4df6529b95c9aff89f67de20cf19b5ad20ffdb0af6eff57936c693e4c7897af0", + "matches_local": true + } + ], + "errors": [], + "stable_index_matches": true + }, + { + "repo": "rein-aharness", + "files": [ + { + "path": "registry/indexes/capabilities.yaml", + "http": 200, + "sha256": "20990ed0e016b3330a0d44e9b86bfff8966e82bc8800469ec0f320cfa8a63fbc", + "matches_local": true + }, + { + "path": "registry/capabilities/capability.execution.scheduled-agent-rein.md", + "http": 200, + "sha256": "85231cd1f473ed28cf826fa561f098e8b829efb9177108e9678fcb4f3205d513", + "matches_local": true + } + ], + "errors": [], + "stable_index_matches": true + }, + { + "repo": "rein-openweights", + "files": [ + { + "path": "registry/indexes/capabilities.yaml", + "http": 200, + "sha256": "2f4e59b2dbc7ea6078405f5c2f2be0aebd9434bee171a429b9ebe6baa83f3c2b", + "matches_local": true + }, + { + "path": "registry/capabilities/capability.execution.openweight-rein.md", + "http": 200, + "sha256": "cfb35d7d7285e5e632e09869457ccd262aa0ceb3f8a6c3461f5a6d4f82002810", + "matches_local": true + } + ], + "errors": [], + "stable_index_matches": true + }, + { + "repo": "repo-manager", + "files": [ + { + "path": "registry/indexes/capabilities.yaml", + "http": 200, + "sha256": "ba068a8dd7a282664e94e2acdbbb170a2ba48e260e7ce0ec8eaaa8aee6f9e7c2", + "matches_local": true + }, + { + "path": "registry/capabilities/capability.security.repository-secret-prevention.md", + "http": 200, + "sha256": "67f2846ff664489c938edc6ba4f65369ea0d3d5fd24fb7dbd287b9ee903281be", + "matches_local": true + } + ], + "errors": [], + "stable_index_matches": true + }, + { + "repo": "resource-control", + "files": [ + { + "path": "registry/indexes/capabilities.yaml", + "http": 200, + "sha256": "31b16f7d824e376f21de44aff46d5d532d3679d5e72035ce84813572b11bbd44", + "matches_local": true + }, + { + "path": "registry/capabilities/capability.resources.security-capacity.md", + "http": 200, + "sha256": "013900e2dc836b3eb3bc4470f0f27308cb9a923575aea998eb4693302df7ebd8", + "matches_local": true + } + ], + "errors": [], + "stable_index_matches": true + }, + { + "repo": "reuse-surface", + "files": [ + { + "path": "registry/indexes/capabilities.yaml", + "http": 200, + "sha256": "a77c952e9e1a37a3504ccd7001a7a2493d60d46f147f277071829cd05646e31c", + "matches_local": true + }, + { + "path": "registry/capabilities/capability.registry.register.md", + "http": 200, + "sha256": "f0a188e7ff8a2057b4eb9a0fb998e6b777d893cad5f024a507ca22639958fb2b", + "matches_local": true + }, + { + "path": "registry/capabilities/capability.registry.validate.md", + "http": 200, + "sha256": "dfbfeec7c795e9e666860913400b777718dd99d1eb1ed1146715edf51a3f846a", + "matches_local": true + } + ], + "errors": [], + "stable_index_matches": true + }, + { + "repo": "risk-nexus", + "files": [ + { + "path": "registry/indexes/capabilities.yaml", + "http": 200, + "sha256": "53f0bf9e0aa06a6db0aa991f0ebdaa1d2bf74cfaf4e30eed8c003a4d2186a746", + "matches_local": true + }, + { + "path": "registry/capabilities/capability.security.risk-register.md", + "http": 200, + "sha256": "f71a783e4bf946271b471c39921f88a21772cf5424017eea5b7660bb3614deb6", + "matches_local": true + } + ], + "errors": [], + "stable_index_matches": true + }, + { + "repo": "role-engine", + "files": [ + { + "path": "registry/indexes/capabilities.yaml", + "http": 200, + "sha256": "1188e02e0333664b3554d11c59c12942fef9e29e6491b186dcec67e6ae3e8a5c", + "matches_local": true + }, + { + "path": "registry/capabilities/capability.organization.role-catalog.md", + "http": 200, + "sha256": "e9b6bfdedafcaf2e61145d33bddcd24f85b281b42853d27c0e512f985726d5f1", + "matches_local": true + } + ], + "errors": [], + "stable_index_matches": true + }, + { + "repo": "runbook-tutorials", + "files": [ + { + "path": "registry/indexes/capabilities.yaml", + "http": 200, + "sha256": "a74fa708c4fea755f5c52504bd227393888cca78c043349cdc182c4d783390e5", + "matches_local": true + }, + { + "path": "registry/capabilities/capability.infotech.runbook-tutorials.md", + "http": 200, + "sha256": "f25e5d9b0a061b65b0f6cc02f2f961cdcb71f7b09cfc76e42d36ae959033962f", + "matches_local": true + } + ], + "errors": [], + "stable_index_matches": true + }, + { + "repo": "sand-boxer", + "files": [ + { + "path": "registry/indexes/capabilities.yaml", + "http": 200, + "sha256": "a182d5672d088e80b79eacff9cb37fc2f3518c3c100537e18d125abc7f169e96", + "matches_local": true + }, + { + "path": "registry/capabilities/execution.sandbox-provision.md", + "http": 200, + "sha256": "64c68c78b383541c9e9fb832f215ca360733d692289331f012d7b8479ef81a6a", + "matches_local": true + } + ], + "errors": [], + "stable_index_matches": true + }, + { + "repo": "sbom-nexus", + "files": [ + { + "path": "registry/indexes/capabilities.yaml", + "http": 200, + "sha256": "ed6efd581483d0058a3928b365b933cbbd98c035b0a75c866156a69961d74edc", + "matches_local": true + }, + { + "path": "registry/capabilities/capability.infotech.sbom-management.md", + "http": 200, + "sha256": "f9dafe83c7d497a7c412106e2ce743bd34d75e419d075a5b19fd99ded87f2a72", + "matches_local": true + } + ], + "errors": [], + "stable_index_matches": true + }, + { + "repo": "secrets-engine", + "files": [ + { + "path": "registry/indexes/capabilities.yaml", + "http": 200, + "sha256": "d5d903336d05ccd0b75a49f7be7e866ef89c6bdb6444e8c747a5ffa934a6bbe2", + "matches_local": true + }, + { + "path": "registry/capabilities/capability.security.credential-lifecycle.md", + "http": 200, + "sha256": "84accd50647b46594ad1f456b157b71c9ad32fe202072773675923584408d4e0", + "matches_local": true + } + ], + "errors": [], + "stable_index_matches": true + }, + { + "repo": "security-canon", + "files": [ + { + "path": "registry/indexes/capabilities.yaml", + "http": 200, + "sha256": "9b9f9348e560a72d8032f62b2ba4820d81fe182353ccc886e4f5c57cbdea563f", + "matches_local": true + }, + { + "path": "registry/capabilities/capability.security.practice-canon.md", + "http": 200, + "sha256": "e189be678ef2510848caa5f5b3c190ecd9f7db9db8be762e9e215ae6827e62d5", + "matches_local": true + } + ], + "errors": [], + "stable_index_matches": true + }, + { + "repo": "state-hub", + "files": [ + { + "path": "registry/indexes/capabilities.yaml", + "http": 200, + "sha256": "eab454bd985ab6ab83c38fd29e6e58ebdfe1e09bbc5388edddaa07a88133619a", + "matches_local": true + }, + { + "path": "registry/capabilities/capability.statehub.workstream-coordinate.md", + "http": 200, + "sha256": "818428997975e8f2d64d85946937b4076806a9eedda1a31d1f701f80d1a6cf31", + "matches_local": true + }, + { + "path": "registry/capabilities/capability.statehub.progress-log.md", + "http": 200, + "sha256": "71dacad9cca8df30d487720e3072a278c489e6c52d74b3fcb84f26d61c07c846", + "matches_local": true + } + ], + "errors": [], + "stable_index_matches": true + }, + { + "repo": "tenant-engine", + "files": [ + { + "path": "registry/indexes/capabilities.yaml", + "http": 200, + "sha256": "4131f1b63e52306f267204cdfb0faa90d6550f8bca51f97cf1062becea95d805", + "matches_local": true + }, + { + "path": "registry/capabilities/capability.identity.tenant-facts.md", + "http": 200, + "sha256": "504341300c2e0760af214ae33946fdd31ebef605855d1250b34503349ec38370", + "matches_local": true + } + ], + "errors": [], + "stable_index_matches": true + }, + { + "repo": "the-custodian", + "files": [ + { + "path": "registry/indexes/capabilities.yaml", + "http": 200, + "sha256": "cfe7f25e91fee824347aa22ccee2a70e7dd58ac06a7106f7e2ec9e54dc163020", + "matches_local": true + }, + { + "path": "registry/capabilities/capability.agents.custodian-runtime-tooling.md", + "http": 200, + "sha256": "9e136682c02d963076c89eb1f4d4b956666ece2cf0747db27986a77d0c63d1b0", + "matches_local": true + } + ], + "errors": [], + "stable_index_matches": true + }, + { + "repo": "user-engine", + "files": [ + { + "path": "registry/indexes/capabilities.yaml", + "http": 200, + "sha256": "7133c52c2013cea70d07d33960f145c370d33a8b4397c1dc927769fcaf200cbd", + "matches_local": true + }, + { + "path": "registry/capabilities/capability.identity.user-engine.md", + "http": 200, + "sha256": "3c9c8f1bb00b66d5f556da9cd33f42470a2e6bcfa3b3f3e9b4e8d1881db5f010", + "matches_local": true + } + ], + "errors": [], + "stable_index_matches": true + }, + { + "repo": "whitehat-security", + "files": [ + { + "path": "registry/indexes/capabilities.yaml", + "http": 200, + "sha256": "e4bf9bac7f62f60cf10a9fbd25aa8200c22f13d61861862ff5fa1cc90b55f37c", + "matches_local": true + }, + { + "path": "registry/capabilities/capability.security.adversarial-evidence.md", + "http": 200, + "sha256": "0fecc5e5ede496b0f5c4a42e1b0166e5c3f156ede9c6f3813d59b6e8eb54c82d", + "matches_local": true + } + ], + "errors": [], + "stable_index_matches": true + }, + { + "repo": "zone-engine", + "files": [ + { + "path": "registry/indexes/capabilities.yaml", + "http": 200, + "sha256": "b6b196089a3011f16e8a38f6914589efc72a29ee3aca4bc0c5e3cf30bee97ec4", + "matches_local": true + }, + { + "path": "registry/capabilities/capability.security.zone-reference.md", + "http": 200, + "sha256": "d1b4c433ad91bf4a30b02c353f5cef387b6d63d8f58defabf6f16fed15816aae", + "matches_local": true + } + ], + "errors": [], + "stable_index_matches": true + }, + { + "repo": "rail-knative", + "files": [ + { + "path": "registry/indexes/capabilities.yaml", + "http": 200, + "sha256": "e044b8f2f6bc1b8aa7c672ee33dfac69d6813fd672388713a70ab7ea4d4052c0", + "matches_local": true + }, + { + "path": "registry/capabilities/capability.railiance.knative-rail.md", + "http": 200, + "sha256": "4287dcb909f2ff7c6342ed91a1b59fa9776ecc15f97b3e953093a050ad6c8fb0", + "matches_local": true + } + ], + "errors": [], + "stable_index_matches": true + }, + { + "repo": "rapp-sbom-nexus", + "files": [], + "errors": [ + "registry/indexes/capabilities.yaml: HTTPError", + "registry/capabilities/capability.railiance.sbom-runtime-package.md: HTTPError", + "Stable URL: HTTPError" + ] + } + ] +} diff --git a/docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/protected-backup/docs/assessments/netkingdom-2026-10-14/scoped-access-handoff.json b/docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/protected-backup/docs/assessments/netkingdom-2026-10-14/scoped-access-handoff.json new file mode 100644 index 00000000..34bb7c00 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/protected-backup/docs/assessments/netkingdom-2026-10-14/scoped-access-handoff.json @@ -0,0 +1,67 @@ +{ + "workplan": "CUST-WP-0081", + "user_disposition": "Arrange scoped owner access", + "status": "Public batch verified; founder metadata disclosure consent recorded for all seven sources. Restricted hub reader and native fetch verification remain pending.", + "owner_repo": "railiance-platform", + "write_access": { + "route": "reuse-surface-hub-write-token", + "kv_path": "platform/workloads/reuse/reuse-surface/runtime-secrets", + "field": "REUSE_SURFACE_TOKEN", + "existing_read_policy": "workload-kv-read-reuse-surface-runtime", + "request": "Owner to admit the caller through the existing scoped route and verify its metadata lookup and selected-field read. Retain existing denial evidence; do not grant administrative or unrelated workload access.", + "delivery": "warden access --exec, without exporting or recording the value", + "approval_record": "CCR-2026-0005 is active and was previously approved by platform-operator and reuse-surface-owner; it does not name the caller principal.", + "principal_binding": "Identify the caller OpenBao OIDC entity/accessor without recording any token. If a binding change is needed, record approval for that exact principal with both required approvers before applying the existing policy.", + "existing_policy": "workload-kv-read-reuse-surface-runtime", + "exact_allowed_paths": [ + "platform/data/workloads/reuse/reuse-surface/runtime-secrets", + "platform/metadata/workloads/reuse/reuse-surface/runtime-secrets" + ], + "negative_check": "An unrelated sibling path must remain denied. Do not broaden policy contents or attach administrative/ESO-only permissions.", + "verified_execution_receipt": "hosted-public-registration-verification.json" + }, + "private_source_access": { + "repositories": [ + "rapp-openbao", + "rapp-policy-nexus", + "rapp-postgres", + "rapp-sbom-nexus", + "rapp-telemetry", + "rapp-tenant-engine", + "rapp-user-engine" + ], + "request": "Owner to provision a repository-read credential at the federation hub, confirm source-owner consent to public capability metadata, and verify hub-side index and entry fetches.", + "return_only": [ + "configured auth_env name", + "auth_header name if nondefault", + "scope and verification receipts" + ], + "visibility_change_authorized": false, + "credential_design": "A new dedicated Forgejo identity/PAT restricted to repository-read for exactly these seven sources; verify a denied-write/admin negative. Keep it distinct from the hub writer token and from other workload credentials.", + "delivery": "Owner-reviewed OpenBao custody and ESO delivery to the reuse-surface hub runtime, then configure the registration auth_env with the environment variable name only.", + "approval": "Platform operator and each relevant source owner must approve the limited private capability-metadata disclosure and reader scope before provisioning.", + "metadata_disclosure_consent": { + "status": "approved", + "receipt": "private-source-owner-consent-r1.json", + "decision_id": "d8f8f216-ac94-4295-97f3-825fd21254ea", + "proposal_sha256": "46ac4fe49a08054cc215aade2fbaf3b36ee334520997857ed84f530b52da9d35" + }, + "consumer_delivery_gate": "Reuse owner reports native authenticated redirect forwarding hazard. Require owner-reviewed native fetch fix and verified fixed hub image before introducing source-reader PAT; initial same-image assumption is superseded by private-reader-owner-assembly-receipt.json." + }, + "resume": "Update and retain the exact registration proposal revision with admitted auth_env references, verify source parity, execute bounded registrations, then require all 70 reviewed IDs exactly once in a fresh hosted federation.", + "secret_values_requested_in_chat": false, + "owner_message_sent": true, + "attended_sequence": [ + "Review CCR-2026-0005 and identify the actual intended caller principal. The existing OIDC identity is not named in the CCR.", + "If needed, obtain and record platform-operator plus reuse-surface-owner approval for binding that exact identity to the existing read-only policy. Verify read on the two exact paths and denial on a sibling path.", + "Use warden access reuse-surface-hub-write-token --exec --field REUSE_SURFACE_TOKEN with a silent helper. Do not use --fetch or print/export the field.", + "After the public-only helper receipt confirms 31 registrations, request the distinct seven-repository read-only source credential and owner disclosure consent. Return only the auth_env name, scope and sanitized verification receipts." + ], + "public_batch_prepared": { + "proposal": "hub-public-registration-proposal-r1.json", + "plan": "hub-public-resume-plan.json", + "helper": "register_public_hub.py", + "repositories": 31, + "status": "31 registrations applied, readback verified, hosted public capabilities verified, login session revoked" + } +} diff --git a/docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/publication-backup/.custodian-brief.md.worktree b/docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/publication-backup/.custodian-brief.md.worktree new file mode 100644 index 00000000..6aad3a57 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/publication-backup/.custodian-brief.md.worktree @@ -0,0 +1,62 @@ + +# Custodian Brief — the-custodian + +**Domain:** infotech +**Last synced:** 2026-10-15 23:45 UTC +**State Hub:** http://127.0.0.1:8000 *(adjust if running on a remote machine)* + +## Active Workstreams + +### NetKingdom target security architecture and SEC-DSM implementation +Progress: 2/6 done | workplan_id: `dfadb19b-445e-59c6-bcd6-48e87b912c52` + +**Open tasks:** +- ► Close source defects and reconcile uncovered lifecycle gaps `87637402` +- ► Coordinate self-service Playwright identity delivery `ed8b909a` +- ► Establish the target architecture and drive the restructuring `6aca534c` +- · Verify native closure against every requirement `82378b0f` + +### Separate agent credentials and establish supervised privileged execution +Progress: 2/5 done | workplan_id: `a98a9f34-83b4-5c8c-8107-e05f7806d50d` + +**Open tasks:** +- ! Build and hand out the agent identity `4b88b0b7` + *(wait: Await GLAS-WP-0012 (glas-harness) production acceptance of the local supervised profile; then railiance-platform RBAC, railiance-enablement k3s config, railiance-infra principal mapping and ops-warden certificate issuance.)* +- ! Fleet guidance and harness guards `90725511` + *(wait: Await CUST-WP-0073-T02: the verified agent identity and execution path must exist before it can be documented; Codex/Grok read-denial guard needs glas-harness or harness-owner delivery.)* +- ! Rotate what was exposed `1b41b532` + *(wait: Deferred by founder decision 2026-09-24; reopened only by the founder on one of the three triggers below, in one attended rotation window.)* + +### Size Railiance workloads from actual demand and establish a weekly allocation review +Progress: 1/5 done | workplan_id: `2249bddb-7524-5add-bd5c-c4163a6ca0f3` + +**Open tasks:** +- ! Measure the Vergabe prototype under representative pilot use `82192370` + *(wait: Await RAPPS-WP-0014-T03 (blocked, needs_human): populated two-user/document workflow run by Bernd and the company contact, giving representative load for p95 <= 2s / zero failures.)* +- ! Review ecosystem allocations and reserve operating room `6dc67558` + *(wait: Await CUST-WP-0071-T02 representative sample; Forgejo/runner demand and zero-request workload review need owner input (railiance-cluster, railiance-forge, resource-control).)* +- ! Apply accepted sizing changes and verify their effects `944f98e2` + *(wait: Await reviewed allocation proposals and applicable workload-owner authority.)* +- ! Establish the regular weekly workload-demand and allocation assessment `20ddd4d7` + *(wait: Await the repeatable observation and accepted sizing/review procedure.)* + +### Deterministic security flow: standing authorizations, flow report, and agent re-engagement +Progress: 3/7 done | workplan_id: `ab288f92-d364-5c24-9362-aee451f266c3` + +**Open tasks:** +- ! Schedule the flow report and decision sync `1be0569c` + *(wait: Activity Core accepted the schedule in ACTIVITY-WP-0047. Await claiming rein workstation/hub acceptance, owner activation and the first unassisted fire with report/checkpoint evidence.)* +- ! Encode the standing change rule as a policy-nexus guardrail `5dd52156` + *(wait: policy-nexus owns guardrail publication; handoff written as PNEX-WP-0006.)* +- ! Stop agent-authored index and sync commits `8a3dc219` + *(wait: Local guard and repeatable shared-hook installation are implemented. Fleet generator adoption and the required full week of clean history remain unverified; repo-manager owner acceptance is needed.)* +- ! Attended window #1 `e3da807f` + *(wait: Partly done 2026-10-11: list cut from 14 to 3; U05-U07 accepted, U08 waits on KEY-WP-0040-T01. Remaining founder acts: USER-WP-0026-T03 readback, RPF-WP-0044-T08 coulombcore check, the U08 re-run, and Sol's added RPF-WP-0044-T04 issuer login.)* + +--- +## MCP Orientation (when available) + +If the state-hub MCP server is reachable, call: +`get_domain_summary("infotech")` +This provides richer cross-domain context. +If the MCP call fails, use this file as your orientation source. diff --git a/docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/publication-backup/WORK-RECORDS.md.worktree b/docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/publication-backup/WORK-RECORDS.md.worktree new file mode 100644 index 00000000..6dc81634 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/publication-backup/WORK-RECORDS.md.worktree @@ -0,0 +1,536 @@ +# Work Records — the-custodian + +> Generated by `statehub fix-consistency` (CUST-WP-0061-T04, work-record +> stage 3). Do not edit by hand — edit the source file/block listed for +> each record and re-run fix-consistency to refresh this index. Archived +> workplans are omitted; closed decisions/intakes/engagements stay listed +> so recently-resolved work is still visible. [auto] + +| Kind | ID | Status | Lane | Source | +| --- | --- | --- | --- | --- | +| workplan | CUST-WP-ADHOC-2026-05-02 | done | — | workplans/ADHOC-2026-05-02.md | +| workplan | CUST-WP-ADHOC-2026-07-02 | finished | — | workplans/ADHOC-2026-07-02.md | +| workplan | CUST-WP-ADHOC-2026-08-25-RETIRED | finished | — | workplans/ADHOC-2026-08-25-retired.md | +| workplan | CUST-WP-ADHOC-2026-08-25 | finished | — | workplans/ADHOC-2026-08-25.md | +| workplan | CUST-WP-ADHOC-2026-09-06 | finished | — | workplans/ADHOC-2026-09-06.md | +| workplan | CUST-WP-0000 | finished | — | workplans/CUST-WP-0000-state-hub-v0.1-build-deploy.md | +| workplan | CUST-WP-0000b | finished | — | workplans/CUST-WP-0000b-state-hub-v0.2-decisions-dependencies.md | +| workplan | CUST-WP-0001 | done | — | workplans/CUST-WP-0001-custodian-agent-runtime.md | +| workplan | CUST-WP-0002 | finished | — | workplans/CUST-WP-0002-contribution-tracking-sbom.md | +| workplan | CUST-WP-0004 | finished | — | workplans/CUST-WP-0004-ep-td-tracking.md | +| workplan | CUST-WP-0005 | finished | — | workplans/CUST-WP-0005-dynamic-domains.md | +| workplan | CUST-WP-0006 | done | — | workplans/CUST-WP-0006-gems-state-hub.md | +| workplan | CUST-WP-0007 | finished | — | workplans/CUST-WP-0007-gems-migration.md | +| workplan | CUST-WP-0008 | done | — | workplans/CUST-WP-0008-consistency-engine.md | +| workplan | CUST-WP-0009 | finished | — | workplans/CUST-WP-0009-human-interventions.md | +| workplan | CUST-WP-0010 | finished | — | workplans/CUST-WP-0010-workstream-lifecycle-docs.md | +| workplan | CUST-WP-0010b | finished | — | workplans/CUST-WP-0010b-domain-and-repo-goals.md | +| workplan | CUST-WP-0013 | finished | — | workplans/CUST-WP-0013-sbom-infra-expansion.md | +| workplan | CUST-WP-0015 | done | — | workplans/CUST-WP-0015-agent-inbox.md | +| workplan | CUST-WP-0016 | done | — | workplans/CUST-WP-0016-kaizen-agentic-integration.md | +| workplan | CUST-WP-0017 | done | — | workplans/CUST-WP-0017-scope-md-agent-and-coverage.md | +| workplan | CUST-WP-0018 | done | — | workplans/CUST-WP-0018-api-hardening-code-quality.md | +| workplan | CUST-WP-0019 | done | — | workplans/CUST-WP-0019-dashboard-ux-polish.md | +| workplan | CUST-WP-0020 | done | — | workplans/CUST-WP-0020-test-suite.md | +| workplan | CUST-WP-0021 | done | — | workplans/CUST-WP-0021-multi-host-repo-paths.md | +| workplan | CUST-WP-0022 | done | — | workplans/CUST-WP-0022-capability-requests.md | +| workplan | CUST-WP-0023 | done | — | workplans/CUST-WP-0023-tpsc.md | +| workplan | CUST-WP-0024 | done | — | workplans/CUST-WP-0024-repo-doi-gate.md | +| workplan | CUST-WP-0026 | done | — | workplans/CUST-WP-0026-distributed-consistency.md | +| workplan | CUST-WP-0027 | done | — | workplans/CUST-WP-0027-capability-request-dispute.md | +| workplan | CUST-WP-0029 | done | — | workplans/CUST-WP-0029-token-consumption-tracking.md | +| workplan | CUST-WP-0030 | done | — | workplans/CUST-WP-0030-dashboard-entity-list-ux.md | +| workplan | CUST-WP-0031 | done | — | workplans/CUST-WP-0031-capability-registry.md | +| workplan | CUST-WP-0032 | done | — | workplans/CUST-WP-0032-haskell-build-machine.md | +| workplan | CUST-WP-0033 | done | — | workplans/CUST-WP-0033-interface-change-registry.md | +| workplan | CUST-WP-0039 | done | — | workplans/CUST-WP-0039-dashboard-poll-optimization.md | +| workplan | CUST-WP-0040 | finished | — | workplans/CUST-WP-0040-state-hub-nats-activity-core-integration.md | +| workplan | CUST-WP-0041 | done | — | workplans/CUST-WP-0041-api-performance.md | +| workplan | CUST-WP-0043 | finished | — | workplans/CUST-WP-0043-state-hub-repo-extraction.md | +| workplan | CUST-WP-0044 | finished | — | workplans/CUST-WP-0044-daily-statehub-wsjf-triage.md | +| workplan | CUST-WP-0045 | finished | — | workplans/CUST-WP-0045-activity-core-daily-triage-runner.md | +| workplan | CUST-WP-0069 | finished | — | workplans/CUST-WP-0045-cutover-runbook.md | +| workplan | CUST-WP-0046 | finished | — | workplans/CUST-WP-0046-hourly-recently-on-scope-activity-core.md | +| workplan | CUST-WP-0047 | finished | — | workplans/CUST-WP-0047-ops-hub-service-inventory-now-view.md | +| workplan | CUST-WP-0048 | finished | — | workplans/CUST-WP-0048-hub-core-state-hub-import-refactor.md | +| workplan | CUST-WP-0049 | finished | — | workplans/CUST-WP-0049-interhub-bootstrap-access-lane.md | +| workplan | CUST-WP-0050 | finished | — | workplans/CUST-WP-0050-repo-classification-registration-redesign.md | +| workplan | CUST-WP-0051 | finished | — | workplans/CUST-WP-0051-infrastructure-stabilization-metaplan.md | +| workplan | CUST-WP-0052 | finished | — | workplans/CUST-WP-0052-core-hub-fos-reset.md | +| workplan | CUST-WP-0053 | finished | — | workplans/CUST-WP-0053-coordination-hygiene-improvements.md | +| workplan | CUST-WP-0055 | finished | — | workplans/CUST-WP-0055-workplan-terminology-fleet-refactor.md | +| workplan | CUST-WP-0056 | finished | — | workplans/CUST-WP-0056-daily-todo-md-stale-review.md | +| workplan | CUST-WP-0059 | finished | — | workplans/CUST-WP-0059-forgejo-ci-publish-cutover-tail.md | +| workplan | CUST-WP-0060 | finished | — | workplans/CUST-WP-0060-work-record-canonization.md | +| workplan | CUST-WP-0061 | finished | — | workplans/CUST-WP-0061-work-record-stage3-intake-promotion-index.md | +| workplan | CUST-WP-0062 | finished | — | workplans/CUST-WP-0062-sbom-nexus-daily-catchup.md | +| workplan | CUST-WP-0063 | finished | — | workplans/CUST-WP-0063-inbox-governance-packets.md | +| workplan | CUST-WP-0064 | finished | — | workplans/CUST-WP-0064-sbom-controlled-scan-inputs.md | +| workplan | CUST-WP-0065 | finished | — | workplans/CUST-WP-0065-reclassify-repos-and-guidance-docs-to-the-new-sector-domain.md | +| workplan | CUST-WP-0066 | finished | — | workplans/CUST-WP-0066-repository-qualified-ad-hoc-identifiers.md | +| workplan | CUST-WP-0067 | finished | — | workplans/CUST-WP-0067-hub-authority-target-resolution.md | +| workplan | CUST-WP-0068 | finished | — | workplans/CUST-WP-0068-cache-only-work-record-recovery.md | +| workplan | CUST-WP-0070 | finished | — | workplans/CUST-WP-0070-publication-repo-category.md | +| workplan | CUST-WP-0071 | blocked | — | workplans/CUST-WP-0071-measured-workload-sizing-and-weekly-review.md | +| workplan | CUST-WP-0072 | finished | — | workplans/CUST-WP-0072-fleet-flavor-and-depends-on-backfill.md | +| workplan | CUST-WP-0073 | blocked | — | workplans/CUST-WP-0073-agent-credential-separation.md | +| workplan | CUST-WP-0074 | finished | — | workplans/CUST-WP-0074-qualified-wait-states.md | +| workplan | CUST-WP-0075 | finished | — | workplans/CUST-WP-0075-dependency-graph-driven-unblocking.md | +| workplan | CUST-WP-0076 | finished | — | workplans/CUST-WP-0076-provisioning-window-cadence.md | +| workplan | CUST-WP-0077 | finished | — | workplans/CUST-WP-0077-dependency-root-execution-wave.md | +| workplan | CUST-WP-0078 | finished | — | workplans/CUST-WP-0078-security-collaboration-consolidation.md | +| workplan | CUST-WP-0079 | blocked | — | workplans/CUST-WP-0079-deterministic-security-flow.md | +| workplan | CUST-WP-0080 | active | — | workplans/CUST-WP-0080-netkingdom-security-blueprint-and-gap-closure.md | +| workplan | CUST-WP-0081 | finished | — | workplans/CUST-WP-0081-netkingdom-architecture-review-and-reuse.md | +| workplan | THE-WP-0001 | finished | — | workplans/THE-WP-0001-federation-interface.md | +| task | CUST-WP-ADHOC-2026-05-02-T01 | done | — | workplans/ADHOC-2026-05-02.md | +| task | CUST-WP-ADHOC-2026-07-02-T01 | done | — | workplans/ADHOC-2026-07-02.md | +| task | ADHOC-2026-08-25-T01 | done | — | workplans/ADHOC-2026-08-25-retired.md | +| task | CUST-WP-ADHOC-2026-08-25-T01 | done | — | workplans/ADHOC-2026-08-25.md | +| task | CUST-WP-ADHOC-2026-09-06-T01 | done | — | workplans/ADHOC-2026-09-06.md | +| task | CUST-WP-0001-T01 | done | — | workplans/CUST-WP-0001-custodian-agent-runtime.md | +| task | CUST-WP-0001-T02 | done | — | workplans/CUST-WP-0001-custodian-agent-runtime.md | +| task | CUST-WP-0002-T01 | done | — | workplans/CUST-WP-0002-contribution-tracking-sbom.md | +| task | CUST-WP-0002-T02 | done | — | workplans/CUST-WP-0002-contribution-tracking-sbom.md | +| task | CUST-WP-0002-T03 | done | — | workplans/CUST-WP-0002-contribution-tracking-sbom.md | +| task | CUST-WP-0002-T04 | done | — | workplans/CUST-WP-0002-contribution-tracking-sbom.md | +| task | CUST-WP-0002-T05 | done | — | workplans/CUST-WP-0002-contribution-tracking-sbom.md | +| task | CUST-WP-0002-T06 | done | — | workplans/CUST-WP-0002-contribution-tracking-sbom.md | +| task | CUST-WP-0002-T07 | done | — | workplans/CUST-WP-0002-contribution-tracking-sbom.md | +| task | CUST-WP-0002-T08 | done | — | workplans/CUST-WP-0002-contribution-tracking-sbom.md | +| task | CUST-WP-0002-T09 | done | — | workplans/CUST-WP-0002-contribution-tracking-sbom.md | +| task | CUST-WP-0002-T10 | done | — | workplans/CUST-WP-0002-contribution-tracking-sbom.md | +| task | CUST-WP-0002-T11 | done | — | workplans/CUST-WP-0002-contribution-tracking-sbom.md | +| task | CUST-WP-0002-T12 | done | — | workplans/CUST-WP-0002-contribution-tracking-sbom.md | +| task | CUST-WP-0002-T13 | done | — | workplans/CUST-WP-0002-contribution-tracking-sbom.md | +| task | CUST-WP-0002-T14 | done | — | workplans/CUST-WP-0002-contribution-tracking-sbom.md | +| task | CUST-WP-0002-T15 | done | — | workplans/CUST-WP-0002-contribution-tracking-sbom.md | +| task | CUST-WP-0004-T01 | done | — | workplans/CUST-WP-0004-ep-td-tracking.md | +| task | CUST-WP-0004-T02 | done | — | workplans/CUST-WP-0004-ep-td-tracking.md | +| task | CUST-WP-0004-T03 | done | — | workplans/CUST-WP-0004-ep-td-tracking.md | +| task | CUST-WP-0004-T04 | done | — | workplans/CUST-WP-0004-ep-td-tracking.md | +| task | CUST-WP-0004-T05 | done | — | workplans/CUST-WP-0004-ep-td-tracking.md | +| task | CUST-WP-0004-T06 | done | — | workplans/CUST-WP-0004-ep-td-tracking.md | +| task | CUST-WP-0004-T07 | done | — | workplans/CUST-WP-0004-ep-td-tracking.md | +| task | CUST-WP-0004-T08 | done | — | workplans/CUST-WP-0004-ep-td-tracking.md | +| task | CUST-WP-0004-T09 | done | — | workplans/CUST-WP-0004-ep-td-tracking.md | +| task | CUST-WP-0004-T10 | done | — | workplans/CUST-WP-0004-ep-td-tracking.md | +| task | CUST-WP-0005-T01 | done | — | workplans/CUST-WP-0005-dynamic-domains.md | +| task | CUST-WP-0005-T02 | done | — | workplans/CUST-WP-0005-dynamic-domains.md | +| task | CUST-WP-0005-T03 | done | — | workplans/CUST-WP-0005-dynamic-domains.md | +| task | CUST-WP-0005-T04 | done | — | workplans/CUST-WP-0005-dynamic-domains.md | +| task | CUST-WP-0005-T05 | done | — | workplans/CUST-WP-0005-dynamic-domains.md | +| task | CUST-WP-0005-T06 | done | — | workplans/CUST-WP-0005-dynamic-domains.md | +| task | CUST-WP-0005-T07 | done | — | workplans/CUST-WP-0005-dynamic-domains.md | +| task | CUST-WP-0005-T08 | done | — | workplans/CUST-WP-0005-dynamic-domains.md | +| task | CUST-WP-0005-T09 | done | — | workplans/CUST-WP-0005-dynamic-domains.md | +| task | CUST-WP-0005-T10 | done | — | workplans/CUST-WP-0005-dynamic-domains.md | +| task | CUST-WP-0005-T11 | done | — | workplans/CUST-WP-0005-dynamic-domains.md | +| task | T01 | done | — | workplans/CUST-WP-0006-gems-state-hub.md | +| task | T02 | done | — | workplans/CUST-WP-0006-gems-state-hub.md | +| task | T03 | done | — | workplans/CUST-WP-0006-gems-state-hub.md | +| task | T04 | done | — | workplans/CUST-WP-0006-gems-state-hub.md | +| task | T05 | done | — | workplans/CUST-WP-0006-gems-state-hub.md | +| task | T01 | done | — | workplans/CUST-WP-0007-gems-migration.md | +| task | T02 | done | — | workplans/CUST-WP-0007-gems-migration.md | +| task | T03 | done | — | workplans/CUST-WP-0007-gems-migration.md | +| task | T04 | done | — | workplans/CUST-WP-0007-gems-migration.md | +| task | T05 | done | — | workplans/CUST-WP-0007-gems-migration.md | +| task | T06 | done | — | workplans/CUST-WP-0007-gems-migration.md | +| task | T07 | done | — | workplans/CUST-WP-0007-gems-migration.md | +| task | T08 | done | — | workplans/CUST-WP-0007-gems-migration.md | +| task | T09 | done | — | workplans/CUST-WP-0007-gems-migration.md | +| task | T10 | done | — | workplans/CUST-WP-0007-gems-migration.md | +| task | T11 | done | — | workplans/CUST-WP-0007-gems-migration.md | +| task | T12 | done | — | workplans/CUST-WP-0007-gems-migration.md | +| task | T13 | done | — | workplans/CUST-WP-0007-gems-migration.md | +| task | T14 | done | — | workplans/CUST-WP-0007-gems-migration.md | +| task | T15 | done | — | workplans/CUST-WP-0007-gems-migration.md | +| task | T16 | done | — | workplans/CUST-WP-0007-gems-migration.md | +| task | T17 | done | — | workplans/CUST-WP-0007-gems-migration.md | +| task | CUST-WP-0008-T01 | done | — | workplans/CUST-WP-0008-consistency-engine.md | +| task | CUST-WP-0008-T02 | done | — | workplans/CUST-WP-0008-consistency-engine.md | +| task | CUST-WP-0008-T03 | done | — | workplans/CUST-WP-0008-consistency-engine.md | +| task | CUST-WP-0008-T04 | done | — | workplans/CUST-WP-0008-consistency-engine.md | +| task | CUST-WP-0009-T1 | done | — | workplans/CUST-WP-0009-human-interventions.md | +| task | CUST-WP-0009-T2 | done | — | workplans/CUST-WP-0009-human-interventions.md | +| task | CUST-WP-0009-T3 | done | — | workplans/CUST-WP-0009-human-interventions.md | +| task | CUST-WP-0010-T1 | done | — | workplans/CUST-WP-0010-workstream-lifecycle-docs.md | +| task | CUST-WP-0010-T2 | done | — | workplans/CUST-WP-0010-workstream-lifecycle-docs.md | +| task | CUST-WP-0010-T3 | done | — | workplans/CUST-WP-0010-workstream-lifecycle-docs.md | +| task | CUST-WP-0010b-T01 | done | — | workplans/CUST-WP-0010b-domain-and-repo-goals.md | +| task | CUST-WP-0010b-T02 | done | — | workplans/CUST-WP-0010b-domain-and-repo-goals.md | +| task | CUST-WP-0013-T01 | done | — | workplans/CUST-WP-0013-sbom-infra-expansion.md | +| task | CUST-WP-0013-T02 | done | — | workplans/CUST-WP-0013-sbom-infra-expansion.md | +| task | CUST-WP-0013-T03 | done | — | workplans/CUST-WP-0013-sbom-infra-expansion.md | +| task | CUST-WP-0013-T04 | done | — | workplans/CUST-WP-0013-sbom-infra-expansion.md | +| task | CUST-WP-0013-T05 | done | — | workplans/CUST-WP-0013-sbom-infra-expansion.md | +| task | CUST-WP-0013-T06 | done | — | workplans/CUST-WP-0013-sbom-infra-expansion.md | +| task | CUST-WP-0013-T07 | done | — | workplans/CUST-WP-0013-sbom-infra-expansion.md | +| task | CUST-WP-0013-T08 | done | — | workplans/CUST-WP-0013-sbom-infra-expansion.md | +| task | CUST-WP-0013-T09 | done | — | workplans/CUST-WP-0013-sbom-infra-expansion.md | +| task | CUST-WP-0013-T10 | done | — | workplans/CUST-WP-0013-sbom-infra-expansion.md | +| task | CUST-WP-0013-T11 | done | — | workplans/CUST-WP-0013-sbom-infra-expansion.md | +| task | CUST-WP-0013-T12 | done | — | workplans/CUST-WP-0013-sbom-infra-expansion.md | +| task | CUST-WP-0013-T13 | done | — | workplans/CUST-WP-0013-sbom-infra-expansion.md | +| task | CUST-WP-0013-T14 | done | — | workplans/CUST-WP-0013-sbom-infra-expansion.md | +| task | CUST-WP-0015-T01 | done | — | workplans/CUST-WP-0015-agent-inbox.md | +| task | CUST-WP-0015-T02 | done | — | workplans/CUST-WP-0015-agent-inbox.md | +| task | CUST-WP-0015-T03 | done | — | workplans/CUST-WP-0015-agent-inbox.md | +| task | CUST-WP-0015-T04 | done | — | workplans/CUST-WP-0015-agent-inbox.md | +| task | CUST-WP-0015-T05 | done | — | workplans/CUST-WP-0015-agent-inbox.md | +| task | CUST-WP-0016-T01 | done | — | workplans/CUST-WP-0016-kaizen-agentic-integration.md | +| task | CUST-WP-0016-T02 | done | — | workplans/CUST-WP-0016-kaizen-agentic-integration.md | +| task | CUST-WP-0016-T03 | done | — | workplans/CUST-WP-0016-kaizen-agentic-integration.md | +| task | CUST-WP-0016-T04 | done | — | workplans/CUST-WP-0016-kaizen-agentic-integration.md | +| task | CUST-WP-0016-T05 | done | — | workplans/CUST-WP-0016-kaizen-agentic-integration.md | +| task | CUST-WP-0017-T01 | done | — | workplans/CUST-WP-0017-scope-md-agent-and-coverage.md | +| task | CUST-WP-0017-T02 | done | — | workplans/CUST-WP-0017-scope-md-agent-and-coverage.md | +| task | CUST-WP-0017-T03 | done | — | workplans/CUST-WP-0017-scope-md-agent-and-coverage.md | +| task | CUST-WP-0017-T04 | done | — | workplans/CUST-WP-0017-scope-md-agent-and-coverage.md | +| task | CUST-WP-0017-T05 | done | — | workplans/CUST-WP-0017-scope-md-agent-and-coverage.md | +| task | CUST-WP-0017-T06 | done | — | workplans/CUST-WP-0017-scope-md-agent-and-coverage.md | +| task | CUST-WP-0018-T01 | done | — | workplans/CUST-WP-0018-api-hardening-code-quality.md | +| task | CUST-WP-0018-T02 | done | — | workplans/CUST-WP-0018-api-hardening-code-quality.md | +| task | CUST-WP-0018-T03 | done | — | workplans/CUST-WP-0018-api-hardening-code-quality.md | +| task | CUST-WP-0018-T04 | done | — | workplans/CUST-WP-0018-api-hardening-code-quality.md | +| task | CUST-WP-0018-T05 | done | — | workplans/CUST-WP-0018-api-hardening-code-quality.md | +| task | CUST-WP-0018-T06 | done | — | workplans/CUST-WP-0018-api-hardening-code-quality.md | +| task | CUST-WP-0018-T07 | done | — | workplans/CUST-WP-0018-api-hardening-code-quality.md | +| task | CUST-WP-0019-T01 | done | — | workplans/CUST-WP-0019-dashboard-ux-polish.md | +| task | CUST-WP-0019-T02 | done | — | workplans/CUST-WP-0019-dashboard-ux-polish.md | +| task | CUST-WP-0019-T03 | done | — | workplans/CUST-WP-0019-dashboard-ux-polish.md | +| task | CUST-WP-0020-T01 | done | — | workplans/CUST-WP-0020-test-suite.md | +| task | CUST-WP-0020-T02 | done | — | workplans/CUST-WP-0020-test-suite.md | +| task | CUST-WP-0020-T03 | done | — | workplans/CUST-WP-0020-test-suite.md | +| task | CUST-WP-0020-T04 | done | — | workplans/CUST-WP-0020-test-suite.md | +| task | CUST-WP-0020-T05 | done | — | workplans/CUST-WP-0020-test-suite.md | +| task | CUST-WP-0021-T01 | done | — | workplans/CUST-WP-0021-multi-host-repo-paths.md | +| task | CUST-WP-0021-T02 | done | — | workplans/CUST-WP-0021-multi-host-repo-paths.md | +| task | CUST-WP-0021-T03 | done | — | workplans/CUST-WP-0021-multi-host-repo-paths.md | +| task | CUST-WP-0021-T04 | done | — | workplans/CUST-WP-0021-multi-host-repo-paths.md | +| task | CUST-WP-0021-T05 | done | — | workplans/CUST-WP-0021-multi-host-repo-paths.md | +| task | CUST-WP-0022-T01 | done | — | workplans/CUST-WP-0022-capability-requests.md | +| task | CUST-WP-0022-T02 | done | — | workplans/CUST-WP-0022-capability-requests.md | +| task | CUST-WP-0022-T03 | done | — | workplans/CUST-WP-0022-capability-requests.md | +| task | CUST-WP-0022-T04 | done | — | workplans/CUST-WP-0022-capability-requests.md | +| task | CUST-WP-0022-T05 | done | — | workplans/CUST-WP-0022-capability-requests.md | +| task | CUST-WP-0022-T06 | done | — | workplans/CUST-WP-0022-capability-requests.md | +| task | CUST-WP-0022-T07 | done | — | workplans/CUST-WP-0022-capability-requests.md | +| task | CUST-WP-0022-T08 | done | — | workplans/CUST-WP-0022-capability-requests.md | +| task | CUST-WP-0022-T09 | done | — | workplans/CUST-WP-0022-capability-requests.md | +| task | CUST-WP-0022-T10 | done | — | workplans/CUST-WP-0022-capability-requests.md | +| task | CUST-WP-0023-T01 | done | — | workplans/CUST-WP-0023-tpsc.md | +| task | CUST-WP-0023-T02 | done | — | workplans/CUST-WP-0023-tpsc.md | +| task | CUST-WP-0023-T03 | done | — | workplans/CUST-WP-0023-tpsc.md | +| task | CUST-WP-0023-T04 | done | — | workplans/CUST-WP-0023-tpsc.md | +| task | CUST-WP-0023-T05 | done | — | workplans/CUST-WP-0023-tpsc.md | +| task | CUST-WP-0023-T06 | done | — | workplans/CUST-WP-0023-tpsc.md | +| task | CUST-WP-0023-T07 | done | — | workplans/CUST-WP-0023-tpsc.md | +| task | CUST-WP-0023-T08 | done | — | workplans/CUST-WP-0023-tpsc.md | +| task | CUST-WP-0023-T09 | done | — | workplans/CUST-WP-0023-tpsc.md | +| task | CUST-WP-0023-T10 | done | — | workplans/CUST-WP-0023-tpsc.md | +| task | CUST-WP-0024-T01 | done | — | workplans/CUST-WP-0024-repo-doi-gate.md | +| task | CUST-WP-0024-T02 | done | — | workplans/CUST-WP-0024-repo-doi-gate.md | +| task | CUST-WP-0024-T03 | done | — | workplans/CUST-WP-0024-repo-doi-gate.md | +| task | CUST-WP-0024-T04 | done | — | workplans/CUST-WP-0024-repo-doi-gate.md | +| task | CUST-WP-0024-T05 | done | — | workplans/CUST-WP-0024-repo-doi-gate.md | +| task | CUST-WP-0026-T01 | done | — | workplans/CUST-WP-0026-distributed-consistency.md | +| task | CUST-WP-0026-T02 | done | — | workplans/CUST-WP-0026-distributed-consistency.md | +| task | CUST-WP-0026-T03 | done | — | workplans/CUST-WP-0026-distributed-consistency.md | +| task | CUST-WP-0026-T04 | done | — | workplans/CUST-WP-0026-distributed-consistency.md | +| task | CUST-WP-0026-T05 | done | — | workplans/CUST-WP-0026-distributed-consistency.md | +| task | CUST-WP-0027-T01 | done | — | workplans/CUST-WP-0027-capability-request-dispute.md | +| task | CUST-WP-0027-T02 | done | — | workplans/CUST-WP-0027-capability-request-dispute.md | +| task | CUST-WP-0027-T03 | done | — | workplans/CUST-WP-0027-capability-request-dispute.md | +| task | CUST-WP-0027-T04 | done | — | workplans/CUST-WP-0027-capability-request-dispute.md | +| task | CUST-WP-0027-T05 | done | — | workplans/CUST-WP-0027-capability-request-dispute.md | +| task | CUST-WP-0029-T01 | done | — | workplans/CUST-WP-0029-token-consumption-tracking.md | +| task | CUST-WP-0029-T02 | done | — | workplans/CUST-WP-0029-token-consumption-tracking.md | +| task | CUST-WP-0029-T03 | done | — | workplans/CUST-WP-0029-token-consumption-tracking.md | +| task | CUST-WP-0029-T04 | done | — | workplans/CUST-WP-0029-token-consumption-tracking.md | +| task | CUST-WP-0029-T05 | done | — | workplans/CUST-WP-0029-token-consumption-tracking.md | +| task | CUST-WP-0029-T06 | done | — | workplans/CUST-WP-0029-token-consumption-tracking.md | +| task | CUST-WP-0029-T07 | done | — | workplans/CUST-WP-0029-token-consumption-tracking.md | +| task | CUST-WP-0029-T08 | done | — | workplans/CUST-WP-0029-token-consumption-tracking.md | +| task | T01 | done | — | workplans/CUST-WP-0030-dashboard-entity-list-ux.md | +| task | T02 | done | — | workplans/CUST-WP-0030-dashboard-entity-list-ux.md | +| task | T03 | done | — | workplans/CUST-WP-0030-dashboard-entity-list-ux.md | +| task | T04 | done | — | workplans/CUST-WP-0030-dashboard-entity-list-ux.md | +| task | T05 | done | — | workplans/CUST-WP-0030-dashboard-entity-list-ux.md | +| task | T06 | done | — | workplans/CUST-WP-0030-dashboard-entity-list-ux.md | +| task | T07 | done | — | workplans/CUST-WP-0030-dashboard-entity-list-ux.md | +| task | T08 | done | — | workplans/CUST-WP-0030-dashboard-entity-list-ux.md | +| task | T09 | done | — | workplans/CUST-WP-0030-dashboard-entity-list-ux.md | +| task | T01 | done | — | workplans/CUST-WP-0031-capability-registry.md | +| task | T02 | done | — | workplans/CUST-WP-0031-capability-registry.md | +| task | T03 | done | — | workplans/CUST-WP-0031-capability-registry.md | +| task | T04 | done | — | workplans/CUST-WP-0031-capability-registry.md | +| task | T05 | done | — | workplans/CUST-WP-0031-capability-registry.md | +| task | T06 | done | — | workplans/CUST-WP-0031-capability-registry.md | +| task | T07 | done | — | workplans/CUST-WP-0031-capability-registry.md | +| task | CUST-WP-0032-T01 | done | — | workplans/CUST-WP-0032-haskell-build-machine.md | +| task | CUST-WP-0032-T02 | done | — | workplans/CUST-WP-0032-haskell-build-machine.md | +| task | CUST-WP-0032-T03 | done | — | workplans/CUST-WP-0032-haskell-build-machine.md | +| task | CUST-WP-0032-T04 | done | — | workplans/CUST-WP-0032-haskell-build-machine.md | +| task | CUST-WP-0032-T05 | done | — | workplans/CUST-WP-0032-haskell-build-machine.md | +| task | CUST-WP-0032-T06 | done | — | workplans/CUST-WP-0032-haskell-build-machine.md | +| task | CUST-WP-0032-T07 | done | — | workplans/CUST-WP-0032-haskell-build-machine.md | +| task | CUST-WP-0032-T08 | done | — | workplans/CUST-WP-0032-haskell-build-machine.md | +| task | CUST-WP-0032-T09 | done | — | workplans/CUST-WP-0032-haskell-build-machine.md | +| task | CUST-WP-0032-T10 | done | — | workplans/CUST-WP-0032-haskell-build-machine.md | +| task | CUST-WP-0032-T11 | done | — | workplans/CUST-WP-0032-haskell-build-machine.md | +| task | CUST-WP-0032-T12 | done | — | workplans/CUST-WP-0032-haskell-build-machine.md | +| task | CUST-WP-0032-T13 | done | — | workplans/CUST-WP-0032-haskell-build-machine.md | +| task | CUST-WP-0032-T14 | done | — | workplans/CUST-WP-0032-haskell-build-machine.md | +| task | CUST-WP-0032-T15 | done | — | workplans/CUST-WP-0032-haskell-build-machine.md | +| task | CUST-WP-0033-T01 | done | — | workplans/CUST-WP-0033-interface-change-registry.md | +| task | CUST-WP-0033-T02 | done | — | workplans/CUST-WP-0033-interface-change-registry.md | +| task | CUST-WP-0033-T03 | done | — | workplans/CUST-WP-0033-interface-change-registry.md | +| task | CUST-WP-0033-T04 | done | — | workplans/CUST-WP-0033-interface-change-registry.md | +| task | CUST-WP-0033-T05 | done | — | workplans/CUST-WP-0033-interface-change-registry.md | +| task | CUST-WP-0033-T06 | done | — | workplans/CUST-WP-0033-interface-change-registry.md | +| task | CUST-WP-0039-T1 | done | — | workplans/CUST-WP-0039-dashboard-poll-optimization.md | +| task | CUST-WP-0039-T2 | done | — | workplans/CUST-WP-0039-dashboard-poll-optimization.md | +| task | CUST-WP-0039-T3 | done | — | workplans/CUST-WP-0039-dashboard-poll-optimization.md | +| task | CUST-WP-0039-T4 | done | — | workplans/CUST-WP-0039-dashboard-poll-optimization.md | +| task | CUST-WP-0039-T5 | done | — | workplans/CUST-WP-0039-dashboard-poll-optimization.md | +| task | CUST-WP-0039-T6 | done | — | workplans/CUST-WP-0039-dashboard-poll-optimization.md | +| task | T01 | done | — | workplans/CUST-WP-0040-state-hub-nats-activity-core-integration.md | +| task | T02 | done | — | workplans/CUST-WP-0040-state-hub-nats-activity-core-integration.md | +| task | T03 | done | — | workplans/CUST-WP-0040-state-hub-nats-activity-core-integration.md | +| task | T04 | done | — | workplans/CUST-WP-0040-state-hub-nats-activity-core-integration.md | +| task | T05 | done | — | workplans/CUST-WP-0040-state-hub-nats-activity-core-integration.md | +| task | T06 | done | — | workplans/CUST-WP-0040-state-hub-nats-activity-core-integration.md | +| task | CUST-WP-0041-T1 | done | — | workplans/CUST-WP-0041-api-performance.md | +| task | CUST-WP-0041-T2 | done | — | workplans/CUST-WP-0041-api-performance.md | +| task | CUST-WP-0041-T3 | done | — | workplans/CUST-WP-0041-api-performance.md | +| task | CUST-WP-0041-T4 | done | — | workplans/CUST-WP-0041-api-performance.md | +| task | CUST-WP-0043-T01 | done | — | workplans/CUST-WP-0043-state-hub-repo-extraction.md | +| task | CUST-WP-0043-T02 | done | — | workplans/CUST-WP-0043-state-hub-repo-extraction.md | +| task | CUST-WP-0043-T03 | done | — | workplans/CUST-WP-0043-state-hub-repo-extraction.md | +| task | CUST-WP-0043-T04 | done | — | workplans/CUST-WP-0043-state-hub-repo-extraction.md | +| task | CUST-WP-0043-T05 | done | — | workplans/CUST-WP-0043-state-hub-repo-extraction.md | +| task | CUST-WP-0043-T06 | done | — | workplans/CUST-WP-0043-state-hub-repo-extraction.md | +| task | CUST-WP-0043-T07 | done | — | workplans/CUST-WP-0043-state-hub-repo-extraction.md | +| task | CUST-WP-0043-T08 | done | — | workplans/CUST-WP-0043-state-hub-repo-extraction.md | +| task | CUST-WP-0043-T09 | done | — | workplans/CUST-WP-0043-state-hub-repo-extraction.md | +| task | CUST-WP-0044-T01 | done | — | workplans/CUST-WP-0044-daily-statehub-wsjf-triage.md | +| task | CUST-WP-0044-T02 | done | — | workplans/CUST-WP-0044-daily-statehub-wsjf-triage.md | +| task | CUST-WP-0044-T03 | done | — | workplans/CUST-WP-0044-daily-statehub-wsjf-triage.md | +| task | CUST-WP-0044-T04 | done | — | workplans/CUST-WP-0044-daily-statehub-wsjf-triage.md | +| task | CUST-WP-0044-T05 | done | — | workplans/CUST-WP-0044-daily-statehub-wsjf-triage.md | +| task | CUST-WP-0044-T06 | done | — | workplans/CUST-WP-0044-daily-statehub-wsjf-triage.md | +| task | CUST-WP-0045-T01 | done | — | workplans/CUST-WP-0045-activity-core-daily-triage-runner.md | +| task | CUST-WP-0045-T02 | done | — | workplans/CUST-WP-0045-activity-core-daily-triage-runner.md | +| task | CUST-WP-0045-T03 | done | — | workplans/CUST-WP-0045-activity-core-daily-triage-runner.md | +| task | CUST-WP-0045-T04 | done | — | workplans/CUST-WP-0045-activity-core-daily-triage-runner.md | +| task | CUST-WP-0045-T05 | done | — | workplans/CUST-WP-0045-activity-core-daily-triage-runner.md | +| task | CUST-WP-0045-T06 | done | — | workplans/CUST-WP-0045-activity-core-daily-triage-runner.md | +| task | CUST-WP-0045-T07 | done | — | workplans/CUST-WP-0045-activity-core-daily-triage-runner.md | +| task | CUST-WP-0045-T08 | done | — | workplans/CUST-WP-0045-activity-core-daily-triage-runner.md | +| task | CUST-WP-0046-T01 | done | — | workplans/CUST-WP-0046-hourly-recently-on-scope-activity-core.md | +| task | CUST-WP-0046-T02 | done | — | workplans/CUST-WP-0046-hourly-recently-on-scope-activity-core.md | +| task | CUST-WP-0046-T03 | done | — | workplans/CUST-WP-0046-hourly-recently-on-scope-activity-core.md | +| task | CUST-WP-0046-T04 | done | — | workplans/CUST-WP-0046-hourly-recently-on-scope-activity-core.md | +| task | CUST-WP-0046-T05 | done | — | workplans/CUST-WP-0046-hourly-recently-on-scope-activity-core.md | +| task | CUST-WP-0046-T06 | done | — | workplans/CUST-WP-0046-hourly-recently-on-scope-activity-core.md | +| task | CUST-WP-0046-T07 | done | — | workplans/CUST-WP-0046-hourly-recently-on-scope-activity-core.md | +| task | CUST-WP-0047-T01 | done | — | workplans/CUST-WP-0047-ops-hub-service-inventory-now-view.md | +| task | CUST-WP-0047-T02 | done | — | workplans/CUST-WP-0047-ops-hub-service-inventory-now-view.md | +| task | CUST-WP-0047-T03 | done | — | workplans/CUST-WP-0047-ops-hub-service-inventory-now-view.md | +| task | CUST-WP-0047-T04 | done | — | workplans/CUST-WP-0047-ops-hub-service-inventory-now-view.md | +| task | CUST-WP-0047-T05 | cancel | — | workplans/CUST-WP-0047-ops-hub-service-inventory-now-view.md | +| task | CUST-WP-0047-T06 | done | — | workplans/CUST-WP-0047-ops-hub-service-inventory-now-view.md | +| task | CUST-WP-0047-T07 | done | — | workplans/CUST-WP-0047-ops-hub-service-inventory-now-view.md | +| task | CUST-WP-0048-T01 | done | — | workplans/CUST-WP-0048-hub-core-state-hub-import-refactor.md | +| task | CUST-WP-0048-T02 | done | — | workplans/CUST-WP-0048-hub-core-state-hub-import-refactor.md | +| task | CUST-WP-0048-T03 | done | — | workplans/CUST-WP-0048-hub-core-state-hub-import-refactor.md | +| task | CUST-WP-0048-T04 | done | — | workplans/CUST-WP-0048-hub-core-state-hub-import-refactor.md | +| task | CUST-WP-0048-T05 | done | — | workplans/CUST-WP-0048-hub-core-state-hub-import-refactor.md | +| task | CUST-WP-0048-T06 | done | — | workplans/CUST-WP-0048-hub-core-state-hub-import-refactor.md | +| task | CUST-WP-0048-T07 | done | — | workplans/CUST-WP-0048-hub-core-state-hub-import-refactor.md | +| task | CUST-WP-0048-T08 | done | — | workplans/CUST-WP-0048-hub-core-state-hub-import-refactor.md | +| task | CUST-WP-0049-T01 | done | — | workplans/CUST-WP-0049-interhub-bootstrap-access-lane.md | +| task | CUST-WP-0049-T02 | done | — | workplans/CUST-WP-0049-interhub-bootstrap-access-lane.md | +| task | CUST-WP-0049-T03 | done | — | workplans/CUST-WP-0049-interhub-bootstrap-access-lane.md | +| task | CUST-WP-0049-T04 | done | — | workplans/CUST-WP-0049-interhub-bootstrap-access-lane.md | +| task | CUST-WP-0049-T05 | done | — | workplans/CUST-WP-0049-interhub-bootstrap-access-lane.md | +| task | CUST-WP-0049-T06 | cancel | — | workplans/CUST-WP-0049-interhub-bootstrap-access-lane.md | +| task | CUST-WP-0050-T01 | done | — | workplans/CUST-WP-0050-repo-classification-registration-redesign.md | +| task | CUST-WP-0050-T02 | done | — | workplans/CUST-WP-0050-repo-classification-registration-redesign.md | +| task | CUST-WP-0050-T03 | cancel | — | workplans/CUST-WP-0050-repo-classification-registration-redesign.md | +| task | CUST-WP-0050-T04 | cancel | — | workplans/CUST-WP-0050-repo-classification-registration-redesign.md | +| task | CUST-WP-0050-T05 | cancel | — | workplans/CUST-WP-0050-repo-classification-registration-redesign.md | +| task | CUST-WP-0050-T06 | cancel | — | workplans/CUST-WP-0050-repo-classification-registration-redesign.md | +| task | CUST-WP-0050-T07 | cancel | — | workplans/CUST-WP-0050-repo-classification-registration-redesign.md | +| task | CUST-WP-0050-T08 | cancel | — | workplans/CUST-WP-0050-repo-classification-registration-redesign.md | +| task | CUST-WP-0050-T09 | cancel | — | workplans/CUST-WP-0050-repo-classification-registration-redesign.md | +| task | CUST-WP-0050-T10 | cancel | — | workplans/CUST-WP-0050-repo-classification-registration-redesign.md | +| task | CUST-WP-0050-T11 | done | — | workplans/CUST-WP-0050-repo-classification-registration-redesign.md | +| task | CUST-WP-0051-T01 | done | — | workplans/CUST-WP-0051-infrastructure-stabilization-metaplan.md | +| task | CUST-WP-0051-T02 | done | — | workplans/CUST-WP-0051-infrastructure-stabilization-metaplan.md | +| task | CUST-WP-0051-T03 | done | — | workplans/CUST-WP-0051-infrastructure-stabilization-metaplan.md | +| task | CUST-WP-0051-T04 | done | — | workplans/CUST-WP-0051-infrastructure-stabilization-metaplan.md | +| task | CUST-WP-0051-T05 | done | — | workplans/CUST-WP-0051-infrastructure-stabilization-metaplan.md | +| task | CUST-WP-0051-T06 | done | — | workplans/CUST-WP-0051-infrastructure-stabilization-metaplan.md | +| task | CUST-WP-0051-T07 | done | — | workplans/CUST-WP-0051-infrastructure-stabilization-metaplan.md | +| task | CUST-WP-0051-T08 | done | — | workplans/CUST-WP-0051-infrastructure-stabilization-metaplan.md | +| task | CUST-WP-0052-T01 | done | — | workplans/CUST-WP-0052-core-hub-fos-reset.md | +| task | CUST-WP-0052-T02 | done | — | workplans/CUST-WP-0052-core-hub-fos-reset.md | +| task | CUST-WP-0052-T03 | done | — | workplans/CUST-WP-0052-core-hub-fos-reset.md | +| task | CUST-WP-0052-T04 | done | — | workplans/CUST-WP-0052-core-hub-fos-reset.md | +| task | CUST-WP-0052-T05 | done | — | workplans/CUST-WP-0052-core-hub-fos-reset.md | +| task | CUST-WP-0052-T06 | done | — | workplans/CUST-WP-0052-core-hub-fos-reset.md | +| task | CUST-WP-0052-T07 | done | — | workplans/CUST-WP-0052-core-hub-fos-reset.md | +| task | CUST-WP-0053-T01 | done | — | workplans/CUST-WP-0053-coordination-hygiene-improvements.md | +| task | CUST-WP-0053-T02 | done | — | workplans/CUST-WP-0053-coordination-hygiene-improvements.md | +| task | CUST-WP-0053-T03 | done | — | workplans/CUST-WP-0053-coordination-hygiene-improvements.md | +| task | CUST-WP-0053-T04 | done | — | workplans/CUST-WP-0053-coordination-hygiene-improvements.md | +| task | CUST-WP-0053-T05 | done | — | workplans/CUST-WP-0053-coordination-hygiene-improvements.md | +| task | CUST-WP-0055-T01 | done | — | workplans/CUST-WP-0055-workplan-terminology-fleet-refactor.md | +| task | CUST-WP-0055-T02 | done | — | workplans/CUST-WP-0055-workplan-terminology-fleet-refactor.md | +| task | CUST-WP-0055-T03 | done | — | workplans/CUST-WP-0055-workplan-terminology-fleet-refactor.md | +| task | CUST-WP-0055-T04 | done | — | workplans/CUST-WP-0055-workplan-terminology-fleet-refactor.md | +| task | CUST-WP-0055-T05 | done | — | workplans/CUST-WP-0055-workplan-terminology-fleet-refactor.md | +| task | CUST-WP-0055-T06 | done | — | workplans/CUST-WP-0055-workplan-terminology-fleet-refactor.md | +| task | CUST-WP-0055-T07 | done | — | workplans/CUST-WP-0055-workplan-terminology-fleet-refactor.md | +| task | CUST-WP-0055-T08 | done | — | workplans/CUST-WP-0055-workplan-terminology-fleet-refactor.md | +| task | CUST-WP-0056-T01 | done | — | workplans/CUST-WP-0056-daily-todo-md-stale-review.md | +| task | CUST-WP-0056-T02 | done | — | workplans/CUST-WP-0056-daily-todo-md-stale-review.md | +| task | CUST-WP-0056-T03 | done | — | workplans/CUST-WP-0056-daily-todo-md-stale-review.md | +| task | CUST-WP-0059-T01 | done | — | workplans/CUST-WP-0059-forgejo-ci-publish-cutover-tail.md | +| task | CUST-WP-0059-T02 | done | — | workplans/CUST-WP-0059-forgejo-ci-publish-cutover-tail.md | +| task | CUST-WP-0059-T03 | done | — | workplans/CUST-WP-0059-forgejo-ci-publish-cutover-tail.md | +| task | CUST-WP-0059-T04 | done | — | workplans/CUST-WP-0059-forgejo-ci-publish-cutover-tail.md | +| task | CUST-WP-0059-T05 | done | — | workplans/CUST-WP-0059-forgejo-ci-publish-cutover-tail.md | +| task | CUST-WP-0059-T06 | done | — | workplans/CUST-WP-0059-forgejo-ci-publish-cutover-tail.md | +| task | CUST-WP-0060-T01 | done | — | workplans/CUST-WP-0060-work-record-canonization.md | +| task | CUST-WP-0060-T02 | done | — | workplans/CUST-WP-0060-work-record-canonization.md | +| task | CUST-WP-0060-T03 | done | — | workplans/CUST-WP-0060-work-record-canonization.md | +| task | CUST-WP-0060-T04 | done | — | workplans/CUST-WP-0060-work-record-canonization.md | +| task | CUST-WP-0060-T05 | done | — | workplans/CUST-WP-0060-work-record-canonization.md | +| task | CUST-WP-0060-T06 | done | — | workplans/CUST-WP-0060-work-record-canonization.md | +| task | CUST-WP-0060-T07 | done | — | workplans/CUST-WP-0060-work-record-canonization.md | +| task | CUST-WP-0061-T01 | done | — | workplans/CUST-WP-0061-work-record-stage3-intake-promotion-index.md | +| task | CUST-WP-0061-T02 | done | — | workplans/CUST-WP-0061-work-record-stage3-intake-promotion-index.md | +| task | CUST-WP-0061-T03 | done | — | workplans/CUST-WP-0061-work-record-stage3-intake-promotion-index.md | +| task | CUST-WP-0061-T04 | done | — | workplans/CUST-WP-0061-work-record-stage3-intake-promotion-index.md | +| task | CUST-WP-0061-T05 | done | — | workplans/CUST-WP-0061-work-record-stage3-intake-promotion-index.md | +| task | CUST-WP-0061-T06 | done | — | workplans/CUST-WP-0061-work-record-stage3-intake-promotion-index.md | +| task | CUST-WP-0062-T01 | done | — | workplans/CUST-WP-0062-sbom-nexus-daily-catchup.md | +| task | CUST-WP-0062-T02 | done | — | workplans/CUST-WP-0062-sbom-nexus-daily-catchup.md | +| task | CUST-WP-0062-T03 | done | — | workplans/CUST-WP-0062-sbom-nexus-daily-catchup.md | +| task | CUST-WP-0062-T04 | done | — | workplans/CUST-WP-0062-sbom-nexus-daily-catchup.md | +| task | CUST-WP-0062-T05 | done | — | workplans/CUST-WP-0062-sbom-nexus-daily-catchup.md | +| task | CUST-WP-0062-T06 | done | — | workplans/CUST-WP-0062-sbom-nexus-daily-catchup.md | +| task | CUST-WP-0063-T01 | done | — | workplans/CUST-WP-0063-inbox-governance-packets.md | +| task | CUST-WP-0063-T02 | done | — | workplans/CUST-WP-0063-inbox-governance-packets.md | +| task | CUST-WP-0063-T03 | done | — | workplans/CUST-WP-0063-inbox-governance-packets.md | +| task | CUST-WP-0063-T04 | done | — | workplans/CUST-WP-0063-inbox-governance-packets.md | +| task | CUST-WP-0063-T05 | done | — | workplans/CUST-WP-0063-inbox-governance-packets.md | +| task | CUST-WP-0064-T01 | done | — | workplans/CUST-WP-0064-sbom-controlled-scan-inputs.md | +| task | CUST-WP-0064-T02 | done | — | workplans/CUST-WP-0064-sbom-controlled-scan-inputs.md | +| task | CUST-WP-0064-T03 | done | — | workplans/CUST-WP-0064-sbom-controlled-scan-inputs.md | +| task | CUST-WP-0064-T04 | done | — | workplans/CUST-WP-0064-sbom-controlled-scan-inputs.md | +| task | CUST-WP-0065-T01 | done | — | workplans/CUST-WP-0065-reclassify-repos-and-guidance-docs-to-the-new-sector-domain.md | +| task | CUST-WP-0065-T02 | done | — | workplans/CUST-WP-0065-reclassify-repos-and-guidance-docs-to-the-new-sector-domain.md | +| task | CUST-WP-0065-T03 | done | — | workplans/CUST-WP-0065-reclassify-repos-and-guidance-docs-to-the-new-sector-domain.md | +| task | CUST-WP-0065-T04 | done | — | workplans/CUST-WP-0065-reclassify-repos-and-guidance-docs-to-the-new-sector-domain.md | +| task | CUST-WP-0066-T01 | done | — | workplans/CUST-WP-0066-repository-qualified-ad-hoc-identifiers.md | +| task | CUST-WP-0066-T02 | done | — | workplans/CUST-WP-0066-repository-qualified-ad-hoc-identifiers.md | +| task | CUST-WP-0066-T03 | done | — | workplans/CUST-WP-0066-repository-qualified-ad-hoc-identifiers.md | +| task | CUST-WP-0067-T01 | done | — | workplans/CUST-WP-0067-hub-authority-target-resolution.md | +| task | CUST-WP-0067-T02 | done | — | workplans/CUST-WP-0067-hub-authority-target-resolution.md | +| task | CUST-WP-0067-T03 | done | — | workplans/CUST-WP-0067-hub-authority-target-resolution.md | +| task | CUST-WP-0067-T04 | done | — | workplans/CUST-WP-0067-hub-authority-target-resolution.md | +| task | CUST-WP-0067-T05 | done | — | workplans/CUST-WP-0067-hub-authority-target-resolution.md | +| task | CUST-WP-0067-T06 | done | — | workplans/CUST-WP-0067-hub-authority-target-resolution.md | +| task | CUST-WP-0067-T07 | done | — | workplans/CUST-WP-0067-hub-authority-target-resolution.md | +| task | CUST-WP-0067-T08 | done | — | workplans/CUST-WP-0067-hub-authority-target-resolution.md | +| task | CUST-WP-0067-T09 | done | — | workplans/CUST-WP-0067-hub-authority-target-resolution.md | +| task | CUST-WP-0067-T10 | done | — | workplans/CUST-WP-0067-hub-authority-target-resolution.md | +| task | CUST-WP-0068-T01 | done | — | workplans/CUST-WP-0068-cache-only-work-record-recovery.md | +| task | CUST-WP-0068-T02 | done | — | workplans/CUST-WP-0068-cache-only-work-record-recovery.md | +| task | CUST-WP-0068-T03 | done | — | workplans/CUST-WP-0068-cache-only-work-record-recovery.md | +| task | CUST-WP-0068-T04 | done | — | workplans/CUST-WP-0068-cache-only-work-record-recovery.md | +| task | CUST-WP-0068-T05 | done | — | workplans/CUST-WP-0068-cache-only-work-record-recovery.md | +| task | CUST-WP-0068-T06 | done | — | workplans/CUST-WP-0068-cache-only-work-record-recovery.md | +| task | CUST-WP-0068-T07 | done | — | workplans/CUST-WP-0068-cache-only-work-record-recovery.md | +| task | CUST-WP-0068-T08 | done | — | workplans/CUST-WP-0068-cache-only-work-record-recovery.md | +| task | CUST-WP-0068-T09 | done | — | workplans/CUST-WP-0068-cache-only-work-record-recovery.md | +| task | CUST-WP-0070-T01 | done | — | workplans/CUST-WP-0070-publication-repo-category.md | +| task | CUST-WP-0070-T02 | done | — | workplans/CUST-WP-0070-publication-repo-category.md | +| task | CUST-WP-0070-T03 | done | — | workplans/CUST-WP-0070-publication-repo-category.md | +| task | CUST-WP-0071-T01 | done | — | workplans/CUST-WP-0071-measured-workload-sizing-and-weekly-review.md | +| task | CUST-WP-0071-T02 | wait | — | workplans/CUST-WP-0071-measured-workload-sizing-and-weekly-review.md | +| task | CUST-WP-0071-T03 | wait | — | workplans/CUST-WP-0071-measured-workload-sizing-and-weekly-review.md | +| task | CUST-WP-0071-T04 | wait | — | workplans/CUST-WP-0071-measured-workload-sizing-and-weekly-review.md | +| task | CUST-WP-0071-T05 | wait | — | workplans/CUST-WP-0071-measured-workload-sizing-and-weekly-review.md | +| task | CUST-WP-0072-T01 | done | — | workplans/CUST-WP-0072-fleet-flavor-and-depends-on-backfill.md | +| task | CUST-WP-0072-T02 | done | — | workplans/CUST-WP-0072-fleet-flavor-and-depends-on-backfill.md | +| task | CUST-WP-0072-T03 | done | — | workplans/CUST-WP-0072-fleet-flavor-and-depends-on-backfill.md | +| task | CUST-WP-0072-T04 | done | — | workplans/CUST-WP-0072-fleet-flavor-and-depends-on-backfill.md | +| task | CUST-WP-0073-T01 | done | — | workplans/CUST-WP-0073-agent-credential-separation.md | +| task | CUST-WP-0073-T02 | wait | — | workplans/CUST-WP-0073-agent-credential-separation.md | +| task | CUST-WP-0073-T03 | done | — | workplans/CUST-WP-0073-agent-credential-separation.md | +| task | CUST-WP-0073-T04 | wait | — | workplans/CUST-WP-0073-agent-credential-separation.md | +| task | CUST-WP-0073-T05 | wait | — | workplans/CUST-WP-0073-agent-credential-separation.md | +| task | CUST-WP-0074-T01 | done | — | workplans/CUST-WP-0074-qualified-wait-states.md | +| task | CUST-WP-0074-T02 | done | — | workplans/CUST-WP-0074-qualified-wait-states.md | +| task | CUST-WP-0074-T03 | done | — | workplans/CUST-WP-0074-qualified-wait-states.md | +| task | CUST-WP-0074-T04 | done | — | workplans/CUST-WP-0074-qualified-wait-states.md | +| task | CUST-WP-0074-T05 | done | — | workplans/CUST-WP-0074-qualified-wait-states.md | +| task | CUST-WP-0074-T06 | done | — | workplans/CUST-WP-0074-qualified-wait-states.md | +| task | CUST-WP-0075-T01 | done | — | workplans/CUST-WP-0075-dependency-graph-driven-unblocking.md | +| task | CUST-WP-0075-T02 | done | — | workplans/CUST-WP-0075-dependency-graph-driven-unblocking.md | +| task | CUST-WP-0075-T03 | done | — | workplans/CUST-WP-0075-dependency-graph-driven-unblocking.md | +| task | CUST-WP-0075-T04 | done | — | workplans/CUST-WP-0075-dependency-graph-driven-unblocking.md | +| task | CUST-WP-0075-T05 | done | — | workplans/CUST-WP-0075-dependency-graph-driven-unblocking.md | +| task | CUST-WP-0075-T06 | done | — | workplans/CUST-WP-0075-dependency-graph-driven-unblocking.md | +| task | CUST-WP-0076-T01 | done | — | workplans/CUST-WP-0076-provisioning-window-cadence.md | +| task | CUST-WP-0077-T01 | done | — | workplans/CUST-WP-0077-dependency-root-execution-wave.md | +| task | CUST-WP-0077-T02 | done | — | workplans/CUST-WP-0077-dependency-root-execution-wave.md | +| task | CUST-WP-0077-T03 | done | — | workplans/CUST-WP-0077-dependency-root-execution-wave.md | +| task | CUST-WP-0077-T04 | done | — | workplans/CUST-WP-0077-dependency-root-execution-wave.md | +| task | CUST-WP-0077-T05 | done | — | workplans/CUST-WP-0077-dependency-root-execution-wave.md | +| task | CUST-WP-0078-T01 | done | — | workplans/CUST-WP-0078-security-collaboration-consolidation.md | +| task | CUST-WP-0078-T02 | done | — | workplans/CUST-WP-0078-security-collaboration-consolidation.md | +| task | CUST-WP-0078-T03 | done | — | workplans/CUST-WP-0078-security-collaboration-consolidation.md | +| task | CUST-WP-0078-T04 | done | — | workplans/CUST-WP-0078-security-collaboration-consolidation.md | +| task | CUST-WP-0079-T01 | done | — | workplans/CUST-WP-0079-deterministic-security-flow.md | +| task | CUST-WP-0079-T02 | done | — | workplans/CUST-WP-0079-deterministic-security-flow.md | +| task | CUST-WP-0079-T03 | wait | — | workplans/CUST-WP-0079-deterministic-security-flow.md | +| task | CUST-WP-0079-T04 | wait | — | workplans/CUST-WP-0079-deterministic-security-flow.md | +| task | CUST-WP-0079-T05 | wait | — | workplans/CUST-WP-0079-deterministic-security-flow.md | +| task | CUST-WP-0079-T06 | wait | — | workplans/CUST-WP-0079-deterministic-security-flow.md | +| task | CUST-WP-0079-T07 | done | — | workplans/CUST-WP-0079-deterministic-security-flow.md | +| task | CUST-WP-0080-T01 | done | — | workplans/CUST-WP-0080-netkingdom-security-blueprint-and-gap-closure.md | +| task | CUST-WP-0080-T02 | done | — | workplans/CUST-WP-0080-netkingdom-security-blueprint-and-gap-closure.md | +| task | CUST-WP-0080-T03 | progress | — | workplans/CUST-WP-0080-netkingdom-security-blueprint-and-gap-closure.md | +| task | CUST-WP-0080-T04 | todo | — | workplans/CUST-WP-0080-netkingdom-security-blueprint-and-gap-closure.md | +| task | CUST-WP-0080-T05 | progress | — | workplans/CUST-WP-0080-netkingdom-security-blueprint-and-gap-closure.md | +| task | CUST-WP-0080-T06 | progress | — | workplans/CUST-WP-0080-netkingdom-security-blueprint-and-gap-closure.md | +| task | CUST-WP-0081-T01 | done | — | workplans/CUST-WP-0081-netkingdom-architecture-review-and-reuse.md | +| task | CUST-WP-0081-T02 | done | — | workplans/CUST-WP-0081-netkingdom-architecture-review-and-reuse.md | +| task | CUST-WP-0081-T03 | done | — | workplans/CUST-WP-0081-netkingdom-architecture-review-and-reuse.md | +| task | THE-WP-0001-T01 | done | — | workplans/THE-WP-0001-federation-interface.md | +| task | THE-WP-0001-T02 | done | — | workplans/THE-WP-0001-federation-interface.md | +| intake | CUST-IN-0001 | closed | green | intake-legacy-suggestions-migration.md | +| intake | CUST-IN-0002 | closed | green | intake-legacy-suggestions-migration.md | +| intake | CUST-IN-0003 | closed | green | intake-legacy-suggestions-migration.md | +| intake | CUST-IN-0004 | routed | green | intake-legacy-suggestions-migration.md | +| intake | CUST-IN-0005 | closed | green | intake-legacy-suggestions-migration.md | +| intake | CUST-IN-0006 | closed | green | intake-legacy-suggestions-migration.md | +| intake | CUST-IN-0007 | closed | green | intake-legacy-suggestions-migration.md | +| intake | CUST-IN-0008 | closed | blue | intake-legacy-suggestions-migration.md | +| intake | CUST-IN-0009 | closed | blue | intake-legacy-suggestions-migration.md | +| intake | CUST-IN-0010 | closed | green | intake-legacy-suggestions-migration.md | +| intake | CUST-IN-0011 | routed | red | intakes.md | +| intake | CUST-IN-0012 | closed | green | intakes.md | +| intake | CUST-IN-0013 | closed | blue | intakes.md | +| intake | CUST-IN-0014 | closed | blue | intakes.md | +| intake | CUST-IN-0015 | open | blue | intakes.md | +| intake | CUST-IN-0016 | open | green | intakes.md | +| intake | CUST-IN-0017 | open | green | intakes.md | +| intake | CUST-IN-0018 | open | green | intakes.md | diff --git a/docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/publication-backup/originals.json b/docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/publication-backup/originals.json new file mode 100644 index 00000000..5a19a7b4 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/publication-backup/originals.json @@ -0,0 +1,10 @@ +[ + { + "path": ".custodian-brief.md", + "index": "100644 6aad3a5788fad3abd60c0f1270f160062dc0168b 0\t.custodian-brief.md" + }, + { + "path": "WORK-RECORDS.md", + "index": "100644 d535e0f2dcc3d454e041a8f1b547b5dea4493380 0\tWORK-RECORDS.md" + } +] diff --git a/docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/publication-commit.json b/docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/publication-commit.json new file mode 100644 index 00000000..f8abcbde --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/publication-commit.json @@ -0,0 +1,45 @@ +{ + "exit_code": 0, + "commit": "16236ef479135df4361686aa69e9eeb987f33129", + "base": "90490002c16d353d675f4295f395d1917d30b9ef", + "files": [ + { + "path": "docs/netkingdom-target-implementation.md", + "sha256": "798093602f9c11d086af960b97a97f1a1d116962136e01de1b336df8a9603cec" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/publication-summary.json", + "sha256": "69f2797c42f6ce33b731956457fc958f189202463845ae7a7c6cc930673651aa" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/public-digest-refinement.json", + "sha256": "770a5831999df8a8dc1b66dc65b738f58bedd4eb0dab56127f8d37b2b10107b2" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/publication-refinement.json", + "sha256": "957d1347771eecb16cb50713dc3cbaa43e96c3b3e162eb357be57bf12632a721" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/dirty-workplan-refinement.json", + "sha256": "c90e021d41a3218f8a1e3b13af00dc7cf3a383a8bf0efe52c583c7f0d63dfc97" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/metadata-reconstruction-failure.json", + "sha256": "ab943a848c811e22ec7ef4273bf2097ec20ac76902947491de01852c270b26b4" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/reconstruct_public_metadata.py", + "sha256": "ec20b3378ece29dcf7faf4ad5e610e1e85bfa260121f2b8c537665f9530466e0" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/recover_pending.py", + "sha256": "1cfad4dd82966efcea13dfe02d702a873a0fa6a422e654551fc1c9f530052a0b" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/verify_current.py", + "sha256": "8aaa076520f5c954ebca6b1cf5b28df7223b0162e4edba4c7307a5a80cff6c43" + } + ], + "stdout": "[main 16236ef] Record verified consumer source and exact public evidence provenance\n 9 files changed, 793 insertions(+)\n create mode 100644 docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/dirty-workplan-refinement.json\n create mode 100644 docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/metadata-reconstruction-failure.json\n create mode 100644 docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/public-digest-refinement.json\n create mode 100644 docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/publication-refinement.json\n create mode 100644 docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/publication-summary.json\n create mode 100644 docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/reconstruct_public_metadata.py\n create mode 100644 docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/recover_pending.py\n create mode 100644 docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/verify_current.py\n", + "stderr": "{\"blocked_blob_ids\": [], \"scanned_blobs\": 9, \"scanner_sha256\": \"88f91962aa2f93ac6ab281d553b9e125f5197bbbce38f9f2437f7299c32e5509\", \"scanner_version\": \"8.30.1\", \"scope\": \"changed-staged-blobs\", \"status\": \"clean\"}\n" +} diff --git a/docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/state.json b/docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/state.json new file mode 100644 index 00000000..4de965a7 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/state.json @@ -0,0 +1,47 @@ +{ + "head": "90490002c16d353d675f4295f395d1917d30b9ef", + "completed": [ + { + "batch": 1, + "commit": "518369dbee08b7f7690ee70d0cbc405ea4158397", + "files": 18, + "publication": "verified as unchanged ancestor of the exact projected notes commit" + }, + { + "batch": 2, + "commit": "1513dad6528ab766a7631e88d9c04be4bc605f42", + "files": 2, + "required_consistency": "passed" + }, + { + "batch": 3, + "commit": "6548540fd46c7fcd365f399516a5d36e38d1e7fd", + "files": 18 + }, + { + "batch": 4, + "commit": "14670441cd567621ca86e466b113944ec8363798", + "files": 18 + }, + { + "batch": 5, + "commit": "11a485f1dd9267cc63f9beb0436aa17c0d3317cb", + "files": 8 + }, + { + "batch": 6, + "commit": "31f5cbf7b52c909ceebfdfc39ef0100908f63d5b", + "files": 1 + }, + { + "batch": 7, + "commit": "0caed1f5300c1f5a6ededcca7948b120a126b85d", + "files": 18 + }, + { + "batch": 8, + "commit": "90490002c16d353d675f4295f395d1917d30b9ef", + "files": 13 + } + ] +} diff --git a/docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/verify_current_r2.py b/docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/verify_current_r2.py new file mode 100644 index 00000000..9ccf1b10 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/owner-followthrough-publication-r1/verify_current_r2.py @@ -0,0 +1,120 @@ +"""Verify this source checkpoint, current primary bindings and original edits.""" +from pathlib import Path +import datetime +import hashlib +import json +import re +import subprocess +import urllib.request + +ROOT = Path('/home/worsch/the-custodian') +HERE = Path(__file__).resolve().parent +summary = json.loads((HERE / 'publication-summary.json').read_text()) + + +def git(root, *args): + return subprocess.check_output(['git', '-C', str(root), *args], + stderr=subprocess.PIPE, timeout=45).decode().strip() + + +def get(path): + with urllib.request.urlopen('http://127.0.0.1:8000' + path, timeout=20) as response: + return json.load(response) + + +snapshot = summary['root_commit'].removeprefix('git:') +head = git(ROOT, 'rev-parse', 'HEAD') +projection = json.loads((HERE / 'close-sync.json').read_text()) +receipt = projection['receipt'] +assert projection['ok'] and projection['status'] in ('applied', 'noop') +assert projection['commit'] == receipt['expected_commit'] == receipt['derived_commit'] == head +assert (receipt['instance_role'], receipt['instance_label']) == ('primary', 'railiance01') +assert receipt['outcome']['counts']['retired'] == 0 +assert git(ROOT, 'ls-remote', 'origin', 'refs/heads/main').split()[0] == head +exceptions = {row['path']: row['sha256'] for row in summary['current_source_exceptions']} +for row in summary['files']: + committed = subprocess.check_output(['git', '-C', str(ROOT), 'show', snapshot + ':' + row['path']]) + assert 'sha256:' + hashlib.sha256(committed).hexdigest() == row['sha256'] + current_expected = exceptions.get(row['path'], row['sha256']) + assert 'sha256:' + hashlib.sha256((ROOT / row['path']).read_bytes()).hexdigest() == current_expected + current_committed = subprocess.check_output(['git', '-C', str(ROOT), 'show', head + ':' + row['path']]) + assert 'sha256:' + hashlib.sha256(current_committed).hexdigest() == current_expected +protected = json.loads((HERE.parent / 'source-corrections-publication-r1/protected-state.json').read_text()) +for row in protected: + assert hashlib.sha256((ROOT / row['path']).read_bytes()).hexdigest() == row['sha256'] + assert git(ROOT, 'ls-files', '--stage', '--', row['path']) == row['index'] +assert set(git(ROOT, 'diff', '--name-only', 'HEAD').splitlines()) == {r['path'] for r in protected} +for owner, expected in summary['expected_owner_heads'].items(): + path = ROOT.parent / owner + assert git(path, 'rev-parse', 'HEAD') == expected.removeprefix('git:') + assert git(path, 'ls-remote', 'origin', 'refs/heads/main').split()[0] == expected.removeprefix('git:') +checks = [] +for owner, filename, count in [ + ('the-custodian', 'CUST-WP-0080-netkingdom-security-blueprint-and-gap-closure.md', 6), + ('ops-warden', 'WARDEN-WP-0044-security-case-follow-through.md', 4), + ('net-kingdom', 'NK-WP-0048-security-case-types.md', 39), + ('key-cape', 'KEY-WP-0039-openbao-negative-probe-tokens.md', 3), + ('railiance-platform', 'RPF-WP-0060-browser-case-platform-bindings.md', 4), +]: + text = (ROOT.parent / owner / 'workplans' / filename).read_text() + wp = re.search(r'state_hub_workstream_id: "([^"]+)"', text).group(1) + tasks = get('/tasks/?workplan_id=' + wp) + assert len(tasks) == count + by_uuid = {t['id']: t for t in tasks} + rows = [] + for block in re.findall(r'```task\n.*?```', text, re.S): + record = re.search(r'^id: (.+)$', block, re.M).group(1) + uid = re.search(r'state_hub_task_id: "([^"]+)"', block).group(1) + status = re.search(r'^status: (.+)$', block, re.M).group(1) + actual = by_uuid[uid] + assert (actual['record_id'], actual['status'], actual['workplan_id']) == (record, status, wp) + rows.append({'record_id': record, 'uuid': uid, 'status': status}) + assert len(rows) == count + item = {'owner': owner, 'task_count': count, 'workplan_uuid': wp, 'bindings': rows} + if owner == 'net-kingdom': + selected = {row['record_id']: row['uuid'] for row in rows} + dependencies = get('/workplans/' + wp + '/dependencies/') + matching = [d for d in dependencies if d.get('from_task_id') == selected['NK-WP-0048-T39'] + and d.get('to_task_id') == selected['NK-WP-0048-T38']] + assert len(matching) == 1 and matching[0]['relationship_type'] == 'starts_after' + # Existing source edges retain their creation commit. Reconciliation + # only creates/removes edges; a status-only commit does not rewrite it. + creation = '6201a1d2de282a218fde8d81b32f790a526942ef' + current = summary['expected_owner_heads'][owner].removeprefix('git:') + assert matching[0]['source_owner'] == 'forge:net-kingdom' + assert matching[0]['source_commit'] == creation + assert git(ROOT.parent / owner, 'merge-base', creation, current) == creation + wp_path = 'workplans/' + filename + created_text = subprocess.check_output( + ['git', '-C', str(ROOT.parent / owner), 'show', creation + ':' + wp_path]).decode() + def dependency_declaration(value): + blocks = re.findall(r'```task\n.*?```', value, re.S) + task = next(b for b in blocks if 'id: NK-WP-0048-T39\n' in b) + return re.search(r'^depends_on: (.+)$', task, re.M).group(1) + assert dependency_declaration(text) == dependency_declaration(created_text) == '[NK-WP-0048-T38]' + first_projection = json.loads((HERE.parent / 'nk48-consumer-gate-implementation-r1/projection.json').read_text()) + current_projection = json.loads((HERE.parent / 'nk48-release-build-progress-r1/projection.json').read_text()) + for evidence, commit in ((first_projection, creation), (current_projection, current)): + assert evidence['ok'] and evidence['status'] == 'applied' + assert evidence['commit'] == evidence['receipt']['expected_commit'] == evidence['receipt']['derived_commit'] == commit + assert evidence['receipt']['instance_role'] == 'primary' + assert evidence['receipt']['instance_label'] == 'railiance01' + assert current_projection['receipt']['outcome']['counts']['created_edges'] == 0 + assert current_projection['receipt']['outcome']['counts']['removed_edges'] == 0 + item['release_dependency'] = matching[0] + checks.append(item) +for name in [r['path'] for r in [json.loads((HERE / 'public-digest-refinement.json').read_text()), + *json.loads((HERE / 'public-digest-refinement.json').read_text())['additional_metadata_derivatives']]]: + subprocess.run(['python3', str(HERE / 'reconstruct_public_metadata.py'), + str((ROOT / name).relative_to(HERE.parent))], check=True, stdout=subprocess.DEVNULL) +result = {'status': 'verified', 'root_commit': 'git:' + head, + 'frozen_publication_snapshot': 'git:' + snapshot, 'snapshot_files_verified': len(summary['files']), + 'current_document_delta_verified': list(exceptions), 'source_primary_bindings': checks, + 'task_bindings_verified': sum(c['task_count'] for c in checks), 'protected_original_paths': len(protected), + 'owner_heads': summary['expected_owner_heads'], 'public_original_metadata_reconstructions': 4, + 'all_sixteen_native_gates': 'open', 'goal': 'active', + 'observed_at': datetime.datetime.now(datetime.timezone.utc).isoformat()} +(HERE / 'final-verification-r2.json').write_text(json.dumps(result, indent=2) + '\n') +print(json.dumps({'status': 'verified', 'root_commit': head, 'files': len(summary['files']), + 'task_bindings': result['task_bindings_verified'], 'preserved_original_paths': len(protected), + 'public_original_metadata_reconstructions': 4, 'goal': 'active'})) diff --git a/docs/changes/netkingdom-target-2026-10-17/platform-current-source-integration-r1/publication-backup/.custodian-brief.md.worktree b/docs/changes/netkingdom-target-2026-10-17/platform-current-source-integration-r1/publication-backup/.custodian-brief.md.worktree new file mode 100644 index 00000000..1c9e6350 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/platform-current-source-integration-r1/publication-backup/.custodian-brief.md.worktree @@ -0,0 +1,144 @@ + +# Custodian Brief — railiance-platform + +**Domain:** financials +**Last synced:** 2026-10-17 02:33 UTC +**State Hub:** http://127.0.0.1:8000 *(adjust if running on a remote machine)* + +## Active Workstreams + +### Prepare the exact seven private-source reader for reuse-surface +Progress: 4/5 done | workplan_id: `b55a8aec-7457-563b-990b-4a55ca04d2f6` + +**Open tasks:** +- ! Rotate or revoke dedicated seven-source reader within 90 days `f0780e75` + +### Coordinate audit-core temporary custody and recovery exercises +Progress: 3/4 done | workplan_id: `f4640325-e89c-591d-b58e-ec6b087900ac` + +**Open tasks:** +- ! T03 — Define the coordinated railiance01 reboot exercise `ba729d18` + *(wait: Single-node outage exercise needs operator access and a declared restart order coordinated with railiance-infra, railiance-cluster, audit-core and the OpenBao package owner.)* + +### Admit hourly receiver cadence and exact Qonto collector custody +Progress: 3/4 done | workplan_id: `d57eeafb-c406-5e1e-841c-5d1288aacabf` + +**Open tasks:** +- ► Collect actual producer and independent observer acceptance `7d8e5315` + +### Let the hub-writer caller prove its own reach and pick the workload role in the OpenBao UI +Progress: 3/4 done | workplan_id: `04f7c4aa-0a24-5d61-994d-2487ca6bfb91` + +**Open tasks:** +- ► Deploy the overlay `94cdfd1c` + +### Coordinate KeyCape live Secret exposure recovery +Progress: 4/6 done | workplan_id: `b2c25a01-4a80-55c1-90cf-8538000f7e0e` + +**Open tasks:** +- ! T03 — Collect exact owner acknowledgements `714ae011` + *(wait: Owner work first: NetKingdom packages the complete resolver sequence as one receipt-producing command; then it enters the attended-window checklist (D1 standing rule).)* +- ! T06 — Publish the Railiance/OpenBao custody handoff `3b9748c4` + *(wait: Owner work first: the OpenBao owner confirms mount/path/fields and writer/reader authority and publishes the non-secret routing receipt; the handoff enters the attended window only when a command exists.)* + +### Admit the Playwright pilot and establish scoped consumer group access +Progress: 5/6 done | workplan_id: `31933d08-c877-5fda-aff9-6220c3705c92` + +**Open tasks:** +- ! Connect platform identities and prove managed consumer operation `a82762eb` + *(wait: User admitted all identified platform humans and coding agents; their supported Kubernetes authentication/group-mapping route remains unimplemented.)* + +### ArgoCD phase B: adopt the four existing Applications on railiance01 +Progress: 5/8 done | workplan_id: `98140775-3b9a-5cf9-9af6-722502d487dc` + +**Open tasks:** +- ! Adopt issue-core (from rapp-issue-core) `0e619658` +- ! Adopt external-secrets (highest risk) `4ca54868` + *(wait: Ordered D2 adoption: issue-core T04 must provide automated Argo health/diff and private/authenticated smoke proof; target-revenue T05 is already proven. No further founder approval is required.)* +- ! Turn the root back to the production lane `844ff115` + *(wait: Restore prune/selfHeal once T03-T06 have automated proof; no further founder go-ahead (D2).)* + +### Onboard rapp-policy-nexus to the ArgoCD production lane +Progress: 3/5 done | workplan_id: `ec41a4bd-df18-5b07-9b63-ccb80d9f001c` + +**Open tasks:** +- ! Adopt policy-nexus (live, founder go-ahead) `8638fa73` + *(wait: Merge approved by D2; runs once T03 diff and root automation (RPF-WP-0044-T07) are proven.)* +- ! Prepare the Application and the dry-run evidence `2afb1aac` + *(wait: Steps 1 and 3 done 2026-10-11 (docs/evidence/2026-10-11-policy-nexus-adoption-readonly-precheck.json, no stop condition). Step 2 render diff needs the railiance-apps values path from RAPP-POLICY-NEXUS-WP-0002-T02; agents can run it read-only via the k3s tunnel.)* + +### Close Forgejo primary backup coverage on Scaleway +Progress: 3/4 done | workplan_id: `7beec1a7-aa82-5a36-9a66-6b60008a2455` + +**Open tasks:** +- ! Establish primary full-archive delivery and application recovery `a4807df0` + *(wait: Durable scheduled caller, verified inventory and quota/retention gates remain open; September 15 prohibits expiration and scheduled secondary cutover.)* + +### Close S3 service assurance and ownership gaps +Progress: 4/7 done | workplan_id: `ca639c3d-3a87-5fa4-ad13-6f2e014b0c84` + +**Open tasks:** +- ! Make backup freshness and recurring recovery evidence checkable `d64446fb` + *(wait: D2 accepts the activity-core scheduling role; ACTIVITY-WP-0042 must supply the implemented bounded scheduler and scoped custody/execution proof before recurring backup/restore work can execute. No new founder go-ahead is required.)* +- ! Produce S3 signals and prove their delivery to the evidence owner `5351e0e4` + *(wait: Needs a concrete receiving contract from railiance-telemetry (RTEL-WP-0002) before health transport is accepted; unmonitored state retained meanwhile.)* +- ! Obtain acceptance for compatibility assets and derived-record cleanup `3b74f79c` + *(wait: Owner-ready handoffs to railiance-forge, rapp-openbao and the ArgoCD bootstrap owner await acceptance; S3 custody contracts stay here until then.)* + +### Adopt activity-core application runtime into railiance01 GitOps +Progress: 2/3 done | workplan_id: `b8cf2fc2-60c2-5d4e-a60a-55f1304d9f54` + +**Open tasks:** +- ! Observe adoption and enforce bounded promotion readiness `63b44949` + +### Implement reviewed credential lanes with separate owner gates +Progress: 7/13 done | workplan_id: `975db491-5412-5e27-8e34-14a2417bb039` + +**Open tasks:** +- ! Accept and provision secrets-engine service JWT login `e0c82ea9` + *(wait: Agent work, not a human gate: claim refusals (KEY-WP-0039-T02 probe) and capabilities-self deny, then ops-mason MASON-WP-0009-T05.)* +- ! Implement the platform operator-write CCR contract and Fluid lane `f47e5bc5` + *(wait: Founder session done 2026-10-09; waits on ops-mason verification (MASON-WP-0005-T05) and the FT-WP-0002 owner salt CAS probe.)* +- ! Dispose of the ungoverned whynot-design npm duplicate `0dd7c9a5` + *(wait: Hold kept by D3 until SECRETS-WP-0006-T06 moves publishing off the legacy whynot-design npm path; then rotate and prove a fresh publish.)* +- ! Admit the two factory audit-sender custody lanes `0ef52c26` + *(wait: Approved conditionally (ab85ae8a, standing change rule): executes once independent archive readback and the interim bearer revocation receipt exist; owner work with audit-core.)* +- ! Retire the approval-engine-operator client delivery and custody (CCR-2026-0018) `af2d4dba` + *(wait: Blue lane under the standing change rule (removing access, with reviewed order, rollback and receipts): executes once approval-engine confirms there is no presenter. No founder window needed.)* +- ► Implement and prove the separately approved task execution entitlement `914bf383` + +### Coordinate KeyCape live Secret exposure recovery +Progress: 4/6 done | workplan_id: `038bc3c0-4492-5b91-95eb-ae515ca205df` + +**Open tasks:** +- ! T03 — Collect exact owner acknowledgements `e019c166` + *(wait: Canonical RPF-WP-0027-T03: complete platform custody acknowledgements remain missing.)* +- ! T06 — Publish the Railiance/OpenBao custody handoff `69326850` + *(wait: Canonical RPF-WP-0027-T06: complete field/auth/expiry/handoff contract absent; September 15 operator block remains in force.)* + +### Coordinate audit-core temporary custody and recovery exercises +Progress: 2/4 done | workplan_id: `88c4ef7f-0af8-580e-90dc-a2bae2675a4d` + +**Open tasks:** +- ! T02 — Define the runtime database lease recovery exercise `caba7fcd` + *(wait: Canonical RPF-WP-0015-T02: approved sender identity, fresh bounded attended window, named abort operator and live recovery receipt required.)* +- ! T03 — Define the coordinated railiance01 reboot exercise `09cf4065` + *(wait: Canonical RPF-WP-0015-T03: fresh outage approval, off-host snapshot, quorum/access evidence and owner execution required.)* + +### Retract public OpenBao listener behind operator-only access +Progress: 2/3 done | workplan_id: `6f8a6cbc-c076-5f0a-ade2-281a7ec71360` + +**Open tasks:** +- ! T03 — Complete the attended operator cutover `99f8b41f` + +## Inbox Hygiene + +**Missing thread_id:** 14 unread message(s) lack supersession chains. + +--- +## MCP Orientation (when available) + +If the state-hub MCP server is reachable, call: +`get_domain_summary("financials")` +This provides richer cross-domain context. +If the MCP call fails, use this file as your orientation source. diff --git a/docs/changes/netkingdom-target-2026-10-17/platform-current-source-integration-r1/publication-backup/WORK-RECORDS.md.worktree b/docs/changes/netkingdom-target-2026-10-17/platform-current-source-integration-r1/publication-backup/WORK-RECORDS.md.worktree new file mode 100644 index 00000000..76d36c83 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/platform-current-source-integration-r1/publication-backup/WORK-RECORDS.md.worktree @@ -0,0 +1,176 @@ +# Work Records — railiance-platform + +> Generated by `statehub fix-consistency` (CUST-WP-0061-T04, work-record +> stage 3). Do not edit by hand — edit the source file/block listed for +> each record and re-run fix-consistency to refresh this index. Archived +> workplans are omitted; closed decisions/intakes/engagements stay listed +> so recently-resolved work is still visible. [auto] + +| Kind | ID | Status | Lane | Source | +| --- | --- | --- | --- | --- | +| workplan | RPF-WP-ADHOC-2026-10-10 | finished | — | workplans/ADHOC-2026-10-10.md | +| workplan | RPF-WP-ADHOC-2026-10-11 | finished | — | workplans/ADHOC-2026-10-11.md | +| workplan | RPF-WP-0015 | active | — | workplans/RPF-WP-0015-audit-core-custody-and-recovery-coordination.md | +| workplan | RPF-WP-0025 | finished | — | workplans/RPF-WP-0025-openbao-operator-only-access.md | +| workplan | RPF-WP-0027 | blocked | — | workplans/RPF-WP-0027-keycape-live-secret-exposure-recovery.md | +| workplan | RPF-WP-0029 | finished | — | workplans/RPF-WP-0029-backup-credential-default-removal.md | +| workplan | RPF-WP-0035 | blocked | — | workplans/RPF-WP-0035-credential-lane-implementation.md | +| workplan | RPF-WP-0036 | blocked | — | workplans/RPF-WP-0036-platform-service-assurance.md | +| workplan | RPF-WP-0038 | blocked | — | workplans/RPF-WP-0038-forgejo-scaleway-primary-coverage.md | +| workplan | RPF-WP-0039 | finished | — | workplans/RPF-WP-0039-vergabe-demo-company-database.md | +| workplan | RPF-WP-0040 | finished | — | workplans/RPF-WP-0040-keycape-factor-credential-custody.md | +| workplan | RPF-WP-0041 | finished | — | workplans/RPF-WP-0041-state-hub-105m-not-provisioning.md | +| workplan | RPF-WP-0042 | finished | — | workplans/RPF-WP-0042-informed-decision-sitting-requester.md | +| workplan | RPF-WP-0043 | blocked | — | workplans/RPF-WP-0043-policy-nexus-argocd-onboarding.md | +| workplan | RPF-WP-0044 | blocked | — | workplans/RPF-WP-0044-argocd-phase-b-adopt-existing-applications.md | +| workplan | RPF-WP-0045 | finished | — | workplans/RPF-WP-0045-eso-kubernetes-auth-activity-core-email-connect.md | +| workplan | RPF-WP-0046 | finished | — | workplans/RPF-WP-0046-eso-database-token-renewal.md | +| workplan | RPF-WP-0047 | finished | — | workplans/RPF-WP-0047-bao-coulomb-social-notice-page.md | +| workplan | RPF-WP-0048 | blocked | — | workplans/RPF-WP-0048-activity-core-gitops-adoption.md | +| workplan | RPF-WP-0049 | finished | — | workplans/RPF-WP-0049-platform-root-custody-record-delivery.md | +| workplan | RPF-WP-0050 | finished | — | workplans/RPF-WP-0050-attended-provisioning-preparation.md | +| workplan | RPF-WP-0051 | finished | — | workplans/RPF-WP-0051-informed-decision-requester-workload-delivery.md | +| workplan | RPF-WP-0052 | finished | — | workplans/RPF-WP-0052-authelia-platform-smtp-admission.md | +| workplan | RPF-WP-0053 | proposed | — | workplans/RPF-WP-0053-coulomb-paired-backup-admission.md | +| workplan | RPF-WP-0054 | finished | — | workplans/RPF-WP-0054-keycape-client-secret-generate-lane.md | +| workplan | RPF-WP-0055 | backlog | — | workplans/RPF-WP-0055-keycape-client-secret-rotation-residual.md | +| workplan | RPF-WP-0056 | blocked | — | workplans/RPF-WP-0056-playwright-consumer-admission.md | +| workplan | RPF-WP-0057 | active | — | workplans/RPF-WP-0057-sign-in-diagnosis-caller-proof.md | +| workplan | RPF-WP-0058 | active | — | workplans/RPF-WP-0058-reuse-private-source-reader.md | +| workplan | RPF-WP-0059 | active | — | workplans/RPF-WP-0059-hourly-cadence-receiver-custody.md | +| task | RPF-WP-ADHOC-2026-10-10-T01 | done | — | workplans/ADHOC-2026-10-10.md | +| task | RPF-WP-ADHOC-2026-10-11-T01 | done | — | workplans/ADHOC-2026-10-11.md | +| task | RPF-WP-ADHOC-2026-10-11-T02 | done | — | workplans/ADHOC-2026-10-11.md | +| task | RPF-WP-ADHOC-2026-10-11-T03 | done | — | workplans/ADHOC-2026-10-11.md | +| task | RPF-WP-0015-T01 | done | — | workplans/RPF-WP-0015-audit-core-custody-and-recovery-coordination.md | +| task | RPF-WP-0015-T02 | done | — | workplans/RPF-WP-0015-audit-core-custody-and-recovery-coordination.md | +| task | RPF-WP-0015-T03 | wait | — | workplans/RPF-WP-0015-audit-core-custody-and-recovery-coordination.md | +| task | RPF-WP-0015-T04 | done | — | workplans/RPF-WP-0015-audit-core-custody-and-recovery-coordination.md | +| task | RPF-WP-0025-T01 | done | — | workplans/RPF-WP-0025-openbao-operator-only-access.md | +| task | RPF-WP-0025-T02 | done | — | workplans/RPF-WP-0025-openbao-operator-only-access.md | +| task | RPF-WP-0025-T03 | done | — | workplans/RPF-WP-0025-openbao-operator-only-access.md | +| task | RPF-WP-0027-T01 | done | — | workplans/RPF-WP-0027-keycape-live-secret-exposure-recovery.md | +| task | RPF-WP-0027-T02 | done | — | workplans/RPF-WP-0027-keycape-live-secret-exposure-recovery.md | +| task | RPF-WP-0027-T03 | wait | — | workplans/RPF-WP-0027-keycape-live-secret-exposure-recovery.md | +| task | RPF-WP-0027-T04 | done | — | workplans/RPF-WP-0027-keycape-live-secret-exposure-recovery.md | +| task | RPF-WP-0027-T05 | done | — | workplans/RPF-WP-0027-keycape-live-secret-exposure-recovery.md | +| task | RPF-WP-0027-T06 | wait | — | workplans/RPF-WP-0027-keycape-live-secret-exposure-recovery.md | +| task | RPF-WP-0029-T01 | done | — | workplans/RPF-WP-0029-backup-credential-default-removal.md | +| task | RPF-WP-0029-T02 | done | — | workplans/RPF-WP-0029-backup-credential-default-removal.md | +| task | RPF-WP-0029-T03 | done | — | workplans/RPF-WP-0029-backup-credential-default-removal.md | +| task | RPF-WP-0035-T01 | done | — | workplans/RPF-WP-0035-credential-lane-implementation.md | +| task | RPF-WP-0035-T02 | wait | — | workplans/RPF-WP-0035-credential-lane-implementation.md | +| task | RPF-WP-0035-T03 | wait | — | workplans/RPF-WP-0035-credential-lane-implementation.md | +| task | RPF-WP-0035-T04 | done | — | workplans/RPF-WP-0035-credential-lane-implementation.md | +| task | RPF-WP-0035-T05 | done | — | workplans/RPF-WP-0035-credential-lane-implementation.md | +| task | RPF-WP-0035-T06 | done | — | workplans/RPF-WP-0035-credential-lane-implementation.md | +| task | RPF-WP-0035-T07 | wait | — | workplans/RPF-WP-0035-credential-lane-implementation.md | +| task | RPF-WP-0035-T08 | wait | — | workplans/RPF-WP-0035-credential-lane-implementation.md | +| task | RPF-WP-0035-T09 | done | — | workplans/RPF-WP-0035-credential-lane-implementation.md | +| task | RPF-WP-0035-T10 | wait | — | workplans/RPF-WP-0035-credential-lane-implementation.md | +| task | RPF-WP-0035-T11 | done | — | workplans/RPF-WP-0035-credential-lane-implementation.md | +| task | RPF-WP-0035-T12 | progress | — | workplans/RPF-WP-0035-credential-lane-implementation.md | +| task | RPF-WP-0035-T13 | done | — | workplans/RPF-WP-0035-credential-lane-implementation.md | +| task | RPF-WP-0036-T01 | done | — | workplans/RPF-WP-0036-platform-service-assurance.md | +| task | RPF-WP-0036-T02 | done | — | workplans/RPF-WP-0036-platform-service-assurance.md | +| task | RPF-WP-0036-T03 | wait | — | workplans/RPF-WP-0036-platform-service-assurance.md | +| task | RPF-WP-0036-T04 | wait | — | workplans/RPF-WP-0036-platform-service-assurance.md | +| task | RPF-WP-0036-T05 | done | — | workplans/RPF-WP-0036-platform-service-assurance.md | +| task | RPF-WP-0036-T06 | wait | — | workplans/RPF-WP-0036-platform-service-assurance.md | +| task | RPF-WP-0036-T07 | done | — | workplans/RPF-WP-0036-platform-service-assurance.md | +| task | RPF-WP-0038-T01 | done | — | workplans/RPF-WP-0038-forgejo-scaleway-primary-coverage.md | +| task | RPF-WP-0038-T02 | done | — | workplans/RPF-WP-0038-forgejo-scaleway-primary-coverage.md | +| task | RPF-WP-0038-T03 | done | — | workplans/RPF-WP-0038-forgejo-scaleway-primary-coverage.md | +| task | RPF-WP-0038-T04 | wait | — | workplans/RPF-WP-0038-forgejo-scaleway-primary-coverage.md | +| task | RPF-WP-0039-T01 | done | — | workplans/RPF-WP-0039-vergabe-demo-company-database.md | +| task | RPF-WP-0039-T02 | done | — | workplans/RPF-WP-0039-vergabe-demo-company-database.md | +| task | RPF-WP-0040-T01 | done | — | workplans/RPF-WP-0040-keycape-factor-credential-custody.md | +| task | RPF-WP-0040-T02 | done | — | workplans/RPF-WP-0040-keycape-factor-credential-custody.md | +| task | RPF-WP-0040-T03 | done | — | workplans/RPF-WP-0040-keycape-factor-credential-custody.md | +| task | RPF-WP-0040-T04 | done | — | workplans/RPF-WP-0040-keycape-factor-credential-custody.md | +| task | RPF-WP-0041-T01 | done | — | workplans/RPF-WP-0041-state-hub-105m-not-provisioning.md | +| task | RPF-WP-0042-T01 | done | — | workplans/RPF-WP-0042-informed-decision-sitting-requester.md | +| task | RPF-WP-0042-T02 | done | — | workplans/RPF-WP-0042-informed-decision-sitting-requester.md | +| task | RPF-WP-0043-T01 | done | — | workplans/RPF-WP-0043-policy-nexus-argocd-onboarding.md | +| task | RPF-WP-0043-T02 | done | — | workplans/RPF-WP-0043-policy-nexus-argocd-onboarding.md | +| task | RPF-WP-0043-T03 | wait | — | workplans/RPF-WP-0043-policy-nexus-argocd-onboarding.md | +| task | RPF-WP-0043-T04 | wait | — | workplans/RPF-WP-0043-policy-nexus-argocd-onboarding.md | +| task | RPF-WP-0043-T05 | done | — | workplans/RPF-WP-0043-policy-nexus-argocd-onboarding.md | +| task | RPF-WP-0044-T01 | done | — | workplans/RPF-WP-0044-argocd-phase-b-adopt-existing-applications.md | +| task | RPF-WP-0044-T02 | done | — | workplans/RPF-WP-0044-argocd-phase-b-adopt-existing-applications.md | +| task | RPF-WP-0044-T03 | done | — | workplans/RPF-WP-0044-argocd-phase-b-adopt-existing-applications.md | +| task | RPF-WP-0044-T04 | wait | — | workplans/RPF-WP-0044-argocd-phase-b-adopt-existing-applications.md | +| task | RPF-WP-0044-T05 | done | — | workplans/RPF-WP-0044-argocd-phase-b-adopt-existing-applications.md | +| task | RPF-WP-0044-T06 | wait | — | workplans/RPF-WP-0044-argocd-phase-b-adopt-existing-applications.md | +| task | RPF-WP-0044-T07 | wait | — | workplans/RPF-WP-0044-argocd-phase-b-adopt-existing-applications.md | +| task | RPF-WP-0044-T08 | done | — | workplans/RPF-WP-0044-argocd-phase-b-adopt-existing-applications.md | +| task | RPF-WP-0045-T01 | done | — | workplans/RPF-WP-0045-eso-kubernetes-auth-activity-core-email-connect.md | +| task | RPF-WP-0045-T02 | done | — | workplans/RPF-WP-0045-eso-kubernetes-auth-activity-core-email-connect.md | +| task | RPF-WP-0045-T03 | done | — | workplans/RPF-WP-0045-eso-kubernetes-auth-activity-core-email-connect.md | +| task | RPF-WP-0045-T04 | done | — | workplans/RPF-WP-0045-eso-kubernetes-auth-activity-core-email-connect.md | +| task | RPF-WP-0045-T05 | cancel | — | workplans/RPF-WP-0045-eso-kubernetes-auth-activity-core-email-connect.md | +| task | RPF-WP-0045-T06 | done | — | workplans/RPF-WP-0045-eso-kubernetes-auth-activity-core-email-connect.md | +| task | RPF-WP-0046-T01 | done | — | workplans/RPF-WP-0046-eso-database-token-renewal.md | +| task | RPF-WP-0046-T02 | done | — | workplans/RPF-WP-0046-eso-database-token-renewal.md | +| task | RPF-WP-0046-T03 | done | — | workplans/RPF-WP-0046-eso-database-token-renewal.md | +| task | RPF-WP-0046-T04 | done | — | workplans/RPF-WP-0046-eso-database-token-renewal.md | +| task | RPF-WP-0046-T05 | done | — | workplans/RPF-WP-0046-eso-database-token-renewal.md | +| task | RPF-WP-0046-T06 | done | — | workplans/RPF-WP-0046-eso-database-token-renewal.md | +| task | RPF-WP-0047-T01 | done | — | workplans/RPF-WP-0047-bao-coulomb-social-notice-page.md | +| task | RPF-WP-0047-T02 | done | — | workplans/RPF-WP-0047-bao-coulomb-social-notice-page.md | +| task | RPF-WP-0047-T03 | done | — | workplans/RPF-WP-0047-bao-coulomb-social-notice-page.md | +| task | RPF-WP-0048-T01 | done | — | workplans/RPF-WP-0048-activity-core-gitops-adoption.md | +| task | RPF-WP-0048-T02 | wait | — | workplans/RPF-WP-0048-activity-core-gitops-adoption.md | +| task | RPF-WP-0048-T03 | done | — | workplans/RPF-WP-0048-activity-core-gitops-adoption.md | +| task | RPF-WP-0049-T01 | done | — | workplans/RPF-WP-0049-platform-root-custody-record-delivery.md | +| task | RPF-WP-0049-T02 | done | — | workplans/RPF-WP-0049-platform-root-custody-record-delivery.md | +| task | RPF-WP-0049-T03 | done | — | workplans/RPF-WP-0049-platform-root-custody-record-delivery.md | +| task | RPF-WP-0049-T04 | done | — | workplans/RPF-WP-0049-platform-root-custody-record-delivery.md | +| task | RPF-WP-0049-T05 | done | — | workplans/RPF-WP-0049-platform-root-custody-record-delivery.md | +| task | RPF-WP-0050-T01 | done | — | workplans/RPF-WP-0050-attended-provisioning-preparation.md | +| task | RPF-WP-0050-T02 | done | — | workplans/RPF-WP-0050-attended-provisioning-preparation.md | +| task | RPF-WP-0050-T03 | cancel | — | workplans/RPF-WP-0050-attended-provisioning-preparation.md | +| task | RPF-WP-0050-T04 | done | — | workplans/RPF-WP-0050-attended-provisioning-preparation.md | +| task | RPF-WP-0051-T01 | done | — | workplans/RPF-WP-0051-informed-decision-requester-workload-delivery.md | +| task | RPF-WP-0051-T02 | done | — | workplans/RPF-WP-0051-informed-decision-requester-workload-delivery.md | +| task | RPF-WP-0051-T03 | done | — | workplans/RPF-WP-0051-informed-decision-requester-workload-delivery.md | +| task | RPF-WP-0052-T01 | done | — | workplans/RPF-WP-0052-authelia-platform-smtp-admission.md | +| task | RPF-WP-0052-T02 | done | — | workplans/RPF-WP-0052-authelia-platform-smtp-admission.md | +| task | RPF-WP-0052-T03 | done | — | workplans/RPF-WP-0052-authelia-platform-smtp-admission.md | +| task | RPF-WP-0053-T01 | progress | — | workplans/RPF-WP-0053-coulomb-paired-backup-admission.md | +| task | RPF-WP-0053-T02 | todo | — | workplans/RPF-WP-0053-coulomb-paired-backup-admission.md | +| task | RPF-WP-0053-T03 | todo | — | workplans/RPF-WP-0053-coulomb-paired-backup-admission.md | +| task | RPF-WP-0053-T04 | todo | — | workplans/RPF-WP-0053-coulomb-paired-backup-admission.md | +| task | RPF-WP-0053-T05 | progress | — | workplans/RPF-WP-0053-coulomb-paired-backup-admission.md | +| task | RPF-WP-0053-T06 | done | — | workplans/RPF-WP-0053-coulomb-paired-backup-admission.md | +| task | RPF-WP-0054-T01 | done | — | workplans/RPF-WP-0054-keycape-client-secret-generate-lane.md | +| task | RPF-WP-0054-T02 | done | — | workplans/RPF-WP-0054-keycape-client-secret-generate-lane.md | +| task | RPF-WP-0054-T03 | done | — | workplans/RPF-WP-0054-keycape-client-secret-generate-lane.md | +| task | RPF-WP-0054-T04 | done | — | workplans/RPF-WP-0054-keycape-client-secret-generate-lane.md | +| task | RPF-WP-0054-T05 | done | — | workplans/RPF-WP-0054-keycape-client-secret-generate-lane.md | +| task | RPF-WP-0054-T06 | done | — | workplans/RPF-WP-0054-keycape-client-secret-generate-lane.md | +| task | RPF-WP-0054-T07 | cancel | — | workplans/RPF-WP-0054-keycape-client-secret-generate-lane.md | +| task | RPF-WP-0055-T01 | todo | — | workplans/RPF-WP-0055-keycape-client-secret-rotation-residual.md | +| task | RPF-WP-0056-T01 | done | — | workplans/RPF-WP-0056-playwright-consumer-admission.md | +| task | RPF-WP-0056-T02 | done | — | workplans/RPF-WP-0056-playwright-consumer-admission.md | +| task | RPF-WP-0056-T03 | wait | — | workplans/RPF-WP-0056-playwright-consumer-admission.md | +| task | RPF-WP-0056-T04 | done | — | workplans/RPF-WP-0056-playwright-consumer-admission.md | +| task | RPF-WP-0056-T05 | done | — | workplans/RPF-WP-0056-playwright-consumer-admission.md | +| task | RPF-WP-0056-T06 | done | — | workplans/RPF-WP-0056-playwright-consumer-admission.md | +| task | RPF-WP-0057-T01 | done | — | workplans/RPF-WP-0057-sign-in-diagnosis-caller-proof.md | +| task | RPF-WP-0057-T02 | done | — | workplans/RPF-WP-0057-sign-in-diagnosis-caller-proof.md | +| task | RPF-WP-0057-T03 | done | — | workplans/RPF-WP-0057-sign-in-diagnosis-caller-proof.md | +| task | RPF-WP-0057-T04 | progress | — | workplans/RPF-WP-0057-sign-in-diagnosis-caller-proof.md | +| task | RPF-WP-0058-T01 | done | — | workplans/RPF-WP-0058-reuse-private-source-reader.md | +| task | RPF-WP-0058-T02 | done | — | workplans/RPF-WP-0058-reuse-private-source-reader.md | +| task | RPF-WP-0058-T03 | done | — | workplans/RPF-WP-0058-reuse-private-source-reader.md | +| task | RPF-WP-0058-T04 | done | — | workplans/RPF-WP-0058-reuse-private-source-reader.md | +| task | RPF-WP-0058-T05 | wait | — | workplans/RPF-WP-0058-reuse-private-source-reader.md | +| task | RPF-WP-0059-T01 | done | — | workplans/RPF-WP-0059-hourly-cadence-receiver-custody.md | +| task | RPF-WP-0059-T02 | done | — | workplans/RPF-WP-0059-hourly-cadence-receiver-custody.md | +| task | RPF-WP-0059-T03 | done | — | workplans/RPF-WP-0059-hourly-cadence-receiver-custody.md | +| task | RPF-WP-0059-T04 | progress | — | workplans/RPF-WP-0059-hourly-cadence-receiver-custody.md | +| intake | RPF-IN-0001 | closed | — | intakes/intakes.md | +| intake | RPF-IN-0002 | open | — | intakes/intakes.md | +| intake | RPF-IN-0003 | open | — | intakes/intakes.md | +| intake | RPF-IN-0004 | open | — | intakes/intakes.md | diff --git a/docs/changes/netkingdom-target-2026-10-17/platform-current-source-integration-r1/publication-backup/originals.json b/docs/changes/netkingdom-target-2026-10-17/platform-current-source-integration-r1/publication-backup/originals.json new file mode 100644 index 00000000..d55853d5 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/platform-current-source-integration-r1/publication-backup/originals.json @@ -0,0 +1,10 @@ +[ + { + "path": ".custodian-brief.md", + "index": "100644 1c9e635012d170c76646bf76d5202f6de163a643 0\t.custodian-brief.md" + }, + { + "path": "WORK-RECORDS.md", + "index": "100644 dc7f3aab6de945558ddb9e48a482a9d92d734c3b 0\tWORK-RECORDS.md" + } +] diff --git a/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r1/README.md b/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r1/README.md new file mode 100644 index 00000000..86215536 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r1/README.md @@ -0,0 +1,50 @@ +# Platform delivery backend source preparation r1 + +Prepared for existing NK-WP-0048-T16 / RPF-1.3-T4. The exact patch targets +railiance-platform `104ed90f42a9a86bf9fda343b49b65a85c9ee4b9`. No canonical +Platform, NetKingdom or Warden source was changed; no commit, native action, +credential read, grant, authority, acknowledgement or owner acceptance occurred. + +`railiance-platform.patch` adds a concrete command implementation and meaningful +offline regressions, and extends the existing contract. `prepared-source/` +contains the exact resulting files. `source-pins.json` pins inputs, reviewed +facilities and output hashes. `regressions.json` records final runs and the one +initial test assertion failure. Both final suites pass: 17 command regressions +and 8 existing fixed-contract tests. Delivery tests clear the environment, forbid +socket creation and intercept subprocesses; no HOME, ambient credentials or +network supply fixture authority. + +The code creates only the fixed ExternalSecret after validating a preexisting +fixed Store, reports matching existing objects unchanged, refuses conflicting +objects, waits boundedly for Ready, captures public UID/resourceVersion, and +deletes only verified instance-created objects with UID **and** resourceVersion +API preconditions. Lost responses and uncertain cleanup preserve reconciliation +obligations. The receipt has the existing closed metadata-only adapter shape. +Receipt construction by these private mechanics is not authenticated evidence. + +| Facility | Existing source and actual status | +| --- | --- | +| Fixed Store/ExternalSecret metadata | Real `keycape_issuer_eso.documents` and `require_fixed_store`; reused without Store creation or caller overrides. Template SHA-256 remains `e17498bd8a4c820041a4105e4efe9c60c6a50953db8f621ff54887080065c743`. | +| Contained command execution | Real `state_hub_preflight_lane.command`; reused for actual kubectl argv, fixed error containment and process timeout. Source-preparation transport has empty env and `/dev/null` kubeconfig. | +| Safe remote transport | Real `remote_exec.run_remote` and custody-projection Operator exist for attended procedures; reviewed, deliberately not borrowed as an authenticated delivery session. | +| Caller case verification | Real mint broker `check_case` checks exact bytes/reference, materialized/confidential/client match and @1.1. It does not acknowledge @1.3 or authenticate a delivery caller. **Missing for this delivery binding.** | +| Current human aal2 delivery caller/session | Mint-lane OpenBao lookup-self and CCR-0043 issuer binding are real but mint-specific. No scoped Kubernetes delivery credential/session issuer exists here. **Missing; production factory refuses before dispatch.** | +| Durable admission/recovery journal | `task_execution_journal.Journal` exists and explicitly cannot authorize native work. No lane-specific trusted admission/consume binding or creation-to-evidence durable transaction exists. **Missing for this lane; not replaced with in-memory provenance.** | +| Evidence publication/authentication | Credential-change evidence writes and State Hub progress logging exist for their original purpose; neither authenticates delivery adapter payloads. **Missing.** No always-true verifier or local receipt oracle is added. | +| Per-attempt delivery session revocation | OpenBao revoke-self exists in other lanes, but there is no actual delivery session/accessor/owner identity binding to revoke or prove revoked. **Missing.** | +| Adapter integration | Existing frozen deliver-secret adapter still expects owner deliver/remove/publish/authenticate/revoke_session. `production_bindings()` deliberately refuses rather than provide any forged binding. Descriptor/source digest remains unchanged. | + +This is a concrete backend algorithm prepared for owner review, **not completion +of RPF-1.3-T4**. Real authenticated callable production capabilities remain open. +Future work must supply the missing existing-obligation facilities with reviewed +authority, exact admitted case/client binding, one-shot durable journaling, +authenticated evidence and actual per-attempt revocation. Removing the deliberate +transport restrictions alone would not satisfy those obligations. + +The shared ESO service account/role pattern is grounded only in CCR-0041. No +family-wide role grant, new Store, service, admission authority or native access +is inferred. Target Secret cleanup by Kubernetes/ESO remains outside these +commands; no direct Secret read/list/delete occurs. A lost create response with +unknown UID requires owner reconciliation at the fixed resource name, never a +blind delete. Process restart never recreates deletion provenance from a supplied +revision or unsigned receipt. diff --git a/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r1/prepared-source/docs/keycape-issuer-eso-contract.md b/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r1/prepared-source/docs/keycape-issuer-eso-contract.md new file mode 100644 index 00000000..40f3befa --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r1/prepared-source/docs/keycape-issuer-eso-contract.md @@ -0,0 +1,114 @@ +# Proposed fixed issuer ESO contract — RPF-1.3-T4 / NK48-R5-F2 + +Status: proposed source; not admitted, deployed, or owner-acknowledged. This is a +new Platform declaration for review, not an existing Forgejo grant discovery. + +The only substitution in `manifests/templates/keycape-issuer-eso.yaml` +(sha256:e17498bd8a4c820041a4105e4efe9c60c6a50953db8f621ff54887080065c743) is the admitted `client_id`. The proposed issuer +store is **ClusterSecretStore `openbao-keycape-{client_id}-issuer`**, cluster +scoped (no metadata namespace), restricted by conditions to ExternalSecrets in +`sso`. For Forgejo it is `openbao-keycape-forgejo-issuer`. The suffix makes the +issuer purpose explicit and keeps type-admitted IDs ending in `-` representable. +The store's DNS subdomain name can exceed 63 characters; the existing adapter's +63-character target Secret reference bound is preserved (client ID at most 48). +No type parameter is added or changed. + +The provider is `http://openbao.openbao.svc:8200`, KV v2 mount `platform`, +Kubernetes auth mount `kubernetes`, role `external-secrets-keycape-{client_id}`, +serviceAccountRef `external-secrets/external-secrets`, audience `openbao`. +CCR-2026-0041 already records that Forgejo role/identity/audience, not this new +store name or an unrestricted client family grant. Other clients still require +their own existing owner-controlled role/case admission. + +The ExternalSecret is `sso/keycape-{client_id}-client`, references precisely that +ClusterSecretStore, refreshes every 5m and targets the same name with Owner +creation and Retain remote deletion. Its only data entry maps `client-secret` +from remoteRef `workloads/keycape/{client_id}`, property `CLIENT_SECRET`. The +provider mount plus mount-relative remoteRef equals custody reference +`platform/workloads/keycape/{client_id}`; do not double-prefix `platform/`. +This is distinct from the Apps-owned consumer namespace SecretStore +`openbao-keycape-{client_id}-consumer` and its dedicated consumer identity. + +Store provisioning belongs to Platform's separate owner-controlled path. The +proposed `platform-secret-delivery` lane retains ONLY create/get/watch/delete on +the exact ExternalSecret in `sso`; this source does not delegate store creation +or Secret read/list. Never apply the multi-document template through that lane. +The pure `scripts/keycape_issuer_eso.py` module exposes only the ExternalSecret +document through `external_secret()` and validates fixed public observations +through `require_fixed_store()` and `require_ready_delivery()`. It performs no +API call and issues no evidence/receipt. It is not a real capability or admission +boundary; a caller-supplied observation alone establishes no authenticity. + +The existing RPF-1.3-T4 delivery binding must validate the case/client binding, +verify this exact preexisting store's public identity/spec/Ready state, apply or +compare the exact ExternalSecret, and wait for matching spec plus Ready=True. +The proposed helper accepts extra controller metadata but deliberately refuses +spec drift. Actual API defaulting/normalization and installed ESO schema support +must be checked in that implementation; these fixtures establish no native +compatibility or credential isolation. ESO's shared controller remains trusted. +No Secret bodies, values, arbitrary remoteRefs, or raw errors may enter receipts. + +Existing owner requirements remain: closed `netkingdom.deliver-secret.v1` +metadata receipt with authenticated before/after UID/resourceVersion provenance, +unchanged objects reported NO_CHANGE, attempt-session revocation, and created-only +rollback guarded by exact UID/resourceVersion (drift or uncertain ownership goes +to owner reconciliation). The helper neither deletes a target Secret nor creates +an OpenBao role/policy nor changes CCR-0041. These capabilities and catalog routing +remain unimplemented/unaccepted under RPF-1.3-T4. Independent Platform source +review of this declaration precedes adopting the conditional NetKingdom type +text; no descriptor acceptance, acknowledgment, pin, grant, catalog or native +permission is inferred. + +Grounding: CCR-2026-0041 exact role/identity; the approved historical +`docs/provisioning-windows/2026-10-04-1000/keycape/eso.draft.yaml` issuer +ClusterSecretStore and target pattern; `scripts/catalog_lane_scaffold.py` +ClusterSecretStore/namespace restriction and mount-relative remoteRef model; +RPF-1.3-C1 and RPF-1.3-T4 in the frozen owner review. The scaffold's historical +audience field placement differs from the issuer draft; this candidate uses +the issuer draft's serviceAccountRef.audiences, not a claim of installed support. + +## Prepared delivery mechanics (still unavailable as a production lane) + +`scripts/keycape_issuer_delivery.py` now contains concrete command mechanics for +the existing RPF-1.3-T4 obligation. It reuses `keycape_issuer_eso.documents()` and +`require_fixed_store()` and the existing contained `state_hub_preflight_lane.command()` +transport. Only exact named public ESO resources are read. It proves absence +with successful empty `get --ignore-not-found`, creates only the ExternalSecret, +compares an existing object's full spec, and polls at most ten times for Ready +with matching spec and stable UID. Each request has a 5s kubectl request timeout +inside the existing 60s process bound; nine sleeps of 1s bound each poll loop. +Controller resourceVersion updates during successful readiness are captured. + +Removal is permitted only for that instance's verified case-created revision, +rechecks spec/UID/resourceVersion, uses Kubernetes DeleteOptions with both UID +and resourceVersion preconditions, and confirms absence with bounded polling. +Existing matching objects return changed=false and are never rollback candidates. +Creation response loss, readiness failure after creation, replacement, delete +conflict or unconfirmed deletion raises a fixed uncertainty diagnostic and keeps +the last verified revision when known. No retry adopts an uncertain create; no +replacement is deleted. An unknown create outcome requires owner reconciliation +using the fixed resource name even when no UID could be recovered. Target Secret +garbage collection by ESO/Kubernetes is outside this helper; it never reads or +directly deletes Secret objects. There is no claim that deletion proves target +Secret disappearance. + +The callable `production_bindings()` **always refuses before any command**. +The private mechanics use empty environment and `/dev/null` kubeconfig and +therefore cannot borrow the caller's native session. They are source preparation, +not an adapter/evidence authority. Tests intercept the actual subprocess call; +they do not provide production callbacks or attestations. A real lane requires +an owner-reviewed authenticated human aal2 session, acknowledged case revision +and caller binding, scoped Kubernetes credentials, one-shot durable admission +journaling and authenticated evidence publication/verification, plus real +per-attempt session revocation and recovery. None exists for this lane at the +pinned source. The generic task_execution_journal module is real storage but +explicitly does not admit native work; it is not silently repurposed here. +The mint broker's case checker and OpenBao lookup-self are real for its existing +mint lane, not delivery authentication. The attended custody-projection SSH +transport and credential-change evidence writer likewise confer no delivery +lane authority. No unsigned receipt or `True` oracle replaces these facilities. + +Consequently RPF-1.3-T4 remains open for real capability binding and owner review; +this patch completes only concrete ESO command/state mechanics and regressions. +No ESO role, policy, Store, admission service, grant, catalog acceptance, native +permission or owner descriptor acceptance is created or inferred. diff --git a/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r1/prepared-source/scripts/keycape_issuer_delivery.py b/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r1/prepared-source/scripts/keycape_issuer_delivery.py new file mode 100644 index 00000000..a96df061 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r1/prepared-source/scripts/keycape_issuer_delivery.py @@ -0,0 +1,182 @@ +"""RPF-1.3-T4 concrete source-preparation mechanics; NO production admission. + +Only public ESO objects are fetched. The production factory refuses before +dispatch: no authenticated delivery session/evidence service currently exists. +Private mechanics are exercised by fake-command tests, never a grant boundary. +""" +from __future__ import annotations + +from copy import deepcopy +import json +import re +import time + +from keycape_issuer_eso import documents, require_fixed_store, Refused +from state_hub_preflight_lane import command, LaneError + +POLL_COUNT = 10 +POLL_DELAY = 1 +API = '/apis/external-secrets.io/v1' + + +class Uncertain(Refused): + """No success receipt: retain exact metadata for owner reconciliation.""" + def __init__(self, reason, revision=None): + super().__init__(reason) + self.revision = deepcopy(revision) + + +def production_bindings(*args, **kwargs): + # Neither Warden invocation fields, an operator's kubeconfig, an unsigned + # local receipt nor mint-lane lookup-self authenticates THIS lane/caller. + raise Refused('delivery_admission_session_journal_and_evidence_unavailable') + + +def _revision(obj): + meta = obj.get('metadata', {}) + result = {'uid': meta.get('uid'), 'resource_version': meta.get('resourceVersion')} + if any(not isinstance(x, str) or not re.fullmatch(r'[A-Za-z0-9._:-]{1,192}', x) + for x in result.values()): + raise Refused('public_revision_invalid') + return result + + +class _DeliveryMechanics: + """Concrete fixed kubectl algorithm, not an authenticated owner capability. + + No caller resource, remoteRef, namespace, store or path argument is accepted. + The deliberately unauthenticated transport cannot load ambient credentials. + A reviewed session transport must replace it only after real lane admission, + one-shot durable journaling, evidence and revocation are implemented. + """ + def __init__(self, client_id): + self.client_id = client_id + self.store, self.external = documents(client_id) + self.created = None + self.pending = False + + def _call(self, args, payload=None): + try: + result = command(['kubectl', '--kubeconfig=/dev/null', + '--request-timeout=5s', *args], + payload=payload, env={}) + if len(result.stdout) > 262144: + raise Refused('public_response_too_large') + if not result.stdout.strip(): + return None + value = json.loads(result.stdout) + if not isinstance(value, dict): + raise Refused('public_response_shape') + return value + except (LaneError, OSError, ValueError, TimeoutError): + raise Refused('public_command_failed') from None + except Exception: + # Includes subprocess timeouts; never expose stdout/stderr or argv. + raise Refused('public_command_failed') from None + + def _get(self, store=False): + obj = self.store if store else self.external + resource = 'clustersecretstores.external-secrets.io' if store else 'externalsecrets.external-secrets.io' + args = [] if store else ['-n', 'sso'] + return self._call([*args, 'get', resource, obj['metadata']['name'], + '--ignore-not-found=true', '-o', 'json']) + + def _match(self, obj): + if not isinstance(obj, dict): + raise Refused('delivery_missing') + metadata = obj.get('metadata', {}) + if (obj.get('apiVersion') != self.external['apiVersion'] + or obj.get('kind') != 'ExternalSecret' + or metadata.get('name') != self.external['metadata']['name'] + or metadata.get('namespace') != 'sso' + or metadata.get('deletionTimestamp') + or obj.get('spec') != self.external['spec']): + raise Refused('delivery_conflict') + return _revision(obj) + + @staticmethod + def _ready(obj): + status = obj.get('status', {}) + conditions = status.get('conditions', []) if isinstance(status, dict) else [] + return isinstance(conditions, list) and any( + isinstance(c, dict) and c.get('type') == 'Ready' and c.get('status') == 'True' + for c in conditions) + + def _receipt(self, operation, before, after): + return {'schema': 'netkingdom.deliver-secret.v1', 'operation': operation, + 'namespace': 'sso', 'name': self.external['metadata']['name'], + 'custody_reference': 'platform/workloads/keycape/' + self.client_id, + 'key_names': ['client-secret'], 'values_emitted': False, + 'changed': before is None if operation == 'deliver' else True, + 'before': before, 'after': after} + + def deliver(self): + if self.pending or self.created is not None: + raise Refused('attempt_already_used_or_unreconciled') + # A successful empty response proves absence; errors never do. + store = self._get(store=True) + require_fixed_store(store, self.client_id) + if store.get('metadata', {}).get('deletionTimestamp'): + raise Refused('store_terminating') + old = self._get() + before = self._match(old) if old is not None else None + uid = before['uid'] if before else None + if old is None: + # Set before dispatch: a lost create response MUST NOT be retried, + # adopted, or claimed as a pre-existing no-change result. + self.pending = True + try: + created = self._call(['-n', 'sso', 'create', '-f', '-', '-o', 'json'], self.external) + self.created = self._match(created) + uid = self.created['uid'] + except Refused: + raise Uncertain('create_outcome_unverified') from None + try: + for index in range(POLL_COUNT): + current = self._get() + after = self._match(current) + if after['uid'] != uid: + raise Refused('delivery_replaced') + if self._ready(current): + if self.created is not None: + self.created = after + self.pending = False + return self._receipt('deliver', before, after) + if index + 1 < POLL_COUNT: + time.sleep(POLL_DELAY) + raise Refused('delivery_ready_timeout') + except Refused: + if self.created is not None: + raise Uncertain('created_delivery_requires_reconciliation', self.created) from None + raise + + def remove(self, revision): + # An arbitrary adapter argument alone is never deletion provenance. + if self.pending or self.created is None or revision != self.created: + raise Refused('no_verified_case_creation') + try: + if self._match(self._get()) != self.created: + raise Refused('created_revision_changed') + except Refused: + raise Uncertain('created_revision_changed', self.created) from None + uri = API + '/namespaces/sso/externalsecrets/' + self.external['metadata']['name'] + options = {'apiVersion': 'v1', 'kind': 'DeleteOptions', + 'preconditions': {'uid': self.created['uid'], + 'resourceVersion': self.created['resource_version']}} + prior = deepcopy(self.created) + self.pending = True + try: + self._call(['delete', '--raw', uri, '-f', '-'], options) + for index in range(POLL_COUNT): + current = self._get() + if current is None: + self.created = None + self.pending = False + return self._receipt('remove', prior, None) + if _revision(current)['uid'] != prior['uid']: + raise Refused('replacement_preserved') + if index + 1 < POLL_COUNT: + time.sleep(POLL_DELAY) + raise Refused('delete_unconfirmed') + except Refused: + raise Uncertain('cleanup_requires_reconciliation', prior) from None diff --git a/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r1/prepared-source/tests/test_keycape_issuer_delivery.py b/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r1/prepared-source/tests/test_keycape_issuer_delivery.py new file mode 100644 index 00000000..74207565 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r1/prepared-source/tests/test_keycape_issuer_delivery.py @@ -0,0 +1,196 @@ +"""Offline command-level regressions. No HOME/env/network/credential access.""" +from copy import deepcopy +import json +import os +from pathlib import Path +import subprocess +import sys +import unittest +from unittest.mock import patch + +sys.path.insert(0, str(Path(__file__).resolve().parents[1] / 'scripts')) +import keycape_issuer_delivery as lane + + +class DeliveryTests(unittest.TestCase): + def setUp(self): + self.environment = patch.dict(os.environ, {}, clear=True) + self.environment.start() + self.addCleanup(self.environment.stop) + self.network = patch('socket.socket', side_effect=AssertionError('network forbidden')) + self.network.start() + self.addCleanup(self.network.stop) + self.m = lane._DeliveryMechanics('forgejo') + self.store = self.resource(self.m.store) + self.obj = self.resource(self.m.external) + self.calls = [] + self.responses = [] + self.native = patch('subprocess.run', side_effect=self.run_command) + self.native.start() + self.addCleanup(self.native.stop) + self.sleep = patch.object(lane.time, 'sleep') + self.sleep.start() + self.addCleanup(self.sleep.stop) + + def resource(self, template, uid='uid-1', rv='10', ready=True): + obj = deepcopy(template) + obj['metadata'].update(uid=uid, resourceVersion=rv) + obj['status'] = {'conditions': [{'type': 'Ready', 'status': str(ready)}]} + return obj + + def run_command(self, argv, **kw): + self.calls.append((deepcopy(argv), deepcopy(kw))) + self.assertEqual(kw['env'], {}) + self.assertEqual(argv[:3], ['kubectl', '--kubeconfig=/dev/null', '--request-timeout=5s']) + self.assertTrue(kw['capture_output']) + self.assertEqual(kw['timeout'], 60) + if not self.responses: + self.fail('unexpected command dispatch') + result = self.responses.pop(0) + if isinstance(result, Exception): + raise result + if result == 'error': + return subprocess.CompletedProcess(argv, 1, b'PRIVATE PROVIDER VALUE', b'PRIVATE ERROR') + return subprocess.CompletedProcess(argv, 0, b'' if result is None else json.dumps(result).encode(), b'') + + def create(self): + self.responses += [self.store, None, self.obj, self.obj] + return self.m.deliver() + + def test_production_factory_fails_before_dispatch(self): + with self.assertRaisesRegex(lane.Refused, 'unavailable'): + lane.production_bindings(case_source=b'arbitrary', admitted=object()) + self.assertEqual(self.calls, []) + + def test_absent_create_ready_and_uid_rv_precondition_delete(self): + receipt = self.create() + self.assertTrue(receipt['changed']) + self.assertIsNone(receipt['before']) + self.assertEqual(receipt['after'], {'uid': 'uid-1', 'resource_version': '10'}) + self.responses += [self.obj, {'kind': 'Status'}, None] + removed = self.m.remove(receipt['after']) + self.assertEqual(removed['operation'], 'remove') + delete = next(c for c in self.calls if 'delete' in c[0]) + options = json.loads(delete[1]['input']) + self.assertEqual(options['preconditions'], {'uid': 'uid-1', 'resourceVersion': '10'}) + self.assertEqual(delete[0][5], '/apis/external-secrets.io/v1/namespaces/sso/externalsecrets/keycape-forgejo-client') + self.assertFalse(any('secret' == a or 'secrets' == a for c in self.calls for a in c[0])) + + def test_matching_existing_no_change_never_delete(self): + self.responses = [self.store, self.obj, self.obj] + receipt = self.m.deliver() + self.assertFalse(receipt['changed']) + with self.assertRaises(lane.Refused): + self.m.remove(receipt['after']) + self.assertFalse(any('create' in a or 'delete' in a for a, k in self.calls)) + + def test_conflicting_existing_untouched(self): + drift = deepcopy(self.obj) + drift['spec']['data'][0]['remoteRef']['key'] = 'elsewhere' + self.responses = [self.store, drift] + with self.assertRaises(lane.Refused): + self.m.deliver() + self.assertEqual(len(self.calls), 2) + + def test_missing_drifted_unready_store_never_create(self): + for kind in ('missing', 'spec', 'ready', 'namespace', 'terminating'): + with self.subTest(kind=kind): + store = deepcopy(self.store) + if kind == 'missing': store = None + if kind == 'spec': store['spec']['provider']['vault']['path'] = 'wrong' + if kind == 'ready': store['status']['conditions'] = [] + if kind == 'namespace': store['metadata']['namespace'] = 'sso' + if kind == 'terminating': store['metadata']['deletionTimestamp'] = 'now' + self.responses = [store] + with self.assertRaises(lane.Refused): + lane._DeliveryMechanics('forgejo').deliver() + self.assertFalse(any('create' in a for a,k in self.calls)) + + def test_get_error_not_absence_and_output_contained(self): + self.responses = [self.store, 'error'] + with self.assertRaisesRegex(lane.Refused, '^public_command_failed$'): + self.m.deliver() + self.assertEqual(len(self.calls), 2) + + def test_create_lost_response_or_race_uncertain_no_retry(self): + self.responses = [self.store, None, 'error'] + with self.assertRaises(lane.Uncertain): self.m.deliver() + with self.assertRaises(lane.Refused): self.m.deliver() + self.assertEqual(len(self.calls), 3) + + def test_ready_poll_is_bounded_and_created_provenance_retained(self): + obj = deepcopy(self.obj) + obj['status']['conditions'] = [] + self.responses = [self.store, None, obj] + [obj] * lane.POLL_COUNT + with self.assertRaises(lane.Uncertain) as error: self.m.deliver() + self.assertEqual(error.exception.revision['uid'], 'uid-1') + self.assertEqual(len(self.calls), lane.POLL_COUNT + 3) + with self.assertRaises(lane.Refused): self.m.remove(error.exception.revision) + + def test_readiness_updates_resource_version(self): + fresh = self.resource(self.m.external, rv='11') + self.responses = [self.store, None, self.obj, fresh] + receipt = self.m.deliver() + self.assertEqual(receipt['after']['resource_version'], '11') + + def test_replacement_during_ready_not_adopted(self): + other = self.resource(self.m.external, uid='other') + self.responses = [self.store, None, self.obj, other] + with self.assertRaises(lane.Uncertain): self.m.deliver() + + def test_drift_and_replacement_never_deleted(self): + for field in ('uid', 'resourceVersion'): + with self.subTest(field=field): + self.m = lane._DeliveryMechanics('forgejo') + receipt = self.create() + changed = deepcopy(self.obj) + changed['metadata'][field] = 'different' + self.responses = [changed] + count = len(self.calls) + with self.assertRaises(lane.Uncertain): self.m.remove(receipt['after']) + self.assertEqual(len(self.calls), count + 1) + + def test_delete_response_loss_and_conflict_uncertain(self): + receipt = self.create() + self.responses = [self.obj, 'error'] + with self.assertRaises(lane.Uncertain): self.m.remove(receipt['after']) + with self.assertRaises(lane.Refused): self.m.remove(receipt['after']) + + def test_delete_unconfirmed_or_replaced_never_repeated(self): + for replacement in (False, True): + with self.subTest(replacement=replacement): + self.m = lane._DeliveryMechanics('forgejo') + receipt = self.create() + tail = [self.resource(self.m.external, uid='new')] if replacement else [self.obj] * lane.POLL_COUNT + self.responses = [self.obj, {'kind': 'Status'}] + tail + with self.assertRaises(lane.Uncertain): self.m.remove(receipt['after']) + + def test_no_caller_paths_or_injection(self): + for client in ('a/../../secret', 'x;echo', 'a' * 49, 'A', None): + with self.subTest(client=client): + with self.assertRaises(lane.Refused): lane._DeliveryMechanics(client) + self.assertEqual(self.calls, []) + + def test_forged_revision_and_fresh_process_have_no_delete_provenance(self): + receipt = self.create() + fresh = lane._DeliveryMechanics('forgejo') + with self.assertRaises(lane.Refused): fresh.remove(receipt['after']) + with self.assertRaises(lane.Refused): self.m.remove({'uid': 'uid-1', 'resource_version': '11'}) + self.assertFalse(any('delete' in a for a,k in self.calls)) + + def test_malformed_create_or_poll_never_returns_success(self): + for malformed in ({}, {'kind': 'Status'}): + with self.subTest(malformed=malformed): + self.m = lane._DeliveryMechanics('forgejo') + self.responses = [self.store, None, malformed] + with self.assertRaises(lane.Uncertain): self.m.deliver() + + def test_poll_timeout_contains_provider_details(self): + self.responses = [self.store, None, self.obj, + subprocess.TimeoutExpired('PRIVATE PROVIDER', 60, output='PRIVATE VALUE')] + with self.assertRaises(lane.Uncertain) as error: self.m.deliver() + self.assertNotIn('PRIVATE', str(error.exception)) + + +if __name__ == '__main__': + unittest.main() diff --git a/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r1/railiance-platform.patch b/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r1/railiance-platform.patch new file mode 100644 index 00000000..85ce1875 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r1/railiance-platform.patch @@ -0,0 +1,436 @@ +--- /dev/null ++++ b/scripts/keycape_issuer_delivery.py +@@ -0,0 +1,182 @@ ++"""RPF-1.3-T4 concrete source-preparation mechanics; NO production admission. ++ ++Only public ESO objects are fetched. The production factory refuses before ++dispatch: no authenticated delivery session/evidence service currently exists. ++Private mechanics are exercised by fake-command tests, never a grant boundary. ++""" ++from __future__ import annotations ++ ++from copy import deepcopy ++import json ++import re ++import time ++ ++from keycape_issuer_eso import documents, require_fixed_store, Refused ++from state_hub_preflight_lane import command, LaneError ++ ++POLL_COUNT = 10 ++POLL_DELAY = 1 ++API = '/apis/external-secrets.io/v1' ++ ++ ++class Uncertain(Refused): ++ """No success receipt: retain exact metadata for owner reconciliation.""" ++ def __init__(self, reason, revision=None): ++ super().__init__(reason) ++ self.revision = deepcopy(revision) ++ ++ ++def production_bindings(*args, **kwargs): ++ # Neither Warden invocation fields, an operator's kubeconfig, an unsigned ++ # local receipt nor mint-lane lookup-self authenticates THIS lane/caller. ++ raise Refused('delivery_admission_session_journal_and_evidence_unavailable') ++ ++ ++def _revision(obj): ++ meta = obj.get('metadata', {}) ++ result = {'uid': meta.get('uid'), 'resource_version': meta.get('resourceVersion')} ++ if any(not isinstance(x, str) or not re.fullmatch(r'[A-Za-z0-9._:-]{1,192}', x) ++ for x in result.values()): ++ raise Refused('public_revision_invalid') ++ return result ++ ++ ++class _DeliveryMechanics: ++ """Concrete fixed kubectl algorithm, not an authenticated owner capability. ++ ++ No caller resource, remoteRef, namespace, store or path argument is accepted. ++ The deliberately unauthenticated transport cannot load ambient credentials. ++ A reviewed session transport must replace it only after real lane admission, ++ one-shot durable journaling, evidence and revocation are implemented. ++ """ ++ def __init__(self, client_id): ++ self.client_id = client_id ++ self.store, self.external = documents(client_id) ++ self.created = None ++ self.pending = False ++ ++ def _call(self, args, payload=None): ++ try: ++ result = command(['kubectl', '--kubeconfig=/dev/null', ++ '--request-timeout=5s', *args], ++ payload=payload, env={}) ++ if len(result.stdout) > 262144: ++ raise Refused('public_response_too_large') ++ if not result.stdout.strip(): ++ return None ++ value = json.loads(result.stdout) ++ if not isinstance(value, dict): ++ raise Refused('public_response_shape') ++ return value ++ except (LaneError, OSError, ValueError, TimeoutError): ++ raise Refused('public_command_failed') from None ++ except Exception: ++ # Includes subprocess timeouts; never expose stdout/stderr or argv. ++ raise Refused('public_command_failed') from None ++ ++ def _get(self, store=False): ++ obj = self.store if store else self.external ++ resource = 'clustersecretstores.external-secrets.io' if store else 'externalsecrets.external-secrets.io' ++ args = [] if store else ['-n', 'sso'] ++ return self._call([*args, 'get', resource, obj['metadata']['name'], ++ '--ignore-not-found=true', '-o', 'json']) ++ ++ def _match(self, obj): ++ if not isinstance(obj, dict): ++ raise Refused('delivery_missing') ++ metadata = obj.get('metadata', {}) ++ if (obj.get('apiVersion') != self.external['apiVersion'] ++ or obj.get('kind') != 'ExternalSecret' ++ or metadata.get('name') != self.external['metadata']['name'] ++ or metadata.get('namespace') != 'sso' ++ or metadata.get('deletionTimestamp') ++ or obj.get('spec') != self.external['spec']): ++ raise Refused('delivery_conflict') ++ return _revision(obj) ++ ++ @staticmethod ++ def _ready(obj): ++ status = obj.get('status', {}) ++ conditions = status.get('conditions', []) if isinstance(status, dict) else [] ++ return isinstance(conditions, list) and any( ++ isinstance(c, dict) and c.get('type') == 'Ready' and c.get('status') == 'True' ++ for c in conditions) ++ ++ def _receipt(self, operation, before, after): ++ return {'schema': 'netkingdom.deliver-secret.v1', 'operation': operation, ++ 'namespace': 'sso', 'name': self.external['metadata']['name'], ++ 'custody_reference': 'platform/workloads/keycape/' + self.client_id, ++ 'key_names': ['client-secret'], 'values_emitted': False, ++ 'changed': before is None if operation == 'deliver' else True, ++ 'before': before, 'after': after} ++ ++ def deliver(self): ++ if self.pending or self.created is not None: ++ raise Refused('attempt_already_used_or_unreconciled') ++ # A successful empty response proves absence; errors never do. ++ store = self._get(store=True) ++ require_fixed_store(store, self.client_id) ++ if store.get('metadata', {}).get('deletionTimestamp'): ++ raise Refused('store_terminating') ++ old = self._get() ++ before = self._match(old) if old is not None else None ++ uid = before['uid'] if before else None ++ if old is None: ++ # Set before dispatch: a lost create response MUST NOT be retried, ++ # adopted, or claimed as a pre-existing no-change result. ++ self.pending = True ++ try: ++ created = self._call(['-n', 'sso', 'create', '-f', '-', '-o', 'json'], self.external) ++ self.created = self._match(created) ++ uid = self.created['uid'] ++ except Refused: ++ raise Uncertain('create_outcome_unverified') from None ++ try: ++ for index in range(POLL_COUNT): ++ current = self._get() ++ after = self._match(current) ++ if after['uid'] != uid: ++ raise Refused('delivery_replaced') ++ if self._ready(current): ++ if self.created is not None: ++ self.created = after ++ self.pending = False ++ return self._receipt('deliver', before, after) ++ if index + 1 < POLL_COUNT: ++ time.sleep(POLL_DELAY) ++ raise Refused('delivery_ready_timeout') ++ except Refused: ++ if self.created is not None: ++ raise Uncertain('created_delivery_requires_reconciliation', self.created) from None ++ raise ++ ++ def remove(self, revision): ++ # An arbitrary adapter argument alone is never deletion provenance. ++ if self.pending or self.created is None or revision != self.created: ++ raise Refused('no_verified_case_creation') ++ try: ++ if self._match(self._get()) != self.created: ++ raise Refused('created_revision_changed') ++ except Refused: ++ raise Uncertain('created_revision_changed', self.created) from None ++ uri = API + '/namespaces/sso/externalsecrets/' + self.external['metadata']['name'] ++ options = {'apiVersion': 'v1', 'kind': 'DeleteOptions', ++ 'preconditions': {'uid': self.created['uid'], ++ 'resourceVersion': self.created['resource_version']}} ++ prior = deepcopy(self.created) ++ self.pending = True ++ try: ++ self._call(['delete', '--raw', uri, '-f', '-'], options) ++ for index in range(POLL_COUNT): ++ current = self._get() ++ if current is None: ++ self.created = None ++ self.pending = False ++ return self._receipt('remove', prior, None) ++ if _revision(current)['uid'] != prior['uid']: ++ raise Refused('replacement_preserved') ++ if index + 1 < POLL_COUNT: ++ time.sleep(POLL_DELAY) ++ raise Refused('delete_unconfirmed') ++ except Refused: ++ raise Uncertain('cleanup_requires_reconciliation', prior) from None +--- /dev/null ++++ b/tests/test_keycape_issuer_delivery.py +@@ -0,0 +1,196 @@ ++"""Offline command-level regressions. No HOME/env/network/credential access.""" ++from copy import deepcopy ++import json ++import os ++from pathlib import Path ++import subprocess ++import sys ++import unittest ++from unittest.mock import patch ++ ++sys.path.insert(0, str(Path(__file__).resolve().parents[1] / 'scripts')) ++import keycape_issuer_delivery as lane ++ ++ ++class DeliveryTests(unittest.TestCase): ++ def setUp(self): ++ self.environment = patch.dict(os.environ, {}, clear=True) ++ self.environment.start() ++ self.addCleanup(self.environment.stop) ++ self.network = patch('socket.socket', side_effect=AssertionError('network forbidden')) ++ self.network.start() ++ self.addCleanup(self.network.stop) ++ self.m = lane._DeliveryMechanics('forgejo') ++ self.store = self.resource(self.m.store) ++ self.obj = self.resource(self.m.external) ++ self.calls = [] ++ self.responses = [] ++ self.native = patch('subprocess.run', side_effect=self.run_command) ++ self.native.start() ++ self.addCleanup(self.native.stop) ++ self.sleep = patch.object(lane.time, 'sleep') ++ self.sleep.start() ++ self.addCleanup(self.sleep.stop) ++ ++ def resource(self, template, uid='uid-1', rv='10', ready=True): ++ obj = deepcopy(template) ++ obj['metadata'].update(uid=uid, resourceVersion=rv) ++ obj['status'] = {'conditions': [{'type': 'Ready', 'status': str(ready)}]} ++ return obj ++ ++ def run_command(self, argv, **kw): ++ self.calls.append((deepcopy(argv), deepcopy(kw))) ++ self.assertEqual(kw['env'], {}) ++ self.assertEqual(argv[:3], ['kubectl', '--kubeconfig=/dev/null', '--request-timeout=5s']) ++ self.assertTrue(kw['capture_output']) ++ self.assertEqual(kw['timeout'], 60) ++ if not self.responses: ++ self.fail('unexpected command dispatch') ++ result = self.responses.pop(0) ++ if isinstance(result, Exception): ++ raise result ++ if result == 'error': ++ return subprocess.CompletedProcess(argv, 1, b'PRIVATE PROVIDER VALUE', b'PRIVATE ERROR') ++ return subprocess.CompletedProcess(argv, 0, b'' if result is None else json.dumps(result).encode(), b'') ++ ++ def create(self): ++ self.responses += [self.store, None, self.obj, self.obj] ++ return self.m.deliver() ++ ++ def test_production_factory_fails_before_dispatch(self): ++ with self.assertRaisesRegex(lane.Refused, 'unavailable'): ++ lane.production_bindings(case_source=b'arbitrary', admitted=object()) ++ self.assertEqual(self.calls, []) ++ ++ def test_absent_create_ready_and_uid_rv_precondition_delete(self): ++ receipt = self.create() ++ self.assertTrue(receipt['changed']) ++ self.assertIsNone(receipt['before']) ++ self.assertEqual(receipt['after'], {'uid': 'uid-1', 'resource_version': '10'}) ++ self.responses += [self.obj, {'kind': 'Status'}, None] ++ removed = self.m.remove(receipt['after']) ++ self.assertEqual(removed['operation'], 'remove') ++ delete = next(c for c in self.calls if 'delete' in c[0]) ++ options = json.loads(delete[1]['input']) ++ self.assertEqual(options['preconditions'], {'uid': 'uid-1', 'resourceVersion': '10'}) ++ self.assertEqual(delete[0][5], '/apis/external-secrets.io/v1/namespaces/sso/externalsecrets/keycape-forgejo-client') ++ self.assertFalse(any('secret' == a or 'secrets' == a for c in self.calls for a in c[0])) ++ ++ def test_matching_existing_no_change_never_delete(self): ++ self.responses = [self.store, self.obj, self.obj] ++ receipt = self.m.deliver() ++ self.assertFalse(receipt['changed']) ++ with self.assertRaises(lane.Refused): ++ self.m.remove(receipt['after']) ++ self.assertFalse(any('create' in a or 'delete' in a for a, k in self.calls)) ++ ++ def test_conflicting_existing_untouched(self): ++ drift = deepcopy(self.obj) ++ drift['spec']['data'][0]['remoteRef']['key'] = 'elsewhere' ++ self.responses = [self.store, drift] ++ with self.assertRaises(lane.Refused): ++ self.m.deliver() ++ self.assertEqual(len(self.calls), 2) ++ ++ def test_missing_drifted_unready_store_never_create(self): ++ for kind in ('missing', 'spec', 'ready', 'namespace', 'terminating'): ++ with self.subTest(kind=kind): ++ store = deepcopy(self.store) ++ if kind == 'missing': store = None ++ if kind == 'spec': store['spec']['provider']['vault']['path'] = 'wrong' ++ if kind == 'ready': store['status']['conditions'] = [] ++ if kind == 'namespace': store['metadata']['namespace'] = 'sso' ++ if kind == 'terminating': store['metadata']['deletionTimestamp'] = 'now' ++ self.responses = [store] ++ with self.assertRaises(lane.Refused): ++ lane._DeliveryMechanics('forgejo').deliver() ++ self.assertFalse(any('create' in a for a,k in self.calls)) ++ ++ def test_get_error_not_absence_and_output_contained(self): ++ self.responses = [self.store, 'error'] ++ with self.assertRaisesRegex(lane.Refused, '^public_command_failed$'): ++ self.m.deliver() ++ self.assertEqual(len(self.calls), 2) ++ ++ def test_create_lost_response_or_race_uncertain_no_retry(self): ++ self.responses = [self.store, None, 'error'] ++ with self.assertRaises(lane.Uncertain): self.m.deliver() ++ with self.assertRaises(lane.Refused): self.m.deliver() ++ self.assertEqual(len(self.calls), 3) ++ ++ def test_ready_poll_is_bounded_and_created_provenance_retained(self): ++ obj = deepcopy(self.obj) ++ obj['status']['conditions'] = [] ++ self.responses = [self.store, None, obj] + [obj] * lane.POLL_COUNT ++ with self.assertRaises(lane.Uncertain) as error: self.m.deliver() ++ self.assertEqual(error.exception.revision['uid'], 'uid-1') ++ self.assertEqual(len(self.calls), lane.POLL_COUNT + 3) ++ with self.assertRaises(lane.Refused): self.m.remove(error.exception.revision) ++ ++ def test_readiness_updates_resource_version(self): ++ fresh = self.resource(self.m.external, rv='11') ++ self.responses = [self.store, None, self.obj, fresh] ++ receipt = self.m.deliver() ++ self.assertEqual(receipt['after']['resource_version'], '11') ++ ++ def test_replacement_during_ready_not_adopted(self): ++ other = self.resource(self.m.external, uid='other') ++ self.responses = [self.store, None, self.obj, other] ++ with self.assertRaises(lane.Uncertain): self.m.deliver() ++ ++ def test_drift_and_replacement_never_deleted(self): ++ for field in ('uid', 'resourceVersion'): ++ with self.subTest(field=field): ++ self.m = lane._DeliveryMechanics('forgejo') ++ receipt = self.create() ++ changed = deepcopy(self.obj) ++ changed['metadata'][field] = 'different' ++ self.responses = [changed] ++ count = len(self.calls) ++ with self.assertRaises(lane.Uncertain): self.m.remove(receipt['after']) ++ self.assertEqual(len(self.calls), count + 1) ++ ++ def test_delete_response_loss_and_conflict_uncertain(self): ++ receipt = self.create() ++ self.responses = [self.obj, 'error'] ++ with self.assertRaises(lane.Uncertain): self.m.remove(receipt['after']) ++ with self.assertRaises(lane.Refused): self.m.remove(receipt['after']) ++ ++ def test_delete_unconfirmed_or_replaced_never_repeated(self): ++ for replacement in (False, True): ++ with self.subTest(replacement=replacement): ++ self.m = lane._DeliveryMechanics('forgejo') ++ receipt = self.create() ++ tail = [self.resource(self.m.external, uid='new')] if replacement else [self.obj] * lane.POLL_COUNT ++ self.responses = [self.obj, {'kind': 'Status'}] + tail ++ with self.assertRaises(lane.Uncertain): self.m.remove(receipt['after']) ++ ++ def test_no_caller_paths_or_injection(self): ++ for client in ('a/../../secret', 'x;echo', 'a' * 49, 'A', None): ++ with self.subTest(client=client): ++ with self.assertRaises(lane.Refused): lane._DeliveryMechanics(client) ++ self.assertEqual(self.calls, []) ++ ++ def test_forged_revision_and_fresh_process_have_no_delete_provenance(self): ++ receipt = self.create() ++ fresh = lane._DeliveryMechanics('forgejo') ++ with self.assertRaises(lane.Refused): fresh.remove(receipt['after']) ++ with self.assertRaises(lane.Refused): self.m.remove({'uid': 'uid-1', 'resource_version': '11'}) ++ self.assertFalse(any('delete' in a for a,k in self.calls)) ++ ++ def test_malformed_create_or_poll_never_returns_success(self): ++ for malformed in ({}, {'kind': 'Status'}): ++ with self.subTest(malformed=malformed): ++ self.m = lane._DeliveryMechanics('forgejo') ++ self.responses = [self.store, None, malformed] ++ with self.assertRaises(lane.Uncertain): self.m.deliver() ++ ++ def test_poll_timeout_contains_provider_details(self): ++ self.responses = [self.store, None, self.obj, ++ subprocess.TimeoutExpired('PRIVATE PROVIDER', 60, output='PRIVATE VALUE')] ++ with self.assertRaises(lane.Uncertain) as error: self.m.deliver() ++ self.assertNotIn('PRIVATE', str(error.exception)) ++ ++ ++if __name__ == '__main__': ++ unittest.main() +--- a/docs/keycape-issuer-eso-contract.md ++++ b/docs/keycape-issuer-eso-contract.md +@@ -66,3 +66,49 @@ + RPF-1.3-C1 and RPF-1.3-T4 in the frozen owner review. The scaffold's historical + audience field placement differs from the issuer draft; this candidate uses + the issuer draft's serviceAccountRef.audiences, not a claim of installed support. ++ ++## Prepared delivery mechanics (still unavailable as a production lane) ++ ++`scripts/keycape_issuer_delivery.py` now contains concrete command mechanics for ++the existing RPF-1.3-T4 obligation. It reuses `keycape_issuer_eso.documents()` and ++`require_fixed_store()` and the existing contained `state_hub_preflight_lane.command()` ++transport. Only exact named public ESO resources are read. It proves absence ++with successful empty `get --ignore-not-found`, creates only the ExternalSecret, ++compares an existing object's full spec, and polls at most ten times for Ready ++with matching spec and stable UID. Each request has a 5s kubectl request timeout ++inside the existing 60s process bound; nine sleeps of 1s bound each poll loop. ++Controller resourceVersion updates during successful readiness are captured. ++ ++Removal is permitted only for that instance's verified case-created revision, ++rechecks spec/UID/resourceVersion, uses Kubernetes DeleteOptions with both UID ++and resourceVersion preconditions, and confirms absence with bounded polling. ++Existing matching objects return changed=false and are never rollback candidates. ++Creation response loss, readiness failure after creation, replacement, delete ++conflict or unconfirmed deletion raises a fixed uncertainty diagnostic and keeps ++the last verified revision when known. No retry adopts an uncertain create; no ++replacement is deleted. An unknown create outcome requires owner reconciliation ++using the fixed resource name even when no UID could be recovered. Target Secret ++garbage collection by ESO/Kubernetes is outside this helper; it never reads or ++directly deletes Secret objects. There is no claim that deletion proves target ++Secret disappearance. ++ ++The callable `production_bindings()` **always refuses before any command**. ++The private mechanics use empty environment and `/dev/null` kubeconfig and ++therefore cannot borrow the caller's native session. They are source preparation, ++not an adapter/evidence authority. Tests intercept the actual subprocess call; ++they do not provide production callbacks or attestations. A real lane requires ++an owner-reviewed authenticated human aal2 session, acknowledged case revision ++and caller binding, scoped Kubernetes credentials, one-shot durable admission ++journaling and authenticated evidence publication/verification, plus real ++per-attempt session revocation and recovery. None exists for this lane at the ++pinned source. The generic task_execution_journal module is real storage but ++explicitly does not admit native work; it is not silently repurposed here. ++The mint broker's case checker and OpenBao lookup-self are real for its existing ++mint lane, not delivery authentication. The attended custody-projection SSH ++transport and credential-change evidence writer likewise confer no delivery ++lane authority. No unsigned receipt or `True` oracle replaces these facilities. ++ ++Consequently RPF-1.3-T4 remains open for real capability binding and owner review; ++this patch completes only concrete ESO command/state mechanics and regressions. ++No ESO role, policy, Store, admission service, grant, catalog acceptance, native ++permission or owner descriptor acceptance is created or inferred. diff --git a/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r1/regressions.json b/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r1/regressions.json new file mode 100644 index 00000000..9298828e --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r1/regressions.json @@ -0,0 +1,15 @@ +{ + "schema": "platform.delivery-source-regressions.v1", + "initial_regression": "First 14-test run had one incorrect test assertion: delete argv URI index 4 instead of 5. Corrected test; no backend behavior changed for that failure.", + "final_runs": { + "test_keycape_issuer_delivery.py": { + "exit_code": 0, + "output": "test_absent_create_ready_and_uid_rv_precondition_delete (test_keycape_issuer_delivery.DeliveryTests.test_absent_create_ready_and_uid_rv_precondition_delete) ... ok\ntest_conflicting_existing_untouched (test_keycape_issuer_delivery.DeliveryTests.test_conflicting_existing_untouched) ... ok\ntest_create_lost_response_or_race_uncertain_no_retry (test_keycape_issuer_delivery.DeliveryTests.test_create_lost_response_or_race_uncertain_no_retry) ... ok\ntest_delete_response_loss_and_conflict_uncertain (test_keycape_issuer_delivery.DeliveryTests.test_delete_response_loss_and_conflict_uncertain) ... ok\ntest_delete_unconfirmed_or_replaced_never_repeated (test_keycape_issuer_delivery.DeliveryTests.test_delete_unconfirmed_or_replaced_never_repeated) ... ok\ntest_drift_and_replacement_never_deleted (test_keycape_issuer_delivery.DeliveryTests.test_drift_and_replacement_never_deleted) ... ok\ntest_forged_revision_and_fresh_process_have_no_delete_provenance (test_keycape_issuer_delivery.DeliveryTests.test_forged_revision_and_fresh_process_have_no_delete_provenance) ... ok\ntest_get_error_not_absence_and_output_contained (test_keycape_issuer_delivery.DeliveryTests.test_get_error_not_absence_and_output_contained) ... ok\ntest_malformed_create_or_poll_never_returns_success (test_keycape_issuer_delivery.DeliveryTests.test_malformed_create_or_poll_never_returns_success) ... ok\ntest_matching_existing_no_change_never_delete (test_keycape_issuer_delivery.DeliveryTests.test_matching_existing_no_change_never_delete) ... ok\ntest_missing_drifted_unready_store_never_create (test_keycape_issuer_delivery.DeliveryTests.test_missing_drifted_unready_store_never_create) ... ok\ntest_no_caller_paths_or_injection (test_keycape_issuer_delivery.DeliveryTests.test_no_caller_paths_or_injection) ... ok\ntest_poll_timeout_contains_provider_details (test_keycape_issuer_delivery.DeliveryTests.test_poll_timeout_contains_provider_details) ... ok\ntest_production_factory_fails_before_dispatch (test_keycape_issuer_delivery.DeliveryTests.test_production_factory_fails_before_dispatch) ... ok\ntest_readiness_updates_resource_version (test_keycape_issuer_delivery.DeliveryTests.test_readiness_updates_resource_version) ... ok\ntest_ready_poll_is_bounded_and_created_provenance_retained (test_keycape_issuer_delivery.DeliveryTests.test_ready_poll_is_bounded_and_created_provenance_retained) ... ok\ntest_replacement_during_ready_not_adopted (test_keycape_issuer_delivery.DeliveryTests.test_replacement_during_ready_not_adopted) ... ok\n\n----------------------------------------------------------------------\nRan 17 tests in 0.186s\n\nOK\n" + }, + "test_keycape_issuer_eso.py": { + "exit_code": 0, + "output": "test_delivery_document_is_only_external_and_detached (test_keycape_issuer_eso.FixedIssuerContract.test_delivery_document_is_only_external_and_detached) ... ok\ntest_external_drift_refuses (test_keycape_issuer_eso.FixedIssuerContract.test_external_drift_refuses) ... ok\ntest_independent_forgejo_binding (test_keycape_issuer_eso.FixedIssuerContract.test_independent_forgejo_binding) ... ok\ntest_missing_or_unready_refuses (test_keycape_issuer_eso.FixedIssuerContract.test_missing_or_unready_refuses) ... ok\ntest_no_caller_store_or_path_override (test_keycape_issuer_eso.FixedIssuerContract.test_no_caller_store_or_path_override) ... ok\ntest_store_drift_refuses (test_keycape_issuer_eso.FixedIssuerContract.test_store_drift_refuses) ... ok\ntest_type_admitted_trailing_dash_is_preserved (test_keycape_issuer_eso.FixedIssuerContract.test_type_admitted_trailing_dash_is_preserved) ... ok\ntest_valid_ready_metadata_allows_extra_controller_metadata (test_keycape_issuer_eso.FixedIssuerContract.test_valid_ready_metadata_allows_extra_controller_metadata) ... ok\n\n----------------------------------------------------------------------\nRan 8 tests in 0.316s\n\nOK\n" + } + }, + "scope": "offline only, subprocess mocked; environment cleared and socket.socket forbidden by delivery tests; no native credentials or network" +} diff --git a/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r1/source-pins.json b/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r1/source-pins.json new file mode 100644 index 00000000..23692bf3 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r1/source-pins.json @@ -0,0 +1,40 @@ +{ + "railiance-platform": { + "commit": "104ed90f42a9a86bf9fda343b49b65a85c9ee4b9", + "files": { + "AGENTS.md": "sha256:8d2f1a165e3f4eecf2e6e1d6a19c4bac9e8dcb64a76650e7185d55722f233c8b", + "INTENT.md": "sha256:12c6540735af591f7a4a9fe85ff10d3cd00eec55b5b6b1ddf148abf2d3ac9a9c", + "scripts/keycape_issuer_eso.py": "sha256:21c5797280c2a7825dbda8ac970d34a0cdef4c6458f68bce0b1c12407f122a87", + "manifests/templates/keycape-issuer-eso.yaml": "sha256:e17498bd8a4c820041a4105e4efe9c60c6a50953db8f621ff54887080065c743", + "scripts/state_hub_preflight_lane.py": "sha256:8bf63beda028de7c2375084afb7c22fb00a731c54ebf0b3ba369a699d920e0f5", + "scripts/keycape_client_secret_generate.py": "sha256:9576cd211cfc06526ec4cc9bd0a5b06f0f59287a68931bb2d6e3592a951ddc7d", + "scripts/task_execution_journal.py": "sha256:604cf285286a8a1be23d9bc0d8a547bd1195b7f80286778e1d4f30c711fd6a7e", + "scripts/custody-projection.py": "sha256:d5ca1017b4877a12c7fa24535e6b497492cd63e5e99c0674a8e9fc3ad93ead87", + "scripts/remote_exec.py": "sha256:4d60c1bf3f6aa3a0c6867fadb137abf23699b4cf7ebeea317e4ce794bacc7968", + "scripts/repair_eso_kubernetes_auth.py": "sha256:dd58e4ab3f88c777aacb44368c2c099b6e32637ce76f5157e7a874cfbe9d5f80", + "scripts/credential-change.py": "sha256:54ca1bc33ed4bb7807917f71ed45b6b35da978bc789e013386e1e90a20bee06d" + } + }, + "net-kingdom": { + "commit": "befb0bc68cd25b625804fce559476eefd702521d", + "files": { + "sso-mfa/k8s/keycape/deliver-secret-adapter.py": "sha256:cd1a515a1ae2def2093660ed2a810a9c66d1d4cf7126f573ee7e9c4c42afb98d" + } + }, + "ops-warden": { + "commit": "b593c879831378db7dc62adfabdfedf2d44ddd38", + "files": { + "registry/routing/catalog.yaml": "sha256:ba9634a92e584dac8c42e8624ab52adce06e79a285209cb64c0daa4dac9cd7d8" + } + }, + "owner_review": { + "path": "/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-17/nk48-1.3-owner-review/owner-review-railiance-platform.json", + "sha256": "d3f523b60924b530376b7aac4459cecda5da183848dd6e35889c1013c73439d9" + }, + "prepared_files": { + "scripts/keycape_issuer_delivery.py": "sha256:bd0fbbbdd0b08037e0d39e76f458e143663afa7e1e4ea9697639863c74e27ef7", + "tests/test_keycape_issuer_delivery.py": "sha256:1d35c24a2ddaab17ef6419afd52d2c9b45a72fc1f107a504f5d5ed1d33fd456f", + "docs/keycape-issuer-eso-contract.md": "sha256:ac26dd1bf204fba691da4c34308c6051f1cf23d8e19c989c51672de74817c044" + }, + "patch_sha256": "9b9b5a81bfc65693112e136fff44ce98c35a91151dc645659d80e6a5b2da86ca" +} diff --git a/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/README.md b/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/README.md new file mode 100644 index 00000000..ca8ad6ea --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/README.md @@ -0,0 +1,130 @@ +# Platform delivery backend source preparation r2 + +Same candidate trial as r1. **Source preparation only; production dispatch +remains refused.** No canonical Platform/NetKingdom/Warden files, commits, +credentials, grants, owner acceptance, native cluster operations or authenticated +delivery evidence were created. Original r1 and its adverse independent review +remain preserved in their existing directories. + +`railiance-platform.patch` applies to Platform +`104ed90f42a9a86bf9fda343b49b65a85c9ee4b9`. +`r1-to-r2.patch` is an alternative incremental patch after the original r1 +candidate. Do not apply both. Both were checked and applied only in scratch; +the resulting files match `prepared-source/` byte for byte. The fixed template +is unchanged. These artifacts do not authorize canonical integration. + +## Source-grounded corrections + +Official ESO v0.16.1 resolves to commit +`8d037d197c88eeb9c4fe489e906c9cb3ab9ee6af`. Pinned public upstream source, +URLs and digests are retained in `upstream/` and `upstream-sources.json`. + +- The controller's syncedResourceVersion is **generation plus metadata hash**, + rather than Kubernetes resourceVersion or an observedGeneration field. + The exact [upstream metadata token](https://github.com/external-secrets/external-secrets/blob/8d037d197c88eeb9c4fe489e906c9cb3ab9ee6af/pkg/controllers/util/util.go) + hashes a Go-formatted annotations/labels struct using the + [upstream ObjectHash algorithm](https://github.com/external-secrets/external-secrets/blob/8d037d197c88eeb9c4fe489e906c9cb3ab9ee6af/pkg/utils/utils.go). + r2 matches this token, including metadata changes. Five actual standalone + Go extraction vectors cover nil/empty maps, map ordering, delimiters, + newlines, Unicode, NUL and maximum signed generation; Python matches all. +- `_ready` additionally requires one Ready=True/SecretSynced condition with + the successful-sync message, and a valid refreshTime within the fixed + five-minute interval and not in the future. The + [actual controller](https://github.com/external-secrets/external-secrets/blob/8d037d197c88eeb9c4fe489e906c9cb3ab9ee6af/pkg/controllers/externalsecret/externalsecret_controller.go) + sets refreshTime to reconcile start. Its Retain/missing-provider branch can + also set Ready=True with the same reason, but uses a different message; + r2 refuses that branch. lastTransitionTime cannot replace this check: + unchanged Ready conditions retain it across reconciles. +- Only the three cached v1 CRD remoteRef defaults are normalized: + conversionStrategy=Default, decodingStrategy=None, metadataPolicy=None. + Alternate values and any other extra/spec/path/identity changes are refused. + The helper remains a metadata contract; the runtime freshness gate is in + delivery mechanics. +- Nested metadata, generation, maps and collection shapes are checked before + use. Malformed create/poll/remove responses yield fixed refusal/uncertainty + without provider detail. Valid create-response identity is retained even if + later shape/spec validation fails; pending state still bars blind cleanup. +- Reads use explicit raw v1 paths. A fixed field-selected ExternalSecret + collection GET provides structured successful absence; nonzero/error output + is never interpreted as absence. Returned namespace/name/API/kind, item + count and continuation are checked. There is no Secret read/list or Store + mutation. This changes future session requirements to permit a fixed + field-selected **ExternalSecret** list; no such permission is supplied here. +- `bounded_public_command.py` enforces an aggregate 262144-byte stdout+stderr + cap while reading, retains no stderr, and handles input concurrently. + Commands have a six-second maximum; an operation has a 30-second monotonic + deadline and refuses success after it. Each cleanup has a separate one-second + reap budget. The process group is killed on success, overflow, timeout and + failure, before the leader is reaped, avoiding reuse of its process-group ID. + Unconfirmed cleanup is refusal. Tests launch only local synthetic Python + children, including descendants holding pipes or closing them early. + +## Store freshness and the remaining race + +The [actual Store controller](https://github.com/external-secrets/external-secrets/blob/8d037d197c88eeb9c4fe489e906c9cb3ab9ee6af/pkg/controllers/secretstore/common.go) +publishes no generation-bound readiness token. r2 requires its fixed spec, +one Valid/validated Ready condition, valid UID/RV, no termination and +**generation 1**. Under Kubernetes generation/status-subresource semantics, +that excludes a previous spec revision; any later generation gets the specific +`store_ready_generation_unverifiable` refusal. No observedGeneration is invented +and a changed Store cannot simply be accepted because Ready stayed true. + +The sealed Store UID/RV/spec is observed again before create, during readiness +and immediately before returning a receipt. These observations detect drift +that is visible at those times. They **do not** atomically bind the +ExternalSecret create or ESO's Store lookup to that Store revision. A Store +change/restoration entirely between observations is undetectable. One negative +test deliberately performs this interleaving and observes that private mechanics +can still return a receipt. Another changes the Store in the create window and +proves that mutation already happened before subsequent refusal. + +Accordingly, actual owner-controlled exclusion/admission remains a production +blocker. Double reads are not described as exclusion, and this preparation +does not create an admission service or confer new authority. Remove uses +ExternalSecret UID **and** RV server preconditions and does not require a +working Store, so safe reconciliation is not coupled to Store availability. + +## Evidence and limits + +`regressions.log` records **42/42** passing tests on an isolated tree produced +by applying the full patch: 9 runner abuse/process tests, 25 delivery tests and +8 existing fixed-contract tests. `assert_corrected_regressions.py` exercises +desired assertions against both versions: untouched r1 produces two failures +and two errors; r2 passes all four. The predecessor failure log is retained. +`patch-runs.json` records both alternative patch check/application results. + +The initial 33-test run had one fixture assertion failure: a descendant was +still scheduled in state R immediately after SIGKILL. Its log is preserved. +The verification now allows up to 200ms for the scheduler to expose termination; +it accepts only disappearance or zombie state, never a still-running child. +No authority or runtime promotion is scored from this correction. + +Run `python3 reproduce.py` to assemble from pinned local Git objects and rerun +the final 42 plus four corrected assertions. Tests clear inherited environment, +use an empty scratch HOME and deny socket construction. Actual child fixtures +also deny socket creation within their own interpreters; no kubectl or other +owner executable is launched. Public upstream retrieval occurred separately +with no authentication. Go vectors use only local standard-library compilation +with GOPROXY/GOSUMDB disabled. + +Supported claims are source-level exact payload/endpoint generation, synthetic +current-metadata controller-report checks, bounded collection and conservative +unknown-outcome handling. Server enforcement of delete preconditions, installed +ESO version, trusted status custody, runtime credentials, present Secret contents +and native outcomes have not been tested. Hash tokens are not authentication. +Recent refresh checks assume a sane local clock. Store generation 1 does not +prove current provider availability or solve cross-object exclusion. + +Process cleanup covers ordinary descendants in the command's process group; +it is not a sandbox against a program escaping the group/session. The leader +is reaped within the cleanup budget or cleanup is explicitly unconfirmed; +orphan descendants may be zombies awaiting their actual parent/reaper. Kernel +process creation and scheduling are not a hard real-time guarantee. A production +runtime still needs its actual reviewed isolation and executable/session binding. + +`production_bindings()` continues refusing missing actual source/case/client +authorization, owner exclusion/admission, scoped caller/session, durable one-shot +admission/recovery journal, authenticated evidence publication/verification and +per-attempt revocation. Local one-shot state, source hashes, unsigned receipts, +public source retrieval and passing tests supply none of those. This is not +completion of RPF-1.3-T4 and does not make dispatch operational. diff --git a/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/assert_corrected_regressions.py b/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/assert_corrected_regressions.py new file mode 100644 index 00000000..2d3cfe8b --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/assert_corrected_regressions.py @@ -0,0 +1,47 @@ +"""Desired assertions: original r1 must fail, corrected r2 must pass. No dispatch.""" +from copy import deepcopy +import os +from pathlib import Path +import sys +import unittest +from unittest.mock import patch + +sys.path.insert(0, str(Path(sys.argv.pop(1)).resolve() / 'scripts')) +import keycape_issuer_delivery as lane + + +class CorrectedContract(unittest.TestCase): + def setUp(self): + p = patch.dict(os.environ, {}, clear=True); p.start(); self.addCleanup(p.stop) + p = patch('socket.socket', side_effect=AssertionError('network forbidden')) + p.start(); self.addCleanup(p.stop) + self.m = lane._DeliveryMechanics('forgejo') + self.obj = deepcopy(self.m.external) + self.obj['metadata'].update(uid='fixture', resourceVersion='10', generation=2) + self.obj['status'] = {'conditions': [{'type': 'Ready', 'status': 'True'}], + 'syncedResourceVersion': '1-stale', + 'refreshTime': '2020-01-01T00:00:00Z'} + + def ready(self, value): + if hasattr(lane, '_ready'): return lane._ready(value) + return self.m._ready(value) + + def test_stale_generation_is_not_ready(self): + self.assertFalse(self.ready(self.obj)) + + def test_retained_provider_absence_is_not_successful_sync(self): + self.obj['status']['conditions'][0].update( + reason='SecretSynced', message='secret retained due to DeletionPolicy=Retain') + self.assertFalse(self.ready(self.obj)) + + def test_actual_crd_defaults_match_fixed_external(self): + self.obj['spec']['data'][0]['remoteRef'].update( + conversionStrategy='Default', decodingStrategy='None', metadataPolicy='None') + self.assertEqual(self.m._match(self.obj)['uid'], 'fixture') + + def test_malformed_metadata_is_a_fixed_refusal(self): + self.obj['metadata'] = None + with self.assertRaises(lane.Refused): self.m._match(self.obj) + + +if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/eso-hash-inputs.json b/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/eso-hash-inputs.json new file mode 100644 index 00000000..2381c5d7 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/eso-hash-inputs.json @@ -0,0 +1,42 @@ +[ + { + "generation": 1 + }, + { + "generation": 2, + "annotations": {}, + "labels": {} + }, + { + "generation": 3, + "annotations": { + "b": "2", + "a": "1" + }, + "labels": { + "z": "last", + "c": "first" + } + }, + { + "generation": 4, + "annotations": { + "a:b": "c d", + "a b": "[]:%+v" + }, + "labels": { + "newline\n": "tab\t" + } + }, + { + "generation": 9223372036854775807, + "annotations": { + "é": "好", + "🦊": "😀" + }, + "labels": { + "": "", + "null": "\u0000" + } + } +] diff --git a/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/eso-hash-vectors.go b/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/eso-hash-vectors.go new file mode 100644 index 00000000..f9ceda05 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/eso-hash-vectors.go @@ -0,0 +1,39 @@ +// Standalone extraction of ESO v0.16.1 HashMeta/ObjectHash formatting. +// Upstream 8d037d197c88eeb9c4fe489e906c9cb3ab9ee6af is Apache-2.0; +// original licensed source and exact hashes are retained under upstream/. +package main + +import ( + "crypto/md5" + "encoding/json" + "fmt" + "os" +) + +type input struct { + Generation int64 `json:"generation"` + Annotations map[string]string `json:"annotations"` + Labels map[string]string `json:"labels"` +} + +func main() { + var fixtures []input + if err := json.NewDecoder(os.Stdin).Decode(&fixtures); err != nil { + panic("fixture invalid") + } + results := []string{} + for _, fixture := range fixtures { + type meta struct { + annotations map[string]string + labels map[string]string + } + textualVersion := fmt.Sprintf("%+v", meta{ + annotations: fixture.Annotations, + labels: fixture.Labels, + }) + results = append(results, fmt.Sprintf("%d-%x", fixture.Generation, md5.Sum([]byte(textualVersion)))) + } + if err := json.NewEncoder(os.Stdout).Encode(results); err != nil { + panic("fixture output failed") + } +} diff --git a/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/patch-runs.json b/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/patch-runs.json new file mode 100644 index 00000000..f838bba4 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/patch-runs.json @@ -0,0 +1,22 @@ +[ + { + "label": "full-check", + "exit_code": 0 + }, + { + "label": "full-apply-in-scratch", + "exit_code": 0 + }, + { + "label": "full-patch-applied-regressions", + "exit_code": 0 + }, + { + "label": "incremental-check", + "exit_code": 0 + }, + { + "label": "incremental-apply-in-scratch", + "exit_code": 0 + } +] diff --git a/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/prepared-source/docs/keycape-issuer-eso-contract.md b/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/prepared-source/docs/keycape-issuer-eso-contract.md new file mode 100644 index 00000000..1783d4b3 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/prepared-source/docs/keycape-issuer-eso-contract.md @@ -0,0 +1,175 @@ +# Proposed fixed issuer ESO contract — RPF-1.3-T4 / NK48-R5-F2 + +Status: proposed source; not admitted, deployed, or owner-acknowledged. This is a +new Platform declaration for review, not an existing Forgejo grant discovery. + +The only substitution in `manifests/templates/keycape-issuer-eso.yaml` +(sha256:e17498bd8a4c820041a4105e4efe9c60c6a50953db8f621ff54887080065c743) is the admitted `client_id`. The proposed issuer +store is **ClusterSecretStore `openbao-keycape-{client_id}-issuer`**, cluster +scoped (no metadata namespace), restricted by conditions to ExternalSecrets in +`sso`. For Forgejo it is `openbao-keycape-forgejo-issuer`. The suffix makes the +issuer purpose explicit and keeps type-admitted IDs ending in `-` representable. +The store's DNS subdomain name can exceed 63 characters; the existing adapter's +63-character target Secret reference bound is preserved (client ID at most 48). +No type parameter is added or changed. + +The provider is `http://openbao.openbao.svc:8200`, KV v2 mount `platform`, +Kubernetes auth mount `kubernetes`, role `external-secrets-keycape-{client_id}`, +serviceAccountRef `external-secrets/external-secrets`, audience `openbao`. +CCR-2026-0041 already records that Forgejo role/identity/audience, not this new +store name or an unrestricted client family grant. Other clients still require +their own existing owner-controlled role/case admission. + +The ExternalSecret is `sso/keycape-{client_id}-client`, references precisely that +ClusterSecretStore, refreshes every 5m and targets the same name with Owner +creation and Retain remote deletion. Its only data entry maps `client-secret` +from remoteRef `workloads/keycape/{client_id}`, property `CLIENT_SECRET`. The +provider mount plus mount-relative remoteRef equals custody reference +`platform/workloads/keycape/{client_id}`; do not double-prefix `platform/`. +This is distinct from the Apps-owned consumer namespace SecretStore +`openbao-keycape-{client_id}-consumer` and its dedicated consumer identity. + +Store provisioning belongs to Platform's separate owner-controlled path. The +proposed `platform-secret-delivery` lane retains ONLY create/get/watch/delete on +the exact ExternalSecret in `sso`; this source does not delegate store creation +or Secret read/list. Never apply the multi-document template through that lane. +The pure `scripts/keycape_issuer_eso.py` module exposes only the ExternalSecret +document through `external_secret()` and validates fixed public observations +through `require_fixed_store()` and `require_ready_delivery()`. It performs no +API call and issues no evidence/receipt. It is not a real capability or admission +boundary; a caller-supplied observation alone establishes no authenticity. + +The existing RPF-1.3-T4 delivery binding must validate the case/client binding, +verify this exact preexisting store's public identity/spec/Ready state, apply or +compare the exact ExternalSecret, and wait for matching spec plus Ready=True. +The proposed helper accepts extra controller metadata but deliberately refuses +spec drift. Actual API defaulting/normalization and installed ESO schema support +must be checked in that implementation; these fixtures establish no native +compatibility or credential isolation. ESO's shared controller remains trusted. +No Secret bodies, values, arbitrary remoteRefs, or raw errors may enter receipts. + +Existing owner requirements remain: closed `netkingdom.deliver-secret.v1` +metadata receipt with authenticated before/after UID/resourceVersion provenance, +unchanged objects reported NO_CHANGE, attempt-session revocation, and created-only +rollback guarded by exact UID/resourceVersion (drift or uncertain ownership goes +to owner reconciliation). The helper neither deletes a target Secret nor creates +an OpenBao role/policy nor changes CCR-0041. These capabilities and catalog routing +remain unimplemented/unaccepted under RPF-1.3-T4. Independent Platform source +review of this declaration precedes adopting the conditional NetKingdom type +text; no descriptor acceptance, acknowledgment, pin, grant, catalog or native +permission is inferred. + +Grounding: CCR-2026-0041 exact role/identity; the approved historical +`docs/provisioning-windows/2026-10-04-1000/keycape/eso.draft.yaml` issuer +ClusterSecretStore and target pattern; `scripts/catalog_lane_scaffold.py` +ClusterSecretStore/namespace restriction and mount-relative remoteRef model; +RPF-1.3-C1 and RPF-1.3-T4 in the frozen owner review. The scaffold's historical +audience field placement differs from the issuer draft; this candidate uses +the issuer draft's serviceAccountRef.audiences, not a claim of installed support. + +## Prepared delivery mechanics (still unavailable as a production lane) + +`scripts/keycape_issuer_delivery.py` now contains concrete command mechanics for +the existing RPF-1.3-T4 obligation. It reuses `keycape_issuer_eso.documents()` and +`require_fixed_store()` with the byte-bounded `bounded_public_command.command()` +transport. Only the fixed public ESO resources are read using raw v1 paths. +A successful field-selected collection response proves observed absence. +The helper creates only the ExternalSecret, compares its fixed spec with only +the three pinned defaults normalized, and polls at most ten times for a recent +current-metadata controller report with stable UID. Request, command and total +attempt deadlines apply as described below. Controller resourceVersion updates +during successful readiness are captured. + +Removal is permitted only for that instance's verified case-created revision, +rechecks spec/UID/resourceVersion, uses Kubernetes DeleteOptions with both UID +and resourceVersion preconditions, and confirms absence with bounded polling. +Existing matching objects return changed=false and are never rollback candidates. +Creation response loss, readiness failure after creation, replacement, delete +conflict or unconfirmed deletion raises a fixed uncertainty diagnostic and keeps +the last verified revision when known. No retry adopts an uncertain create; no +replacement is deleted. An unknown create outcome requires owner reconciliation +using the fixed resource name even when no UID could be recovered. Target Secret +garbage collection by ESO/Kubernetes is outside this helper; it never reads or +directly deletes Secret objects. There is no claim that deletion proves target +Secret disappearance. + +The callable `production_bindings()` **always refuses before any command**. +The private mechanics use empty environment and `/dev/null` kubeconfig and +therefore cannot borrow the caller's native session. They are source preparation, +not an adapter/evidence authority. Mechanics tests intercept command dispatch; +runner tests launch only synthetic local children. Neither provides production +callbacks or attestations. A real lane requires +an owner-reviewed authenticated human aal2 session, acknowledged case revision +and caller binding, scoped Kubernetes credentials, one-shot durable admission +journaling and authenticated evidence publication/verification, plus real +per-attempt session revocation and recovery. None exists for this lane at the +pinned source. The generic task_execution_journal module is real storage but +explicitly does not admit native work; it is not silently repurposed here. +The mint broker's case checker and OpenBao lookup-self are real for its existing +mint lane, not delivery authentication. The attended custody-projection SSH +transport and credential-change evidence writer likewise confer no delivery +lane authority. No unsigned receipt or `True` oracle replaces these facilities. + +Consequently RPF-1.3-T4 remains open for real capability binding and owner review; +this patch prepares concrete ESO command/state mechanics and regressions. +No ESO role, policy, Store, admission service, grant, catalog acceptance, native +permission or owner descriptor acceptance is created or inferred. + + +## r2 source mechanics: current controller report and containment + +This preparation is not production admission or owner acceptance. The factory +continues to refuse absent actual case/client authorization, scoped caller and +session, owner exclusion/admission, durable admission/recovery, authenticated +evidence and per-attempt revocation. Empty transport environment and /dev/null +kubeconfig deliberately remain unusable for ambient production access. + +The pinned ESO v0.16.1 controller (commit +8d037d197c88eeb9c4fe489e906c9cb3ab9ee6af) sets syncedResourceVersion to +its metadata generation plus '-' plus the MD5 of its Go-formatted annotations +and labels struct. That token is not Kubernetes resourceVersion or authenticated +evidence. r2 compares the exact token, a unique Ready=True/SecretSynced condition +with the controller's successful 'secret synced' message, and a valid refreshTime +no older than the fixed five-minute refresh interval and not in the future. +The controller sets refreshTime to reconcile start. Its Retain/missing-provider +branch also sets Ready but uses a different message; r2 refuses that branch. +These checks prove a recent controller report for current object metadata under +the pinned implementation, not present Secret contents, provider availability, +a live credential outcome or runtime authentication. Deployed-version matching, +trusted API/status custody and a sane local clock remain production prerequisites. + +Store status has no generation binding. r2 accepts only generation 1 with the +fixed spec and unique Valid/'store validated' Ready condition, and records its +UID/resourceVersion. A later generation is refused with +store_ready_generation_unverifiable. Repeated exact Store observations before +create, during readiness and before a receipt detect observed drift; they cannot +atomically bind create or ESO's Store lookup to a Store revision. A wholly +interleaved change/restoration remains invisible. Actual owner-controlled +exclusion/admission is missing and remains a production blocker. Cleanup of a +verified ExternalSecret uses its own UID/RV preconditions and does not depend on +Store availability. + +All reads use explicit raw v1 paths. The fixed ExternalSecret collection query +includes an exact metadata.name field selector and limit 2 to obtain structured +successful absence. Its response must be a v1 ExternalSecretList with at most +one fixed namespace/name item and no continuation. Errors are never absence. +This needs field-selected ExternalSecret list access in any future real session; +no such access is granted by this code. No Kubernetes Secret is read or listed. + +The new POSIX runner bounds combined stdout+stderr during collection to 262144 +bytes, discards stderr, uses a six-second maximum command deadline, sends input +without a pipe deadlock, and kills the process group before reaping its leader +on success or failure. Wait/reap has a separate one-second cleanup budget; +unconfirmed cleanup is refusal. Operations have a 30-second monotonic deadline +and cannot return success once it has elapsed. Descendant tests verify ordinary +same-group children with held and closed pipes. This is process-group cleanup, +not a sandbox guarantee against a program escaping its group/session. Privileged +runtime isolation is not established by this runner or its tests. + +The three actual v1 remoteRef defaults (conversionStrategy=Default, +decodingStrategy=None, metadataPolicy=None) compare equally when omitted or +explicit. Other spec changes remain conflicts. The metadata-only contract helper +is not itself the runtime freshness gate; r2 delivery adds the checks above. +The fixed template bytes remain unchanged. Unknown mutation responses preserve +pending reconciliation, including any valid returned UID/RV, and never gain a +blind delete/retry path. Local one-shot state remains non-durable. diff --git a/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/prepared-source/scripts/bounded_public_command.py b/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/prepared-source/scripts/bounded_public_command.py new file mode 100644 index 00000000..68f10973 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/prepared-source/scripts/bounded_public_command.py @@ -0,0 +1,107 @@ +"""POSIX byte/time bounded subprocess collection; no credential/session authority.""" +from __future__ import annotations + +import json +import os +import selectors +import signal +import subprocess +import time + +MAX_OUTPUT = 262144 # combined stdout + stderr; stderr is counted, never retained +MAX_INPUT = 65536 +CLEANUP_TIMEOUT = 1 + + +class CommandRefused(Exception): + """Only fixed diagnostics escape this transport.""" + + +def command(argv, *, payload=None, timeout=6): + if not isinstance(timeout, (float, int)) or not 0 < timeout <= 6: + raise CommandRefused('command_budget_invalid') + try: + body = b'' if payload is None else json.dumps(payload).encode() + if len(body) > MAX_INPUT: + raise CommandRefused('command_input_bound') + except (ValueError, TypeError): + raise CommandRefused('command_input_invalid') from None + process = None + selector = selectors.DefaultSelector() + output = bytearray() + total = 0 + offset = 0 + failure = None + code = None + deadline = time.monotonic() + timeout + try: + process = subprocess.Popen(argv, stdin=subprocess.PIPE, + stdout=subprocess.PIPE, stderr=subprocess.PIPE, env={}, + start_new_session=True, close_fds=True, shell=False) + for stream in (process.stdout, process.stderr, process.stdin): + os.set_blocking(stream.fileno(), False) + selector.register(process.stdout, selectors.EVENT_READ, 'stdout') + selector.register(process.stderr, selectors.EVENT_READ, 'stderr') + if body: + selector.register(process.stdin, selectors.EVENT_WRITE, 'stdin') + else: + process.stdin.close() + while True: + remaining = deadline - time.monotonic() + if remaining <= 0: + raise CommandRefused('command_timeout') + # WNOWAIT deliberately leaves the leader unreaped so its PID/PGID + # cannot be recycled before final process-group cleanup. + state = os.waitid(os.P_PID, process.pid, + os.WEXITED | os.WNOHANG | os.WNOWAIT) + if state is not None and not selector.get_map(): + break + for key, _ in selector.select(min(remaining, .05)): + stream = key.fileobj + if key.data == 'stdin': + try: + offset += os.write(stream.fileno(), body[offset:offset + 4096]) + except BlockingIOError: + continue + if offset == len(body): + selector.unregister(stream); stream.close() + continue + try: + block = os.read(stream.fileno(), 4096) + except BlockingIOError: + continue + if not block: + selector.unregister(stream); stream.close() + continue + total += len(block) + if total > MAX_OUTPUT: + raise CommandRefused('command_output_bound') + if key.data == 'stdout': + output.extend(block) + except CommandRefused as error: + failure = str(error) + except Exception: + failure = 'command_failed' + finally: + selector.close() + if process is not None: + # Also kills descendants that kept pipes open or closed all pipes. + # The leader has not been reaped, avoiding PGID reuse races. + try: + os.killpg(process.pid, signal.SIGKILL) + except ProcessLookupError: + pass + except Exception: + failure = 'command_cleanup_unconfirmed' + for stream in (process.stdin, process.stdout, process.stderr): + if stream is not None: + stream.close() + try: + code = process.wait(timeout=CLEANUP_TIMEOUT) + except Exception: + failure = 'command_cleanup_unconfirmed' + if failure is not None: + raise CommandRefused(failure) from None + if code != 0: + raise CommandRefused('command_failed') from None + return bytes(output) diff --git a/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/prepared-source/scripts/keycape_issuer_delivery.py b/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/prepared-source/scripts/keycape_issuer_delivery.py new file mode 100644 index 00000000..66384d6d --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/prepared-source/scripts/keycape_issuer_delivery.py @@ -0,0 +1,284 @@ +"""RPF-1.3-T4 source mechanics; production admission deliberately unavailable.""" +from __future__ import annotations + +from copy import deepcopy +from datetime import datetime, timezone +import hashlib +import json +import re +import time + +from keycape_issuer_eso import documents, require_fixed_store, Refused, canonical_external_spec +from bounded_public_command import command, CommandRefused + +POLL_COUNT = 10 +POLL_DELAY = 1 +ATTEMPT_TIMEOUT = 30 +API = '/apis/external-secrets.io/v1' + + +class Uncertain(Refused): + def __init__(self, reason, revision=None): + super().__init__(reason) + self.revision = deepcopy(revision) + + +def production_bindings(*args, **kwargs): + raise Refused('delivery_admission_exclusion_session_journal_evidence_revocation_unavailable') + + +def _metadata(obj): + if not isinstance(obj, dict) or not isinstance(obj.get('metadata'), dict): + raise Refused('public_metadata_invalid') + return obj['metadata'] + + +def _revision(obj): + meta = _metadata(obj) + result = {'uid': meta.get('uid'), 'resource_version': meta.get('resourceVersion')} + if any(not isinstance(x, str) or not re.fullmatch(r'[A-Za-z0-9._:-]{1,192}', x) + for x in result.values()): + raise Refused('public_revision_invalid') + return result + + +def _generation(obj): + value = _metadata(obj).get('generation') + if type(value) is not int or not 1 <= value <= 2**63 - 1: + raise Refused('public_generation_invalid') + return value + + +def _synced_version(obj): + """ESO v0.16.1 util.GetResourceVersion + utils.ObjectHash, not metadata RV. + + Go fmt %+v on the actual two-field struct uses sorted string maps, raw + keys/values, and map[] for both nil and empty maps. Tests compare this + implementation to a standalone Go extraction of that exact upstream code. + This MD5 is the controller's revision token, never authentication evidence. + """ + meta = _metadata(obj) + maps = [] + for name in ('annotations', 'labels'): + value = meta.get(name) + if value is None: + value = {} + if not isinstance(value, dict) or any( + not isinstance(k, str) or not isinstance(v, str) for k, v in value.items()): + raise Refused('public_metadata_map_invalid') + try: + textual = 'map[' + ' '.join(k + ':' + value[k] for k in sorted(value)) + ']' + textual.encode('utf-8', errors='strict') + except UnicodeError: + raise Refused('public_metadata_map_invalid') from None + maps.append(name + ':' + textual) + hashed = hashlib.md5(('{' + ' '.join(maps) + '}').encode(), usedforsecurity=False).hexdigest() + return str(_generation(obj)) + '-' + hashed + + +def _ready(obj): + status = obj.get('status') + if not isinstance(status, dict): + return False + conditions = status.get('conditions') + if not isinstance(conditions, list) or any(not isinstance(c, dict) for c in conditions): + return False + ready = [c for c in conditions if c.get('type') == 'Ready'] + if len(ready) != 1 or any(ready[0].get(k) != v for k, v in { + 'status': 'True', 'reason': 'SecretSynced', 'message': 'secret synced'}.items()): + return False + if status.get('syncedResourceVersion') != _synced_version(obj): + return False + stamp = status.get('refreshTime') + if not isinstance(stamp, str) or not re.fullmatch( + r'\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d(?:\.\d{1,9})?(?:Z|[+-]\d\d:\d\d)', stamp): + return False + try: + refreshed = datetime.fromisoformat(stamp.replace('Z', '+00:00')) + age = (datetime.now(timezone.utc) - refreshed).total_seconds() + return 0 <= age <= 300 + except (ValueError, OverflowError): + return False + + +class _DeliveryMechanics: + """No authority boundary: injected calls in tests, unusable ambient transport. + + Store observations detect drift; they cannot exclude an ABA race between + observations or bind ESO's store lookup atomically. Production needs real + owner exclusion/admission in addition to authenticated caller facilities. + """ + def __init__(self, client_id): + self.client_id = client_id + self.store, self.external = documents(client_id) + self.created = None + self.pending = False + self.used = False + self.deadline = None + + def _call(self, args, payload=None): + try: + remaining = self.deadline - time.monotonic() + if remaining <= 0: + raise Refused('attempt_deadline') + raw = command(['kubectl', '--kubeconfig=/dev/null', + '--request-timeout=5s', *args], + payload=payload, timeout=min(6, remaining)) + if time.monotonic() >= self.deadline: + raise Refused('attempt_deadline') + # Runner enforces the same bound during collection, not just here. + if not isinstance(raw, bytes) or len(raw) > 262144: + raise Refused('public_response_bound') + if not raw.strip(): + return None + value = json.loads(raw) + if not isinstance(value, dict): + raise Refused('public_response_shape') + return value + except Refused: + raise + except Exception: + raise Refused('public_command_failed') from None + + def _get(self, store=False): + if store: + return self._call(['get', '--raw', API + '/clustersecretstores/' + self.store['metadata']['name']]) + # Raw GET fixes API version and avoids kubectl mapper fallback. A fixed + # field-selected collection provides structured successful absence; + # stderr/nonzero is never interpreted as NotFound. No Secret is listed. + uri = (API + '/namespaces/sso/externalsecrets?fieldSelector=metadata.name%3D' + + self.external['metadata']['name'] + '&limit=2') + listing = self._call(['get', '--raw', uri]) + if (not isinstance(listing, dict) or listing.get('apiVersion') != 'external-secrets.io/v1' + or listing.get('kind') != 'ExternalSecretList' + or not isinstance(listing.get('metadata'), dict) + or listing['metadata'].get('continue') not in (None, '') + or not isinstance(listing.get('items'), list) or len(listing['items']) > 1): + raise Refused('public_collection_invalid') + if not listing['items']: + return None + value = listing['items'][0] + meta = _metadata(value) + if (value.get('apiVersion') != 'external-secrets.io/v1' + or value.get('kind') != 'ExternalSecret' + or meta.get('name') != self.external['metadata']['name'] + or meta.get('namespace') != 'sso'): + raise Refused('public_collection_identity_invalid') + return value + + def _store_revision(self, sealed=None): + store = self._get(store=True) + require_fixed_store(store, self.client_id) + if _metadata(store).get('deletionTimestamp'): + raise Refused('store_terminating') + revision = _revision(store) + # Actual Store status has no generation binding. Generation 1 excludes + # a prior spec update under Kubernetes generation semantics. For any + # later generation Ready cannot establish current-spec validation. + if _generation(store) != 1: + raise Refused('store_ready_generation_unverifiable') + conditions = store.get('status', {}).get('conditions', []) + ready = [c for c in conditions if isinstance(c, dict) and c.get('type') == 'Ready'] + if len(ready) != 1 or ready[0].get('reason') != 'Valid' or ready[0].get('message') != 'store validated': + raise Refused('store_validation_unverified') + if sealed is not None and sealed != revision: + raise Refused('store_revision_changed') + return revision + + def _match(self, obj): + metadata = _metadata(obj) + if (obj.get('apiVersion') != self.external['apiVersion'] + or obj.get('kind') != 'ExternalSecret' + or metadata.get('name') != self.external['metadata']['name'] + or metadata.get('namespace') != 'sso' or metadata.get('deletionTimestamp') + or canonical_external_spec(obj.get('spec')) != canonical_external_spec(self.external['spec'])): + raise Refused('delivery_conflict') + _generation(obj) + return _revision(obj) + + def _receipt(self, operation, before, after): + return {'schema': 'netkingdom.deliver-secret.v1', 'operation': operation, + 'namespace': 'sso', 'name': self.external['metadata']['name'], + 'custody_reference': 'platform/workloads/keycape/' + self.client_id, + 'key_names': ['client-secret'], 'values_emitted': False, + 'changed': before is None if operation == 'deliver' else True, + 'before': before, 'after': after} + + def deliver(self): + if self.used or self.pending or self.created is not None: + raise Refused('attempt_already_used_or_unreconciled') + self.used = True # local one-shot only, never a durable admission journal + self.deadline = time.monotonic() + ATTEMPT_TIMEOUT + sealed_store = self._store_revision() + old = self._get() + before = self._match(old) if old is not None else None + uid = before['uid'] if before else None + self._store_revision(sealed_store) + if old is None: + self.pending = True + try: + created = self._call(['-n', 'sso', 'create', '-f', '-', '-o', 'json'], self.external) + # Retain response identity even if admission changed its spec. + # Pending prevents deletion until exact verified delivery. + self.created = _revision(created) + self._match(created) + uid = self.created['uid'] + except Exception: + raise Uncertain('create_outcome_unverified', self.created) from None + try: + for index in range(POLL_COUNT): + self._store_revision(sealed_store) + current = self._get() + after = self._match(current) + if after['uid'] != uid: + raise Refused('delivery_replaced') + if _ready(current): + self._store_revision(sealed_store) + if self.created is not None: + self.created = after + self.pending = False + return self._receipt('deliver', before, after) + if index + 1 < POLL_COUNT: + remaining = self.deadline - time.monotonic() + if remaining <= 0: + raise Refused('attempt_deadline') + time.sleep(min(POLL_DELAY, remaining)) + raise Refused('delivery_current_ready_timeout') + except Exception: + if self.pending or self.created is not None: + raise Uncertain('created_delivery_requires_reconciliation', self.created) from None + raise Refused('delivery_verification_failed') from None + + def remove(self, revision): + if self.pending or self.created is None or revision != self.created: + raise Refused('no_verified_case_creation') + self.deadline = time.monotonic() + ATTEMPT_TIMEOUT + try: + if self._match(self._get()) != self.created: + raise Refused('created_revision_changed') + except Exception: + raise Uncertain('created_revision_changed', self.created) from None + uri = API + '/namespaces/sso/externalsecrets/' + self.external['metadata']['name'] + options = {'apiVersion': 'v1', 'kind': 'DeleteOptions', + 'preconditions': {'uid': self.created['uid'], + 'resourceVersion': self.created['resource_version']}} + prior = deepcopy(self.created) + self.pending = True + try: + self._call(['delete', '--raw', uri, '-f', '-'], options) + for index in range(POLL_COUNT): + current = self._get() + if current is None: + self.created = None + self.pending = False + return self._receipt('remove', prior, None) + if _revision(current)['uid'] != prior['uid']: + raise Refused('replacement_preserved') + if index + 1 < POLL_COUNT: + remaining = self.deadline - time.monotonic() + if remaining <= 0: + raise Refused('attempt_deadline') + time.sleep(min(POLL_DELAY, remaining)) + raise Refused('delete_unconfirmed') + except Exception: + raise Uncertain('cleanup_requires_reconciliation', prior) from None diff --git a/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/prepared-source/scripts/keycape_issuer_eso.py b/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/prepared-source/scripts/keycape_issuer_eso.py new file mode 100644 index 00000000..08cd23e5 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/prepared-source/scripts/keycape_issuer_eso.py @@ -0,0 +1,90 @@ +"""Proposed fixed issuer ESO source contract for RPF-1.3-T4 / NK48-R5-F2. + +Pure public metadata only. No API/credential/Secret access or apply path. This +new declaration requires independent Platform review before type integration. +The delivery lane may use only external_secret(); store provisioning is a +separate owner action and is never delegated by this module. +""" +from __future__ import annotations + +from copy import deepcopy +from pathlib import Path +import re + +import yaml + +ROOT = Path(__file__).resolve().parents[1] +TEMPLATE = ROOT / 'manifests/templates/keycape-issuer-eso.yaml' + + +class Refused(ValueError): + """Fixed issuer metadata differs; never return an owner delivery receipt.""" + + +def documents(client_id: str) -> tuple[dict, dict]: + # Match the frozen type client_id pattern and existing adapter's derived bound. + # No caller can supply a store name/kind/namespace or an OpenBao path. + if not isinstance(client_id, str) or not re.fullmatch(r'[a-z0-9][a-z0-9-]{1,62}', client_id): + raise Refused('bounded admitted client_id required') + if len('keycape-' + client_id + '-client') > 63: + raise Refused('derived issuer reference exceeds existing adapter bound') + store, external = yaml.safe_load_all(TEMPLATE.read_text().format(client_id=client_id)) + return store, external + + +def external_secret(client_id: str) -> dict: + """The ONLY document the proposed delivery capability may apply.""" + return deepcopy(documents(client_id)[1]) + + +def canonical_external_spec(spec): + """Normalize only the three pinned ESO v0.16.1 remoteRef defaults. + + Omitted and explicit defaults are equivalent; all other fields remain exact. + This says nothing about freshness, admission authority or Store continuity. + """ + value = deepcopy(spec) + if isinstance(value, dict) and isinstance(value.get('data'), list): + for item in value['data']: + if isinstance(item, dict) and isinstance(item.get('remoteRef'), dict): + for name, default in (('conversionStrategy', 'Default'), + ('decodingStrategy', 'None'), + ('metadataPolicy', 'None')): + item['remoteRef'].setdefault(name, default) + return value + + +def _matching_public_resource(observed: dict, expected: dict) -> None: + if not isinstance(observed, dict): + raise Refused('public resource observation required') + metadata = observed.get('metadata') + if not isinstance(metadata, dict): + raise Refused('resource metadata required') + expected_namespace = expected['metadata'].get('namespace') + namespace_matches = (metadata.get('namespace') == expected_namespace + if expected_namespace else metadata.get('namespace') in (None, '')) + if (observed.get('apiVersion') != expected['apiVersion'] + or observed.get('kind') != expected['kind'] + or metadata.get('name') != expected['metadata']['name'] + or not namespace_matches + or (canonical_external_spec(observed.get('spec')) + != canonical_external_spec(expected['spec']) + if expected['kind'] == 'ExternalSecret' + else observed.get('spec') != expected['spec'])): + raise Refused('fixed issuer resource differs') + status = observed.get('status') + conditions = status.get('conditions', []) if isinstance(status, dict) else [] + if not isinstance(conditions, list) or not any( + isinstance(c, dict) and c.get('type') == 'Ready' and c.get('status') == 'True' + for c in conditions): + raise Refused('fixed issuer resource not ready') + + +def require_fixed_store(observed: dict, client_id: str) -> None: + """Refuse a missing/drifted/not-ready store; never create it through delivery.""" + _matching_public_resource(observed, documents(client_id)[0]) + + +def require_ready_delivery(observed: dict, client_id: str) -> None: + """Spec equality and Ready precede a created/NO_CHANGE owner receipt.""" + _matching_public_resource(observed, documents(client_id)[1]) diff --git a/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/prepared-source/tests/eso-hash-vectors.json b/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/prepared-source/tests/eso-hash-vectors.json new file mode 100644 index 00000000..73b23248 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/prepared-source/tests/eso-hash-vectors.json @@ -0,0 +1,57 @@ +[ + { + "metadata": { + "generation": 1 + }, + "expected": "1-2f2c636d06132f67992c15a1daeb0a5f" + }, + { + "metadata": { + "generation": 2, + "annotations": {}, + "labels": {} + }, + "expected": "2-2f2c636d06132f67992c15a1daeb0a5f" + }, + { + "metadata": { + "generation": 3, + "annotations": { + "b": "2", + "a": "1" + }, + "labels": { + "z": "last", + "c": "first" + } + }, + "expected": "3-7ad4f86f5aba928cde01913c7b9d962a" + }, + { + "metadata": { + "generation": 4, + "annotations": { + "a:b": "c d", + "a b": "[]:%+v" + }, + "labels": { + "newline\n": "tab\t" + } + }, + "expected": "4-48e35b520c38b2944d461645440cc3cd" + }, + { + "metadata": { + "generation": 9223372036854775807, + "annotations": { + "é": "好", + "🦊": "😀" + }, + "labels": { + "": "", + "null": "\u0000" + } + }, + "expected": "9223372036854775807-da6b2f7c16875cb96b3a00d83378eda3" + } +] diff --git a/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/prepared-source/tests/test_bounded_public_command.py b/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/prepared-source/tests/test_bounded_public_command.py new file mode 100644 index 00000000..21d6a52c --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/prepared-source/tests/test_bounded_public_command.py @@ -0,0 +1,95 @@ +import os +from pathlib import Path +import sys +import tempfile +import time +import unittest +from unittest.mock import patch + +sys.path.insert(0, str(Path(__file__).resolve().parents[1] / 'scripts')) +import bounded_public_command as runner + +# Every launched fixture denies socket creation in its own interpreter as well +# as the parent. They only perform controlled local stdout/stderr/process work. +PREFIX = "import socket; socket.socket=lambda *a,**k: (_ for _ in ()).throw(AssertionError('network forbidden')); " + + +class BoundedCommand(unittest.TestCase): + def setUp(self): + p = patch.dict(os.environ, {}, clear=True); p.start(); self.addCleanup(p.stop) + p = patch('socket.socket', side_effect=AssertionError('network forbidden')) + p.start(); self.addCleanup(p.stop) + + def call(self, code, timeout=2, payload=None): + return runner.command(['/usr/bin/python3', '-c', PREFIX + code], timeout=timeout, payload=payload) + + def test_empty_environment_and_input_roundtrip(self): + raw = self.call("import os,sys,json; b=sys.stdin.buffer.read(); print(json.dumps({'keys':sorted(os.environ),'body':json.loads(b)}))", payload={'fixed': 'metadata'}) + import json + response = json.loads(raw) + # CPython may inject LC_CTYPE during startup even though exec env={}; + # neither HOME nor credential variables are inherited. + self.assertFalse(set(response['keys']) - {'LC_CTYPE'}) + self.assertEqual(response['body'], {'fixed': 'metadata'}) + + def test_stdout_flood_stopped_with_fixed_error(self): + with self.assertRaisesRegex(runner.CommandRefused, '^command_output_bound$'): + self.call("import os; [os.write(1,b'PRIVATE'*4096) for _ in range(1000)]") + + def test_stderr_flood_stopped_with_fixed_error(self): + with self.assertRaisesRegex(runner.CommandRefused, '^command_output_bound$'): + self.call("import os; [os.write(2,b'PRIVATE'*4096) for _ in range(1000)]") + + def test_combined_cap_not_independent_per_stream(self): + with self.assertRaisesRegex(runner.CommandRefused, '^command_output_bound$'): + self.call("import os; os.write(1,b'x'*140000); os.write(2,b'y'*140000)") + + def test_error_output_never_escapes(self): + with self.assertRaisesRegex(runner.CommandRefused, '^command_failed$') as error: + self.call("import sys; print('PRIVATE'); print('PRIVATE',file=sys.stderr); sys.exit(7)") + self.assertNotIn('PRIVATE', str(error.exception)) + + def test_sleep_timeout_is_real_and_bounded(self): + start = time.monotonic() + with self.assertRaisesRegex(runner.CommandRefused, '^command_timeout$'): + self.call('import time; time.sleep(60)', timeout=.15) + self.assertLess(time.monotonic() - start, 2) + + def test_descendant_holding_pipes_is_killed(self): + with tempfile.TemporaryDirectory(prefix='delivery-runner-') as scratch: + pidfile = Path(scratch) / 'descendant' + code = ("import os,time; pid=os.fork(); " + "(time.sleep(60) if pid==0 else " + f"(open({str(pidfile)!r},'w').write(str(pid)),os._exit(0)))") + with self.assertRaisesRegex(runner.CommandRefused, '^command_timeout$'): + self.call(code, timeout=.2) + self.assertTrue(pidfile.exists()) + pid = pidfile.read_text() + status = Path('/proc') / pid / 'stat' + for _ in range(20): + if not status.exists() or status.read_text().split()[2] == 'Z': break + time.sleep(.01) + if status.exists(): self.assertEqual(status.read_text().split()[2], 'Z') + + def test_descendant_with_closed_pipes_is_killed_on_success(self): + with tempfile.TemporaryDirectory(prefix='delivery-runner-') as scratch: + pidfile = Path(scratch) / 'descendant' + code = ("import os,time; pid=os.fork(); " + "(os.close(0),os.close(1),os.close(2),time.sleep(60)) if pid==0 else " + f"(open({str(pidfile)!r},'w').write(str(pid)),print('ok'))") + self.assertEqual(self.call(code), b'ok\n') + status = Path('/proc') / pidfile.read_text() / 'stat' + # Kill delivery may race the proc-state observation; wait boundedly. + for _ in range(20): + if not status.exists() or status.read_text().split()[2] == 'Z': break + time.sleep(.01) + if status.exists(): self.assertEqual(status.read_text().split()[2], 'Z') + + def test_input_bound_and_spawn_failure_fixed(self): + with self.assertRaisesRegex(runner.CommandRefused, '^command_input_bound$'): + self.call('pass', payload={'data': 'x'*65536}) + with self.assertRaisesRegex(runner.CommandRefused, '^command_failed$'): + runner.command(['/nonexistent-fixture-binary'], timeout=.1) + + +if __name__ == '__main__': unittest.main() diff --git a/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/prepared-source/tests/test_keycape_issuer_delivery.py b/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/prepared-source/tests/test_keycape_issuer_delivery.py new file mode 100644 index 00000000..f24846aa --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/prepared-source/tests/test_keycape_issuer_delivery.py @@ -0,0 +1,284 @@ +from copy import deepcopy +from datetime import datetime, timedelta, timezone +import json +import os +from pathlib import Path +import sys +import unittest +from unittest.mock import patch + +sys.path.insert(0, str(Path(__file__).resolve().parents[1] / 'scripts')) +import keycape_issuer_delivery as lane +from bounded_public_command import CommandRefused + + +class Mechanics(unittest.TestCase): + def setUp(self): + p = patch.dict(os.environ, {}, clear=True); p.start(); self.addCleanup(p.stop) + p = patch('socket.socket', side_effect=AssertionError('network forbidden')) + p.start(); self.addCleanup(p.stop) + self.m = lane._DeliveryMechanics('forgejo') + self.store = self.resource(self.m.store, store=True) + self.obj = None + self.calls = [] + self.store_reads = 0 + self.hook = lambda args: None + self.fail_create = False + self.fail_delete = False + self.create_mutate = lambda obj: obj + self.force_list = None + self.poll_object = None + self.clock = 0 + self.clock_step = 0 + p = patch.object(lane, 'command', side_effect=self.command) + p.start(); self.addCleanup(p.stop) + p = patch.object(lane.time, 'sleep'); p.start(); self.addCleanup(p.stop) + p = patch.object(lane.time, 'monotonic', side_effect=lambda: self.clock) + p.start(); self.addCleanup(p.stop) + + def resource(self, doc, store=False): + value = deepcopy(doc) + value['metadata'].update(uid='store-uid' if store else 'created-uid', resourceVersion='10', generation=1) + if store: + value['status'] = {'conditions': [{'type': 'Ready', 'status': 'True', + 'reason': 'Valid', 'message': 'store validated'}]} + else: + value['spec'] = lane.canonical_external_spec(value['spec']) + value['status'] = {'conditions': [{'type': 'Ready', 'status': 'True', + 'reason': 'SecretSynced', 'message': 'secret synced'}], + 'syncedResourceVersion': lane._synced_version(value), + 'refreshTime': datetime.now(timezone.utc).isoformat()} + return value + + def command(self, args, *, payload=None, timeout=6): + self.assertEqual(args[:3], ['kubectl', '--kubeconfig=/dev/null', '--request-timeout=5s']) + self.assertGreater(timeout, 0); self.assertLessEqual(timeout, 6) + self.calls.append((deepcopy(args), deepcopy(payload))) + self.clock += self.clock_step + self.hook(args) + if 'create' in args: + if self.fail_create: + raise CommandRefused('PRIVATE PROVIDER VALUE') + self.obj = self.create_mutate(self.resource(self.m.external)) + value = self.obj + elif 'delete' in args: + if self.fail_delete: + raise CommandRefused('PRIVATE PROVIDER VALUE') + revision = lane._revision(self.obj) + if payload['preconditions'] != {'uid': revision['uid'], 'resourceVersion': revision['resource_version']}: + raise CommandRefused('conditional conflict fixture') + self.obj = None + value = {'kind': 'Status', 'status': 'Success'} + elif '/clustersecretstores/' in args[-1]: + self.store_reads += 1 + value = self.store + else: + self.assertIn('get', args); self.assertIn('--raw', args) + self.assertEqual(args[-1], lane.API + '/namespaces/sso/externalsecrets?fieldSelector=metadata.name%3Dkeycape-forgejo-client&limit=2') + value = self.force_list if self.force_list is not None else { + 'apiVersion': 'external-secrets.io/v1', 'kind': 'ExternalSecretList', + 'metadata': {}, 'items': [] if self.obj is None else [self.poll_object or self.obj]} + return json.dumps(deepcopy(value)).encode() + + def test_actual_defaults_create_current_ready_and_conditional_delete(self): + delivered = self.m.deliver() + self.assertTrue(delivered['changed']); self.assertIsNone(delivered['before']) + self.assertGreaterEqual(self.store_reads, 4) + removed = self.m.remove(delivered['after']) + self.assertEqual(removed['after'], None) + args, payload = next(c for c in self.calls if 'delete' in c[0]) + self.assertEqual(args[5], lane.API + '/namespaces/sso/externalsecrets/keycape-forgejo-client') + self.assertEqual(payload['preconditions'], {'uid': 'created-uid', 'resourceVersion': '10'}) + + def test_existing_no_change_cannot_be_deleted_or_reused(self): + self.obj = self.resource(self.m.external) + delivered = self.m.deliver() + self.assertFalse(delivered['changed']) + with self.assertRaises(lane.Refused): self.m.remove(delivered['after']) + with self.assertRaises(lane.Refused): self.m.deliver() + self.assertFalse(any('create' in args or 'delete' in args for args, _ in self.calls)) + + def test_stale_current_spec_and_metadata_hash_never_ready(self): + obj = self.resource(self.m.external) + for field, value in [('generation', 2), ('labels', {'new': 'metadata'}), ('annotations', {'new': 'metadata'})]: + with self.subTest(field=field): + changed = deepcopy(obj); changed['metadata'][field] = value + self.assertFalse(lane._ready(changed)) + + def test_exact_go_upstream_revision_vectors(self): + vectors = json.loads((Path(__file__).parent / 'eso-hash-vectors.json').read_text()) + for vector in vectors: + self.assertEqual(lane._synced_version({'metadata': vector['metadata']}), vector['expected']) + + def test_wrong_default_strategies_and_identity_never_match(self): + for field, value in [('conversionStrategy', 'Unicode'), ('decodingStrategy', 'Base64'), + ('metadataPolicy', 'Fetch'), ('key', 'elsewhere'), ('extra', 'value')]: + obj = self.resource(self.m.external) + obj['spec']['data'][0]['remoteRef'][field] = value + with self.assertRaises(lane.Refused): self.m._match(obj) + for field, value in [('namespace', 'default'), ('name', 'other'), ('generation', True)]: + obj = self.resource(self.m.external); obj['metadata'][field] = value + with self.assertRaises(lane.Refused): self.m._match(obj) + + def test_invalid_metadata_maps_never_produce_hash(self): + for value in ([], 'str', {'key': None}, {'key': '\ud800'}): + obj = self.resource(self.m.external); obj['metadata']['labels'] = value + with self.assertRaises(lane.Refused): lane._synced_version(obj) + + def test_client_override_injection_refused_before_command(self): + for client in ('x/../../secret', 'x;echo', 'A', None, 'a'*49): + with self.assertRaises(lane.Refused): lane._DeliveryMechanics(client) + self.assertEqual(self.calls, []) + + def test_retained_missing_stale_future_duplicate_status_not_ready(self): + for variant in ('retained', 'missing', 'old', 'future', 'duplicate', 'invalid_time'): + with self.subTest(variant=variant): + obj = self.resource(self.m.external) + if variant == 'retained': obj['status']['conditions'][0]['message'] = 'secret retained due to DeletionPolicy=Retain' + if variant == 'missing': obj['status'].pop('syncedResourceVersion') + if variant in ('old', 'future'): + delta = -301 if variant == 'old' else 60 + obj['status']['refreshTime'] = (datetime.now(timezone.utc) + timedelta(seconds=delta)).isoformat() + if variant == 'duplicate': obj['status']['conditions'] *= 2 + if variant == 'invalid_time': obj['status']['refreshTime'] = 'PRIVATE MALFORMED' + self.assertFalse(lane._ready(obj)) + + def test_stale_delivery_times_out_with_reconciliation_provenance(self): + self.create_mutate = lambda obj: {**obj, 'status': {'conditions': [{'type': 'Ready', 'status': 'True'}]}} + with self.assertRaises(lane.Uncertain) as error: self.m.deliver() + self.assertEqual(error.exception.revision['uid'], 'created-uid') + with self.assertRaises(lane.Refused): self.m.remove(error.exception.revision) + self.assertLessEqual(len(self.calls), 2 * lane.POLL_COUNT + 4) + + def test_store_prior_generation_refused_before_create(self): + self.store['metadata']['generation'] = 2 + with self.assertRaisesRegex(lane.Refused, 'generation_unverifiable'): self.m.deliver() + self.assertEqual(len(self.calls), 1) + + def test_store_replaced_before_precreate_read_prevents_create(self): + def hook(args): + if '/externalsecrets?' in args[-1]: self.store['metadata']['uid'] = 'replacement' + self.hook = hook + with self.assertRaisesRegex(lane.Refused, 'store_revision_changed'): self.m.deliver() + self.assertFalse(any('create' in args for args, _ in self.calls)) + + def test_store_replaced_inside_create_window_detected_after_mutation(self): + def hook(args): + if 'create' in args: self.store['metadata']['uid'] = 'replacement' + self.hook = hook + with self.assertRaises(lane.Uncertain): self.m.deliver() + self.assertIsNotNone(self.obj) # double-read did not exclude mutation + self.assertTrue(self.m.pending) + + def test_store_aba_race_is_not_claimed_excluded(self): + # A mutation and restoration wholly between observations is invisible. + seen = [] + def hook(args): + if 'create' in args: + original = deepcopy(self.store) + self.store['metadata']['uid'] = 'transient-replacement' + seen.append(self.store['metadata']['uid']) + self.store = original + seen.append(self.store['metadata']['uid']) + self.hook = hook + self.assertTrue(self.m.deliver()['changed']) + self.assertEqual(len(seen), 2) + self.assertEqual(seen, ['transient-replacement', 'store-uid']) + + def test_malformed_create_preserves_fixed_unknown_outcome(self): + for metadata in (None, [], {}, {'uid': 'created-uid', 'resourceVersion': None}): + with self.subTest(metadata=metadata): + self.m = lane._DeliveryMechanics('forgejo'); self.obj = None + self.create_mutate = lambda obj: {**obj, 'metadata': metadata} + with self.assertRaises(lane.Uncertain) as error: self.m.deliver() + self.assertEqual(str(error.exception), 'create_outcome_unverified') + self.assertTrue(self.m.pending) + + def test_unknown_create_and_delete_never_blindly_retry(self): + self.fail_create = True + with self.assertRaises(lane.Uncertain): self.m.deliver() + with self.assertRaises(lane.Refused): self.m.deliver() + self.fail_create = False; self.m = lane._DeliveryMechanics('forgejo') + delivered = self.m.deliver(); self.fail_delete = True + with self.assertRaises(lane.Uncertain) as error: self.m.remove(delivered['after']) + self.assertNotIn('PRIVATE', str(error.exception)) + with self.assertRaises(lane.Refused): self.m.remove(delivered['after']) + + def test_delete_race_uses_server_preconditions_and_preserves_replacement(self): + delivered = self.m.deliver() + def hook(args): + if 'delete' in args: self.obj['metadata']['uid'] = 'replacement' + self.hook = hook + with self.assertRaises(lane.Uncertain): self.m.remove(delivered['after']) + self.assertEqual(self.obj['metadata']['uid'], 'replacement') + + def test_status_rv_changed_before_remove_no_delete(self): + delivered = self.m.deliver(); self.obj['metadata']['resourceVersion'] = '11' + with self.assertRaises(lane.Uncertain): self.m.remove(delivered['after']) + self.assertFalse(any('delete' in args for args, _ in self.calls)) + + def test_fresh_process_and_forged_revision_never_delete(self): + delivered = self.m.deliver() + with self.assertRaises(lane.Refused): lane._DeliveryMechanics('forgejo').remove(delivered['after']) + with self.assertRaises(lane.Refused): self.m.remove({'uid': 'fake', 'resource_version': '10'}) + self.assertFalse(any('delete' in args for args, _ in self.calls)) + + def test_poll_rv_is_captured(self): + reads = [] + def hook(args): + if '/externalsecrets?' in args[-1] and self.obj is not None: + self.obj['metadata']['resourceVersion'] = '11' + reads.append(True) + self.hook = hook + delivered = self.m.deliver() + self.assertEqual(delivered['after']['resource_version'], '11') + self.assertTrue(reads) + + def test_unconfirmed_delete_is_bounded_and_preserves_reconciliation(self): + delivered = self.m.deliver() + prior = deepcopy(self.obj) + terminating = deepcopy(prior); terminating['metadata']['deletionTimestamp'] = 'now' + with patch.object(self.m, '_get', side_effect=[prior] + [terminating]*lane.POLL_COUNT): + with self.assertRaises(lane.Uncertain): self.m.remove(delivered['after']) + self.assertTrue(self.m.pending) + with self.assertRaises(lane.Refused): self.m.remove(delivered['after']) + + def test_lost_delete_after_server_removal_is_still_uncertain(self): + delivered = self.m.deliver(); self.fail_delete = True + def hook(args): + if 'delete' in args: self.obj = None + self.hook = hook + with self.assertRaises(lane.Uncertain): self.m.remove(delivered['after']) + self.assertIsNone(self.obj) + self.assertIsNotNone(self.m.created) + self.assertTrue(self.m.pending) + + def test_collection_foreign_object_cannot_be_adopted(self): + self.obj = self.resource(self.m.external); self.obj['metadata']['name'] = 'other' + with self.assertRaisesRegex(lane.Refused, 'collection_identity'): self.m.deliver() + self.assertFalse(any('create' in args for args, _ in self.calls)) + + def test_unknown_conflicting_or_unbounded_collection_never_absence(self): + for listing in ({}, {'kind': 'Status'}, {'apiVersion': 'external-secrets.io/v1', + 'kind': 'ExternalSecretList', 'metadata': {'continue': 'more'}, 'items': []}): + with self.subTest(listing=listing): + self.m = lane._DeliveryMechanics('forgejo'); self.force_list = listing + with self.assertRaises(lane.Refused): self.m.deliver() + self.assertFalse(any('create' in args for args, _ in self.calls)) + + def test_total_attempt_deadline_blocks_late_success(self): + self.clock_step = 6 + with self.assertRaises(lane.Uncertain): self.m.deliver() + self.assertLessEqual(len(self.calls), 6) + + def test_no_secret_or_store_mutation_and_factory_refuses(self): + self.m.deliver() + for args, payload in self.calls: + self.assertFalse(set(args) & {'secret', 'secrets'}) + if payload and 'create' in args: self.assertEqual(payload['kind'], 'ExternalSecret') + before = len(self.calls) + with self.assertRaises(lane.Refused): lane.production_bindings(authenticated=True) + self.assertEqual(len(self.calls), before) + + +if __name__ == '__main__': unittest.main() diff --git a/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/r1-to-r2.patch b/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/r1-to-r2.patch new file mode 100644 index 00000000..7ab200f1 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/r1-to-r2.patch @@ -0,0 +1,1217 @@ +--- a/docs/keycape-issuer-eso-contract.md ++++ b/docs/keycape-issuer-eso-contract.md +@@ -71,13 +71,14 @@ + + `scripts/keycape_issuer_delivery.py` now contains concrete command mechanics for + the existing RPF-1.3-T4 obligation. It reuses `keycape_issuer_eso.documents()` and +-`require_fixed_store()` and the existing contained `state_hub_preflight_lane.command()` +-transport. Only exact named public ESO resources are read. It proves absence +-with successful empty `get --ignore-not-found`, creates only the ExternalSecret, +-compares an existing object's full spec, and polls at most ten times for Ready +-with matching spec and stable UID. Each request has a 5s kubectl request timeout +-inside the existing 60s process bound; nine sleeps of 1s bound each poll loop. +-Controller resourceVersion updates during successful readiness are captured. ++`require_fixed_store()` with the byte-bounded `bounded_public_command.command()` ++transport. Only the fixed public ESO resources are read using raw v1 paths. ++A successful field-selected collection response proves observed absence. ++The helper creates only the ExternalSecret, compares its fixed spec with only ++the three pinned defaults normalized, and polls at most ten times for a recent ++current-metadata controller report with stable UID. Request, command and total ++attempt deadlines apply as described below. Controller resourceVersion updates ++during successful readiness are captured. + + Removal is permitted only for that instance's verified case-created revision, + rechecks spec/UID/resourceVersion, uses Kubernetes DeleteOptions with both UID +@@ -95,8 +96,9 @@ + The callable `production_bindings()` **always refuses before any command**. + The private mechanics use empty environment and `/dev/null` kubeconfig and + therefore cannot borrow the caller's native session. They are source preparation, +-not an adapter/evidence authority. Tests intercept the actual subprocess call; +-they do not provide production callbacks or attestations. A real lane requires ++not an adapter/evidence authority. Mechanics tests intercept command dispatch; ++runner tests launch only synthetic local children. Neither provides production ++callbacks or attestations. A real lane requires + an owner-reviewed authenticated human aal2 session, acknowledged case revision + and caller binding, scoped Kubernetes credentials, one-shot durable admission + journaling and authenticated evidence publication/verification, plus real +@@ -109,6 +111,65 @@ + lane authority. No unsigned receipt or `True` oracle replaces these facilities. + + Consequently RPF-1.3-T4 remains open for real capability binding and owner review; +-this patch completes only concrete ESO command/state mechanics and regressions. ++this patch prepares concrete ESO command/state mechanics and regressions. + No ESO role, policy, Store, admission service, grant, catalog acceptance, native + permission or owner descriptor acceptance is created or inferred. ++ ++ ++## r2 source mechanics: current controller report and containment ++ ++This preparation is not production admission or owner acceptance. The factory ++continues to refuse absent actual case/client authorization, scoped caller and ++session, owner exclusion/admission, durable admission/recovery, authenticated ++evidence and per-attempt revocation. Empty transport environment and /dev/null ++kubeconfig deliberately remain unusable for ambient production access. ++ ++The pinned ESO v0.16.1 controller (commit ++8d037d197c88eeb9c4fe489e906c9cb3ab9ee6af) sets syncedResourceVersion to ++its metadata generation plus '-' plus the MD5 of its Go-formatted annotations ++and labels struct. That token is not Kubernetes resourceVersion or authenticated ++evidence. r2 compares the exact token, a unique Ready=True/SecretSynced condition ++with the controller's successful 'secret synced' message, and a valid refreshTime ++no older than the fixed five-minute refresh interval and not in the future. ++The controller sets refreshTime to reconcile start. Its Retain/missing-provider ++branch also sets Ready but uses a different message; r2 refuses that branch. ++These checks prove a recent controller report for current object metadata under ++the pinned implementation, not present Secret contents, provider availability, ++a live credential outcome or runtime authentication. Deployed-version matching, ++trusted API/status custody and a sane local clock remain production prerequisites. ++ ++Store status has no generation binding. r2 accepts only generation 1 with the ++fixed spec and unique Valid/'store validated' Ready condition, and records its ++UID/resourceVersion. A later generation is refused with ++store_ready_generation_unverifiable. Repeated exact Store observations before ++create, during readiness and before a receipt detect observed drift; they cannot ++atomically bind create or ESO's Store lookup to a Store revision. A wholly ++interleaved change/restoration remains invisible. Actual owner-controlled ++exclusion/admission is missing and remains a production blocker. Cleanup of a ++verified ExternalSecret uses its own UID/RV preconditions and does not depend on ++Store availability. ++ ++All reads use explicit raw v1 paths. The fixed ExternalSecret collection query ++includes an exact metadata.name field selector and limit 2 to obtain structured ++successful absence. Its response must be a v1 ExternalSecretList with at most ++one fixed namespace/name item and no continuation. Errors are never absence. ++This needs field-selected ExternalSecret list access in any future real session; ++no such access is granted by this code. No Kubernetes Secret is read or listed. ++ ++The new POSIX runner bounds combined stdout+stderr during collection to 262144 ++bytes, discards stderr, uses a six-second maximum command deadline, sends input ++without a pipe deadlock, and kills the process group before reaping its leader ++on success or failure. Wait/reap has a separate one-second cleanup budget; ++unconfirmed cleanup is refusal. Operations have a 30-second monotonic deadline ++and cannot return success once it has elapsed. Descendant tests verify ordinary ++same-group children with held and closed pipes. This is process-group cleanup, ++not a sandbox guarantee against a program escaping its group/session. Privileged ++runtime isolation is not established by this runner or its tests. ++ ++The three actual v1 remoteRef defaults (conversionStrategy=Default, ++decodingStrategy=None, metadataPolicy=None) compare equally when omitted or ++explicit. Other spec changes remain conflicts. The metadata-only contract helper ++is not itself the runtime freshness gate; r2 delivery adds the checks above. ++The fixed template bytes remain unchanged. Unknown mutation responses preserve ++pending reconciliation, including any valid returned UID/RV, and never gain a ++blind delete/retry path. Local one-shot state remains non-durable. +--- /dev/null ++++ b/scripts/bounded_public_command.py +@@ -0,0 +1,107 @@ ++"""POSIX byte/time bounded subprocess collection; no credential/session authority.""" ++from __future__ import annotations ++ ++import json ++import os ++import selectors ++import signal ++import subprocess ++import time ++ ++MAX_OUTPUT = 262144 # combined stdout + stderr; stderr is counted, never retained ++MAX_INPUT = 65536 ++CLEANUP_TIMEOUT = 1 ++ ++ ++class CommandRefused(Exception): ++ """Only fixed diagnostics escape this transport.""" ++ ++ ++def command(argv, *, payload=None, timeout=6): ++ if not isinstance(timeout, (float, int)) or not 0 < timeout <= 6: ++ raise CommandRefused('command_budget_invalid') ++ try: ++ body = b'' if payload is None else json.dumps(payload).encode() ++ if len(body) > MAX_INPUT: ++ raise CommandRefused('command_input_bound') ++ except (ValueError, TypeError): ++ raise CommandRefused('command_input_invalid') from None ++ process = None ++ selector = selectors.DefaultSelector() ++ output = bytearray() ++ total = 0 ++ offset = 0 ++ failure = None ++ code = None ++ deadline = time.monotonic() + timeout ++ try: ++ process = subprocess.Popen(argv, stdin=subprocess.PIPE, ++ stdout=subprocess.PIPE, stderr=subprocess.PIPE, env={}, ++ start_new_session=True, close_fds=True, shell=False) ++ for stream in (process.stdout, process.stderr, process.stdin): ++ os.set_blocking(stream.fileno(), False) ++ selector.register(process.stdout, selectors.EVENT_READ, 'stdout') ++ selector.register(process.stderr, selectors.EVENT_READ, 'stderr') ++ if body: ++ selector.register(process.stdin, selectors.EVENT_WRITE, 'stdin') ++ else: ++ process.stdin.close() ++ while True: ++ remaining = deadline - time.monotonic() ++ if remaining <= 0: ++ raise CommandRefused('command_timeout') ++ # WNOWAIT deliberately leaves the leader unreaped so its PID/PGID ++ # cannot be recycled before final process-group cleanup. ++ state = os.waitid(os.P_PID, process.pid, ++ os.WEXITED | os.WNOHANG | os.WNOWAIT) ++ if state is not None and not selector.get_map(): ++ break ++ for key, _ in selector.select(min(remaining, .05)): ++ stream = key.fileobj ++ if key.data == 'stdin': ++ try: ++ offset += os.write(stream.fileno(), body[offset:offset + 4096]) ++ except BlockingIOError: ++ continue ++ if offset == len(body): ++ selector.unregister(stream); stream.close() ++ continue ++ try: ++ block = os.read(stream.fileno(), 4096) ++ except BlockingIOError: ++ continue ++ if not block: ++ selector.unregister(stream); stream.close() ++ continue ++ total += len(block) ++ if total > MAX_OUTPUT: ++ raise CommandRefused('command_output_bound') ++ if key.data == 'stdout': ++ output.extend(block) ++ except CommandRefused as error: ++ failure = str(error) ++ except Exception: ++ failure = 'command_failed' ++ finally: ++ selector.close() ++ if process is not None: ++ # Also kills descendants that kept pipes open or closed all pipes. ++ # The leader has not been reaped, avoiding PGID reuse races. ++ try: ++ os.killpg(process.pid, signal.SIGKILL) ++ except ProcessLookupError: ++ pass ++ except Exception: ++ failure = 'command_cleanup_unconfirmed' ++ for stream in (process.stdin, process.stdout, process.stderr): ++ if stream is not None: ++ stream.close() ++ try: ++ code = process.wait(timeout=CLEANUP_TIMEOUT) ++ except Exception: ++ failure = 'command_cleanup_unconfirmed' ++ if failure is not None: ++ raise CommandRefused(failure) from None ++ if code != 0: ++ raise CommandRefused('command_failed') from None ++ return bytes(output) +--- a/scripts/keycape_issuer_delivery.py ++++ b/scripts/keycape_issuer_delivery.py +@@ -1,39 +1,40 @@ +-"""RPF-1.3-T4 concrete source-preparation mechanics; NO production admission. +- +-Only public ESO objects are fetched. The production factory refuses before +-dispatch: no authenticated delivery session/evidence service currently exists. +-Private mechanics are exercised by fake-command tests, never a grant boundary. +-""" ++"""RPF-1.3-T4 source mechanics; production admission deliberately unavailable.""" + from __future__ import annotations + + from copy import deepcopy ++from datetime import datetime, timezone ++import hashlib + import json + import re + import time + +-from keycape_issuer_eso import documents, require_fixed_store, Refused +-from state_hub_preflight_lane import command, LaneError ++from keycape_issuer_eso import documents, require_fixed_store, Refused, canonical_external_spec ++from bounded_public_command import command, CommandRefused + + POLL_COUNT = 10 + POLL_DELAY = 1 ++ATTEMPT_TIMEOUT = 30 + API = '/apis/external-secrets.io/v1' + + + class Uncertain(Refused): +- """No success receipt: retain exact metadata for owner reconciliation.""" + def __init__(self, reason, revision=None): + super().__init__(reason) + self.revision = deepcopy(revision) + + + def production_bindings(*args, **kwargs): +- # Neither Warden invocation fields, an operator's kubeconfig, an unsigned +- # local receipt nor mint-lane lookup-self authenticates THIS lane/caller. +- raise Refused('delivery_admission_session_journal_and_evidence_unavailable') ++ raise Refused('delivery_admission_exclusion_session_journal_evidence_revocation_unavailable') ++ ++ ++def _metadata(obj): ++ if not isinstance(obj, dict) or not isinstance(obj.get('metadata'), dict): ++ raise Refused('public_metadata_invalid') ++ return obj['metadata'] + + + def _revision(obj): +- meta = obj.get('metadata', {}) ++ meta = _metadata(obj) + result = {'uid': meta.get('uid'), 'resource_version': meta.get('resourceVersion')} + if any(not isinstance(x, str) or not re.fullmatch(r'[A-Za-z0-9._:-]{1,192}', x) + for x in result.values()): +@@ -41,66 +42,159 @@ + return result + + ++def _generation(obj): ++ value = _metadata(obj).get('generation') ++ if type(value) is not int or not 1 <= value <= 2**63 - 1: ++ raise Refused('public_generation_invalid') ++ return value ++ ++ ++def _synced_version(obj): ++ """ESO v0.16.1 util.GetResourceVersion + utils.ObjectHash, not metadata RV. ++ ++ Go fmt %+v on the actual two-field struct uses sorted string maps, raw ++ keys/values, and map[] for both nil and empty maps. Tests compare this ++ implementation to a standalone Go extraction of that exact upstream code. ++ This MD5 is the controller's revision token, never authentication evidence. ++ """ ++ meta = _metadata(obj) ++ maps = [] ++ for name in ('annotations', 'labels'): ++ value = meta.get(name) ++ if value is None: ++ value = {} ++ if not isinstance(value, dict) or any( ++ not isinstance(k, str) or not isinstance(v, str) for k, v in value.items()): ++ raise Refused('public_metadata_map_invalid') ++ try: ++ textual = 'map[' + ' '.join(k + ':' + value[k] for k in sorted(value)) + ']' ++ textual.encode('utf-8', errors='strict') ++ except UnicodeError: ++ raise Refused('public_metadata_map_invalid') from None ++ maps.append(name + ':' + textual) ++ hashed = hashlib.md5(('{' + ' '.join(maps) + '}').encode(), usedforsecurity=False).hexdigest() ++ return str(_generation(obj)) + '-' + hashed ++ ++ ++def _ready(obj): ++ status = obj.get('status') ++ if not isinstance(status, dict): ++ return False ++ conditions = status.get('conditions') ++ if not isinstance(conditions, list) or any(not isinstance(c, dict) for c in conditions): ++ return False ++ ready = [c for c in conditions if c.get('type') == 'Ready'] ++ if len(ready) != 1 or any(ready[0].get(k) != v for k, v in { ++ 'status': 'True', 'reason': 'SecretSynced', 'message': 'secret synced'}.items()): ++ return False ++ if status.get('syncedResourceVersion') != _synced_version(obj): ++ return False ++ stamp = status.get('refreshTime') ++ if not isinstance(stamp, str) or not re.fullmatch( ++ r'\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d(?:\.\d{1,9})?(?:Z|[+-]\d\d:\d\d)', stamp): ++ return False ++ try: ++ refreshed = datetime.fromisoformat(stamp.replace('Z', '+00:00')) ++ age = (datetime.now(timezone.utc) - refreshed).total_seconds() ++ return 0 <= age <= 300 ++ except (ValueError, OverflowError): ++ return False ++ ++ + class _DeliveryMechanics: +- """Concrete fixed kubectl algorithm, not an authenticated owner capability. +- +- No caller resource, remoteRef, namespace, store or path argument is accepted. +- The deliberately unauthenticated transport cannot load ambient credentials. +- A reviewed session transport must replace it only after real lane admission, +- one-shot durable journaling, evidence and revocation are implemented. ++ """No authority boundary: injected calls in tests, unusable ambient transport. ++ ++ Store observations detect drift; they cannot exclude an ABA race between ++ observations or bind ESO's store lookup atomically. Production needs real ++ owner exclusion/admission in addition to authenticated caller facilities. + """ + def __init__(self, client_id): + self.client_id = client_id + self.store, self.external = documents(client_id) + self.created = None + self.pending = False ++ self.used = False ++ self.deadline = None + + def _call(self, args, payload=None): + try: +- result = command(['kubectl', '--kubeconfig=/dev/null', +- '--request-timeout=5s', *args], +- payload=payload, env={}) +- if len(result.stdout) > 262144: +- raise Refused('public_response_too_large') +- if not result.stdout.strip(): ++ remaining = self.deadline - time.monotonic() ++ if remaining <= 0: ++ raise Refused('attempt_deadline') ++ raw = command(['kubectl', '--kubeconfig=/dev/null', ++ '--request-timeout=5s', *args], ++ payload=payload, timeout=min(6, remaining)) ++ if time.monotonic() >= self.deadline: ++ raise Refused('attempt_deadline') ++ # Runner enforces the same bound during collection, not just here. ++ if not isinstance(raw, bytes) or len(raw) > 262144: ++ raise Refused('public_response_bound') ++ if not raw.strip(): + return None +- value = json.loads(result.stdout) ++ value = json.loads(raw) + if not isinstance(value, dict): + raise Refused('public_response_shape') + return value +- except (LaneError, OSError, ValueError, TimeoutError): ++ except Refused: ++ raise ++ except Exception: + raise Refused('public_command_failed') from None +- except Exception: +- # Includes subprocess timeouts; never expose stdout/stderr or argv. +- raise Refused('public_command_failed') from None + + def _get(self, store=False): +- obj = self.store if store else self.external +- resource = 'clustersecretstores.external-secrets.io' if store else 'externalsecrets.external-secrets.io' +- args = [] if store else ['-n', 'sso'] +- return self._call([*args, 'get', resource, obj['metadata']['name'], +- '--ignore-not-found=true', '-o', 'json']) ++ if store: ++ return self._call(['get', '--raw', API + '/clustersecretstores/' + self.store['metadata']['name']]) ++ # Raw GET fixes API version and avoids kubectl mapper fallback. A fixed ++ # field-selected collection provides structured successful absence; ++ # stderr/nonzero is never interpreted as NotFound. No Secret is listed. ++ uri = (API + '/namespaces/sso/externalsecrets?fieldSelector=metadata.name%3D' ++ + self.external['metadata']['name'] + '&limit=2') ++ listing = self._call(['get', '--raw', uri]) ++ if (not isinstance(listing, dict) or listing.get('apiVersion') != 'external-secrets.io/v1' ++ or listing.get('kind') != 'ExternalSecretList' ++ or not isinstance(listing.get('metadata'), dict) ++ or listing['metadata'].get('continue') not in (None, '') ++ or not isinstance(listing.get('items'), list) or len(listing['items']) > 1): ++ raise Refused('public_collection_invalid') ++ if not listing['items']: ++ return None ++ value = listing['items'][0] ++ meta = _metadata(value) ++ if (value.get('apiVersion') != 'external-secrets.io/v1' ++ or value.get('kind') != 'ExternalSecret' ++ or meta.get('name') != self.external['metadata']['name'] ++ or meta.get('namespace') != 'sso'): ++ raise Refused('public_collection_identity_invalid') ++ return value ++ ++ def _store_revision(self, sealed=None): ++ store = self._get(store=True) ++ require_fixed_store(store, self.client_id) ++ if _metadata(store).get('deletionTimestamp'): ++ raise Refused('store_terminating') ++ revision = _revision(store) ++ # Actual Store status has no generation binding. Generation 1 excludes ++ # a prior spec update under Kubernetes generation semantics. For any ++ # later generation Ready cannot establish current-spec validation. ++ if _generation(store) != 1: ++ raise Refused('store_ready_generation_unverifiable') ++ conditions = store.get('status', {}).get('conditions', []) ++ ready = [c for c in conditions if isinstance(c, dict) and c.get('type') == 'Ready'] ++ if len(ready) != 1 or ready[0].get('reason') != 'Valid' or ready[0].get('message') != 'store validated': ++ raise Refused('store_validation_unverified') ++ if sealed is not None and sealed != revision: ++ raise Refused('store_revision_changed') ++ return revision + + def _match(self, obj): +- if not isinstance(obj, dict): +- raise Refused('delivery_missing') +- metadata = obj.get('metadata', {}) ++ metadata = _metadata(obj) + if (obj.get('apiVersion') != self.external['apiVersion'] + or obj.get('kind') != 'ExternalSecret' + or metadata.get('name') != self.external['metadata']['name'] +- or metadata.get('namespace') != 'sso' +- or metadata.get('deletionTimestamp') +- or obj.get('spec') != self.external['spec']): ++ or metadata.get('namespace') != 'sso' or metadata.get('deletionTimestamp') ++ or canonical_external_spec(obj.get('spec')) != canonical_external_spec(self.external['spec'])): + raise Refused('delivery_conflict') ++ _generation(obj) + return _revision(obj) +- +- @staticmethod +- def _ready(obj): +- status = obj.get('status', {}) +- conditions = status.get('conditions', []) if isinstance(status, dict) else [] +- return isinstance(conditions, list) and any( +- isinstance(c, dict) and c.get('type') == 'Ready' and c.get('status') == 'True' +- for c in conditions) + + def _receipt(self, operation, before, after): + return {'schema': 'netkingdom.deliver-secret.v1', 'operation': operation, +@@ -111,53 +205,58 @@ + 'before': before, 'after': after} + + def deliver(self): +- if self.pending or self.created is not None: ++ if self.used or self.pending or self.created is not None: + raise Refused('attempt_already_used_or_unreconciled') +- # A successful empty response proves absence; errors never do. +- store = self._get(store=True) +- require_fixed_store(store, self.client_id) +- if store.get('metadata', {}).get('deletionTimestamp'): +- raise Refused('store_terminating') ++ self.used = True # local one-shot only, never a durable admission journal ++ self.deadline = time.monotonic() + ATTEMPT_TIMEOUT ++ sealed_store = self._store_revision() + old = self._get() + before = self._match(old) if old is not None else None + uid = before['uid'] if before else None ++ self._store_revision(sealed_store) + if old is None: +- # Set before dispatch: a lost create response MUST NOT be retried, +- # adopted, or claimed as a pre-existing no-change result. + self.pending = True + try: + created = self._call(['-n', 'sso', 'create', '-f', '-', '-o', 'json'], self.external) +- self.created = self._match(created) ++ # Retain response identity even if admission changed its spec. ++ # Pending prevents deletion until exact verified delivery. ++ self.created = _revision(created) ++ self._match(created) + uid = self.created['uid'] +- except Refused: +- raise Uncertain('create_outcome_unverified') from None ++ except Exception: ++ raise Uncertain('create_outcome_unverified', self.created) from None + try: + for index in range(POLL_COUNT): ++ self._store_revision(sealed_store) + current = self._get() + after = self._match(current) + if after['uid'] != uid: + raise Refused('delivery_replaced') +- if self._ready(current): ++ if _ready(current): ++ self._store_revision(sealed_store) + if self.created is not None: + self.created = after + self.pending = False + return self._receipt('deliver', before, after) + if index + 1 < POLL_COUNT: +- time.sleep(POLL_DELAY) +- raise Refused('delivery_ready_timeout') +- except Refused: +- if self.created is not None: ++ remaining = self.deadline - time.monotonic() ++ if remaining <= 0: ++ raise Refused('attempt_deadline') ++ time.sleep(min(POLL_DELAY, remaining)) ++ raise Refused('delivery_current_ready_timeout') ++ except Exception: ++ if self.pending or self.created is not None: + raise Uncertain('created_delivery_requires_reconciliation', self.created) from None +- raise ++ raise Refused('delivery_verification_failed') from None + + def remove(self, revision): +- # An arbitrary adapter argument alone is never deletion provenance. + if self.pending or self.created is None or revision != self.created: + raise Refused('no_verified_case_creation') ++ self.deadline = time.monotonic() + ATTEMPT_TIMEOUT + try: + if self._match(self._get()) != self.created: + raise Refused('created_revision_changed') +- except Refused: ++ except Exception: + raise Uncertain('created_revision_changed', self.created) from None + uri = API + '/namespaces/sso/externalsecrets/' + self.external['metadata']['name'] + options = {'apiVersion': 'v1', 'kind': 'DeleteOptions', +@@ -176,7 +275,10 @@ + if _revision(current)['uid'] != prior['uid']: + raise Refused('replacement_preserved') + if index + 1 < POLL_COUNT: +- time.sleep(POLL_DELAY) ++ remaining = self.deadline - time.monotonic() ++ if remaining <= 0: ++ raise Refused('attempt_deadline') ++ time.sleep(min(POLL_DELAY, remaining)) + raise Refused('delete_unconfirmed') +- except Refused: ++ except Exception: + raise Uncertain('cleanup_requires_reconciliation', prior) from None +--- a/scripts/keycape_issuer_eso.py ++++ b/scripts/keycape_issuer_eso.py +@@ -37,6 +37,23 @@ + return deepcopy(documents(client_id)[1]) + + ++def canonical_external_spec(spec): ++ """Normalize only the three pinned ESO v0.16.1 remoteRef defaults. ++ ++ Omitted and explicit defaults are equivalent; all other fields remain exact. ++ This says nothing about freshness, admission authority or Store continuity. ++ """ ++ value = deepcopy(spec) ++ if isinstance(value, dict) and isinstance(value.get('data'), list): ++ for item in value['data']: ++ if isinstance(item, dict) and isinstance(item.get('remoteRef'), dict): ++ for name, default in (('conversionStrategy', 'Default'), ++ ('decodingStrategy', 'None'), ++ ('metadataPolicy', 'None')): ++ item['remoteRef'].setdefault(name, default) ++ return value ++ ++ + def _matching_public_resource(observed: dict, expected: dict) -> None: + if not isinstance(observed, dict): + raise Refused('public resource observation required') +@@ -50,7 +67,10 @@ + or observed.get('kind') != expected['kind'] + or metadata.get('name') != expected['metadata']['name'] + or not namespace_matches +- or observed.get('spec') != expected['spec']): ++ or (canonical_external_spec(observed.get('spec')) ++ != canonical_external_spec(expected['spec']) ++ if expected['kind'] == 'ExternalSecret' ++ else observed.get('spec') != expected['spec'])): + raise Refused('fixed issuer resource differs') + status = observed.get('status') + conditions = status.get('conditions', []) if isinstance(status, dict) else [] +--- /dev/null ++++ b/tests/eso-hash-vectors.json +@@ -0,0 +1,57 @@ ++[ ++ { ++ "metadata": { ++ "generation": 1 ++ }, ++ "expected": "1-2f2c636d06132f67992c15a1daeb0a5f" ++ }, ++ { ++ "metadata": { ++ "generation": 2, ++ "annotations": {}, ++ "labels": {} ++ }, ++ "expected": "2-2f2c636d06132f67992c15a1daeb0a5f" ++ }, ++ { ++ "metadata": { ++ "generation": 3, ++ "annotations": { ++ "b": "2", ++ "a": "1" ++ }, ++ "labels": { ++ "z": "last", ++ "c": "first" ++ } ++ }, ++ "expected": "3-7ad4f86f5aba928cde01913c7b9d962a" ++ }, ++ { ++ "metadata": { ++ "generation": 4, ++ "annotations": { ++ "a:b": "c d", ++ "a b": "[]:%+v" ++ }, ++ "labels": { ++ "newline\n": "tab\t" ++ } ++ }, ++ "expected": "4-48e35b520c38b2944d461645440cc3cd" ++ }, ++ { ++ "metadata": { ++ "generation": 9223372036854775807, ++ "annotations": { ++ "é": "好", ++ "🦊": "😀" ++ }, ++ "labels": { ++ "": "", ++ "null": "\u0000" ++ } ++ }, ++ "expected": "9223372036854775807-da6b2f7c16875cb96b3a00d83378eda3" ++ } ++] +--- /dev/null ++++ b/tests/test_bounded_public_command.py +@@ -0,0 +1,95 @@ ++import os ++from pathlib import Path ++import sys ++import tempfile ++import time ++import unittest ++from unittest.mock import patch ++ ++sys.path.insert(0, str(Path(__file__).resolve().parents[1] / 'scripts')) ++import bounded_public_command as runner ++ ++# Every launched fixture denies socket creation in its own interpreter as well ++# as the parent. They only perform controlled local stdout/stderr/process work. ++PREFIX = "import socket; socket.socket=lambda *a,**k: (_ for _ in ()).throw(AssertionError('network forbidden')); " ++ ++ ++class BoundedCommand(unittest.TestCase): ++ def setUp(self): ++ p = patch.dict(os.environ, {}, clear=True); p.start(); self.addCleanup(p.stop) ++ p = patch('socket.socket', side_effect=AssertionError('network forbidden')) ++ p.start(); self.addCleanup(p.stop) ++ ++ def call(self, code, timeout=2, payload=None): ++ return runner.command(['/usr/bin/python3', '-c', PREFIX + code], timeout=timeout, payload=payload) ++ ++ def test_empty_environment_and_input_roundtrip(self): ++ raw = self.call("import os,sys,json; b=sys.stdin.buffer.read(); print(json.dumps({'keys':sorted(os.environ),'body':json.loads(b)}))", payload={'fixed': 'metadata'}) ++ import json ++ response = json.loads(raw) ++ # CPython may inject LC_CTYPE during startup even though exec env={}; ++ # neither HOME nor credential variables are inherited. ++ self.assertFalse(set(response['keys']) - {'LC_CTYPE'}) ++ self.assertEqual(response['body'], {'fixed': 'metadata'}) ++ ++ def test_stdout_flood_stopped_with_fixed_error(self): ++ with self.assertRaisesRegex(runner.CommandRefused, '^command_output_bound$'): ++ self.call("import os; [os.write(1,b'PRIVATE'*4096) for _ in range(1000)]") ++ ++ def test_stderr_flood_stopped_with_fixed_error(self): ++ with self.assertRaisesRegex(runner.CommandRefused, '^command_output_bound$'): ++ self.call("import os; [os.write(2,b'PRIVATE'*4096) for _ in range(1000)]") ++ ++ def test_combined_cap_not_independent_per_stream(self): ++ with self.assertRaisesRegex(runner.CommandRefused, '^command_output_bound$'): ++ self.call("import os; os.write(1,b'x'*140000); os.write(2,b'y'*140000)") ++ ++ def test_error_output_never_escapes(self): ++ with self.assertRaisesRegex(runner.CommandRefused, '^command_failed$') as error: ++ self.call("import sys; print('PRIVATE'); print('PRIVATE',file=sys.stderr); sys.exit(7)") ++ self.assertNotIn('PRIVATE', str(error.exception)) ++ ++ def test_sleep_timeout_is_real_and_bounded(self): ++ start = time.monotonic() ++ with self.assertRaisesRegex(runner.CommandRefused, '^command_timeout$'): ++ self.call('import time; time.sleep(60)', timeout=.15) ++ self.assertLess(time.monotonic() - start, 2) ++ ++ def test_descendant_holding_pipes_is_killed(self): ++ with tempfile.TemporaryDirectory(prefix='delivery-runner-') as scratch: ++ pidfile = Path(scratch) / 'descendant' ++ code = ("import os,time; pid=os.fork(); " ++ "(time.sleep(60) if pid==0 else " ++ f"(open({str(pidfile)!r},'w').write(str(pid)),os._exit(0)))") ++ with self.assertRaisesRegex(runner.CommandRefused, '^command_timeout$'): ++ self.call(code, timeout=.2) ++ self.assertTrue(pidfile.exists()) ++ pid = pidfile.read_text() ++ status = Path('/proc') / pid / 'stat' ++ for _ in range(20): ++ if not status.exists() or status.read_text().split()[2] == 'Z': break ++ time.sleep(.01) ++ if status.exists(): self.assertEqual(status.read_text().split()[2], 'Z') ++ ++ def test_descendant_with_closed_pipes_is_killed_on_success(self): ++ with tempfile.TemporaryDirectory(prefix='delivery-runner-') as scratch: ++ pidfile = Path(scratch) / 'descendant' ++ code = ("import os,time; pid=os.fork(); " ++ "(os.close(0),os.close(1),os.close(2),time.sleep(60)) if pid==0 else " ++ f"(open({str(pidfile)!r},'w').write(str(pid)),print('ok'))") ++ self.assertEqual(self.call(code), b'ok\n') ++ status = Path('/proc') / pidfile.read_text() / 'stat' ++ # Kill delivery may race the proc-state observation; wait boundedly. ++ for _ in range(20): ++ if not status.exists() or status.read_text().split()[2] == 'Z': break ++ time.sleep(.01) ++ if status.exists(): self.assertEqual(status.read_text().split()[2], 'Z') ++ ++ def test_input_bound_and_spawn_failure_fixed(self): ++ with self.assertRaisesRegex(runner.CommandRefused, '^command_input_bound$'): ++ self.call('pass', payload={'data': 'x'*65536}) ++ with self.assertRaisesRegex(runner.CommandRefused, '^command_failed$'): ++ runner.command(['/nonexistent-fixture-binary'], timeout=.1) ++ ++ ++if __name__ == '__main__': unittest.main() +--- a/tests/test_keycape_issuer_delivery.py ++++ b/tests/test_keycape_issuer_delivery.py +@@ -1,196 +1,284 @@ +-"""Offline command-level regressions. No HOME/env/network/credential access.""" + from copy import deepcopy ++from datetime import datetime, timedelta, timezone + import json + import os + from pathlib import Path +-import subprocess + import sys + import unittest + from unittest.mock import patch + + sys.path.insert(0, str(Path(__file__).resolve().parents[1] / 'scripts')) + import keycape_issuer_delivery as lane +- +- +-class DeliveryTests(unittest.TestCase): ++from bounded_public_command import CommandRefused ++ ++ ++class Mechanics(unittest.TestCase): + def setUp(self): +- self.environment = patch.dict(os.environ, {}, clear=True) +- self.environment.start() +- self.addCleanup(self.environment.stop) +- self.network = patch('socket.socket', side_effect=AssertionError('network forbidden')) +- self.network.start() +- self.addCleanup(self.network.stop) ++ p = patch.dict(os.environ, {}, clear=True); p.start(); self.addCleanup(p.stop) ++ p = patch('socket.socket', side_effect=AssertionError('network forbidden')) ++ p.start(); self.addCleanup(p.stop) + self.m = lane._DeliveryMechanics('forgejo') +- self.store = self.resource(self.m.store) ++ self.store = self.resource(self.m.store, store=True) ++ self.obj = None ++ self.calls = [] ++ self.store_reads = 0 ++ self.hook = lambda args: None ++ self.fail_create = False ++ self.fail_delete = False ++ self.create_mutate = lambda obj: obj ++ self.force_list = None ++ self.poll_object = None ++ self.clock = 0 ++ self.clock_step = 0 ++ p = patch.object(lane, 'command', side_effect=self.command) ++ p.start(); self.addCleanup(p.stop) ++ p = patch.object(lane.time, 'sleep'); p.start(); self.addCleanup(p.stop) ++ p = patch.object(lane.time, 'monotonic', side_effect=lambda: self.clock) ++ p.start(); self.addCleanup(p.stop) ++ ++ def resource(self, doc, store=False): ++ value = deepcopy(doc) ++ value['metadata'].update(uid='store-uid' if store else 'created-uid', resourceVersion='10', generation=1) ++ if store: ++ value['status'] = {'conditions': [{'type': 'Ready', 'status': 'True', ++ 'reason': 'Valid', 'message': 'store validated'}]} ++ else: ++ value['spec'] = lane.canonical_external_spec(value['spec']) ++ value['status'] = {'conditions': [{'type': 'Ready', 'status': 'True', ++ 'reason': 'SecretSynced', 'message': 'secret synced'}], ++ 'syncedResourceVersion': lane._synced_version(value), ++ 'refreshTime': datetime.now(timezone.utc).isoformat()} ++ return value ++ ++ def command(self, args, *, payload=None, timeout=6): ++ self.assertEqual(args[:3], ['kubectl', '--kubeconfig=/dev/null', '--request-timeout=5s']) ++ self.assertGreater(timeout, 0); self.assertLessEqual(timeout, 6) ++ self.calls.append((deepcopy(args), deepcopy(payload))) ++ self.clock += self.clock_step ++ self.hook(args) ++ if 'create' in args: ++ if self.fail_create: ++ raise CommandRefused('PRIVATE PROVIDER VALUE') ++ self.obj = self.create_mutate(self.resource(self.m.external)) ++ value = self.obj ++ elif 'delete' in args: ++ if self.fail_delete: ++ raise CommandRefused('PRIVATE PROVIDER VALUE') ++ revision = lane._revision(self.obj) ++ if payload['preconditions'] != {'uid': revision['uid'], 'resourceVersion': revision['resource_version']}: ++ raise CommandRefused('conditional conflict fixture') ++ self.obj = None ++ value = {'kind': 'Status', 'status': 'Success'} ++ elif '/clustersecretstores/' in args[-1]: ++ self.store_reads += 1 ++ value = self.store ++ else: ++ self.assertIn('get', args); self.assertIn('--raw', args) ++ self.assertEqual(args[-1], lane.API + '/namespaces/sso/externalsecrets?fieldSelector=metadata.name%3Dkeycape-forgejo-client&limit=2') ++ value = self.force_list if self.force_list is not None else { ++ 'apiVersion': 'external-secrets.io/v1', 'kind': 'ExternalSecretList', ++ 'metadata': {}, 'items': [] if self.obj is None else [self.poll_object or self.obj]} ++ return json.dumps(deepcopy(value)).encode() ++ ++ def test_actual_defaults_create_current_ready_and_conditional_delete(self): ++ delivered = self.m.deliver() ++ self.assertTrue(delivered['changed']); self.assertIsNone(delivered['before']) ++ self.assertGreaterEqual(self.store_reads, 4) ++ removed = self.m.remove(delivered['after']) ++ self.assertEqual(removed['after'], None) ++ args, payload = next(c for c in self.calls if 'delete' in c[0]) ++ self.assertEqual(args[5], lane.API + '/namespaces/sso/externalsecrets/keycape-forgejo-client') ++ self.assertEqual(payload['preconditions'], {'uid': 'created-uid', 'resourceVersion': '10'}) ++ ++ def test_existing_no_change_cannot_be_deleted_or_reused(self): + self.obj = self.resource(self.m.external) +- self.calls = [] +- self.responses = [] +- self.native = patch('subprocess.run', side_effect=self.run_command) +- self.native.start() +- self.addCleanup(self.native.stop) +- self.sleep = patch.object(lane.time, 'sleep') +- self.sleep.start() +- self.addCleanup(self.sleep.stop) +- +- def resource(self, template, uid='uid-1', rv='10', ready=True): +- obj = deepcopy(template) +- obj['metadata'].update(uid=uid, resourceVersion=rv) +- obj['status'] = {'conditions': [{'type': 'Ready', 'status': str(ready)}]} +- return obj +- +- def run_command(self, argv, **kw): +- self.calls.append((deepcopy(argv), deepcopy(kw))) +- self.assertEqual(kw['env'], {}) +- self.assertEqual(argv[:3], ['kubectl', '--kubeconfig=/dev/null', '--request-timeout=5s']) +- self.assertTrue(kw['capture_output']) +- self.assertEqual(kw['timeout'], 60) +- if not self.responses: +- self.fail('unexpected command dispatch') +- result = self.responses.pop(0) +- if isinstance(result, Exception): +- raise result +- if result == 'error': +- return subprocess.CompletedProcess(argv, 1, b'PRIVATE PROVIDER VALUE', b'PRIVATE ERROR') +- return subprocess.CompletedProcess(argv, 0, b'' if result is None else json.dumps(result).encode(), b'') +- +- def create(self): +- self.responses += [self.store, None, self.obj, self.obj] +- return self.m.deliver() +- +- def test_production_factory_fails_before_dispatch(self): +- with self.assertRaisesRegex(lane.Refused, 'unavailable'): +- lane.production_bindings(case_source=b'arbitrary', admitted=object()) ++ delivered = self.m.deliver() ++ self.assertFalse(delivered['changed']) ++ with self.assertRaises(lane.Refused): self.m.remove(delivered['after']) ++ with self.assertRaises(lane.Refused): self.m.deliver() ++ self.assertFalse(any('create' in args or 'delete' in args for args, _ in self.calls)) ++ ++ def test_stale_current_spec_and_metadata_hash_never_ready(self): ++ obj = self.resource(self.m.external) ++ for field, value in [('generation', 2), ('labels', {'new': 'metadata'}), ('annotations', {'new': 'metadata'})]: ++ with self.subTest(field=field): ++ changed = deepcopy(obj); changed['metadata'][field] = value ++ self.assertFalse(lane._ready(changed)) ++ ++ def test_exact_go_upstream_revision_vectors(self): ++ vectors = json.loads((Path(__file__).parent / 'eso-hash-vectors.json').read_text()) ++ for vector in vectors: ++ self.assertEqual(lane._synced_version({'metadata': vector['metadata']}), vector['expected']) ++ ++ def test_wrong_default_strategies_and_identity_never_match(self): ++ for field, value in [('conversionStrategy', 'Unicode'), ('decodingStrategy', 'Base64'), ++ ('metadataPolicy', 'Fetch'), ('key', 'elsewhere'), ('extra', 'value')]: ++ obj = self.resource(self.m.external) ++ obj['spec']['data'][0]['remoteRef'][field] = value ++ with self.assertRaises(lane.Refused): self.m._match(obj) ++ for field, value in [('namespace', 'default'), ('name', 'other'), ('generation', True)]: ++ obj = self.resource(self.m.external); obj['metadata'][field] = value ++ with self.assertRaises(lane.Refused): self.m._match(obj) ++ ++ def test_invalid_metadata_maps_never_produce_hash(self): ++ for value in ([], 'str', {'key': None}, {'key': '\ud800'}): ++ obj = self.resource(self.m.external); obj['metadata']['labels'] = value ++ with self.assertRaises(lane.Refused): lane._synced_version(obj) ++ ++ def test_client_override_injection_refused_before_command(self): ++ for client in ('x/../../secret', 'x;echo', 'A', None, 'a'*49): ++ with self.assertRaises(lane.Refused): lane._DeliveryMechanics(client) + self.assertEqual(self.calls, []) + +- def test_absent_create_ready_and_uid_rv_precondition_delete(self): +- receipt = self.create() +- self.assertTrue(receipt['changed']) +- self.assertIsNone(receipt['before']) +- self.assertEqual(receipt['after'], {'uid': 'uid-1', 'resource_version': '10'}) +- self.responses += [self.obj, {'kind': 'Status'}, None] +- removed = self.m.remove(receipt['after']) +- self.assertEqual(removed['operation'], 'remove') +- delete = next(c for c in self.calls if 'delete' in c[0]) +- options = json.loads(delete[1]['input']) +- self.assertEqual(options['preconditions'], {'uid': 'uid-1', 'resourceVersion': '10'}) +- self.assertEqual(delete[0][5], '/apis/external-secrets.io/v1/namespaces/sso/externalsecrets/keycape-forgejo-client') +- self.assertFalse(any('secret' == a or 'secrets' == a for c in self.calls for a in c[0])) +- +- def test_matching_existing_no_change_never_delete(self): +- self.responses = [self.store, self.obj, self.obj] +- receipt = self.m.deliver() +- self.assertFalse(receipt['changed']) +- with self.assertRaises(lane.Refused): +- self.m.remove(receipt['after']) +- self.assertFalse(any('create' in a or 'delete' in a for a, k in self.calls)) +- +- def test_conflicting_existing_untouched(self): +- drift = deepcopy(self.obj) +- drift['spec']['data'][0]['remoteRef']['key'] = 'elsewhere' +- self.responses = [self.store, drift] +- with self.assertRaises(lane.Refused): +- self.m.deliver() +- self.assertEqual(len(self.calls), 2) +- +- def test_missing_drifted_unready_store_never_create(self): +- for kind in ('missing', 'spec', 'ready', 'namespace', 'terminating'): +- with self.subTest(kind=kind): +- store = deepcopy(self.store) +- if kind == 'missing': store = None +- if kind == 'spec': store['spec']['provider']['vault']['path'] = 'wrong' +- if kind == 'ready': store['status']['conditions'] = [] +- if kind == 'namespace': store['metadata']['namespace'] = 'sso' +- if kind == 'terminating': store['metadata']['deletionTimestamp'] = 'now' +- self.responses = [store] +- with self.assertRaises(lane.Refused): +- lane._DeliveryMechanics('forgejo').deliver() +- self.assertFalse(any('create' in a for a,k in self.calls)) +- +- def test_get_error_not_absence_and_output_contained(self): +- self.responses = [self.store, 'error'] +- with self.assertRaisesRegex(lane.Refused, '^public_command_failed$'): +- self.m.deliver() +- self.assertEqual(len(self.calls), 2) +- +- def test_create_lost_response_or_race_uncertain_no_retry(self): +- self.responses = [self.store, None, 'error'] ++ def test_retained_missing_stale_future_duplicate_status_not_ready(self): ++ for variant in ('retained', 'missing', 'old', 'future', 'duplicate', 'invalid_time'): ++ with self.subTest(variant=variant): ++ obj = self.resource(self.m.external) ++ if variant == 'retained': obj['status']['conditions'][0]['message'] = 'secret retained due to DeletionPolicy=Retain' ++ if variant == 'missing': obj['status'].pop('syncedResourceVersion') ++ if variant in ('old', 'future'): ++ delta = -301 if variant == 'old' else 60 ++ obj['status']['refreshTime'] = (datetime.now(timezone.utc) + timedelta(seconds=delta)).isoformat() ++ if variant == 'duplicate': obj['status']['conditions'] *= 2 ++ if variant == 'invalid_time': obj['status']['refreshTime'] = 'PRIVATE MALFORMED' ++ self.assertFalse(lane._ready(obj)) ++ ++ def test_stale_delivery_times_out_with_reconciliation_provenance(self): ++ self.create_mutate = lambda obj: {**obj, 'status': {'conditions': [{'type': 'Ready', 'status': 'True'}]}} ++ with self.assertRaises(lane.Uncertain) as error: self.m.deliver() ++ self.assertEqual(error.exception.revision['uid'], 'created-uid') ++ with self.assertRaises(lane.Refused): self.m.remove(error.exception.revision) ++ self.assertLessEqual(len(self.calls), 2 * lane.POLL_COUNT + 4) ++ ++ def test_store_prior_generation_refused_before_create(self): ++ self.store['metadata']['generation'] = 2 ++ with self.assertRaisesRegex(lane.Refused, 'generation_unverifiable'): self.m.deliver() ++ self.assertEqual(len(self.calls), 1) ++ ++ def test_store_replaced_before_precreate_read_prevents_create(self): ++ def hook(args): ++ if '/externalsecrets?' in args[-1]: self.store['metadata']['uid'] = 'replacement' ++ self.hook = hook ++ with self.assertRaisesRegex(lane.Refused, 'store_revision_changed'): self.m.deliver() ++ self.assertFalse(any('create' in args for args, _ in self.calls)) ++ ++ def test_store_replaced_inside_create_window_detected_after_mutation(self): ++ def hook(args): ++ if 'create' in args: self.store['metadata']['uid'] = 'replacement' ++ self.hook = hook ++ with self.assertRaises(lane.Uncertain): self.m.deliver() ++ self.assertIsNotNone(self.obj) # double-read did not exclude mutation ++ self.assertTrue(self.m.pending) ++ ++ def test_store_aba_race_is_not_claimed_excluded(self): ++ # A mutation and restoration wholly between observations is invisible. ++ seen = [] ++ def hook(args): ++ if 'create' in args: ++ original = deepcopy(self.store) ++ self.store['metadata']['uid'] = 'transient-replacement' ++ seen.append(self.store['metadata']['uid']) ++ self.store = original ++ seen.append(self.store['metadata']['uid']) ++ self.hook = hook ++ self.assertTrue(self.m.deliver()['changed']) ++ self.assertEqual(len(seen), 2) ++ self.assertEqual(seen, ['transient-replacement', 'store-uid']) ++ ++ def test_malformed_create_preserves_fixed_unknown_outcome(self): ++ for metadata in (None, [], {}, {'uid': 'created-uid', 'resourceVersion': None}): ++ with self.subTest(metadata=metadata): ++ self.m = lane._DeliveryMechanics('forgejo'); self.obj = None ++ self.create_mutate = lambda obj: {**obj, 'metadata': metadata} ++ with self.assertRaises(lane.Uncertain) as error: self.m.deliver() ++ self.assertEqual(str(error.exception), 'create_outcome_unverified') ++ self.assertTrue(self.m.pending) ++ ++ def test_unknown_create_and_delete_never_blindly_retry(self): ++ self.fail_create = True + with self.assertRaises(lane.Uncertain): self.m.deliver() + with self.assertRaises(lane.Refused): self.m.deliver() +- self.assertEqual(len(self.calls), 3) +- +- def test_ready_poll_is_bounded_and_created_provenance_retained(self): +- obj = deepcopy(self.obj) +- obj['status']['conditions'] = [] +- self.responses = [self.store, None, obj] + [obj] * lane.POLL_COUNT +- with self.assertRaises(lane.Uncertain) as error: self.m.deliver() +- self.assertEqual(error.exception.revision['uid'], 'uid-1') +- self.assertEqual(len(self.calls), lane.POLL_COUNT + 3) +- with self.assertRaises(lane.Refused): self.m.remove(error.exception.revision) +- +- def test_readiness_updates_resource_version(self): +- fresh = self.resource(self.m.external, rv='11') +- self.responses = [self.store, None, self.obj, fresh] +- receipt = self.m.deliver() +- self.assertEqual(receipt['after']['resource_version'], '11') +- +- def test_replacement_during_ready_not_adopted(self): +- other = self.resource(self.m.external, uid='other') +- self.responses = [self.store, None, self.obj, other] ++ self.fail_create = False; self.m = lane._DeliveryMechanics('forgejo') ++ delivered = self.m.deliver(); self.fail_delete = True ++ with self.assertRaises(lane.Uncertain) as error: self.m.remove(delivered['after']) ++ self.assertNotIn('PRIVATE', str(error.exception)) ++ with self.assertRaises(lane.Refused): self.m.remove(delivered['after']) ++ ++ def test_delete_race_uses_server_preconditions_and_preserves_replacement(self): ++ delivered = self.m.deliver() ++ def hook(args): ++ if 'delete' in args: self.obj['metadata']['uid'] = 'replacement' ++ self.hook = hook ++ with self.assertRaises(lane.Uncertain): self.m.remove(delivered['after']) ++ self.assertEqual(self.obj['metadata']['uid'], 'replacement') ++ ++ def test_status_rv_changed_before_remove_no_delete(self): ++ delivered = self.m.deliver(); self.obj['metadata']['resourceVersion'] = '11' ++ with self.assertRaises(lane.Uncertain): self.m.remove(delivered['after']) ++ self.assertFalse(any('delete' in args for args, _ in self.calls)) ++ ++ def test_fresh_process_and_forged_revision_never_delete(self): ++ delivered = self.m.deliver() ++ with self.assertRaises(lane.Refused): lane._DeliveryMechanics('forgejo').remove(delivered['after']) ++ with self.assertRaises(lane.Refused): self.m.remove({'uid': 'fake', 'resource_version': '10'}) ++ self.assertFalse(any('delete' in args for args, _ in self.calls)) ++ ++ def test_poll_rv_is_captured(self): ++ reads = [] ++ def hook(args): ++ if '/externalsecrets?' in args[-1] and self.obj is not None: ++ self.obj['metadata']['resourceVersion'] = '11' ++ reads.append(True) ++ self.hook = hook ++ delivered = self.m.deliver() ++ self.assertEqual(delivered['after']['resource_version'], '11') ++ self.assertTrue(reads) ++ ++ def test_unconfirmed_delete_is_bounded_and_preserves_reconciliation(self): ++ delivered = self.m.deliver() ++ prior = deepcopy(self.obj) ++ terminating = deepcopy(prior); terminating['metadata']['deletionTimestamp'] = 'now' ++ with patch.object(self.m, '_get', side_effect=[prior] + [terminating]*lane.POLL_COUNT): ++ with self.assertRaises(lane.Uncertain): self.m.remove(delivered['after']) ++ self.assertTrue(self.m.pending) ++ with self.assertRaises(lane.Refused): self.m.remove(delivered['after']) ++ ++ def test_lost_delete_after_server_removal_is_still_uncertain(self): ++ delivered = self.m.deliver(); self.fail_delete = True ++ def hook(args): ++ if 'delete' in args: self.obj = None ++ self.hook = hook ++ with self.assertRaises(lane.Uncertain): self.m.remove(delivered['after']) ++ self.assertIsNone(self.obj) ++ self.assertIsNotNone(self.m.created) ++ self.assertTrue(self.m.pending) ++ ++ def test_collection_foreign_object_cannot_be_adopted(self): ++ self.obj = self.resource(self.m.external); self.obj['metadata']['name'] = 'other' ++ with self.assertRaisesRegex(lane.Refused, 'collection_identity'): self.m.deliver() ++ self.assertFalse(any('create' in args for args, _ in self.calls)) ++ ++ def test_unknown_conflicting_or_unbounded_collection_never_absence(self): ++ for listing in ({}, {'kind': 'Status'}, {'apiVersion': 'external-secrets.io/v1', ++ 'kind': 'ExternalSecretList', 'metadata': {'continue': 'more'}, 'items': []}): ++ with self.subTest(listing=listing): ++ self.m = lane._DeliveryMechanics('forgejo'); self.force_list = listing ++ with self.assertRaises(lane.Refused): self.m.deliver() ++ self.assertFalse(any('create' in args for args, _ in self.calls)) ++ ++ def test_total_attempt_deadline_blocks_late_success(self): ++ self.clock_step = 6 + with self.assertRaises(lane.Uncertain): self.m.deliver() +- +- def test_drift_and_replacement_never_deleted(self): +- for field in ('uid', 'resourceVersion'): +- with self.subTest(field=field): +- self.m = lane._DeliveryMechanics('forgejo') +- receipt = self.create() +- changed = deepcopy(self.obj) +- changed['metadata'][field] = 'different' +- self.responses = [changed] +- count = len(self.calls) +- with self.assertRaises(lane.Uncertain): self.m.remove(receipt['after']) +- self.assertEqual(len(self.calls), count + 1) +- +- def test_delete_response_loss_and_conflict_uncertain(self): +- receipt = self.create() +- self.responses = [self.obj, 'error'] +- with self.assertRaises(lane.Uncertain): self.m.remove(receipt['after']) +- with self.assertRaises(lane.Refused): self.m.remove(receipt['after']) +- +- def test_delete_unconfirmed_or_replaced_never_repeated(self): +- for replacement in (False, True): +- with self.subTest(replacement=replacement): +- self.m = lane._DeliveryMechanics('forgejo') +- receipt = self.create() +- tail = [self.resource(self.m.external, uid='new')] if replacement else [self.obj] * lane.POLL_COUNT +- self.responses = [self.obj, {'kind': 'Status'}] + tail +- with self.assertRaises(lane.Uncertain): self.m.remove(receipt['after']) +- +- def test_no_caller_paths_or_injection(self): +- for client in ('a/../../secret', 'x;echo', 'a' * 49, 'A', None): +- with self.subTest(client=client): +- with self.assertRaises(lane.Refused): lane._DeliveryMechanics(client) +- self.assertEqual(self.calls, []) +- +- def test_forged_revision_and_fresh_process_have_no_delete_provenance(self): +- receipt = self.create() +- fresh = lane._DeliveryMechanics('forgejo') +- with self.assertRaises(lane.Refused): fresh.remove(receipt['after']) +- with self.assertRaises(lane.Refused): self.m.remove({'uid': 'uid-1', 'resource_version': '11'}) +- self.assertFalse(any('delete' in a for a,k in self.calls)) +- +- def test_malformed_create_or_poll_never_returns_success(self): +- for malformed in ({}, {'kind': 'Status'}): +- with self.subTest(malformed=malformed): +- self.m = lane._DeliveryMechanics('forgejo') +- self.responses = [self.store, None, malformed] +- with self.assertRaises(lane.Uncertain): self.m.deliver() +- +- def test_poll_timeout_contains_provider_details(self): +- self.responses = [self.store, None, self.obj, +- subprocess.TimeoutExpired('PRIVATE PROVIDER', 60, output='PRIVATE VALUE')] +- with self.assertRaises(lane.Uncertain) as error: self.m.deliver() +- self.assertNotIn('PRIVATE', str(error.exception)) +- +- +-if __name__ == '__main__': +- unittest.main() ++ self.assertLessEqual(len(self.calls), 6) ++ ++ def test_no_secret_or_store_mutation_and_factory_refuses(self): ++ self.m.deliver() ++ for args, payload in self.calls: ++ self.assertFalse(set(args) & {'secret', 'secrets'}) ++ if payload and 'create' in args: self.assertEqual(payload['kind'], 'ExternalSecret') ++ before = len(self.calls) ++ with self.assertRaises(lane.Refused): lane.production_bindings(authenticated=True) ++ self.assertEqual(len(self.calls), before) ++ ++ ++if __name__ == '__main__': unittest.main() diff --git a/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/railiance-platform.patch b/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/railiance-platform.patch new file mode 100644 index 00000000..9c21da2e --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/railiance-platform.patch @@ -0,0 +1,993 @@ +--- a/docs/keycape-issuer-eso-contract.md ++++ b/docs/keycape-issuer-eso-contract.md +@@ -66,3 +66,110 @@ + RPF-1.3-C1 and RPF-1.3-T4 in the frozen owner review. The scaffold's historical + audience field placement differs from the issuer draft; this candidate uses + the issuer draft's serviceAccountRef.audiences, not a claim of installed support. ++ ++## Prepared delivery mechanics (still unavailable as a production lane) ++ ++`scripts/keycape_issuer_delivery.py` now contains concrete command mechanics for ++the existing RPF-1.3-T4 obligation. It reuses `keycape_issuer_eso.documents()` and ++`require_fixed_store()` with the byte-bounded `bounded_public_command.command()` ++transport. Only the fixed public ESO resources are read using raw v1 paths. ++A successful field-selected collection response proves observed absence. ++The helper creates only the ExternalSecret, compares its fixed spec with only ++the three pinned defaults normalized, and polls at most ten times for a recent ++current-metadata controller report with stable UID. Request, command and total ++attempt deadlines apply as described below. Controller resourceVersion updates ++during successful readiness are captured. ++ ++Removal is permitted only for that instance's verified case-created revision, ++rechecks spec/UID/resourceVersion, uses Kubernetes DeleteOptions with both UID ++and resourceVersion preconditions, and confirms absence with bounded polling. ++Existing matching objects return changed=false and are never rollback candidates. ++Creation response loss, readiness failure after creation, replacement, delete ++conflict or unconfirmed deletion raises a fixed uncertainty diagnostic and keeps ++the last verified revision when known. No retry adopts an uncertain create; no ++replacement is deleted. An unknown create outcome requires owner reconciliation ++using the fixed resource name even when no UID could be recovered. Target Secret ++garbage collection by ESO/Kubernetes is outside this helper; it never reads or ++directly deletes Secret objects. There is no claim that deletion proves target ++Secret disappearance. ++ ++The callable `production_bindings()` **always refuses before any command**. ++The private mechanics use empty environment and `/dev/null` kubeconfig and ++therefore cannot borrow the caller's native session. They are source preparation, ++not an adapter/evidence authority. Mechanics tests intercept command dispatch; ++runner tests launch only synthetic local children. Neither provides production ++callbacks or attestations. A real lane requires ++an owner-reviewed authenticated human aal2 session, acknowledged case revision ++and caller binding, scoped Kubernetes credentials, one-shot durable admission ++journaling and authenticated evidence publication/verification, plus real ++per-attempt session revocation and recovery. None exists for this lane at the ++pinned source. The generic task_execution_journal module is real storage but ++explicitly does not admit native work; it is not silently repurposed here. ++The mint broker's case checker and OpenBao lookup-self are real for its existing ++mint lane, not delivery authentication. The attended custody-projection SSH ++transport and credential-change evidence writer likewise confer no delivery ++lane authority. No unsigned receipt or `True` oracle replaces these facilities. ++ ++Consequently RPF-1.3-T4 remains open for real capability binding and owner review; ++this patch prepares concrete ESO command/state mechanics and regressions. ++No ESO role, policy, Store, admission service, grant, catalog acceptance, native ++permission or owner descriptor acceptance is created or inferred. ++ ++ ++## r2 source mechanics: current controller report and containment ++ ++This preparation is not production admission or owner acceptance. The factory ++continues to refuse absent actual case/client authorization, scoped caller and ++session, owner exclusion/admission, durable admission/recovery, authenticated ++evidence and per-attempt revocation. Empty transport environment and /dev/null ++kubeconfig deliberately remain unusable for ambient production access. ++ ++The pinned ESO v0.16.1 controller (commit ++8d037d197c88eeb9c4fe489e906c9cb3ab9ee6af) sets syncedResourceVersion to ++its metadata generation plus '-' plus the MD5 of its Go-formatted annotations ++and labels struct. That token is not Kubernetes resourceVersion or authenticated ++evidence. r2 compares the exact token, a unique Ready=True/SecretSynced condition ++with the controller's successful 'secret synced' message, and a valid refreshTime ++no older than the fixed five-minute refresh interval and not in the future. ++The controller sets refreshTime to reconcile start. Its Retain/missing-provider ++branch also sets Ready but uses a different message; r2 refuses that branch. ++These checks prove a recent controller report for current object metadata under ++the pinned implementation, not present Secret contents, provider availability, ++a live credential outcome or runtime authentication. Deployed-version matching, ++trusted API/status custody and a sane local clock remain production prerequisites. ++ ++Store status has no generation binding. r2 accepts only generation 1 with the ++fixed spec and unique Valid/'store validated' Ready condition, and records its ++UID/resourceVersion. A later generation is refused with ++store_ready_generation_unverifiable. Repeated exact Store observations before ++create, during readiness and before a receipt detect observed drift; they cannot ++atomically bind create or ESO's Store lookup to a Store revision. A wholly ++interleaved change/restoration remains invisible. Actual owner-controlled ++exclusion/admission is missing and remains a production blocker. Cleanup of a ++verified ExternalSecret uses its own UID/RV preconditions and does not depend on ++Store availability. ++ ++All reads use explicit raw v1 paths. The fixed ExternalSecret collection query ++includes an exact metadata.name field selector and limit 2 to obtain structured ++successful absence. Its response must be a v1 ExternalSecretList with at most ++one fixed namespace/name item and no continuation. Errors are never absence. ++This needs field-selected ExternalSecret list access in any future real session; ++no such access is granted by this code. No Kubernetes Secret is read or listed. ++ ++The new POSIX runner bounds combined stdout+stderr during collection to 262144 ++bytes, discards stderr, uses a six-second maximum command deadline, sends input ++without a pipe deadlock, and kills the process group before reaping its leader ++on success or failure. Wait/reap has a separate one-second cleanup budget; ++unconfirmed cleanup is refusal. Operations have a 30-second monotonic deadline ++and cannot return success once it has elapsed. Descendant tests verify ordinary ++same-group children with held and closed pipes. This is process-group cleanup, ++not a sandbox guarantee against a program escaping its group/session. Privileged ++runtime isolation is not established by this runner or its tests. ++ ++The three actual v1 remoteRef defaults (conversionStrategy=Default, ++decodingStrategy=None, metadataPolicy=None) compare equally when omitted or ++explicit. Other spec changes remain conflicts. The metadata-only contract helper ++is not itself the runtime freshness gate; r2 delivery adds the checks above. ++The fixed template bytes remain unchanged. Unknown mutation responses preserve ++pending reconciliation, including any valid returned UID/RV, and never gain a ++blind delete/retry path. Local one-shot state remains non-durable. +--- /dev/null ++++ b/scripts/bounded_public_command.py +@@ -0,0 +1,107 @@ ++"""POSIX byte/time bounded subprocess collection; no credential/session authority.""" ++from __future__ import annotations ++ ++import json ++import os ++import selectors ++import signal ++import subprocess ++import time ++ ++MAX_OUTPUT = 262144 # combined stdout + stderr; stderr is counted, never retained ++MAX_INPUT = 65536 ++CLEANUP_TIMEOUT = 1 ++ ++ ++class CommandRefused(Exception): ++ """Only fixed diagnostics escape this transport.""" ++ ++ ++def command(argv, *, payload=None, timeout=6): ++ if not isinstance(timeout, (float, int)) or not 0 < timeout <= 6: ++ raise CommandRefused('command_budget_invalid') ++ try: ++ body = b'' if payload is None else json.dumps(payload).encode() ++ if len(body) > MAX_INPUT: ++ raise CommandRefused('command_input_bound') ++ except (ValueError, TypeError): ++ raise CommandRefused('command_input_invalid') from None ++ process = None ++ selector = selectors.DefaultSelector() ++ output = bytearray() ++ total = 0 ++ offset = 0 ++ failure = None ++ code = None ++ deadline = time.monotonic() + timeout ++ try: ++ process = subprocess.Popen(argv, stdin=subprocess.PIPE, ++ stdout=subprocess.PIPE, stderr=subprocess.PIPE, env={}, ++ start_new_session=True, close_fds=True, shell=False) ++ for stream in (process.stdout, process.stderr, process.stdin): ++ os.set_blocking(stream.fileno(), False) ++ selector.register(process.stdout, selectors.EVENT_READ, 'stdout') ++ selector.register(process.stderr, selectors.EVENT_READ, 'stderr') ++ if body: ++ selector.register(process.stdin, selectors.EVENT_WRITE, 'stdin') ++ else: ++ process.stdin.close() ++ while True: ++ remaining = deadline - time.monotonic() ++ if remaining <= 0: ++ raise CommandRefused('command_timeout') ++ # WNOWAIT deliberately leaves the leader unreaped so its PID/PGID ++ # cannot be recycled before final process-group cleanup. ++ state = os.waitid(os.P_PID, process.pid, ++ os.WEXITED | os.WNOHANG | os.WNOWAIT) ++ if state is not None and not selector.get_map(): ++ break ++ for key, _ in selector.select(min(remaining, .05)): ++ stream = key.fileobj ++ if key.data == 'stdin': ++ try: ++ offset += os.write(stream.fileno(), body[offset:offset + 4096]) ++ except BlockingIOError: ++ continue ++ if offset == len(body): ++ selector.unregister(stream); stream.close() ++ continue ++ try: ++ block = os.read(stream.fileno(), 4096) ++ except BlockingIOError: ++ continue ++ if not block: ++ selector.unregister(stream); stream.close() ++ continue ++ total += len(block) ++ if total > MAX_OUTPUT: ++ raise CommandRefused('command_output_bound') ++ if key.data == 'stdout': ++ output.extend(block) ++ except CommandRefused as error: ++ failure = str(error) ++ except Exception: ++ failure = 'command_failed' ++ finally: ++ selector.close() ++ if process is not None: ++ # Also kills descendants that kept pipes open or closed all pipes. ++ # The leader has not been reaped, avoiding PGID reuse races. ++ try: ++ os.killpg(process.pid, signal.SIGKILL) ++ except ProcessLookupError: ++ pass ++ except Exception: ++ failure = 'command_cleanup_unconfirmed' ++ for stream in (process.stdin, process.stdout, process.stderr): ++ if stream is not None: ++ stream.close() ++ try: ++ code = process.wait(timeout=CLEANUP_TIMEOUT) ++ except Exception: ++ failure = 'command_cleanup_unconfirmed' ++ if failure is not None: ++ raise CommandRefused(failure) from None ++ if code != 0: ++ raise CommandRefused('command_failed') from None ++ return bytes(output) +--- /dev/null ++++ b/scripts/keycape_issuer_delivery.py +@@ -0,0 +1,284 @@ ++"""RPF-1.3-T4 source mechanics; production admission deliberately unavailable.""" ++from __future__ import annotations ++ ++from copy import deepcopy ++from datetime import datetime, timezone ++import hashlib ++import json ++import re ++import time ++ ++from keycape_issuer_eso import documents, require_fixed_store, Refused, canonical_external_spec ++from bounded_public_command import command, CommandRefused ++ ++POLL_COUNT = 10 ++POLL_DELAY = 1 ++ATTEMPT_TIMEOUT = 30 ++API = '/apis/external-secrets.io/v1' ++ ++ ++class Uncertain(Refused): ++ def __init__(self, reason, revision=None): ++ super().__init__(reason) ++ self.revision = deepcopy(revision) ++ ++ ++def production_bindings(*args, **kwargs): ++ raise Refused('delivery_admission_exclusion_session_journal_evidence_revocation_unavailable') ++ ++ ++def _metadata(obj): ++ if not isinstance(obj, dict) or not isinstance(obj.get('metadata'), dict): ++ raise Refused('public_metadata_invalid') ++ return obj['metadata'] ++ ++ ++def _revision(obj): ++ meta = _metadata(obj) ++ result = {'uid': meta.get('uid'), 'resource_version': meta.get('resourceVersion')} ++ if any(not isinstance(x, str) or not re.fullmatch(r'[A-Za-z0-9._:-]{1,192}', x) ++ for x in result.values()): ++ raise Refused('public_revision_invalid') ++ return result ++ ++ ++def _generation(obj): ++ value = _metadata(obj).get('generation') ++ if type(value) is not int or not 1 <= value <= 2**63 - 1: ++ raise Refused('public_generation_invalid') ++ return value ++ ++ ++def _synced_version(obj): ++ """ESO v0.16.1 util.GetResourceVersion + utils.ObjectHash, not metadata RV. ++ ++ Go fmt %+v on the actual two-field struct uses sorted string maps, raw ++ keys/values, and map[] for both nil and empty maps. Tests compare this ++ implementation to a standalone Go extraction of that exact upstream code. ++ This MD5 is the controller's revision token, never authentication evidence. ++ """ ++ meta = _metadata(obj) ++ maps = [] ++ for name in ('annotations', 'labels'): ++ value = meta.get(name) ++ if value is None: ++ value = {} ++ if not isinstance(value, dict) or any( ++ not isinstance(k, str) or not isinstance(v, str) for k, v in value.items()): ++ raise Refused('public_metadata_map_invalid') ++ try: ++ textual = 'map[' + ' '.join(k + ':' + value[k] for k in sorted(value)) + ']' ++ textual.encode('utf-8', errors='strict') ++ except UnicodeError: ++ raise Refused('public_metadata_map_invalid') from None ++ maps.append(name + ':' + textual) ++ hashed = hashlib.md5(('{' + ' '.join(maps) + '}').encode(), usedforsecurity=False).hexdigest() ++ return str(_generation(obj)) + '-' + hashed ++ ++ ++def _ready(obj): ++ status = obj.get('status') ++ if not isinstance(status, dict): ++ return False ++ conditions = status.get('conditions') ++ if not isinstance(conditions, list) or any(not isinstance(c, dict) for c in conditions): ++ return False ++ ready = [c for c in conditions if c.get('type') == 'Ready'] ++ if len(ready) != 1 or any(ready[0].get(k) != v for k, v in { ++ 'status': 'True', 'reason': 'SecretSynced', 'message': 'secret synced'}.items()): ++ return False ++ if status.get('syncedResourceVersion') != _synced_version(obj): ++ return False ++ stamp = status.get('refreshTime') ++ if not isinstance(stamp, str) or not re.fullmatch( ++ r'\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d(?:\.\d{1,9})?(?:Z|[+-]\d\d:\d\d)', stamp): ++ return False ++ try: ++ refreshed = datetime.fromisoformat(stamp.replace('Z', '+00:00')) ++ age = (datetime.now(timezone.utc) - refreshed).total_seconds() ++ return 0 <= age <= 300 ++ except (ValueError, OverflowError): ++ return False ++ ++ ++class _DeliveryMechanics: ++ """No authority boundary: injected calls in tests, unusable ambient transport. ++ ++ Store observations detect drift; they cannot exclude an ABA race between ++ observations or bind ESO's store lookup atomically. Production needs real ++ owner exclusion/admission in addition to authenticated caller facilities. ++ """ ++ def __init__(self, client_id): ++ self.client_id = client_id ++ self.store, self.external = documents(client_id) ++ self.created = None ++ self.pending = False ++ self.used = False ++ self.deadline = None ++ ++ def _call(self, args, payload=None): ++ try: ++ remaining = self.deadline - time.monotonic() ++ if remaining <= 0: ++ raise Refused('attempt_deadline') ++ raw = command(['kubectl', '--kubeconfig=/dev/null', ++ '--request-timeout=5s', *args], ++ payload=payload, timeout=min(6, remaining)) ++ if time.monotonic() >= self.deadline: ++ raise Refused('attempt_deadline') ++ # Runner enforces the same bound during collection, not just here. ++ if not isinstance(raw, bytes) or len(raw) > 262144: ++ raise Refused('public_response_bound') ++ if not raw.strip(): ++ return None ++ value = json.loads(raw) ++ if not isinstance(value, dict): ++ raise Refused('public_response_shape') ++ return value ++ except Refused: ++ raise ++ except Exception: ++ raise Refused('public_command_failed') from None ++ ++ def _get(self, store=False): ++ if store: ++ return self._call(['get', '--raw', API + '/clustersecretstores/' + self.store['metadata']['name']]) ++ # Raw GET fixes API version and avoids kubectl mapper fallback. A fixed ++ # field-selected collection provides structured successful absence; ++ # stderr/nonzero is never interpreted as NotFound. No Secret is listed. ++ uri = (API + '/namespaces/sso/externalsecrets?fieldSelector=metadata.name%3D' ++ + self.external['metadata']['name'] + '&limit=2') ++ listing = self._call(['get', '--raw', uri]) ++ if (not isinstance(listing, dict) or listing.get('apiVersion') != 'external-secrets.io/v1' ++ or listing.get('kind') != 'ExternalSecretList' ++ or not isinstance(listing.get('metadata'), dict) ++ or listing['metadata'].get('continue') not in (None, '') ++ or not isinstance(listing.get('items'), list) or len(listing['items']) > 1): ++ raise Refused('public_collection_invalid') ++ if not listing['items']: ++ return None ++ value = listing['items'][0] ++ meta = _metadata(value) ++ if (value.get('apiVersion') != 'external-secrets.io/v1' ++ or value.get('kind') != 'ExternalSecret' ++ or meta.get('name') != self.external['metadata']['name'] ++ or meta.get('namespace') != 'sso'): ++ raise Refused('public_collection_identity_invalid') ++ return value ++ ++ def _store_revision(self, sealed=None): ++ store = self._get(store=True) ++ require_fixed_store(store, self.client_id) ++ if _metadata(store).get('deletionTimestamp'): ++ raise Refused('store_terminating') ++ revision = _revision(store) ++ # Actual Store status has no generation binding. Generation 1 excludes ++ # a prior spec update under Kubernetes generation semantics. For any ++ # later generation Ready cannot establish current-spec validation. ++ if _generation(store) != 1: ++ raise Refused('store_ready_generation_unverifiable') ++ conditions = store.get('status', {}).get('conditions', []) ++ ready = [c for c in conditions if isinstance(c, dict) and c.get('type') == 'Ready'] ++ if len(ready) != 1 or ready[0].get('reason') != 'Valid' or ready[0].get('message') != 'store validated': ++ raise Refused('store_validation_unverified') ++ if sealed is not None and sealed != revision: ++ raise Refused('store_revision_changed') ++ return revision ++ ++ def _match(self, obj): ++ metadata = _metadata(obj) ++ if (obj.get('apiVersion') != self.external['apiVersion'] ++ or obj.get('kind') != 'ExternalSecret' ++ or metadata.get('name') != self.external['metadata']['name'] ++ or metadata.get('namespace') != 'sso' or metadata.get('deletionTimestamp') ++ or canonical_external_spec(obj.get('spec')) != canonical_external_spec(self.external['spec'])): ++ raise Refused('delivery_conflict') ++ _generation(obj) ++ return _revision(obj) ++ ++ def _receipt(self, operation, before, after): ++ return {'schema': 'netkingdom.deliver-secret.v1', 'operation': operation, ++ 'namespace': 'sso', 'name': self.external['metadata']['name'], ++ 'custody_reference': 'platform/workloads/keycape/' + self.client_id, ++ 'key_names': ['client-secret'], 'values_emitted': False, ++ 'changed': before is None if operation == 'deliver' else True, ++ 'before': before, 'after': after} ++ ++ def deliver(self): ++ if self.used or self.pending or self.created is not None: ++ raise Refused('attempt_already_used_or_unreconciled') ++ self.used = True # local one-shot only, never a durable admission journal ++ self.deadline = time.monotonic() + ATTEMPT_TIMEOUT ++ sealed_store = self._store_revision() ++ old = self._get() ++ before = self._match(old) if old is not None else None ++ uid = before['uid'] if before else None ++ self._store_revision(sealed_store) ++ if old is None: ++ self.pending = True ++ try: ++ created = self._call(['-n', 'sso', 'create', '-f', '-', '-o', 'json'], self.external) ++ # Retain response identity even if admission changed its spec. ++ # Pending prevents deletion until exact verified delivery. ++ self.created = _revision(created) ++ self._match(created) ++ uid = self.created['uid'] ++ except Exception: ++ raise Uncertain('create_outcome_unverified', self.created) from None ++ try: ++ for index in range(POLL_COUNT): ++ self._store_revision(sealed_store) ++ current = self._get() ++ after = self._match(current) ++ if after['uid'] != uid: ++ raise Refused('delivery_replaced') ++ if _ready(current): ++ self._store_revision(sealed_store) ++ if self.created is not None: ++ self.created = after ++ self.pending = False ++ return self._receipt('deliver', before, after) ++ if index + 1 < POLL_COUNT: ++ remaining = self.deadline - time.monotonic() ++ if remaining <= 0: ++ raise Refused('attempt_deadline') ++ time.sleep(min(POLL_DELAY, remaining)) ++ raise Refused('delivery_current_ready_timeout') ++ except Exception: ++ if self.pending or self.created is not None: ++ raise Uncertain('created_delivery_requires_reconciliation', self.created) from None ++ raise Refused('delivery_verification_failed') from None ++ ++ def remove(self, revision): ++ if self.pending or self.created is None or revision != self.created: ++ raise Refused('no_verified_case_creation') ++ self.deadline = time.monotonic() + ATTEMPT_TIMEOUT ++ try: ++ if self._match(self._get()) != self.created: ++ raise Refused('created_revision_changed') ++ except Exception: ++ raise Uncertain('created_revision_changed', self.created) from None ++ uri = API + '/namespaces/sso/externalsecrets/' + self.external['metadata']['name'] ++ options = {'apiVersion': 'v1', 'kind': 'DeleteOptions', ++ 'preconditions': {'uid': self.created['uid'], ++ 'resourceVersion': self.created['resource_version']}} ++ prior = deepcopy(self.created) ++ self.pending = True ++ try: ++ self._call(['delete', '--raw', uri, '-f', '-'], options) ++ for index in range(POLL_COUNT): ++ current = self._get() ++ if current is None: ++ self.created = None ++ self.pending = False ++ return self._receipt('remove', prior, None) ++ if _revision(current)['uid'] != prior['uid']: ++ raise Refused('replacement_preserved') ++ if index + 1 < POLL_COUNT: ++ remaining = self.deadline - time.monotonic() ++ if remaining <= 0: ++ raise Refused('attempt_deadline') ++ time.sleep(min(POLL_DELAY, remaining)) ++ raise Refused('delete_unconfirmed') ++ except Exception: ++ raise Uncertain('cleanup_requires_reconciliation', prior) from None +--- a/scripts/keycape_issuer_eso.py ++++ b/scripts/keycape_issuer_eso.py +@@ -37,6 +37,23 @@ + return deepcopy(documents(client_id)[1]) + + ++def canonical_external_spec(spec): ++ """Normalize only the three pinned ESO v0.16.1 remoteRef defaults. ++ ++ Omitted and explicit defaults are equivalent; all other fields remain exact. ++ This says nothing about freshness, admission authority or Store continuity. ++ """ ++ value = deepcopy(spec) ++ if isinstance(value, dict) and isinstance(value.get('data'), list): ++ for item in value['data']: ++ if isinstance(item, dict) and isinstance(item.get('remoteRef'), dict): ++ for name, default in (('conversionStrategy', 'Default'), ++ ('decodingStrategy', 'None'), ++ ('metadataPolicy', 'None')): ++ item['remoteRef'].setdefault(name, default) ++ return value ++ ++ + def _matching_public_resource(observed: dict, expected: dict) -> None: + if not isinstance(observed, dict): + raise Refused('public resource observation required') +@@ -50,7 +67,10 @@ + or observed.get('kind') != expected['kind'] + or metadata.get('name') != expected['metadata']['name'] + or not namespace_matches +- or observed.get('spec') != expected['spec']): ++ or (canonical_external_spec(observed.get('spec')) ++ != canonical_external_spec(expected['spec']) ++ if expected['kind'] == 'ExternalSecret' ++ else observed.get('spec') != expected['spec'])): + raise Refused('fixed issuer resource differs') + status = observed.get('status') + conditions = status.get('conditions', []) if isinstance(status, dict) else [] +--- /dev/null ++++ b/tests/eso-hash-vectors.json +@@ -0,0 +1,57 @@ ++[ ++ { ++ "metadata": { ++ "generation": 1 ++ }, ++ "expected": "1-2f2c636d06132f67992c15a1daeb0a5f" ++ }, ++ { ++ "metadata": { ++ "generation": 2, ++ "annotations": {}, ++ "labels": {} ++ }, ++ "expected": "2-2f2c636d06132f67992c15a1daeb0a5f" ++ }, ++ { ++ "metadata": { ++ "generation": 3, ++ "annotations": { ++ "b": "2", ++ "a": "1" ++ }, ++ "labels": { ++ "z": "last", ++ "c": "first" ++ } ++ }, ++ "expected": "3-7ad4f86f5aba928cde01913c7b9d962a" ++ }, ++ { ++ "metadata": { ++ "generation": 4, ++ "annotations": { ++ "a:b": "c d", ++ "a b": "[]:%+v" ++ }, ++ "labels": { ++ "newline\n": "tab\t" ++ } ++ }, ++ "expected": "4-48e35b520c38b2944d461645440cc3cd" ++ }, ++ { ++ "metadata": { ++ "generation": 9223372036854775807, ++ "annotations": { ++ "é": "好", ++ "🦊": "😀" ++ }, ++ "labels": { ++ "": "", ++ "null": "\u0000" ++ } ++ }, ++ "expected": "9223372036854775807-da6b2f7c16875cb96b3a00d83378eda3" ++ } ++] +--- /dev/null ++++ b/tests/test_bounded_public_command.py +@@ -0,0 +1,95 @@ ++import os ++from pathlib import Path ++import sys ++import tempfile ++import time ++import unittest ++from unittest.mock import patch ++ ++sys.path.insert(0, str(Path(__file__).resolve().parents[1] / 'scripts')) ++import bounded_public_command as runner ++ ++# Every launched fixture denies socket creation in its own interpreter as well ++# as the parent. They only perform controlled local stdout/stderr/process work. ++PREFIX = "import socket; socket.socket=lambda *a,**k: (_ for _ in ()).throw(AssertionError('network forbidden')); " ++ ++ ++class BoundedCommand(unittest.TestCase): ++ def setUp(self): ++ p = patch.dict(os.environ, {}, clear=True); p.start(); self.addCleanup(p.stop) ++ p = patch('socket.socket', side_effect=AssertionError('network forbidden')) ++ p.start(); self.addCleanup(p.stop) ++ ++ def call(self, code, timeout=2, payload=None): ++ return runner.command(['/usr/bin/python3', '-c', PREFIX + code], timeout=timeout, payload=payload) ++ ++ def test_empty_environment_and_input_roundtrip(self): ++ raw = self.call("import os,sys,json; b=sys.stdin.buffer.read(); print(json.dumps({'keys':sorted(os.environ),'body':json.loads(b)}))", payload={'fixed': 'metadata'}) ++ import json ++ response = json.loads(raw) ++ # CPython may inject LC_CTYPE during startup even though exec env={}; ++ # neither HOME nor credential variables are inherited. ++ self.assertFalse(set(response['keys']) - {'LC_CTYPE'}) ++ self.assertEqual(response['body'], {'fixed': 'metadata'}) ++ ++ def test_stdout_flood_stopped_with_fixed_error(self): ++ with self.assertRaisesRegex(runner.CommandRefused, '^command_output_bound$'): ++ self.call("import os; [os.write(1,b'PRIVATE'*4096) for _ in range(1000)]") ++ ++ def test_stderr_flood_stopped_with_fixed_error(self): ++ with self.assertRaisesRegex(runner.CommandRefused, '^command_output_bound$'): ++ self.call("import os; [os.write(2,b'PRIVATE'*4096) for _ in range(1000)]") ++ ++ def test_combined_cap_not_independent_per_stream(self): ++ with self.assertRaisesRegex(runner.CommandRefused, '^command_output_bound$'): ++ self.call("import os; os.write(1,b'x'*140000); os.write(2,b'y'*140000)") ++ ++ def test_error_output_never_escapes(self): ++ with self.assertRaisesRegex(runner.CommandRefused, '^command_failed$') as error: ++ self.call("import sys; print('PRIVATE'); print('PRIVATE',file=sys.stderr); sys.exit(7)") ++ self.assertNotIn('PRIVATE', str(error.exception)) ++ ++ def test_sleep_timeout_is_real_and_bounded(self): ++ start = time.monotonic() ++ with self.assertRaisesRegex(runner.CommandRefused, '^command_timeout$'): ++ self.call('import time; time.sleep(60)', timeout=.15) ++ self.assertLess(time.monotonic() - start, 2) ++ ++ def test_descendant_holding_pipes_is_killed(self): ++ with tempfile.TemporaryDirectory(prefix='delivery-runner-') as scratch: ++ pidfile = Path(scratch) / 'descendant' ++ code = ("import os,time; pid=os.fork(); " ++ "(time.sleep(60) if pid==0 else " ++ f"(open({str(pidfile)!r},'w').write(str(pid)),os._exit(0)))") ++ with self.assertRaisesRegex(runner.CommandRefused, '^command_timeout$'): ++ self.call(code, timeout=.2) ++ self.assertTrue(pidfile.exists()) ++ pid = pidfile.read_text() ++ status = Path('/proc') / pid / 'stat' ++ for _ in range(20): ++ if not status.exists() or status.read_text().split()[2] == 'Z': break ++ time.sleep(.01) ++ if status.exists(): self.assertEqual(status.read_text().split()[2], 'Z') ++ ++ def test_descendant_with_closed_pipes_is_killed_on_success(self): ++ with tempfile.TemporaryDirectory(prefix='delivery-runner-') as scratch: ++ pidfile = Path(scratch) / 'descendant' ++ code = ("import os,time; pid=os.fork(); " ++ "(os.close(0),os.close(1),os.close(2),time.sleep(60)) if pid==0 else " ++ f"(open({str(pidfile)!r},'w').write(str(pid)),print('ok'))") ++ self.assertEqual(self.call(code), b'ok\n') ++ status = Path('/proc') / pidfile.read_text() / 'stat' ++ # Kill delivery may race the proc-state observation; wait boundedly. ++ for _ in range(20): ++ if not status.exists() or status.read_text().split()[2] == 'Z': break ++ time.sleep(.01) ++ if status.exists(): self.assertEqual(status.read_text().split()[2], 'Z') ++ ++ def test_input_bound_and_spawn_failure_fixed(self): ++ with self.assertRaisesRegex(runner.CommandRefused, '^command_input_bound$'): ++ self.call('pass', payload={'data': 'x'*65536}) ++ with self.assertRaisesRegex(runner.CommandRefused, '^command_failed$'): ++ runner.command(['/nonexistent-fixture-binary'], timeout=.1) ++ ++ ++if __name__ == '__main__': unittest.main() +--- /dev/null ++++ b/tests/test_keycape_issuer_delivery.py +@@ -0,0 +1,284 @@ ++from copy import deepcopy ++from datetime import datetime, timedelta, timezone ++import json ++import os ++from pathlib import Path ++import sys ++import unittest ++from unittest.mock import patch ++ ++sys.path.insert(0, str(Path(__file__).resolve().parents[1] / 'scripts')) ++import keycape_issuer_delivery as lane ++from bounded_public_command import CommandRefused ++ ++ ++class Mechanics(unittest.TestCase): ++ def setUp(self): ++ p = patch.dict(os.environ, {}, clear=True); p.start(); self.addCleanup(p.stop) ++ p = patch('socket.socket', side_effect=AssertionError('network forbidden')) ++ p.start(); self.addCleanup(p.stop) ++ self.m = lane._DeliveryMechanics('forgejo') ++ self.store = self.resource(self.m.store, store=True) ++ self.obj = None ++ self.calls = [] ++ self.store_reads = 0 ++ self.hook = lambda args: None ++ self.fail_create = False ++ self.fail_delete = False ++ self.create_mutate = lambda obj: obj ++ self.force_list = None ++ self.poll_object = None ++ self.clock = 0 ++ self.clock_step = 0 ++ p = patch.object(lane, 'command', side_effect=self.command) ++ p.start(); self.addCleanup(p.stop) ++ p = patch.object(lane.time, 'sleep'); p.start(); self.addCleanup(p.stop) ++ p = patch.object(lane.time, 'monotonic', side_effect=lambda: self.clock) ++ p.start(); self.addCleanup(p.stop) ++ ++ def resource(self, doc, store=False): ++ value = deepcopy(doc) ++ value['metadata'].update(uid='store-uid' if store else 'created-uid', resourceVersion='10', generation=1) ++ if store: ++ value['status'] = {'conditions': [{'type': 'Ready', 'status': 'True', ++ 'reason': 'Valid', 'message': 'store validated'}]} ++ else: ++ value['spec'] = lane.canonical_external_spec(value['spec']) ++ value['status'] = {'conditions': [{'type': 'Ready', 'status': 'True', ++ 'reason': 'SecretSynced', 'message': 'secret synced'}], ++ 'syncedResourceVersion': lane._synced_version(value), ++ 'refreshTime': datetime.now(timezone.utc).isoformat()} ++ return value ++ ++ def command(self, args, *, payload=None, timeout=6): ++ self.assertEqual(args[:3], ['kubectl', '--kubeconfig=/dev/null', '--request-timeout=5s']) ++ self.assertGreater(timeout, 0); self.assertLessEqual(timeout, 6) ++ self.calls.append((deepcopy(args), deepcopy(payload))) ++ self.clock += self.clock_step ++ self.hook(args) ++ if 'create' in args: ++ if self.fail_create: ++ raise CommandRefused('PRIVATE PROVIDER VALUE') ++ self.obj = self.create_mutate(self.resource(self.m.external)) ++ value = self.obj ++ elif 'delete' in args: ++ if self.fail_delete: ++ raise CommandRefused('PRIVATE PROVIDER VALUE') ++ revision = lane._revision(self.obj) ++ if payload['preconditions'] != {'uid': revision['uid'], 'resourceVersion': revision['resource_version']}: ++ raise CommandRefused('conditional conflict fixture') ++ self.obj = None ++ value = {'kind': 'Status', 'status': 'Success'} ++ elif '/clustersecretstores/' in args[-1]: ++ self.store_reads += 1 ++ value = self.store ++ else: ++ self.assertIn('get', args); self.assertIn('--raw', args) ++ self.assertEqual(args[-1], lane.API + '/namespaces/sso/externalsecrets?fieldSelector=metadata.name%3Dkeycape-forgejo-client&limit=2') ++ value = self.force_list if self.force_list is not None else { ++ 'apiVersion': 'external-secrets.io/v1', 'kind': 'ExternalSecretList', ++ 'metadata': {}, 'items': [] if self.obj is None else [self.poll_object or self.obj]} ++ return json.dumps(deepcopy(value)).encode() ++ ++ def test_actual_defaults_create_current_ready_and_conditional_delete(self): ++ delivered = self.m.deliver() ++ self.assertTrue(delivered['changed']); self.assertIsNone(delivered['before']) ++ self.assertGreaterEqual(self.store_reads, 4) ++ removed = self.m.remove(delivered['after']) ++ self.assertEqual(removed['after'], None) ++ args, payload = next(c for c in self.calls if 'delete' in c[0]) ++ self.assertEqual(args[5], lane.API + '/namespaces/sso/externalsecrets/keycape-forgejo-client') ++ self.assertEqual(payload['preconditions'], {'uid': 'created-uid', 'resourceVersion': '10'}) ++ ++ def test_existing_no_change_cannot_be_deleted_or_reused(self): ++ self.obj = self.resource(self.m.external) ++ delivered = self.m.deliver() ++ self.assertFalse(delivered['changed']) ++ with self.assertRaises(lane.Refused): self.m.remove(delivered['after']) ++ with self.assertRaises(lane.Refused): self.m.deliver() ++ self.assertFalse(any('create' in args or 'delete' in args for args, _ in self.calls)) ++ ++ def test_stale_current_spec_and_metadata_hash_never_ready(self): ++ obj = self.resource(self.m.external) ++ for field, value in [('generation', 2), ('labels', {'new': 'metadata'}), ('annotations', {'new': 'metadata'})]: ++ with self.subTest(field=field): ++ changed = deepcopy(obj); changed['metadata'][field] = value ++ self.assertFalse(lane._ready(changed)) ++ ++ def test_exact_go_upstream_revision_vectors(self): ++ vectors = json.loads((Path(__file__).parent / 'eso-hash-vectors.json').read_text()) ++ for vector in vectors: ++ self.assertEqual(lane._synced_version({'metadata': vector['metadata']}), vector['expected']) ++ ++ def test_wrong_default_strategies_and_identity_never_match(self): ++ for field, value in [('conversionStrategy', 'Unicode'), ('decodingStrategy', 'Base64'), ++ ('metadataPolicy', 'Fetch'), ('key', 'elsewhere'), ('extra', 'value')]: ++ obj = self.resource(self.m.external) ++ obj['spec']['data'][0]['remoteRef'][field] = value ++ with self.assertRaises(lane.Refused): self.m._match(obj) ++ for field, value in [('namespace', 'default'), ('name', 'other'), ('generation', True)]: ++ obj = self.resource(self.m.external); obj['metadata'][field] = value ++ with self.assertRaises(lane.Refused): self.m._match(obj) ++ ++ def test_invalid_metadata_maps_never_produce_hash(self): ++ for value in ([], 'str', {'key': None}, {'key': '\ud800'}): ++ obj = self.resource(self.m.external); obj['metadata']['labels'] = value ++ with self.assertRaises(lane.Refused): lane._synced_version(obj) ++ ++ def test_client_override_injection_refused_before_command(self): ++ for client in ('x/../../secret', 'x;echo', 'A', None, 'a'*49): ++ with self.assertRaises(lane.Refused): lane._DeliveryMechanics(client) ++ self.assertEqual(self.calls, []) ++ ++ def test_retained_missing_stale_future_duplicate_status_not_ready(self): ++ for variant in ('retained', 'missing', 'old', 'future', 'duplicate', 'invalid_time'): ++ with self.subTest(variant=variant): ++ obj = self.resource(self.m.external) ++ if variant == 'retained': obj['status']['conditions'][0]['message'] = 'secret retained due to DeletionPolicy=Retain' ++ if variant == 'missing': obj['status'].pop('syncedResourceVersion') ++ if variant in ('old', 'future'): ++ delta = -301 if variant == 'old' else 60 ++ obj['status']['refreshTime'] = (datetime.now(timezone.utc) + timedelta(seconds=delta)).isoformat() ++ if variant == 'duplicate': obj['status']['conditions'] *= 2 ++ if variant == 'invalid_time': obj['status']['refreshTime'] = 'PRIVATE MALFORMED' ++ self.assertFalse(lane._ready(obj)) ++ ++ def test_stale_delivery_times_out_with_reconciliation_provenance(self): ++ self.create_mutate = lambda obj: {**obj, 'status': {'conditions': [{'type': 'Ready', 'status': 'True'}]}} ++ with self.assertRaises(lane.Uncertain) as error: self.m.deliver() ++ self.assertEqual(error.exception.revision['uid'], 'created-uid') ++ with self.assertRaises(lane.Refused): self.m.remove(error.exception.revision) ++ self.assertLessEqual(len(self.calls), 2 * lane.POLL_COUNT + 4) ++ ++ def test_store_prior_generation_refused_before_create(self): ++ self.store['metadata']['generation'] = 2 ++ with self.assertRaisesRegex(lane.Refused, 'generation_unverifiable'): self.m.deliver() ++ self.assertEqual(len(self.calls), 1) ++ ++ def test_store_replaced_before_precreate_read_prevents_create(self): ++ def hook(args): ++ if '/externalsecrets?' in args[-1]: self.store['metadata']['uid'] = 'replacement' ++ self.hook = hook ++ with self.assertRaisesRegex(lane.Refused, 'store_revision_changed'): self.m.deliver() ++ self.assertFalse(any('create' in args for args, _ in self.calls)) ++ ++ def test_store_replaced_inside_create_window_detected_after_mutation(self): ++ def hook(args): ++ if 'create' in args: self.store['metadata']['uid'] = 'replacement' ++ self.hook = hook ++ with self.assertRaises(lane.Uncertain): self.m.deliver() ++ self.assertIsNotNone(self.obj) # double-read did not exclude mutation ++ self.assertTrue(self.m.pending) ++ ++ def test_store_aba_race_is_not_claimed_excluded(self): ++ # A mutation and restoration wholly between observations is invisible. ++ seen = [] ++ def hook(args): ++ if 'create' in args: ++ original = deepcopy(self.store) ++ self.store['metadata']['uid'] = 'transient-replacement' ++ seen.append(self.store['metadata']['uid']) ++ self.store = original ++ seen.append(self.store['metadata']['uid']) ++ self.hook = hook ++ self.assertTrue(self.m.deliver()['changed']) ++ self.assertEqual(len(seen), 2) ++ self.assertEqual(seen, ['transient-replacement', 'store-uid']) ++ ++ def test_malformed_create_preserves_fixed_unknown_outcome(self): ++ for metadata in (None, [], {}, {'uid': 'created-uid', 'resourceVersion': None}): ++ with self.subTest(metadata=metadata): ++ self.m = lane._DeliveryMechanics('forgejo'); self.obj = None ++ self.create_mutate = lambda obj: {**obj, 'metadata': metadata} ++ with self.assertRaises(lane.Uncertain) as error: self.m.deliver() ++ self.assertEqual(str(error.exception), 'create_outcome_unverified') ++ self.assertTrue(self.m.pending) ++ ++ def test_unknown_create_and_delete_never_blindly_retry(self): ++ self.fail_create = True ++ with self.assertRaises(lane.Uncertain): self.m.deliver() ++ with self.assertRaises(lane.Refused): self.m.deliver() ++ self.fail_create = False; self.m = lane._DeliveryMechanics('forgejo') ++ delivered = self.m.deliver(); self.fail_delete = True ++ with self.assertRaises(lane.Uncertain) as error: self.m.remove(delivered['after']) ++ self.assertNotIn('PRIVATE', str(error.exception)) ++ with self.assertRaises(lane.Refused): self.m.remove(delivered['after']) ++ ++ def test_delete_race_uses_server_preconditions_and_preserves_replacement(self): ++ delivered = self.m.deliver() ++ def hook(args): ++ if 'delete' in args: self.obj['metadata']['uid'] = 'replacement' ++ self.hook = hook ++ with self.assertRaises(lane.Uncertain): self.m.remove(delivered['after']) ++ self.assertEqual(self.obj['metadata']['uid'], 'replacement') ++ ++ def test_status_rv_changed_before_remove_no_delete(self): ++ delivered = self.m.deliver(); self.obj['metadata']['resourceVersion'] = '11' ++ with self.assertRaises(lane.Uncertain): self.m.remove(delivered['after']) ++ self.assertFalse(any('delete' in args for args, _ in self.calls)) ++ ++ def test_fresh_process_and_forged_revision_never_delete(self): ++ delivered = self.m.deliver() ++ with self.assertRaises(lane.Refused): lane._DeliveryMechanics('forgejo').remove(delivered['after']) ++ with self.assertRaises(lane.Refused): self.m.remove({'uid': 'fake', 'resource_version': '10'}) ++ self.assertFalse(any('delete' in args for args, _ in self.calls)) ++ ++ def test_poll_rv_is_captured(self): ++ reads = [] ++ def hook(args): ++ if '/externalsecrets?' in args[-1] and self.obj is not None: ++ self.obj['metadata']['resourceVersion'] = '11' ++ reads.append(True) ++ self.hook = hook ++ delivered = self.m.deliver() ++ self.assertEqual(delivered['after']['resource_version'], '11') ++ self.assertTrue(reads) ++ ++ def test_unconfirmed_delete_is_bounded_and_preserves_reconciliation(self): ++ delivered = self.m.deliver() ++ prior = deepcopy(self.obj) ++ terminating = deepcopy(prior); terminating['metadata']['deletionTimestamp'] = 'now' ++ with patch.object(self.m, '_get', side_effect=[prior] + [terminating]*lane.POLL_COUNT): ++ with self.assertRaises(lane.Uncertain): self.m.remove(delivered['after']) ++ self.assertTrue(self.m.pending) ++ with self.assertRaises(lane.Refused): self.m.remove(delivered['after']) ++ ++ def test_lost_delete_after_server_removal_is_still_uncertain(self): ++ delivered = self.m.deliver(); self.fail_delete = True ++ def hook(args): ++ if 'delete' in args: self.obj = None ++ self.hook = hook ++ with self.assertRaises(lane.Uncertain): self.m.remove(delivered['after']) ++ self.assertIsNone(self.obj) ++ self.assertIsNotNone(self.m.created) ++ self.assertTrue(self.m.pending) ++ ++ def test_collection_foreign_object_cannot_be_adopted(self): ++ self.obj = self.resource(self.m.external); self.obj['metadata']['name'] = 'other' ++ with self.assertRaisesRegex(lane.Refused, 'collection_identity'): self.m.deliver() ++ self.assertFalse(any('create' in args for args, _ in self.calls)) ++ ++ def test_unknown_conflicting_or_unbounded_collection_never_absence(self): ++ for listing in ({}, {'kind': 'Status'}, {'apiVersion': 'external-secrets.io/v1', ++ 'kind': 'ExternalSecretList', 'metadata': {'continue': 'more'}, 'items': []}): ++ with self.subTest(listing=listing): ++ self.m = lane._DeliveryMechanics('forgejo'); self.force_list = listing ++ with self.assertRaises(lane.Refused): self.m.deliver() ++ self.assertFalse(any('create' in args for args, _ in self.calls)) ++ ++ def test_total_attempt_deadline_blocks_late_success(self): ++ self.clock_step = 6 ++ with self.assertRaises(lane.Uncertain): self.m.deliver() ++ self.assertLessEqual(len(self.calls), 6) ++ ++ def test_no_secret_or_store_mutation_and_factory_refuses(self): ++ self.m.deliver() ++ for args, payload in self.calls: ++ self.assertFalse(set(args) & {'secret', 'secrets'}) ++ if payload and 'create' in args: self.assertEqual(payload['kind'], 'ExternalSecret') ++ before = len(self.calls) ++ with self.assertRaises(lane.Refused): lane.production_bindings(authenticated=True) ++ self.assertEqual(len(self.calls), before) ++ ++ ++if __name__ == '__main__': unittest.main() diff --git a/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/reproduce.py b/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/reproduce.py new file mode 100644 index 00000000..3363c0d6 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/reproduce.py @@ -0,0 +1,43 @@ +"""Reproduce locally from pinned Git objects. No network or native owner actions.""" +from pathlib import Path +import json +import os +import shutil +import subprocess +import tempfile + +HERE = Path(__file__).resolve().parent +PLATFORM = Path('/home/worsch/railiance-platform') +BASE = '104ed90f42a9a86bf9fda343b49b65a85c9ee4b9' +ENV = {'PATH': '/usr/bin:/bin', 'PYTHONDONTWRITEBYTECODE': '1'} + + +def base_file(name): + return subprocess.check_output(['git', '-C', str(PLATFORM), 'show', BASE + ':' + name], + env=ENV, stderr=subprocess.DEVNULL) + + +def main(): + with tempfile.TemporaryDirectory(prefix='platform-delivery-r2-') as scratch: + scratch = Path(scratch) + home = scratch / 'empty-home'; home.mkdir() + env = {**ENV, 'HOME': str(home)} + tree = scratch / 'corrected' + shutil.copytree(HERE / 'prepared-source', tree) + for name in ('manifests/templates/keycape-issuer-eso.yaml', 'tests/test_keycape_issuer_eso.py'): + target = tree / name; target.parent.mkdir(parents=True, exist_ok=True) + target.write_bytes(base_file(name)) + runs = [] + commands = [ + ('corrected-regressions', ['/usr/bin/python3', '-m', 'unittest', 'discover', '-s', str(tree / 'tests'), '-v']), + ('corrected-predecessor-assertions', ['/usr/bin/python3', str(HERE / 'assert_corrected_regressions.py'), str(tree)]), + ] + for label, argv in commands: + result = subprocess.run(argv, env=env, capture_output=True) + print(label + ': ' + str(result.returncode)) + print((result.stdout + result.stderr).decode()) + runs.append(result.returncode) + if any(runs): raise SystemExit(1) + + +if __name__ == '__main__': main() diff --git a/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/upstream-sources.json b/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/upstream-sources.json new file mode 100644 index 00000000..cf2cbc46 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/upstream-sources.json @@ -0,0 +1,37 @@ +{ + "eso_tag": "v0.16.1", + "eso_commit": "8d037d197c88eeb9c4fe489e906c9cb3ab9ee6af", + "sources": [ + { + "url": "https://raw.githubusercontent.com/external-secrets/external-secrets/8d037d197c88eeb9c4fe489e906c9cb3ab9ee6af/pkg/controllers/externalsecret/externalsecret_controller.go", + "file": "upstream/eso-pkg_controllers_externalsecret_externalsecret_controller.go", + "sha256": "e6b18f869361227a19b74fb5d617014e7d8964fbfb6dc47402d128a35cd7bbf1" + }, + { + "url": "https://raw.githubusercontent.com/external-secrets/external-secrets/8d037d197c88eeb9c4fe489e906c9cb3ab9ee6af/pkg/controllers/externalsecret/util.go", + "file": "upstream/eso-pkg_controllers_externalsecret_util.go", + "sha256": "350cbef4a8fd56080003a141b3f6bb19af89de7c33999e77159c4f85f11adeef" + }, + { + "url": "https://raw.githubusercontent.com/external-secrets/external-secrets/8d037d197c88eeb9c4fe489e906c9cb3ab9ee6af/pkg/controllers/util/util.go", + "file": "upstream/eso-pkg_controllers_util_util.go", + "sha256": "d0337f622f83127cc7798fe493b11f26e9e1c0cb2bde7bfdecc57931edece67e" + }, + { + "url": "https://raw.githubusercontent.com/external-secrets/external-secrets/8d037d197c88eeb9c4fe489e906c9cb3ab9ee6af/pkg/controllers/secretstore/common.go", + "file": "upstream/eso-pkg_controllers_secretstore_common.go", + "sha256": "4f2d8ed21d9dd271f839a3d03fb91c1ad1e2ee695ce944770745dd2b52c0117a" + }, + { + "url": "https://raw.githubusercontent.com/external-secrets/external-secrets/8d037d197c88eeb9c4fe489e906c9cb3ab9ee6af/pkg/utils/utils.go", + "file": "upstream/eso-pkg_utils_utils.go", + "sha256": "17c722af9a16d07b1e8088b94783ce63ab6d86207b5b6039b1e4daebe0021764" + }, + { + "url": "https://raw.githubusercontent.com/external-secrets/external-secrets/8d037d197c88eeb9c4fe489e906c9cb3ab9ee6af/apis/externalsecrets/v1/secretstore_types.go", + "file": "upstream/eso-apis_externalsecrets_v1_secretstore_types.go", + "sha256": "573f93672c18301c85d978b6184fe0286a8beb6fd3c3620057720ea95196a67d" + } + ], + "readiness_limit": "These are source pins, not proof of deployed ESO version or native outcome." +} diff --git a/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/upstream/eso-apis_externalsecrets_v1_secretstore_types.go b/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/upstream/eso-apis_externalsecrets_v1_secretstore_types.go new file mode 100644 index 00000000..0b056095 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/upstream/eso-apis_externalsecrets_v1_secretstore_types.go @@ -0,0 +1,348 @@ +/* +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1 + +import ( + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// SecretStoreSpec defines the desired state of SecretStore. +type SecretStoreSpec struct { + // Used to select the correct ESO controller (think: ingress.ingressClassName) + // The ESO controller is instantiated with a specific controller name and filters ES based on this property + // +optional + Controller string `json:"controller,omitempty"` + + // Used to configure the provider. Only one provider may be set + Provider *SecretStoreProvider `json:"provider"` + + // Used to configure http retries if failed + // +optional + RetrySettings *SecretStoreRetrySettings `json:"retrySettings,omitempty"` + + // Used to configure store refresh interval in seconds. Empty or 0 will default to the controller config. + // +optional + RefreshInterval int `json:"refreshInterval,omitempty"` + + // Used to constraint a ClusterSecretStore to specific namespaces. Relevant only to ClusterSecretStore + // +optional + Conditions []ClusterSecretStoreCondition `json:"conditions,omitempty"` +} + +// ClusterSecretStoreCondition describes a condition by which to choose namespaces to process ExternalSecrets in +// for a ClusterSecretStore instance. +type ClusterSecretStoreCondition struct { + // Choose namespace using a labelSelector + // +optional + NamespaceSelector *metav1.LabelSelector `json:"namespaceSelector,omitempty"` + + // Choose namespaces by name + // +optional + // +kubebuilder:validation:items:MinLength:=1 + // +kubebuilder:validation:items:MaxLength:=63 + // +kubebuilder:validation:items:Pattern:=^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + Namespaces []string `json:"namespaces,omitempty"` + + // Choose namespaces by using regex matching + // +optional + NamespaceRegexes []string `json:"namespaceRegexes,omitempty"` +} + +// SecretStoreProvider contains the provider-specific configuration. +// +kubebuilder:validation:MinProperties=1 +// +kubebuilder:validation:MaxProperties=1 +type SecretStoreProvider struct { + // AWS configures this store to sync secrets using AWS Secret Manager provider + // +optional + AWS *AWSProvider `json:"aws,omitempty"` + + // AzureKV configures this store to sync secrets using Azure Key Vault provider + // +optional + AzureKV *AzureKVProvider `json:"azurekv,omitempty"` + + // Akeyless configures this store to sync secrets using Akeyless Vault provider + // +optional + Akeyless *AkeylessProvider `json:"akeyless,omitempty"` + + // BitwardenSecretsManager configures this store to sync secrets using BitwardenSecretsManager provider + // +optional + BitwardenSecretsManager *BitwardenSecretsManagerProvider `json:"bitwardensecretsmanager,omitempty"` + + // Vault configures this store to sync secrets using Hashi provider + // +optional + Vault *VaultProvider `json:"vault,omitempty"` + + // GCPSM configures this store to sync secrets using Google Cloud Platform Secret Manager provider + // +optional + GCPSM *GCPSMProvider `json:"gcpsm,omitempty"` + + // Oracle configures this store to sync secrets using Oracle Vault provider + // +optional + Oracle *OracleProvider `json:"oracle,omitempty"` + + // IBM configures this store to sync secrets using IBM Cloud provider + // +optional + IBM *IBMProvider `json:"ibm,omitempty"` + + // YandexCertificateManager configures this store to sync secrets using Yandex Certificate Manager provider + // +optional + YandexCertificateManager *YandexCertificateManagerProvider `json:"yandexcertificatemanager,omitempty"` + + // YandexLockbox configures this store to sync secrets using Yandex Lockbox provider + // +optional + YandexLockbox *YandexLockboxProvider `json:"yandexlockbox,omitempty"` + + // Github configures this store to push Github Action secrets using Github API provider + // +optional + Github *GithubProvider `json:"github,omitempty"` + + // GitLab configures this store to sync secrets using GitLab Variables provider + // +optional + Gitlab *GitlabProvider `json:"gitlab,omitempty"` + + // Alibaba configures this store to sync secrets using Alibaba Cloud provider + // +optional + Alibaba *AlibabaProvider `json:"alibaba,omitempty"` + + // OnePassword configures this store to sync secrets using the 1Password Cloud provider + // +optional + OnePassword *OnePasswordProvider `json:"onepassword,omitempty"` + + // Webhook configures this store to sync secrets using a generic templated webhook + // +optional + Webhook *WebhookProvider `json:"webhook,omitempty"` + + // Kubernetes configures this store to sync secrets using a Kubernetes cluster provider + // +optional + Kubernetes *KubernetesProvider `json:"kubernetes,omitempty"` + + // Fake configures a store with static key/value pairs + // +optional + Fake *FakeProvider `json:"fake,omitempty"` + + // Senhasegura configures this store to sync secrets using senhasegura provider + // +optional + Senhasegura *SenhaseguraProvider `json:"senhasegura,omitempty"` + + // Scaleway + // +optional + Scaleway *ScalewayProvider `json:"scaleway,omitempty"` + + // Doppler configures this store to sync secrets using the Doppler provider + // +optional + Doppler *DopplerProvider `json:"doppler,omitempty"` + + // Previder configures this store to sync secrets using the Previder provider + // +optional + Previder *PreviderProvider `json:"previder,omitempty"` + + // Onboardbase configures this store to sync secrets using the Onboardbase provider + // +optional + Onboardbase *OnboardbaseProvider `json:"onboardbase,omitempty"` + + // KeeperSecurity configures this store to sync secrets using the KeeperSecurity provider + // +optional + KeeperSecurity *KeeperSecurityProvider `json:"keepersecurity,omitempty"` + + // Conjur configures this store to sync secrets using conjur provider + // +optional + Conjur *ConjurProvider `json:"conjur,omitempty"` + + // Delinea DevOps Secrets Vault + // https://docs.delinea.com/online-help/products/devops-secrets-vault/current + // +optional + Delinea *DelineaProvider `json:"delinea,omitempty"` + + // SecretServer configures this store to sync secrets using SecretServer provider + // https://docs.delinea.com/online-help/secret-server/start.htm + // +optional + SecretServer *SecretServerProvider `json:"secretserver,omitempty"` + + // Chef configures this store to sync secrets with chef server + // +optional + Chef *ChefProvider `json:"chef,omitempty"` + + // Pulumi configures this store to sync secrets using the Pulumi provider + // +optional + Pulumi *PulumiProvider `json:"pulumi,omitempty"` + + // Fortanix configures this store to sync secrets using the Fortanix provider + // +optional + Fortanix *FortanixProvider `json:"fortanix,omitempty"` + + // +optional + PasswordDepot *PasswordDepotProvider `json:"passworddepot,omitempty"` + + // +optional + Passbolt *PassboltProvider `json:"passbolt,omitempty"` + + // Device42 configures this store to sync secrets using the Device42 provider + // +optional + Device42 *Device42Provider `json:"device42,omitempty"` + + // Infisical configures this store to sync secrets using the Infisical provider + // +optional + Infisical *InfisicalProvider `json:"infisical,omitempty"` + + // Beyondtrust configures this store to sync secrets using Password Safe provider. + // +optional + Beyondtrust *BeyondtrustProvider `json:"beyondtrust,omitempty"` + + // CloudruSM configures this store to sync secrets using the Cloud.ru Secret Manager provider + // +optional + CloudruSM *CloudruSMProvider `json:"cloudrusm,omitempty"` +} + +type CAProviderType string + +const ( + CAProviderTypeSecret CAProviderType = "Secret" + CAProviderTypeConfigMap CAProviderType = "ConfigMap" +) + +// Used to provide custom certificate authority (CA) certificates +// for a secret store. The CAProvider points to a Secret or ConfigMap resource +// that contains a PEM-encoded certificate. +type CAProvider struct { + // The type of provider to use such as "Secret", or "ConfigMap". + // +kubebuilder:validation:Enum="Secret";"ConfigMap" + Type CAProviderType `json:"type"` + + // The name of the object located at the provider type. + // +kubebuilder:validation:MinLength:=1 + // +kubebuilder:validation:MaxLength:=253 + // +kubebuilder:validation:Pattern:=^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + Name string `json:"name"` + + // The key where the CA certificate can be found in the Secret or ConfigMap. + // +kubebuilder:validation:Optional + // +kubebuilder:validation:MinLength:=1 + // +kubebuilder:validation:MaxLength:=253 + // +kubebuilder:validation:Pattern:=^[-._a-zA-Z0-9]+$ + Key string `json:"key,omitempty"` + + // The namespace the Provider type is in. + // Can only be defined when used in a ClusterSecretStore. + // +optional + // +kubebuilder:validation:MinLength:=1 + // +kubebuilder:validation:MaxLength:=63 + // +kubebuilder:validation:Pattern:=^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + Namespace *string `json:"namespace,omitempty"` +} + +type SecretStoreRetrySettings struct { + MaxRetries *int32 `json:"maxRetries,omitempty"` + RetryInterval *string `json:"retryInterval,omitempty"` +} + +type SecretStoreConditionType string + +const ( + SecretStoreReady SecretStoreConditionType = "Ready" + + ReasonInvalidStore = "InvalidStoreConfiguration" + ReasonInvalidProviderConfig = "InvalidProviderConfig" + ReasonValidationFailed = "ValidationFailed" + ReasonStoreValid = "Valid" + StoreUnmaintained = "StoreUnmaintained" +) + +type SecretStoreStatusCondition struct { + Type SecretStoreConditionType `json:"type"` + Status corev1.ConditionStatus `json:"status"` + + // +optional + Reason string `json:"reason,omitempty"` + + // +optional + Message string `json:"message,omitempty"` + + // +optional + LastTransitionTime metav1.Time `json:"lastTransitionTime,omitempty"` +} + +// SecretStoreCapabilities defines the possible operations a SecretStore can do. +type SecretStoreCapabilities string + +const ( + SecretStoreReadOnly SecretStoreCapabilities = "ReadOnly" + SecretStoreWriteOnly SecretStoreCapabilities = "WriteOnly" + SecretStoreReadWrite SecretStoreCapabilities = "ReadWrite" +) + +// SecretStoreStatus defines the observed state of the SecretStore. +type SecretStoreStatus struct { + // +optional + Conditions []SecretStoreStatusCondition `json:"conditions,omitempty"` + // +optional + Capabilities SecretStoreCapabilities `json:"capabilities,omitempty"` +} + +// +kubebuilder:object:root=true +// +kubebuilder:storageversion + +// SecretStore represents a secure external location for storing secrets, which can be referenced as part of `storeRef` fields. +// +kubebuilder:printcolumn:name="AGE",type="date",JSONPath=".metadata.creationTimestamp" +// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.conditions[?(@.type=="Ready")].reason` +// +kubebuilder:printcolumn:name="Capabilities",type=string,JSONPath=`.status.capabilities` +// +kubebuilder:printcolumn:name="Ready",type=string,JSONPath=`.status.conditions[?(@.type=="Ready")].status` +// +kubebuilder:subresource:status +// +kubebuilder:metadata:labels="external-secrets.io/component=controller" +// +kubebuilder:resource:scope=Namespaced,categories={external-secrets},shortName=ss +type SecretStore struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec SecretStoreSpec `json:"spec,omitempty"` + Status SecretStoreStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// SecretStoreList contains a list of SecretStore resources. +type SecretStoreList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []SecretStore `json:"items"` +} + +// +kubebuilder:object:root=true +// +kubebuilder:storageversion + +// ClusterSecretStore represents a secure external location for storing secrets, which can be referenced as part of `storeRef` fields. +// +kubebuilder:printcolumn:name="AGE",type="date",JSONPath=".metadata.creationTimestamp" +// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.conditions[?(@.type=="Ready")].reason` +// +kubebuilder:printcolumn:name="Capabilities",type=string,JSONPath=`.status.capabilities` +// +kubebuilder:printcolumn:name="Ready",type=string,JSONPath=`.status.conditions[?(@.type=="Ready")].status` +// +kubebuilder:subresource:status +// +kubebuilder:metadata:labels="external-secrets.io/component=controller" +// +kubebuilder:resource:scope=Cluster,categories={external-secrets},shortName=css +type ClusterSecretStore struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec SecretStoreSpec `json:"spec,omitempty"` + Status SecretStoreStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// ClusterSecretStoreList contains a list of ClusterSecretStore resources. +type ClusterSecretStoreList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []ClusterSecretStore `json:"items"` +} diff --git a/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/upstream/eso-pkg_controllers_externalsecret_externalsecret_controller.go b/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/upstream/eso-pkg_controllers_externalsecret_externalsecret_controller.go new file mode 100644 index 00000000..22b96fa5 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/upstream/eso-pkg_controllers_externalsecret_externalsecret_controller.go @@ -0,0 +1,985 @@ +/* +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package externalsecret + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "maps" + "slices" + "strings" + "time" + + "github.com/go-logr/logr" + "github.com/prometheus/client_golang/prometheus" + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/equality" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/fields" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/record" + "k8s.io/utils/ptr" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/builder" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/handler" + "sigs.k8s.io/controller-runtime/pkg/predicate" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + + esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1" + // Metrics. + "github.com/external-secrets/external-secrets/pkg/controllers/externalsecret/esmetrics" + ctrlmetrics "github.com/external-secrets/external-secrets/pkg/controllers/metrics" + "github.com/external-secrets/external-secrets/pkg/controllers/util" + "github.com/external-secrets/external-secrets/pkg/utils" + "github.com/external-secrets/external-secrets/pkg/utils/resolvers" + + // Loading registered generators. + _ "github.com/external-secrets/external-secrets/pkg/generator/register" + // Loading registered providers. + _ "github.com/external-secrets/external-secrets/pkg/provider/register" +) + +const ( + fieldOwnerTemplate = "externalsecrets.external-secrets.io/%v" + + // condition messages for "SecretSynced" reason. + msgSynced = "secret synced" + msgSyncedRetain = "secret retained due to DeletionPolicy=Retain" + + // condition messages for "SecretDeleted" reason. + msgDeleted = "secret deleted due to DeletionPolicy=Delete" + + // condition messages for "SecretMissing" reason. + msgMissing = "secret will not be created due to CreationPolicy=Merge" + + // condition messages for "SecretSyncedError" reason. + msgErrorGetSecretData = "could not get secret data from provider" + msgErrorDeleteSecret = "could not delete secret" + msgErrorDeleteOrphaned = "could not delete orphaned secrets" + msgErrorUpdateSecret = "could not update secret" + msgErrorUpdateImmutable = "could not update secret, target is immutable" + msgErrorBecomeOwner = "failed to take ownership of target secret" + msgErrorIsOwned = "target is owned by another ExternalSecret" + + // log messages. + logErrorGetES = "unable to get ExternalSecret" + logErrorUpdateESStatus = "unable to update ExternalSecret status" + logErrorGetSecret = "unable to get Secret" + logErrorPatchSecret = "unable to patch Secret" + logErrorSecretCacheNotSynced = "controller caches for Secret are not in sync" + logErrorUnmanagedStore = "unable to determine if store is managed" + + // error formats. + errConvert = "error applying conversion strategy %s to keys: %w" + errRewrite = "error applying rewrite to keys: %w" + errDecode = "error applying decoding strategy %s to data: %w" + errGenerate = "error using generator: %w" + errInvalidKeys = "invalid secret keys (TIP: use rewrite or conversionStrategy to change keys): %w" + errFetchTplFrom = "error fetching templateFrom data: %w" + errApplyTemplate = "could not apply template: %w" + errExecTpl = "could not execute template: %w" + errMutate = "unable to mutate secret %s: %w" + errUpdate = "unable to update secret %s: %w" + errUpdateNotFound = "unable to update secret %s: not found" + errDeleteCreatePolicy = "unable to delete secret %s: creationPolicy=%s is not Owner" + errSecretCachesNotSynced = "controller caches for secret %s are not in sync" + + // event messages. + eventCreated = "secret created" + eventUpdated = "secret updated" + eventDeleted = "secret deleted due to DeletionPolicy=Delete" + eventDeletedOrphaned = "secret deleted because it was orphaned" + eventMissingProviderSecret = "secret does not exist at provider using spec.dataFrom[%d]" + eventMissingProviderSecretKey = "secret does not exist at provider using spec.dataFrom[%d] (key=%s)" +) + +// these errors are explicitly defined so we can detect them with `errors.Is()`. +var ( + ErrSecretImmutable = fmt.Errorf("secret is immutable") + ErrSecretIsOwned = fmt.Errorf("secret is owned by another ExternalSecret") + ErrSecretSetCtrlRef = fmt.Errorf("could not set controller reference on secret") + ErrSecretRemoveCtrlRef = fmt.Errorf("could not remove controller reference on secret") +) + +const indexESTargetSecretNameField = ".metadata.targetSecretName" + +// Reconciler reconciles a ExternalSecret object. +type Reconciler struct { + client.Client + SecretClient client.Client + Log logr.Logger + Scheme *runtime.Scheme + RestConfig *rest.Config + ControllerClass string + RequeueInterval time.Duration + ClusterSecretStoreEnabled bool + EnableFloodGate bool + recorder record.EventRecorder +} + +// Reconcile implements the main reconciliation loop +// for watched objects (ExternalSecret, ClusterSecretStore and SecretStore), +// and updates/creates a Kubernetes secret based on them. +func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (result ctrl.Result, err error) { + log := r.Log.WithValues("ExternalSecret", req.NamespacedName) + + resourceLabels := ctrlmetrics.RefineNonConditionMetricLabels(map[string]string{"name": req.Name, "namespace": req.Namespace}) + start := time.Now() + + syncCallsError := esmetrics.GetCounterVec(esmetrics.SyncCallsErrorKey) + + // use closures to dynamically update resourceLabels + defer func() { + esmetrics.GetGaugeVec(esmetrics.ExternalSecretReconcileDurationKey).With(resourceLabels).Set(float64(time.Since(start))) + esmetrics.GetCounterVec(esmetrics.SyncCallsKey).With(resourceLabels).Inc() + }() + + externalSecret := &esv1.ExternalSecret{} + err = r.Get(ctx, req.NamespacedName, externalSecret) + if err != nil { + if apierrors.IsNotFound(err) { + // NOTE: this does not actually set the condition on the ExternalSecret, because it does not exist + // this is a hack to disable metrics for deleted ExternalSecrets, see: + // https://github.com/external-secrets/external-secrets/pull/612 + conditionSynced := NewExternalSecretCondition(esv1.ExternalSecretDeleted, v1.ConditionFalse, esv1.ConditionReasonSecretDeleted, "Secret was deleted") + SetExternalSecretCondition(&esv1.ExternalSecret{ + ObjectMeta: metav1.ObjectMeta{ + Name: req.Name, + Namespace: req.Namespace, + }, + }, *conditionSynced) + + return ctrl.Result{}, nil + } + + log.Error(err, logErrorGetES) + syncCallsError.With(resourceLabels).Inc() + return ctrl.Result{}, err + } + + // skip reconciliation if deletion timestamp is set on external secret + if !externalSecret.GetDeletionTimestamp().IsZero() { + log.V(1).Info("skipping ExternalSecret, it is marked for deletion") + return ctrl.Result{}, nil + } + + // if extended metrics is enabled, refine the time series vector + resourceLabels = ctrlmetrics.RefineLabels(resourceLabels, externalSecret.Labels) + + // skip this ExternalSecret if it uses a ClusterSecretStore and the feature is disabled + if shouldSkipClusterSecretStore(r, externalSecret) { + log.V(1).Info("skipping ExternalSecret, ClusterSecretStore feature is disabled") + return ctrl.Result{}, nil + } + + // skip this ExternalSecret if it uses any SecretStore not managed by this controller + skip, err := shouldSkipUnmanagedStore(ctx, req.Namespace, r, externalSecret) + if err != nil { + log.Error(err, logErrorUnmanagedStore) + syncCallsError.With(resourceLabels).Inc() + return ctrl.Result{}, err + } + if skip { + log.V(1).Info("skipping ExternalSecret, uses unmanaged SecretStore") + return ctrl.Result{}, nil + } + + // the target secret name defaults to the ExternalSecret name, if not explicitly set + secretName := externalSecret.Spec.Target.Name + if secretName == "" { + secretName = externalSecret.Name + } + + // fetch the existing secret (from the partial cache) + // - please note that the ~partial cache~ is different from the ~full cache~ + // so there can be race conditions between the two caches + // - the WatchesMetadata(v1.Secret{}) in SetupWithManager() is using the partial cache + // so we might receive a reconcile request before the full cache is updated + // - furthermore, when `--enable-managed-secrets-caching` is true, the full cache + // will ONLY include secrets with the "managed" label, so we cant use the full cache + // to reliably determine if a secret exists or not + secretPartial := &metav1.PartialObjectMetadata{} + secretPartial.SetGroupVersionKind(v1.SchemeGroupVersion.WithKind("Secret")) + err = r.Get(ctx, client.ObjectKey{Name: secretName, Namespace: externalSecret.Namespace}, secretPartial) + if err != nil && !apierrors.IsNotFound(err) { + log.Error(err, logErrorGetSecret, "secretName", secretName, "secretNamespace", externalSecret.Namespace) + syncCallsError.With(resourceLabels).Inc() + return ctrl.Result{}, err + } + + // if the secret exists but does not have the "managed" label, add the label + // using a PATCH so it is visible in the cache, then requeue immediately + if secretPartial.UID != "" && secretPartial.Labels[esv1.LabelManaged] != esv1.LabelManagedValue { + fqdn := fmt.Sprintf(fieldOwnerTemplate, externalSecret.Name) + patch := client.MergeFrom(secretPartial.DeepCopy()) + if secretPartial.Labels == nil { + secretPartial.Labels = make(map[string]string) + } + secretPartial.Labels[esv1.LabelManaged] = esv1.LabelManagedValue + err = r.Patch(ctx, secretPartial, patch, client.FieldOwner(fqdn)) + if err != nil { + log.Error(err, logErrorPatchSecret, "secretName", secretName, "secretNamespace", externalSecret.Namespace) + syncCallsError.With(resourceLabels).Inc() + return ctrl.Result{}, err + } + return ctrl.Result{Requeue: true}, nil + } + + // fetch existing secret (from the full cache) + // NOTE: we are using the `r.SecretClient` which we only use for managed secrets. + // when `enableManagedSecretsCache` is true, this is a cached client that only sees our managed secrets, + // otherwise it will be the normal controller-runtime client which may be cached or make direct API calls, + // depending on if `enabledSecretCache` is true or false. + existingSecret := &v1.Secret{} + err = r.SecretClient.Get(ctx, client.ObjectKey{Name: secretName, Namespace: externalSecret.Namespace}, existingSecret) + if err != nil && !apierrors.IsNotFound(err) { + log.Error(err, logErrorGetSecret, "secretName", secretName, "secretNamespace", externalSecret.Namespace) + syncCallsError.With(resourceLabels).Inc() + return ctrl.Result{}, err + } + + // ensure the full cache is up-to-date + // NOTE: this prevents race conditions between the partial and full cache. + // we return an error so we get an exponential backoff if we end up looping, + // for example, during high cluster load and frequent updates to the target secret by other controllers. + if secretPartial.UID != existingSecret.UID || secretPartial.ResourceVersion != existingSecret.ResourceVersion { + err = fmt.Errorf(errSecretCachesNotSynced, secretName) + log.Error(err, logErrorSecretCacheNotSynced, "secretName", secretName, "secretNamespace", externalSecret.Namespace) + syncCallsError.With(resourceLabels).Inc() + return ctrl.Result{}, err + } + + // refresh will be skipped if ALL the following conditions are met: + // 1. refresh interval is not 0 + // 2. resource generation of the ExternalSecret has not changed + // 3. the last refresh time of the ExternalSecret is within the refresh interval + // 4. the target secret is valid: + // - it exists + // - it has the correct "managed" label + // - it has the correct "data-hash" annotation + if !shouldRefresh(externalSecret) && isSecretValid(existingSecret) { + log.V(1).Info("skipping refresh") + return r.getRequeueResult(externalSecret), nil + } + + // update status of the ExternalSecret when this function returns, if needed. + // NOTE: we use the ability of deferred functions to update named return values `result` and `err` + // NOTE: we dereference the DeepCopy of the status field because status fields are NOT pointers, + // so otherwise the `equality.Semantic.DeepEqual` will always return false. + currentStatus := *externalSecret.Status.DeepCopy() + defer func() { + // if the status has not changed, we don't need to update it + if equality.Semantic.DeepEqual(currentStatus, externalSecret.Status) { + return + } + + // update the status of the ExternalSecret, storing any error in a new variable + // if there was no new error, we don't need to change the `result` or `err` values + updateErr := r.Status().Update(ctx, externalSecret) + if updateErr == nil { + return + } + + // if we got an update conflict, we should requeue immediately + if apierrors.IsConflict(updateErr) { + log.V(1).Info("conflict while updating status, will requeue") + + // we only explicitly request a requeue if the main function did not return an `err`. + // otherwise, we get an annoying log saying that results are ignored when there is an error, + // as errors are always retried. + if err == nil { + result = ctrl.Result{Requeue: true} + } + return + } + + // for other errors, log and update the `err` variable if there is no error already + // so the reconciler will requeue the request + log.Error(updateErr, logErrorUpdateESStatus) + if err == nil { + err = updateErr + } + }() + + // retrieve the provider secret data. + dataMap, err := r.getProviderSecretData(ctx, externalSecret) + if err != nil { + r.markAsFailed(msgErrorGetSecretData, err, externalSecret, syncCallsError.With(resourceLabels)) + return ctrl.Result{}, err + } + + // if no data was found we can delete the secret if needed. + if len(dataMap) == 0 { + switch externalSecret.Spec.Target.DeletionPolicy { + // delete secret and return early. + case esv1.DeletionPolicyDelete: + // safeguard that we only can delete secrets we own. + // this is also implemented in the es validation webhook. + // NOTE: this error cant be fixed by retrying so we don't return an error (which would requeue immediately) + creationPolicy := externalSecret.Spec.Target.CreationPolicy + if creationPolicy != esv1.CreatePolicyOwner { + err = fmt.Errorf(errDeleteCreatePolicy, secretName, creationPolicy) + r.markAsFailed(msgErrorDeleteSecret, err, externalSecret, syncCallsError.With(resourceLabels)) + return ctrl.Result{}, nil + } + + // delete the secret, if it exists + if existingSecret.UID != "" { + err = r.Delete(ctx, existingSecret) + if err != nil && !apierrors.IsNotFound(err) { + r.markAsFailed(msgErrorDeleteSecret, err, externalSecret, syncCallsError.With(resourceLabels)) + return ctrl.Result{}, err + } + r.recorder.Event(externalSecret, v1.EventTypeNormal, esv1.ReasonDeleted, eventDeleted) + } + + r.markAsDone(externalSecret, start, log, esv1.ConditionReasonSecretDeleted, msgDeleted) + return r.getRequeueResult(externalSecret), nil + // In case provider secrets don't exist the kubernetes secret will be kept as-is. + case esv1.DeletionPolicyRetain: + r.markAsDone(externalSecret, start, log, esv1.ConditionReasonSecretSynced, msgSyncedRetain) + return r.getRequeueResult(externalSecret), nil + // noop, handled below + case esv1.DeletionPolicyMerge: + } + } + + // mutationFunc is a function which can be applied to a secret to make it match the desired state. + mutationFunc := func(secret *v1.Secret) error { + // get information about the current owner of the secret + // - we ignore the API version as it can change over time + // - we ignore the UID for consistency with the SetControllerReference function + currentOwner := metav1.GetControllerOf(secret) + ownerIsESKind := false + ownerIsCurrentES := false + if currentOwner != nil { + currentOwnerGK := schema.FromAPIVersionAndKind(currentOwner.APIVersion, currentOwner.Kind).GroupKind() + ownerIsESKind = currentOwnerGK.String() == esv1.ExtSecretGroupKind + ownerIsCurrentES = ownerIsESKind && currentOwner.Name == externalSecret.Name + } + + // if another ExternalSecret is the owner, we should return an error + // otherwise the controller will fight with itself to update the secret. + // note, this does not prevent other controllers from owning the secret. + if ownerIsESKind && !ownerIsCurrentES { + return fmt.Errorf("%w: %s", ErrSecretIsOwned, currentOwner.Name) + } + + // if the CreationPolicy is Owner, we should set ourselves as the owner of the secret + if externalSecret.Spec.Target.CreationPolicy == esv1.CreatePolicyOwner { + err = controllerutil.SetControllerReference(externalSecret, secret, r.Scheme) + if err != nil { + return fmt.Errorf("%w: %w", ErrSecretSetCtrlRef, err) + } + } + + // if the creation policy is not Owner, we should remove ourselves as the owner + // this could happen if the creation policy was changed after the secret was created + if externalSecret.Spec.Target.CreationPolicy != esv1.CreatePolicyOwner && ownerIsCurrentES { + err = controllerutil.RemoveControllerReference(externalSecret, secret, r.Scheme) + if err != nil { + return fmt.Errorf("%w: %w", ErrSecretRemoveCtrlRef, err) + } + } + + // initialize maps within the secret so it's safe to set values + if secret.Annotations == nil { + secret.Annotations = make(map[string]string) + } + if secret.Labels == nil { + secret.Labels = make(map[string]string) + } + if secret.Data == nil { + secret.Data = make(map[string][]byte) + } + + // get the list of keys that are managed by this ExternalSecret + keys, err := getManagedDataKeys(secret, externalSecret.Name) + if err != nil { + return err + } + + // remove any data keys that are managed by this ExternalSecret, so we can re-add them + // this ensures keys added by templates are not left behind when they are removed from the template + for _, key := range keys { + delete(secret.Data, key) + } + + // WARNING: this will remove any labels or annotations managed by this ExternalSecret + // so any updates to labels and annotations should be done AFTER this point + err = r.applyTemplate(ctx, externalSecret, secret, dataMap) + if err != nil { + return fmt.Errorf(errApplyTemplate, err) + } + + // set the immutable flag on the secret if requested by the ExternalSecret + if externalSecret.Spec.Target.Immutable { + secret.Immutable = ptr.To(true) + } + + // we also use a label to keep track of the owner of the secret + // this lets us remove secrets that are no longer needed if the target secret name changes + if externalSecret.Spec.Target.CreationPolicy == esv1.CreatePolicyOwner { + lblValue := utils.ObjectHash(fmt.Sprintf("%v/%v", externalSecret.Namespace, externalSecret.Name)) + secret.Labels[esv1.LabelOwner] = lblValue + } else { + // the label should not be set if the creation policy is not Owner + delete(secret.Labels, esv1.LabelOwner) + } + + secret.Labels[esv1.LabelManaged] = esv1.LabelManagedValue + secret.Annotations[esv1.AnnotationDataHash] = utils.ObjectHash(secret.Data) + + return nil + } + + switch externalSecret.Spec.Target.CreationPolicy { + case esv1.CreatePolicyNone: + log.V(1).Info("secret creation skipped due to CreationPolicy=None") + err = nil + case esv1.CreatePolicyMerge: + // update the secret, if it exists + if existingSecret.UID != "" { + err = r.updateSecret(ctx, existingSecret, mutationFunc, externalSecret, secretName) + } else { + // if the secret does not exist, we wait until the next refresh interval + // rather than returning an error which would requeue immediately + r.markAsDone(externalSecret, start, log, esv1.ConditionReasonSecretMissing, msgMissing) + return r.getRequeueResult(externalSecret), nil + } + case esv1.CreatePolicyOrphan: + // create the secret, if it does not exist + if existingSecret.UID == "" { + err = r.createSecret(ctx, mutationFunc, externalSecret, secretName) + } else { + // if the secret exists, we should update it + err = r.updateSecret(ctx, existingSecret, mutationFunc, externalSecret, secretName) + } + case esv1.CreatePolicyOwner: + // we may have orphaned secrets to clean up, + // for example, if the target secret name was changed + err = r.deleteOrphanedSecrets(ctx, externalSecret, secretName) + if err != nil { + r.markAsFailed(msgErrorDeleteOrphaned, err, externalSecret, syncCallsError.With(resourceLabels)) + return ctrl.Result{}, err + } + + // create the secret, if it does not exist + if existingSecret.UID == "" { + err = r.createSecret(ctx, mutationFunc, externalSecret, secretName) + } else { + // if the secret exists, we should update it + err = r.updateSecret(ctx, existingSecret, mutationFunc, externalSecret, secretName) + } + } + if err != nil { + // if we got an update conflict, we should requeue immediately + if apierrors.IsConflict(err) { + log.V(1).Info("conflict while updating secret, will requeue") + return ctrl.Result{Requeue: true}, nil + } + + // detect errors indicating that we failed to set ourselves as the owner of the secret + // NOTE: this error cant be fixed by retrying so we don't return an error (which would requeue immediately) + if errors.Is(err, ErrSecretSetCtrlRef) { + r.markAsFailed(msgErrorBecomeOwner, err, externalSecret, syncCallsError.With(resourceLabels)) + return ctrl.Result{}, nil + } + + // detect errors indicating that the secret has another ExternalSecret as owner + // NOTE: this error cant be fixed by retrying so we don't return an error (which would requeue immediately) + if errors.Is(err, ErrSecretIsOwned) { + r.markAsFailed(msgErrorIsOwned, err, externalSecret, syncCallsError.With(resourceLabels)) + return ctrl.Result{}, nil + } + + // detect errors indicating that the secret is immutable + // NOTE: this error cant be fixed by retrying so we don't return an error (which would requeue immediately) + if errors.Is(err, ErrSecretImmutable) { + r.markAsFailed(msgErrorUpdateImmutable, err, externalSecret, syncCallsError.With(resourceLabels)) + return ctrl.Result{}, nil + } + + r.markAsFailed(msgErrorUpdateSecret, err, externalSecret, syncCallsError.With(resourceLabels)) + return ctrl.Result{}, err + } + + r.markAsDone(externalSecret, start, log, esv1.ConditionReasonSecretSynced, msgSynced) + return r.getRequeueResult(externalSecret), nil +} + +// getRequeueResult create a result with requeueAfter based on the ExternalSecret refresh interval. +func (r *Reconciler) getRequeueResult(externalSecret *esv1.ExternalSecret) ctrl.Result { + // default to the global requeue interval + // note, this will never be used because the CRD has a default value of 1 hour + refreshInterval := r.RequeueInterval + if externalSecret.Spec.RefreshInterval != nil { + refreshInterval = externalSecret.Spec.RefreshInterval.Duration + } + + // if the refresh interval is <= 0, we should not requeue + if refreshInterval <= 0 { + return ctrl.Result{} + } + + // if the last refresh time is not set, requeue after the refresh interval + // note, this should not happen, as we only call this function on ExternalSecrets + // that have been reconciled at least once + if externalSecret.Status.RefreshTime.IsZero() { + return ctrl.Result{RequeueAfter: refreshInterval} + } + + timeSinceLastRefresh := time.Since(externalSecret.Status.RefreshTime.Time) + + // if the last refresh time is in the future, we should requeue immediately + // note, this should not happen, as we always refresh an ExternalSecret + // that has a last refresh time in the future + if timeSinceLastRefresh < 0 { + return ctrl.Result{Requeue: true} + } + + // if there is time remaining, requeue after the remaining time + if timeSinceLastRefresh < refreshInterval { + return ctrl.Result{RequeueAfter: refreshInterval - timeSinceLastRefresh} + } + + // otherwise, requeue immediately + return ctrl.Result{Requeue: true} +} + +func (r *Reconciler) markAsDone(externalSecret *esv1.ExternalSecret, start time.Time, log logr.Logger, reason, msg string) { + oldReadyCondition := GetExternalSecretCondition(externalSecret.Status, esv1.ExternalSecretReady) + newReadyCondition := NewExternalSecretCondition(esv1.ExternalSecretReady, v1.ConditionTrue, reason, msg) + SetExternalSecretCondition(externalSecret, *newReadyCondition) + + externalSecret.Status.RefreshTime = metav1.NewTime(start) + externalSecret.Status.SyncedResourceVersion = util.GetResourceVersion(externalSecret.ObjectMeta) + + // if the status or reason has changed, log at the appropriate verbosity level + if oldReadyCondition == nil || oldReadyCondition.Status != newReadyCondition.Status || oldReadyCondition.Reason != newReadyCondition.Reason { + if newReadyCondition.Reason == esv1.ConditionReasonSecretDeleted { + log.Info("deleted secret") + } else { + log.Info("reconciled secret") + } + } else { + log.V(1).Info("reconciled secret") + } +} + +func (r *Reconciler) markAsFailed(msg string, err error, externalSecret *esv1.ExternalSecret, counter prometheus.Counter) { + r.recorder.Event(externalSecret, v1.EventTypeWarning, esv1.ReasonUpdateFailed, err.Error()) + conditionSynced := NewExternalSecretCondition(esv1.ExternalSecretReady, v1.ConditionFalse, esv1.ConditionReasonSecretSyncedError, msg) + SetExternalSecretCondition(externalSecret, *conditionSynced) + counter.Inc() +} + +func (r *Reconciler) deleteOrphanedSecrets(ctx context.Context, externalSecret *esv1.ExternalSecret, secretName string) error { + ownerLabel := utils.ObjectHash(fmt.Sprintf("%v/%v", externalSecret.Namespace, externalSecret.Name)) + + // we use a PartialObjectMetadataList to avoid loading the full secret objects + // and because the Secrets partials are always cached due to WatchesMetadata() in SetupWithManager() + secretListPartial := &metav1.PartialObjectMetadataList{} + secretListPartial.SetGroupVersionKind(v1.SchemeGroupVersion.WithKind("SecretList")) + listOpts := &client.ListOptions{ + LabelSelector: labels.SelectorFromSet(map[string]string{ + esv1.LabelOwner: ownerLabel, + }), + Namespace: externalSecret.Namespace, + } + if err := r.List(ctx, secretListPartial, listOpts); err != nil { + return err + } + + // delete all secrets that are not the target secret + for _, secretPartial := range secretListPartial.Items { + if secretPartial.GetName() != secretName { + err := r.Delete(ctx, &secretPartial) + if err != nil && !apierrors.IsNotFound(err) { + return err + } + r.recorder.Event(externalSecret, v1.EventTypeNormal, esv1.ReasonDeleted, eventDeletedOrphaned) + } + } + + return nil +} + +// createSecret creates a new secret with the given mutation function. +func (r *Reconciler) createSecret(ctx context.Context, mutationFunc func(secret *v1.Secret) error, es *esv1.ExternalSecret, secretName string) error { + fqdn := fmt.Sprintf(fieldOwnerTemplate, es.Name) + + // define and mutate the new secret + newSecret := &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: secretName, + Namespace: es.Namespace, + }, + Data: make(map[string][]byte), + } + if err := mutationFunc(newSecret); err != nil { + return err + } + + // note, we set field owner even for Create + if err := r.Create(ctx, newSecret, client.FieldOwner(fqdn)); err != nil { + return err + } + + // set the binding reference to the secret + // https://github.com/external-secrets/external-secrets/pull/2263 + es.Status.Binding = v1.LocalObjectReference{Name: newSecret.Name} + + r.recorder.Event(es, v1.EventTypeNormal, esv1.ReasonCreated, eventCreated) + return nil +} + +func (r *Reconciler) updateSecret(ctx context.Context, existingSecret *v1.Secret, mutationFunc func(secret *v1.Secret) error, es *esv1.ExternalSecret, secretName string) error { + fqdn := fmt.Sprintf(fieldOwnerTemplate, es.Name) + + // fail if the secret does not exist + // this should never happen because we check this before calling this function + if existingSecret.UID == "" { + return fmt.Errorf(errUpdateNotFound, secretName) + } + + // set the binding reference to the secret + // https://github.com/external-secrets/external-secrets/pull/2263 + es.Status.Binding = v1.LocalObjectReference{Name: secretName} + + // mutate a copy of the existing secret with the mutation function + updatedSecret := existingSecret.DeepCopy() + if err := mutationFunc(updatedSecret); err != nil { + return fmt.Errorf(errMutate, updatedSecret.Name, err) + } + + // if the secret does not need to be updated, return early + if equality.Semantic.DeepEqual(existingSecret, updatedSecret) { + return nil + } + + // if the existing secret is immutable, we can only update the object metadata + if ptr.Deref(existingSecret.Immutable, false) { + // check if the metadata was changed + metadataChanged := !equality.Semantic.DeepEqual(existingSecret.ObjectMeta, updatedSecret.ObjectMeta) + + // check if the immutable data/type was changed + var dataChanged bool + if metadataChanged { + // update the `existingSecret` object with the metadata from `updatedSecret` + // this lets us compare the objects to see if the immutable data/type was changed + existingSecret.ObjectMeta = *updatedSecret.ObjectMeta.DeepCopy() + dataChanged = !equality.Semantic.DeepEqual(existingSecret, updatedSecret) + + // because we use labels and annotations to keep track of the secret, + // we need to update the metadata, regardless of if the immutable data was changed + // NOTE: we are using the `existingSecret` object here, as we ONLY want to update the metadata, + // and we previously copied the metadata from the `updatedSecret` object + if err := r.Update(ctx, existingSecret, client.FieldOwner(fqdn)); err != nil { + // if we get a conflict, we should return early to requeue immediately + // note, we don't wrap this error so we can handle it in the caller + if apierrors.IsConflict(err) { + return err + } + return fmt.Errorf(errUpdate, existingSecret.Name, err) + } + } else { + // we know there was some change in the secret (or we would have returned early) + // we know the metadata was NOT changed (metadataChanged == false) + // so, the only thing that could have changed is the immutable data/type fields + dataChanged = true + } + + // if the immutable data was changed, we should return an error + if dataChanged { + return fmt.Errorf(errUpdate, existingSecret.Name, ErrSecretImmutable) + } + } + + // update the secret + if err := r.Update(ctx, updatedSecret, client.FieldOwner(fqdn)); err != nil { + // if we get a conflict, we should return early to requeue immediately + // note, we don't wrap this error so we can handle it in the caller + if apierrors.IsConflict(err) { + return err + } + return fmt.Errorf(errUpdate, updatedSecret.Name, err) + } + + r.recorder.Event(es, v1.EventTypeNormal, esv1.ReasonUpdated, eventUpdated) + return nil +} + +// getManagedDataKeys returns the list of data keys in a secret which are managed by a specified owner. +func getManagedDataKeys(secret *v1.Secret, fieldOwner string) ([]string, error) { + return getManagedFieldKeys(secret, fieldOwner, func(fields map[string]any) []string { + dataFields := fields["f:data"] + if dataFields == nil { + return nil + } + df, ok := dataFields.(map[string]any) + if !ok { + return nil + } + + return slices.Collect(maps.Keys(df)) + }) +} + +func getManagedFieldKeys( + secret *v1.Secret, + fieldOwner string, + process func(fields map[string]any) []string, +) ([]string, error) { + fqdn := fmt.Sprintf(fieldOwnerTemplate, fieldOwner) + var keys []string + for _, v := range secret.ObjectMeta.ManagedFields { + if v.Manager != fqdn { + continue + } + fields := make(map[string]any) + err := json.Unmarshal(v.FieldsV1.Raw, &fields) + if err != nil { + return nil, fmt.Errorf("error unmarshaling managed fields: %w", err) + } + for _, key := range process(fields) { + if key == "." { + continue + } + keys = append(keys, strings.TrimPrefix(key, "f:")) + } + } + return keys, nil +} + +func shouldSkipClusterSecretStore(r *Reconciler, es *esv1.ExternalSecret) bool { + return !r.ClusterSecretStoreEnabled && es.Spec.SecretStoreRef.Kind == esv1.ClusterSecretStoreKind +} + +// shouldSkipUnmanagedStore iterates over all secretStore references in the externalSecret spec, +// fetches the store and evaluates the controllerClass property. +// Returns true if any storeRef points to store with a non-matching controllerClass. +func shouldSkipUnmanagedStore(ctx context.Context, namespace string, r *Reconciler, es *esv1.ExternalSecret) (bool, error) { + var storeList []esv1.SecretStoreRef + + if es.Spec.SecretStoreRef.Name != "" { + storeList = append(storeList, es.Spec.SecretStoreRef) + } + + for _, ref := range es.Spec.Data { + if ref.SourceRef != nil { + storeList = append(storeList, ref.SourceRef.SecretStoreRef) + } + } + + for _, ref := range es.Spec.DataFrom { + if ref.SourceRef != nil && ref.SourceRef.SecretStoreRef != nil { + storeList = append(storeList, *ref.SourceRef.SecretStoreRef) + } + + // verify that generator's controllerClass matches + if ref.SourceRef != nil && ref.SourceRef.GeneratorRef != nil { + _, obj, err := resolvers.GeneratorRef(ctx, r.Client, r.Scheme, namespace, ref.SourceRef.GeneratorRef) + if err != nil { + if apierrors.IsNotFound(err) { + // skip non-existent generators + continue + } + if errors.Is(err, resolvers.ErrUnableToGetGenerator) { + // skip generators that we can't get (e.g. due to being invalid) + continue + } + return false, err + } + skipGenerator, err := shouldSkipGenerator(r, obj) + if err != nil { + return false, err + } + if skipGenerator { + return true, nil + } + } + } + + for _, ref := range storeList { + var store esv1.GenericStore + + switch ref.Kind { + case esv1.SecretStoreKind, "": + store = &esv1.SecretStore{} + case esv1.ClusterSecretStoreKind: + store = &esv1.ClusterSecretStore{} + namespace = "" + default: + return false, fmt.Errorf("unsupported secret store kind: %s", ref.Kind) + } + + err := r.Get(ctx, types.NamespacedName{ + Name: ref.Name, + Namespace: namespace, + }, store) + if err != nil { + if apierrors.IsNotFound(err) { + // skip non-existent stores + continue + } + return false, err + } + class := store.GetSpec().Controller + if class != "" && class != r.ControllerClass { + return true, nil + } + } + return false, nil +} + +func shouldRefresh(es *esv1.ExternalSecret) bool { + switch es.Spec.RefreshPolicy { + case esv1.RefreshPolicyCreatedOnce: + if es.Status.SyncedResourceVersion == "" || es.Status.RefreshTime.IsZero() { + return true + } + return false + + case esv1.RefreshPolicyOnChange: + if es.Status.SyncedResourceVersion == "" || es.Status.RefreshTime.IsZero() { + return true + } + + return es.Status.SyncedResourceVersion != util.GetResourceVersion(es.ObjectMeta) + + case esv1.RefreshPolicyPeriodic: + return shouldRefreshPeriodic(es) + + default: + return shouldRefreshPeriodic(es) + } +} + +func shouldRefreshPeriodic(es *esv1.ExternalSecret) bool { + // if the refresh interval is 0, and we have synced previously, we should not refresh + if es.Spec.RefreshInterval.Duration <= 0 && es.Status.SyncedResourceVersion != "" { + return false + } + + // if the ExternalSecret has been updated, we should refresh + if es.Status.SyncedResourceVersion != util.GetResourceVersion(es.ObjectMeta) { + return true + } + + // if the last refresh time is zero, we should refresh + if es.Status.RefreshTime.IsZero() { + return true + } + + // if the last refresh time is in the future, we should refresh + if es.Status.RefreshTime.Time.After(time.Now()) { + return true + } + + // if the last refresh time + refresh interval is before now, we should refresh + return es.Status.RefreshTime.Add(es.Spec.RefreshInterval.Duration).Before(time.Now()) +} + +// isSecretValid checks if the secret exists, and it's data is consistent with the calculated hash. +func isSecretValid(existingSecret *v1.Secret) bool { + // if target secret doesn't exist, we need to refresh + if existingSecret.UID == "" { + return false + } + + // if the managed label is missing or incorrect, then it's invalid + if existingSecret.Labels[esv1.LabelManaged] != esv1.LabelManagedValue { + return false + } + + // if the data-hash annotation is missing or incorrect, then it's invalid + // this is how we know if the data has chanced since we last updated the secret + if existingSecret.Annotations[esv1.AnnotationDataHash] != utils.ObjectHash(existingSecret.Data) { + return false + } + + return true +} + +// SetupWithManager returns a new controller builder that will be started by the provided Manager. +func (r *Reconciler) SetupWithManager(mgr ctrl.Manager, opts controller.Options) error { + r.recorder = mgr.GetEventRecorderFor("external-secrets") + + // index ExternalSecrets based on the target secret name, + // this lets us quickly find all ExternalSecrets which target a specific Secret + if err := mgr.GetFieldIndexer().IndexField(context.Background(), &esv1.ExternalSecret{}, indexESTargetSecretNameField, func(obj client.Object) []string { + es := obj.(*esv1.ExternalSecret) + // if the target name is set, use that as the index + if es.Spec.Target.Name != "" { + return []string{es.Spec.Target.Name} + } + // otherwise, use the ExternalSecret name + return []string{es.Name} + }); err != nil { + return err + } + + // predicate function to ignore secret events unless they have the "managed" label + secretHasESLabel := predicate.NewPredicateFuncs(func(object client.Object) bool { + value, hasLabel := object.GetLabels()[esv1.LabelManaged] + return hasLabel && value == esv1.LabelManagedValue + }) + + return ctrl.NewControllerManagedBy(mgr). + WithOptions(opts). + For(&esv1.ExternalSecret{}). + // we cant use Owns(), as we don't set ownerReferences when the creationPolicy is not Owner. + // we use WatchesMetadata() to reduce memory usage, as otherwise we have to process full secret objects. + WatchesMetadata( + &v1.Secret{}, + handler.EnqueueRequestsFromMapFunc(r.findObjectsForSecret), + builder.WithPredicates(predicate.ResourceVersionChangedPredicate{}, secretHasESLabel), + ). + Complete(r) +} + +func (r *Reconciler) findObjectsForSecret(ctx context.Context, secret client.Object) []reconcile.Request { + externalSecretsList := &esv1.ExternalSecretList{} + listOps := &client.ListOptions{ + FieldSelector: fields.OneTermEqualSelector(indexESTargetSecretNameField, secret.GetName()), + Namespace: secret.GetNamespace(), + } + err := r.List(ctx, externalSecretsList, listOps) + if err != nil { + return []reconcile.Request{} + } + + requests := make([]reconcile.Request, len(externalSecretsList.Items)) + for i := range externalSecretsList.Items { + requests[i] = reconcile.Request{ + NamespacedName: types.NamespacedName{ + Name: externalSecretsList.Items[i].GetName(), + Namespace: externalSecretsList.Items[i].GetNamespace(), + }, + } + } + return requests +} diff --git a/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/upstream/eso-pkg_controllers_externalsecret_util.go b/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/upstream/eso-pkg_controllers_externalsecret_util.go new file mode 100644 index 00000000..fa3139c0 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/upstream/eso-pkg_controllers_externalsecret_util.go @@ -0,0 +1,81 @@ +/* +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package externalsecret + +import ( + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1" + "github.com/external-secrets/external-secrets/pkg/controllers/externalsecret/esmetrics" +) + +// NewExternalSecretCondition a set of default options for creating an External Secret Condition. +func NewExternalSecretCondition(condType esv1.ExternalSecretConditionType, status v1.ConditionStatus, reason, message string) *esv1.ExternalSecretStatusCondition { + return &esv1.ExternalSecretStatusCondition{ + Type: condType, + Status: status, + LastTransitionTime: metav1.Now(), + Reason: reason, + Message: message, + } +} + +// GetExternalSecretCondition returns the condition with the provided type. +func GetExternalSecretCondition(status esv1.ExternalSecretStatus, condType esv1.ExternalSecretConditionType) *esv1.ExternalSecretStatusCondition { + for _, c := range status.Conditions { + if c.Type == condType { + return &c + } + } + return nil +} + +// SetExternalSecretCondition updates the external secret to include the provided +// condition. +func SetExternalSecretCondition(es *esv1.ExternalSecret, condition esv1.ExternalSecretStatusCondition) { + currentCond := GetExternalSecretCondition(es.Status, condition.Type) + + if currentCond != nil && currentCond.Status == condition.Status && + currentCond.Reason == condition.Reason && currentCond.Message == condition.Message { + esmetrics.UpdateExternalSecretCondition(es, &condition, 1.0) + return + } + + // Do not update lastTransitionTime if the status of the condition doesn't change. + if currentCond != nil && currentCond.Status == condition.Status { + condition.LastTransitionTime = currentCond.LastTransitionTime + } + + es.Status.Conditions = append(filterOutCondition(es.Status.Conditions, condition.Type), condition) + + if currentCond != nil { + esmetrics.UpdateExternalSecretCondition(es, currentCond, 0.0) + } + + esmetrics.UpdateExternalSecretCondition(es, &condition, 1.0) +} + +// filterOutCondition returns an empty set of conditions with the provided type. +func filterOutCondition(conditions []esv1.ExternalSecretStatusCondition, condType esv1.ExternalSecretConditionType) []esv1.ExternalSecretStatusCondition { + newConditions := make([]esv1.ExternalSecretStatusCondition, 0, len(conditions)) + for _, c := range conditions { + if c.Type == condType { + continue + } + newConditions = append(newConditions, c) + } + return newConditions +} diff --git a/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/upstream/eso-pkg_controllers_secretstore_common.go b/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/upstream/eso-pkg_controllers_secretstore_common.go new file mode 100644 index 00000000..b95f6ccd --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/upstream/eso-pkg_controllers_secretstore_common.go @@ -0,0 +1,141 @@ +/* +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package secretstore + +import ( + "context" + "fmt" + "time" + + "github.com/go-logr/logr" + v1 "k8s.io/api/core/v1" + "k8s.io/client-go/tools/record" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + + esapi "github.com/external-secrets/external-secrets/apis/externalsecrets/v1" + "github.com/external-secrets/external-secrets/pkg/controllers/secretstore/metrics" +) + +const ( + errStoreClient = "could not get provider client: %w" + errValidationFailed = "could not validate provider: %w" + errPatchStatus = "unable to patch status: %w" + errUnableCreateClient = "unable to create client" + errUnableValidateStore = "unable to validate store: %s" + + msgStoreValidated = "store validated" + msgStoreNotMaintained = "store isn't currently maintained. Please plan and prepare accordingly." +) + +type Opts struct { + ControllerClass string + GaugeVecGetter metrics.GaugeVevGetter + Recorder record.EventRecorder + RequeueInterval time.Duration +} + +func reconcile(ctx context.Context, req ctrl.Request, ss esapi.GenericStore, cl client.Client, log logr.Logger, opts Opts) (ctrl.Result, error) { + if !ShouldProcessStore(ss, opts.ControllerClass) { + log.V(1).Info("skip store") + return ctrl.Result{}, nil + } + + requeueInterval := opts.RequeueInterval + + if ss.GetSpec().RefreshInterval != 0 { + requeueInterval = time.Second * time.Duration(ss.GetSpec().RefreshInterval) + } + + // patch status when done processing + p := client.MergeFrom(ss.Copy()) + defer func() { + err := cl.Status().Patch(ctx, ss, p) + if err != nil { + log.Error(err, errPatchStatus) + } + }() + + // validateStore modifies the store conditions + // we have to patch the status + log.V(1).Info("validating") + err := validateStore(ctx, req.Namespace, opts.ControllerClass, ss, cl, opts.GaugeVecGetter, opts.Recorder) + if err != nil { + log.Error(err, "unable to validate store") + return ctrl.Result{}, err + } + storeProvider, err := esapi.GetProvider(ss) + if err != nil { + return ctrl.Result{}, err + } + + isMaintained, err := esapi.GetMaintenanceStatus(ss) + if err != nil { + return ctrl.Result{}, err + } + annotations := ss.GetAnnotations() + _, ok := annotations["external-secrets.io/ignore-maintenance-checks"] + + if !bool(isMaintained) && !ok { + opts.Recorder.Event(ss, v1.EventTypeWarning, esapi.StoreUnmaintained, msgStoreNotMaintained) + } + + capStatus := esapi.SecretStoreStatus{ + Capabilities: storeProvider.Capabilities(), + Conditions: ss.GetStatus().Conditions, + } + ss.SetStatus(capStatus) + + opts.Recorder.Event(ss, v1.EventTypeNormal, esapi.ReasonStoreValid, msgStoreValidated) + cond := NewSecretStoreCondition(esapi.SecretStoreReady, v1.ConditionTrue, esapi.ReasonStoreValid, msgStoreValidated) + SetExternalSecretCondition(ss, *cond, opts.GaugeVecGetter) + + return ctrl.Result{ + RequeueAfter: requeueInterval, + }, err +} + +// validateStore tries to construct a new client +// if it fails sets a condition and writes events. +func validateStore(ctx context.Context, namespace, controllerClass string, store esapi.GenericStore, + client client.Client, gaugeVecGetter metrics.GaugeVevGetter, recorder record.EventRecorder) error { + mgr := NewManager(client, controllerClass, false) + defer mgr.Close(ctx) + cl, err := mgr.GetFromStore(ctx, store, namespace) + if err != nil { + cond := NewSecretStoreCondition(esapi.SecretStoreReady, v1.ConditionFalse, esapi.ReasonInvalidProviderConfig, errUnableCreateClient) + SetExternalSecretCondition(store, *cond, gaugeVecGetter) + recorder.Event(store, v1.EventTypeWarning, esapi.ReasonInvalidProviderConfig, err.Error()) + return fmt.Errorf(errStoreClient, err) + } + validationResult, err := cl.Validate() + if err != nil && validationResult != esapi.ValidationResultUnknown { + cond := NewSecretStoreCondition(esapi.SecretStoreReady, v1.ConditionFalse, esapi.ReasonValidationFailed, fmt.Sprintf(errUnableValidateStore, err)) + SetExternalSecretCondition(store, *cond, gaugeVecGetter) + recorder.Event(store, v1.EventTypeWarning, esapi.ReasonValidationFailed, err.Error()) + return fmt.Errorf(errValidationFailed, err) + } + + return nil +} + +// ShouldProcessStore returns true if the store should be processed. +func ShouldProcessStore(store esapi.GenericStore, class string) bool { + if store == nil || store.GetSpec().Controller == "" || store.GetSpec().Controller == class { + return true + } + + return false +} diff --git a/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/upstream/eso-pkg_controllers_util_util.go b/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/upstream/eso-pkg_controllers_util_util.go new file mode 100644 index 00000000..5a2afb19 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/platform-delivery-backend-r2/upstream/eso-pkg_controllers_util_util.go @@ -0,0 +1,37 @@ +/* +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package util + +import ( + "fmt" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/external-secrets/external-secrets/pkg/utils" +) + +func GetResourceVersion(meta metav1.ObjectMeta) string { + return fmt.Sprintf("%d-%s", meta.GetGeneration(), HashMeta(meta)) +} + +func HashMeta(m metav1.ObjectMeta) string { + type meta struct { + annotations map[string]string + labels map[string]string + } + return utils.ObjectHash(meta{ + annotations: m.Annotations, + labels: m.Labels, + }) +}