From a69be6cb0773efdc9c3bf214311d11998a6b438f Mon Sep 17 00:00:00 2001 From: codex Date: Sat, 17 Oct 2026 22:14:06 +0200 Subject: [PATCH] Record reviewed case admission fixes, exact issuer source and isolated unit checks --- .../finalization-recheck-progress.json | 14 + .../finalization-recheck.json | 148 ++ .../apply_prepare.py | 92 + .../candidate.patch | 243 +++ .../preparation.json | 36 + .../publication-inputs.json | 176 ++ .../run_validation.py | 73 + .../task-T35.md | 38 + .../validation-child.py | 43 + .../validation-guard.py | 35 + .../workplan.candidate.md | 1647 ++++++++++++++++ .../workplan.preimage.md | 1609 ++++++++++++++++ .../apply_prepare.py | 92 + .../candidate.patch | 243 +++ .../canonical-validation.json | 15 + .../identifier-assignment.json | 20 + .../identifier-preview.json | 20 + .../preparation.json | 36 + .../publication-inputs.allocated.json | 23 + .../publication-inputs.json | 176 ++ .../revision-refresh.json | 8 + .../run_validation.py | 73 + .../task-T35.md | 38 + .../validation-child.py | 43 + .../validation-guard.py | 35 + .../workplan.allocated.md | 1648 +++++++++++++++++ .../workplan.candidate.md | 1647 ++++++++++++++++ .../workplan.preimage.md | 1609 ++++++++++++++++ .../candidate.patch | 243 +++ .../nk48-catalog-fixture-update-r1/edit.py | 46 + .../implementation-r1.json | 75 + .../nk48-catalog-fixture-update-r1/run.py | 8 + .../test_runner.py | 18 + .../canonical-validation.json | 15 + .../consistency.json | 365 ++++ .../consistency.stderr.txt | 1 + .../projection.json | 989 ++++++++++ .../projection.stderr.txt | 0 .../publication-commit.json | 29 + .../publication-inputs.final.json | 27 + .../publication-inputs.json | 176 ++ .../run_validation.py | 73 + .../validation-child.py | 43 + .../validation-guard.py | 35 + .../validation-inputs-refresh.json | 12 + .../verified-task-bindings.json | 30 + .../apply.py | 57 + .../composition-regenerated.json | 254 +++ .../initial-syntax-failure.py.txt | 79 + .../type.preimage.yaml | 495 +++++ .../verification.json | 13 + .../workplan.preimage.md | 1648 +++++++++++++++++ .../nk48-r5-f1-implementation-r1/README.md | 11 + .../canonical-application.json | 8 + .../canonical-net-kingdom-status-after.txt | 6 + .../canonical-validation.json | 8 + .../implementation.json | 112 ++ .../manifest.json | 19 + .../preimage-frozen.txt | 2 + .../preimage-mutant.txt | 2 + .../reproduce-scratch-checks.py | 30 + .../scratch-kernel-mutation.patch | 13 + .../nk48-r5-f1-implementation-r1/scratch.json | 6 + .../successor-frozen.txt | 2 + .../successor-mutant.txt | 30 + .../test-admit-1-3-agent-mint-coverage.patch | 68 + .../test-results.json | 74 + .../test_admit_1_3_type.after.py | 175 ++ .../test_admit_1_3_type.before.py | 157 ++ .../OWNER-REVIEW.md | 69 + .../canonical-source-readonly-state.json | 12 + .../focused-tests.json | 10 + .../focused-tests.txt | 13 + .../manifest.json | 20 + .../netkingdom-entry-point.conditional.patch | 16 + .../docs/keycape-issuer-eso-contract.md | 68 + .../templates/keycape-issuer-eso.yaml | 46 + .../scripts/keycape_issuer_eso.py | 70 + .../tests/test_keycape_issuer_eso.py | 129 ++ .../platform.candidate.patch | 325 ++++ .../nk48-r5-f2-implementation-r1/review.json | 66 + .../source-pins.json | 108 ++ .../structural-and-patch-checks.json | 8 + .../type.before.yaml | 495 +++++ .../type.candidate.yaml | 498 +++++ .../nk48-r5-f2-publication-r1/README.md | 43 + .../canonical-validation.json | 5 + .../consistency.json | 604 ++++++ .../consistency.stderr.txt | 0 .../integrate_platform.py | 84 + .../integration-checks.json | 39 + .../nk48-r5-f2-publication-r1/manifest.json | 40 + .../netkingdom-entry-point.conditional.patch | 16 + .../packet-hashes.json | 12 + .../docs/keycape-issuer-eso-contract.md | 68 + .../templates/keycape-issuer-eso.yaml | 46 + .../scripts/keycape_issuer_eso.py | 70 + .../tests/test_keycape_issuer_eso.py | 129 ++ .../progress-paragraph.md | 19 + .../nk48-r5-f2-publication-r1/projection.json | 443 +++++ .../projection.stderr.txt | 0 .../publication-commit.json | 25 + .../publication-inputs.json | 23 + .../nk48-r5-f2-review-r1/REVIEW.md | 70 + .../nk48-r5-f2-review-r1/evidence.json | 39 + .../nk48-r5-f2-review-r1/focused-tests.txt | 13 + .../nk48-r5-f2-review-r1/reproduce.py | 87 + .../nk48-r5-f2-review-r1/review.json | 23 + .../nk48-r5-f3-implementation-r1/README.md | 15 + .../candidate-validation.json | 29 + .../failure_inverse_omitted.txt | 34 + ...ocused-tests-before-admission-binding.json | 19 + ...focused-tests-before-admission-binding.txt | 2 + .../focused-tests.json | 18 + .../focused-tests.txt | 2 + .../implementation.json | 143 ++ .../manifest.json | 23 + .../negative-controls.json | 42 + .../net-kingdom-unchanged-owner-status.txt | 6 + .../owner-contract-candidate.md | 29 + .../owner_configuration_contract.py | 67 + .../post_revision_guard_removed.txt | 19 + .../railiance-apps-unchanged-owner-status.txt | 1 + .../test_owner_configuration_contract.py | 310 ++++ ...wner-configuration-inverse.candidate.patch | 61 + .../type.before.yaml | 495 +++++ .../type.candidate.yaml | 514 +++++ .../nk48-r5-f3-implementation-r2/README.md | 13 + .../candidate-validation.json | 30 + .../focused-tests.json | 19 + .../focused-tests.txt | 2 + .../fresh-inverse-session.candidate.patch | 16 + .../fresh-session-guard-omitted.txt | 353 ++++ .../implementation.json | 146 ++ .../manifest.json | 69 + .../negative-controls.json | 18 + .../owner-backend-next.md | 28 + .../owner-contract-candidate.md | 31 + .../owner-source-inventory.json | 62 + .../owner_configuration_contract.py | 72 + .../test_owner_configuration_contract.py | 421 +++++ ...wner-configuration-inverse.candidate.patch | 69 + .../type.before.yaml | 495 +++++ .../type.candidate.yaml | 518 ++++++ .../nk48-r5-f3-review-r1/README.md | 29 + .../fresh-inverse-session.candidate.patch | 16 + .../original-authority-tests.txt | 43 + .../patched-authority-tests.txt | 8 + .../nk48-r5-f3-review-r1/review.json | 177 ++ .../test_inverse_authority.py | 63 + .../apply_prepare.py | 26 + .../consistency.json | 376 ++++ .../consistency.stderr.txt | 1 + .../identifier-assignment.json | 32 + .../identifier-preview.json | 32 + .../preimage.json | 18 + .../projection.json | 991 ++++++++++ .../projection.stderr.txt | 0 .../publication-commit.json | 17 + .../publication-inputs.json | 15 + .../workplan.allocated.md | 1609 ++++++++++++++++ .../workplan.candidate.md | 1606 ++++++++++++++++ .../workplan.preimage.md | 1519 +++++++++++++++ .../netkingdom-progress.json | 14 + .../owner-source-state.json | 29 + .../platform-progress.json | 14 + .../prepare.py | 36 + .../publication-commit.json | 937 ++++++++++ .../scanner-blocked-paths.json | 17 + .../warden-progress.json | 14 + .../public-digest-derivation.json | 32 + .../candidate.patch | 314 ++++ .../candidate/src/warden/case_run.py | 1072 +++++++++++ .../candidate/src/warden/case_type.py | 173 ++ .../candidate/src/warden/routing/models.py | 343 ++++ .../tests/test_case_1_3_integration.py | 151 ++ .../tests/test_case_admission_modes.py | 136 ++ .../tests/test_case_attended_boundary.py | 113 ++ .../evidence/run.py | 12 + .../evidence/sitecustomize.py | 21 + .../implementation-r1.json | 93 + .../test-isolation-proposal.patch | 48 + .../integration_runner.py | 18 + .../kernel-review-r2.json | 128 ++ .../reviewer-probes.py | 94 + .../run-baseline.py | 9 + .../warden-1.3-kernel-review-r2/run.py | 9 + .../warden-1.3-kernel-review-r2/setup.py | 13 + .../sitecustomize.py | 17 + .../commit.json | 5 + .../consistency-preimage.json | 267 +++ .../consistency.json | 151 ++ .../consistency.stderr.txt | 0 .../focused-tests.txt | 3 + .../integrate.py | 93 + .../integration.json | 22 + .../normal-sync.json | 296 +++ .../normal-sync.stderr.txt | 0 .../publish.py | 64 + .../verification.json | 53 + .../workplan.preimage.md | 312 ++++ .../commit.json | 5 + .../consistency.json | 140 ++ .../consistency.stderr.txt | 0 .../full-unit-result.json | 14 + .../integrate.py | 65 + .../integration.json | 26 + .../prepare_publication.py | 19 + .../projection.json | 287 +++ .../projection.stderr.txt | 0 .../publication-commit.json | 65 + .../publication-inputs.json | 63 + .../root-disposition.json | 18 + .../warden-safe-unit-runner-r1/README.md | 60 + .../candidate.patch | 660 +++++++ .../candidate/Makefile | 50 + .../docs/development/safe-unit-tests.md | 64 + .../candidate/scripts/run_unit_tests.py | 283 +++ .../workload-coverage/provenance.json | 20 + .../rapp-core-hub/declarations/rapp.yaml | 15 + .../rapp-issue-core/declarations/rapp.yaml | 11 + .../rapp-qonto/declarations/rapp.yaml | 11 + .../candidate/tests/test_case_plan.py | 205 ++ .../candidate/tests/test_case_type.py | 130 ++ .../candidate/tests/test_layer_conformance.py | 355 ++++ .../candidate/tests/test_safe_unit_runner.py | 81 + .../candidate/tests/test_workload_join.py | 70 + .../candidate/tests/unit_isolation_probe.py | 29 + ...EN-WP-0044-security-case-follow-through.md | 378 ++++ .../implementation.json | 120 ++ .../packet-hashes.json | 26 + .../review-r1.json | 127 ++ .../reviewer-correction.patch | 56 + .../reviewer-probes.py | 82 + .../warden-safe-unit-runner-review-r1/run.py | 8 + docs/netkingdom-target-implementation.md | 85 + ...netkingdom-target-security-architecture.md | 14 + ...gdom-security-blueprint-and-gap-closure.md | 30 + 238 files changed, 38749 insertions(+) create mode 100644 docs/changes/netkingdom-target-2026-10-17/key39-infra-finalization-r1/finalization-recheck-progress.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/key39-infra-finalization-r1/finalization-recheck.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r1/apply_prepare.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r1/candidate.patch create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r1/preparation.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r1/publication-inputs.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r1/run_validation.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r1/task-T35.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r1/validation-child.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r1/validation-guard.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r1/workplan.candidate.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r1/workplan.preimage.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/apply_prepare.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/candidate.patch create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/canonical-validation.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/identifier-assignment.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/identifier-preview.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/preparation.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/publication-inputs.allocated.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/publication-inputs.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/revision-refresh.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/run_validation.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/task-T35.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/validation-child.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/validation-guard.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/workplan.allocated.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/workplan.candidate.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/workplan.preimage.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-update-r1/candidate.patch create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-update-r1/edit.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-update-r1/implementation-r1.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-update-r1/run.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-update-r1/test_runner.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-current-source-publication-r3/canonical-validation.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-current-source-publication-r3/consistency.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-current-source-publication-r3/consistency.stderr.txt create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-current-source-publication-r3/projection.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-current-source-publication-r3/projection.stderr.txt create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-current-source-publication-r3/publication-commit.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-current-source-publication-r3/publication-inputs.final.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-current-source-publication-r3/publication-inputs.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-current-source-publication-r3/run_validation.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-current-source-publication-r3/validation-child.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-current-source-publication-r3/validation-guard.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-current-source-publication-r3/validation-inputs-refresh.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-current-source-publication-r3/verified-task-bindings.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-issuer-reference-integration-r1/apply.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-issuer-reference-integration-r1/composition-regenerated.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-issuer-reference-integration-r1/initial-syntax-failure.py.txt create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-issuer-reference-integration-r1/type.preimage.yaml create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-issuer-reference-integration-r1/verification.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-issuer-reference-integration-r1/workplan.preimage.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/README.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/canonical-application.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/canonical-net-kingdom-status-after.txt create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/canonical-validation.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/implementation.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/manifest.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/preimage-frozen.txt create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/preimage-mutant.txt create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/reproduce-scratch-checks.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/scratch-kernel-mutation.patch create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/scratch.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/successor-frozen.txt create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/successor-mutant.txt create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/test-admit-1-3-agent-mint-coverage.patch create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/test-results.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/test_admit_1_3_type.after.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/test_admit_1_3_type.before.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/OWNER-REVIEW.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/canonical-source-readonly-state.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/focused-tests.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/focused-tests.txt create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/manifest.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/netkingdom-entry-point.conditional.patch create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/platform-candidate/docs/keycape-issuer-eso-contract.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/platform-candidate/manifests/templates/keycape-issuer-eso.yaml create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/platform-candidate/scripts/keycape_issuer_eso.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/platform-candidate/tests/test_keycape_issuer_eso.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/platform.candidate.patch create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/review.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/source-pins.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/structural-and-patch-checks.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/type.before.yaml create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/type.candidate.yaml create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/README.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/canonical-validation.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/consistency.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/consistency.stderr.txt create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/integrate_platform.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/integration-checks.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/manifest.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/netkingdom-entry-point.conditional.patch create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/packet-hashes.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/platform-source/docs/keycape-issuer-eso-contract.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/platform-source/manifests/templates/keycape-issuer-eso.yaml create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/platform-source/scripts/keycape_issuer_eso.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/platform-source/tests/test_keycape_issuer_eso.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/progress-paragraph.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/projection.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/projection.stderr.txt create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/publication-commit.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/publication-inputs.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-review-r1/REVIEW.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-review-r1/evidence.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-review-r1/focused-tests.txt create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-review-r1/reproduce.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-review-r1/review.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/README.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/candidate-validation.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/failure_inverse_omitted.txt create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/focused-tests-before-admission-binding.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/focused-tests-before-admission-binding.txt create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/focused-tests.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/focused-tests.txt create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/implementation.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/manifest.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/negative-controls.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/net-kingdom-unchanged-owner-status.txt create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/owner-contract-candidate.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/owner_configuration_contract.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/post_revision_guard_removed.txt create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/railiance-apps-unchanged-owner-status.txt create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/test_owner_configuration_contract.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/type-owner-configuration-inverse.candidate.patch create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/type.before.yaml create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/type.candidate.yaml create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/README.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/candidate-validation.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/focused-tests.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/focused-tests.txt create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/fresh-inverse-session.candidate.patch create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/fresh-session-guard-omitted.txt create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/implementation.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/manifest.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/negative-controls.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/owner-backend-next.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/owner-contract-candidate.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/owner-source-inventory.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/owner_configuration_contract.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/test_owner_configuration_contract.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/type-owner-configuration-inverse.candidate.patch create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/type.before.yaml create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/type.candidate.yaml create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-review-r1/README.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-review-r1/fresh-inverse-session.candidate.patch create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-review-r1/original-authority-tests.txt create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-review-r1/patched-authority-tests.txt create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-review-r1/review.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-review-r1/test_inverse_authority.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-source-followthrough-r1/apply_prepare.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-source-followthrough-r1/consistency.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-source-followthrough-r1/consistency.stderr.txt create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-source-followthrough-r1/identifier-assignment.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-source-followthrough-r1/identifier-preview.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-source-followthrough-r1/preimage.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-source-followthrough-r1/projection.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-source-followthrough-r1/projection.stderr.txt create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-source-followthrough-r1/publication-commit.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-source-followthrough-r1/publication-inputs.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-source-followthrough-r1/workplan.allocated.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-source-followthrough-r1/workplan.candidate.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-r5-source-followthrough-r1/workplan.preimage.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/source-corrections-publication-r1/netkingdom-progress.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/source-corrections-publication-r1/owner-source-state.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/source-corrections-publication-r1/platform-progress.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/source-corrections-publication-r1/prepare.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/source-corrections-publication-r1/publication-commit.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/source-corrections-publication-r1/scanner-blocked-paths.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/source-corrections-publication-r1/warden-progress.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/source-corrections-publication-r2/public-digest-derivation.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-fixes-r1/candidate.patch create mode 100644 docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-fixes-r1/candidate/src/warden/case_run.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-fixes-r1/candidate/src/warden/case_type.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-fixes-r1/candidate/src/warden/routing/models.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-fixes-r1/candidate/tests/test_case_1_3_integration.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-fixes-r1/candidate/tests/test_case_admission_modes.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-fixes-r1/candidate/tests/test_case_attended_boundary.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-fixes-r1/evidence/run.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-fixes-r1/evidence/sitecustomize.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-fixes-r1/implementation-r1.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-fixes-r1/test-isolation-proposal.patch create mode 100644 docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-review-r2/integration_runner.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-review-r2/kernel-review-r2.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-review-r2/reviewer-probes.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-review-r2/run-baseline.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-review-r2/run.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-review-r2/setup.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-review-r2/sitecustomize.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/warden-kernel-source-integration-r1/commit.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/warden-kernel-source-integration-r1/consistency-preimage.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/warden-kernel-source-integration-r1/consistency.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/warden-kernel-source-integration-r1/consistency.stderr.txt create mode 100644 docs/changes/netkingdom-target-2026-10-17/warden-kernel-source-integration-r1/focused-tests.txt create mode 100644 docs/changes/netkingdom-target-2026-10-17/warden-kernel-source-integration-r1/integrate.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/warden-kernel-source-integration-r1/integration.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/warden-kernel-source-integration-r1/normal-sync.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/warden-kernel-source-integration-r1/normal-sync.stderr.txt create mode 100644 docs/changes/netkingdom-target-2026-10-17/warden-kernel-source-integration-r1/publish.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/warden-kernel-source-integration-r1/verification.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/warden-kernel-source-integration-r1/workplan.preimage.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-integration-r1/commit.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-integration-r1/consistency.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-integration-r1/consistency.stderr.txt create mode 100644 docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-integration-r1/full-unit-result.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-integration-r1/integrate.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-integration-r1/integration.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-integration-r1/prepare_publication.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-integration-r1/projection.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-integration-r1/projection.stderr.txt create mode 100644 docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-integration-r1/publication-commit.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-integration-r1/publication-inputs.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-integration-r1/root-disposition.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/README.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate.patch create mode 100644 docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/Makefile create mode 100644 docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/docs/development/safe-unit-tests.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/scripts/run_unit_tests.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/tests/fixtures/workload-coverage/provenance.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/tests/fixtures/workload-coverage/rapp-core-hub/declarations/rapp.yaml create mode 100644 docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/tests/fixtures/workload-coverage/rapp-issue-core/declarations/rapp.yaml create mode 100644 docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/tests/fixtures/workload-coverage/rapp-qonto/declarations/rapp.yaml create mode 100644 docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/tests/test_case_plan.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/tests/test_case_type.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/tests/test_layer_conformance.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/tests/test_safe_unit_runner.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/tests/test_workload_join.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/tests/unit_isolation_probe.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/workplans/WARDEN-WP-0044-security-case-follow-through.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/implementation.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/packet-hashes.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-review-r1/review-r1.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-review-r1/reviewer-correction.patch create mode 100644 docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-review-r1/reviewer-probes.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-review-r1/run.py diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-infra-finalization-r1/finalization-recheck-progress.json b/docs/changes/netkingdom-target-2026-10-17/key39-infra-finalization-r1/finalization-recheck-progress.json new file mode 100644 index 0000000..9da18a0 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-infra-finalization-r1/finalization-recheck-progress.json @@ -0,0 +1,14 @@ +{ + "workplan_id": "09c3bddf-6058-5603-8488-cccdb7be4f86", + "id": "ebeaa526-9ece-45ca-a4cb-c8afd4c863c4", + "topic_id": null, + "task_id": "03da4c38-1dac-53b1-8b0b-e697df48e9d3", + "decision_id": null, + "event_type": "note", + "summary": "Rechecked KEY-WP-0039 against current infrastructure source review and primary State Hub: all 14 executable/test/producer-decision pins and 3 Platform pins match; T01/T03 done, T02 wait, parent active; KEY38 T02/T03 remain wait. Existing finalization assessment remains valid. Source-only approval is not live readiness/custody or a negative/expiry/cleanup receipt. No workplan/status edits, credentials, live changes or renewed approval request. Recheck evidence: docs/changes/netkingdom-target-2026-10-17/key39-infra-finalization-r1/finalization-recheck.json.", + "detail": null, + "author": "codex", + "session_id": null, + "created_at": "2026-10-17T17:27:33.253863Z", + "workstream_id": "09c3bddf-6058-5603-8488-cccdb7be4f86" +} diff --git a/docs/changes/netkingdom-target-2026-10-17/key39-infra-finalization-r1/finalization-recheck.json b/docs/changes/netkingdom-target-2026-10-17/key39-infra-finalization-r1/finalization-recheck.json new file mode 100644 index 0000000..cb13f4f --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/key39-infra-finalization-r1/finalization-recheck.json @@ -0,0 +1,148 @@ +{ + "checked_at": "2026-10-17T17:27:31.694709+00:00", + "owner_head": "d803e9282a47b49daf930fb905312fa5104c0ace", + "source_pin_checks": [ + { + "path": "net-kingdom/sso-mfa/k8s/keycape/openbao-negative-node-route.md", + "expected_sha256": "cad3e3dc481d45295cc014e48285e81614911dfb4a10523883f3044f6085f363", + "actual_sha256": "cad3e3dc481d45295cc014e48285e81614911dfb4a10523883f3044f6085f363", + "match": true + }, + { + "path": "net-kingdom/sso-mfa/k8s/keycape/openbao-negative-node-wrapper.py", + "expected_sha256": "76ac013d1a676a0dfa58aea1ce87b0c3e72483ccd21f586a22c5475e9132f6c1", + "actual_sha256": "76ac013d1a676a0dfa58aea1ce87b0c3e72483ccd21f586a22c5475e9132f6c1", + "match": true + }, + { + "path": "net-kingdom/sso-mfa/k8s/keycape/openbao-negative-session.py", + "expected_sha256": "24b7b803e4eb7e6e7c0700a069dad0ec315ee7eb5538a88b9e56147f8fab5cdd", + "actual_sha256": "24b7b803e4eb7e6e7c0700a069dad0ec315ee7eb5538a88b9e56147f8fab5cdd", + "match": true + }, + { + "path": "net-kingdom/sso-mfa/k8s/keycape/test_openbao_negative_session.py", + "expected_sha256": "01e23f2b4fc77824a161a57acb3a52d19aad846b3b6c0fd3abd81afa1dc2985c", + "actual_sha256": "01e23f2b4fc77824a161a57acb3a52d19aad846b3b6c0fd3abd81afa1dc2985c", + "match": true + }, + { + "path": "net-kingdom/sso-mfa/k8s/keycape/test_openbao_node_route.py", + "expected_sha256": "1c57053be6e8472174e4034fdfbf34b3e1729f5e239b3a9af89d9e59a08d6cb6", + "actual_sha256": "1c57053be6e8472174e4034fdfbf34b3e1729f5e239b3a9af89d9e59a08d6cb6", + "match": true + }, + { + "path": "net-kingdom/sso-mfa/k8s/keycape/test_openbao_private_input.py", + "expected_sha256": "84277edb5bd7858885173dea802b6bdc4f0f0c7785d6e200ee9a98d8553891a1", + "actual_sha256": "84277edb5bd7858885173dea802b6bdc4f0f0c7785d6e200ee9a98d8553891a1", + "match": true + }, + { + "path": "net-kingdom/sso-mfa/k8s/keycape/test_openbao_private_ready.py", + "expected_sha256": "8a7a033b1c509ba21397d1d74580c44f24592045b2b9de50fe87638607224277", + "actual_sha256": "8a7a033b1c509ba21397d1d74580c44f24592045b2b9de50fe87638607224277", + "match": true + }, + { + "path": "key-cape/docs/openbao-negative-token-producer-decision.json", + "expected_sha256": "7b3e4a53ac4af33c780f16c3f1aa9cde0154040ad2747a8d361dd79bbb7f435c", + "actual_sha256": "7b3e4a53ac4af33c780f16c3f1aa9cde0154040ad2747a8d361dd79bbb7f435c", + "match": true + }, + { + "path": "key-cape/docs/openbao-negative-token-producer-decision.md", + "expected_sha256": "38e4c9660ca57a1fb6e5ff04f214af61dd1a58abf564a5ed22216d909e27fc73", + "actual_sha256": "38e4c9660ca57a1fb6e5ff04f214af61dd1a58abf564a5ed22216d909e27fc73", + "match": true + }, + { + "path": "key-cape/scripts/openbao_negative_private_handoff.py", + "expected_sha256": "faf63a3e9504dcbb46847a114275f83d508521e095410a47f92f02b86ba82b1b", + "actual_sha256": "faf63a3e9504dcbb46847a114275f83d508521e095410a47f92f02b86ba82b1b", + "match": true + }, + { + "path": "key-cape/scripts/openbao_negative_private_launcher.py", + "expected_sha256": "46e57504b3185cc13f1656901a0ca02cc3887527cb09566e270f57b76099ab9c", + "actual_sha256": "46e57504b3185cc13f1656901a0ca02cc3887527cb09566e270f57b76099ab9c", + "match": true + }, + { + "path": "key-cape/scripts/test_openbao_negative_private_handoff.py", + "expected_sha256": "e4eb600718e7d93b0e801ac3ab1427af9895a6e5d877c1134369e049e0849797", + "actual_sha256": "e4eb600718e7d93b0e801ac3ab1427af9895a6e5d877c1134369e049e0849797", + "match": true + }, + { + "path": "key-cape/scripts/test_openbao_negative_private_launcher.py", + "expected_sha256": "243426a85075b3447adeb2ca320a13711409744f71837a06699de6ad5b04ee9d", + "actual_sha256": "243426a85075b3447adeb2ca320a13711409744f71837a06699de6ad5b04ee9d", + "match": true + }, + { + "path": "key-cape/scripts/test_openbao_negative_private_preparation.py", + "expected_sha256": "8e4dd327c4a339a0151ecba57db5987965b5d536ca18123fb7c74a047a241548", + "actual_sha256": "8e4dd327c4a339a0151ecba57db5987965b5d536ca18123fb7c74a047a241548", + "match": true + }, + { + "path": "railiance-platform/helm/openbao-ui-overlay-k8s.yaml", + "expected_sha256": "7b345359906bf1ecc4cb8e61de7a9f1a121068a66bd919e606190a9217a82daf", + "actual_sha256": "7b345359906bf1ecc4cb8e61de7a9f1a121068a66bd919e606190a9217a82daf", + "match": true + }, + { + "path": "railiance-platform/helm/openbao-ui-overlay/nginx.conf", + "expected_sha256": "464f7f5cecc69ea343c1e2acc5932e832b2a9c0e6a5099113634a12abc80ccb3", + "actual_sha256": "464f7f5cecc69ea343c1e2acc5932e832b2a9c0e6a5099113634a12abc80ccb3", + "match": true + }, + { + "path": "railiance-platform/helm/openbao-values.yaml", + "expected_sha256": "a10239851277889dd69dacb8636109dddd095551d85b46ca4ed2c141944e8d1e", + "actual_sha256": "a10239851277889dd69dacb8636109dddd095551d85b46ca4ed2c141944e8d1e", + "match": true + } + ], + "primary_task_readback": [ + { + "id": "4d06b059-3768-53bb-83a4-a9a71c0850b8", + "record_id": "KEY-WP-0039-T01", + "status": "done", + "blocking_reason": null, + "needs_human": false + }, + { + "id": "03da4c38-1dac-53b1-8b0b-e697df48e9d3", + "record_id": "KEY-WP-0039-T02", + "status": "wait", + "blocking_reason": "Ordered identity gate: Forgejo first SecurityCase registration/claims proof, then NetKingdom registry session and attended human/coding-agent custody; original secrets-engine-openbao registration go remains resolved; probe/coding-client custody and attended readiness are not established.", + "needs_human": false + }, + { + "id": "54d37ad8-2352-51c2-bb2e-af6070dbbd73", + "record_id": "KEY-WP-0039-T03", + "status": "done", + "blocking_reason": null, + "needs_human": false + }, + { + "id": "ddcfb482-8f33-5c81-9ce8-84be8fa5dc30", + "record_id": "KEY-WP-0038-T02", + "status": "wait", + "blocking_reason": "Registration runs as the first KnownSecurityCase of type oidc-browser-relying-party (NK-WP-0048-T06), not as a hand-built rollout.", + "needs_human": false + }, + { + "id": "40ddaa29-7245-508c-ad48-ddc063e25115", + "record_id": "KEY-WP-0038-T03", + "status": "wait", + "blocking_reason": "Claims are verified against the registered client.", + "needs_human": false + } + ], + "parent_status": "active", + "disposition": "Source review finalized; native acceptance outstanding. Preserve active parent and T02 wait. No new approval request, credential access or native action.", + "assessment": "ASSESSMENT.md" +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r1/apply_prepare.py b/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r1/apply_prepare.py new file mode 100644 index 0000000..771fde3 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r1/apply_prepare.py @@ -0,0 +1,92 @@ +"""Prepared source integration only. Root runs --execute; no commit/projection here.""" +from pathlib import Path +import argparse +import hashlib +import json +import re +import subprocess +import sys + +PACKET = Path(__file__).resolve().parent +ROOT = Path('/home/worsch/net-kingdom') +WARDEN = Path('/home/worsch/ops-warden') + +def sha(data): + return 'sha256:' + hashlib.sha256(data).hexdigest() + +def git(root, *args): + return subprocess.check_output(['git', '-C', str(root), *args], text=True).strip() + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--execute', action='store_true') + args = parser.parse_args() + manifest = json.loads((PACKET / 'publication-inputs.json').read_text()) + assert git(ROOT, 'rev-parse', '--abbrev-ref', 'HEAD') == 'main' + assert git(ROOT, 'rev-parse', 'HEAD') == manifest['base_revision'] + assert git(WARDEN, 'rev-parse', 'HEAD') == manifest['warden_revision'] + workplan = ROOT / manifest['workplan'] + original = workplan.read_bytes() + candidate = (PACKET / 'workplan.candidate.md').read_bytes() + assert sha(original) == manifest['workplan_preimage_sha256'] + assert sha(candidate) == manifest['workplan_candidate_sha256'] + assert sha((PACKET / 'task-T35.md').read_bytes()) == manifest['task_addition_sha256'] + assert candidate == original + (PACKET / 'task-T35.md').read_bytes() + assert len(re.findall(rb'^```task$', original, re.M)) == 34 + assert len(re.findall(rb'^```task$', candidate, re.M)) == 35 + assert b'id: NK-WP-0048-T35' not in original + assert sha((PACKET / 'candidate.patch').read_bytes()) == manifest['patch_sha256'] + for row in manifest['files']: + path = ROOT / row['path'] + if row['preimage_sha256'] is None: + assert not path.exists() and not path.is_symlink(), str(path) + else: + assert sha(path.read_bytes()) == row['preimage_sha256'], str(path) + protected_before = {name: sha((ROOT / name).read_bytes()) for name in manifest['protected_files']} + assert protected_before == manifest['protected_files'], 'Operator brief/index changed: refresh the prepared inputs first.' + # Preserve the original index exactly; this script never stages anything. + index = git(ROOT, 'ls-files', '--stage', '-z') + assert sha(Path('/home/worsch/repo-manager/src/repo_manager/identifiers.py').read_bytes()) == manifest['repo_manager_identifiers_sha256'] + sys.path.insert(0, '/home/worsch/repo-manager/src') + from repo_manager.identifiers import ensure_missing_work_record_identifiers + before = ensure_missing_work_record_identifiers(ROOT, execute=False) + assert before['ok'] and before['assignments'] == [] and before['files_changed'] == [] + subprocess.run(['git', 'apply', '--check', str(PACKET / 'candidate.patch')], cwd=ROOT, check=True) + if not args.execute: + print(json.dumps({'prepared_only': True, 'preimages_verified': True, 'files': 4, + 'next': 'Run this script with --execute, then run_validation.py --execute.'})) + return + subprocess.run(['git', 'apply', str(PACKET / 'candidate.patch')], cwd=ROOT, check=True) + for row in manifest['files']: + assert sha((ROOT / row['path']).read_bytes()) == row['sha256'], row['path'] + workplan.write_bytes(candidate) + preview = ensure_missing_work_record_identifiers(ROOT, execute=False) + (PACKET / 'identifier-preview.json').write_text(json.dumps(preview, indent=2) + '\n') + assert preview['ok'] and preview['files_changed'] == [manifest['workplan']] + assert len(preview['assignments']) == 1 + assignment = preview['assignments'][0] + assert (assignment['kind'], assignment['record_id'], assignment['path']) == ( + 'task', 'NK-WP-0048-T35', manifest['workplan']) + # UUID comes only from the ordinary allocator, never from this packet. + actual = ensure_missing_work_record_identifiers(ROOT, execute=True) + assert actual['assignments'] == preview['assignments'] + assert actual['files_changed'] == [manifest['workplan']] + (PACKET / 'identifier-assignment.json').write_text(json.dumps(actual, indent=2) + '\n') + allocated = workplan.read_bytes() + assert allocated.startswith(original), 'Existing workplan bytes must be preserved.' + assert len(re.findall(rb'^```task$', allocated, re.M)) == 35 + assert allocated.count(b'id: NK-WP-0048-T35\n') == 1 + assert not ensure_missing_work_record_identifiers(ROOT, execute=False)['assignments'] + assert git(ROOT, 'ls-files', '--stage', '-z') == index, 'Index must remain unchanged.' + assert {name: sha((ROOT / name).read_bytes()) for name in manifest['protected_files']} == protected_before + (PACKET / 'workplan.allocated.md').write_bytes(allocated) + publication = {'owner': 'net-kingdom', 'base_revision': manifest['base_revision'], + 'message': manifest['message'], 'files': [ + {'path': row['path'], 'sha256': sha((ROOT / row['path']).read_bytes()).removeprefix('sha256:')} + for row in manifest['files'] + [{'path': manifest['workplan']}]]} + (PACKET / 'publication-inputs.allocated.json').write_text(json.dumps(publication, indent=2) + '\n') + print(json.dumps({'source_applied': True, 'task_count': 35, 'allocated_only': assignment['record_id'], + 'files': len(publication['files']), 'commit_and_projection': 'Root handles separately.'})) + +if __name__ == '__main__': + main() diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r1/candidate.patch b/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r1/candidate.patch new file mode 100644 index 0000000..671f3e2 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r1/candidate.patch @@ -0,0 +1,243 @@ +diff --git a/sso-mfa/k8s/keycape/test_admit_1_3_type.py b/sso-mfa/k8s/keycape/test_admit_1_3_type.py +--- a/sso-mfa/k8s/keycape/test_admit_1_3_type.py ++++ b/sso-mfa/k8s/keycape/test_admit_1_3_type.py +@@ -8,7 +8,9 @@ + acknowledged 1.3 is still refused until ops-warden adds a 1.3 pin. That is an + ops-warden owner change, and it is not made here. The tests show that 1.3's + parameters carry 1.2's pinned fingerprint, and that with only that one pin added in +-memory the committed map admits exactly the composed descriptors and nothing else. ++memory the five resolvable steps admit. The two Apps attended steps remain refused ++because their establishing login is unresolved in the synthetic catalog. Four real ++draft lane shapes are hypothetically active in that fixture; no catalog is promoted. + Trust is fixture-only, nothing is dispatched, and none of this is owner admission + or an acknowledgment. + """ +@@ -37,6 +39,8 @@ + TYPE_DIR = REPO / 'security-case-type-revisions/1.3/oidc-browser-relying-party' + TYPE_BYTES = (TYPE_DIR / 'type.yaml').read_bytes() + ARTIFACT = json.loads((ROOT / 'descriptor-composition-1.3.candidate.json').read_text()) ++CATALOG = (ROOT / 'kernel-catalog.fixture.yaml').read_bytes() ++UNRESOLVED_APPS = {'deliver-consumer-secret', 'hand-over'} + EVIDENCE = {'fixture-acceptance', 'fixture-materialization', 'fixture-descriptor', 'fixture-session'} + + +@@ -74,12 +78,13 @@ + ARTIFACT['params']['consumer_owner_repo'], + version='1.3')) + +- def admit(self, desc, type_source, *, actor=None, lane=None, agent_id=None): ++ def admit(self, desc, type_source, *, actor=None, lane=None, agent_id=None, catalog=CATALOG): + desc_bytes = c.encode(desc) + adapter = Adapter(desc.adapter, desc.adapter_source, unused, unused) + executor = Executor(Journal(Path(self.temp.name) / desc.step), + {adapter.reference: adapter}, +- lambda purpose, source, evidence: evidence in EVIDENCE, ++ lambda purpose, source, evidence: (evidence in EVIDENCE and ++ (purpose != 'catalog-routing-acceptance' or source == digest(CATALOG))), + browser_inputs=inputs.BrowserInputs(REPO)) + with mock.patch.dict(os.environ): + if agent_id is None: +@@ -87,7 +92,7 @@ + else: + os.environ['WARDEN_AGENT_ID'] = agent_id + return executor.admit(self.case_source, type_source, desc_bytes, +- b'fixture-only-catalog', principal='founder', actor=actor or desc.actor, ++ catalog, principal='founder', actor=actor or desc.actor, + lane=lane or desc.lane, + acceptance_evidence='fixture-acceptance', + materialization_evidence='fixture-materialization', +@@ -112,18 +117,33 @@ + self.assertEqual(parameter_fingerprint(TYPE_BYTES), inputs.PARAMETER_PINS['1.2']) + acknowledged = with_status('acknowledged') + for step, desc in self.descriptors.items(): ++ if step in UNRESOLVED_APPS: ++ continue # The attended session gate correctly precedes parameter validation. + with self.subTest(step=step), self.assertRaisesRegex( + Refused, 'failed exact-version validation'): + self.admit(desc, acknowledged) + +- def test_committed_map_admits_every_descriptor_once_acknowledged_and_pinned(self): ++ def test_committed_map_conditionally_admits_five_resolvable_steps(self): + acknowledged = with_status('acknowledged') + for step, desc in self.descriptors.items(): ++ if step in UNRESOLVED_APPS: ++ continue + with self.subTest(step=step), pinned_13(): + admitted, invocation, _ = self.admit(desc, acknowledged) + self.assertEqual(admitted, desc) + self.assertEqual((invocation.step, invocation.actor, invocation.target), + (desc.step, desc.actor, 'forgejo')) ++ ++ def test_unresolved_apps_sessions_refused_even_once_acknowledged_and_pinned(self): ++ for step in sorted(UNRESOLVED_APPS): ++ with self.subTest(step=step), pinned_13(), self.assertRaisesRegex( ++ Refused, 'does not resolve to an active, reviewed login lane'): ++ self.admit(self.descriptors[step], with_status('acknowledged')) ++ ++ def test_catalog_evidence_is_bound_to_exact_fixture_bytes(self): ++ mint = self.descriptors['mint-secret'] ++ with pinned_13(), self.assertRaisesRegex(Refused, 'independent owner evidence not verified'): ++ self.admit(mint, with_status('acknowledged'), catalog=CATALOG + b'\n# changed source\n') + + def test_descriptor_outside_the_map_is_refused(self): + acknowledged = with_status('acknowledged') +diff --git a/sso-mfa/k8s/keycape/test_keycape_registration_adapter.py b/sso-mfa/k8s/keycape/test_keycape_registration_adapter.py +--- a/sso-mfa/k8s/keycape/test_keycape_registration_adapter.py ++++ b/sso-mfa/k8s/keycape/test_keycape_registration_adapter.py +@@ -259,8 +259,13 @@ + case_source = yaml.safe_dump(case).encode() + typ = yaml.safe_load((root / 'security-case-types/oidc-browser-relying-party/type.yaml').read_bytes()) + typ['spec']['execution_descriptors'] = {'register-client': digest(self.desc_bytes)} ++ # Fixture-only successor lane for the adapter's current descriptor. ++ # Canonical 1.1 still names the earlier login lane and is not edited. ++ typ['spec']['follow_through'] = [ ++ dict(step, lane=self.admitted.lane) if step['id'] == 'register-client' else step ++ for step in typ['spec']['follow_through']] + type_source = yaml.safe_dump(typ).encode() +- catalog = b'fixture-only-catalog' ++ catalog = (ROOT / 'kernel-catalog.fixture.yaml').read_bytes() + sheet = json.dumps(['register-client', 'net-kingdom', 'forgejo', 'human', + 'founder', 'keycape-attended-case-change', 'fixture-acceptance', 'fixture-materialization', + 'fixture-descriptor', 'fixture-session'], separators=(',', ':')).encode() +@@ -270,6 +275,8 @@ + adapter = a.build(**dict(self.kw, admitted=invocation, case_source=case_source, + case_acceptance='fixture-acceptance')) + def fixture_verifier(purpose, source, evidence): ++ if purpose == 'catalog-routing-acceptance': ++ return source == digest(catalog) and evidence == 'fixture-descriptor' + if purpose.startswith('session-revocation:'): + _, lane, operation, attempt = purpose.split(':') + return source == digest(case_source) and lane == invocation.lane and evidence == ( +diff --git a/sso-mfa/k8s/keycape/kernel-catalog.fixture.yaml b/sso-mfa/k8s/keycape/kernel-catalog.fixture.yaml +new file mode 100644 +--- /dev/null ++++ b/sso-mfa/k8s/keycape/kernel-catalog.fixture.yaml +@@ -0,0 +1,128 @@ ++# TEST ONLY: synthetic subset shaped after ops-warden 5abde817 catalog. ++# Four draft entries are hypothetically active; Apps session_lane remains unresolved. ++# Not an authenticated owner catalog, promotion, login choice or native authority. ++entries: ++- id: key-cape-oidc-login ++ risk: standard ++ owner_repo: key-cape ++ warden_executes: false ++ reviewed: '2026-06-27' ++ status: active ++ delegation: ++ mode: interim ++ intended_owner: key-cape ++ reviewed: '2026-09-08' ++ blocked_on: fixture-only; no native authority ++ lane: login ++ title: Synthetic key-cape-oidc-login ++ need_keywords: ++ - key-cape-oidc-login ++ subsystem: synthetic source test ++ wiki_ref: fixture-only ++ canon_ref: fixture-only ++ workload_ref: ++ applicability: not-applicable ++ reason: Synthetic source test; no workload or native authority. ++- id: keycape-client-secret-generate ++ risk: high ++ owner_repo: railiance-platform ++ warden_executes: false ++ reviewed: '2026-10-10' ++ status: active ++ delegation: ++ mode: native ++ intended_owner: railiance-platform ++ reviewed: '2026-10-10' ++ lane: secret ++ title: Synthetic keycape-client-secret-generate ++ need_keywords: ++ - keycape-client-secret-generate ++ subsystem: synthetic source test ++ wiki_ref: fixture-only ++ canon_ref: fixture-only ++ workload_ref: ++ applicability: not-applicable ++ reason: Synthetic source test; no workload or native authority. ++- id: keycape-attended-case-change ++ lane: attended-change ++ session_lane: key-cape-oidc-login ++ risk: high ++ owner_repo: key-cape/net-kingdom ++ warden_executes: false ++ reviewed: '2026-10-17' ++ status: active ++ delegation: ++ mode: interim ++ intended_owner: key-cape ++ reviewed: '2026-10-17' ++ blocked_on: fixture-only; no native authority ++ title: Synthetic keycape-attended-case-change ++ need_keywords: ++ - keycape-attended-case-change ++ subsystem: synthetic source test ++ wiki_ref: fixture-only ++ canon_ref: fixture-only ++ workload_ref: ++ applicability: not-applicable ++ reason: Synthetic source test; no workload or native authority. ++- id: platform-secret-delivery ++ risk: high ++ owner_repo: railiance-platform ++ warden_executes: false ++ reviewed: '2026-10-17' ++ status: active ++ delegation: ++ mode: native ++ intended_owner: railiance-platform ++ reviewed: '2026-10-17' ++ title: Synthetic platform-secret-delivery ++ need_keywords: ++ - platform-secret-delivery ++ subsystem: synthetic source test ++ wiki_ref: fixture-only ++ canon_ref: fixture-only ++ workload_ref: ++ applicability: not-applicable ++ reason: Synthetic source test; no workload or native authority. ++- id: consumer-owner-deployment ++ lane: attended-change ++ session_lane: pending-owner-confirmation ++ risk: high ++ owner_repo: railiance-apps ++ warden_executes: false ++ reviewed: '2026-10-17' ++ status: active ++ delegation: ++ mode: native ++ intended_owner: railiance-apps ++ reviewed: '2026-10-17' ++ title: Synthetic consumer-owner-deployment ++ need_keywords: ++ - consumer-owner-deployment ++ subsystem: synthetic source test ++ wiki_ref: fixture-only ++ canon_ref: fixture-only ++ workload_ref: ++ applicability: not-applicable ++ reason: Synthetic source test; no workload or native authority. ++- id: apps-consumer-handover ++ lane: attended-change ++ session_lane: pending-owner-confirmation ++ risk: high ++ owner_repo: railiance-apps ++ warden_executes: false ++ reviewed: '2026-10-17' ++ status: active ++ delegation: ++ mode: native ++ intended_owner: railiance-apps ++ reviewed: '2026-10-17' ++ title: Synthetic apps-consumer-handover ++ need_keywords: ++ - apps-consumer-handover ++ subsystem: synthetic source test ++ wiki_ref: fixture-only ++ canon_ref: fixture-only ++ workload_ref: ++ applicability: not-applicable ++ reason: Synthetic source test; no workload or native authority. diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r1/preparation.json b/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r1/preparation.json new file mode 100644 index 0000000..2077087 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r1/preparation.json @@ -0,0 +1,36 @@ +{ + "schema": "custodian.nk48-catalog-fixture-publication-preparation.v1", + "status": "prepared only; canonical apply and validation runners not executed, including dry-run", + "owner_base": "342a6f25af5cdc602841948f42759ea52ef1c8f5", + "warden_base": "ae7bc73b4f4bac22454144959b870d161498e0af", + "root_source_review": "Root explicitly accepted fixture patch SHA256 df69fb2ec9f1e1cf0dd94194e3c046c995ed8a7deabf65a8e9bec34662adb501 for source integration; no owner/native assent inferred.", + "task_refinement": "Append completed T35 for distinct current authenticated-catalog/mode-aware fixture compatibility; preserve exact original workplan bytes, all34 existing task blocks/frontmatter/status/UUID. Normal allocator preview must yield exactlyT35 before execution; no manual UUID.", + "root_commands": [ + "python3 /home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r1/apply_prepare.py --execute", + "python3 /home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r1/run_validation.py --execute", + "Root normal scoped commit using publication-inputs.allocated.json; normal owner projection afterwards." + ], + "mutation_scope": [ + "Exactly the three reviewed fixture files plus the appended T35 workplan task.", + "No git stage,commit,push,projection,brief refresh or index generation in prepared scripts.", + "Preexisting operator .custodian-brief.md/WORK-RECORDS.md remain byte-identical; unrelated dirty/untracked paths are never read or written by the apply script." + ], + "validation_scope": "Canonical NK test and source files against Warden mainae7bc73;105 explicit public source hashes checked before and after. Two scoped test modules only; expected31 tests/53 subtests; no skip/deselection accepted. EmptyHOME and allowlisted environment; fresh redirected Python caches; pytest cache/conftest discovery disabled. Network denied. Native processes denied except exact pinned offline metadata validator with guarded empty-environment child. Reads from /home or /root denied except exact prehashed public source files; canonical writes denied.", + "unverified_limits": [ + "Prepared runner has only been AST-parsed; root must execute and inspect logs.", + "Python audit controls are bounded to reviewed test execution and do not claim operating-system credential isolation or an autopilot grant.", + "Warden code is source-only; 1.3 parameter pin, draft promotion, pending Apps login, nativeT03 and downstream acceptance remain gated.", + "Existing adverse review history remains unchanged." + ], + "files": { + "apply_prepare.py": "sha256:b294e6339e8c9af9ba911667646dd0ab52e75cf798de93238d84d1057c8a9e70", + "run_validation.py": "sha256:16236aa55a774f8ea6d66da4ee17f7737c6d1b013d844ca9b9759e02b615af51", + "validation-guard.py": "sha256:13611f4de25dc7d01348905c4b365a0ffbb95fd93a61c75c8faf38d705c54f50", + "workplan.preimage.md": "sha256:f92962c5aa21aade0fc2f6b4f8d9a95932fe85b107413cdd2824b6ebe72c36d9", + "publication-inputs.json": "sha256:d9bf5555013aa9e399037672c80a72af23c6815db03088bee3db4c8583197146", + "candidate.patch": "sha256:df69fb2ec9f1e1cf0dd94194e3c046c995ed8a7deabf65a8e9bec34662adb501", + "task-T35.md": "sha256:7d14d261ffbab2a930c4a3ff962cec786060cad6177280773cf1c0b0f006127f", + "validation-child.py": "sha256:6bd4e3cda22e65f90bbb4006b403270405947293be2ae8bf8fdade0abed61f45", + "workplan.candidate.md": "sha256:3bce122c5b83d9044b142fc2cabc48de5558ab0d65dc0223a8ed0325f219e981" + } +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r1/publication-inputs.json b/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r1/publication-inputs.json new file mode 100644 index 0000000..6e6f530 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r1/publication-inputs.json @@ -0,0 +1,176 @@ +{ + "schema": "custodian.nk48-catalog-fixture-publication-inputs.v1", + "prepared_only": true, + "owner": "net-kingdom", + "base_revision": "342a6f25af5cdc602841948f42759ea52ef1c8f5", + "warden_revision": "ae7bc73b4f4bac22454144959b870d161498e0af", + "message": "test: align security case fixtures with authenticated catalog admission", + "workplan": "workplans/NK-WP-0048-security-case-types.md", + "workplan_preimage_sha256": "sha256:f92962c5aa21aade0fc2f6b4f8d9a95932fe85b107413cdd2824b6ebe72c36d9", + "workplan_candidate_sha256": "sha256:3bce122c5b83d9044b142fc2cabc48de5558ab0d65dc0223a8ed0325f219e981", + "task_addition_sha256": "sha256:7d14d261ffbab2a930c4a3ff962cec786060cad6177280773cf1c0b0f006127f", + "patch_sha256": "sha256:df69fb2ec9f1e1cf0dd94194e3c046c995ed8a7deabf65a8e9bec34662adb501", + "files": [ + { + "path": "sso-mfa/k8s/keycape/test_admit_1_3_type.py", + "preimage_sha256": "sha256:c512ba00513d066fd1ca8af08fd6bf106fbd7a132981b1dcc5af80a745ab03f0", + "sha256": "sha256:2186eab00b000a534ab1452f14fa7b1a7ed352d5b27c400687c1c8ec7c4c3019" + }, + { + "path": "sso-mfa/k8s/keycape/test_keycape_registration_adapter.py", + "preimage_sha256": "sha256:4d0126416522e54eec1ae4c83b5488e09ff11ffb97ec1ee6d234548780c60f63", + "sha256": "sha256:86268e082763c388528e5175184fd32362167c3441716436e4b1cab150c3e79a" + }, + { + "path": "sso-mfa/k8s/keycape/kernel-catalog.fixture.yaml", + "preimage_sha256": null, + "sha256": "sha256:7885b8c380c03ccb0198e540532ce80c791c8e86698521d7173642e0c7a56e53" + } + ], + "allocation": { + "method": "repo_manager.identifiers.ensure_missing_work_record_identifiers", + "manual_uuid": false, + "preexisting_assignments": [], + "expected_new_record_ids": [ + "NK-WP-0048-T35" + ], + "expected_files_changed": [ + "workplans/NK-WP-0048-security-case-types.md" + ], + "expected_task_count": 35, + "concrete_postallocation_manifest": "publication-inputs.allocated.json" + }, + "protected_files": { + ".custodian-brief.md": "sha256:3eb7f91f2443090c9b859a716d83711310364897b6144dcf10d3254342fec3d1", + "WORK-RECORDS.md": "sha256:a1fec217a6e380f528778d781dbea1fc5a836fdefa1dd4dd0308100fbe08112a" + }, + "validation": { + "tests": [ + "sso-mfa/k8s/keycape/test_admit_1_3_type.py", + "sso-mfa/k8s/keycape/test_keycape_registration_adapter.py" + ], + "expected_result": "31 passed,53 subtests passed; no skipped or deselected tests", + "python": "/tmp/warden-kernel-review-r1/final/.venv/bin/python", + "source_hashes": { + "net-kingdom": { + "sso-mfa/k8s/keycape/test_vergabe_client_rollout.py": "sha256:f9b7defc312b0fea24ded92ef479dcc8aef063f7a66a8245760918bcf44c4af1", + "sso-mfa/k8s/keycape/test_compose_1_2_descriptors.py": "sha256:9a4b9fb0141221d6e5bcb72b3656613afda54d16ede300bffa8f71956ae6fd2c", + "sso-mfa/k8s/keycape/test_keycape_client.py": "sha256:9572044679feaedfe732e15a2b6f8ef6b41a8fe2e4430257f309a3e83e97736c", + "sso-mfa/k8s/keycape/openbao-negative-node-wrapper.py": "sha256:76ac013d1a676a0dfa58aea1ce87b0c3e72483ccd21f586a22c5475e9132f6c1", + "sso-mfa/k8s/keycape/portal-client-rollout.py": "sha256:158e1d36eb881cdd0a2eda91b472adea5e8fde5061e00d26dda368580aeb871f", + "sso-mfa/k8s/keycape/test_playwright_activation.py": "sha256:6fc22d091f27a76c32d2fb0a7fc3c6e18e54ff98d242699c518b2f9dc9fd4cbd", + "sso-mfa/k8s/keycape/exercise-approval-clients.py": "sha256:e2d3dd741ac52af28dce752344227a4443b78fedd4cfcc06ba902e679dfc9351", + "sso-mfa/k8s/keycape/keycape-client.py": "sha256:4b63161cf3365d34dd684a3116e2c935baaf7f6aa4b30983f6883546db3d6591", + "sso-mfa/k8s/keycape/test_openbao_negative_session.py": "sha256:01e23f2b4fc77824a161a57acb3a52d19aad846b3b6c0fd3abd81afa1dc2985c", + "sso-mfa/k8s/keycape/authentication-policy-rollout.py": "sha256:2c1bd23fded4855bae1eb3a08dbc4510d7f60f2bc4d625d60e471ab78f6f0251", + "sso-mfa/k8s/keycape/handover-adapter.py": "sha256:bad7c1132258f2140b0d83548031ac86eecea4f4019f34adedc1e9e3774a3832", + "sso-mfa/k8s/keycape/openbao-signin-diagnosis-rollout.py": "sha256:a57b957d6eed9e8fb09d2a7aa7405a39deeafd9aaf57a9e6dc907afabd380371", + "sso-mfa/k8s/keycape/test_handover_adapter.py": "sha256:cd1fb0eb64a777699221ebdfcd6fea96bafb9b27ec388e95dcfe66189eac8ca2", + "sso-mfa/k8s/keycape/test_authentication_policy_rollout.py": "sha256:11c10039d99a34d2819b09146a966c8b7cba6095e7a8b51c055a01e7167e3ed5", + "sso-mfa/k8s/keycape/test_deliver_consumer_secret_adapter.py": "sha256:f4e6c6acd75a36f675007d2f62a7c7c4f7bfe2951cbb5e3e4cdde8b53f17f473", + "sso-mfa/k8s/keycape/bind-env-adapter.py": "sha256:fa17fae21776b5c620ee9782adf050649c3a28719a6687d2524ff67ca7ad1925", + "sso-mfa/k8s/keycape/test_keycape_checker_snapshot.py": "sha256:9271f25c760ad732c257d59d259b5c767436a2dc2d5ba24fca259bc57761d6d1", + "sso-mfa/k8s/keycape/test_openbao_signin_diagnosis_rollout.py": "sha256:25d46fe7caca2ad4c262bd2a25188c91847304b6b051c4f3b3705c69f2803f01", + "sso-mfa/k8s/keycape/test_openbao_private_ready.py": "sha256:8a7a033b1c509ba21397d1d74580c44f24592045b2b9de50fe87638607224277", + "sso-mfa/k8s/keycape/test_mint_secret_adapter.py": "sha256:8110b6a8abd8e9a1f5e1feae11ec09c4e28cb7486c759b0e8459da1fb1b7144d", + "sso-mfa/k8s/keycape/playwright-activation.py": "sha256:5ac683b3324b00e6253f6040f4c73057027b1f98f7071160a6153794ba522757", + "sso-mfa/k8s/keycape/test_admit_1_3_type.py": "sha256:2186eab00b000a534ab1452f14fa7b1a7ed352d5b27c400687c1c8ec7c4c3019", + "sso-mfa/k8s/keycape/test_openbao_node_route.py": "sha256:1c57053be6e8472174e4034fdfbf34b3e1729f5e239b3a9af89d9e59a08d6cb6", + "sso-mfa/k8s/keycape/test_secrets_engine_openbao_client_rollout.py": "sha256:e6f86fb7712c1b9c01d9b684f26d5cc949649b823d2732eced132d59acd3fe01", + "sso-mfa/k8s/keycape/openbao-client-config.py": "sha256:1b908b4c36134212d78f9fbe7a2a201eb3ced06d31bb1dcea4a59d80689d84b1", + "sso-mfa/k8s/keycape/test_registration_original_intent_reconcile.py": "sha256:e2897b8200ec97445fc4a26c55ad82651fcb1a4d22efe7fe59d0fcfc5cd8ec71", + "sso-mfa/k8s/keycape/test_ensure_groups_adapter.py": "sha256:c8754a2ba735116edc6fe257aa993d0530cea9ca9f954ea5a621e38694906be2", + "sso-mfa/k8s/keycape/test_keycape_checker_environment.py": "sha256:35f316eed168d3df588498faf24ab9377733a7d857ddfa5e0e4322a6287c93ff", + "sso-mfa/k8s/keycape/test_bind_env_adapter.py": "sha256:a1e5e7a631a4843f9a0d2310a3942b26936103d786aeae47a142ff7c7b2fdbdb", + "sso-mfa/k8s/keycape/create-secrets.py": "sha256:5da672fff36eb94101741ad99e2a1a96ae81fea15ed1871304c40e63ce27c787", + "sso-mfa/k8s/keycape/approval-clients-rollout.py": "sha256:488d5cc59e081993dd2b5fc6d9e46bbddcaec4f5170b8f7a5ad0aae3a211a790", + "sso-mfa/k8s/keycape/vergabe-client-rollout.py": "sha256:8cee6cc5ff3e3e8617304055263d04b393d0e005c34d9af12cb7e8bc685bac1a", + "sso-mfa/k8s/keycape/keycape-registration-adapter.py": "sha256:a53beea4dbbe373ce7a5b84d55e79be8407ab9688037c7ee057afcc7be576047", + "sso-mfa/k8s/keycape/compose-1.2-descriptors.py": "sha256:39021cac900b11a59d00c5a1d5491b1ee4c639955cde07d07854c3db8bc04958", + "sso-mfa/k8s/keycape/test_approval_clients_rollout.py": "sha256:745316d42c3aba6b59b562d3bbcd4b2cb92871fb5e51a954e2254e2e550bb408", + "sso-mfa/k8s/keycape/register-user-engine-portal.py": "sha256:67173edc44f5457e7b6a3e0fb2321b398159cfce5dea8ead01eabf835fb38d76", + "sso-mfa/k8s/keycape/test_issuer_pin.py": "sha256:e471ed815af246633c0b21ac2cf5cae94d0dbdeff9e6ce801b4ddd033097dc20", + "sso-mfa/k8s/keycape/openbao-negative-session.py": "sha256:24b7b803e4eb7e6e7c0700a069dad0ec315ee7eb5538a88b9e56147f8fab5cdd", + "sso-mfa/k8s/keycape/test_keycape_registration_adapter.py": "sha256:86268e082763c388528e5175184fd32362167c3441716436e4b1cab150c3e79a", + "sso-mfa/k8s/keycape/deliver-consumer-secret-adapter.py": "sha256:65db5386cdf08a1aa932886c046abed20330a36c96e63d4267adb8341b23da42", + "sso-mfa/k8s/keycape/test_portal_client_rollout.py": "sha256:a16cd4bf278970c10de2e1b1025597b3872c9515713dd947ac6b50cb78feea1f", + "sso-mfa/k8s/keycape/test_deliver_secret_adapter.py": "sha256:24cedec29071b62fc2c2458950ed244feeff542bc20a7d480667dd99cda16b03", + "sso-mfa/k8s/keycape/deliver-secret-adapter.py": "sha256:cd1a515a1ae2def2093660ed2a810a9c66d1d4cf7126f573ee7e9c4c42afb98d", + "sso-mfa/k8s/keycape/test_openbao_private_input.py": "sha256:84277edb5bd7858885173dea802b6bdc4f0f0c7785d6e200ee9a98d8553891a1", + "sso-mfa/k8s/keycape/register-coulomb-social.py": "sha256:b40ff4bdba71b797f8f393e79cb3c9d73b790045a0f7df6ecea35803369793c7", + "sso-mfa/k8s/keycape/mint-secret-adapter.py": "sha256:246323c7243ded18229ab063956577e151d56d9be485d3bd44fc38e5af0edf3c", + "sso-mfa/k8s/keycape/test_adapter_source_closure.py": "sha256:63cb442636d996fcf1c36b7b746ed860106a7da35dbaeb1c5129a41420ca4155", + "sso-mfa/k8s/keycape/secrets-engine-openbao-client-rollout.py": "sha256:8b834e5f0353a5e11797637420609e06e7bf7de47777c7e78b774f74d4a6afbd", + "sso-mfa/k8s/keycape/test_openbao_client_config.py": "sha256:7104a73fb936aca4829ef8046e39497d0b7347d3e74810f672e4d4ddc3346457", + "sso-mfa/k8s/keycape/ensure-groups-adapter.py": "sha256:6cba5b07a9262a8e0bf445dc4095ba03b31ddc15e2efbfe374e93f4f78b6aed4", + "sso-mfa/k8s/keycape/verify-user-engine-portal.py": "sha256:ccd5106395bd5d7b4f3dbbcbad449e3793aff217d4429b5d5af9e52ab9a58103", + "sso-mfa/k8s/keycape/test_playwright_registration.py": "sha256:b97ea8619ac768f520e4058aeb50b0b1e8ef6d2e9a1a0fff932a151a3d4b61ae", + "sso-mfa/k8s/keycape/playwright-registration.py": "sha256:bf2d6b83d93629df8ddaf6c31d6c7408ae3866969d63895714df0e08a570da99", + "identity-provisioner/group_provenance_postgres.py": "sha256:2a0111e83ad7b611076a1b45634e7f698b3d2e9acc473320b4a8ba009ebd0932", + "identity-provisioner/password_setup.py": "sha256:53012fc708384fc9cd0fff4f3fa5f204ab85866db91d726457dd130799e88ca0", + "identity-provisioner/provisioner.py": "sha256:19d1b837cbafeeacb1aa815856cd68091e6702281d72b1d860230fa320e1725b", + "identity-provisioner/copy-client-secret.py": "sha256:0f1907a9f16688148aede9fceb53a55cfd0504bcb8552f158d0fa77e9ad960c8", + "identity-provisioner/group_provenance.py": "sha256:405e08eaaab7c21d618dff4db5970607806f32ab6c461effb81c737e32182b10", + "identity-provisioner/server.py": "sha256:b61c83da6c7ce20fa2790d5bb00aeca6a91dccd133526bc559ed26562f90eb29", + "security-case-types/oidc-browser-relying-party/type.yaml": "sha256:f705c64e4875e9b8e18a912fb51c135e77898926d6a451f5cb74ab3c0cbe4d1c", + "security-case-type-revisions/1.2/oidc-browser-relying-party/type.yaml": "sha256:9e60ff569fb0d860a2bbde7bbb0991a54d4b0688f62b56df0fa5d161fd624578", + "security-case-type-revisions/1.3/oidc-browser-relying-party/type.yaml": "sha256:e75fe94110ef9fb7fb18072fe2856941b33507c99cb406d36576046481485d0a", + "security-case-type-revisions/1.3/oidc-browser-relying-party/examples/forgejo.yaml": "sha256:546aad5cf26842c484fc6ba99940d56697b85531afea11872556761008923c94", + "security-cases/forgejo-netkingdom-oidc.yaml": "sha256:bcc2c6d5bae0d28242b3cdb71e74d8ca297c2afe04766b55d53597f86893351d", + "sso-mfa/k8s/keycape/descriptor-composition-1.3.candidate.json": "sha256:773daa1df72cc7cf6a6d8aa082208181ca2438cdecc9179b1bc5b1c3a09099c2", + "sso-mfa/k8s/keycape/kernel-catalog.fixture.yaml": "sha256:7885b8c380c03ccb0198e540532ce80c791c8e86698521d7173642e0c7a56e53", + "tools/security-case-types/security_case_evidence.py": "sha256:da8efa59c52ad73283397ebe639ae259d0cb9c6348b5cb829135c24230dcdc75", + "tools/security-case-types/security_case_validator.py": "sha256:1bcc62e9bc374c711363295549f3cd5bb0b32952671ed4b08fddc7f74742959b", + "tools/security-scenario-composer/security_scenario_composer.py": "sha256:9f0cc69930aa8ee2cc3fb92236e6a99728df17b8c0fe36e1250241c38fc01c4f", + "tools/playbook-capability-contract/playbook_contract_validator.py": "sha256:2e1e0c809d4bc9d64c2d974cc23fecd1c5636785154c2554edd9191852fd926d" + }, + "ops-warden": { + "src/warden/ca.py": "sha256:d5282819c5d29386fd59a7dc46d1abdc01e99fc6838a1a6637f90868389fe557", + "src/warden/inventory.py": "sha256:9401db92d7cbf3bea962b97e83ef2c252a19c69ab94ce4836ba58f68638cb311", + "src/warden/memory.py": "sha256:ecf5f7ccabb669b56bfca3a109b10bbe8437bdd2ea30a47a4c08570f97d7be2a", + "src/warden/case_inputs.py": "sha256:1334b0cd9897e763cfea2d306e997c659f3f284ecc3f9612140c2104835da7c7", + "src/warden/audit.py": "sha256:6b5b8fe053064d3905294bfde1b691fcca8a0806c889e52a49494470d29c24a0", + "src/warden/case_type.py": "sha256:7b659b8d682863cd28df24025be4b10dd7ca3df724350a22d360c6af9615bca7", + "src/warden/vault.py": "sha256:860b8c9f498b3955b363a74dd74087b6d0c399599ef22b951a076ee33c95929f", + "src/warden/taint.py": "sha256:8c3d161d18d429d026aad715ac01e2d33e0f4e84ac03fbaea3cea15017ad39a1", + "src/warden/case_run.py": "sha256:1bc73e30ac718fd75bbaff2a739495fc051aeaec73802ac71f0583cb2d53ab0a", + "src/warden/vault_hints.py": "sha256:0d158ffcc3a93d5442df5fbe13d4c1e3b3159dd60301530bbdae6db9bc9c709e", + "src/warden/access.py": "sha256:258d45d2f84b54b2464373ad4d7d6df6ff95b8e8d60f5e85ceb2d962fcfa8a61", + "src/warden/clock_attribution.py": "sha256:5fcf07a2525a6a250442e14b6f6229f7db2a6ca02fc6aac23cdaa4c1a78c63eb", + "src/warden/case_plan.py": "sha256:9352ae8683d1181980c4ff38524582e4be448d33c85b558f60582c1e5bb5bbaa", + "src/warden/caller_identity.py": "sha256:747edfaceecd470981949253bda0c3bd2f487d40a9a2f77c2cc1eff3f34f04f7", + "src/warden/policy.py": "sha256:aac46f538e986731cd841581c91efe004b8c1940966b954ef635780a719dada4", + "src/warden/case_receipt_state.py": "sha256:a2cda7f187e77bd76d4d209505638de44a587fae0bc033ec75c23ee9ea9f73bc", + "src/warden/config.py": "sha256:8a857faa3811b00303d4d5c904d69255ebdaff3239e8516f97e6606e53b46924", + "src/warden/openbao_caller.py": "sha256:7e6d07827d00eae7de3b2367c97bdcda1a13d6559627201a4ba09a0e42d46a17", + "src/warden/doubles.py": "sha256:5460e07f5bd0f51a81d3f3c0ac84efde65ca9130b1039f94ee22ca484976a9de", + "src/warden/posture.py": "sha256:1bb60c4dd4634ea42cb130ae1325d7415adf43096906bde2515396c368256e33", + "src/warden/cli.py": "sha256:196cd829d3dbfbd2333598a983687bde1f9bcdfadef95b2fd1d237c4a5f060a9", + "src/warden/proxy.py": "sha256:69649bcf8979e4ed7f9104e927b458827a355c4846143367f4cd49c8b89b96da", + "src/warden/plan.py": "sha256:001b3197e644221344d52e6b1594eb8eae78d10e73d18ed31273505909f3916b", + "src/warden/worker.py": "sha256:d7c2b391b36033d44d8c6841b9bce18a05c74b41ef2910b91689820b6a1cb44e", + "src/warden/mask.py": "sha256:0876412d453fa84109dc8ee0468449f40cf4d3421b056f30fc205f0f59ec494b", + "src/warden/models.py": "sha256:f4100b02c5d96d537980e00d4b874159cd5822ba8219f1f533a2516912b7bd85", + "src/warden/__init__.py": "sha256:047e0feba479474235f0707a207655ecfddfb33cd4f54069134419b8ca4ea24a", + "src/warden/scorecard.py": "sha256:0fa5aef34aeb004fa7fcfabc287759cc47421ca5244c1f31fc467b22a653353c", + "src/warden/desk.py": "sha256:2499120392a5a1c0fd8f423d5b5c787c75a4efb9a986a1b7bcb85eae13423e85", + "src/warden/routing/scaffold.py": "sha256:5079c8e51f8d487eae3f322a66324bd70cefb637bb28f46c861032de8ec54aa2", + "src/warden/routing/models.py": "sha256:b85372b2572a49af62e9534f26a33963b894633c7cd43dd2a28416b8959cba3e", + "src/warden/routing/__init__.py": "sha256:e9124811ebe11ad32c98ab462b2eed634d4ddca08792981d0f353b223a239902", + "src/warden/routing/catalog.py": "sha256:5b95810df9e6102a33d3a116a096cd75292c8572bad721fe6baa772caacc9bc3", + "src/warden/scripts/ops_ssh_wrapper.py": "sha256:e8c289882f2ad439d2eb32f9a871967420fefaffe745c9271c1a2e948fbdfe50", + "src/warden/scripts/__init__.py": "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + } + }, + "repo_manager_identifiers_sha256": "sha256:4e35fcd009c20822574fd33171176bd14a1ff2dd5af8b0bb2820954974593a2a", + "authorization": "Root accepted the exact source fixture patch for source integration. This packet adds no owner/native authority or catalog/pin promotion. No prepared script has been executed.", + "publishable": false, + "requires_allocated_manifest": true, + "publication_file_paths": [ + "sso-mfa/k8s/keycape/test_admit_1_3_type.py", + "sso-mfa/k8s/keycape/test_keycape_registration_adapter.py", + "sso-mfa/k8s/keycape/kernel-catalog.fixture.yaml", + "workplans/NK-WP-0048-security-case-types.md" + ] +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r1/run_validation.py b/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r1/run_validation.py new file mode 100644 index 0000000..12f783d --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r1/run_validation.py @@ -0,0 +1,73 @@ +"""Prepared canonical source validation; --execute launches only the two scoped tests.""" +from pathlib import Path +import argparse +import hashlib +import json +import re +import shutil +import subprocess +import tempfile + +PACKET = Path(__file__).resolve().parent +ROOTS = {'net-kingdom': Path('/home/worsch/net-kingdom'), 'ops-warden': Path('/home/worsch/ops-warden')} + +def sha(data): + return 'sha256:' + hashlib.sha256(data).hexdigest() + +def sources(manifest): + allowed = [] + for owner, rows in manifest['validation']['source_hashes'].items(): + for relative, expected in rows.items(): + path = ROOTS[owner] / relative + assert not path.is_symlink(), str(path) + assert sha(path.read_bytes()) == expected, str(path) + allowed.append(str(path.resolve())) + return allowed + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--execute', action='store_true') + args = parser.parse_args() + manifest = json.loads((PACKET / 'publication-inputs.json').read_text()) + allowed = sources(manifest) + revision = subprocess.check_output(['git', '-C', str(ROOTS['ops-warden']), 'rev-parse', 'HEAD'], text=True).strip() + assert revision == manifest['warden_revision'] + if not args.execute: + print(json.dumps({'prepared_only': True, 'public_source_files_verified': len(allowed), + 'tests': manifest['validation']['tests']})) + return + protected = {name: sha((ROOTS['net-kingdom'] / name).read_bytes()) for name in manifest['protected_files']} + scratch = Path(tempfile.mkdtemp(prefix='nk48-catalog-canonical-validation-')) + for name in ['home', 'tmp', 'guard', 'pycache']: + (scratch / name).mkdir() + shutil.copyfile(PACKET / 'validation-guard.py', scratch / 'guard/sitecustomize.py') + shutil.copyfile(PACKET / 'validation-child.py', scratch / 'child.py') + (scratch / 'allowed.json').write_text(json.dumps(allowed)) + (scratch / 'pytest.ini').write_text('[pytest]\n') + environment = { + 'HOME': str(scratch / 'home'), 'TMPDIR': str(scratch / 'tmp'), 'PATH': '/usr/bin:/bin', + 'PYTHONPATH': str(scratch / 'guard') + ':' + str(ROOTS['ops-warden'] / 'src'), + 'PYTHONDONTWRITEBYTECODE': '1', 'PYTHONPYCACHEPREFIX': str(scratch / 'pycache'), + 'LANG': 'C.UTF-8', 'WARDEN_REVIEW_ALLOWED_SOURCE_LIST': str(scratch / 'allowed.json'), + 'WARDEN_REVIEW_VALIDATOR_PREFIX': str(scratch / 'tmp/warden-case-validator-'), + } + with (PACKET / 'canonical-validation.log').open('w') as log: + result = subprocess.run([manifest['validation']['python'], str(scratch / 'child.py')], + cwd=ROOTS['net-kingdom'], env=environment, + stdout=log, stderr=subprocess.STDOUT) + sources(manifest) + assert protected == {name: sha((ROOTS['net-kingdom'] / name).read_bytes()) for name in manifest['protected_files']} + log_text = (PACKET / 'canonical-validation.log').read_text() + counts_verified = bool(re.search(r'\b31 passed, 53 subtests passed\b', log_text)) + counts_verified = counts_verified and not re.search(r'\b[1-9][0-9]* (?:skipped|deselected)\b', log_text) + receipt = {'executed': True, 'counts_verified': counts_verified, 'returncode': result.returncode, 'warden_revision': revision, + 'net_kingdom_head': subprocess.check_output(['git', '-C', str(ROOTS['net-kingdom']), 'rev-parse', 'HEAD'], text=True).strip(), + 'verified_public_source_files': len(allowed), 'tests': manifest['validation']['tests'], + 'log_sha256': sha((PACKET / 'canonical-validation.log').read_bytes()), 'scratch': str(scratch), + 'limitations': 'Source fixture tests only; no native owner execution, acceptance, catalog promotion or pin.'} + (PACKET / 'canonical-validation.json').write_text(json.dumps(receipt, indent=2) + '\n') + print(json.dumps(receipt, indent=2)) + raise SystemExit(result.returncode or (0 if counts_verified else 1)) + +if __name__ == '__main__': + main() diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r1/task-T35.md b/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r1/task-T35.md new file mode 100644 index 0000000..78735f2 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r1/task-T35.md @@ -0,0 +1,38 @@ + +## T35 — Align source admission fixtures with the authenticated catalog and attended-session API + +```task +id: NK-WP-0048-T35 +status: done +priority: high +``` + +Completed 2026-10-17, source fixture compatibility only. Distinct from T32's +independent agent-mint regression repair: both remaining opaque catalog byte +placeholders now use one well-formed synthetic catalog whose exact bytes are +bound by the fixture evidence verifier. The fixture mirrors the six relevant +Warden lane shapes; four draft statuses are hypothetically active, while both +Apps establishing login references remain unresolved. No real catalog entry, +owner login choice or production parameter pin changes. + +Against the reviewed mode-aware Warden source now integrated at +`ae7bc73b4f4bac22454144959b870d161498e0af`, scratch validation passes 31 tests +and 53 subtests. Five resolvable 1.3 descriptors conditionally admit under +fixture-only acknowledgment and pinning; both Apps attended steps refuse before +execution. Changed catalog bytes refuse, all independent mint-guard tests still +reach their intended guard, and the registration fixture's actual kernel +run/rollback and uncertain-result paths pass with synthetic owner callbacks. +The registration fixture explicitly aligns its in-memory proposed type lane +with its current descriptor; canonical 1.1 is unchanged. + +The same tests against predecessor `117fec9241e7b985dfb187b992e173d0a5dc3700` +pass 31 tests and 51 subtests, with only the two new Apps refusal assertions +failing because that predecessor lacks the attended-session catalog check. +Tests target the reviewed current API; the parser or Apps gate is not weakened +for predecessor compatibility. Evidence and root source review: +`the-custodian/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-update-r1/` +and `nk48-catalog-fixture-publication-r1/`. Canonical validation/publication +receipts belong in the latter packet when performed. This records source +fixture work, not native T03, owner assent, exact-type acknowledgment, PIN-1, +composition/admission completion under T16 or the native first case under T06. +T33 and T34 retain their existing issuer-store and consumer-inverse work. diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r1/validation-child.py b/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r1/validation-child.py new file mode 100644 index 0000000..7e659ae --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r1/validation-child.py @@ -0,0 +1,43 @@ +"""Focused canonical-source test process. Network/native/estate-read guard required.""" +from pathlib import Path +import os +import socket +import subprocess +import sys +import sitecustomize + +assert sitecustomize.ACTIVE is True +assert not {'BAO_TOKEN', 'VAULT_TOKEN', 'WARDEN_AGENT_ID'} & os.environ.keys() +assert list(Path.home().iterdir()) == [] +try: + socket.socket() +except RuntimeError as error: + assert str(error) == 'fixture validation: network disabled' +else: + raise AssertionError('network guard inactive') + +import warden.case_inputs as inputs +import pytest + +assert Path(inputs.__file__).resolve() == Path('/home/worsch/ops-warden/src/warden/case_inputs.py') +PREAMBLE = '''import sys as _review_sys\ndef _review_guard(event,args):\n if event.startswith('socket.') or event in {'subprocess.Popen','os.system','os.exec','os.posix_spawn'}:\n raise RuntimeError('review validator: network/native disabled')\n if event=='open' and isinstance(args[0],str) and (args[0].startswith('/home/') or args[0].startswith('/root/')):\n raise RuntimeError('review validator: ambient home denied')\n_review_sys.addaudithook(_review_guard)\n''' +old_run = subprocess.run + +def run_pinned(arguments, *positional, **keywords): + if (type(arguments) is not list or len(arguments) != 5 + or arguments[:3] != [sys.executable, '-I', '-c'] + or arguments[3] != inputs._VALIDATE + or not str(arguments[4]).startswith(sitecustomize.VALIDATOR_PREFIX) + or keywords.get('env') != {}): + raise RuntimeError('fixture validation: unexpected process refused') + return old_run([*arguments[:3], PREAMBLE + arguments[3], arguments[4]], + *positional, **keywords) + +inputs.subprocess.run = run_pinned +raise SystemExit(pytest.main([ + '-q', '-p', 'no:cacheprovider', '--noconftest', + '-c', str(Path(os.environ['TMPDIR']).parent / 'pytest.ini'), + '--rootdir=/home/worsch/net-kingdom', + '/home/worsch/net-kingdom/sso-mfa/k8s/keycape/test_admit_1_3_type.py', + '/home/worsch/net-kingdom/sso-mfa/k8s/keycape/test_keycape_registration_adapter.py', +])) diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r1/validation-guard.py b/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r1/validation-guard.py new file mode 100644 index 0000000..05f7294 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r1/validation-guard.py @@ -0,0 +1,35 @@ +"""Copied as sitecustomize.py into a fresh scratch directory by run_validation.""" +import json +import os +from pathlib import Path +import sys + +ALLOWED = frozenset(json.loads(Path(os.environ['WARDEN_REVIEW_ALLOWED_SOURCE_LIST']).read_text())) +VALIDATOR_PREFIX = os.environ['WARDEN_REVIEW_VALIDATOR_PREFIX'] + +def guard(event, args): + if event.startswith('socket.'): + raise RuntimeError('fixture validation: network disabled') + if event == 'subprocess.Popen': + command = args[1] + if (type(command) is list and len(command) == 5 + and command[:3] == [sys.executable, '-I', '-c'] + and command[3].startswith('import sys as _review_sys\ndef _review_guard(') + and str(command[4]).startswith(VALIDATOR_PREFIX) and args[3] == {}): + return + raise RuntimeError('fixture validation: native process disabled') + if event in {'os.system', 'os.exec', 'os.posix_spawn'}: + raise RuntimeError('fixture validation: native process disabled') + if event == 'open' and isinstance(args[0], str): + path = str(Path(args[0]).absolute()) + if path.startswith(('/home/', '/root/')): + # Only exact prehashed public source may be read from the estate. + if str(Path(path).resolve()) not in ALLOWED: + raise PermissionError('fixture validation: unlisted estate file disabled') + mode, flags = args[1:3] + if ((isinstance(mode, str) and any(c in mode for c in 'wax+')) + or (flags & (os.O_WRONLY | os.O_RDWR | os.O_CREAT | os.O_TRUNC | os.O_APPEND))): + raise PermissionError('fixture validation: canonical source writes disabled') + +sys.addaudithook(guard) +ACTIVE = True diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r1/workplan.candidate.md b/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r1/workplan.candidate.md new file mode 100644 index 0000000..82f67d2 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r1/workplan.candidate.md @@ -0,0 +1,1647 @@ +--- +id: NK-WP-0048 +type: workplan +title: "Establish SecurityCaseTypes so that known security cases are light" +domain: infotech +repo: net-kingdom +status: active +flavor: implementation +owner: claude-code +topic_slug: netkingdom +created: "2026-10-08" +updated: "2026-10-17" +depends_on: [] +related_repos: + - ops-mason + - ops-warden + - key-cape + - railiance-platform + - informed-decision +state_hub_workstream_id: "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" +--- + +# NK-WP-0048 - Establish SecurityCaseTypes so that known security cases are light + +## Why + +The founder's direction (2026-10-08): setting up a security integration of a +kind that has been done before should be easy, meaning accept the case, then +ops-mason arranges it and ops-warden follows through. Setting up a new kind +needs ample documentation and tests and an informed-decision, because its shape +is part of the security architecture. + +Today every integration is hand-built: per-client helper scripts, per-lane +custody, founder checklists. The trigger was KEY-WP-0038 (Forgejo as an OIDC +client), whose next step was going to be yet another hand-written rollout +script. + +This plan continues the pattern work of NK-WP-0008 (pattern infospace), +NK-WP-0009 (tutorials) and NK-WP-0030 (scenario composition). It turns patterns +into **SecurityCaseTypes** that ops-mason and ops-warden can execute. Canon: +`canon/standards/security-case-types_v0.1.md` (proposed). + +Companion plans: ops-mason `MASON-WP-0010` (materialization) and ops-warden +`WARDEN-WP-0044` (follow-through). + +## Tasks + +```task +id: NK-WP-0048-T01 +status: done +priority: high +state_hub_task_id: "930ac3e9-20ec-5473-98cd-3fe8fddb730f" +``` + +### T01 — Accept the terminology and the canon + +Founder review of `canon/standards/security-case-types_v0.1.md`, including its +section 10 open questions. On acceptance the canon status goes from `proposed` +to `accepted`. + +2026-10-08: accepted by the founder (in session). Canon status `accepted`; +section 10's open questions are carried forward to the first type (T04, T05). + +```task +id: NK-WP-0048-T02 +status: done +priority: high +state_hub_task_id: "19c25d96-a55c-58f0-9168-3fdbc4955fc2" +``` + +### T02 — Inventory the de-facto types already built by hand + +Read-only. List every security integration built so far and group them by shape. +Known starting points: + +- browser OIDC relying parties on key-cape: informed-decision-approver, + coulomb-social, user-engine portal, openbao-admin, and Forgejo (pending) +- confidential service clients with `client_credentials`: secrets-engine + approval, requester and openbao clients, approval-engine operator +- workload KV read lanes on OpenBao: the railiance-platform credential lane + designs and CCRs, for example `railiance-backup-offsite-lane` +- short-lived SSH certificates through ops-warden (`warden sign`) +- tunnelled operator access (ops-bridge) + +For each: the repositories involved, the hand-built steps, attended human steps, +custody paths, verification evidence, and rollback (if any). Output +`docs/security-case-type-inventory.md`, ranking candidate types by how often the +shape recurs and how much manual work each instance costs today. + +2026-10-08: done, `docs/security-case-type-inventory.md`. 36 of 37 CCRs are +already `request_type: workload-kv-read`, a de-facto type reviewed each time as +if it were new. Browser clients: 5 hand-built plus Forgejo, each with its own +rollout script. Confidential service clients need a registration plus two or +three KV-read CCRs each. A forge read identity is needed in bulk by +ORGREF-WP-0002-T06. Recommendation: oidc-browser-relying-party first (unblocks +the forge), workload-kv-read second (largest payoff), then forge-read-identity. + +```task +id: NK-WP-0048-T03 +status: done +depends_on: [NK-WP-0048-T01] +priority: high +state_hub_task_id: "e487975b-73b1-516c-971d-da4cbbb93063" +``` + +### T03 — Schema, template and validator + +- `canon/schemas/security-case-type_v0.1.schema.json` and + `security-case_v0.1.schema.json` +- `security-case-types/_template/` with `type.yaml`, `README.md` and tests to copy +- a validator, extending `tools/security-scenario-composer/`, that checks a type, + checks a case against its type, and renders the case into the composer's + deterministic plan (still `execution.permitted: false`) + +Acceptance: the validator rejects missing sections, secret-shaped parameter +values, unpinned providers and cases against non-`implemented` types, with tests +for each. + +2026-10-08: done. `canon/schemas/security-case-type_v0.1.schema.json` and +`security-case_v0.1.schema.json`; `tools/security-case-types/security_case_validator.py` +(`validate-type`, `validate-case`, `render`, reusing the scenario composer); +`security-case-types/_template/` (a valid, runnable example type over +`net-kingdom.local-identity`, with README sections, an example case and tests that +travel with every copy). 31 tests pass, including the composer's existing suite. + +```task +id: NK-WP-0048-T04 +status: done +depends_on: [NK-WP-0048-T02, NK-WP-0048-T03] +priority: high +state_hub_task_id: "2fffe112-7d55-5680-9952-78d74f44f87f" +``` + +### T04 — Define the first type: browser OIDC relying party + +`security-case-types/oidc-browser-relying-party/`, version 1.0: + +- parameters: client id, display name, exact redirect URIs, scopes, client type + (confidential or public), MFA pin, the groups the consuming service maps, + custody path for the secret (confidential only), consuming service and its + owner +- composition: key-cape client registration (key-cape declares it as a playbook + capability), secret minting into custody (railiance-platform), LLDAP group + existence and membership (net-kingdom identity-provisioner), handover to the + consuming service +- materialization (ops-mason) and follow-through (ops-warden) sections +- verification: discovery lists the redirect URI; a test sign-in returns the + expected claims for one human; anonymous access to the custody path is denied +- rollback: remove exactly this client and its custody path +- README: threat model, MFA rationale, why redirect URIs are exact, which steps + still need an attended login and how that is minimised +- reference instances: informed-decision-approver, coulomb-social, user-engine + portal, openbao-admin + +Acceptance: complete per canon section 3, tests green, status `proposed`. + +2026-10-08: done. `security-case-types/oidc-browser-relying-party/` 1.0 (proposed): +`type.yaml` with 12 parameters (the custody reference is required only for +confidential clients), composition over key-cape's new +`key-cape.oidc-client-registration` declaration (contract PASS, `maturity: +planned`), materialization and follow-through, verification and rollback, all +conditional on client type where it matters. One attended step, +`register-client`, justified by the live registry being a Kubernetes Secret. +README with threat model, alternatives, an honest fit table (portal and vergabe +fit; Forgejo is the first confidential case; openbao-admin, coulomb.social and +informed-decision-approver do not fit 1.0), and known limits. Examples: Forgejo +(checked against the accepted request) and a public portal. The validator gained +`when` / `required_when` conditions and provider discovery. 43 tests pass. +ops-mason's and ops-warden's sections are drafted in the type. Their owners +review them in MASON-WP-0010-T01 and WARDEN-WP-0044-T01 before acknowledgment +(T05). + +```task +id: NK-WP-0048-T05 +status: done +depends_on: [NK-WP-0048-T04, MASON-WP-0010-T01, WARDEN-WP-0044-T01] +priority: high +state_hub_task_id: "3d987d3d-0797-538b-86e4-51185a327ae2" +``` + +### T05 — Acknowledge the type through informed-decision + +Prepare the Decision Memo from the type's README and `type.yaml` and submit it +through informed-decision's production intake. On acknowledgment the type +moves to `acknowledged`. + +2026-10-08: Decision Memo prepared. Hub decision `e418263e-a71f-4f01-a970-c87c9e187c09`. +`security-case-types/oidc-browser-relying-party/decision/`: `packet.md` (question, +one act, what acknowledging does not do, the shape, alternatives, attended +steps, limits, evidence), `build_intake.py` and the built `intake.json` (memo +class `decision-record`, review group `net-kingdom-admins`, resolves the hub +decision on answer). The envelope passes informed-decision's own +`validate_submission`. Queuing it is an attended operator step, a write inside +the production dispatcher pod: + + kubectl -n state-hub exec -i deploy/state-hub-memo-dispatch -- \ + python -m api.services.memo_dispatch --queue /data/private enqueue --envelope /dev/stdin \ + < security-case-types/oidc-browser-relying-party/decision/intake.json + +The answer then appears for review in informed-decision. + +2026-10-08: queued by the founder (`pending`, `queued`) after one refusal: the +first envelope's idempotency key was not State Hub's stable +`state-hub..` form, and the builder now validates against both +sides. The dispatcher delivered it to informed-decision (ledger 7 → 8 +delivered). T05 waits for the founder's answer in informed-decision, which +resolves hub decision `e418263e`. + +2026-10-08: ops-mason's review (MASON-WP-0010-T01) folded into the type, still +1.0 `proposed`: `secret_ref` is no longer a parameter, the type derives +`platform/workloads/keycape/{client_id}`; policies are +`workload-kv-read-keycape-{client_id}-eso` and +`workload-kv-read-keycape-{client_id}-{consuming_service}`, each with a templated +body (`read` on that one data path); new `openbao_role` +`external-secrets-keycape-{client_id}` (Kubernetes auth, ESO service account, +15m, no default policy); catalog entry `{consuming_service}-keycape-client`; +the two rollback steps on the KV path collapse into one metadata delete. The +schema and validator now require a `body` on policy and role items. 105 tests +pass. Two consequences: the delivered memo describes the type before these +changes (a case-set custody reference, two differently named policies, no ESO +role), so the founder answers on a superseded shape unless the memo is +re-sent; and Forgejo's custody path moves from the accepted request's proposed +`platform/workloads/forgejo/netkingdom-oidc-client` to +`platform/workloads/keycape/forgejo`, which key-cape (KEY-WP-0038) and +railiance-apps (RAPPS-WP-0016-T02) need to adopt in T06. ops-warden's review +(WARDEN-WP-0044-T01) is still in progress. + +2026-10-08: revised memo `nk-wp-0048-t05-oidc-browser-relying-party-r2` built +(`packet.md`, `intake.json`, packet `sha256:af6435e2…`); it states that it +supersedes the first memo and passes informed-decision's and the dispatcher's +validation. It needs a new memo id because the dispatcher refuses new content +under a delivered idempotency key (`idempotency_conflict`). Queuing it is the +same attended step as above. Both memos serve hub decision `e418263e`; the +first answer resolves it, so the first memo must not stay open: an accept on it +would acknowledge the superseded shape, and a decline would resolve the +decision against r2. Close it with `return` (a reviewer verb that requests a +new revision and does not resolve the hub decision; only accept and decline +do), commenting that r2 supersedes it, then answer r2. `withdraw` exists in +the vocabulary but is not offered to reviewers, and lifecycle `revise` only +applies to lifecycle-registered memos, not intake-delivered ones. + +2026-10-09: the founder reports r2 enqueued and the first memo returned. At +close, hub decision `e418263e` still showed `open` (not verified whether r2 +was answered or the answer is still in transit; the dispatcher ledger was not +read). ops-warden's review (WARDEN-WP-0044-T01, ops-warden +`history/2026-10-08-oidc-rp-follow-through-review.md`, d470f2e) arrived at +21:25, before r2 was built, and is not in r2. Required before WARDEN-WP-0044-T02: +R1 a per-step `lane` (missing: identity-provisioner caller credential, +railiance-platform broker grant), R2 canon meaning of `warden_executes: true` +(caller proxy, caller identity, no standing ops-warden token; ADR-0002), R3 a +per-step `value_handling`. Refinements R4 (one type-level catalog lane instead +of a per-case entry, which conflicts with ops-mason's item 5), R5 (ESO Ready gate +before bind-env), R6 (exact accepted memberships), R7 (mfa-pinned and +secret-delivered checks). Next: if r2 was accepted, fold R1-R3 and R5-R7 in as +a follow-up and settle R4 between ops-warden and ops-mason before T06; if not, +hold r2, fold them in and build r3. + key-cape and railiance-apps were told about Forgejo's new custody +path. + +2026-10-09: r2 was not answered (hub decision `e418263e` open; dispatcher 9 +delivered), so r2 is held and r3 replaces it. ops-warden withdrew R4 for R4′ +(message a66a9204): one catalog entry per case with its body templated in the +type. Folded in: + +- Canon section 3: a narrow meaning of `warden_executes: true` (caller proxy, + caller identity, no standing credential; ops-warden ADR-0002), plus per-step + `lane`, `value_handling` and an optional `ready_check`. A catalog entry body is + now required, like policy and role bodies. +- Schema and validator enforce all of these. +- The type: lanes `identity-provisioner-groups` and + `keycape-client-secret-generate` (both new) and `key-cape-oidc-login`; + `mint-secret` is `generated_in_custody`; a Ready gate on `deliver-secret`; + `ensure-groups` adds exactly the accepted case's memberships; new checks + `secret-delivered` and `mfa-pinned`; the catalog-entry body follows R4′. +- 110 tests pass. + +Memo r3 (`nk-wp-0048-t05-oidc-browser-relying-party-r3`, packet +`sha256:726dfc01…`) passes informed-decision's and the dispatcher's validation. +The founder queues it with the same command, returns r2 with a comment that r3 +supersedes it, then answers r3. + +Prerequisites for T06, not for T05: +- the two new lanes, each through its owner; +- a founder-approved rollout of an identity-provisioner image containing + 630ebfd (the deployed one is main-6c4fcaf from 2026-09-24, without + `/v1/groups/ensure`). + +ops-mason's request to create LLDAP group `fluid-telegram-operators` (message +259ec26d) was answered: not possible from here by that path; the founder creates +it in the LLDAP admin UI. + +2026-10-09: ops-warden finished its side of WARDEN-WP-0044-T01 (message +`58d33f40`, commit 0e7ceb8): the revised type conforms to `warden case +check-type`, with no further changes asked of the type. This acknowledges that +review. Its later addendum R8 (message `af274783`, ops-warden +`history/2026-10-08-oidc-rp-follow-through-review.md`) showed that lane +`identity-provisioner-groups` cannot exist as declared: the provisioner's only +HTTP credential is user-engine's runtime token, and its NetworkPolicy admits +only user-engine pods, so a caller-proxy lane would act as user-engine. Taken +option 1 (ops-warden's recommendation): `ensure-groups` and its rollback are +attended, in the `register-client` session on `key-cape-oidc-login`, run inside +the provisioner pod; no token is read. Option 2 (an operator credential of its +own on the provisioner) stays open for a later version. `warden case +check-type` now reports only `keycape-client-secret-generate` +(railiance-platform) as missing. 89 tests pass. + +Hub decision `e418263e` was still open, so memo r4 +(`nk-wp-0048-t05-oidc-browser-relying-party-r4`, packet `sha256:452ea421…`) +replaces r3; it passes informed-decision's and the dispatcher's validation. The +founder queues it with the same command, returns r2/r3 if still open with a +comment that r4 supersedes them, then answers r4. + +Prerequisites for T06 are now: lane `keycape-client-secret-generate` +(railiance-platform), and a founder-approved rollout of an identity-provisioner +image containing 630ebfd. + +2026-10-09: done. The founder queued r4 (`pending`, `queued`), returned r3 as +superseded and accepted r4 in informed-decision. informed-decision's hub +write-back is off in production (`INFD_HUB_WRITEBACK` unset), so net-kingdom +resolved hub decision `e418263e` from the founder's report, naming memo r4 in +the rationale. The type is `acknowledged`; it becomes `implemented` with T06. + +```task +id: NK-WP-0048-T06 +status: wait +depends_on: [NK-WP-0048-T05, NK-WP-0048-T08, NK-WP-0048-T09, NK-WP-0048-T12, NK-WP-0048-T13, NK-WP-0048-T14, NK-WP-0048-T15, NK-WP-0048-T16, NK-WP-0048-T17, NK-WP-0048-T18, NK-WP-0048-T20, MASON-WP-0010-T03, WARDEN-WP-0044-T03] +blocking_reason: "Type1.2 architecture has a confirmed source-only founder acknowledgment; canonical1.1 remains unchanged/default and the1.2 reference case stays proposed. Needs explicit version selection/reference-source integration without normal admission bypass, closed owner adapters plus global/native receipt verifier (WARDEN-WP-0044-T03), independently verified six-object structural materialization and CCR42 claims/created-only provenance, exact broker/case/source reconciliation, then separately authorized native mint/delivery and attended registry/consumer verification. Source acknowledgment, a bundle or local tests do not prove implemented/materialized/live status." +priority: high +state_hub_task_id: "d9b26bcf-a7c6-59a2-92a0-f913d76a203c" +``` + +### T06 — First KnownSecurityCase: Forgejo + +Write `security-cases/forgejo-netkingdom-oidc.yaml` from the parameters the +founder accepted in KEY-WP-0038-T01 (`key-cape/docs/forgejo-oidc-client-request.yaml`), +so that acceptance carries over as the case acceptance. ops-mason materializes it; +ops-warden follows through and verifies it. The type becomes `implemented` once +this reference case is live. + +This replaces the hand-built path proposed for KEY-WP-0038-T02. + +2026-10-09: case written, `security-cases/forgejo-netkingdom-oidc.yaml`, status +`accepted`: the example's parameters plus the founder's 2026-10-08 acceptance of +the key-cape request (owner and founder), with the custody-path derivation +covered by the type's acknowledgment (memo r4). It validates and renders (plan +`execution.permitted: false`; three attended steps on `key-cape-oidc-login`); +it needs `--allow-unimplemented` until it is live, since it is the case that +makes the type `implemented`. A test keeps it equal to the type's example. +Still open before it runs: ops-mason renders and builds it (MASON-WP-0010-T02, +T03, unblocked by T05), ops-warden plans and runs it (WARDEN-WP-0044-T02, T03), +railiance-platform's `keycape-client-secret-generate` lane (asked by ops-mason +`789feb39` and ops-warden `d9f889b1`, both unread), and a founder-approved +identity-provisioner rollout containing 630ebfd. + +2026-10-09: identity-provisioner rolled out, founder-approved in session: +`main-630ebfd` (`sha256:bafb4860…`, built by the image workflow) replaces +`main-6c4fcaf` (`sha256:ffacd5d7…`; only change: 630ebfd, additive). Applied +with `kubectl set image` on the Deployment only (generation 17 → 18, 1/1 Ready); +the file's Service, NetworkPolicies and Ingress were not reapplied. Verified in +the pod: `ensure_service_groups` and `revoke_service_groups` present, `/readyz` +200; no token used, no group changed. Rollback: `kubectl -n sso set image +deploy/identity-provisioner provisioner=forgejo.coulomb.social/coulomb/identity-provisioner@sha256:ffacd5d790a5b7800e0535f3aa95b7de52c7a68e4f575b9192c3e1622fa672c2`. +The pin in `sso-mfa/k8s/identity-provisioner/deployment.yaml` follows. + +2026-10-09: ops-warden closed WARDEN-WP-0044-T01 and shipped `warden case plan` +(T02, message `06303d1d`); the accepted Forgejo case is refused until ops-mason +materializes it, and a materialized copy plans cleanly once +`keycape-client-secret-generate` exists. Its observation that `bind-env` carries +a literal `KEYCAPE__CLIENT_SECRET`: the validator now renders a +`{param:env}` format spec (upper case, `-` as `_`, as `keycape-client.py` +derives the name) and rejects unknown specs, and follow-through and rollback +text is now checked for unknown placeholders too (patch-level, canon section 6). +The type keeps the literal until ops-warden's planner (`text.format`) renders the +spec as well; asked of ops-warden. 53 tests pass. + +2026-10-10: ops-warden rendered `{param:env}` in `warden case plan` +(23ba584, message `c1df14b7`; other specs and `!conversion` refused). The type +adopts it as a 1.0 patch (canon section 6): `bind-env` and its rollback now read +`KEYCAPE_{client_id:env}_CLIENT_SECRET`. ops-mason reports the Forgejo case +rendered and built (message `1ebddae9`; CCR drafts handed to railiance-platform), +so T06 now waits on the platform apply, the mint-secret lane and the attended +register-client session. + +2026-10-10: key-cape adopted the type's custody path (key-cape ab5cccd, message +`88a4559c`): `docs/forgejo-oidc-client-request.yaml` and KEY-WP-0038-T02 name +`platform/workloads/keycape/forgejo`, field `CLIENT_SECRET`, delivered as +`sso/keycape-forgejo-client` through `external-secrets-keycape-forgejo`, and the +`forgejo/netkingdom-oidc-client` proposal is withdrawn. This matches the type and +the Forgejo example; no change needed here. + +2026-10-10: key-cape's type-composition review (key-cape 6038483, message +`8e5ed5f6`) found the composition sound with two blocking findings, taken as a +1.0 patch (stricter validation and documentation, canon section 6): +R1, redirect URIs now refuse `?` and `#` in the type pattern and in +`keycape-client.py`'s `REDIRECT` (KeyCape refuses `?` at startup and the single +replica would stop); R2, `allowed_scopes` always includes `groups`, the renderer +refuses `access_groups` without it, and `access_groups` now says the consumer +enforces the gate because KeyCape has none; R3, `mfa_required` "false" means the +per-user privacyIDEA policy; R4, register-client's justification records the +restart for every client, the quiet window, and that the rendered registry must +pass KeyCape's client validation before it. The accepted Forgejo case and both +examples still validate; 116 tests pass. +Open, needs a decision: R2's verification step that the consumer enforces the +group gate is a new verification step, hence 1.1 (minor), and the validator pins +cases to the exact type version, so the accepted 1.0 Forgejo case would need +re-pinning. Also open with key-cape: a KeyCape entry point that validates a +rendered client registry without starting the server; until then +`keycape-client.py` mirrors the redirect rule. + +2026-10-10: founder go for 1.1, so no 1.0 run overlaps. The type adds +verification `consumer-enforces-groups`, and hand-over now requires the consumer +to enforce `access_groups` (minor, canon section 6; risk class unchanged, no new +acknowledgment; history in the type README). The accepted Forgejo case and both +examples are re-pinned to `oidc-browser-relying-party@1.1` before any apply; the +acknowledgment intake keeps `@1.0` as the record of what was acknowledged. +ops-mason's build and ops-warden's case-ref digest predate this and need a +re-render. + +2026-10-10: ops-mason re-rendered the Forgejo case from `@1.1` (message +`22c6bba6`; plan digest `af1f4f81…`, no collisions) and rebuilt the bundle. The +rendered OpenBao objects are identical to 1.0, so the already applied +CCR-2026-0041 stands; 1.1 changes only consumer-side steps. key-cape shipped +`keycape check-config` (c528daa, message `a7b0aeff`): KeyCape's startup checks +without secret values, including the new duplicate-clientId rule. +`keycape-client.py --check-config ` now runs it on the live and the +proposed registry, fed on stdin, and apply refuses a proposed registry that +fails; register-client's entry point names the option (1.1 patch). The deployed +image predates c528daa, so the binary is built from key-cape source for now. +Before the next KeyCape rollout (Forgejo register-client or the CCR-2026-0018 +window), the attended operator runs `inspect --check-config` so the live +registry is checked too. + +```task +id: NK-WP-0048-T07 +status: wait +depends_on: [NK-WP-0048-T06] +blocking_reason: "The next types follow once the first one is proven end to end." +priority: medium +state_hub_task_id: "c3c9c3ae-4f23-5ba7-9e6f-e63bcf29b9f7" +``` + +### T07 — Queue the next types + +From the T02 ranking, open the next type drafts (likely the confidential service +client and the workload KV read lane), each to be acknowledged on its own. +Record which existing hand-built instances get adopted as cases, without +rewriting them in place. + +```task +id: NK-WP-0048-T08 +status: done +priority: high +state_hub_task_id: "8e519082-dc8b-5e46-b660-03d03e55dcf6" +``` + +### T08 — identity-provisioner: ensure service-access groups + +The type's `ensure-groups` step calls `identity-provisioner groups ensure --groups + --members `. The provisioner already has idempotent +`_ensure_group` and `_add_group`, but exposes only tenant-scoped operations. Add +the general operation (create if absent, add the listed memberships, never +remove others), its rollback (remove only this case's memberships), tests, and +the ops-warden route for its service identity. + +2026-10-08: done. `identity-provisioner/provisioner.py` gained +`ensure_service_groups` (`POST /v1/groups/ensure`) and `revoke_service_groups` +(`POST /v1/groups/revoke`), behind the existing service-token check. Names +matching `tenant*`, `netkingdom-*` and `lldap_*` are reserved; members must +already exist; everything is validated before any change; memberships are read +back before success; nothing else is ever removed; groups are deleted only when +asked and empty. 7 new tests (39 in the provisioner suite). The type's +`ensure-groups` step and its rollback now name these endpoints. The ops-warden +route for the provisioner's service token belongs to WARDEN-WP-0044-T01. + +```task +id: NK-WP-0048-T09 +status: done +priority: high +state_hub_task_id: "0ee12dfc-8260-5038-bd39-cc7fb4113be0" +``` + +### T09 — The `keycape-client` entry point and its declaration + +Planned first as key-cape KEY-WP-0038-T04. Moved here because the machinery a +live registration needs already lives in net-kingdom, where KeyCape is deployed +(`sso-mfa/k8s/keycape/`): the contained kubectl, the cluster-identity pin and the +issuer pin. A copy in key-cape would drift. + +2026-10-08: done. `sso-mfa/k8s/keycape/keycape-client.py` derives the KeyCape +client record from a case (`client-from-case`). It adds or removes exactly one +client with the existing safeguards (byte-preserving merge, exact match or +refuse, shared-reference refusal, compare-and-swap, read-back, contained errors), +requires the Deployment env binding first for confidential clients, and requires +the case acceptance record for `apply` instead of a per-client founder decision +id. 15 tests, including a regression showing that the vergabe case reproduces +its hand-built registration exactly; all 71 helper tests pass. Declaration +`capabilities/playbooks/net-kingdom.keycape-client-registration.yaml` (contract +PASS, `maturity: implemented`) replaces key-cape's draft. The type now pins it, +and for confidential clients adds `deliver-secret` (delivery from custody into +the sso namespace) and `bind-env` (Deployment env), attended in the same session +as `register-client`. The per-client helpers stay until their clients are +adopted as cases. + +2026-10-10 — NetKingdom owner review of the execution seam, source only: +ops-warden/docs/security-case-execution-proposal.md SHA256 78ab1439e4c5beb205aeccf15b1b2e706511be55ab195c5a3aa682f326c7c77c +was reviewed against INTENT, SecurityCase canon, acknowledged type1.1, current +case and owner source. docs/security-case-execution-owner-review.md supports +the closed adapter/receipt direction with explicit versioned schema, structural +materialization provenance, unchanged caller/session boundaries, safe CAS +reconciliation and exact-created-object rollback requirements. This is owner +review, not approval. Supporting file digests are in the adjacent sources JSON. + +Current source exposes a circular wait: ops-mason T03 waits on mint while Warden +and the broker refuse a non-materialized case. Structural acceptance therefore +needs its own native pinned receipt before mint, not a successful mint before +materialized. The platform broker additionally pins type1.0 while Forgejo is +accepted at1.1; platform-owner alignment and tests are required, not an unchecked +version bypass. T06 remains wait. No case/type lifecycle status, grant, authority +or live runtime changed by this review. A proposed informed-decision would +adopt the source contract only; native materialization and execution acceptance +stay distinct. + +Final proposal re-review: staged Warden proposal SHA256 78ab1439e4c5beb205aeccf15b1b2e706511be55ab195c5a3aa682f326c7c77c +adds only the explicitly local reducer appendix, not an accepted wire protocol +or executor. The exact-final review and sources JSON supersede the earlier +1035f049... proposal review. Platform staged the exact type1.1 broker correction +with ten fake-backend subprocess checks (script SHA256 +9576cd211cfc06526ec4cc9bd0a5b06f0f59287a68931bb2d6e3592a951ddc7d). +The next source work is the versioned closed adapter/receipt/materialization +contract, not another relaxation of the broker's status gate. Native work still +requires installed broker provenance, structural reader receipts, exact new +materialized-case bytes, caller grant/session revocation and verified execution. +No installed/native acceptance is inferred from that staged correction. + +2026-10-11 — new source-only type1.2 architecture acknowledgment: +Confirmed native founder ACCEPT of infd-20261011-forgejo-type12-source-r1@1 +at06:47:39Z, approval8e9332c8-754a-420c-b8b7-445cb4fe6765. Exact frozen packet +0b377d647ca748cd03c92f9792396412048055d4bde9f7bb06ff6d413f7acd68 and submission +585cee731de30d2b37883a57abfd9eb32f023ee694e8d911989109d5caf80569; native receipt +retained in docs/evidence/2026-10-11-forgejo-type12-source-acknowledgment/. +The complete acknowledged1.2 source package is isolated under +security-case-type-revisions/1.2/oidc-browser-relying-party; default loader and +current accepted1.1 case remain untouched because load_type is single-version. +The new proposed1.2 case is NOT accepted/materialized/live and has no acceptance +fields. Actual reference-migration design approval is a separate Apps owner +receipt; no1.1 approval is inherited. Source-recording review/test proof and exact +next gates are in docs/forgejo-type12-source-acknowledgment-followthrough.md. +T06 remains wait and T07 remains its existing dependent task; type is NOT +implemented and no first-case/native grant or broader workplan finish is asserted. + +2026-10-11 — bounded explicit version selection source preparation for T06: +The validator now offers optional exact version_roots/--version-root VERSION=PATH +selection with duplicate/mismatch/traversal/symlink refusal and no fallback. The +default1.1 source and historicalacceptedcase hashes/ordinaryrefusal/preparation +render stayunchanged; actual1.1 is acknowledged, not implemented. Acknowledged1.2 +ordinaryadmission also refuses; optionalpreparation remains nonexecuting.43local +validatorchecks pass and original1.1 CLIrenderbytes match exactly. Makerproof: +docs/evidence/2026-10-11-explicit-type-version-selection/source-integration-validation.json. +FormalNetownerreview/reference-source integration and all existing native adapter, +provenance/broker/materialization/attendedproof gates remain; T06 stayswait, no +new residual or firstcase/typeimplementation/native grant is inferred. + + +2026-10-16 — isolated source preparation for T06 (canonical integration pending): +Added `check-evidence` to the existing validator CLI. It reuses exact version +selection and ordinary type/case/render admission, checks actual Mason structural +request source/inventory/projection pins and reads actual Warden journal rows +read-only. Successful rows require exact type-declared descriptors and attempt/ +revocation fingerprints. Existing acknowledged types without machine descriptors +remain plan-only. The compatibility result never claims authenticated native +completion: Request and journal fingerprints are not portable native evidence. +Owner authentication/status binding and actual native gates remain unresolved; +T06/T07 statuses and both type/case lifecycles remain unchanged. No dispatch, +credential access or authority expansion is introduced. See tool README for the +existing owner protocol boundaries and source integration tests. + +Concrete next Warden owner integration: current `Executor.admit` refuses every +case parameter whose sensitivity is not public/operational, so browser +`redirect_uris`, `allowed_scopes`, `client_type`, `mfa_required` (and 1.2 +`consumer_namespace`) fail after otherwise prepared admission. The sensitive +classification is a security-change review level, not secret content. The +Warden owner must validate through NetKingdom's existing exact-version case/type +validator, then project only descriptor-declared safe target metadata; do not +remove the credential/value refusal or copy complete parameters into journals. +Actual owner descriptor acknowledgment and independent evidence admission remain +prerequisites, with normal admission still refusing unimplemented types. + + +## T10 — Source compatibility checker for Mason and Warden evidence + +```task +id: NK-WP-0048-T10 +status: done +priority: high +state_hub_task_id: "4b06ca97-c1e4-5680-bf22-ab75ab1646a4" +``` + +Completed 2026-10-16 in published source `c31d21ba`. The read-only +`check-evidence` command checks the actual Mason structural request and Warden +journal source protocols against an exact version/render. Independent source +review passed 103 tests at that checkpoint. It always reports native completion +false and execution prohibited. Fixtures and local journal fingerprints do not +provide authenticated owner/native evidence. This records completed source scope +previously described under T06; it does not duplicate native acceptance credit. + +## T11 — Validate the optional machine-descriptor pin contract + +```task +id: NK-WP-0048-T11 +status: done +priority: high +state_hub_task_id: "ef2a0119-f59e-5e45-9519-115da9df7b85" +``` + +Completed 2026-10-16. Reproduced the JSON schema rejecting a valid +`execution_descriptors` map while the Python validator accepted malformed maps. +The schema now supports the existing Warden optional exact-byte pin map; semantic +validation binds keys to declared steps, bounds the map and checks digest shape. +The direct evidence API refuses malformed maps before accessing them. Twenty-one +new checks and all 127 affected source checks passed. No current type/case bytes, +status, owner descriptors or native acceptance changed. Source evidence and +publication receipt: Custodian +`docs/changes/netkingdom-target-2026-10-16/browser-acceptance-next/`. + +## T12 — Prepare NetKingdom-owned attended step adapters + +```task +id: NK-WP-0048-T12 +status: todo +priority: high +state_hub_task_id: "7ce4eee3-cd47-55a9-bfa5-4f1835d4d6fd" +``` + +Owner: NetKingdom. Retained scope: prepare bounded attended Deployment env, +config verification and restart/readiness steps around the existing helpers. +Next action: identify exact current entry points and their revision/readback +contracts, then implement fixed source-pinned callbacks or receipt-only attended +steps. Completion requires positive/refusal checks, exact target/source pins and +uncertainty behavior without acquiring user-engine runtime identity or exposing +registry/client secrets. Existing caller/session, cluster/issuer, CAS/readback +and status-only output boundaries remain mandatory. + +2026-10-16 task refinement: the original broad adapter scope is split into T13 +(completed registration source candidate), T14 (group creation provenance), T15 +(durable registration recovery) and T16 (descriptor/artifact selection and owner +review). This preserves T12's task ID and Hub UUID while giving each remaining +outcome an owner and completion gate. T06 now names every source dependency; +its native/reference lifecycle acceptance scope remains unchanged. Completion +of any source task supplies no Warden, Platform or Apps assent and no native +execution authority. + +## T13 — Prepare the fixed registration adapter source candidate + +```task +id: NK-WP-0048-T13 +status: done +priority: high +state_hub_task_id: "b264c10b-565c-5d29-b788-03cf5fbd2883" +``` + +Completed 2026-10-16, source implementation and synthetic fixture proof only. +Added an injection-only adapter around actual `keycape-client.run`: fixed +case/principal/human/lane/source/acceptance binding, existing CAS/readback, +metadata-only owner evidence and per-attempt parent-session revocation boundary. +The descriptor source composition pins the exact checker executable digest +alongside adapter/helper source. Creation-only rollback authenticates its +creation receipt and refuses changed postcreation UID/resourceVersion; +pre-existing clients confer no rollback authority. Missing readback, evidence, +revocation or process-local creation provenance cannot become retry-safe success. + +Evidence: Custodian `docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/` +R3 candidate, explicit patch and postimages, retained R1 rejected custody finding +and R2 revision, and 36 passing source checks (16 new, 20 existing). Three checks +use actual Warden kernel admission/dispatch/rollback with published Warden +`d97652a3bb5fa733b063d71a1b74068efcccf572` producer pins unchanged. Owner trust, +descriptor/type/case and Kubernetes objects remain synthetic fixtures. This +records a completed source deliverable, not independent review, publication, +type acknowledgment, exercised procedure, identity admission or native browser +acceptance. Independent root source review: Custodian browser-adapter-next/coordinator-review-r3.json. Source integration and normal publication/synchronization receipts: Custodian browser-adapter-next/canonical-integration.json. These receipts supply no other-owner assent or native authority. + +Source publication: `0775c2bfa41af236301155bb5a69bbf088bdf823`, independently +reviewed by the Custodian coordinator before integration. The required initial +projection and ordinary UUID writeback both completed with zero failures and +zero automation errors. Generated-only brief commits were refused by the normal +hook; exact pre-existing staged/working brief bytes were restored. The separate +canonical integration receipt retains those refusals and projection evidence. +Native admission and all other-owner acceptance remain open. + +## T14 — Admit governed group rollback against native owner boundaries + +```task +id: NK-WP-0048-T14 +status: progress +depends_on: [NK-WP-0048-T20, NK-WP-0048-T21] +priority: high +state_hub_task_id: "fce95440-a25b-5f27-8ef3-a3d182099113" +``` + +Owner: NetKingdom. Next action: bind the completed T21 PostgreSQL journal to +an admitted dedicated CNPG consumer through the existing `rapp-postgres` +database-per-consumer contract. Identify the actual deployment/package and +custody owner, admitted placement/capacity and role/renewable lease/TLS target; +implement the independently retained exact content checkpoint and authoritative +tenant/case/source/principal callbacks before enabling governed group operations. +The injection-only backend and fixture callbacks do not supply production scope +or authorization. Existing service tokens, caller-supplied JSON and provenance +receipts confer no new grant. The default production endpoint remains disabled +without the trusted integration. SQLite remains local/disposable only; any +production exception requires explicit scoped governance under ADR-0015. + +2026-10-17 owner-integration refinement: T23 records the completed source/package +placement review, distinct from T21's source/disposable-SQL proof. Its dedicated +`rapp-identity-provisioner` recommendation is draft and unbound, not an accepted +package or existing repository. The full six-consumer rapp-postgres renderer +accepts the inert candidate on platform-pg-2 as its third of four declared +consumers; first-cell placement is refused as a fifth. Neither the estimated +four-consumer ceiling nor source requests establish native headroom or backup. + +Next independent owner contracts: NetKingdom/package/substrate owners accept +exact deployment/rollback fate; rapp-postgres admits the dedicated consumer, +roles, five-connection budget and actual backup/restore; Platform admits exact +renewable runtime/migration delivery and TLS/CA/SAN; network owners scope SQL, +broker and checkpoint paths without assuming a narrow policy subtracts additive +same-namespace access. Protected caller owners must provide authoritative +GroupJournalBinding construction and current authorization; an independent +existing holder must authenticate and atomically advance the exact content +checkpoint, not a counter or ordinary Audit receipt. Directory/runtime owners +must select and prove either an exclusive all-writer maintenance barrier or +provider-supported CAS/generation, including established sessions, lifecycle +cascades and remove/readd ABA. No source review establishes those owner facts. + +The default PostgreSQL activation and destructive native rollback remain refused. +Storage/lease acceptance can be proposed separately from LDAP mutation only after +exact owner source/target/custody/verification/recovery and cost/risk review; +State Hub guard approval grants none of it. Source-only integration review and +native acceptance matrix: Custodian native-storage-r1/OWNER-REVIEW.md. T14's +existing status, UUID, dependencies and native completion gate are retained. + +2026-10-17 refinement: T20 retains the historical durable SQLite source and +actual disposable LLDAP proof. T21 extracts the completed PostgreSQL source and +bounded real disposable SQL compatibility proof. T14 remains `progress` for +native CNPG admission/recovery, protected owner integration and exclusion of all +external LDAP writers. Separate source publication/projection and independent +cleanup receipts belong to coordinator follow-through; no missing native assent +is represented as received. + +Completion still requires independently reviewed/admitted ADR-0015-conforming +CNPG storage (or an explicit governed exception), backup/lost-store recovery and +migration of legacy requests; a native +writer-isolation/fencing or provider CAS/version contract that closes external +writer races and remove/re-add ABA; exact case/source/principal and current +native object bindings for protected callbacks; and actual per-attempt evidence, +revocation and readback/refusal verification through the existing owner paths. +Only independently retained confirmed creations may be undone. Every +pre-existing/shared group and membership must remain preserved; unresolved +intents never expire into retry authority. The disposable fixture supplies none +of these native custody, authorization, owner-acceptance or production claims. + +## T15 — Define authenticated recovery for lost registration process state + +```task +id: NK-WP-0048-T15 +status: wait +depends_on: [NK-WP-0048-T31] +blocking_reason: "Source candidate 061461e is merged. Waiting on the owner to select the real authenticated original-attempt holder and native readback, and on independent re-review of the changed T13 adapter source." +priority: high +state_hub_task_id: "1b825705-4848-5edc-832f-b1622cd9bdb7" +``` + +2026-10-17 source part merged (061461e via 65559ef). `build()` takes optional +paired `holder`/`observe` capabilities and installs `reconcile_original`; the T31 +fixtures now run through the adapter's own path. Rollback after restart is still +refused, and there is no add replay. This changes `keycape-registration-adapter.py`, +so the revision reviewed under Custodian `browser-adapter-next/coordinator-review-r3.json` +is superseded. The new revision has no independent review yet and must not be +pinned on the strength of the old one. + +2026-10-17: T31 fixtures done (74adcd7). What remains for T15: the owner +selects the real authenticated original-attempt holder, and the T13 adapter +gains the holder capability, `reconcile_original` and reconcile-aware `verify` +described in T31. + +Owner: NetKingdom for registration state, with Warden owning kernel evidence +admission. T22 records the completed source review and exact existing-seam gaps; +T15 retains implementation and authenticated restart/lost-receipt acceptance. +Next action: select and admit the actual owner publisher/authenticator and bounded +original-attempt readback contract for the merged holder/observe adapter seam. +Warden's original-intent persistence/interface (`a45cf475`) and additive validation +(`117fec9241e7b985dfb187b992e173d0a5dc3700`) are now published and projected. +The local Journal retains admitted nonsecret original context, validates original +actor/attempt and metadata linkage to the original running receipt, and supplies +it separately from the new reconciliation attempt. This local consistency is not +independent original-holder authorization or creation provenance; neither the +journal nor a current matching registry entry reconstructs creation authority. + +The owner contract must bind the original Invocation, case/type/descriptor/catalog/ +acceptance pins, source, principal and admitted tenant/realm provenance, separately +from the new reconciliation attempt and its independently revoked parent session. +Use an existing authenticated holder/readback; Audit Core's custody API is a +candidate only until its actual registration source/class, load-bearing emission +contract and separate scoped reader are admitted. No new broker or receipt trust +root is proposed. A missing original record, ambiguous publication, NO_CHANGE, +changed native revision or lost readback stays uncertain; no add replay or undo. + +Completion requires restart/lost-receipt fixtures against the selected actual owner +contract: independently authenticated original created-only provenance, original +exact client entry and unchanged current postcreation UID/resourceVersion before +any undo, with wrong attempt/case/source/principal and unavailable-holder refusals. +The merged T13 adapter exposes reconciliation only through paired injected +holder/observe capabilities and still refuses post-restart rollback. Existing +source review, JSON receipts and synthetic trust do not satisfy independent +holder authorization/provenance or native admission, or authorize attended recovery. +Exact remaining owner facts: Custodian native-registration-r1/original-attempt-review.json. + +2026-10-17 check (net-kingdom-worker): Warden's original-intent lookup interface +is not published — ops-warden head is `d97652a`, with no WARDEN-WP-0044-T03 +commit, and the net-kingdom inbox holds no announcement. No restart/lost-receipt +fixture or readback seam was added this session; the work is deferred behind +T16's decomposition under the same token budget. Remains todo, gated on Warden +publishing the reviewed lookup/context interface and on owner selection of the +authenticated original-attempt holder. + +2026-10-17 current-source correction: the earlier same-day `d97652a` lookup +checkpoint and T31 adapter-next-action note above are historical and superseded +by the published Warden interface/validation and merged `061461e` adapter seam. +The 54 focused Warden checks establish source behavior only. Current admission +leaves tenant/realm empty; no admitted tenant/realm provenance is inferred. +T15 remains wait for actual independently authenticated owner-holder/readback +selection, independent review of the changed adapter, and native restart/lost- +receipt acceptance. Existing task metadata, owner gates and historical reviews +are unchanged. + +## T16 — Select and review the actual machine descriptor composition + +```task +id: NK-WP-0048-T16 +status: todo +depends_on: [NK-WP-0048-T24, NK-WP-0048-T25, NK-WP-0048-T26, NK-WP-0048-T27, NK-WP-0048-T28, NK-WP-0048-T29, NK-WP-0048-T30] +priority: high +state_hub_task_id: "200bf500-5084-5ae7-be26-3033fe1f296e" +``` + +2026-10-17 coordinator review r4 approved the 1.3 candidate at 1920b13 with no +findings (the-custodian `nk48-adapters-1.2-review/coordinator-review-r4.json`). The founder chose +option A of the T16 owner-review packet (`nk48-t16-owner-review/founder-option-decision.md`): +1.3 with execution descriptors for all seven steps. Owner reviews for A are open +in the-custodian `docs/changes/netkingdom-target-2026-10-17/nk48-1.3-owner-review/` (net-kingdom, +railiance-platform, railiance-apps, ops-warden, key-cape). The owner-review packet +supersedes the r1 composition pointer: T16's exact composition is now +`sso-mfa/k8s/keycape/descriptor-composition-1.3.candidate.json`. + +2026-10-17 successor candidate prepared (source only, `proposed`): +`security-case-type-revisions/1.3/oidc-browser-relying-party/` is the 1.2 package plus +an `execution_descriptors` map of the seven composed descriptors, with examples +pinned to `@1.3` and no acknowledgment. ensure-groups, hand-over, +deliver-consumer-secret and the composer accept 1.3 (835680c, merged acda26c). +`test_admit_1_3_type.py` runs Warden's real admission against the committed 1.3 type: +the proposed type is refused; an acknowledged copy is refused because ops-warden +pins parameter fingerprints only for 1.1 and 1.2; and with only a 1.3 pin (equal to +1.2's fingerprint) all seven are admitted. This is a major change under canon +section 6. Next: r4 source review of the changed adapters and the map, then owner +reviews, including the ops-warden 1.3 pin, the mint-secret warden_executes/human +question and the owner-run steps question, then the founder's Decision Memo. + +Owner: NetKingdom for descriptor/type source and exact checker artifact; Warden +independently owns global provenance/admission. T22 records the completed seven-step +source1.2 inventory and regenerated register-client descriptor using the delivered +checker and current sealed-snapshot source; T16 retains exact owner review, +complete descriptor composition and admission. Next action: bind actual native +publisher/authenticator, original/current session verification and revocation, +executable storage/custody and config pins, then obtain the six missing fixed +owner descriptors/adapters for ensure-groups, mint-secret, deliver-secret, +bind-env, deliver-consumer-secret and hand-over. The inventory identifies each +existing owner seam, exact missing contract and created-only rollback requirement; +it is not an executable bundle. T14 and T15 remain independent prerequisites. + +Review the dedicated consumer source1.2 composition only through the existing +explicit version-root selection. Keep canonical1.1/default and its accepted case +unchanged, and the source-only acknowledged1.2 reference case proposed without +acceptance fields. New descriptor bytes require a reviewed exact successor +revision/version under canon section6; they do not inherit historical source +acknowledgment. Changed authentication/materialization/trust needs the prescribed +informed-decision acknowledgment. Do not insert a partial descriptor map or use +--allow-unimplemented preparation as ordinary case/native admission. The registry +helper reports restart_required; separately review quiet-window restart/readiness +and actual consumer verification rather than infer activation from registry CAS. +Exact composition: Custodian native-registration-r1/descriptor-composition.json. +Completion requires separate owner source review of exact machine descriptors, +artifact/source/config pins, normal type/version admission and independent +evidence-verification boundaries. Warden d97652a refreshes producer validator +pins for NetKingdom 015694ee; it does not attest T13's new adapter. Pinning any +machine descriptor into an acknowledged type is a separately reviewed source +change. Platform owns structural/mint/broker/delivery evidence; Apps owns actual +consumer login/group enforcement. Neither their assent nor native acceptance is +supplied by this task. + +2026-10-16 source checkpoint: T19 below extracts the completed per-attempt +executable snapshot implementation and reviewed fixture proof. T16 retains +native executable storage/custody, exact owner evidence/session callbacks, +complete descriptor composition and ordinary type/version admission. The +successor descriptor/source pins are proposed only; the earlier descriptor +history is preserved. T14 group provenance and T15 durable restart/recovery +remain independent open requirements. No native acceptance is counted here. + +2026-10-17 budget checkpoint (net-kingdom-worker): no source change made. The +T13 registration adapter alone is 217 source and 317 test lines plus an +independent review; six further owner descriptors/adapters cannot be completed +within one per-task token budget (soft 8000 / hard 10000), so the task was +stopped below 50% and is decomposed rather than pushed through. Proposed split, +one source-candidate task each with positive/refusal fixtures, canonical1.1 +default unchanged and no --allow-unimplemented admission: (a) ensure-groups, +bound to T14's journal/provenance seam; (b) mint-secret and (c) deliver-secret, +Platform-owned evidence seams; (d) bind-env and (e) deliver-consumer-secret; +(f) hand-over, Apps-owned consumer verification; then (g) descriptor composition +over the reviewed set. Each candidate needs its owner's source review before +pinning; none is authored here as owner-accepted. Status stays todo. + +2026-10-17 task refinement after coordinator r5: completed independent mint- +guard regression coverage is extracted as T32. T33 tracks the exact Platform +issuer-store source/reference correction; T34 tracks the Apps configuration +inverse reachable without secret or hand-over resource creation. T16 retains +final exact seven-descriptor composition, independent owner reviews and admission; +its status and existing dependencies are preserved. The kernel branch also needs +its own KR1-F1/F2 admission/dispatch corrections and independent review under +WARDEN-WP-0044-T03 before any 1.3 pin. Historical r4 acceptance does not supersede +r5's findings or establish native readiness. + +## T17 — Build and exercise the exact offline config-checker artifact + +```task +id: NK-WP-0048-T17 +status: done +priority: high +state_hub_task_id: "8c3e4baa-a218-5996-af9e-fe1153a46dac" +``` + +Completed 2026-10-16, isolated artifact/source fixture proof only. Built the +existing `key-cape/src/cmd/keycape` command from published clean source revision +`3c71226a1a7b8a2bdb710e9c497e1a1f7f8a3987`, source tree +`f0010b9cab17dbc3a787628a29657d98b900320f`, with cached Go 1.23.0, +CGO disabled, Linux/amd64 v1, trimmed paths, disabled VCS stamping and offline +read-only module selection. The full retained closure pins 108 source files, +9876 toolchain files and ten used module source directories. Three isolated +build locations produced identical 13633409-byte artifacts: +`sha256:be37738fe1c08259fb4f9ca0daf0f9301bfffc237e5bb38435525dfbf11ccff5`. + +Eight disposable checks use the actual executable and maintained helper/kernel, +with complete synthetic configs and a minimal environment: add/created-only +removal, normal pinned producer admission, rollback, lost-readback uncertainty, +stale revisions, substituted checker refusal, invalid startup config and missing +secret-env refusal. No checker stub or producer-pin override remains. The +artifact-bound descriptor proposal is +`sha256:76b1153d188062ece53fcd5e8d35290fdbfd02a69dd79d4b001fc42781afcdc3`. +Evidence: Custodian +`docs/changes/netkingdom-target-2026-10-16/browser-checker-artifact-next/`. +The first two build attempts succeeded but their closure collection failed; +both failures are retained, as is refusal of the older incomplete synthetic +fixture. No production config, credentials or cluster mutation was used. + +This extracts completed build/fixture scope from T16 without claiming KeyCape +owner endorsement, native checker selection/storage custody, acknowledged type +integration, native evidence/session callbacks or attended browser acceptance. +T16 retains exact owner review and composition/admission scope. The attended +registration procedure remains unexercised. + +## T18 — Make checker input and executable selection deterministic + +```task +id: NK-WP-0048-T18 +status: done +priority: high +state_hub_task_id: "9c45da71-fb4c-5f20-9065-90f448f37c64" +``` + +Completed 2026-10-16, isolated source correction and real-artifact fixture proof +only; independent integration/publication remain separately recorded. KeyCape +source review identified non-secret `KEYCAPE_ACCOUNT_PORTAL_URL` and +`KEYCAPE_BROWSER_LOGOUT_URL` overrides in config.Load. The original helper +inherited ambient environment, allowing valid supplied configuration to be +rejected or invalid supplied URL configuration to be admitted. A bare relative +checker filename could also be replaced by PATH lookup after the adapter had +hashed the selected local file. All three counterexamples are retained against +the unchanged predecessor. + +The existing helper now resolves the selected checker file to an absolute path +and passes an explicit empty subprocess environment. Three opt-in real-checker +regressions verify both URL-verdict directions and relative-file selection under +a non-checker PATH decoy. All 39 affected checks pass with the exact supplied +artifact, as do 11 real-artifact helper/kernel scenarios. Source evidence: +Custodian `docs/changes/netkingdom-target-2026-10-16/browser-checker-environment-r2/`. +Historical descriptors remain retained; the helper correction changes successor +source-composition pins and requires their separate review/admission. An absolute +path is not immutable custody and does not eliminate file replacement races; +T16 retains native executable storage/custody and callback/type admission. +No production configuration, credentials or cluster mutation was used. + +Source integration and publication: `916ba4b33e03179274d03b78bd898a4843e8653f`, following independent +Custodian source review in browser-checker-environment-r2/coordinator-review.json. +The ordinary protected projection pass assigned T17/T18 UUIDs with zero failures +and zero automation errors; generated-only brief commit refusal is retained. +The exact prior staged/working brief bytes were restored. Final source/publication/ +projection evidence is retained in Custodian browser-checker-environment-r2/ +canonical-integration.json. These source receipts do not admit native executable +custody, owner callbacks, acknowledged type changes or browser identities. + +## T19 — Bind checker execution to verified immutable attempt bytes + +```task +id: NK-WP-0048-T19 +status: done +priority: high +state_hub_task_id: "f50fdcd1-00be-5c3c-be0c-345aa2126032" +``` + +Completed 2026-10-16, independently reviewed source implementation and synthetic +fixture proof only. The registration adapter now opens a bounded regular checker +artifact without following its terminal symlink, verifies the reviewed digest, +and snapshots the exact bytes into a write/grow/shrink/seal-removal-sealed Linux +memfd before helper access. Live and proposed config checks execute that same fd +through procfs with the existing empty environment; the fd closes on completion +or failure. Unsupported snapshot/seal/exec policy refuses through the existing +parent-session revocation and uncertainty boundaries. The earlier unbounded +pre-snapshot path read was removed during independent review; actual adapter +symlink/FIFO/oversize refusal tests verify helper access never starts. + +Evidence: Custodian `docs/changes/netkingdom-target-2026-10-16/browser-resume-r1/`, +including exact base/preimages/postimages/patch, proposed descriptor, coordinator +source review and canonical integration receipt. Independent Custodian checks +passed 49 affected source tests and 12 actual Go checker/helper/kernel scenarios; +the sets overlap and are not separate acceptance counts. Existing published +Warden d97652a producer pins remain unchanged. The exact KeyCape artifact remains +`sha256:be37738fe1c08259fb4f9ca0daf0f9301bfffc237e5bb38435525dfbf11ccff5`. +The predecessor path-replacement refusal and the first expanded reopened-fd test +failure are retained; the latter fixture was corrected to restore write mode +before independently proving seals block reopened-fd writes and shared mmap. + +This source task adds no native owner custody, callback, evidence trust, type +acknowledgment, credential access, consumer sign-in or rollback acceptance. +Executable memfds, Linux seals and usable procfs are explicit runtime dependencies +and still require native admission. This is an extracted source outcome from +T16, not completion of T16; T12/T14/T15 and all external-owner gates remain open. + + +Publication verified by the coordinator after the source worker usage limit: +`8790dd18ae83c4651ef3d1d923312a118a5b436b` is the actual remote main revision. +All five canonical source hashes match the reviewed packet. Normal synchronization +completed with zero failures/automation errors; all 19 task statuses match the +projection. T19's assigned UUID is retained. The original staged and working brief +bytes were restored and independently compared. The exact source, projection and +publication checks are in Custodian browser-resume-r1/canonical-integration.json. +Native acceptance remains open under T16 and the other named owner tasks. + + +## T20 — Prepare durable group provenance and prove disposable LLDAP compatibility + +```task +id: NK-WP-0048-T20 +status: done +priority: high +state_hub_task_id: "863fcbdb-e246-51ba-87ca-a2f44d01d6f8" +``` + +Completed 2026-10-17, source candidate preparation and bounded synthetic +compatibility proof only. This extracts evidenced implementation/fixture scope +from T14; T14 retains native storage, writer isolation, protected callback and +owner-acceptance gates. Source integration/publication and State Hub projection +are pending the coordinator's separate receipts; no revision or task UUID is +invented here. + +The existing identity-provisioner group ensure/revoke interface now has a +reviewed candidate for a bounded owner-private SQLite journal, proven only in +local/disposable use. The initial storage instructions wrongly suggested a native +persistent-volume path; source review against accepted ADR-0015 corrected them +before publication. Production SQLite-on-a-PVC is not admitted: T14 retains +CNPG/rapp-postgres storage admission or a separately governed exception. +The local journal saves +complete prestate and intent before each attempted directory mutation, inventories +only confirmed new groups/memberships, binds original ensure idempotency and +correlation to the exact request digest, and refuses unsigned caller inventories +or legacy broad revoke requests. Replays never recreate removed state. Missing, +replaced or untrusted storage and missing intent rows stop execution; definite +before-mutation refusals retain evidence without stranding unrelated valid work. +Interrupted writes/readback remain unresolved across reopening. Revoke validates +all targeted native ids and complete member postimages before destructive work, +removes only confirmed creations and never deletes a preexisting empty group. +Server/configuration wiring, image-copy closure and owner storage/request +migration instructions are included. The existing endpoint's caller +authentication remains the existing boundary, not a new grant. + +The candidate was prepared against NetKingdom +`878abc93588e12b6da1bac055cfbb3abb8d7adf1`; exact source patch digest +`sha256:1f3f3ca94c46dadac8f24896053d4d23179de795e50a24298a0cfbcc46cca30e`. +The independent Custodian review passed all 65 provisioner checks. The earlier +62-check candidate, two independent-review findings and subsequent fixes remain +retained rather than overwritten. Seven additional fixture-plan checks exercise +the preparation boundary; these counts overlap the same candidate and do not +measure task-completion percentage or independent acceptance. + +A separately reviewed bounded fixture then exercised the exact candidate against +cached pinned disposable LLDAP and Python images, through actual GraphQL. All 14 +closed compatibility/containment oracles passed: exact creation inventory, +separate-process SQLite reopening/replay/revoke, complete preexisting directory +preservation including empty/shared groups and memberships, refusal of shared +membership drift, retained uncertainty after a lost response following a real +addition, and refusal of unknown/cross-case receipt bindings. The fixture used +only newly generated synthetic users/passwords/key material in private tmpfs, +with a network-none anchor and shared loopback helper, no host ports/mounts, +no image pulls and no production or actual credential contact. The coordinator's +safe receipt reports both exact owned containers absent after cleanup; final +independent cleanup verification remains the coordinator's integration duty. + +Evidence: Custodian +`docs/changes/netkingdom-target-2026-10-16/group-provenance-r1/`, including exact +source base/pre/postimages, patch/manifest, independent review and retained +refinements. Native fixture manifest +`sha256:8a0514f5f14137355972400cd4852f48c892316d28e3abd3c86635d7cb81eef2`; +retained safe result `native-fixture/execution-result.json` (original +`/tmp/nk48-groups-native-review-5pywb19z/result.json`). The source task supplies +no native owner assent, production rollout, protected session/evidence callbacks, +ordinary type admission or consumer login proof. LLDAP has no conditional +membership mutation or generation token here: exact readback cannot exclude +ABA or a concurrent external writer, so T14 remains open. + +2026-10-17 verified source follow-through: source commit `4f65e41` passed normal +local scanning and all 65 canonical provisioner tests. The actual disposable +LLDAP receipt passed all 14 oracles; independent verification on the pinned +daemon found both exact names and the run label absent. Source integration +includes the ADR-0015 documentation correction; the journal remains local-only. + +T14 source preparation is underway. Twenty-three pinned public sources identify +NetKingdom implementation/reconciliation, Platform custody/database governance +and rapp-postgres consumer provisioning; the inspected runtime binding does not +assign identity-provisioner a package. The existing User Engine runtime token +and pod route are not a protected-case caller lane. The native proposal preserves +the same journal seam on a dedicated CNPG consumer and requires authoritative +scope binding, admitted role/lease/placement/recovery and all LDAP writers fenced. +The declared exclusive maintenance window is not mechanical exclusion of WebUI, +helper or lifecycle writers. Capacity placement and EUR cost/risk remain owner +decisions. No package, database, credential, rollout or owner assent was created. +Custodian evidence: group-provenance-r1/native-storage-proposal-r1; final source, +projection and cleanup review: resume-integration-r1/publication-verification.json. + + +## T21 — Implement the PostgreSQL journal and prove disposable SQL compatibility + +```task +id: NK-WP-0048-T21 +status: done +depends_on: [NK-WP-0048-T20] +priority: high +state_hub_task_id: "afe78e4d-408a-516a-ab92-e212e8a2d11d" +``` + +Completed 2026-10-17: bounded source preparation and actual disposable PostgreSQL +compatibility only. T14 retains native storage, custody, callbacks, authorization, +all-writer isolation and production admission. Coordinator source publication and +projection remain separate follow-through; no source revision or task UUID is +invented here. T20 remains the historical local SQLite/actual LLDAP proof. + +The eight-path source candidate adds an injection-only PostgreSQL implementation +behind the existing owner journal seam, with explicit trusted tenant/directory/ +case/source/principal/epoch binding and independent continuity callbacks. It +persists bounded intent/created-only receipts, exact-one version updates, durable +active fences, and a content-bound journal chain. SQL commits precede independent +exact-marker acknowledgment; lost replies/checkpoints poison continuation and +retain uncertainty. A counter-only checkpoint is explicitly insufficient for +divergent clones. Migration does not create database/schema/credential, admit an +epoch or clear quarantine; production HTTP activation is absent pending trusted +callbacks. Pinned psycopg package/image-copy closure is included, without claiming +an image build or native dependency admission. + +Candidate base `6cb43c0cc55212363a5406b02db046e2384a7c40`; final eight-path patch +`sha256:5549d2982b61ff70aba4b3928fecd5639db0f1a732ab2f8630f678e7acf4d34d`. +All 82 source tests pass, alongside 13 fixture plan/diagnostic checks and two +source-backed snapshot regressions. Counts overlap the same source and fixture; +they are not additive native acceptance or completion percentages. + +The independently reviewed R5 fixture ran the actual SQL migration/backend and +existing provisioner path against cached pinned disposable PostgreSQL. Directory +operations deliberately used the existing synthetic LLDAP test double. All +manifest-declared phase oracles passed: creation-only inventory and DDL refusal; +separate-process replay/revoke with complete preexisting/shared visible-state +preservation; foreign-context refusal; concurrent exactly-once reservation and +stale version refusal; actual database fork with divergent bodies at the same +counter and fresh-object rejection of the losing exact checkpoint; retained +uncertainty after checkpoint/directory/SQL response loss; and actual pg_dump/ +pg_restore rollback refusal against the independently retained fixture checkpoint. +The checkpoint is fixture-only, not an admitted native owner evidence holder. + +Only generated credentials/state were used, with two nonroot/read-only containers +on private loopback, bounded tmpfs/CPU/memory/PIDs/lifetimes, no host ports/mounts, +no pulls and no production contact. The safe receipt reports both exact owned +containers absent, with no daemon uncertainty. Independent final pinned-daemon +cleanup verification remains coordinator follow-through. + +Failed R2/R3/R4 receipts and source revisions remain retained. R2's helper failure +subphase is unknown; R3 exposed a real backend bug where replacing nested receipt +objects lost the created ID held by ensure, fixed by acknowledging only the backend +version after commit/checkpoint. R4 verified that fix through actual SQL bootstrap, +then exposed fixture snapshot aliasing and empty deleted-group backing entries; +source-backed regressions corrected the snapshot without weakening complete +visible-prestate equality, and reject nonempty orphan membership. These refinements +belong to one trial, not extra native acceptance successes. + +Custodian evidence: `docs/changes/netkingdom-target-2026-10-16/group-postgres-r1/`; +exact source/pre/postimages and retained predecessors; native R5 manifest +`sha256:d51f90816616848b93bcdc482e10a98d07061deb01eeed9e50700fc6e68b6051`; +`final-integration-r1/typed-execution-evidence.json` and byte-exact safe receipt +(original `/tmp/nk48-postgres-native-review-sguj7t8p/result.json`). No CNPG native +storage/HA/backup/lease/TLS/capacity/restore admission, authoritative protected +callbacks, real LDAP writer fencing/CAS/ABA exclusion, owner assent or production +endpoint activation is claimed. Those remain live T14 work. + + +## T22 — Establish registration recovery source gaps and complete descriptor review inventory + +```task +id: NK-WP-0048-T22 +status: done +priority: high +state_hub_task_id: "ea50d93b-0d04-55d2-98c4-9b6c3fe925b1" +``` + +Completed 2026-10-17: source-only owner-contract review and complete review +inventory. Inspected the actual registration adapter, Warden Journal/Executor, +NetKingdom evidence compatibility checker and Audit Core custody/read surfaces. +No existing admitted holder/readback supplies authenticated original-attempt +recovery: payload/creation are process-local, persisted evidence references are +hashed, and reconcile receives a new attempt without original context. The exact +four-owner contract requirements and non-replayable missing/ambiguous cases are +recorded; no recovery callback, publisher, credential grant or native trust root +was installed. T15 retains implementation and actual acceptance. + +The complete source1.2 inventory maps all seven follow-through steps to their +existing owners, missing exact contracts and created-only rollback requirements. +The actual maintained adapter regenerated the sole existing register-client +machine descriptor against the delivered 13,633,409-byte checker and current +sealed-memfd snapshot source. The checker rehash and Warden Descriptor.decode +round-trip passed; six owner descriptors remain absent. Actual source commands +render canonical1.1 and explicitly selected1.2 with execution.permitted false; +ordinary1.2 case admission still refuses its acknowledged/unimplemented type. +Canonical type/case/acknowledgment bytes were hash-checked unchanged. No tests were +added or rerun, no native configuration or effect occurred, and no owner assent +or complete machine bundle is claimed. T16 retains native executable custody, +complete exact descriptors, independent source review and normal version/type +admission. This extracted outcome supplies no native gate completion credit. + +Evidence in the-custodian: +`docs/changes/netkingdom-target-2026-10-17/native-registration-r1/`, especially +original-attempt-review.json, descriptor-composition.json, source-verification.json, +source-manifest.json and manifest.json. The prior sixteen-gate audit remains +immutable. Publication, normal task UUID allocation and projection are separate +coordinator follow-through; no Hub UUID is assigned by this source record. + + +## T23 — Review native journal package assignment and storage integration boundaries + +```task +id: NK-WP-0048-T23 +status: done +priority: high +state_hub_task_id: "6e7cd483-c7af-53a2-9d0a-3d76b6fb49ee" +``` + +Completed 2026-10-17: source/package placement and protected integration review +only, separate from T21's completed PostgreSQL source and disposable-SQL proof. +The owner review recommends a dedicated independently rolled-back +`rapp-identity-provisioner`, owned by NetKingdom on rail-kubernetes, with a +schema-valid draft declaration and no reef binding. No repository, registry +entry, native package assignment or owner assent was created. Existing +rapp-user-engine ownership/token delivery does not assign it the provisioner. + +The complete current rapp-postgres catalog has six declared consumers: four on +platform-pg and two on platform-pg-2. The inert renderer-valid provisioner +candidate fits platform-pg-2 at three of four, requesting one migration plus +four runtime connections; placing it on the first cell is refused as a fifth. +Normative package schema and full-catalog capacity/render checks passed. The +four-consumer ceiling is estimated, not measured native headroom, and no current +SQL session, backup or restore acceptance is claimed. + +The exact review identifies remaining package/substrate, SQL role/lease/TLS, +network and backup/recovery contracts; independently authenticated protected +scope callbacks; an external content-bound checkpoint holder with atomic +advance/loss handling; and all-LDAP-writer exclusion or provider CAS/generation, +including lifecycle cascades and remove/readd ABA. Generic Audit custody is not +that checkpoint service. A database-only fence, final reread or one replica +cannot exclude foreign directory writers. The native acceptance/recovery matrix +keeps storage/lease admission separate from separately approved directory +mutation; the production activation remains disabled pending trusted integration. + +Evidence in the-custodian: +`docs/changes/netkingdom-target-2026-10-17/native-storage-r1/OWNER-REVIEW.md`, +source-validation.json and hash-bound source-references.json, plus the inert +proposed declaration/consumer and renderer outputs. No native resource, lease, +credential, deployment, destructive effect, independent holder or native owner +acceptance was delivered. T14 retains these live gates with its original UUID +and progress status. This completed review adds no duplicate T21 acceptance +credit; publication, task UUID allocation and projection remain coordinator +follow-through and no UUID is hand-assigned here. + +## T24 — Ensure-groups adapter source candidate + +```task +id: NK-WP-0048-T24 +status: done +priority: high +state_hub_task_id: "1a3931ee-3b5f-52fc-9295-4073804f4bd2" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): ensure-groups-adapter.py, 8 tests (commit 5a2e7d6). Binds to the T14 PostgreSQL journal and refuses SQLite or a foreign binding. It accepts only canonical 1.1 cases, so T30 must add an explicit 1.2 path. It calls the provisioner's private `_group_binding`; a public helper is the cleaner shared edit. + +Split from T16 on 2026-10-17 so each candidate fits one per-task budget. Bind group creation to T14's journal/provenance seam; refuse without trusted journal callbacks. +Source candidate only, modelled on the T13 registration adapter +(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and +`registration-adapter.md`). Requires positive and refusal fixtures and +created-only rollback. Keeps canonical 1.1 as the default and uses no +`--allow-unimplemented` admission. Done means the candidate and its tests are +committed. NetKingdom (directory) owner source review before pinning, and any native action, +stay separate gates and are not supplied by this task. + +## T25 — Mint-secret adapter source candidate + +```task +id: NK-WP-0048-T25 +status: done +priority: high +state_hub_task_id: "0de2a7d9-874d-578b-a076-f653fe42e30b" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): mint-secret-adapter.py, 9 tests (commit 558c5bc). Value-free receipts only. The receipt schema `railiance-platform.custody-mint.v1` and the mint/destroy capability interface are proposals that Platform must confirm. + +Split from T16 on 2026-10-17 so each candidate fits one per-task budget. Platform-owned custody evidence seam: the adapter records value-free mint evidence only and never handles secret values. +Source candidate only, modelled on the T13 registration adapter +(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and +`registration-adapter.md`). Requires positive and refusal fixtures and +created-only rollback. Keeps canonical 1.1 as the default and uses no +`--allow-unimplemented` admission. Done means the candidate and its tests are +committed. Platform owner source review before pinning, and any native action, +stay separate gates and are not supplied by this task. + +## T26 — Deliver-secret adapter source candidate + +```task +id: NK-WP-0048-T26 +status: done +priority: high +state_hub_task_id: "94b3cbf0-c908-5ac6-b0db-67385c3e021c" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): deliver-secret-adapter.py, 8 tests (commit 1b60f6e). The lane `platform-secret-delivery` and the schema `netkingdom.deliver-secret.v1` are proposals that Platform must confirm. + +Split from T16 on 2026-10-17 so each candidate fits one per-task budget. Platform-owned delivery evidence seam (ESO/custody), value-free. +Source candidate only, modelled on the T13 registration adapter +(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and +`registration-adapter.md`). Requires positive and refusal fixtures and +created-only rollback. Keeps canonical 1.1 as the default and uses no +`--allow-unimplemented` admission. Done means the candidate and its tests are +committed. Platform owner source review before pinning, and any native action, +stay separate gates and are not supplied by this task. + +## T27 — Bind-env adapter source candidate + +```task +id: NK-WP-0048-T27 +status: done +priority: high +state_hub_task_id: "e56365f8-29b5-5c89-a329-8115f9f3b2a7" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): bind-env-adapter.py, 5 tests (commit 6fbce73). Binds references only. To fit the budget it does not yet wrap a full Warden `Adapter` like T13 (source-digest pin, authenticated evidence, parent-session revocation); it returns `(reference, run, rollback)`. That wrapping is a prerequisite for T30. + +Split from T16 on 2026-10-17 so each candidate fits one per-task budget. Bind the issuer Deployment env reference to the delivered secret reference, without values. +Source candidate only, modelled on the T13 registration adapter +(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and +`registration-adapter.md`). Requires positive and refusal fixtures and +created-only rollback. Keeps canonical 1.1 as the default and uses no +`--allow-unimplemented` admission. Done means the candidate and its tests are +committed. NetKingdom runtime owner source review before pinning, and any native action, +stay separate gates and are not supplied by this task. + +## T28 — Deliver-consumer-secret adapter source candidate + +```task +id: NK-WP-0048-T28 +status: done +priority: high +state_hub_task_id: "6dab281c-ea9a-5a17-a83f-e8e9a4d4484f" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): deliver-consumer-secret-adapter.py, 5 tests (commit 650c1ac). Type 1.2 only. It assumes the owner has validated ESO TokenRequest and the controller-RBAC admission; that stays an owner gate. + +Split from T16 on 2026-10-17 so each candidate fits one per-task budget. Consumer-side delivery evidence seam, value-free. +Source candidate only, modelled on the T13 registration adapter +(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and +`registration-adapter.md`). Requires positive and refusal fixtures and +created-only rollback. Keeps canonical 1.1 as the default and uses no +`--allow-unimplemented` admission. Done means the candidate and its tests are +committed. Platform and Apps owner source review before pinning, and any native action, +stay separate gates and are not supplied by this task. + +## T29 — Hand-over adapter source candidate + +```task +id: NK-WP-0048-T29 +status: done +priority: high +state_hub_task_id: "129db41d-4560-5e20-9f3b-c3c91e0a5601" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): handover-adapter.py, 8 tests (commit ec63539). The owner `apps` and lane `apps-consumer-handover` are proposals that the Apps owner must confirm. It has no reconcile path; restart handling follows T15. + +Split from T16 on 2026-10-17 so each candidate fits one per-task budget. Apps-owned consumer verification and hand-over receipt seam. +Source candidate only, modelled on the T13 registration adapter +(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and +`registration-adapter.md`). Requires positive and refusal fixtures and +created-only rollback. Keeps canonical 1.1 as the default and uses no +`--allow-unimplemented` admission. Done means the candidate and its tests are +committed. Apps owner source review before pinning, and any native action, +stay separate gates and are not supplied by this task. + +## T30 — Compose the descriptor set over the reviewed candidates + +```task +id: NK-WP-0048-T30 +status: done +depends_on: [NK-WP-0048-T24, NK-WP-0048-T25, NK-WP-0048-T26, NK-WP-0048-T27, NK-WP-0048-T28, NK-WP-0048-T29] +priority: high +state_hub_task_id: "6f6073ac-52dd-5b70-8874-6d76d4c072e4" +``` + +2026-10-17 done: coordinator review r3 (the-custodian `nk48-adapters-1.2-review/coordinator-review-r3.json`, +fd07eaa) approved all nine files at ff8f597, with R2-F1..F3 resolved, an independent +reconstruction of 117 passing and no open findings. This is a source review only: +it grants no owner admission, pin or native authority. Owner review and admission +continue under T16, which also takes the r2 observations, including the +`warden_executes: false` steps that carry execution descriptors. + +2026-10-17 coordinator review r2 (the-custodian `nk48-adapters-1.2-review/coordinator-review-r2.json`) +approved five files and rejected three, plus the artifact. The fixes are merged in +2ee4ed0: R2-F1, mint-secret revokes the session and the broker grant +independently (eeed8e1); R2-F2, deliver-secret binds to the admitted case and +derives its Secret and custody path, with no caller reference (640292c); R2-F3, +bind-env pins its five-file closure, and a new closure-completeness test checks +every adapter (d16db30). Each regression test failed on 68f4bbb. The artifact is +regenerated, and the full suite including kernel admission passed 117. Next is +the r3 review. Open for owner review under T16: the type marks four steps +`warden_executes: false`, yet each has an execution descriptor. + +2026-10-17 founder confirmed every proposed lane and schema name for T25-T29 +as-is. The artifact records them as `names_confirmed` (by founder, 2026-10-17), +and no step is still `owner_confirmation_required`. Next: independent source +review of the seven adapters. + +2026-10-17 founder decision (supersedes the `agent` decision below): the +mint-secret actor is `human`. Coordinator review r1 (the-custodian +`docs/changes/netkingdom-target-2026-10-17/nk48-adapters-1.2-review/coordinator-review-r1.json`, +finding NK48-R1-F1) showed that unchanged kernel a45cf47 refuses every +`effect: mint` admission whose actor is not `human` ("agent mint is +forbidden"), so the `agent` descriptor could never be admitted. Adapter, tests +and composer comment updated and the artifact regenerated (mint-secret +descriptor digest now `sha256:8c40c2884f8ba15e8cb6c98f33419c395e54fce8ef33d6e1a34782cf3f1fcfa9`); +98 tests pass against a45cf47. The set needs an independent r2 review. + +2026-10-17 `test_admit_1_2_adapter_descriptors.py` runs Warden's own +`Executor.admit` on all seven composed 1.2 descriptors, with the real 1.2 +type, the 1.2 forgejo example case, the reviewed `BrowserInputs` validator and +fixture-only trust (stand-in adapters, nothing dispatched). It also pins that +an agent mint descriptor is refused ("agent mint is forbidden") and that a +wrong lane is refused for every step. Against the old `agent` revision +(582ab9f) it fails as expected. Combined suite: 102 passed on ops-warden a45cf47 +and on current ops-warden. Fixture admission is not owner admission or a pin. + +2026-10-17 founder decision: the mint-secret actor is `agent`, not `warden`. +The kernel is unchanged. The adapter, its test and the composer were updated and +the artifact regenerated; all seven steps pass `Descriptor.decode` and +`complete_executable_descriptor_set` is true (98 tests passed). The artifact +stays `not_an_admission`, with no owner review, admission or dispatch permission. + +2026-10-17 composer merged (60b41a1 via 3c62bec) with follow-up 376b44b (merged; +combined suite 98 passed). `compose-1.2-descriptors.py` loads 1.2 only through an +explicit version root and refuses partial, duplicate, unknown or mismatched maps. +`descriptor-composition-1.2.candidate.json` is a reviewable candidate, marked +`not_an_admission`, with canonical 1.1 unchanged. ensure-groups and hand-over now +carry and honour `parent-session` revocation, and the hand-over owner is +parameterised (`railiance-apps` for Forgejo). Six of the seven steps pass +`Descriptor.decode`. mint-secret is still refused: its actor `warden` is outside +the kernel's human/agent set, and Platform (with ops-warden, if the kernel should +change) must decide. Five steps carry `owner_confirmation_required` names. The +set is complete only after both. Owner review, the canon section 6 successor +revision and admission remain T16's. + +2026-10-17 prerequisites merged (65559ef; combined suite 78 passed): +ensure-groups accepts 1.2 via an explicit `version_root='1.2'` (531151b), with +1.1 still the default. bind-env is now a full Warden v1 `Adapter` (7442d21); its +kernel-level fixture tests like T13's are still missing, and its `build()` +signature changed. Composition is unblocked in source. Owner confirmation of +the proposed step, lane and schema names remains before pinning. + +2026-10-17 inputs from T24–T29 that composition must resolve: T24 accepts +only 1.1 while T28 accepts only 1.2, so add an explicit 1.2 ensure-groups path; +T27 still needs full Warden `Adapter` wrapping; the lane, schema and owner names +proposed in T25, T26 and T29 need owner confirmation before pinning. + +Split from T16 on 2026-10-17. Compose the seven-step source 1.2 descriptor set +through the existing explicit version-root selection over T13 and T24–T29. No +partial descriptor map, canonical 1.1 stays the default, and new descriptor bytes +need a reviewed successor revision under canon section 6. Owner review and +admission remain T16's. + +## T31 — Restart and lost-receipt fixtures against Warden's original-intent interface + +```task +id: NK-WP-0048-T31 +status: done +priority: high +state_hub_task_id: "ed1ccd87-89a0-5e69-885c-0eaf5ab0bb5a" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): test_registration_original_intent_reconcile.py, 8 tests (commit 74adcd7), runs against the unmodified ops-warden a45cf47. The holder is a test-only in-memory store. For real T15 support the T13 adapter's `build()` needs an injected holder/readback capability, a `reconcile_original` callback, and a `verify` that accepts reconcile results. Rollback after restart stays refused until there is an owner contract. + +Split from T15 on 2026-10-17. ops-warden published the original-intent +lookup/context interface in `src/warden/case_run.py` (`OriginalIntent`, +`Journal.original_intent`, `Adapter.reconcile_original`; commit `a45cf47`). +Build T15's restart/lost-receipt fixtures for the registration adapter against +it: the original intent is kept separate from the reconcile attempt; wrong +attempt/case/source/principal and unavailable-holder cases refuse; and a missing +or ambiguous record stays uncertain, with no add replay or undo. The interface +is Warden's own dispatch record, not owner-authenticated created-only provenance. +Selecting the actual authenticated holder stays a T15 owner gate. + + +## T32 — Restore independent agent-mint admission regression coverage + +```task +id: NK-WP-0048-T32 +status: done +priority: high +state_hub_task_id: "c6d0a27e-f6a7-5df0-95f6-3e7473d38bb5" +``` + +Completed 2026-10-17, source tests only. Extracted from T16's coordinator r5 +finding NK48-R5-F1; no earlier adapter outcome is counted again. The real 1.3 +admission test now separately checks the committed descriptor map, an in-memory +fixture map acknowledging the changed agent descriptor to reach the independent +mint prohibition, and the human descriptor under an explicit fixture agent +runtime marker. Only `sso-mfa/k8s/keycape/test_admit_1_3_type.py` changes. + +Against frozen Warden `117fec9241e7b985dfb187b992e173d0a5dc3700`, the new +suite passes 10 tests / 32 subtests. Independent mutation evidence removes only +the mint admission guard in scratch: both new guard tests fail while eight +controls pass; the old suite passed that mutant. Custodian independently reran +the canonical successor: 10 tests / 32 subtests pass. Evidence: +`the-custodian/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/` +(patch, frozen/mutant results and canonical-validation.json). No descriptor, +parameter pin, catalog, acknowledgment, kernel or native execution changed. +T16 still owns actual composition and admission; F2/F3 follow below. + +## T33 — Close the issuer-store reference finding with Platform source + +```task +id: NK-WP-0048-T33 +status: todo +priority: high +state_hub_task_id: "5ace28ff-a4cb-5647-a8f6-3cd54bb9d0a5" +``` + +Owner: Platform for the fixed store/template source and its delivery boundary; +NetKingdom for the 1.3 type reference. Split from T16's current coordinator r5 +finding NK48-R5-F2. Next action: independently review the prepared Platform +candidate and publish a concrete owner-controlled issuer store kind/name/scope +and template reference, then integrate only that exact proposed source into the +1.3 deliver-secret entry point and regenerate its composition artifact. Candidate +and current review evidence are under Custodian +`docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/` +and `nk48-r5-f2-review-r1/`. + +Completion requires exact committed source, a passing independent source review +and unchanged type parameter bytes. No consumer store may be inferred as issuer +store, and no source declaration supplies owner assent, installed ESO support, +CCR/grant changes or a real delivery capability. Those remain existing +RPF-1.3-T4 and T16 admission/native requirements. + +## T34 — Close the consumer configuration inverse finding + +```task +id: NK-WP-0048-T34 +status: todo +priority: high +state_hub_task_id: "37f43c40-d014-544e-aa0b-5219dd4a6b93" +``` + +Owner: Apps for consumer configuration and inverse; NetKingdom for exact type +placement. Split from T16's coordinator r5 finding NK48-R5-F3. Next action: +independently review and implement the owner transaction/inverse through the +existing RAPPS-WP-0016-T05 path, with authenticated durable before/after release +provenance, exclusive serialization of all release writers and a fresh authorized +attended session for every inverse attempt after the hand-over session is revoked. +The inverse must remain reachable for public clients, confidential NO_CHANGE +secret delivery and verification failure before a hand-over receipt exists. + +Completion requires real owner configuration/inverse source bindings and +independent regressions, then exact reviewed 1.3 entry/rollback text and composed +artifact. A synthetic wrapper with an injected backend alone is insufficient. +Keep hand-over receipt rollback receipt-only and secret deletion created-only; +preexisting or drifted resources must refuse unsafe inverse. Candidate evidence: +Custodian `docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/` +and `nk48-r5-f3-review-r1/`. Source review, owner acknowledgment and native proof +remain distinct. T16's final composition/admission and T06's native first case +are not completed by this refinement. + +## T35 — Align source admission fixtures with the authenticated catalog and attended-session API + +```task +id: NK-WP-0048-T35 +status: done +priority: high +``` + +Completed 2026-10-17, source fixture compatibility only. Distinct from T32's +independent agent-mint regression repair: both remaining opaque catalog byte +placeholders now use one well-formed synthetic catalog whose exact bytes are +bound by the fixture evidence verifier. The fixture mirrors the six relevant +Warden lane shapes; four draft statuses are hypothetically active, while both +Apps establishing login references remain unresolved. No real catalog entry, +owner login choice or production parameter pin changes. + +Against the reviewed mode-aware Warden source now integrated at +`ae7bc73b4f4bac22454144959b870d161498e0af`, scratch validation passes 31 tests +and 53 subtests. Five resolvable 1.3 descriptors conditionally admit under +fixture-only acknowledgment and pinning; both Apps attended steps refuse before +execution. Changed catalog bytes refuse, all independent mint-guard tests still +reach their intended guard, and the registration fixture's actual kernel +run/rollback and uncertain-result paths pass with synthetic owner callbacks. +The registration fixture explicitly aligns its in-memory proposed type lane +with its current descriptor; canonical 1.1 is unchanged. + +The same tests against predecessor `117fec9241e7b985dfb187b992e173d0a5dc3700` +pass 31 tests and 51 subtests, with only the two new Apps refusal assertions +failing because that predecessor lacks the attended-session catalog check. +Tests target the reviewed current API; the parser or Apps gate is not weakened +for predecessor compatibility. Evidence and root source review: +`the-custodian/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-update-r1/` +and `nk48-catalog-fixture-publication-r1/`. Canonical validation/publication +receipts belong in the latter packet when performed. This records source +fixture work, not native T03, owner assent, exact-type acknowledgment, PIN-1, +composition/admission completion under T16 or the native first case under T06. +T33 and T34 retain their existing issuer-store and consumer-inverse work. diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r1/workplan.preimage.md b/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r1/workplan.preimage.md new file mode 100644 index 0000000..16914fc --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r1/workplan.preimage.md @@ -0,0 +1,1609 @@ +--- +id: NK-WP-0048 +type: workplan +title: "Establish SecurityCaseTypes so that known security cases are light" +domain: infotech +repo: net-kingdom +status: active +flavor: implementation +owner: claude-code +topic_slug: netkingdom +created: "2026-10-08" +updated: "2026-10-17" +depends_on: [] +related_repos: + - ops-mason + - ops-warden + - key-cape + - railiance-platform + - informed-decision +state_hub_workstream_id: "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" +--- + +# NK-WP-0048 - Establish SecurityCaseTypes so that known security cases are light + +## Why + +The founder's direction (2026-10-08): setting up a security integration of a +kind that has been done before should be easy, meaning accept the case, then +ops-mason arranges it and ops-warden follows through. Setting up a new kind +needs ample documentation and tests and an informed-decision, because its shape +is part of the security architecture. + +Today every integration is hand-built: per-client helper scripts, per-lane +custody, founder checklists. The trigger was KEY-WP-0038 (Forgejo as an OIDC +client), whose next step was going to be yet another hand-written rollout +script. + +This plan continues the pattern work of NK-WP-0008 (pattern infospace), +NK-WP-0009 (tutorials) and NK-WP-0030 (scenario composition). It turns patterns +into **SecurityCaseTypes** that ops-mason and ops-warden can execute. Canon: +`canon/standards/security-case-types_v0.1.md` (proposed). + +Companion plans: ops-mason `MASON-WP-0010` (materialization) and ops-warden +`WARDEN-WP-0044` (follow-through). + +## Tasks + +```task +id: NK-WP-0048-T01 +status: done +priority: high +state_hub_task_id: "930ac3e9-20ec-5473-98cd-3fe8fddb730f" +``` + +### T01 — Accept the terminology and the canon + +Founder review of `canon/standards/security-case-types_v0.1.md`, including its +section 10 open questions. On acceptance the canon status goes from `proposed` +to `accepted`. + +2026-10-08: accepted by the founder (in session). Canon status `accepted`; +section 10's open questions are carried forward to the first type (T04, T05). + +```task +id: NK-WP-0048-T02 +status: done +priority: high +state_hub_task_id: "19c25d96-a55c-58f0-9168-3fdbc4955fc2" +``` + +### T02 — Inventory the de-facto types already built by hand + +Read-only. List every security integration built so far and group them by shape. +Known starting points: + +- browser OIDC relying parties on key-cape: informed-decision-approver, + coulomb-social, user-engine portal, openbao-admin, and Forgejo (pending) +- confidential service clients with `client_credentials`: secrets-engine + approval, requester and openbao clients, approval-engine operator +- workload KV read lanes on OpenBao: the railiance-platform credential lane + designs and CCRs, for example `railiance-backup-offsite-lane` +- short-lived SSH certificates through ops-warden (`warden sign`) +- tunnelled operator access (ops-bridge) + +For each: the repositories involved, the hand-built steps, attended human steps, +custody paths, verification evidence, and rollback (if any). Output +`docs/security-case-type-inventory.md`, ranking candidate types by how often the +shape recurs and how much manual work each instance costs today. + +2026-10-08: done, `docs/security-case-type-inventory.md`. 36 of 37 CCRs are +already `request_type: workload-kv-read`, a de-facto type reviewed each time as +if it were new. Browser clients: 5 hand-built plus Forgejo, each with its own +rollout script. Confidential service clients need a registration plus two or +three KV-read CCRs each. A forge read identity is needed in bulk by +ORGREF-WP-0002-T06. Recommendation: oidc-browser-relying-party first (unblocks +the forge), workload-kv-read second (largest payoff), then forge-read-identity. + +```task +id: NK-WP-0048-T03 +status: done +depends_on: [NK-WP-0048-T01] +priority: high +state_hub_task_id: "e487975b-73b1-516c-971d-da4cbbb93063" +``` + +### T03 — Schema, template and validator + +- `canon/schemas/security-case-type_v0.1.schema.json` and + `security-case_v0.1.schema.json` +- `security-case-types/_template/` with `type.yaml`, `README.md` and tests to copy +- a validator, extending `tools/security-scenario-composer/`, that checks a type, + checks a case against its type, and renders the case into the composer's + deterministic plan (still `execution.permitted: false`) + +Acceptance: the validator rejects missing sections, secret-shaped parameter +values, unpinned providers and cases against non-`implemented` types, with tests +for each. + +2026-10-08: done. `canon/schemas/security-case-type_v0.1.schema.json` and +`security-case_v0.1.schema.json`; `tools/security-case-types/security_case_validator.py` +(`validate-type`, `validate-case`, `render`, reusing the scenario composer); +`security-case-types/_template/` (a valid, runnable example type over +`net-kingdom.local-identity`, with README sections, an example case and tests that +travel with every copy). 31 tests pass, including the composer's existing suite. + +```task +id: NK-WP-0048-T04 +status: done +depends_on: [NK-WP-0048-T02, NK-WP-0048-T03] +priority: high +state_hub_task_id: "2fffe112-7d55-5680-9952-78d74f44f87f" +``` + +### T04 — Define the first type: browser OIDC relying party + +`security-case-types/oidc-browser-relying-party/`, version 1.0: + +- parameters: client id, display name, exact redirect URIs, scopes, client type + (confidential or public), MFA pin, the groups the consuming service maps, + custody path for the secret (confidential only), consuming service and its + owner +- composition: key-cape client registration (key-cape declares it as a playbook + capability), secret minting into custody (railiance-platform), LLDAP group + existence and membership (net-kingdom identity-provisioner), handover to the + consuming service +- materialization (ops-mason) and follow-through (ops-warden) sections +- verification: discovery lists the redirect URI; a test sign-in returns the + expected claims for one human; anonymous access to the custody path is denied +- rollback: remove exactly this client and its custody path +- README: threat model, MFA rationale, why redirect URIs are exact, which steps + still need an attended login and how that is minimised +- reference instances: informed-decision-approver, coulomb-social, user-engine + portal, openbao-admin + +Acceptance: complete per canon section 3, tests green, status `proposed`. + +2026-10-08: done. `security-case-types/oidc-browser-relying-party/` 1.0 (proposed): +`type.yaml` with 12 parameters (the custody reference is required only for +confidential clients), composition over key-cape's new +`key-cape.oidc-client-registration` declaration (contract PASS, `maturity: +planned`), materialization and follow-through, verification and rollback, all +conditional on client type where it matters. One attended step, +`register-client`, justified by the live registry being a Kubernetes Secret. +README with threat model, alternatives, an honest fit table (portal and vergabe +fit; Forgejo is the first confidential case; openbao-admin, coulomb.social and +informed-decision-approver do not fit 1.0), and known limits. Examples: Forgejo +(checked against the accepted request) and a public portal. The validator gained +`when` / `required_when` conditions and provider discovery. 43 tests pass. +ops-mason's and ops-warden's sections are drafted in the type. Their owners +review them in MASON-WP-0010-T01 and WARDEN-WP-0044-T01 before acknowledgment +(T05). + +```task +id: NK-WP-0048-T05 +status: done +depends_on: [NK-WP-0048-T04, MASON-WP-0010-T01, WARDEN-WP-0044-T01] +priority: high +state_hub_task_id: "3d987d3d-0797-538b-86e4-51185a327ae2" +``` + +### T05 — Acknowledge the type through informed-decision + +Prepare the Decision Memo from the type's README and `type.yaml` and submit it +through informed-decision's production intake. On acknowledgment the type +moves to `acknowledged`. + +2026-10-08: Decision Memo prepared. Hub decision `e418263e-a71f-4f01-a970-c87c9e187c09`. +`security-case-types/oidc-browser-relying-party/decision/`: `packet.md` (question, +one act, what acknowledging does not do, the shape, alternatives, attended +steps, limits, evidence), `build_intake.py` and the built `intake.json` (memo +class `decision-record`, review group `net-kingdom-admins`, resolves the hub +decision on answer). The envelope passes informed-decision's own +`validate_submission`. Queuing it is an attended operator step, a write inside +the production dispatcher pod: + + kubectl -n state-hub exec -i deploy/state-hub-memo-dispatch -- \ + python -m api.services.memo_dispatch --queue /data/private enqueue --envelope /dev/stdin \ + < security-case-types/oidc-browser-relying-party/decision/intake.json + +The answer then appears for review in informed-decision. + +2026-10-08: queued by the founder (`pending`, `queued`) after one refusal: the +first envelope's idempotency key was not State Hub's stable +`state-hub..` form, and the builder now validates against both +sides. The dispatcher delivered it to informed-decision (ledger 7 → 8 +delivered). T05 waits for the founder's answer in informed-decision, which +resolves hub decision `e418263e`. + +2026-10-08: ops-mason's review (MASON-WP-0010-T01) folded into the type, still +1.0 `proposed`: `secret_ref` is no longer a parameter, the type derives +`platform/workloads/keycape/{client_id}`; policies are +`workload-kv-read-keycape-{client_id}-eso` and +`workload-kv-read-keycape-{client_id}-{consuming_service}`, each with a templated +body (`read` on that one data path); new `openbao_role` +`external-secrets-keycape-{client_id}` (Kubernetes auth, ESO service account, +15m, no default policy); catalog entry `{consuming_service}-keycape-client`; +the two rollback steps on the KV path collapse into one metadata delete. The +schema and validator now require a `body` on policy and role items. 105 tests +pass. Two consequences: the delivered memo describes the type before these +changes (a case-set custody reference, two differently named policies, no ESO +role), so the founder answers on a superseded shape unless the memo is +re-sent; and Forgejo's custody path moves from the accepted request's proposed +`platform/workloads/forgejo/netkingdom-oidc-client` to +`platform/workloads/keycape/forgejo`, which key-cape (KEY-WP-0038) and +railiance-apps (RAPPS-WP-0016-T02) need to adopt in T06. ops-warden's review +(WARDEN-WP-0044-T01) is still in progress. + +2026-10-08: revised memo `nk-wp-0048-t05-oidc-browser-relying-party-r2` built +(`packet.md`, `intake.json`, packet `sha256:af6435e2…`); it states that it +supersedes the first memo and passes informed-decision's and the dispatcher's +validation. It needs a new memo id because the dispatcher refuses new content +under a delivered idempotency key (`idempotency_conflict`). Queuing it is the +same attended step as above. Both memos serve hub decision `e418263e`; the +first answer resolves it, so the first memo must not stay open: an accept on it +would acknowledge the superseded shape, and a decline would resolve the +decision against r2. Close it with `return` (a reviewer verb that requests a +new revision and does not resolve the hub decision; only accept and decline +do), commenting that r2 supersedes it, then answer r2. `withdraw` exists in +the vocabulary but is not offered to reviewers, and lifecycle `revise` only +applies to lifecycle-registered memos, not intake-delivered ones. + +2026-10-09: the founder reports r2 enqueued and the first memo returned. At +close, hub decision `e418263e` still showed `open` (not verified whether r2 +was answered or the answer is still in transit; the dispatcher ledger was not +read). ops-warden's review (WARDEN-WP-0044-T01, ops-warden +`history/2026-10-08-oidc-rp-follow-through-review.md`, d470f2e) arrived at +21:25, before r2 was built, and is not in r2. Required before WARDEN-WP-0044-T02: +R1 a per-step `lane` (missing: identity-provisioner caller credential, +railiance-platform broker grant), R2 canon meaning of `warden_executes: true` +(caller proxy, caller identity, no standing ops-warden token; ADR-0002), R3 a +per-step `value_handling`. Refinements R4 (one type-level catalog lane instead +of a per-case entry, which conflicts with ops-mason's item 5), R5 (ESO Ready gate +before bind-env), R6 (exact accepted memberships), R7 (mfa-pinned and +secret-delivered checks). Next: if r2 was accepted, fold R1-R3 and R5-R7 in as +a follow-up and settle R4 between ops-warden and ops-mason before T06; if not, +hold r2, fold them in and build r3. + key-cape and railiance-apps were told about Forgejo's new custody +path. + +2026-10-09: r2 was not answered (hub decision `e418263e` open; dispatcher 9 +delivered), so r2 is held and r3 replaces it. ops-warden withdrew R4 for R4′ +(message a66a9204): one catalog entry per case with its body templated in the +type. Folded in: + +- Canon section 3: a narrow meaning of `warden_executes: true` (caller proxy, + caller identity, no standing credential; ops-warden ADR-0002), plus per-step + `lane`, `value_handling` and an optional `ready_check`. A catalog entry body is + now required, like policy and role bodies. +- Schema and validator enforce all of these. +- The type: lanes `identity-provisioner-groups` and + `keycape-client-secret-generate` (both new) and `key-cape-oidc-login`; + `mint-secret` is `generated_in_custody`; a Ready gate on `deliver-secret`; + `ensure-groups` adds exactly the accepted case's memberships; new checks + `secret-delivered` and `mfa-pinned`; the catalog-entry body follows R4′. +- 110 tests pass. + +Memo r3 (`nk-wp-0048-t05-oidc-browser-relying-party-r3`, packet +`sha256:726dfc01…`) passes informed-decision's and the dispatcher's validation. +The founder queues it with the same command, returns r2 with a comment that r3 +supersedes it, then answers r3. + +Prerequisites for T06, not for T05: +- the two new lanes, each through its owner; +- a founder-approved rollout of an identity-provisioner image containing + 630ebfd (the deployed one is main-6c4fcaf from 2026-09-24, without + `/v1/groups/ensure`). + +ops-mason's request to create LLDAP group `fluid-telegram-operators` (message +259ec26d) was answered: not possible from here by that path; the founder creates +it in the LLDAP admin UI. + +2026-10-09: ops-warden finished its side of WARDEN-WP-0044-T01 (message +`58d33f40`, commit 0e7ceb8): the revised type conforms to `warden case +check-type`, with no further changes asked of the type. This acknowledges that +review. Its later addendum R8 (message `af274783`, ops-warden +`history/2026-10-08-oidc-rp-follow-through-review.md`) showed that lane +`identity-provisioner-groups` cannot exist as declared: the provisioner's only +HTTP credential is user-engine's runtime token, and its NetworkPolicy admits +only user-engine pods, so a caller-proxy lane would act as user-engine. Taken +option 1 (ops-warden's recommendation): `ensure-groups` and its rollback are +attended, in the `register-client` session on `key-cape-oidc-login`, run inside +the provisioner pod; no token is read. Option 2 (an operator credential of its +own on the provisioner) stays open for a later version. `warden case +check-type` now reports only `keycape-client-secret-generate` +(railiance-platform) as missing. 89 tests pass. + +Hub decision `e418263e` was still open, so memo r4 +(`nk-wp-0048-t05-oidc-browser-relying-party-r4`, packet `sha256:452ea421…`) +replaces r3; it passes informed-decision's and the dispatcher's validation. The +founder queues it with the same command, returns r2/r3 if still open with a +comment that r4 supersedes them, then answers r4. + +Prerequisites for T06 are now: lane `keycape-client-secret-generate` +(railiance-platform), and a founder-approved rollout of an identity-provisioner +image containing 630ebfd. + +2026-10-09: done. The founder queued r4 (`pending`, `queued`), returned r3 as +superseded and accepted r4 in informed-decision. informed-decision's hub +write-back is off in production (`INFD_HUB_WRITEBACK` unset), so net-kingdom +resolved hub decision `e418263e` from the founder's report, naming memo r4 in +the rationale. The type is `acknowledged`; it becomes `implemented` with T06. + +```task +id: NK-WP-0048-T06 +status: wait +depends_on: [NK-WP-0048-T05, NK-WP-0048-T08, NK-WP-0048-T09, NK-WP-0048-T12, NK-WP-0048-T13, NK-WP-0048-T14, NK-WP-0048-T15, NK-WP-0048-T16, NK-WP-0048-T17, NK-WP-0048-T18, NK-WP-0048-T20, MASON-WP-0010-T03, WARDEN-WP-0044-T03] +blocking_reason: "Type1.2 architecture has a confirmed source-only founder acknowledgment; canonical1.1 remains unchanged/default and the1.2 reference case stays proposed. Needs explicit version selection/reference-source integration without normal admission bypass, closed owner adapters plus global/native receipt verifier (WARDEN-WP-0044-T03), independently verified six-object structural materialization and CCR42 claims/created-only provenance, exact broker/case/source reconciliation, then separately authorized native mint/delivery and attended registry/consumer verification. Source acknowledgment, a bundle or local tests do not prove implemented/materialized/live status." +priority: high +state_hub_task_id: "d9b26bcf-a7c6-59a2-92a0-f913d76a203c" +``` + +### T06 — First KnownSecurityCase: Forgejo + +Write `security-cases/forgejo-netkingdom-oidc.yaml` from the parameters the +founder accepted in KEY-WP-0038-T01 (`key-cape/docs/forgejo-oidc-client-request.yaml`), +so that acceptance carries over as the case acceptance. ops-mason materializes it; +ops-warden follows through and verifies it. The type becomes `implemented` once +this reference case is live. + +This replaces the hand-built path proposed for KEY-WP-0038-T02. + +2026-10-09: case written, `security-cases/forgejo-netkingdom-oidc.yaml`, status +`accepted`: the example's parameters plus the founder's 2026-10-08 acceptance of +the key-cape request (owner and founder), with the custody-path derivation +covered by the type's acknowledgment (memo r4). It validates and renders (plan +`execution.permitted: false`; three attended steps on `key-cape-oidc-login`); +it needs `--allow-unimplemented` until it is live, since it is the case that +makes the type `implemented`. A test keeps it equal to the type's example. +Still open before it runs: ops-mason renders and builds it (MASON-WP-0010-T02, +T03, unblocked by T05), ops-warden plans and runs it (WARDEN-WP-0044-T02, T03), +railiance-platform's `keycape-client-secret-generate` lane (asked by ops-mason +`789feb39` and ops-warden `d9f889b1`, both unread), and a founder-approved +identity-provisioner rollout containing 630ebfd. + +2026-10-09: identity-provisioner rolled out, founder-approved in session: +`main-630ebfd` (`sha256:bafb4860…`, built by the image workflow) replaces +`main-6c4fcaf` (`sha256:ffacd5d7…`; only change: 630ebfd, additive). Applied +with `kubectl set image` on the Deployment only (generation 17 → 18, 1/1 Ready); +the file's Service, NetworkPolicies and Ingress were not reapplied. Verified in +the pod: `ensure_service_groups` and `revoke_service_groups` present, `/readyz` +200; no token used, no group changed. Rollback: `kubectl -n sso set image +deploy/identity-provisioner provisioner=forgejo.coulomb.social/coulomb/identity-provisioner@sha256:ffacd5d790a5b7800e0535f3aa95b7de52c7a68e4f575b9192c3e1622fa672c2`. +The pin in `sso-mfa/k8s/identity-provisioner/deployment.yaml` follows. + +2026-10-09: ops-warden closed WARDEN-WP-0044-T01 and shipped `warden case plan` +(T02, message `06303d1d`); the accepted Forgejo case is refused until ops-mason +materializes it, and a materialized copy plans cleanly once +`keycape-client-secret-generate` exists. Its observation that `bind-env` carries +a literal `KEYCAPE__CLIENT_SECRET`: the validator now renders a +`{param:env}` format spec (upper case, `-` as `_`, as `keycape-client.py` +derives the name) and rejects unknown specs, and follow-through and rollback +text is now checked for unknown placeholders too (patch-level, canon section 6). +The type keeps the literal until ops-warden's planner (`text.format`) renders the +spec as well; asked of ops-warden. 53 tests pass. + +2026-10-10: ops-warden rendered `{param:env}` in `warden case plan` +(23ba584, message `c1df14b7`; other specs and `!conversion` refused). The type +adopts it as a 1.0 patch (canon section 6): `bind-env` and its rollback now read +`KEYCAPE_{client_id:env}_CLIENT_SECRET`. ops-mason reports the Forgejo case +rendered and built (message `1ebddae9`; CCR drafts handed to railiance-platform), +so T06 now waits on the platform apply, the mint-secret lane and the attended +register-client session. + +2026-10-10: key-cape adopted the type's custody path (key-cape ab5cccd, message +`88a4559c`): `docs/forgejo-oidc-client-request.yaml` and KEY-WP-0038-T02 name +`platform/workloads/keycape/forgejo`, field `CLIENT_SECRET`, delivered as +`sso/keycape-forgejo-client` through `external-secrets-keycape-forgejo`, and the +`forgejo/netkingdom-oidc-client` proposal is withdrawn. This matches the type and +the Forgejo example; no change needed here. + +2026-10-10: key-cape's type-composition review (key-cape 6038483, message +`8e5ed5f6`) found the composition sound with two blocking findings, taken as a +1.0 patch (stricter validation and documentation, canon section 6): +R1, redirect URIs now refuse `?` and `#` in the type pattern and in +`keycape-client.py`'s `REDIRECT` (KeyCape refuses `?` at startup and the single +replica would stop); R2, `allowed_scopes` always includes `groups`, the renderer +refuses `access_groups` without it, and `access_groups` now says the consumer +enforces the gate because KeyCape has none; R3, `mfa_required` "false" means the +per-user privacyIDEA policy; R4, register-client's justification records the +restart for every client, the quiet window, and that the rendered registry must +pass KeyCape's client validation before it. The accepted Forgejo case and both +examples still validate; 116 tests pass. +Open, needs a decision: R2's verification step that the consumer enforces the +group gate is a new verification step, hence 1.1 (minor), and the validator pins +cases to the exact type version, so the accepted 1.0 Forgejo case would need +re-pinning. Also open with key-cape: a KeyCape entry point that validates a +rendered client registry without starting the server; until then +`keycape-client.py` mirrors the redirect rule. + +2026-10-10: founder go for 1.1, so no 1.0 run overlaps. The type adds +verification `consumer-enforces-groups`, and hand-over now requires the consumer +to enforce `access_groups` (minor, canon section 6; risk class unchanged, no new +acknowledgment; history in the type README). The accepted Forgejo case and both +examples are re-pinned to `oidc-browser-relying-party@1.1` before any apply; the +acknowledgment intake keeps `@1.0` as the record of what was acknowledged. +ops-mason's build and ops-warden's case-ref digest predate this and need a +re-render. + +2026-10-10: ops-mason re-rendered the Forgejo case from `@1.1` (message +`22c6bba6`; plan digest `af1f4f81…`, no collisions) and rebuilt the bundle. The +rendered OpenBao objects are identical to 1.0, so the already applied +CCR-2026-0041 stands; 1.1 changes only consumer-side steps. key-cape shipped +`keycape check-config` (c528daa, message `a7b0aeff`): KeyCape's startup checks +without secret values, including the new duplicate-clientId rule. +`keycape-client.py --check-config ` now runs it on the live and the +proposed registry, fed on stdin, and apply refuses a proposed registry that +fails; register-client's entry point names the option (1.1 patch). The deployed +image predates c528daa, so the binary is built from key-cape source for now. +Before the next KeyCape rollout (Forgejo register-client or the CCR-2026-0018 +window), the attended operator runs `inspect --check-config` so the live +registry is checked too. + +```task +id: NK-WP-0048-T07 +status: wait +depends_on: [NK-WP-0048-T06] +blocking_reason: "The next types follow once the first one is proven end to end." +priority: medium +state_hub_task_id: "c3c9c3ae-4f23-5ba7-9e6f-e63bcf29b9f7" +``` + +### T07 — Queue the next types + +From the T02 ranking, open the next type drafts (likely the confidential service +client and the workload KV read lane), each to be acknowledged on its own. +Record which existing hand-built instances get adopted as cases, without +rewriting them in place. + +```task +id: NK-WP-0048-T08 +status: done +priority: high +state_hub_task_id: "8e519082-dc8b-5e46-b660-03d03e55dcf6" +``` + +### T08 — identity-provisioner: ensure service-access groups + +The type's `ensure-groups` step calls `identity-provisioner groups ensure --groups + --members `. The provisioner already has idempotent +`_ensure_group` and `_add_group`, but exposes only tenant-scoped operations. Add +the general operation (create if absent, add the listed memberships, never +remove others), its rollback (remove only this case's memberships), tests, and +the ops-warden route for its service identity. + +2026-10-08: done. `identity-provisioner/provisioner.py` gained +`ensure_service_groups` (`POST /v1/groups/ensure`) and `revoke_service_groups` +(`POST /v1/groups/revoke`), behind the existing service-token check. Names +matching `tenant*`, `netkingdom-*` and `lldap_*` are reserved; members must +already exist; everything is validated before any change; memberships are read +back before success; nothing else is ever removed; groups are deleted only when +asked and empty. 7 new tests (39 in the provisioner suite). The type's +`ensure-groups` step and its rollback now name these endpoints. The ops-warden +route for the provisioner's service token belongs to WARDEN-WP-0044-T01. + +```task +id: NK-WP-0048-T09 +status: done +priority: high +state_hub_task_id: "0ee12dfc-8260-5038-bd39-cc7fb4113be0" +``` + +### T09 — The `keycape-client` entry point and its declaration + +Planned first as key-cape KEY-WP-0038-T04. Moved here because the machinery a +live registration needs already lives in net-kingdom, where KeyCape is deployed +(`sso-mfa/k8s/keycape/`): the contained kubectl, the cluster-identity pin and the +issuer pin. A copy in key-cape would drift. + +2026-10-08: done. `sso-mfa/k8s/keycape/keycape-client.py` derives the KeyCape +client record from a case (`client-from-case`). It adds or removes exactly one +client with the existing safeguards (byte-preserving merge, exact match or +refuse, shared-reference refusal, compare-and-swap, read-back, contained errors), +requires the Deployment env binding first for confidential clients, and requires +the case acceptance record for `apply` instead of a per-client founder decision +id. 15 tests, including a regression showing that the vergabe case reproduces +its hand-built registration exactly; all 71 helper tests pass. Declaration +`capabilities/playbooks/net-kingdom.keycape-client-registration.yaml` (contract +PASS, `maturity: implemented`) replaces key-cape's draft. The type now pins it, +and for confidential clients adds `deliver-secret` (delivery from custody into +the sso namespace) and `bind-env` (Deployment env), attended in the same session +as `register-client`. The per-client helpers stay until their clients are +adopted as cases. + +2026-10-10 — NetKingdom owner review of the execution seam, source only: +ops-warden/docs/security-case-execution-proposal.md SHA256 78ab1439e4c5beb205aeccf15b1b2e706511be55ab195c5a3aa682f326c7c77c +was reviewed against INTENT, SecurityCase canon, acknowledged type1.1, current +case and owner source. docs/security-case-execution-owner-review.md supports +the closed adapter/receipt direction with explicit versioned schema, structural +materialization provenance, unchanged caller/session boundaries, safe CAS +reconciliation and exact-created-object rollback requirements. This is owner +review, not approval. Supporting file digests are in the adjacent sources JSON. + +Current source exposes a circular wait: ops-mason T03 waits on mint while Warden +and the broker refuse a non-materialized case. Structural acceptance therefore +needs its own native pinned receipt before mint, not a successful mint before +materialized. The platform broker additionally pins type1.0 while Forgejo is +accepted at1.1; platform-owner alignment and tests are required, not an unchecked +version bypass. T06 remains wait. No case/type lifecycle status, grant, authority +or live runtime changed by this review. A proposed informed-decision would +adopt the source contract only; native materialization and execution acceptance +stay distinct. + +Final proposal re-review: staged Warden proposal SHA256 78ab1439e4c5beb205aeccf15b1b2e706511be55ab195c5a3aa682f326c7c77c +adds only the explicitly local reducer appendix, not an accepted wire protocol +or executor. The exact-final review and sources JSON supersede the earlier +1035f049... proposal review. Platform staged the exact type1.1 broker correction +with ten fake-backend subprocess checks (script SHA256 +9576cd211cfc06526ec4cc9bd0a5b06f0f59287a68931bb2d6e3592a951ddc7d). +The next source work is the versioned closed adapter/receipt/materialization +contract, not another relaxation of the broker's status gate. Native work still +requires installed broker provenance, structural reader receipts, exact new +materialized-case bytes, caller grant/session revocation and verified execution. +No installed/native acceptance is inferred from that staged correction. + +2026-10-11 — new source-only type1.2 architecture acknowledgment: +Confirmed native founder ACCEPT of infd-20261011-forgejo-type12-source-r1@1 +at06:47:39Z, approval8e9332c8-754a-420c-b8b7-445cb4fe6765. Exact frozen packet +0b377d647ca748cd03c92f9792396412048055d4bde9f7bb06ff6d413f7acd68 and submission +585cee731de30d2b37883a57abfd9eb32f023ee694e8d911989109d5caf80569; native receipt +retained in docs/evidence/2026-10-11-forgejo-type12-source-acknowledgment/. +The complete acknowledged1.2 source package is isolated under +security-case-type-revisions/1.2/oidc-browser-relying-party; default loader and +current accepted1.1 case remain untouched because load_type is single-version. +The new proposed1.2 case is NOT accepted/materialized/live and has no acceptance +fields. Actual reference-migration design approval is a separate Apps owner +receipt; no1.1 approval is inherited. Source-recording review/test proof and exact +next gates are in docs/forgejo-type12-source-acknowledgment-followthrough.md. +T06 remains wait and T07 remains its existing dependent task; type is NOT +implemented and no first-case/native grant or broader workplan finish is asserted. + +2026-10-11 — bounded explicit version selection source preparation for T06: +The validator now offers optional exact version_roots/--version-root VERSION=PATH +selection with duplicate/mismatch/traversal/symlink refusal and no fallback. The +default1.1 source and historicalacceptedcase hashes/ordinaryrefusal/preparation +render stayunchanged; actual1.1 is acknowledged, not implemented. Acknowledged1.2 +ordinaryadmission also refuses; optionalpreparation remains nonexecuting.43local +validatorchecks pass and original1.1 CLIrenderbytes match exactly. Makerproof: +docs/evidence/2026-10-11-explicit-type-version-selection/source-integration-validation.json. +FormalNetownerreview/reference-source integration and all existing native adapter, +provenance/broker/materialization/attendedproof gates remain; T06 stayswait, no +new residual or firstcase/typeimplementation/native grant is inferred. + + +2026-10-16 — isolated source preparation for T06 (canonical integration pending): +Added `check-evidence` to the existing validator CLI. It reuses exact version +selection and ordinary type/case/render admission, checks actual Mason structural +request source/inventory/projection pins and reads actual Warden journal rows +read-only. Successful rows require exact type-declared descriptors and attempt/ +revocation fingerprints. Existing acknowledged types without machine descriptors +remain plan-only. The compatibility result never claims authenticated native +completion: Request and journal fingerprints are not portable native evidence. +Owner authentication/status binding and actual native gates remain unresolved; +T06/T07 statuses and both type/case lifecycles remain unchanged. No dispatch, +credential access or authority expansion is introduced. See tool README for the +existing owner protocol boundaries and source integration tests. + +Concrete next Warden owner integration: current `Executor.admit` refuses every +case parameter whose sensitivity is not public/operational, so browser +`redirect_uris`, `allowed_scopes`, `client_type`, `mfa_required` (and 1.2 +`consumer_namespace`) fail after otherwise prepared admission. The sensitive +classification is a security-change review level, not secret content. The +Warden owner must validate through NetKingdom's existing exact-version case/type +validator, then project only descriptor-declared safe target metadata; do not +remove the credential/value refusal or copy complete parameters into journals. +Actual owner descriptor acknowledgment and independent evidence admission remain +prerequisites, with normal admission still refusing unimplemented types. + + +## T10 — Source compatibility checker for Mason and Warden evidence + +```task +id: NK-WP-0048-T10 +status: done +priority: high +state_hub_task_id: "4b06ca97-c1e4-5680-bf22-ab75ab1646a4" +``` + +Completed 2026-10-16 in published source `c31d21ba`. The read-only +`check-evidence` command checks the actual Mason structural request and Warden +journal source protocols against an exact version/render. Independent source +review passed 103 tests at that checkpoint. It always reports native completion +false and execution prohibited. Fixtures and local journal fingerprints do not +provide authenticated owner/native evidence. This records completed source scope +previously described under T06; it does not duplicate native acceptance credit. + +## T11 — Validate the optional machine-descriptor pin contract + +```task +id: NK-WP-0048-T11 +status: done +priority: high +state_hub_task_id: "ef2a0119-f59e-5e45-9519-115da9df7b85" +``` + +Completed 2026-10-16. Reproduced the JSON schema rejecting a valid +`execution_descriptors` map while the Python validator accepted malformed maps. +The schema now supports the existing Warden optional exact-byte pin map; semantic +validation binds keys to declared steps, bounds the map and checks digest shape. +The direct evidence API refuses malformed maps before accessing them. Twenty-one +new checks and all 127 affected source checks passed. No current type/case bytes, +status, owner descriptors or native acceptance changed. Source evidence and +publication receipt: Custodian +`docs/changes/netkingdom-target-2026-10-16/browser-acceptance-next/`. + +## T12 — Prepare NetKingdom-owned attended step adapters + +```task +id: NK-WP-0048-T12 +status: todo +priority: high +state_hub_task_id: "7ce4eee3-cd47-55a9-bfa5-4f1835d4d6fd" +``` + +Owner: NetKingdom. Retained scope: prepare bounded attended Deployment env, +config verification and restart/readiness steps around the existing helpers. +Next action: identify exact current entry points and their revision/readback +contracts, then implement fixed source-pinned callbacks or receipt-only attended +steps. Completion requires positive/refusal checks, exact target/source pins and +uncertainty behavior without acquiring user-engine runtime identity or exposing +registry/client secrets. Existing caller/session, cluster/issuer, CAS/readback +and status-only output boundaries remain mandatory. + +2026-10-16 task refinement: the original broad adapter scope is split into T13 +(completed registration source candidate), T14 (group creation provenance), T15 +(durable registration recovery) and T16 (descriptor/artifact selection and owner +review). This preserves T12's task ID and Hub UUID while giving each remaining +outcome an owner and completion gate. T06 now names every source dependency; +its native/reference lifecycle acceptance scope remains unchanged. Completion +of any source task supplies no Warden, Platform or Apps assent and no native +execution authority. + +## T13 — Prepare the fixed registration adapter source candidate + +```task +id: NK-WP-0048-T13 +status: done +priority: high +state_hub_task_id: "b264c10b-565c-5d29-b788-03cf5fbd2883" +``` + +Completed 2026-10-16, source implementation and synthetic fixture proof only. +Added an injection-only adapter around actual `keycape-client.run`: fixed +case/principal/human/lane/source/acceptance binding, existing CAS/readback, +metadata-only owner evidence and per-attempt parent-session revocation boundary. +The descriptor source composition pins the exact checker executable digest +alongside adapter/helper source. Creation-only rollback authenticates its +creation receipt and refuses changed postcreation UID/resourceVersion; +pre-existing clients confer no rollback authority. Missing readback, evidence, +revocation or process-local creation provenance cannot become retry-safe success. + +Evidence: Custodian `docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/` +R3 candidate, explicit patch and postimages, retained R1 rejected custody finding +and R2 revision, and 36 passing source checks (16 new, 20 existing). Three checks +use actual Warden kernel admission/dispatch/rollback with published Warden +`d97652a3bb5fa733b063d71a1b74068efcccf572` producer pins unchanged. Owner trust, +descriptor/type/case and Kubernetes objects remain synthetic fixtures. This +records a completed source deliverable, not independent review, publication, +type acknowledgment, exercised procedure, identity admission or native browser +acceptance. Independent root source review: Custodian browser-adapter-next/coordinator-review-r3.json. Source integration and normal publication/synchronization receipts: Custodian browser-adapter-next/canonical-integration.json. These receipts supply no other-owner assent or native authority. + +Source publication: `0775c2bfa41af236301155bb5a69bbf088bdf823`, independently +reviewed by the Custodian coordinator before integration. The required initial +projection and ordinary UUID writeback both completed with zero failures and +zero automation errors. Generated-only brief commits were refused by the normal +hook; exact pre-existing staged/working brief bytes were restored. The separate +canonical integration receipt retains those refusals and projection evidence. +Native admission and all other-owner acceptance remain open. + +## T14 — Admit governed group rollback against native owner boundaries + +```task +id: NK-WP-0048-T14 +status: progress +depends_on: [NK-WP-0048-T20, NK-WP-0048-T21] +priority: high +state_hub_task_id: "fce95440-a25b-5f27-8ef3-a3d182099113" +``` + +Owner: NetKingdom. Next action: bind the completed T21 PostgreSQL journal to +an admitted dedicated CNPG consumer through the existing `rapp-postgres` +database-per-consumer contract. Identify the actual deployment/package and +custody owner, admitted placement/capacity and role/renewable lease/TLS target; +implement the independently retained exact content checkpoint and authoritative +tenant/case/source/principal callbacks before enabling governed group operations. +The injection-only backend and fixture callbacks do not supply production scope +or authorization. Existing service tokens, caller-supplied JSON and provenance +receipts confer no new grant. The default production endpoint remains disabled +without the trusted integration. SQLite remains local/disposable only; any +production exception requires explicit scoped governance under ADR-0015. + +2026-10-17 owner-integration refinement: T23 records the completed source/package +placement review, distinct from T21's source/disposable-SQL proof. Its dedicated +`rapp-identity-provisioner` recommendation is draft and unbound, not an accepted +package or existing repository. The full six-consumer rapp-postgres renderer +accepts the inert candidate on platform-pg-2 as its third of four declared +consumers; first-cell placement is refused as a fifth. Neither the estimated +four-consumer ceiling nor source requests establish native headroom or backup. + +Next independent owner contracts: NetKingdom/package/substrate owners accept +exact deployment/rollback fate; rapp-postgres admits the dedicated consumer, +roles, five-connection budget and actual backup/restore; Platform admits exact +renewable runtime/migration delivery and TLS/CA/SAN; network owners scope SQL, +broker and checkpoint paths without assuming a narrow policy subtracts additive +same-namespace access. Protected caller owners must provide authoritative +GroupJournalBinding construction and current authorization; an independent +existing holder must authenticate and atomically advance the exact content +checkpoint, not a counter or ordinary Audit receipt. Directory/runtime owners +must select and prove either an exclusive all-writer maintenance barrier or +provider-supported CAS/generation, including established sessions, lifecycle +cascades and remove/readd ABA. No source review establishes those owner facts. + +The default PostgreSQL activation and destructive native rollback remain refused. +Storage/lease acceptance can be proposed separately from LDAP mutation only after +exact owner source/target/custody/verification/recovery and cost/risk review; +State Hub guard approval grants none of it. Source-only integration review and +native acceptance matrix: Custodian native-storage-r1/OWNER-REVIEW.md. T14's +existing status, UUID, dependencies and native completion gate are retained. + +2026-10-17 refinement: T20 retains the historical durable SQLite source and +actual disposable LLDAP proof. T21 extracts the completed PostgreSQL source and +bounded real disposable SQL compatibility proof. T14 remains `progress` for +native CNPG admission/recovery, protected owner integration and exclusion of all +external LDAP writers. Separate source publication/projection and independent +cleanup receipts belong to coordinator follow-through; no missing native assent +is represented as received. + +Completion still requires independently reviewed/admitted ADR-0015-conforming +CNPG storage (or an explicit governed exception), backup/lost-store recovery and +migration of legacy requests; a native +writer-isolation/fencing or provider CAS/version contract that closes external +writer races and remove/re-add ABA; exact case/source/principal and current +native object bindings for protected callbacks; and actual per-attempt evidence, +revocation and readback/refusal verification through the existing owner paths. +Only independently retained confirmed creations may be undone. Every +pre-existing/shared group and membership must remain preserved; unresolved +intents never expire into retry authority. The disposable fixture supplies none +of these native custody, authorization, owner-acceptance or production claims. + +## T15 — Define authenticated recovery for lost registration process state + +```task +id: NK-WP-0048-T15 +status: wait +depends_on: [NK-WP-0048-T31] +blocking_reason: "Source candidate 061461e is merged. Waiting on the owner to select the real authenticated original-attempt holder and native readback, and on independent re-review of the changed T13 adapter source." +priority: high +state_hub_task_id: "1b825705-4848-5edc-832f-b1622cd9bdb7" +``` + +2026-10-17 source part merged (061461e via 65559ef). `build()` takes optional +paired `holder`/`observe` capabilities and installs `reconcile_original`; the T31 +fixtures now run through the adapter's own path. Rollback after restart is still +refused, and there is no add replay. This changes `keycape-registration-adapter.py`, +so the revision reviewed under Custodian `browser-adapter-next/coordinator-review-r3.json` +is superseded. The new revision has no independent review yet and must not be +pinned on the strength of the old one. + +2026-10-17: T31 fixtures done (74adcd7). What remains for T15: the owner +selects the real authenticated original-attempt holder, and the T13 adapter +gains the holder capability, `reconcile_original` and reconcile-aware `verify` +described in T31. + +Owner: NetKingdom for registration state, with Warden owning kernel evidence +admission. T22 records the completed source review and exact existing-seam gaps; +T15 retains implementation and authenticated restart/lost-receipt acceptance. +Next action: select and admit the actual owner publisher/authenticator and bounded +original-attempt readback contract for the merged holder/observe adapter seam. +Warden's original-intent persistence/interface (`a45cf475`) and additive validation +(`117fec9241e7b985dfb187b992e173d0a5dc3700`) are now published and projected. +The local Journal retains admitted nonsecret original context, validates original +actor/attempt and metadata linkage to the original running receipt, and supplies +it separately from the new reconciliation attempt. This local consistency is not +independent original-holder authorization or creation provenance; neither the +journal nor a current matching registry entry reconstructs creation authority. + +The owner contract must bind the original Invocation, case/type/descriptor/catalog/ +acceptance pins, source, principal and admitted tenant/realm provenance, separately +from the new reconciliation attempt and its independently revoked parent session. +Use an existing authenticated holder/readback; Audit Core's custody API is a +candidate only until its actual registration source/class, load-bearing emission +contract and separate scoped reader are admitted. No new broker or receipt trust +root is proposed. A missing original record, ambiguous publication, NO_CHANGE, +changed native revision or lost readback stays uncertain; no add replay or undo. + +Completion requires restart/lost-receipt fixtures against the selected actual owner +contract: independently authenticated original created-only provenance, original +exact client entry and unchanged current postcreation UID/resourceVersion before +any undo, with wrong attempt/case/source/principal and unavailable-holder refusals. +The merged T13 adapter exposes reconciliation only through paired injected +holder/observe capabilities and still refuses post-restart rollback. Existing +source review, JSON receipts and synthetic trust do not satisfy independent +holder authorization/provenance or native admission, or authorize attended recovery. +Exact remaining owner facts: Custodian native-registration-r1/original-attempt-review.json. + +2026-10-17 check (net-kingdom-worker): Warden's original-intent lookup interface +is not published — ops-warden head is `d97652a`, with no WARDEN-WP-0044-T03 +commit, and the net-kingdom inbox holds no announcement. No restart/lost-receipt +fixture or readback seam was added this session; the work is deferred behind +T16's decomposition under the same token budget. Remains todo, gated on Warden +publishing the reviewed lookup/context interface and on owner selection of the +authenticated original-attempt holder. + +2026-10-17 current-source correction: the earlier same-day `d97652a` lookup +checkpoint and T31 adapter-next-action note above are historical and superseded +by the published Warden interface/validation and merged `061461e` adapter seam. +The 54 focused Warden checks establish source behavior only. Current admission +leaves tenant/realm empty; no admitted tenant/realm provenance is inferred. +T15 remains wait for actual independently authenticated owner-holder/readback +selection, independent review of the changed adapter, and native restart/lost- +receipt acceptance. Existing task metadata, owner gates and historical reviews +are unchanged. + +## T16 — Select and review the actual machine descriptor composition + +```task +id: NK-WP-0048-T16 +status: todo +depends_on: [NK-WP-0048-T24, NK-WP-0048-T25, NK-WP-0048-T26, NK-WP-0048-T27, NK-WP-0048-T28, NK-WP-0048-T29, NK-WP-0048-T30] +priority: high +state_hub_task_id: "200bf500-5084-5ae7-be26-3033fe1f296e" +``` + +2026-10-17 coordinator review r4 approved the 1.3 candidate at 1920b13 with no +findings (the-custodian `nk48-adapters-1.2-review/coordinator-review-r4.json`). The founder chose +option A of the T16 owner-review packet (`nk48-t16-owner-review/founder-option-decision.md`): +1.3 with execution descriptors for all seven steps. Owner reviews for A are open +in the-custodian `docs/changes/netkingdom-target-2026-10-17/nk48-1.3-owner-review/` (net-kingdom, +railiance-platform, railiance-apps, ops-warden, key-cape). The owner-review packet +supersedes the r1 composition pointer: T16's exact composition is now +`sso-mfa/k8s/keycape/descriptor-composition-1.3.candidate.json`. + +2026-10-17 successor candidate prepared (source only, `proposed`): +`security-case-type-revisions/1.3/oidc-browser-relying-party/` is the 1.2 package plus +an `execution_descriptors` map of the seven composed descriptors, with examples +pinned to `@1.3` and no acknowledgment. ensure-groups, hand-over, +deliver-consumer-secret and the composer accept 1.3 (835680c, merged acda26c). +`test_admit_1_3_type.py` runs Warden's real admission against the committed 1.3 type: +the proposed type is refused; an acknowledged copy is refused because ops-warden +pins parameter fingerprints only for 1.1 and 1.2; and with only a 1.3 pin (equal to +1.2's fingerprint) all seven are admitted. This is a major change under canon +section 6. Next: r4 source review of the changed adapters and the map, then owner +reviews, including the ops-warden 1.3 pin, the mint-secret warden_executes/human +question and the owner-run steps question, then the founder's Decision Memo. + +Owner: NetKingdom for descriptor/type source and exact checker artifact; Warden +independently owns global provenance/admission. T22 records the completed seven-step +source1.2 inventory and regenerated register-client descriptor using the delivered +checker and current sealed-snapshot source; T16 retains exact owner review, +complete descriptor composition and admission. Next action: bind actual native +publisher/authenticator, original/current session verification and revocation, +executable storage/custody and config pins, then obtain the six missing fixed +owner descriptors/adapters for ensure-groups, mint-secret, deliver-secret, +bind-env, deliver-consumer-secret and hand-over. The inventory identifies each +existing owner seam, exact missing contract and created-only rollback requirement; +it is not an executable bundle. T14 and T15 remain independent prerequisites. + +Review the dedicated consumer source1.2 composition only through the existing +explicit version-root selection. Keep canonical1.1/default and its accepted case +unchanged, and the source-only acknowledged1.2 reference case proposed without +acceptance fields. New descriptor bytes require a reviewed exact successor +revision/version under canon section6; they do not inherit historical source +acknowledgment. Changed authentication/materialization/trust needs the prescribed +informed-decision acknowledgment. Do not insert a partial descriptor map or use +--allow-unimplemented preparation as ordinary case/native admission. The registry +helper reports restart_required; separately review quiet-window restart/readiness +and actual consumer verification rather than infer activation from registry CAS. +Exact composition: Custodian native-registration-r1/descriptor-composition.json. +Completion requires separate owner source review of exact machine descriptors, +artifact/source/config pins, normal type/version admission and independent +evidence-verification boundaries. Warden d97652a refreshes producer validator +pins for NetKingdom 015694ee; it does not attest T13's new adapter. Pinning any +machine descriptor into an acknowledged type is a separately reviewed source +change. Platform owns structural/mint/broker/delivery evidence; Apps owns actual +consumer login/group enforcement. Neither their assent nor native acceptance is +supplied by this task. + +2026-10-16 source checkpoint: T19 below extracts the completed per-attempt +executable snapshot implementation and reviewed fixture proof. T16 retains +native executable storage/custody, exact owner evidence/session callbacks, +complete descriptor composition and ordinary type/version admission. The +successor descriptor/source pins are proposed only; the earlier descriptor +history is preserved. T14 group provenance and T15 durable restart/recovery +remain independent open requirements. No native acceptance is counted here. + +2026-10-17 budget checkpoint (net-kingdom-worker): no source change made. The +T13 registration adapter alone is 217 source and 317 test lines plus an +independent review; six further owner descriptors/adapters cannot be completed +within one per-task token budget (soft 8000 / hard 10000), so the task was +stopped below 50% and is decomposed rather than pushed through. Proposed split, +one source-candidate task each with positive/refusal fixtures, canonical1.1 +default unchanged and no --allow-unimplemented admission: (a) ensure-groups, +bound to T14's journal/provenance seam; (b) mint-secret and (c) deliver-secret, +Platform-owned evidence seams; (d) bind-env and (e) deliver-consumer-secret; +(f) hand-over, Apps-owned consumer verification; then (g) descriptor composition +over the reviewed set. Each candidate needs its owner's source review before +pinning; none is authored here as owner-accepted. Status stays todo. + +2026-10-17 task refinement after coordinator r5: completed independent mint- +guard regression coverage is extracted as T32. T33 tracks the exact Platform +issuer-store source/reference correction; T34 tracks the Apps configuration +inverse reachable without secret or hand-over resource creation. T16 retains +final exact seven-descriptor composition, independent owner reviews and admission; +its status and existing dependencies are preserved. The kernel branch also needs +its own KR1-F1/F2 admission/dispatch corrections and independent review under +WARDEN-WP-0044-T03 before any 1.3 pin. Historical r4 acceptance does not supersede +r5's findings or establish native readiness. + +## T17 — Build and exercise the exact offline config-checker artifact + +```task +id: NK-WP-0048-T17 +status: done +priority: high +state_hub_task_id: "8c3e4baa-a218-5996-af9e-fe1153a46dac" +``` + +Completed 2026-10-16, isolated artifact/source fixture proof only. Built the +existing `key-cape/src/cmd/keycape` command from published clean source revision +`3c71226a1a7b8a2bdb710e9c497e1a1f7f8a3987`, source tree +`f0010b9cab17dbc3a787628a29657d98b900320f`, with cached Go 1.23.0, +CGO disabled, Linux/amd64 v1, trimmed paths, disabled VCS stamping and offline +read-only module selection. The full retained closure pins 108 source files, +9876 toolchain files and ten used module source directories. Three isolated +build locations produced identical 13633409-byte artifacts: +`sha256:be37738fe1c08259fb4f9ca0daf0f9301bfffc237e5bb38435525dfbf11ccff5`. + +Eight disposable checks use the actual executable and maintained helper/kernel, +with complete synthetic configs and a minimal environment: add/created-only +removal, normal pinned producer admission, rollback, lost-readback uncertainty, +stale revisions, substituted checker refusal, invalid startup config and missing +secret-env refusal. No checker stub or producer-pin override remains. The +artifact-bound descriptor proposal is +`sha256:76b1153d188062ece53fcd5e8d35290fdbfd02a69dd79d4b001fc42781afcdc3`. +Evidence: Custodian +`docs/changes/netkingdom-target-2026-10-16/browser-checker-artifact-next/`. +The first two build attempts succeeded but their closure collection failed; +both failures are retained, as is refusal of the older incomplete synthetic +fixture. No production config, credentials or cluster mutation was used. + +This extracts completed build/fixture scope from T16 without claiming KeyCape +owner endorsement, native checker selection/storage custody, acknowledged type +integration, native evidence/session callbacks or attended browser acceptance. +T16 retains exact owner review and composition/admission scope. The attended +registration procedure remains unexercised. + +## T18 — Make checker input and executable selection deterministic + +```task +id: NK-WP-0048-T18 +status: done +priority: high +state_hub_task_id: "9c45da71-fb4c-5f20-9065-90f448f37c64" +``` + +Completed 2026-10-16, isolated source correction and real-artifact fixture proof +only; independent integration/publication remain separately recorded. KeyCape +source review identified non-secret `KEYCAPE_ACCOUNT_PORTAL_URL` and +`KEYCAPE_BROWSER_LOGOUT_URL` overrides in config.Load. The original helper +inherited ambient environment, allowing valid supplied configuration to be +rejected or invalid supplied URL configuration to be admitted. A bare relative +checker filename could also be replaced by PATH lookup after the adapter had +hashed the selected local file. All three counterexamples are retained against +the unchanged predecessor. + +The existing helper now resolves the selected checker file to an absolute path +and passes an explicit empty subprocess environment. Three opt-in real-checker +regressions verify both URL-verdict directions and relative-file selection under +a non-checker PATH decoy. All 39 affected checks pass with the exact supplied +artifact, as do 11 real-artifact helper/kernel scenarios. Source evidence: +Custodian `docs/changes/netkingdom-target-2026-10-16/browser-checker-environment-r2/`. +Historical descriptors remain retained; the helper correction changes successor +source-composition pins and requires their separate review/admission. An absolute +path is not immutable custody and does not eliminate file replacement races; +T16 retains native executable storage/custody and callback/type admission. +No production configuration, credentials or cluster mutation was used. + +Source integration and publication: `916ba4b33e03179274d03b78bd898a4843e8653f`, following independent +Custodian source review in browser-checker-environment-r2/coordinator-review.json. +The ordinary protected projection pass assigned T17/T18 UUIDs with zero failures +and zero automation errors; generated-only brief commit refusal is retained. +The exact prior staged/working brief bytes were restored. Final source/publication/ +projection evidence is retained in Custodian browser-checker-environment-r2/ +canonical-integration.json. These source receipts do not admit native executable +custody, owner callbacks, acknowledged type changes or browser identities. + +## T19 — Bind checker execution to verified immutable attempt bytes + +```task +id: NK-WP-0048-T19 +status: done +priority: high +state_hub_task_id: "f50fdcd1-00be-5c3c-be0c-345aa2126032" +``` + +Completed 2026-10-16, independently reviewed source implementation and synthetic +fixture proof only. The registration adapter now opens a bounded regular checker +artifact without following its terminal symlink, verifies the reviewed digest, +and snapshots the exact bytes into a write/grow/shrink/seal-removal-sealed Linux +memfd before helper access. Live and proposed config checks execute that same fd +through procfs with the existing empty environment; the fd closes on completion +or failure. Unsupported snapshot/seal/exec policy refuses through the existing +parent-session revocation and uncertainty boundaries. The earlier unbounded +pre-snapshot path read was removed during independent review; actual adapter +symlink/FIFO/oversize refusal tests verify helper access never starts. + +Evidence: Custodian `docs/changes/netkingdom-target-2026-10-16/browser-resume-r1/`, +including exact base/preimages/postimages/patch, proposed descriptor, coordinator +source review and canonical integration receipt. Independent Custodian checks +passed 49 affected source tests and 12 actual Go checker/helper/kernel scenarios; +the sets overlap and are not separate acceptance counts. Existing published +Warden d97652a producer pins remain unchanged. The exact KeyCape artifact remains +`sha256:be37738fe1c08259fb4f9ca0daf0f9301bfffc237e5bb38435525dfbf11ccff5`. +The predecessor path-replacement refusal and the first expanded reopened-fd test +failure are retained; the latter fixture was corrected to restore write mode +before independently proving seals block reopened-fd writes and shared mmap. + +This source task adds no native owner custody, callback, evidence trust, type +acknowledgment, credential access, consumer sign-in or rollback acceptance. +Executable memfds, Linux seals and usable procfs are explicit runtime dependencies +and still require native admission. This is an extracted source outcome from +T16, not completion of T16; T12/T14/T15 and all external-owner gates remain open. + + +Publication verified by the coordinator after the source worker usage limit: +`8790dd18ae83c4651ef3d1d923312a118a5b436b` is the actual remote main revision. +All five canonical source hashes match the reviewed packet. Normal synchronization +completed with zero failures/automation errors; all 19 task statuses match the +projection. T19's assigned UUID is retained. The original staged and working brief +bytes were restored and independently compared. The exact source, projection and +publication checks are in Custodian browser-resume-r1/canonical-integration.json. +Native acceptance remains open under T16 and the other named owner tasks. + + +## T20 — Prepare durable group provenance and prove disposable LLDAP compatibility + +```task +id: NK-WP-0048-T20 +status: done +priority: high +state_hub_task_id: "863fcbdb-e246-51ba-87ca-a2f44d01d6f8" +``` + +Completed 2026-10-17, source candidate preparation and bounded synthetic +compatibility proof only. This extracts evidenced implementation/fixture scope +from T14; T14 retains native storage, writer isolation, protected callback and +owner-acceptance gates. Source integration/publication and State Hub projection +are pending the coordinator's separate receipts; no revision or task UUID is +invented here. + +The existing identity-provisioner group ensure/revoke interface now has a +reviewed candidate for a bounded owner-private SQLite journal, proven only in +local/disposable use. The initial storage instructions wrongly suggested a native +persistent-volume path; source review against accepted ADR-0015 corrected them +before publication. Production SQLite-on-a-PVC is not admitted: T14 retains +CNPG/rapp-postgres storage admission or a separately governed exception. +The local journal saves +complete prestate and intent before each attempted directory mutation, inventories +only confirmed new groups/memberships, binds original ensure idempotency and +correlation to the exact request digest, and refuses unsigned caller inventories +or legacy broad revoke requests. Replays never recreate removed state. Missing, +replaced or untrusted storage and missing intent rows stop execution; definite +before-mutation refusals retain evidence without stranding unrelated valid work. +Interrupted writes/readback remain unresolved across reopening. Revoke validates +all targeted native ids and complete member postimages before destructive work, +removes only confirmed creations and never deletes a preexisting empty group. +Server/configuration wiring, image-copy closure and owner storage/request +migration instructions are included. The existing endpoint's caller +authentication remains the existing boundary, not a new grant. + +The candidate was prepared against NetKingdom +`878abc93588e12b6da1bac055cfbb3abb8d7adf1`; exact source patch digest +`sha256:1f3f3ca94c46dadac8f24896053d4d23179de795e50a24298a0cfbcc46cca30e`. +The independent Custodian review passed all 65 provisioner checks. The earlier +62-check candidate, two independent-review findings and subsequent fixes remain +retained rather than overwritten. Seven additional fixture-plan checks exercise +the preparation boundary; these counts overlap the same candidate and do not +measure task-completion percentage or independent acceptance. + +A separately reviewed bounded fixture then exercised the exact candidate against +cached pinned disposable LLDAP and Python images, through actual GraphQL. All 14 +closed compatibility/containment oracles passed: exact creation inventory, +separate-process SQLite reopening/replay/revoke, complete preexisting directory +preservation including empty/shared groups and memberships, refusal of shared +membership drift, retained uncertainty after a lost response following a real +addition, and refusal of unknown/cross-case receipt bindings. The fixture used +only newly generated synthetic users/passwords/key material in private tmpfs, +with a network-none anchor and shared loopback helper, no host ports/mounts, +no image pulls and no production or actual credential contact. The coordinator's +safe receipt reports both exact owned containers absent after cleanup; final +independent cleanup verification remains the coordinator's integration duty. + +Evidence: Custodian +`docs/changes/netkingdom-target-2026-10-16/group-provenance-r1/`, including exact +source base/pre/postimages, patch/manifest, independent review and retained +refinements. Native fixture manifest +`sha256:8a0514f5f14137355972400cd4852f48c892316d28e3abd3c86635d7cb81eef2`; +retained safe result `native-fixture/execution-result.json` (original +`/tmp/nk48-groups-native-review-5pywb19z/result.json`). The source task supplies +no native owner assent, production rollout, protected session/evidence callbacks, +ordinary type admission or consumer login proof. LLDAP has no conditional +membership mutation or generation token here: exact readback cannot exclude +ABA or a concurrent external writer, so T14 remains open. + +2026-10-17 verified source follow-through: source commit `4f65e41` passed normal +local scanning and all 65 canonical provisioner tests. The actual disposable +LLDAP receipt passed all 14 oracles; independent verification on the pinned +daemon found both exact names and the run label absent. Source integration +includes the ADR-0015 documentation correction; the journal remains local-only. + +T14 source preparation is underway. Twenty-three pinned public sources identify +NetKingdom implementation/reconciliation, Platform custody/database governance +and rapp-postgres consumer provisioning; the inspected runtime binding does not +assign identity-provisioner a package. The existing User Engine runtime token +and pod route are not a protected-case caller lane. The native proposal preserves +the same journal seam on a dedicated CNPG consumer and requires authoritative +scope binding, admitted role/lease/placement/recovery and all LDAP writers fenced. +The declared exclusive maintenance window is not mechanical exclusion of WebUI, +helper or lifecycle writers. Capacity placement and EUR cost/risk remain owner +decisions. No package, database, credential, rollout or owner assent was created. +Custodian evidence: group-provenance-r1/native-storage-proposal-r1; final source, +projection and cleanup review: resume-integration-r1/publication-verification.json. + + +## T21 — Implement the PostgreSQL journal and prove disposable SQL compatibility + +```task +id: NK-WP-0048-T21 +status: done +depends_on: [NK-WP-0048-T20] +priority: high +state_hub_task_id: "afe78e4d-408a-516a-ab92-e212e8a2d11d" +``` + +Completed 2026-10-17: bounded source preparation and actual disposable PostgreSQL +compatibility only. T14 retains native storage, custody, callbacks, authorization, +all-writer isolation and production admission. Coordinator source publication and +projection remain separate follow-through; no source revision or task UUID is +invented here. T20 remains the historical local SQLite/actual LLDAP proof. + +The eight-path source candidate adds an injection-only PostgreSQL implementation +behind the existing owner journal seam, with explicit trusted tenant/directory/ +case/source/principal/epoch binding and independent continuity callbacks. It +persists bounded intent/created-only receipts, exact-one version updates, durable +active fences, and a content-bound journal chain. SQL commits precede independent +exact-marker acknowledgment; lost replies/checkpoints poison continuation and +retain uncertainty. A counter-only checkpoint is explicitly insufficient for +divergent clones. Migration does not create database/schema/credential, admit an +epoch or clear quarantine; production HTTP activation is absent pending trusted +callbacks. Pinned psycopg package/image-copy closure is included, without claiming +an image build or native dependency admission. + +Candidate base `6cb43c0cc55212363a5406b02db046e2384a7c40`; final eight-path patch +`sha256:5549d2982b61ff70aba4b3928fecd5639db0f1a732ab2f8630f678e7acf4d34d`. +All 82 source tests pass, alongside 13 fixture plan/diagnostic checks and two +source-backed snapshot regressions. Counts overlap the same source and fixture; +they are not additive native acceptance or completion percentages. + +The independently reviewed R5 fixture ran the actual SQL migration/backend and +existing provisioner path against cached pinned disposable PostgreSQL. Directory +operations deliberately used the existing synthetic LLDAP test double. All +manifest-declared phase oracles passed: creation-only inventory and DDL refusal; +separate-process replay/revoke with complete preexisting/shared visible-state +preservation; foreign-context refusal; concurrent exactly-once reservation and +stale version refusal; actual database fork with divergent bodies at the same +counter and fresh-object rejection of the losing exact checkpoint; retained +uncertainty after checkpoint/directory/SQL response loss; and actual pg_dump/ +pg_restore rollback refusal against the independently retained fixture checkpoint. +The checkpoint is fixture-only, not an admitted native owner evidence holder. + +Only generated credentials/state were used, with two nonroot/read-only containers +on private loopback, bounded tmpfs/CPU/memory/PIDs/lifetimes, no host ports/mounts, +no pulls and no production contact. The safe receipt reports both exact owned +containers absent, with no daemon uncertainty. Independent final pinned-daemon +cleanup verification remains coordinator follow-through. + +Failed R2/R3/R4 receipts and source revisions remain retained. R2's helper failure +subphase is unknown; R3 exposed a real backend bug where replacing nested receipt +objects lost the created ID held by ensure, fixed by acknowledging only the backend +version after commit/checkpoint. R4 verified that fix through actual SQL bootstrap, +then exposed fixture snapshot aliasing and empty deleted-group backing entries; +source-backed regressions corrected the snapshot without weakening complete +visible-prestate equality, and reject nonempty orphan membership. These refinements +belong to one trial, not extra native acceptance successes. + +Custodian evidence: `docs/changes/netkingdom-target-2026-10-16/group-postgres-r1/`; +exact source/pre/postimages and retained predecessors; native R5 manifest +`sha256:d51f90816616848b93bcdc482e10a98d07061deb01eeed9e50700fc6e68b6051`; +`final-integration-r1/typed-execution-evidence.json` and byte-exact safe receipt +(original `/tmp/nk48-postgres-native-review-sguj7t8p/result.json`). No CNPG native +storage/HA/backup/lease/TLS/capacity/restore admission, authoritative protected +callbacks, real LDAP writer fencing/CAS/ABA exclusion, owner assent or production +endpoint activation is claimed. Those remain live T14 work. + + +## T22 — Establish registration recovery source gaps and complete descriptor review inventory + +```task +id: NK-WP-0048-T22 +status: done +priority: high +state_hub_task_id: "ea50d93b-0d04-55d2-98c4-9b6c3fe925b1" +``` + +Completed 2026-10-17: source-only owner-contract review and complete review +inventory. Inspected the actual registration adapter, Warden Journal/Executor, +NetKingdom evidence compatibility checker and Audit Core custody/read surfaces. +No existing admitted holder/readback supplies authenticated original-attempt +recovery: payload/creation are process-local, persisted evidence references are +hashed, and reconcile receives a new attempt without original context. The exact +four-owner contract requirements and non-replayable missing/ambiguous cases are +recorded; no recovery callback, publisher, credential grant or native trust root +was installed. T15 retains implementation and actual acceptance. + +The complete source1.2 inventory maps all seven follow-through steps to their +existing owners, missing exact contracts and created-only rollback requirements. +The actual maintained adapter regenerated the sole existing register-client +machine descriptor against the delivered 13,633,409-byte checker and current +sealed-memfd snapshot source. The checker rehash and Warden Descriptor.decode +round-trip passed; six owner descriptors remain absent. Actual source commands +render canonical1.1 and explicitly selected1.2 with execution.permitted false; +ordinary1.2 case admission still refuses its acknowledged/unimplemented type. +Canonical type/case/acknowledgment bytes were hash-checked unchanged. No tests were +added or rerun, no native configuration or effect occurred, and no owner assent +or complete machine bundle is claimed. T16 retains native executable custody, +complete exact descriptors, independent source review and normal version/type +admission. This extracted outcome supplies no native gate completion credit. + +Evidence in the-custodian: +`docs/changes/netkingdom-target-2026-10-17/native-registration-r1/`, especially +original-attempt-review.json, descriptor-composition.json, source-verification.json, +source-manifest.json and manifest.json. The prior sixteen-gate audit remains +immutable. Publication, normal task UUID allocation and projection are separate +coordinator follow-through; no Hub UUID is assigned by this source record. + + +## T23 — Review native journal package assignment and storage integration boundaries + +```task +id: NK-WP-0048-T23 +status: done +priority: high +state_hub_task_id: "6e7cd483-c7af-53a2-9d0a-3d76b6fb49ee" +``` + +Completed 2026-10-17: source/package placement and protected integration review +only, separate from T21's completed PostgreSQL source and disposable-SQL proof. +The owner review recommends a dedicated independently rolled-back +`rapp-identity-provisioner`, owned by NetKingdom on rail-kubernetes, with a +schema-valid draft declaration and no reef binding. No repository, registry +entry, native package assignment or owner assent was created. Existing +rapp-user-engine ownership/token delivery does not assign it the provisioner. + +The complete current rapp-postgres catalog has six declared consumers: four on +platform-pg and two on platform-pg-2. The inert renderer-valid provisioner +candidate fits platform-pg-2 at three of four, requesting one migration plus +four runtime connections; placing it on the first cell is refused as a fifth. +Normative package schema and full-catalog capacity/render checks passed. The +four-consumer ceiling is estimated, not measured native headroom, and no current +SQL session, backup or restore acceptance is claimed. + +The exact review identifies remaining package/substrate, SQL role/lease/TLS, +network and backup/recovery contracts; independently authenticated protected +scope callbacks; an external content-bound checkpoint holder with atomic +advance/loss handling; and all-LDAP-writer exclusion or provider CAS/generation, +including lifecycle cascades and remove/readd ABA. Generic Audit custody is not +that checkpoint service. A database-only fence, final reread or one replica +cannot exclude foreign directory writers. The native acceptance/recovery matrix +keeps storage/lease admission separate from separately approved directory +mutation; the production activation remains disabled pending trusted integration. + +Evidence in the-custodian: +`docs/changes/netkingdom-target-2026-10-17/native-storage-r1/OWNER-REVIEW.md`, +source-validation.json and hash-bound source-references.json, plus the inert +proposed declaration/consumer and renderer outputs. No native resource, lease, +credential, deployment, destructive effect, independent holder or native owner +acceptance was delivered. T14 retains these live gates with its original UUID +and progress status. This completed review adds no duplicate T21 acceptance +credit; publication, task UUID allocation and projection remain coordinator +follow-through and no UUID is hand-assigned here. + +## T24 — Ensure-groups adapter source candidate + +```task +id: NK-WP-0048-T24 +status: done +priority: high +state_hub_task_id: "1a3931ee-3b5f-52fc-9295-4073804f4bd2" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): ensure-groups-adapter.py, 8 tests (commit 5a2e7d6). Binds to the T14 PostgreSQL journal and refuses SQLite or a foreign binding. It accepts only canonical 1.1 cases, so T30 must add an explicit 1.2 path. It calls the provisioner's private `_group_binding`; a public helper is the cleaner shared edit. + +Split from T16 on 2026-10-17 so each candidate fits one per-task budget. Bind group creation to T14's journal/provenance seam; refuse without trusted journal callbacks. +Source candidate only, modelled on the T13 registration adapter +(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and +`registration-adapter.md`). Requires positive and refusal fixtures and +created-only rollback. Keeps canonical 1.1 as the default and uses no +`--allow-unimplemented` admission. Done means the candidate and its tests are +committed. NetKingdom (directory) owner source review before pinning, and any native action, +stay separate gates and are not supplied by this task. + +## T25 — Mint-secret adapter source candidate + +```task +id: NK-WP-0048-T25 +status: done +priority: high +state_hub_task_id: "0de2a7d9-874d-578b-a076-f653fe42e30b" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): mint-secret-adapter.py, 9 tests (commit 558c5bc). Value-free receipts only. The receipt schema `railiance-platform.custody-mint.v1` and the mint/destroy capability interface are proposals that Platform must confirm. + +Split from T16 on 2026-10-17 so each candidate fits one per-task budget. Platform-owned custody evidence seam: the adapter records value-free mint evidence only and never handles secret values. +Source candidate only, modelled on the T13 registration adapter +(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and +`registration-adapter.md`). Requires positive and refusal fixtures and +created-only rollback. Keeps canonical 1.1 as the default and uses no +`--allow-unimplemented` admission. Done means the candidate and its tests are +committed. Platform owner source review before pinning, and any native action, +stay separate gates and are not supplied by this task. + +## T26 — Deliver-secret adapter source candidate + +```task +id: NK-WP-0048-T26 +status: done +priority: high +state_hub_task_id: "94b3cbf0-c908-5ac6-b0db-67385c3e021c" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): deliver-secret-adapter.py, 8 tests (commit 1b60f6e). The lane `platform-secret-delivery` and the schema `netkingdom.deliver-secret.v1` are proposals that Platform must confirm. + +Split from T16 on 2026-10-17 so each candidate fits one per-task budget. Platform-owned delivery evidence seam (ESO/custody), value-free. +Source candidate only, modelled on the T13 registration adapter +(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and +`registration-adapter.md`). Requires positive and refusal fixtures and +created-only rollback. Keeps canonical 1.1 as the default and uses no +`--allow-unimplemented` admission. Done means the candidate and its tests are +committed. Platform owner source review before pinning, and any native action, +stay separate gates and are not supplied by this task. + +## T27 — Bind-env adapter source candidate + +```task +id: NK-WP-0048-T27 +status: done +priority: high +state_hub_task_id: "e56365f8-29b5-5c89-a329-8115f9f3b2a7" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): bind-env-adapter.py, 5 tests (commit 6fbce73). Binds references only. To fit the budget it does not yet wrap a full Warden `Adapter` like T13 (source-digest pin, authenticated evidence, parent-session revocation); it returns `(reference, run, rollback)`. That wrapping is a prerequisite for T30. + +Split from T16 on 2026-10-17 so each candidate fits one per-task budget. Bind the issuer Deployment env reference to the delivered secret reference, without values. +Source candidate only, modelled on the T13 registration adapter +(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and +`registration-adapter.md`). Requires positive and refusal fixtures and +created-only rollback. Keeps canonical 1.1 as the default and uses no +`--allow-unimplemented` admission. Done means the candidate and its tests are +committed. NetKingdom runtime owner source review before pinning, and any native action, +stay separate gates and are not supplied by this task. + +## T28 — Deliver-consumer-secret adapter source candidate + +```task +id: NK-WP-0048-T28 +status: done +priority: high +state_hub_task_id: "6dab281c-ea9a-5a17-a83f-e8e9a4d4484f" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): deliver-consumer-secret-adapter.py, 5 tests (commit 650c1ac). Type 1.2 only. It assumes the owner has validated ESO TokenRequest and the controller-RBAC admission; that stays an owner gate. + +Split from T16 on 2026-10-17 so each candidate fits one per-task budget. Consumer-side delivery evidence seam, value-free. +Source candidate only, modelled on the T13 registration adapter +(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and +`registration-adapter.md`). Requires positive and refusal fixtures and +created-only rollback. Keeps canonical 1.1 as the default and uses no +`--allow-unimplemented` admission. Done means the candidate and its tests are +committed. Platform and Apps owner source review before pinning, and any native action, +stay separate gates and are not supplied by this task. + +## T29 — Hand-over adapter source candidate + +```task +id: NK-WP-0048-T29 +status: done +priority: high +state_hub_task_id: "129db41d-4560-5e20-9f3b-c3c91e0a5601" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): handover-adapter.py, 8 tests (commit ec63539). The owner `apps` and lane `apps-consumer-handover` are proposals that the Apps owner must confirm. It has no reconcile path; restart handling follows T15. + +Split from T16 on 2026-10-17 so each candidate fits one per-task budget. Apps-owned consumer verification and hand-over receipt seam. +Source candidate only, modelled on the T13 registration adapter +(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and +`registration-adapter.md`). Requires positive and refusal fixtures and +created-only rollback. Keeps canonical 1.1 as the default and uses no +`--allow-unimplemented` admission. Done means the candidate and its tests are +committed. Apps owner source review before pinning, and any native action, +stay separate gates and are not supplied by this task. + +## T30 — Compose the descriptor set over the reviewed candidates + +```task +id: NK-WP-0048-T30 +status: done +depends_on: [NK-WP-0048-T24, NK-WP-0048-T25, NK-WP-0048-T26, NK-WP-0048-T27, NK-WP-0048-T28, NK-WP-0048-T29] +priority: high +state_hub_task_id: "6f6073ac-52dd-5b70-8874-6d76d4c072e4" +``` + +2026-10-17 done: coordinator review r3 (the-custodian `nk48-adapters-1.2-review/coordinator-review-r3.json`, +fd07eaa) approved all nine files at ff8f597, with R2-F1..F3 resolved, an independent +reconstruction of 117 passing and no open findings. This is a source review only: +it grants no owner admission, pin or native authority. Owner review and admission +continue under T16, which also takes the r2 observations, including the +`warden_executes: false` steps that carry execution descriptors. + +2026-10-17 coordinator review r2 (the-custodian `nk48-adapters-1.2-review/coordinator-review-r2.json`) +approved five files and rejected three, plus the artifact. The fixes are merged in +2ee4ed0: R2-F1, mint-secret revokes the session and the broker grant +independently (eeed8e1); R2-F2, deliver-secret binds to the admitted case and +derives its Secret and custody path, with no caller reference (640292c); R2-F3, +bind-env pins its five-file closure, and a new closure-completeness test checks +every adapter (d16db30). Each regression test failed on 68f4bbb. The artifact is +regenerated, and the full suite including kernel admission passed 117. Next is +the r3 review. Open for owner review under T16: the type marks four steps +`warden_executes: false`, yet each has an execution descriptor. + +2026-10-17 founder confirmed every proposed lane and schema name for T25-T29 +as-is. The artifact records them as `names_confirmed` (by founder, 2026-10-17), +and no step is still `owner_confirmation_required`. Next: independent source +review of the seven adapters. + +2026-10-17 founder decision (supersedes the `agent` decision below): the +mint-secret actor is `human`. Coordinator review r1 (the-custodian +`docs/changes/netkingdom-target-2026-10-17/nk48-adapters-1.2-review/coordinator-review-r1.json`, +finding NK48-R1-F1) showed that unchanged kernel a45cf47 refuses every +`effect: mint` admission whose actor is not `human` ("agent mint is +forbidden"), so the `agent` descriptor could never be admitted. Adapter, tests +and composer comment updated and the artifact regenerated (mint-secret +descriptor digest now `sha256:8c40c2884f8ba15e8cb6c98f33419c395e54fce8ef33d6e1a34782cf3f1fcfa9`); +98 tests pass against a45cf47. The set needs an independent r2 review. + +2026-10-17 `test_admit_1_2_adapter_descriptors.py` runs Warden's own +`Executor.admit` on all seven composed 1.2 descriptors, with the real 1.2 +type, the 1.2 forgejo example case, the reviewed `BrowserInputs` validator and +fixture-only trust (stand-in adapters, nothing dispatched). It also pins that +an agent mint descriptor is refused ("agent mint is forbidden") and that a +wrong lane is refused for every step. Against the old `agent` revision +(582ab9f) it fails as expected. Combined suite: 102 passed on ops-warden a45cf47 +and on current ops-warden. Fixture admission is not owner admission or a pin. + +2026-10-17 founder decision: the mint-secret actor is `agent`, not `warden`. +The kernel is unchanged. The adapter, its test and the composer were updated and +the artifact regenerated; all seven steps pass `Descriptor.decode` and +`complete_executable_descriptor_set` is true (98 tests passed). The artifact +stays `not_an_admission`, with no owner review, admission or dispatch permission. + +2026-10-17 composer merged (60b41a1 via 3c62bec) with follow-up 376b44b (merged; +combined suite 98 passed). `compose-1.2-descriptors.py` loads 1.2 only through an +explicit version root and refuses partial, duplicate, unknown or mismatched maps. +`descriptor-composition-1.2.candidate.json` is a reviewable candidate, marked +`not_an_admission`, with canonical 1.1 unchanged. ensure-groups and hand-over now +carry and honour `parent-session` revocation, and the hand-over owner is +parameterised (`railiance-apps` for Forgejo). Six of the seven steps pass +`Descriptor.decode`. mint-secret is still refused: its actor `warden` is outside +the kernel's human/agent set, and Platform (with ops-warden, if the kernel should +change) must decide. Five steps carry `owner_confirmation_required` names. The +set is complete only after both. Owner review, the canon section 6 successor +revision and admission remain T16's. + +2026-10-17 prerequisites merged (65559ef; combined suite 78 passed): +ensure-groups accepts 1.2 via an explicit `version_root='1.2'` (531151b), with +1.1 still the default. bind-env is now a full Warden v1 `Adapter` (7442d21); its +kernel-level fixture tests like T13's are still missing, and its `build()` +signature changed. Composition is unblocked in source. Owner confirmation of +the proposed step, lane and schema names remains before pinning. + +2026-10-17 inputs from T24–T29 that composition must resolve: T24 accepts +only 1.1 while T28 accepts only 1.2, so add an explicit 1.2 ensure-groups path; +T27 still needs full Warden `Adapter` wrapping; the lane, schema and owner names +proposed in T25, T26 and T29 need owner confirmation before pinning. + +Split from T16 on 2026-10-17. Compose the seven-step source 1.2 descriptor set +through the existing explicit version-root selection over T13 and T24–T29. No +partial descriptor map, canonical 1.1 stays the default, and new descriptor bytes +need a reviewed successor revision under canon section 6. Owner review and +admission remain T16's. + +## T31 — Restart and lost-receipt fixtures against Warden's original-intent interface + +```task +id: NK-WP-0048-T31 +status: done +priority: high +state_hub_task_id: "ed1ccd87-89a0-5e69-885c-0eaf5ab0bb5a" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): test_registration_original_intent_reconcile.py, 8 tests (commit 74adcd7), runs against the unmodified ops-warden a45cf47. The holder is a test-only in-memory store. For real T15 support the T13 adapter's `build()` needs an injected holder/readback capability, a `reconcile_original` callback, and a `verify` that accepts reconcile results. Rollback after restart stays refused until there is an owner contract. + +Split from T15 on 2026-10-17. ops-warden published the original-intent +lookup/context interface in `src/warden/case_run.py` (`OriginalIntent`, +`Journal.original_intent`, `Adapter.reconcile_original`; commit `a45cf47`). +Build T15's restart/lost-receipt fixtures for the registration adapter against +it: the original intent is kept separate from the reconcile attempt; wrong +attempt/case/source/principal and unavailable-holder cases refuse; and a missing +or ambiguous record stays uncertain, with no add replay or undo. The interface +is Warden's own dispatch record, not owner-authenticated created-only provenance. +Selecting the actual authenticated holder stays a T15 owner gate. + + +## T32 — Restore independent agent-mint admission regression coverage + +```task +id: NK-WP-0048-T32 +status: done +priority: high +state_hub_task_id: "c6d0a27e-f6a7-5df0-95f6-3e7473d38bb5" +``` + +Completed 2026-10-17, source tests only. Extracted from T16's coordinator r5 +finding NK48-R5-F1; no earlier adapter outcome is counted again. The real 1.3 +admission test now separately checks the committed descriptor map, an in-memory +fixture map acknowledging the changed agent descriptor to reach the independent +mint prohibition, and the human descriptor under an explicit fixture agent +runtime marker. Only `sso-mfa/k8s/keycape/test_admit_1_3_type.py` changes. + +Against frozen Warden `117fec9241e7b985dfb187b992e173d0a5dc3700`, the new +suite passes 10 tests / 32 subtests. Independent mutation evidence removes only +the mint admission guard in scratch: both new guard tests fail while eight +controls pass; the old suite passed that mutant. Custodian independently reran +the canonical successor: 10 tests / 32 subtests pass. Evidence: +`the-custodian/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/` +(patch, frozen/mutant results and canonical-validation.json). No descriptor, +parameter pin, catalog, acknowledgment, kernel or native execution changed. +T16 still owns actual composition and admission; F2/F3 follow below. + +## T33 — Close the issuer-store reference finding with Platform source + +```task +id: NK-WP-0048-T33 +status: todo +priority: high +state_hub_task_id: "5ace28ff-a4cb-5647-a8f6-3cd54bb9d0a5" +``` + +Owner: Platform for the fixed store/template source and its delivery boundary; +NetKingdom for the 1.3 type reference. Split from T16's current coordinator r5 +finding NK48-R5-F2. Next action: independently review the prepared Platform +candidate and publish a concrete owner-controlled issuer store kind/name/scope +and template reference, then integrate only that exact proposed source into the +1.3 deliver-secret entry point and regenerate its composition artifact. Candidate +and current review evidence are under Custodian +`docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/` +and `nk48-r5-f2-review-r1/`. + +Completion requires exact committed source, a passing independent source review +and unchanged type parameter bytes. No consumer store may be inferred as issuer +store, and no source declaration supplies owner assent, installed ESO support, +CCR/grant changes or a real delivery capability. Those remain existing +RPF-1.3-T4 and T16 admission/native requirements. + +## T34 — Close the consumer configuration inverse finding + +```task +id: NK-WP-0048-T34 +status: todo +priority: high +state_hub_task_id: "37f43c40-d014-544e-aa0b-5219dd4a6b93" +``` + +Owner: Apps for consumer configuration and inverse; NetKingdom for exact type +placement. Split from T16's coordinator r5 finding NK48-R5-F3. Next action: +independently review and implement the owner transaction/inverse through the +existing RAPPS-WP-0016-T05 path, with authenticated durable before/after release +provenance, exclusive serialization of all release writers and a fresh authorized +attended session for every inverse attempt after the hand-over session is revoked. +The inverse must remain reachable for public clients, confidential NO_CHANGE +secret delivery and verification failure before a hand-over receipt exists. + +Completion requires real owner configuration/inverse source bindings and +independent regressions, then exact reviewed 1.3 entry/rollback text and composed +artifact. A synthetic wrapper with an injected backend alone is insufficient. +Keep hand-over receipt rollback receipt-only and secret deletion created-only; +preexisting or drifted resources must refuse unsafe inverse. Candidate evidence: +Custodian `docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/` +and `nk48-r5-f3-review-r1/`. Source review, owner acknowledgment and native proof +remain distinct. T16's final composition/admission and T06's native first case +are not completed by this refinement. diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/apply_prepare.py b/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/apply_prepare.py new file mode 100644 index 0000000..771fde3 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/apply_prepare.py @@ -0,0 +1,92 @@ +"""Prepared source integration only. Root runs --execute; no commit/projection here.""" +from pathlib import Path +import argparse +import hashlib +import json +import re +import subprocess +import sys + +PACKET = Path(__file__).resolve().parent +ROOT = Path('/home/worsch/net-kingdom') +WARDEN = Path('/home/worsch/ops-warden') + +def sha(data): + return 'sha256:' + hashlib.sha256(data).hexdigest() + +def git(root, *args): + return subprocess.check_output(['git', '-C', str(root), *args], text=True).strip() + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--execute', action='store_true') + args = parser.parse_args() + manifest = json.loads((PACKET / 'publication-inputs.json').read_text()) + assert git(ROOT, 'rev-parse', '--abbrev-ref', 'HEAD') == 'main' + assert git(ROOT, 'rev-parse', 'HEAD') == manifest['base_revision'] + assert git(WARDEN, 'rev-parse', 'HEAD') == manifest['warden_revision'] + workplan = ROOT / manifest['workplan'] + original = workplan.read_bytes() + candidate = (PACKET / 'workplan.candidate.md').read_bytes() + assert sha(original) == manifest['workplan_preimage_sha256'] + assert sha(candidate) == manifest['workplan_candidate_sha256'] + assert sha((PACKET / 'task-T35.md').read_bytes()) == manifest['task_addition_sha256'] + assert candidate == original + (PACKET / 'task-T35.md').read_bytes() + assert len(re.findall(rb'^```task$', original, re.M)) == 34 + assert len(re.findall(rb'^```task$', candidate, re.M)) == 35 + assert b'id: NK-WP-0048-T35' not in original + assert sha((PACKET / 'candidate.patch').read_bytes()) == manifest['patch_sha256'] + for row in manifest['files']: + path = ROOT / row['path'] + if row['preimage_sha256'] is None: + assert not path.exists() and not path.is_symlink(), str(path) + else: + assert sha(path.read_bytes()) == row['preimage_sha256'], str(path) + protected_before = {name: sha((ROOT / name).read_bytes()) for name in manifest['protected_files']} + assert protected_before == manifest['protected_files'], 'Operator brief/index changed: refresh the prepared inputs first.' + # Preserve the original index exactly; this script never stages anything. + index = git(ROOT, 'ls-files', '--stage', '-z') + assert sha(Path('/home/worsch/repo-manager/src/repo_manager/identifiers.py').read_bytes()) == manifest['repo_manager_identifiers_sha256'] + sys.path.insert(0, '/home/worsch/repo-manager/src') + from repo_manager.identifiers import ensure_missing_work_record_identifiers + before = ensure_missing_work_record_identifiers(ROOT, execute=False) + assert before['ok'] and before['assignments'] == [] and before['files_changed'] == [] + subprocess.run(['git', 'apply', '--check', str(PACKET / 'candidate.patch')], cwd=ROOT, check=True) + if not args.execute: + print(json.dumps({'prepared_only': True, 'preimages_verified': True, 'files': 4, + 'next': 'Run this script with --execute, then run_validation.py --execute.'})) + return + subprocess.run(['git', 'apply', str(PACKET / 'candidate.patch')], cwd=ROOT, check=True) + for row in manifest['files']: + assert sha((ROOT / row['path']).read_bytes()) == row['sha256'], row['path'] + workplan.write_bytes(candidate) + preview = ensure_missing_work_record_identifiers(ROOT, execute=False) + (PACKET / 'identifier-preview.json').write_text(json.dumps(preview, indent=2) + '\n') + assert preview['ok'] and preview['files_changed'] == [manifest['workplan']] + assert len(preview['assignments']) == 1 + assignment = preview['assignments'][0] + assert (assignment['kind'], assignment['record_id'], assignment['path']) == ( + 'task', 'NK-WP-0048-T35', manifest['workplan']) + # UUID comes only from the ordinary allocator, never from this packet. + actual = ensure_missing_work_record_identifiers(ROOT, execute=True) + assert actual['assignments'] == preview['assignments'] + assert actual['files_changed'] == [manifest['workplan']] + (PACKET / 'identifier-assignment.json').write_text(json.dumps(actual, indent=2) + '\n') + allocated = workplan.read_bytes() + assert allocated.startswith(original), 'Existing workplan bytes must be preserved.' + assert len(re.findall(rb'^```task$', allocated, re.M)) == 35 + assert allocated.count(b'id: NK-WP-0048-T35\n') == 1 + assert not ensure_missing_work_record_identifiers(ROOT, execute=False)['assignments'] + assert git(ROOT, 'ls-files', '--stage', '-z') == index, 'Index must remain unchanged.' + assert {name: sha((ROOT / name).read_bytes()) for name in manifest['protected_files']} == protected_before + (PACKET / 'workplan.allocated.md').write_bytes(allocated) + publication = {'owner': 'net-kingdom', 'base_revision': manifest['base_revision'], + 'message': manifest['message'], 'files': [ + {'path': row['path'], 'sha256': sha((ROOT / row['path']).read_bytes()).removeprefix('sha256:')} + for row in manifest['files'] + [{'path': manifest['workplan']}]]} + (PACKET / 'publication-inputs.allocated.json').write_text(json.dumps(publication, indent=2) + '\n') + print(json.dumps({'source_applied': True, 'task_count': 35, 'allocated_only': assignment['record_id'], + 'files': len(publication['files']), 'commit_and_projection': 'Root handles separately.'})) + +if __name__ == '__main__': + main() diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/candidate.patch b/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/candidate.patch new file mode 100644 index 0000000..671f3e2 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/candidate.patch @@ -0,0 +1,243 @@ +diff --git a/sso-mfa/k8s/keycape/test_admit_1_3_type.py b/sso-mfa/k8s/keycape/test_admit_1_3_type.py +--- a/sso-mfa/k8s/keycape/test_admit_1_3_type.py ++++ b/sso-mfa/k8s/keycape/test_admit_1_3_type.py +@@ -8,7 +8,9 @@ + acknowledged 1.3 is still refused until ops-warden adds a 1.3 pin. That is an + ops-warden owner change, and it is not made here. The tests show that 1.3's + parameters carry 1.2's pinned fingerprint, and that with only that one pin added in +-memory the committed map admits exactly the composed descriptors and nothing else. ++memory the five resolvable steps admit. The two Apps attended steps remain refused ++because their establishing login is unresolved in the synthetic catalog. Four real ++draft lane shapes are hypothetically active in that fixture; no catalog is promoted. + Trust is fixture-only, nothing is dispatched, and none of this is owner admission + or an acknowledgment. + """ +@@ -37,6 +39,8 @@ + TYPE_DIR = REPO / 'security-case-type-revisions/1.3/oidc-browser-relying-party' + TYPE_BYTES = (TYPE_DIR / 'type.yaml').read_bytes() + ARTIFACT = json.loads((ROOT / 'descriptor-composition-1.3.candidate.json').read_text()) ++CATALOG = (ROOT / 'kernel-catalog.fixture.yaml').read_bytes() ++UNRESOLVED_APPS = {'deliver-consumer-secret', 'hand-over'} + EVIDENCE = {'fixture-acceptance', 'fixture-materialization', 'fixture-descriptor', 'fixture-session'} + + +@@ -74,12 +78,13 @@ + ARTIFACT['params']['consumer_owner_repo'], + version='1.3')) + +- def admit(self, desc, type_source, *, actor=None, lane=None, agent_id=None): ++ def admit(self, desc, type_source, *, actor=None, lane=None, agent_id=None, catalog=CATALOG): + desc_bytes = c.encode(desc) + adapter = Adapter(desc.adapter, desc.adapter_source, unused, unused) + executor = Executor(Journal(Path(self.temp.name) / desc.step), + {adapter.reference: adapter}, +- lambda purpose, source, evidence: evidence in EVIDENCE, ++ lambda purpose, source, evidence: (evidence in EVIDENCE and ++ (purpose != 'catalog-routing-acceptance' or source == digest(CATALOG))), + browser_inputs=inputs.BrowserInputs(REPO)) + with mock.patch.dict(os.environ): + if agent_id is None: +@@ -87,7 +92,7 @@ + else: + os.environ['WARDEN_AGENT_ID'] = agent_id + return executor.admit(self.case_source, type_source, desc_bytes, +- b'fixture-only-catalog', principal='founder', actor=actor or desc.actor, ++ catalog, principal='founder', actor=actor or desc.actor, + lane=lane or desc.lane, + acceptance_evidence='fixture-acceptance', + materialization_evidence='fixture-materialization', +@@ -112,18 +117,33 @@ + self.assertEqual(parameter_fingerprint(TYPE_BYTES), inputs.PARAMETER_PINS['1.2']) + acknowledged = with_status('acknowledged') + for step, desc in self.descriptors.items(): ++ if step in UNRESOLVED_APPS: ++ continue # The attended session gate correctly precedes parameter validation. + with self.subTest(step=step), self.assertRaisesRegex( + Refused, 'failed exact-version validation'): + self.admit(desc, acknowledged) + +- def test_committed_map_admits_every_descriptor_once_acknowledged_and_pinned(self): ++ def test_committed_map_conditionally_admits_five_resolvable_steps(self): + acknowledged = with_status('acknowledged') + for step, desc in self.descriptors.items(): ++ if step in UNRESOLVED_APPS: ++ continue + with self.subTest(step=step), pinned_13(): + admitted, invocation, _ = self.admit(desc, acknowledged) + self.assertEqual(admitted, desc) + self.assertEqual((invocation.step, invocation.actor, invocation.target), + (desc.step, desc.actor, 'forgejo')) ++ ++ def test_unresolved_apps_sessions_refused_even_once_acknowledged_and_pinned(self): ++ for step in sorted(UNRESOLVED_APPS): ++ with self.subTest(step=step), pinned_13(), self.assertRaisesRegex( ++ Refused, 'does not resolve to an active, reviewed login lane'): ++ self.admit(self.descriptors[step], with_status('acknowledged')) ++ ++ def test_catalog_evidence_is_bound_to_exact_fixture_bytes(self): ++ mint = self.descriptors['mint-secret'] ++ with pinned_13(), self.assertRaisesRegex(Refused, 'independent owner evidence not verified'): ++ self.admit(mint, with_status('acknowledged'), catalog=CATALOG + b'\n# changed source\n') + + def test_descriptor_outside_the_map_is_refused(self): + acknowledged = with_status('acknowledged') +diff --git a/sso-mfa/k8s/keycape/test_keycape_registration_adapter.py b/sso-mfa/k8s/keycape/test_keycape_registration_adapter.py +--- a/sso-mfa/k8s/keycape/test_keycape_registration_adapter.py ++++ b/sso-mfa/k8s/keycape/test_keycape_registration_adapter.py +@@ -259,8 +259,13 @@ + case_source = yaml.safe_dump(case).encode() + typ = yaml.safe_load((root / 'security-case-types/oidc-browser-relying-party/type.yaml').read_bytes()) + typ['spec']['execution_descriptors'] = {'register-client': digest(self.desc_bytes)} ++ # Fixture-only successor lane for the adapter's current descriptor. ++ # Canonical 1.1 still names the earlier login lane and is not edited. ++ typ['spec']['follow_through'] = [ ++ dict(step, lane=self.admitted.lane) if step['id'] == 'register-client' else step ++ for step in typ['spec']['follow_through']] + type_source = yaml.safe_dump(typ).encode() +- catalog = b'fixture-only-catalog' ++ catalog = (ROOT / 'kernel-catalog.fixture.yaml').read_bytes() + sheet = json.dumps(['register-client', 'net-kingdom', 'forgejo', 'human', + 'founder', 'keycape-attended-case-change', 'fixture-acceptance', 'fixture-materialization', + 'fixture-descriptor', 'fixture-session'], separators=(',', ':')).encode() +@@ -270,6 +275,8 @@ + adapter = a.build(**dict(self.kw, admitted=invocation, case_source=case_source, + case_acceptance='fixture-acceptance')) + def fixture_verifier(purpose, source, evidence): ++ if purpose == 'catalog-routing-acceptance': ++ return source == digest(catalog) and evidence == 'fixture-descriptor' + if purpose.startswith('session-revocation:'): + _, lane, operation, attempt = purpose.split(':') + return source == digest(case_source) and lane == invocation.lane and evidence == ( +diff --git a/sso-mfa/k8s/keycape/kernel-catalog.fixture.yaml b/sso-mfa/k8s/keycape/kernel-catalog.fixture.yaml +new file mode 100644 +--- /dev/null ++++ b/sso-mfa/k8s/keycape/kernel-catalog.fixture.yaml +@@ -0,0 +1,128 @@ ++# TEST ONLY: synthetic subset shaped after ops-warden 5abde817 catalog. ++# Four draft entries are hypothetically active; Apps session_lane remains unresolved. ++# Not an authenticated owner catalog, promotion, login choice or native authority. ++entries: ++- id: key-cape-oidc-login ++ risk: standard ++ owner_repo: key-cape ++ warden_executes: false ++ reviewed: '2026-06-27' ++ status: active ++ delegation: ++ mode: interim ++ intended_owner: key-cape ++ reviewed: '2026-09-08' ++ blocked_on: fixture-only; no native authority ++ lane: login ++ title: Synthetic key-cape-oidc-login ++ need_keywords: ++ - key-cape-oidc-login ++ subsystem: synthetic source test ++ wiki_ref: fixture-only ++ canon_ref: fixture-only ++ workload_ref: ++ applicability: not-applicable ++ reason: Synthetic source test; no workload or native authority. ++- id: keycape-client-secret-generate ++ risk: high ++ owner_repo: railiance-platform ++ warden_executes: false ++ reviewed: '2026-10-10' ++ status: active ++ delegation: ++ mode: native ++ intended_owner: railiance-platform ++ reviewed: '2026-10-10' ++ lane: secret ++ title: Synthetic keycape-client-secret-generate ++ need_keywords: ++ - keycape-client-secret-generate ++ subsystem: synthetic source test ++ wiki_ref: fixture-only ++ canon_ref: fixture-only ++ workload_ref: ++ applicability: not-applicable ++ reason: Synthetic source test; no workload or native authority. ++- id: keycape-attended-case-change ++ lane: attended-change ++ session_lane: key-cape-oidc-login ++ risk: high ++ owner_repo: key-cape/net-kingdom ++ warden_executes: false ++ reviewed: '2026-10-17' ++ status: active ++ delegation: ++ mode: interim ++ intended_owner: key-cape ++ reviewed: '2026-10-17' ++ blocked_on: fixture-only; no native authority ++ title: Synthetic keycape-attended-case-change ++ need_keywords: ++ - keycape-attended-case-change ++ subsystem: synthetic source test ++ wiki_ref: fixture-only ++ canon_ref: fixture-only ++ workload_ref: ++ applicability: not-applicable ++ reason: Synthetic source test; no workload or native authority. ++- id: platform-secret-delivery ++ risk: high ++ owner_repo: railiance-platform ++ warden_executes: false ++ reviewed: '2026-10-17' ++ status: active ++ delegation: ++ mode: native ++ intended_owner: railiance-platform ++ reviewed: '2026-10-17' ++ title: Synthetic platform-secret-delivery ++ need_keywords: ++ - platform-secret-delivery ++ subsystem: synthetic source test ++ wiki_ref: fixture-only ++ canon_ref: fixture-only ++ workload_ref: ++ applicability: not-applicable ++ reason: Synthetic source test; no workload or native authority. ++- id: consumer-owner-deployment ++ lane: attended-change ++ session_lane: pending-owner-confirmation ++ risk: high ++ owner_repo: railiance-apps ++ warden_executes: false ++ reviewed: '2026-10-17' ++ status: active ++ delegation: ++ mode: native ++ intended_owner: railiance-apps ++ reviewed: '2026-10-17' ++ title: Synthetic consumer-owner-deployment ++ need_keywords: ++ - consumer-owner-deployment ++ subsystem: synthetic source test ++ wiki_ref: fixture-only ++ canon_ref: fixture-only ++ workload_ref: ++ applicability: not-applicable ++ reason: Synthetic source test; no workload or native authority. ++- id: apps-consumer-handover ++ lane: attended-change ++ session_lane: pending-owner-confirmation ++ risk: high ++ owner_repo: railiance-apps ++ warden_executes: false ++ reviewed: '2026-10-17' ++ status: active ++ delegation: ++ mode: native ++ intended_owner: railiance-apps ++ reviewed: '2026-10-17' ++ title: Synthetic apps-consumer-handover ++ need_keywords: ++ - apps-consumer-handover ++ subsystem: synthetic source test ++ wiki_ref: fixture-only ++ canon_ref: fixture-only ++ workload_ref: ++ applicability: not-applicable ++ reason: Synthetic source test; no workload or native authority. diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/canonical-validation.json b/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/canonical-validation.json new file mode 100644 index 0000000..dbb901e --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/canonical-validation.json @@ -0,0 +1,15 @@ +{ + "executed": true, + "counts_verified": true, + "returncode": 0, + "warden_revision": "922c26da0f3fcb0182352ea83e6ae06f29878580", + "net_kingdom_head": "342a6f25af5cdc602841948f42759ea52ef1c8f5", + "verified_public_source_files": 105, + "tests": [ + "sso-mfa/k8s/keycape/test_admit_1_3_type.py", + "sso-mfa/k8s/keycape/test_keycape_registration_adapter.py" + ], + "log_sha256": "sha256:2ac155ce37c15bfd4f24483304036fec725bc3f166c0963d56280dea1d43ae73", + "scratch": "/tmp/nk48-catalog-canonical-validation-ntdpunql", + "limitations": "Source fixture tests only; no native owner execution, acceptance, catalog promotion or pin." +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/identifier-assignment.json b/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/identifier-assignment.json new file mode 100644 index 0000000..c3c6731 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/identifier-assignment.json @@ -0,0 +1,20 @@ +{ + "schema": "repo-manager.identifier-assignment.v1", + "ok": true, + "executed": true, + "repo": "net-kingdom", + "namespace": "helixforge", + "assignments": [ + { + "kind": "task", + "record_id": "NK-WP-0048-T35", + "uuid": "cf1d6ab3-846e-53ea-8575-d6347b6f4769", + "path": "workplans/NK-WP-0048-security-case-types.md" + } + ], + "historical_invalid_identifiers": [], + "historical_identity_collisions": [], + "files_changed": [ + "workplans/NK-WP-0048-security-case-types.md" + ] +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/identifier-preview.json b/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/identifier-preview.json new file mode 100644 index 0000000..b0c2357 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/identifier-preview.json @@ -0,0 +1,20 @@ +{ + "schema": "repo-manager.identifier-assignment.v1", + "ok": true, + "executed": false, + "repo": "net-kingdom", + "namespace": "helixforge", + "assignments": [ + { + "kind": "task", + "record_id": "NK-WP-0048-T35", + "uuid": "cf1d6ab3-846e-53ea-8575-d6347b6f4769", + "path": "workplans/NK-WP-0048-security-case-types.md" + } + ], + "historical_invalid_identifiers": [], + "historical_identity_collisions": [], + "files_changed": [ + "workplans/NK-WP-0048-security-case-types.md" + ] +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/preparation.json b/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/preparation.json new file mode 100644 index 0000000..2077087 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/preparation.json @@ -0,0 +1,36 @@ +{ + "schema": "custodian.nk48-catalog-fixture-publication-preparation.v1", + "status": "prepared only; canonical apply and validation runners not executed, including dry-run", + "owner_base": "342a6f25af5cdc602841948f42759ea52ef1c8f5", + "warden_base": "ae7bc73b4f4bac22454144959b870d161498e0af", + "root_source_review": "Root explicitly accepted fixture patch SHA256 df69fb2ec9f1e1cf0dd94194e3c046c995ed8a7deabf65a8e9bec34662adb501 for source integration; no owner/native assent inferred.", + "task_refinement": "Append completed T35 for distinct current authenticated-catalog/mode-aware fixture compatibility; preserve exact original workplan bytes, all34 existing task blocks/frontmatter/status/UUID. Normal allocator preview must yield exactlyT35 before execution; no manual UUID.", + "root_commands": [ + "python3 /home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r1/apply_prepare.py --execute", + "python3 /home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r1/run_validation.py --execute", + "Root normal scoped commit using publication-inputs.allocated.json; normal owner projection afterwards." + ], + "mutation_scope": [ + "Exactly the three reviewed fixture files plus the appended T35 workplan task.", + "No git stage,commit,push,projection,brief refresh or index generation in prepared scripts.", + "Preexisting operator .custodian-brief.md/WORK-RECORDS.md remain byte-identical; unrelated dirty/untracked paths are never read or written by the apply script." + ], + "validation_scope": "Canonical NK test and source files against Warden mainae7bc73;105 explicit public source hashes checked before and after. Two scoped test modules only; expected31 tests/53 subtests; no skip/deselection accepted. EmptyHOME and allowlisted environment; fresh redirected Python caches; pytest cache/conftest discovery disabled. Network denied. Native processes denied except exact pinned offline metadata validator with guarded empty-environment child. Reads from /home or /root denied except exact prehashed public source files; canonical writes denied.", + "unverified_limits": [ + "Prepared runner has only been AST-parsed; root must execute and inspect logs.", + "Python audit controls are bounded to reviewed test execution and do not claim operating-system credential isolation or an autopilot grant.", + "Warden code is source-only; 1.3 parameter pin, draft promotion, pending Apps login, nativeT03 and downstream acceptance remain gated.", + "Existing adverse review history remains unchanged." + ], + "files": { + "apply_prepare.py": "sha256:b294e6339e8c9af9ba911667646dd0ab52e75cf798de93238d84d1057c8a9e70", + "run_validation.py": "sha256:16236aa55a774f8ea6d66da4ee17f7737c6d1b013d844ca9b9759e02b615af51", + "validation-guard.py": "sha256:13611f4de25dc7d01348905c4b365a0ffbb95fd93a61c75c8faf38d705c54f50", + "workplan.preimage.md": "sha256:f92962c5aa21aade0fc2f6b4f8d9a95932fe85b107413cdd2824b6ebe72c36d9", + "publication-inputs.json": "sha256:d9bf5555013aa9e399037672c80a72af23c6815db03088bee3db4c8583197146", + "candidate.patch": "sha256:df69fb2ec9f1e1cf0dd94194e3c046c995ed8a7deabf65a8e9bec34662adb501", + "task-T35.md": "sha256:7d14d261ffbab2a930c4a3ff962cec786060cad6177280773cf1c0b0f006127f", + "validation-child.py": "sha256:6bd4e3cda22e65f90bbb4006b403270405947293be2ae8bf8fdade0abed61f45", + "workplan.candidate.md": "sha256:3bce122c5b83d9044b142fc2cabc48de5558ab0d65dc0223a8ed0325f219e981" + } +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/publication-inputs.allocated.json b/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/publication-inputs.allocated.json new file mode 100644 index 0000000..2a6bfed --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/publication-inputs.allocated.json @@ -0,0 +1,23 @@ +{ + "owner": "net-kingdom", + "base_revision": "342a6f25af5cdc602841948f42759ea52ef1c8f5", + "message": "test: align security case fixtures with authenticated catalog admission", + "files": [ + { + "path": "sso-mfa/k8s/keycape/test_admit_1_3_type.py", + "sha256": "2186eab00b000a534ab1452f14fa7b1a7ed352d5b27c400687c1c8ec7c4c3019" + }, + { + "path": "sso-mfa/k8s/keycape/test_keycape_registration_adapter.py", + "sha256": "86268e082763c388528e5175184fd32362167c3441716436e4b1cab150c3e79a" + }, + { + "path": "sso-mfa/k8s/keycape/kernel-catalog.fixture.yaml", + "sha256": "7885b8c380c03ccb0198e540532ce80c791c8e86698521d7173642e0c7a56e53" + }, + { + "path": "workplans/NK-WP-0048-security-case-types.md", + "sha256": "53f9e5826bd7e34287d59f46089ee50c31598ba1a56bcf6d3249525f7e23bb9b" + } + ] +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/publication-inputs.json b/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/publication-inputs.json new file mode 100644 index 0000000..17da581 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/publication-inputs.json @@ -0,0 +1,176 @@ +{ + "schema": "custodian.nk48-catalog-fixture-publication-inputs.v1", + "prepared_only": true, + "owner": "net-kingdom", + "base_revision": "342a6f25af5cdc602841948f42759ea52ef1c8f5", + "warden_revision": "922c26da0f3fcb0182352ea83e6ae06f29878580", + "message": "test: align security case fixtures with authenticated catalog admission", + "workplan": "workplans/NK-WP-0048-security-case-types.md", + "workplan_preimage_sha256": "sha256:f92962c5aa21aade0fc2f6b4f8d9a95932fe85b107413cdd2824b6ebe72c36d9", + "workplan_candidate_sha256": "sha256:3bce122c5b83d9044b142fc2cabc48de5558ab0d65dc0223a8ed0325f219e981", + "task_addition_sha256": "sha256:7d14d261ffbab2a930c4a3ff962cec786060cad6177280773cf1c0b0f006127f", + "patch_sha256": "sha256:df69fb2ec9f1e1cf0dd94194e3c046c995ed8a7deabf65a8e9bec34662adb501", + "files": [ + { + "path": "sso-mfa/k8s/keycape/test_admit_1_3_type.py", + "preimage_sha256": "sha256:c512ba00513d066fd1ca8af08fd6bf106fbd7a132981b1dcc5af80a745ab03f0", + "sha256": "sha256:2186eab00b000a534ab1452f14fa7b1a7ed352d5b27c400687c1c8ec7c4c3019" + }, + { + "path": "sso-mfa/k8s/keycape/test_keycape_registration_adapter.py", + "preimage_sha256": "sha256:4d0126416522e54eec1ae4c83b5488e09ff11ffb97ec1ee6d234548780c60f63", + "sha256": "sha256:86268e082763c388528e5175184fd32362167c3441716436e4b1cab150c3e79a" + }, + { + "path": "sso-mfa/k8s/keycape/kernel-catalog.fixture.yaml", + "preimage_sha256": null, + "sha256": "sha256:7885b8c380c03ccb0198e540532ce80c791c8e86698521d7173642e0c7a56e53" + } + ], + "allocation": { + "method": "repo_manager.identifiers.ensure_missing_work_record_identifiers", + "manual_uuid": false, + "preexisting_assignments": [], + "expected_new_record_ids": [ + "NK-WP-0048-T35" + ], + "expected_files_changed": [ + "workplans/NK-WP-0048-security-case-types.md" + ], + "expected_task_count": 35, + "concrete_postallocation_manifest": "publication-inputs.allocated.json" + }, + "protected_files": { + ".custodian-brief.md": "sha256:3eb7f91f2443090c9b859a716d83711310364897b6144dcf10d3254342fec3d1", + "WORK-RECORDS.md": "sha256:a1fec217a6e380f528778d781dbea1fc5a836fdefa1dd4dd0308100fbe08112a" + }, + "validation": { + "tests": [ + "sso-mfa/k8s/keycape/test_admit_1_3_type.py", + "sso-mfa/k8s/keycape/test_keycape_registration_adapter.py" + ], + "expected_result": "31 passed,53 subtests passed; no skipped or deselected tests", + "python": "/tmp/warden-kernel-review-r1/final/.venv/bin/python", + "source_hashes": { + "net-kingdom": { + "sso-mfa/k8s/keycape/test_vergabe_client_rollout.py": "sha256:f9b7defc312b0fea24ded92ef479dcc8aef063f7a66a8245760918bcf44c4af1", + "sso-mfa/k8s/keycape/test_compose_1_2_descriptors.py": "sha256:9a4b9fb0141221d6e5bcb72b3656613afda54d16ede300bffa8f71956ae6fd2c", + "sso-mfa/k8s/keycape/test_keycape_client.py": "sha256:9572044679feaedfe732e15a2b6f8ef6b41a8fe2e4430257f309a3e83e97736c", + "sso-mfa/k8s/keycape/openbao-negative-node-wrapper.py": "sha256:76ac013d1a676a0dfa58aea1ce87b0c3e72483ccd21f586a22c5475e9132f6c1", + "sso-mfa/k8s/keycape/portal-client-rollout.py": "sha256:158e1d36eb881cdd0a2eda91b472adea5e8fde5061e00d26dda368580aeb871f", + "sso-mfa/k8s/keycape/test_playwright_activation.py": "sha256:6fc22d091f27a76c32d2fb0a7fc3c6e18e54ff98d242699c518b2f9dc9fd4cbd", + "sso-mfa/k8s/keycape/exercise-approval-clients.py": "sha256:e2d3dd741ac52af28dce752344227a4443b78fedd4cfcc06ba902e679dfc9351", + "sso-mfa/k8s/keycape/keycape-client.py": "sha256:4b63161cf3365d34dd684a3116e2c935baaf7f6aa4b30983f6883546db3d6591", + "sso-mfa/k8s/keycape/test_openbao_negative_session.py": "sha256:01e23f2b4fc77824a161a57acb3a52d19aad846b3b6c0fd3abd81afa1dc2985c", + "sso-mfa/k8s/keycape/authentication-policy-rollout.py": "sha256:2c1bd23fded4855bae1eb3a08dbc4510d7f60f2bc4d625d60e471ab78f6f0251", + "sso-mfa/k8s/keycape/handover-adapter.py": "sha256:bad7c1132258f2140b0d83548031ac86eecea4f4019f34adedc1e9e3774a3832", + "sso-mfa/k8s/keycape/openbao-signin-diagnosis-rollout.py": "sha256:a57b957d6eed9e8fb09d2a7aa7405a39deeafd9aaf57a9e6dc907afabd380371", + "sso-mfa/k8s/keycape/test_handover_adapter.py": "sha256:cd1fb0eb64a777699221ebdfcd6fea96bafb9b27ec388e95dcfe66189eac8ca2", + "sso-mfa/k8s/keycape/test_authentication_policy_rollout.py": "sha256:11c10039d99a34d2819b09146a966c8b7cba6095e7a8b51c055a01e7167e3ed5", + "sso-mfa/k8s/keycape/test_deliver_consumer_secret_adapter.py": "sha256:f4e6c6acd75a36f675007d2f62a7c7c4f7bfe2951cbb5e3e4cdde8b53f17f473", + "sso-mfa/k8s/keycape/bind-env-adapter.py": "sha256:fa17fae21776b5c620ee9782adf050649c3a28719a6687d2524ff67ca7ad1925", + "sso-mfa/k8s/keycape/test_keycape_checker_snapshot.py": "sha256:9271f25c760ad732c257d59d259b5c767436a2dc2d5ba24fca259bc57761d6d1", + "sso-mfa/k8s/keycape/test_openbao_signin_diagnosis_rollout.py": "sha256:25d46fe7caca2ad4c262bd2a25188c91847304b6b051c4f3b3705c69f2803f01", + "sso-mfa/k8s/keycape/test_openbao_private_ready.py": "sha256:8a7a033b1c509ba21397d1d74580c44f24592045b2b9de50fe87638607224277", + "sso-mfa/k8s/keycape/test_mint_secret_adapter.py": "sha256:8110b6a8abd8e9a1f5e1feae11ec09c4e28cb7486c759b0e8459da1fb1b7144d", + "sso-mfa/k8s/keycape/playwright-activation.py": "sha256:5ac683b3324b00e6253f6040f4c73057027b1f98f7071160a6153794ba522757", + "sso-mfa/k8s/keycape/test_admit_1_3_type.py": "sha256:2186eab00b000a534ab1452f14fa7b1a7ed352d5b27c400687c1c8ec7c4c3019", + "sso-mfa/k8s/keycape/test_openbao_node_route.py": "sha256:1c57053be6e8472174e4034fdfbf34b3e1729f5e239b3a9af89d9e59a08d6cb6", + "sso-mfa/k8s/keycape/test_secrets_engine_openbao_client_rollout.py": "sha256:e6f86fb7712c1b9c01d9b684f26d5cc949649b823d2732eced132d59acd3fe01", + "sso-mfa/k8s/keycape/openbao-client-config.py": "sha256:1b908b4c36134212d78f9fbe7a2a201eb3ced06d31bb1dcea4a59d80689d84b1", + "sso-mfa/k8s/keycape/test_registration_original_intent_reconcile.py": "sha256:e2897b8200ec97445fc4a26c55ad82651fcb1a4d22efe7fe59d0fcfc5cd8ec71", + "sso-mfa/k8s/keycape/test_ensure_groups_adapter.py": "sha256:c8754a2ba735116edc6fe257aa993d0530cea9ca9f954ea5a621e38694906be2", + "sso-mfa/k8s/keycape/test_keycape_checker_environment.py": "sha256:35f316eed168d3df588498faf24ab9377733a7d857ddfa5e0e4322a6287c93ff", + "sso-mfa/k8s/keycape/test_bind_env_adapter.py": "sha256:a1e5e7a631a4843f9a0d2310a3942b26936103d786aeae47a142ff7c7b2fdbdb", + "sso-mfa/k8s/keycape/create-secrets.py": "sha256:5da672fff36eb94101741ad99e2a1a96ae81fea15ed1871304c40e63ce27c787", + "sso-mfa/k8s/keycape/approval-clients-rollout.py": "sha256:488d5cc59e081993dd2b5fc6d9e46bbddcaec4f5170b8f7a5ad0aae3a211a790", + "sso-mfa/k8s/keycape/vergabe-client-rollout.py": "sha256:8cee6cc5ff3e3e8617304055263d04b393d0e005c34d9af12cb7e8bc685bac1a", + "sso-mfa/k8s/keycape/keycape-registration-adapter.py": "sha256:a53beea4dbbe373ce7a5b84d55e79be8407ab9688037c7ee057afcc7be576047", + "sso-mfa/k8s/keycape/compose-1.2-descriptors.py": "sha256:39021cac900b11a59d00c5a1d5491b1ee4c639955cde07d07854c3db8bc04958", + "sso-mfa/k8s/keycape/test_approval_clients_rollout.py": "sha256:745316d42c3aba6b59b562d3bbcd4b2cb92871fb5e51a954e2254e2e550bb408", + "sso-mfa/k8s/keycape/register-user-engine-portal.py": "sha256:67173edc44f5457e7b6a3e0fb2321b398159cfce5dea8ead01eabf835fb38d76", + "sso-mfa/k8s/keycape/test_issuer_pin.py": "sha256:e471ed815af246633c0b21ac2cf5cae94d0dbdeff9e6ce801b4ddd033097dc20", + "sso-mfa/k8s/keycape/openbao-negative-session.py": "sha256:24b7b803e4eb7e6e7c0700a069dad0ec315ee7eb5538a88b9e56147f8fab5cdd", + "sso-mfa/k8s/keycape/test_keycape_registration_adapter.py": "sha256:86268e082763c388528e5175184fd32362167c3441716436e4b1cab150c3e79a", + "sso-mfa/k8s/keycape/deliver-consumer-secret-adapter.py": "sha256:65db5386cdf08a1aa932886c046abed20330a36c96e63d4267adb8341b23da42", + "sso-mfa/k8s/keycape/test_portal_client_rollout.py": "sha256:a16cd4bf278970c10de2e1b1025597b3872c9515713dd947ac6b50cb78feea1f", + "sso-mfa/k8s/keycape/test_deliver_secret_adapter.py": "sha256:24cedec29071b62fc2c2458950ed244feeff542bc20a7d480667dd99cda16b03", + "sso-mfa/k8s/keycape/deliver-secret-adapter.py": "sha256:cd1a515a1ae2def2093660ed2a810a9c66d1d4cf7126f573ee7e9c4c42afb98d", + "sso-mfa/k8s/keycape/test_openbao_private_input.py": "sha256:84277edb5bd7858885173dea802b6bdc4f0f0c7785d6e200ee9a98d8553891a1", + "sso-mfa/k8s/keycape/register-coulomb-social.py": "sha256:b40ff4bdba71b797f8f393e79cb3c9d73b790045a0f7df6ecea35803369793c7", + "sso-mfa/k8s/keycape/mint-secret-adapter.py": "sha256:246323c7243ded18229ab063956577e151d56d9be485d3bd44fc38e5af0edf3c", + "sso-mfa/k8s/keycape/test_adapter_source_closure.py": "sha256:63cb442636d996fcf1c36b7b746ed860106a7da35dbaeb1c5129a41420ca4155", + "sso-mfa/k8s/keycape/secrets-engine-openbao-client-rollout.py": "sha256:8b834e5f0353a5e11797637420609e06e7bf7de47777c7e78b774f74d4a6afbd", + "sso-mfa/k8s/keycape/test_openbao_client_config.py": "sha256:7104a73fb936aca4829ef8046e39497d0b7347d3e74810f672e4d4ddc3346457", + "sso-mfa/k8s/keycape/ensure-groups-adapter.py": "sha256:6cba5b07a9262a8e0bf445dc4095ba03b31ddc15e2efbfe374e93f4f78b6aed4", + "sso-mfa/k8s/keycape/verify-user-engine-portal.py": "sha256:ccd5106395bd5d7b4f3dbbcbad449e3793aff217d4429b5d5af9e52ab9a58103", + "sso-mfa/k8s/keycape/test_playwright_registration.py": "sha256:b97ea8619ac768f520e4058aeb50b0b1e8ef6d2e9a1a0fff932a151a3d4b61ae", + "sso-mfa/k8s/keycape/playwright-registration.py": "sha256:bf2d6b83d93629df8ddaf6c31d6c7408ae3866969d63895714df0e08a570da99", + "identity-provisioner/group_provenance_postgres.py": "sha256:2a0111e83ad7b611076a1b45634e7f698b3d2e9acc473320b4a8ba009ebd0932", + "identity-provisioner/password_setup.py": "sha256:53012fc708384fc9cd0fff4f3fa5f204ab85866db91d726457dd130799e88ca0", + "identity-provisioner/provisioner.py": "sha256:19d1b837cbafeeacb1aa815856cd68091e6702281d72b1d860230fa320e1725b", + "identity-provisioner/copy-client-secret.py": "sha256:0f1907a9f16688148aede9fceb53a55cfd0504bcb8552f158d0fa77e9ad960c8", + "identity-provisioner/group_provenance.py": "sha256:405e08eaaab7c21d618dff4db5970607806f32ab6c461effb81c737e32182b10", + "identity-provisioner/server.py": "sha256:b61c83da6c7ce20fa2790d5bb00aeca6a91dccd133526bc559ed26562f90eb29", + "security-case-types/oidc-browser-relying-party/type.yaml": "sha256:f705c64e4875e9b8e18a912fb51c135e77898926d6a451f5cb74ab3c0cbe4d1c", + "security-case-type-revisions/1.2/oidc-browser-relying-party/type.yaml": "sha256:9e60ff569fb0d860a2bbde7bbb0991a54d4b0688f62b56df0fa5d161fd624578", + "security-case-type-revisions/1.3/oidc-browser-relying-party/type.yaml": "sha256:e75fe94110ef9fb7fb18072fe2856941b33507c99cb406d36576046481485d0a", + "security-case-type-revisions/1.3/oidc-browser-relying-party/examples/forgejo.yaml": "sha256:546aad5cf26842c484fc6ba99940d56697b85531afea11872556761008923c94", + "security-cases/forgejo-netkingdom-oidc.yaml": "sha256:bcc2c6d5bae0d28242b3cdb71e74d8ca297c2afe04766b55d53597f86893351d", + "sso-mfa/k8s/keycape/descriptor-composition-1.3.candidate.json": "sha256:773daa1df72cc7cf6a6d8aa082208181ca2438cdecc9179b1bc5b1c3a09099c2", + "sso-mfa/k8s/keycape/kernel-catalog.fixture.yaml": "sha256:7885b8c380c03ccb0198e540532ce80c791c8e86698521d7173642e0c7a56e53", + "tools/security-case-types/security_case_evidence.py": "sha256:da8efa59c52ad73283397ebe639ae259d0cb9c6348b5cb829135c24230dcdc75", + "tools/security-case-types/security_case_validator.py": "sha256:1bcc62e9bc374c711363295549f3cd5bb0b32952671ed4b08fddc7f74742959b", + "tools/security-scenario-composer/security_scenario_composer.py": "sha256:9f0cc69930aa8ee2cc3fb92236e6a99728df17b8c0fe36e1250241c38fc01c4f", + "tools/playbook-capability-contract/playbook_contract_validator.py": "sha256:2e1e0c809d4bc9d64c2d974cc23fecd1c5636785154c2554edd9191852fd926d" + }, + "ops-warden": { + "src/warden/ca.py": "sha256:d5282819c5d29386fd59a7dc46d1abdc01e99fc6838a1a6637f90868389fe557", + "src/warden/inventory.py": "sha256:9401db92d7cbf3bea962b97e83ef2c252a19c69ab94ce4836ba58f68638cb311", + "src/warden/memory.py": "sha256:ecf5f7ccabb669b56bfca3a109b10bbe8437bdd2ea30a47a4c08570f97d7be2a", + "src/warden/case_inputs.py": "sha256:1334b0cd9897e763cfea2d306e997c659f3f284ecc3f9612140c2104835da7c7", + "src/warden/audit.py": "sha256:6b5b8fe053064d3905294bfde1b691fcca8a0806c889e52a49494470d29c24a0", + "src/warden/case_type.py": "sha256:7b659b8d682863cd28df24025be4b10dd7ca3df724350a22d360c6af9615bca7", + "src/warden/vault.py": "sha256:860b8c9f498b3955b363a74dd74087b6d0c399599ef22b951a076ee33c95929f", + "src/warden/taint.py": "sha256:8c3d161d18d429d026aad715ac01e2d33e0f4e84ac03fbaea3cea15017ad39a1", + "src/warden/case_run.py": "sha256:1bc73e30ac718fd75bbaff2a739495fc051aeaec73802ac71f0583cb2d53ab0a", + "src/warden/vault_hints.py": "sha256:0d158ffcc3a93d5442df5fbe13d4c1e3b3159dd60301530bbdae6db9bc9c709e", + "src/warden/access.py": "sha256:258d45d2f84b54b2464373ad4d7d6df6ff95b8e8d60f5e85ceb2d962fcfa8a61", + "src/warden/clock_attribution.py": "sha256:5fcf07a2525a6a250442e14b6f6229f7db2a6ca02fc6aac23cdaa4c1a78c63eb", + "src/warden/case_plan.py": "sha256:9352ae8683d1181980c4ff38524582e4be448d33c85b558f60582c1e5bb5bbaa", + "src/warden/caller_identity.py": "sha256:747edfaceecd470981949253bda0c3bd2f487d40a9a2f77c2cc1eff3f34f04f7", + "src/warden/policy.py": "sha256:aac46f538e986731cd841581c91efe004b8c1940966b954ef635780a719dada4", + "src/warden/case_receipt_state.py": "sha256:a2cda7f187e77bd76d4d209505638de44a587fae0bc033ec75c23ee9ea9f73bc", + "src/warden/config.py": "sha256:8a857faa3811b00303d4d5c904d69255ebdaff3239e8516f97e6606e53b46924", + "src/warden/openbao_caller.py": "sha256:7e6d07827d00eae7de3b2367c97bdcda1a13d6559627201a4ba09a0e42d46a17", + "src/warden/doubles.py": "sha256:5460e07f5bd0f51a81d3f3c0ac84efde65ca9130b1039f94ee22ca484976a9de", + "src/warden/posture.py": "sha256:1bb60c4dd4634ea42cb130ae1325d7415adf43096906bde2515396c368256e33", + "src/warden/cli.py": "sha256:196cd829d3dbfbd2333598a983687bde1f9bcdfadef95b2fd1d237c4a5f060a9", + "src/warden/proxy.py": "sha256:69649bcf8979e4ed7f9104e927b458827a355c4846143367f4cd49c8b89b96da", + "src/warden/plan.py": "sha256:001b3197e644221344d52e6b1594eb8eae78d10e73d18ed31273505909f3916b", + "src/warden/worker.py": "sha256:d7c2b391b36033d44d8c6841b9bce18a05c74b41ef2910b91689820b6a1cb44e", + "src/warden/mask.py": "sha256:0876412d453fa84109dc8ee0468449f40cf4d3421b056f30fc205f0f59ec494b", + "src/warden/models.py": "sha256:f4100b02c5d96d537980e00d4b874159cd5822ba8219f1f533a2516912b7bd85", + "src/warden/__init__.py": "sha256:047e0feba479474235f0707a207655ecfddfb33cd4f54069134419b8ca4ea24a", + "src/warden/scorecard.py": "sha256:0fa5aef34aeb004fa7fcfabc287759cc47421ca5244c1f31fc467b22a653353c", + "src/warden/desk.py": "sha256:2499120392a5a1c0fd8f423d5b5c787c75a4efb9a986a1b7bcb85eae13423e85", + "src/warden/routing/scaffold.py": "sha256:5079c8e51f8d487eae3f322a66324bd70cefb637bb28f46c861032de8ec54aa2", + "src/warden/routing/models.py": "sha256:b85372b2572a49af62e9534f26a33963b894633c7cd43dd2a28416b8959cba3e", + "src/warden/routing/__init__.py": "sha256:e9124811ebe11ad32c98ab462b2eed634d4ddca08792981d0f353b223a239902", + "src/warden/routing/catalog.py": "sha256:5b95810df9e6102a33d3a116a096cd75292c8572bad721fe6baa772caacc9bc3", + "src/warden/scripts/ops_ssh_wrapper.py": "sha256:e8c289882f2ad439d2eb32f9a871967420fefaffe745c9271c1a2e948fbdfe50", + "src/warden/scripts/__init__.py": "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + } + }, + "repo_manager_identifiers_sha256": "sha256:4e35fcd009c20822574fd33171176bd14a1ff2dd5af8b0bb2820954974593a2a", + "authorization": "Root accepted the exact source fixture patch for source integration. This packet adds no owner/native authority or catalog/pin promotion. No prepared script has been executed.", + "publishable": false, + "requires_allocated_manifest": true, + "publication_file_paths": [ + "sso-mfa/k8s/keycape/test_admit_1_3_type.py", + "sso-mfa/k8s/keycape/test_keycape_registration_adapter.py", + "sso-mfa/k8s/keycape/kernel-catalog.fixture.yaml", + "workplans/NK-WP-0048-security-case-types.md" + ] +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/revision-refresh.json b/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/revision-refresh.json new file mode 100644 index 0000000..2e0c4cd --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/revision-refresh.json @@ -0,0 +1,8 @@ +{ + "original_packet": "docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r1", + "reason": "Normal Warden allocator committed one previously missing historical UUID; no code/source fixture changes. Original apply stopped before any writes.", + "previous_warden_head": "ae7bc73b4f4bac22454144959b870d161498e0af", + "current_warden_head": "922c26da0f3fcb0182352ea83e6ae06f29878580", + "only_head_delta_path": "workplans/WARDEN-WP-0038-plan-mutation-intent.md", + "all_validation_source_hashes_unchanged": true +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/run_validation.py b/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/run_validation.py new file mode 100644 index 0000000..12f783d --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/run_validation.py @@ -0,0 +1,73 @@ +"""Prepared canonical source validation; --execute launches only the two scoped tests.""" +from pathlib import Path +import argparse +import hashlib +import json +import re +import shutil +import subprocess +import tempfile + +PACKET = Path(__file__).resolve().parent +ROOTS = {'net-kingdom': Path('/home/worsch/net-kingdom'), 'ops-warden': Path('/home/worsch/ops-warden')} + +def sha(data): + return 'sha256:' + hashlib.sha256(data).hexdigest() + +def sources(manifest): + allowed = [] + for owner, rows in manifest['validation']['source_hashes'].items(): + for relative, expected in rows.items(): + path = ROOTS[owner] / relative + assert not path.is_symlink(), str(path) + assert sha(path.read_bytes()) == expected, str(path) + allowed.append(str(path.resolve())) + return allowed + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--execute', action='store_true') + args = parser.parse_args() + manifest = json.loads((PACKET / 'publication-inputs.json').read_text()) + allowed = sources(manifest) + revision = subprocess.check_output(['git', '-C', str(ROOTS['ops-warden']), 'rev-parse', 'HEAD'], text=True).strip() + assert revision == manifest['warden_revision'] + if not args.execute: + print(json.dumps({'prepared_only': True, 'public_source_files_verified': len(allowed), + 'tests': manifest['validation']['tests']})) + return + protected = {name: sha((ROOTS['net-kingdom'] / name).read_bytes()) for name in manifest['protected_files']} + scratch = Path(tempfile.mkdtemp(prefix='nk48-catalog-canonical-validation-')) + for name in ['home', 'tmp', 'guard', 'pycache']: + (scratch / name).mkdir() + shutil.copyfile(PACKET / 'validation-guard.py', scratch / 'guard/sitecustomize.py') + shutil.copyfile(PACKET / 'validation-child.py', scratch / 'child.py') + (scratch / 'allowed.json').write_text(json.dumps(allowed)) + (scratch / 'pytest.ini').write_text('[pytest]\n') + environment = { + 'HOME': str(scratch / 'home'), 'TMPDIR': str(scratch / 'tmp'), 'PATH': '/usr/bin:/bin', + 'PYTHONPATH': str(scratch / 'guard') + ':' + str(ROOTS['ops-warden'] / 'src'), + 'PYTHONDONTWRITEBYTECODE': '1', 'PYTHONPYCACHEPREFIX': str(scratch / 'pycache'), + 'LANG': 'C.UTF-8', 'WARDEN_REVIEW_ALLOWED_SOURCE_LIST': str(scratch / 'allowed.json'), + 'WARDEN_REVIEW_VALIDATOR_PREFIX': str(scratch / 'tmp/warden-case-validator-'), + } + with (PACKET / 'canonical-validation.log').open('w') as log: + result = subprocess.run([manifest['validation']['python'], str(scratch / 'child.py')], + cwd=ROOTS['net-kingdom'], env=environment, + stdout=log, stderr=subprocess.STDOUT) + sources(manifest) + assert protected == {name: sha((ROOTS['net-kingdom'] / name).read_bytes()) for name in manifest['protected_files']} + log_text = (PACKET / 'canonical-validation.log').read_text() + counts_verified = bool(re.search(r'\b31 passed, 53 subtests passed\b', log_text)) + counts_verified = counts_verified and not re.search(r'\b[1-9][0-9]* (?:skipped|deselected)\b', log_text) + receipt = {'executed': True, 'counts_verified': counts_verified, 'returncode': result.returncode, 'warden_revision': revision, + 'net_kingdom_head': subprocess.check_output(['git', '-C', str(ROOTS['net-kingdom']), 'rev-parse', 'HEAD'], text=True).strip(), + 'verified_public_source_files': len(allowed), 'tests': manifest['validation']['tests'], + 'log_sha256': sha((PACKET / 'canonical-validation.log').read_bytes()), 'scratch': str(scratch), + 'limitations': 'Source fixture tests only; no native owner execution, acceptance, catalog promotion or pin.'} + (PACKET / 'canonical-validation.json').write_text(json.dumps(receipt, indent=2) + '\n') + print(json.dumps(receipt, indent=2)) + raise SystemExit(result.returncode or (0 if counts_verified else 1)) + +if __name__ == '__main__': + main() diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/task-T35.md b/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/task-T35.md new file mode 100644 index 0000000..78735f2 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/task-T35.md @@ -0,0 +1,38 @@ + +## T35 — Align source admission fixtures with the authenticated catalog and attended-session API + +```task +id: NK-WP-0048-T35 +status: done +priority: high +``` + +Completed 2026-10-17, source fixture compatibility only. Distinct from T32's +independent agent-mint regression repair: both remaining opaque catalog byte +placeholders now use one well-formed synthetic catalog whose exact bytes are +bound by the fixture evidence verifier. The fixture mirrors the six relevant +Warden lane shapes; four draft statuses are hypothetically active, while both +Apps establishing login references remain unresolved. No real catalog entry, +owner login choice or production parameter pin changes. + +Against the reviewed mode-aware Warden source now integrated at +`ae7bc73b4f4bac22454144959b870d161498e0af`, scratch validation passes 31 tests +and 53 subtests. Five resolvable 1.3 descriptors conditionally admit under +fixture-only acknowledgment and pinning; both Apps attended steps refuse before +execution. Changed catalog bytes refuse, all independent mint-guard tests still +reach their intended guard, and the registration fixture's actual kernel +run/rollback and uncertain-result paths pass with synthetic owner callbacks. +The registration fixture explicitly aligns its in-memory proposed type lane +with its current descriptor; canonical 1.1 is unchanged. + +The same tests against predecessor `117fec9241e7b985dfb187b992e173d0a5dc3700` +pass 31 tests and 51 subtests, with only the two new Apps refusal assertions +failing because that predecessor lacks the attended-session catalog check. +Tests target the reviewed current API; the parser or Apps gate is not weakened +for predecessor compatibility. Evidence and root source review: +`the-custodian/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-update-r1/` +and `nk48-catalog-fixture-publication-r1/`. Canonical validation/publication +receipts belong in the latter packet when performed. This records source +fixture work, not native T03, owner assent, exact-type acknowledgment, PIN-1, +composition/admission completion under T16 or the native first case under T06. +T33 and T34 retain their existing issuer-store and consumer-inverse work. diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/validation-child.py b/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/validation-child.py new file mode 100644 index 0000000..7e659ae --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/validation-child.py @@ -0,0 +1,43 @@ +"""Focused canonical-source test process. Network/native/estate-read guard required.""" +from pathlib import Path +import os +import socket +import subprocess +import sys +import sitecustomize + +assert sitecustomize.ACTIVE is True +assert not {'BAO_TOKEN', 'VAULT_TOKEN', 'WARDEN_AGENT_ID'} & os.environ.keys() +assert list(Path.home().iterdir()) == [] +try: + socket.socket() +except RuntimeError as error: + assert str(error) == 'fixture validation: network disabled' +else: + raise AssertionError('network guard inactive') + +import warden.case_inputs as inputs +import pytest + +assert Path(inputs.__file__).resolve() == Path('/home/worsch/ops-warden/src/warden/case_inputs.py') +PREAMBLE = '''import sys as _review_sys\ndef _review_guard(event,args):\n if event.startswith('socket.') or event in {'subprocess.Popen','os.system','os.exec','os.posix_spawn'}:\n raise RuntimeError('review validator: network/native disabled')\n if event=='open' and isinstance(args[0],str) and (args[0].startswith('/home/') or args[0].startswith('/root/')):\n raise RuntimeError('review validator: ambient home denied')\n_review_sys.addaudithook(_review_guard)\n''' +old_run = subprocess.run + +def run_pinned(arguments, *positional, **keywords): + if (type(arguments) is not list or len(arguments) != 5 + or arguments[:3] != [sys.executable, '-I', '-c'] + or arguments[3] != inputs._VALIDATE + or not str(arguments[4]).startswith(sitecustomize.VALIDATOR_PREFIX) + or keywords.get('env') != {}): + raise RuntimeError('fixture validation: unexpected process refused') + return old_run([*arguments[:3], PREAMBLE + arguments[3], arguments[4]], + *positional, **keywords) + +inputs.subprocess.run = run_pinned +raise SystemExit(pytest.main([ + '-q', '-p', 'no:cacheprovider', '--noconftest', + '-c', str(Path(os.environ['TMPDIR']).parent / 'pytest.ini'), + '--rootdir=/home/worsch/net-kingdom', + '/home/worsch/net-kingdom/sso-mfa/k8s/keycape/test_admit_1_3_type.py', + '/home/worsch/net-kingdom/sso-mfa/k8s/keycape/test_keycape_registration_adapter.py', +])) diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/validation-guard.py b/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/validation-guard.py new file mode 100644 index 0000000..05f7294 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/validation-guard.py @@ -0,0 +1,35 @@ +"""Copied as sitecustomize.py into a fresh scratch directory by run_validation.""" +import json +import os +from pathlib import Path +import sys + +ALLOWED = frozenset(json.loads(Path(os.environ['WARDEN_REVIEW_ALLOWED_SOURCE_LIST']).read_text())) +VALIDATOR_PREFIX = os.environ['WARDEN_REVIEW_VALIDATOR_PREFIX'] + +def guard(event, args): + if event.startswith('socket.'): + raise RuntimeError('fixture validation: network disabled') + if event == 'subprocess.Popen': + command = args[1] + if (type(command) is list and len(command) == 5 + and command[:3] == [sys.executable, '-I', '-c'] + and command[3].startswith('import sys as _review_sys\ndef _review_guard(') + and str(command[4]).startswith(VALIDATOR_PREFIX) and args[3] == {}): + return + raise RuntimeError('fixture validation: native process disabled') + if event in {'os.system', 'os.exec', 'os.posix_spawn'}: + raise RuntimeError('fixture validation: native process disabled') + if event == 'open' and isinstance(args[0], str): + path = str(Path(args[0]).absolute()) + if path.startswith(('/home/', '/root/')): + # Only exact prehashed public source may be read from the estate. + if str(Path(path).resolve()) not in ALLOWED: + raise PermissionError('fixture validation: unlisted estate file disabled') + mode, flags = args[1:3] + if ((isinstance(mode, str) and any(c in mode for c in 'wax+')) + or (flags & (os.O_WRONLY | os.O_RDWR | os.O_CREAT | os.O_TRUNC | os.O_APPEND))): + raise PermissionError('fixture validation: canonical source writes disabled') + +sys.addaudithook(guard) +ACTIVE = True diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/workplan.allocated.md b/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/workplan.allocated.md new file mode 100644 index 0000000..4589b2b --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/workplan.allocated.md @@ -0,0 +1,1648 @@ +--- +id: NK-WP-0048 +type: workplan +title: "Establish SecurityCaseTypes so that known security cases are light" +domain: infotech +repo: net-kingdom +status: active +flavor: implementation +owner: claude-code +topic_slug: netkingdom +created: "2026-10-08" +updated: "2026-10-17" +depends_on: [] +related_repos: + - ops-mason + - ops-warden + - key-cape + - railiance-platform + - informed-decision +state_hub_workstream_id: "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" +--- + +# NK-WP-0048 - Establish SecurityCaseTypes so that known security cases are light + +## Why + +The founder's direction (2026-10-08): setting up a security integration of a +kind that has been done before should be easy, meaning accept the case, then +ops-mason arranges it and ops-warden follows through. Setting up a new kind +needs ample documentation and tests and an informed-decision, because its shape +is part of the security architecture. + +Today every integration is hand-built: per-client helper scripts, per-lane +custody, founder checklists. The trigger was KEY-WP-0038 (Forgejo as an OIDC +client), whose next step was going to be yet another hand-written rollout +script. + +This plan continues the pattern work of NK-WP-0008 (pattern infospace), +NK-WP-0009 (tutorials) and NK-WP-0030 (scenario composition). It turns patterns +into **SecurityCaseTypes** that ops-mason and ops-warden can execute. Canon: +`canon/standards/security-case-types_v0.1.md` (proposed). + +Companion plans: ops-mason `MASON-WP-0010` (materialization) and ops-warden +`WARDEN-WP-0044` (follow-through). + +## Tasks + +```task +id: NK-WP-0048-T01 +status: done +priority: high +state_hub_task_id: "930ac3e9-20ec-5473-98cd-3fe8fddb730f" +``` + +### T01 — Accept the terminology and the canon + +Founder review of `canon/standards/security-case-types_v0.1.md`, including its +section 10 open questions. On acceptance the canon status goes from `proposed` +to `accepted`. + +2026-10-08: accepted by the founder (in session). Canon status `accepted`; +section 10's open questions are carried forward to the first type (T04, T05). + +```task +id: NK-WP-0048-T02 +status: done +priority: high +state_hub_task_id: "19c25d96-a55c-58f0-9168-3fdbc4955fc2" +``` + +### T02 — Inventory the de-facto types already built by hand + +Read-only. List every security integration built so far and group them by shape. +Known starting points: + +- browser OIDC relying parties on key-cape: informed-decision-approver, + coulomb-social, user-engine portal, openbao-admin, and Forgejo (pending) +- confidential service clients with `client_credentials`: secrets-engine + approval, requester and openbao clients, approval-engine operator +- workload KV read lanes on OpenBao: the railiance-platform credential lane + designs and CCRs, for example `railiance-backup-offsite-lane` +- short-lived SSH certificates through ops-warden (`warden sign`) +- tunnelled operator access (ops-bridge) + +For each: the repositories involved, the hand-built steps, attended human steps, +custody paths, verification evidence, and rollback (if any). Output +`docs/security-case-type-inventory.md`, ranking candidate types by how often the +shape recurs and how much manual work each instance costs today. + +2026-10-08: done, `docs/security-case-type-inventory.md`. 36 of 37 CCRs are +already `request_type: workload-kv-read`, a de-facto type reviewed each time as +if it were new. Browser clients: 5 hand-built plus Forgejo, each with its own +rollout script. Confidential service clients need a registration plus two or +three KV-read CCRs each. A forge read identity is needed in bulk by +ORGREF-WP-0002-T06. Recommendation: oidc-browser-relying-party first (unblocks +the forge), workload-kv-read second (largest payoff), then forge-read-identity. + +```task +id: NK-WP-0048-T03 +status: done +depends_on: [NK-WP-0048-T01] +priority: high +state_hub_task_id: "e487975b-73b1-516c-971d-da4cbbb93063" +``` + +### T03 — Schema, template and validator + +- `canon/schemas/security-case-type_v0.1.schema.json` and + `security-case_v0.1.schema.json` +- `security-case-types/_template/` with `type.yaml`, `README.md` and tests to copy +- a validator, extending `tools/security-scenario-composer/`, that checks a type, + checks a case against its type, and renders the case into the composer's + deterministic plan (still `execution.permitted: false`) + +Acceptance: the validator rejects missing sections, secret-shaped parameter +values, unpinned providers and cases against non-`implemented` types, with tests +for each. + +2026-10-08: done. `canon/schemas/security-case-type_v0.1.schema.json` and +`security-case_v0.1.schema.json`; `tools/security-case-types/security_case_validator.py` +(`validate-type`, `validate-case`, `render`, reusing the scenario composer); +`security-case-types/_template/` (a valid, runnable example type over +`net-kingdom.local-identity`, with README sections, an example case and tests that +travel with every copy). 31 tests pass, including the composer's existing suite. + +```task +id: NK-WP-0048-T04 +status: done +depends_on: [NK-WP-0048-T02, NK-WP-0048-T03] +priority: high +state_hub_task_id: "2fffe112-7d55-5680-9952-78d74f44f87f" +``` + +### T04 — Define the first type: browser OIDC relying party + +`security-case-types/oidc-browser-relying-party/`, version 1.0: + +- parameters: client id, display name, exact redirect URIs, scopes, client type + (confidential or public), MFA pin, the groups the consuming service maps, + custody path for the secret (confidential only), consuming service and its + owner +- composition: key-cape client registration (key-cape declares it as a playbook + capability), secret minting into custody (railiance-platform), LLDAP group + existence and membership (net-kingdom identity-provisioner), handover to the + consuming service +- materialization (ops-mason) and follow-through (ops-warden) sections +- verification: discovery lists the redirect URI; a test sign-in returns the + expected claims for one human; anonymous access to the custody path is denied +- rollback: remove exactly this client and its custody path +- README: threat model, MFA rationale, why redirect URIs are exact, which steps + still need an attended login and how that is minimised +- reference instances: informed-decision-approver, coulomb-social, user-engine + portal, openbao-admin + +Acceptance: complete per canon section 3, tests green, status `proposed`. + +2026-10-08: done. `security-case-types/oidc-browser-relying-party/` 1.0 (proposed): +`type.yaml` with 12 parameters (the custody reference is required only for +confidential clients), composition over key-cape's new +`key-cape.oidc-client-registration` declaration (contract PASS, `maturity: +planned`), materialization and follow-through, verification and rollback, all +conditional on client type where it matters. One attended step, +`register-client`, justified by the live registry being a Kubernetes Secret. +README with threat model, alternatives, an honest fit table (portal and vergabe +fit; Forgejo is the first confidential case; openbao-admin, coulomb.social and +informed-decision-approver do not fit 1.0), and known limits. Examples: Forgejo +(checked against the accepted request) and a public portal. The validator gained +`when` / `required_when` conditions and provider discovery. 43 tests pass. +ops-mason's and ops-warden's sections are drafted in the type. Their owners +review them in MASON-WP-0010-T01 and WARDEN-WP-0044-T01 before acknowledgment +(T05). + +```task +id: NK-WP-0048-T05 +status: done +depends_on: [NK-WP-0048-T04, MASON-WP-0010-T01, WARDEN-WP-0044-T01] +priority: high +state_hub_task_id: "3d987d3d-0797-538b-86e4-51185a327ae2" +``` + +### T05 — Acknowledge the type through informed-decision + +Prepare the Decision Memo from the type's README and `type.yaml` and submit it +through informed-decision's production intake. On acknowledgment the type +moves to `acknowledged`. + +2026-10-08: Decision Memo prepared. Hub decision `e418263e-a71f-4f01-a970-c87c9e187c09`. +`security-case-types/oidc-browser-relying-party/decision/`: `packet.md` (question, +one act, what acknowledging does not do, the shape, alternatives, attended +steps, limits, evidence), `build_intake.py` and the built `intake.json` (memo +class `decision-record`, review group `net-kingdom-admins`, resolves the hub +decision on answer). The envelope passes informed-decision's own +`validate_submission`. Queuing it is an attended operator step, a write inside +the production dispatcher pod: + + kubectl -n state-hub exec -i deploy/state-hub-memo-dispatch -- \ + python -m api.services.memo_dispatch --queue /data/private enqueue --envelope /dev/stdin \ + < security-case-types/oidc-browser-relying-party/decision/intake.json + +The answer then appears for review in informed-decision. + +2026-10-08: queued by the founder (`pending`, `queued`) after one refusal: the +first envelope's idempotency key was not State Hub's stable +`state-hub..` form, and the builder now validates against both +sides. The dispatcher delivered it to informed-decision (ledger 7 → 8 +delivered). T05 waits for the founder's answer in informed-decision, which +resolves hub decision `e418263e`. + +2026-10-08: ops-mason's review (MASON-WP-0010-T01) folded into the type, still +1.0 `proposed`: `secret_ref` is no longer a parameter, the type derives +`platform/workloads/keycape/{client_id}`; policies are +`workload-kv-read-keycape-{client_id}-eso` and +`workload-kv-read-keycape-{client_id}-{consuming_service}`, each with a templated +body (`read` on that one data path); new `openbao_role` +`external-secrets-keycape-{client_id}` (Kubernetes auth, ESO service account, +15m, no default policy); catalog entry `{consuming_service}-keycape-client`; +the two rollback steps on the KV path collapse into one metadata delete. The +schema and validator now require a `body` on policy and role items. 105 tests +pass. Two consequences: the delivered memo describes the type before these +changes (a case-set custody reference, two differently named policies, no ESO +role), so the founder answers on a superseded shape unless the memo is +re-sent; and Forgejo's custody path moves from the accepted request's proposed +`platform/workloads/forgejo/netkingdom-oidc-client` to +`platform/workloads/keycape/forgejo`, which key-cape (KEY-WP-0038) and +railiance-apps (RAPPS-WP-0016-T02) need to adopt in T06. ops-warden's review +(WARDEN-WP-0044-T01) is still in progress. + +2026-10-08: revised memo `nk-wp-0048-t05-oidc-browser-relying-party-r2` built +(`packet.md`, `intake.json`, packet `sha256:af6435e2…`); it states that it +supersedes the first memo and passes informed-decision's and the dispatcher's +validation. It needs a new memo id because the dispatcher refuses new content +under a delivered idempotency key (`idempotency_conflict`). Queuing it is the +same attended step as above. Both memos serve hub decision `e418263e`; the +first answer resolves it, so the first memo must not stay open: an accept on it +would acknowledge the superseded shape, and a decline would resolve the +decision against r2. Close it with `return` (a reviewer verb that requests a +new revision and does not resolve the hub decision; only accept and decline +do), commenting that r2 supersedes it, then answer r2. `withdraw` exists in +the vocabulary but is not offered to reviewers, and lifecycle `revise` only +applies to lifecycle-registered memos, not intake-delivered ones. + +2026-10-09: the founder reports r2 enqueued and the first memo returned. At +close, hub decision `e418263e` still showed `open` (not verified whether r2 +was answered or the answer is still in transit; the dispatcher ledger was not +read). ops-warden's review (WARDEN-WP-0044-T01, ops-warden +`history/2026-10-08-oidc-rp-follow-through-review.md`, d470f2e) arrived at +21:25, before r2 was built, and is not in r2. Required before WARDEN-WP-0044-T02: +R1 a per-step `lane` (missing: identity-provisioner caller credential, +railiance-platform broker grant), R2 canon meaning of `warden_executes: true` +(caller proxy, caller identity, no standing ops-warden token; ADR-0002), R3 a +per-step `value_handling`. Refinements R4 (one type-level catalog lane instead +of a per-case entry, which conflicts with ops-mason's item 5), R5 (ESO Ready gate +before bind-env), R6 (exact accepted memberships), R7 (mfa-pinned and +secret-delivered checks). Next: if r2 was accepted, fold R1-R3 and R5-R7 in as +a follow-up and settle R4 between ops-warden and ops-mason before T06; if not, +hold r2, fold them in and build r3. + key-cape and railiance-apps were told about Forgejo's new custody +path. + +2026-10-09: r2 was not answered (hub decision `e418263e` open; dispatcher 9 +delivered), so r2 is held and r3 replaces it. ops-warden withdrew R4 for R4′ +(message a66a9204): one catalog entry per case with its body templated in the +type. Folded in: + +- Canon section 3: a narrow meaning of `warden_executes: true` (caller proxy, + caller identity, no standing credential; ops-warden ADR-0002), plus per-step + `lane`, `value_handling` and an optional `ready_check`. A catalog entry body is + now required, like policy and role bodies. +- Schema and validator enforce all of these. +- The type: lanes `identity-provisioner-groups` and + `keycape-client-secret-generate` (both new) and `key-cape-oidc-login`; + `mint-secret` is `generated_in_custody`; a Ready gate on `deliver-secret`; + `ensure-groups` adds exactly the accepted case's memberships; new checks + `secret-delivered` and `mfa-pinned`; the catalog-entry body follows R4′. +- 110 tests pass. + +Memo r3 (`nk-wp-0048-t05-oidc-browser-relying-party-r3`, packet +`sha256:726dfc01…`) passes informed-decision's and the dispatcher's validation. +The founder queues it with the same command, returns r2 with a comment that r3 +supersedes it, then answers r3. + +Prerequisites for T06, not for T05: +- the two new lanes, each through its owner; +- a founder-approved rollout of an identity-provisioner image containing + 630ebfd (the deployed one is main-6c4fcaf from 2026-09-24, without + `/v1/groups/ensure`). + +ops-mason's request to create LLDAP group `fluid-telegram-operators` (message +259ec26d) was answered: not possible from here by that path; the founder creates +it in the LLDAP admin UI. + +2026-10-09: ops-warden finished its side of WARDEN-WP-0044-T01 (message +`58d33f40`, commit 0e7ceb8): the revised type conforms to `warden case +check-type`, with no further changes asked of the type. This acknowledges that +review. Its later addendum R8 (message `af274783`, ops-warden +`history/2026-10-08-oidc-rp-follow-through-review.md`) showed that lane +`identity-provisioner-groups` cannot exist as declared: the provisioner's only +HTTP credential is user-engine's runtime token, and its NetworkPolicy admits +only user-engine pods, so a caller-proxy lane would act as user-engine. Taken +option 1 (ops-warden's recommendation): `ensure-groups` and its rollback are +attended, in the `register-client` session on `key-cape-oidc-login`, run inside +the provisioner pod; no token is read. Option 2 (an operator credential of its +own on the provisioner) stays open for a later version. `warden case +check-type` now reports only `keycape-client-secret-generate` +(railiance-platform) as missing. 89 tests pass. + +Hub decision `e418263e` was still open, so memo r4 +(`nk-wp-0048-t05-oidc-browser-relying-party-r4`, packet `sha256:452ea421…`) +replaces r3; it passes informed-decision's and the dispatcher's validation. The +founder queues it with the same command, returns r2/r3 if still open with a +comment that r4 supersedes them, then answers r4. + +Prerequisites for T06 are now: lane `keycape-client-secret-generate` +(railiance-platform), and a founder-approved rollout of an identity-provisioner +image containing 630ebfd. + +2026-10-09: done. The founder queued r4 (`pending`, `queued`), returned r3 as +superseded and accepted r4 in informed-decision. informed-decision's hub +write-back is off in production (`INFD_HUB_WRITEBACK` unset), so net-kingdom +resolved hub decision `e418263e` from the founder's report, naming memo r4 in +the rationale. The type is `acknowledged`; it becomes `implemented` with T06. + +```task +id: NK-WP-0048-T06 +status: wait +depends_on: [NK-WP-0048-T05, NK-WP-0048-T08, NK-WP-0048-T09, NK-WP-0048-T12, NK-WP-0048-T13, NK-WP-0048-T14, NK-WP-0048-T15, NK-WP-0048-T16, NK-WP-0048-T17, NK-WP-0048-T18, NK-WP-0048-T20, MASON-WP-0010-T03, WARDEN-WP-0044-T03] +blocking_reason: "Type1.2 architecture has a confirmed source-only founder acknowledgment; canonical1.1 remains unchanged/default and the1.2 reference case stays proposed. Needs explicit version selection/reference-source integration without normal admission bypass, closed owner adapters plus global/native receipt verifier (WARDEN-WP-0044-T03), independently verified six-object structural materialization and CCR42 claims/created-only provenance, exact broker/case/source reconciliation, then separately authorized native mint/delivery and attended registry/consumer verification. Source acknowledgment, a bundle or local tests do not prove implemented/materialized/live status." +priority: high +state_hub_task_id: "d9b26bcf-a7c6-59a2-92a0-f913d76a203c" +``` + +### T06 — First KnownSecurityCase: Forgejo + +Write `security-cases/forgejo-netkingdom-oidc.yaml` from the parameters the +founder accepted in KEY-WP-0038-T01 (`key-cape/docs/forgejo-oidc-client-request.yaml`), +so that acceptance carries over as the case acceptance. ops-mason materializes it; +ops-warden follows through and verifies it. The type becomes `implemented` once +this reference case is live. + +This replaces the hand-built path proposed for KEY-WP-0038-T02. + +2026-10-09: case written, `security-cases/forgejo-netkingdom-oidc.yaml`, status +`accepted`: the example's parameters plus the founder's 2026-10-08 acceptance of +the key-cape request (owner and founder), with the custody-path derivation +covered by the type's acknowledgment (memo r4). It validates and renders (plan +`execution.permitted: false`; three attended steps on `key-cape-oidc-login`); +it needs `--allow-unimplemented` until it is live, since it is the case that +makes the type `implemented`. A test keeps it equal to the type's example. +Still open before it runs: ops-mason renders and builds it (MASON-WP-0010-T02, +T03, unblocked by T05), ops-warden plans and runs it (WARDEN-WP-0044-T02, T03), +railiance-platform's `keycape-client-secret-generate` lane (asked by ops-mason +`789feb39` and ops-warden `d9f889b1`, both unread), and a founder-approved +identity-provisioner rollout containing 630ebfd. + +2026-10-09: identity-provisioner rolled out, founder-approved in session: +`main-630ebfd` (`sha256:bafb4860…`, built by the image workflow) replaces +`main-6c4fcaf` (`sha256:ffacd5d7…`; only change: 630ebfd, additive). Applied +with `kubectl set image` on the Deployment only (generation 17 → 18, 1/1 Ready); +the file's Service, NetworkPolicies and Ingress were not reapplied. Verified in +the pod: `ensure_service_groups` and `revoke_service_groups` present, `/readyz` +200; no token used, no group changed. Rollback: `kubectl -n sso set image +deploy/identity-provisioner provisioner=forgejo.coulomb.social/coulomb/identity-provisioner@sha256:ffacd5d790a5b7800e0535f3aa95b7de52c7a68e4f575b9192c3e1622fa672c2`. +The pin in `sso-mfa/k8s/identity-provisioner/deployment.yaml` follows. + +2026-10-09: ops-warden closed WARDEN-WP-0044-T01 and shipped `warden case plan` +(T02, message `06303d1d`); the accepted Forgejo case is refused until ops-mason +materializes it, and a materialized copy plans cleanly once +`keycape-client-secret-generate` exists. Its observation that `bind-env` carries +a literal `KEYCAPE__CLIENT_SECRET`: the validator now renders a +`{param:env}` format spec (upper case, `-` as `_`, as `keycape-client.py` +derives the name) and rejects unknown specs, and follow-through and rollback +text is now checked for unknown placeholders too (patch-level, canon section 6). +The type keeps the literal until ops-warden's planner (`text.format`) renders the +spec as well; asked of ops-warden. 53 tests pass. + +2026-10-10: ops-warden rendered `{param:env}` in `warden case plan` +(23ba584, message `c1df14b7`; other specs and `!conversion` refused). The type +adopts it as a 1.0 patch (canon section 6): `bind-env` and its rollback now read +`KEYCAPE_{client_id:env}_CLIENT_SECRET`. ops-mason reports the Forgejo case +rendered and built (message `1ebddae9`; CCR drafts handed to railiance-platform), +so T06 now waits on the platform apply, the mint-secret lane and the attended +register-client session. + +2026-10-10: key-cape adopted the type's custody path (key-cape ab5cccd, message +`88a4559c`): `docs/forgejo-oidc-client-request.yaml` and KEY-WP-0038-T02 name +`platform/workloads/keycape/forgejo`, field `CLIENT_SECRET`, delivered as +`sso/keycape-forgejo-client` through `external-secrets-keycape-forgejo`, and the +`forgejo/netkingdom-oidc-client` proposal is withdrawn. This matches the type and +the Forgejo example; no change needed here. + +2026-10-10: key-cape's type-composition review (key-cape 6038483, message +`8e5ed5f6`) found the composition sound with two blocking findings, taken as a +1.0 patch (stricter validation and documentation, canon section 6): +R1, redirect URIs now refuse `?` and `#` in the type pattern and in +`keycape-client.py`'s `REDIRECT` (KeyCape refuses `?` at startup and the single +replica would stop); R2, `allowed_scopes` always includes `groups`, the renderer +refuses `access_groups` without it, and `access_groups` now says the consumer +enforces the gate because KeyCape has none; R3, `mfa_required` "false" means the +per-user privacyIDEA policy; R4, register-client's justification records the +restart for every client, the quiet window, and that the rendered registry must +pass KeyCape's client validation before it. The accepted Forgejo case and both +examples still validate; 116 tests pass. +Open, needs a decision: R2's verification step that the consumer enforces the +group gate is a new verification step, hence 1.1 (minor), and the validator pins +cases to the exact type version, so the accepted 1.0 Forgejo case would need +re-pinning. Also open with key-cape: a KeyCape entry point that validates a +rendered client registry without starting the server; until then +`keycape-client.py` mirrors the redirect rule. + +2026-10-10: founder go for 1.1, so no 1.0 run overlaps. The type adds +verification `consumer-enforces-groups`, and hand-over now requires the consumer +to enforce `access_groups` (minor, canon section 6; risk class unchanged, no new +acknowledgment; history in the type README). The accepted Forgejo case and both +examples are re-pinned to `oidc-browser-relying-party@1.1` before any apply; the +acknowledgment intake keeps `@1.0` as the record of what was acknowledged. +ops-mason's build and ops-warden's case-ref digest predate this and need a +re-render. + +2026-10-10: ops-mason re-rendered the Forgejo case from `@1.1` (message +`22c6bba6`; plan digest `af1f4f81…`, no collisions) and rebuilt the bundle. The +rendered OpenBao objects are identical to 1.0, so the already applied +CCR-2026-0041 stands; 1.1 changes only consumer-side steps. key-cape shipped +`keycape check-config` (c528daa, message `a7b0aeff`): KeyCape's startup checks +without secret values, including the new duplicate-clientId rule. +`keycape-client.py --check-config ` now runs it on the live and the +proposed registry, fed on stdin, and apply refuses a proposed registry that +fails; register-client's entry point names the option (1.1 patch). The deployed +image predates c528daa, so the binary is built from key-cape source for now. +Before the next KeyCape rollout (Forgejo register-client or the CCR-2026-0018 +window), the attended operator runs `inspect --check-config` so the live +registry is checked too. + +```task +id: NK-WP-0048-T07 +status: wait +depends_on: [NK-WP-0048-T06] +blocking_reason: "The next types follow once the first one is proven end to end." +priority: medium +state_hub_task_id: "c3c9c3ae-4f23-5ba7-9e6f-e63bcf29b9f7" +``` + +### T07 — Queue the next types + +From the T02 ranking, open the next type drafts (likely the confidential service +client and the workload KV read lane), each to be acknowledged on its own. +Record which existing hand-built instances get adopted as cases, without +rewriting them in place. + +```task +id: NK-WP-0048-T08 +status: done +priority: high +state_hub_task_id: "8e519082-dc8b-5e46-b660-03d03e55dcf6" +``` + +### T08 — identity-provisioner: ensure service-access groups + +The type's `ensure-groups` step calls `identity-provisioner groups ensure --groups + --members `. The provisioner already has idempotent +`_ensure_group` and `_add_group`, but exposes only tenant-scoped operations. Add +the general operation (create if absent, add the listed memberships, never +remove others), its rollback (remove only this case's memberships), tests, and +the ops-warden route for its service identity. + +2026-10-08: done. `identity-provisioner/provisioner.py` gained +`ensure_service_groups` (`POST /v1/groups/ensure`) and `revoke_service_groups` +(`POST /v1/groups/revoke`), behind the existing service-token check. Names +matching `tenant*`, `netkingdom-*` and `lldap_*` are reserved; members must +already exist; everything is validated before any change; memberships are read +back before success; nothing else is ever removed; groups are deleted only when +asked and empty. 7 new tests (39 in the provisioner suite). The type's +`ensure-groups` step and its rollback now name these endpoints. The ops-warden +route for the provisioner's service token belongs to WARDEN-WP-0044-T01. + +```task +id: NK-WP-0048-T09 +status: done +priority: high +state_hub_task_id: "0ee12dfc-8260-5038-bd39-cc7fb4113be0" +``` + +### T09 — The `keycape-client` entry point and its declaration + +Planned first as key-cape KEY-WP-0038-T04. Moved here because the machinery a +live registration needs already lives in net-kingdom, where KeyCape is deployed +(`sso-mfa/k8s/keycape/`): the contained kubectl, the cluster-identity pin and the +issuer pin. A copy in key-cape would drift. + +2026-10-08: done. `sso-mfa/k8s/keycape/keycape-client.py` derives the KeyCape +client record from a case (`client-from-case`). It adds or removes exactly one +client with the existing safeguards (byte-preserving merge, exact match or +refuse, shared-reference refusal, compare-and-swap, read-back, contained errors), +requires the Deployment env binding first for confidential clients, and requires +the case acceptance record for `apply` instead of a per-client founder decision +id. 15 tests, including a regression showing that the vergabe case reproduces +its hand-built registration exactly; all 71 helper tests pass. Declaration +`capabilities/playbooks/net-kingdom.keycape-client-registration.yaml` (contract +PASS, `maturity: implemented`) replaces key-cape's draft. The type now pins it, +and for confidential clients adds `deliver-secret` (delivery from custody into +the sso namespace) and `bind-env` (Deployment env), attended in the same session +as `register-client`. The per-client helpers stay until their clients are +adopted as cases. + +2026-10-10 — NetKingdom owner review of the execution seam, source only: +ops-warden/docs/security-case-execution-proposal.md SHA256 78ab1439e4c5beb205aeccf15b1b2e706511be55ab195c5a3aa682f326c7c77c +was reviewed against INTENT, SecurityCase canon, acknowledged type1.1, current +case and owner source. docs/security-case-execution-owner-review.md supports +the closed adapter/receipt direction with explicit versioned schema, structural +materialization provenance, unchanged caller/session boundaries, safe CAS +reconciliation and exact-created-object rollback requirements. This is owner +review, not approval. Supporting file digests are in the adjacent sources JSON. + +Current source exposes a circular wait: ops-mason T03 waits on mint while Warden +and the broker refuse a non-materialized case. Structural acceptance therefore +needs its own native pinned receipt before mint, not a successful mint before +materialized. The platform broker additionally pins type1.0 while Forgejo is +accepted at1.1; platform-owner alignment and tests are required, not an unchecked +version bypass. T06 remains wait. No case/type lifecycle status, grant, authority +or live runtime changed by this review. A proposed informed-decision would +adopt the source contract only; native materialization and execution acceptance +stay distinct. + +Final proposal re-review: staged Warden proposal SHA256 78ab1439e4c5beb205aeccf15b1b2e706511be55ab195c5a3aa682f326c7c77c +adds only the explicitly local reducer appendix, not an accepted wire protocol +or executor. The exact-final review and sources JSON supersede the earlier +1035f049... proposal review. Platform staged the exact type1.1 broker correction +with ten fake-backend subprocess checks (script SHA256 +9576cd211cfc06526ec4cc9bd0a5b06f0f59287a68931bb2d6e3592a951ddc7d). +The next source work is the versioned closed adapter/receipt/materialization +contract, not another relaxation of the broker's status gate. Native work still +requires installed broker provenance, structural reader receipts, exact new +materialized-case bytes, caller grant/session revocation and verified execution. +No installed/native acceptance is inferred from that staged correction. + +2026-10-11 — new source-only type1.2 architecture acknowledgment: +Confirmed native founder ACCEPT of infd-20261011-forgejo-type12-source-r1@1 +at06:47:39Z, approval8e9332c8-754a-420c-b8b7-445cb4fe6765. Exact frozen packet +0b377d647ca748cd03c92f9792396412048055d4bde9f7bb06ff6d413f7acd68 and submission +585cee731de30d2b37883a57abfd9eb32f023ee694e8d911989109d5caf80569; native receipt +retained in docs/evidence/2026-10-11-forgejo-type12-source-acknowledgment/. +The complete acknowledged1.2 source package is isolated under +security-case-type-revisions/1.2/oidc-browser-relying-party; default loader and +current accepted1.1 case remain untouched because load_type is single-version. +The new proposed1.2 case is NOT accepted/materialized/live and has no acceptance +fields. Actual reference-migration design approval is a separate Apps owner +receipt; no1.1 approval is inherited. Source-recording review/test proof and exact +next gates are in docs/forgejo-type12-source-acknowledgment-followthrough.md. +T06 remains wait and T07 remains its existing dependent task; type is NOT +implemented and no first-case/native grant or broader workplan finish is asserted. + +2026-10-11 — bounded explicit version selection source preparation for T06: +The validator now offers optional exact version_roots/--version-root VERSION=PATH +selection with duplicate/mismatch/traversal/symlink refusal and no fallback. The +default1.1 source and historicalacceptedcase hashes/ordinaryrefusal/preparation +render stayunchanged; actual1.1 is acknowledged, not implemented. Acknowledged1.2 +ordinaryadmission also refuses; optionalpreparation remains nonexecuting.43local +validatorchecks pass and original1.1 CLIrenderbytes match exactly. Makerproof: +docs/evidence/2026-10-11-explicit-type-version-selection/source-integration-validation.json. +FormalNetownerreview/reference-source integration and all existing native adapter, +provenance/broker/materialization/attendedproof gates remain; T06 stayswait, no +new residual or firstcase/typeimplementation/native grant is inferred. + + +2026-10-16 — isolated source preparation for T06 (canonical integration pending): +Added `check-evidence` to the existing validator CLI. It reuses exact version +selection and ordinary type/case/render admission, checks actual Mason structural +request source/inventory/projection pins and reads actual Warden journal rows +read-only. Successful rows require exact type-declared descriptors and attempt/ +revocation fingerprints. Existing acknowledged types without machine descriptors +remain plan-only. The compatibility result never claims authenticated native +completion: Request and journal fingerprints are not portable native evidence. +Owner authentication/status binding and actual native gates remain unresolved; +T06/T07 statuses and both type/case lifecycles remain unchanged. No dispatch, +credential access or authority expansion is introduced. See tool README for the +existing owner protocol boundaries and source integration tests. + +Concrete next Warden owner integration: current `Executor.admit` refuses every +case parameter whose sensitivity is not public/operational, so browser +`redirect_uris`, `allowed_scopes`, `client_type`, `mfa_required` (and 1.2 +`consumer_namespace`) fail after otherwise prepared admission. The sensitive +classification is a security-change review level, not secret content. The +Warden owner must validate through NetKingdom's existing exact-version case/type +validator, then project only descriptor-declared safe target metadata; do not +remove the credential/value refusal or copy complete parameters into journals. +Actual owner descriptor acknowledgment and independent evidence admission remain +prerequisites, with normal admission still refusing unimplemented types. + + +## T10 — Source compatibility checker for Mason and Warden evidence + +```task +id: NK-WP-0048-T10 +status: done +priority: high +state_hub_task_id: "4b06ca97-c1e4-5680-bf22-ab75ab1646a4" +``` + +Completed 2026-10-16 in published source `c31d21ba`. The read-only +`check-evidence` command checks the actual Mason structural request and Warden +journal source protocols against an exact version/render. Independent source +review passed 103 tests at that checkpoint. It always reports native completion +false and execution prohibited. Fixtures and local journal fingerprints do not +provide authenticated owner/native evidence. This records completed source scope +previously described under T06; it does not duplicate native acceptance credit. + +## T11 — Validate the optional machine-descriptor pin contract + +```task +id: NK-WP-0048-T11 +status: done +priority: high +state_hub_task_id: "ef2a0119-f59e-5e45-9519-115da9df7b85" +``` + +Completed 2026-10-16. Reproduced the JSON schema rejecting a valid +`execution_descriptors` map while the Python validator accepted malformed maps. +The schema now supports the existing Warden optional exact-byte pin map; semantic +validation binds keys to declared steps, bounds the map and checks digest shape. +The direct evidence API refuses malformed maps before accessing them. Twenty-one +new checks and all 127 affected source checks passed. No current type/case bytes, +status, owner descriptors or native acceptance changed. Source evidence and +publication receipt: Custodian +`docs/changes/netkingdom-target-2026-10-16/browser-acceptance-next/`. + +## T12 — Prepare NetKingdom-owned attended step adapters + +```task +id: NK-WP-0048-T12 +status: todo +priority: high +state_hub_task_id: "7ce4eee3-cd47-55a9-bfa5-4f1835d4d6fd" +``` + +Owner: NetKingdom. Retained scope: prepare bounded attended Deployment env, +config verification and restart/readiness steps around the existing helpers. +Next action: identify exact current entry points and their revision/readback +contracts, then implement fixed source-pinned callbacks or receipt-only attended +steps. Completion requires positive/refusal checks, exact target/source pins and +uncertainty behavior without acquiring user-engine runtime identity or exposing +registry/client secrets. Existing caller/session, cluster/issuer, CAS/readback +and status-only output boundaries remain mandatory. + +2026-10-16 task refinement: the original broad adapter scope is split into T13 +(completed registration source candidate), T14 (group creation provenance), T15 +(durable registration recovery) and T16 (descriptor/artifact selection and owner +review). This preserves T12's task ID and Hub UUID while giving each remaining +outcome an owner and completion gate. T06 now names every source dependency; +its native/reference lifecycle acceptance scope remains unchanged. Completion +of any source task supplies no Warden, Platform or Apps assent and no native +execution authority. + +## T13 — Prepare the fixed registration adapter source candidate + +```task +id: NK-WP-0048-T13 +status: done +priority: high +state_hub_task_id: "b264c10b-565c-5d29-b788-03cf5fbd2883" +``` + +Completed 2026-10-16, source implementation and synthetic fixture proof only. +Added an injection-only adapter around actual `keycape-client.run`: fixed +case/principal/human/lane/source/acceptance binding, existing CAS/readback, +metadata-only owner evidence and per-attempt parent-session revocation boundary. +The descriptor source composition pins the exact checker executable digest +alongside adapter/helper source. Creation-only rollback authenticates its +creation receipt and refuses changed postcreation UID/resourceVersion; +pre-existing clients confer no rollback authority. Missing readback, evidence, +revocation or process-local creation provenance cannot become retry-safe success. + +Evidence: Custodian `docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/` +R3 candidate, explicit patch and postimages, retained R1 rejected custody finding +and R2 revision, and 36 passing source checks (16 new, 20 existing). Three checks +use actual Warden kernel admission/dispatch/rollback with published Warden +`d97652a3bb5fa733b063d71a1b74068efcccf572` producer pins unchanged. Owner trust, +descriptor/type/case and Kubernetes objects remain synthetic fixtures. This +records a completed source deliverable, not independent review, publication, +type acknowledgment, exercised procedure, identity admission or native browser +acceptance. Independent root source review: Custodian browser-adapter-next/coordinator-review-r3.json. Source integration and normal publication/synchronization receipts: Custodian browser-adapter-next/canonical-integration.json. These receipts supply no other-owner assent or native authority. + +Source publication: `0775c2bfa41af236301155bb5a69bbf088bdf823`, independently +reviewed by the Custodian coordinator before integration. The required initial +projection and ordinary UUID writeback both completed with zero failures and +zero automation errors. Generated-only brief commits were refused by the normal +hook; exact pre-existing staged/working brief bytes were restored. The separate +canonical integration receipt retains those refusals and projection evidence. +Native admission and all other-owner acceptance remain open. + +## T14 — Admit governed group rollback against native owner boundaries + +```task +id: NK-WP-0048-T14 +status: progress +depends_on: [NK-WP-0048-T20, NK-WP-0048-T21] +priority: high +state_hub_task_id: "fce95440-a25b-5f27-8ef3-a3d182099113" +``` + +Owner: NetKingdom. Next action: bind the completed T21 PostgreSQL journal to +an admitted dedicated CNPG consumer through the existing `rapp-postgres` +database-per-consumer contract. Identify the actual deployment/package and +custody owner, admitted placement/capacity and role/renewable lease/TLS target; +implement the independently retained exact content checkpoint and authoritative +tenant/case/source/principal callbacks before enabling governed group operations. +The injection-only backend and fixture callbacks do not supply production scope +or authorization. Existing service tokens, caller-supplied JSON and provenance +receipts confer no new grant. The default production endpoint remains disabled +without the trusted integration. SQLite remains local/disposable only; any +production exception requires explicit scoped governance under ADR-0015. + +2026-10-17 owner-integration refinement: T23 records the completed source/package +placement review, distinct from T21's source/disposable-SQL proof. Its dedicated +`rapp-identity-provisioner` recommendation is draft and unbound, not an accepted +package or existing repository. The full six-consumer rapp-postgres renderer +accepts the inert candidate on platform-pg-2 as its third of four declared +consumers; first-cell placement is refused as a fifth. Neither the estimated +four-consumer ceiling nor source requests establish native headroom or backup. + +Next independent owner contracts: NetKingdom/package/substrate owners accept +exact deployment/rollback fate; rapp-postgres admits the dedicated consumer, +roles, five-connection budget and actual backup/restore; Platform admits exact +renewable runtime/migration delivery and TLS/CA/SAN; network owners scope SQL, +broker and checkpoint paths without assuming a narrow policy subtracts additive +same-namespace access. Protected caller owners must provide authoritative +GroupJournalBinding construction and current authorization; an independent +existing holder must authenticate and atomically advance the exact content +checkpoint, not a counter or ordinary Audit receipt. Directory/runtime owners +must select and prove either an exclusive all-writer maintenance barrier or +provider-supported CAS/generation, including established sessions, lifecycle +cascades and remove/readd ABA. No source review establishes those owner facts. + +The default PostgreSQL activation and destructive native rollback remain refused. +Storage/lease acceptance can be proposed separately from LDAP mutation only after +exact owner source/target/custody/verification/recovery and cost/risk review; +State Hub guard approval grants none of it. Source-only integration review and +native acceptance matrix: Custodian native-storage-r1/OWNER-REVIEW.md. T14's +existing status, UUID, dependencies and native completion gate are retained. + +2026-10-17 refinement: T20 retains the historical durable SQLite source and +actual disposable LLDAP proof. T21 extracts the completed PostgreSQL source and +bounded real disposable SQL compatibility proof. T14 remains `progress` for +native CNPG admission/recovery, protected owner integration and exclusion of all +external LDAP writers. Separate source publication/projection and independent +cleanup receipts belong to coordinator follow-through; no missing native assent +is represented as received. + +Completion still requires independently reviewed/admitted ADR-0015-conforming +CNPG storage (or an explicit governed exception), backup/lost-store recovery and +migration of legacy requests; a native +writer-isolation/fencing or provider CAS/version contract that closes external +writer races and remove/re-add ABA; exact case/source/principal and current +native object bindings for protected callbacks; and actual per-attempt evidence, +revocation and readback/refusal verification through the existing owner paths. +Only independently retained confirmed creations may be undone. Every +pre-existing/shared group and membership must remain preserved; unresolved +intents never expire into retry authority. The disposable fixture supplies none +of these native custody, authorization, owner-acceptance or production claims. + +## T15 — Define authenticated recovery for lost registration process state + +```task +id: NK-WP-0048-T15 +status: wait +depends_on: [NK-WP-0048-T31] +blocking_reason: "Source candidate 061461e is merged. Waiting on the owner to select the real authenticated original-attempt holder and native readback, and on independent re-review of the changed T13 adapter source." +priority: high +state_hub_task_id: "1b825705-4848-5edc-832f-b1622cd9bdb7" +``` + +2026-10-17 source part merged (061461e via 65559ef). `build()` takes optional +paired `holder`/`observe` capabilities and installs `reconcile_original`; the T31 +fixtures now run through the adapter's own path. Rollback after restart is still +refused, and there is no add replay. This changes `keycape-registration-adapter.py`, +so the revision reviewed under Custodian `browser-adapter-next/coordinator-review-r3.json` +is superseded. The new revision has no independent review yet and must not be +pinned on the strength of the old one. + +2026-10-17: T31 fixtures done (74adcd7). What remains for T15: the owner +selects the real authenticated original-attempt holder, and the T13 adapter +gains the holder capability, `reconcile_original` and reconcile-aware `verify` +described in T31. + +Owner: NetKingdom for registration state, with Warden owning kernel evidence +admission. T22 records the completed source review and exact existing-seam gaps; +T15 retains implementation and authenticated restart/lost-receipt acceptance. +Next action: select and admit the actual owner publisher/authenticator and bounded +original-attempt readback contract for the merged holder/observe adapter seam. +Warden's original-intent persistence/interface (`a45cf475`) and additive validation +(`117fec9241e7b985dfb187b992e173d0a5dc3700`) are now published and projected. +The local Journal retains admitted nonsecret original context, validates original +actor/attempt and metadata linkage to the original running receipt, and supplies +it separately from the new reconciliation attempt. This local consistency is not +independent original-holder authorization or creation provenance; neither the +journal nor a current matching registry entry reconstructs creation authority. + +The owner contract must bind the original Invocation, case/type/descriptor/catalog/ +acceptance pins, source, principal and admitted tenant/realm provenance, separately +from the new reconciliation attempt and its independently revoked parent session. +Use an existing authenticated holder/readback; Audit Core's custody API is a +candidate only until its actual registration source/class, load-bearing emission +contract and separate scoped reader are admitted. No new broker or receipt trust +root is proposed. A missing original record, ambiguous publication, NO_CHANGE, +changed native revision or lost readback stays uncertain; no add replay or undo. + +Completion requires restart/lost-receipt fixtures against the selected actual owner +contract: independently authenticated original created-only provenance, original +exact client entry and unchanged current postcreation UID/resourceVersion before +any undo, with wrong attempt/case/source/principal and unavailable-holder refusals. +The merged T13 adapter exposes reconciliation only through paired injected +holder/observe capabilities and still refuses post-restart rollback. Existing +source review, JSON receipts and synthetic trust do not satisfy independent +holder authorization/provenance or native admission, or authorize attended recovery. +Exact remaining owner facts: Custodian native-registration-r1/original-attempt-review.json. + +2026-10-17 check (net-kingdom-worker): Warden's original-intent lookup interface +is not published — ops-warden head is `d97652a`, with no WARDEN-WP-0044-T03 +commit, and the net-kingdom inbox holds no announcement. No restart/lost-receipt +fixture or readback seam was added this session; the work is deferred behind +T16's decomposition under the same token budget. Remains todo, gated on Warden +publishing the reviewed lookup/context interface and on owner selection of the +authenticated original-attempt holder. + +2026-10-17 current-source correction: the earlier same-day `d97652a` lookup +checkpoint and T31 adapter-next-action note above are historical and superseded +by the published Warden interface/validation and merged `061461e` adapter seam. +The 54 focused Warden checks establish source behavior only. Current admission +leaves tenant/realm empty; no admitted tenant/realm provenance is inferred. +T15 remains wait for actual independently authenticated owner-holder/readback +selection, independent review of the changed adapter, and native restart/lost- +receipt acceptance. Existing task metadata, owner gates and historical reviews +are unchanged. + +## T16 — Select and review the actual machine descriptor composition + +```task +id: NK-WP-0048-T16 +status: todo +depends_on: [NK-WP-0048-T24, NK-WP-0048-T25, NK-WP-0048-T26, NK-WP-0048-T27, NK-WP-0048-T28, NK-WP-0048-T29, NK-WP-0048-T30] +priority: high +state_hub_task_id: "200bf500-5084-5ae7-be26-3033fe1f296e" +``` + +2026-10-17 coordinator review r4 approved the 1.3 candidate at 1920b13 with no +findings (the-custodian `nk48-adapters-1.2-review/coordinator-review-r4.json`). The founder chose +option A of the T16 owner-review packet (`nk48-t16-owner-review/founder-option-decision.md`): +1.3 with execution descriptors for all seven steps. Owner reviews for A are open +in the-custodian `docs/changes/netkingdom-target-2026-10-17/nk48-1.3-owner-review/` (net-kingdom, +railiance-platform, railiance-apps, ops-warden, key-cape). The owner-review packet +supersedes the r1 composition pointer: T16's exact composition is now +`sso-mfa/k8s/keycape/descriptor-composition-1.3.candidate.json`. + +2026-10-17 successor candidate prepared (source only, `proposed`): +`security-case-type-revisions/1.3/oidc-browser-relying-party/` is the 1.2 package plus +an `execution_descriptors` map of the seven composed descriptors, with examples +pinned to `@1.3` and no acknowledgment. ensure-groups, hand-over, +deliver-consumer-secret and the composer accept 1.3 (835680c, merged acda26c). +`test_admit_1_3_type.py` runs Warden's real admission against the committed 1.3 type: +the proposed type is refused; an acknowledged copy is refused because ops-warden +pins parameter fingerprints only for 1.1 and 1.2; and with only a 1.3 pin (equal to +1.2's fingerprint) all seven are admitted. This is a major change under canon +section 6. Next: r4 source review of the changed adapters and the map, then owner +reviews, including the ops-warden 1.3 pin, the mint-secret warden_executes/human +question and the owner-run steps question, then the founder's Decision Memo. + +Owner: NetKingdom for descriptor/type source and exact checker artifact; Warden +independently owns global provenance/admission. T22 records the completed seven-step +source1.2 inventory and regenerated register-client descriptor using the delivered +checker and current sealed-snapshot source; T16 retains exact owner review, +complete descriptor composition and admission. Next action: bind actual native +publisher/authenticator, original/current session verification and revocation, +executable storage/custody and config pins, then obtain the six missing fixed +owner descriptors/adapters for ensure-groups, mint-secret, deliver-secret, +bind-env, deliver-consumer-secret and hand-over. The inventory identifies each +existing owner seam, exact missing contract and created-only rollback requirement; +it is not an executable bundle. T14 and T15 remain independent prerequisites. + +Review the dedicated consumer source1.2 composition only through the existing +explicit version-root selection. Keep canonical1.1/default and its accepted case +unchanged, and the source-only acknowledged1.2 reference case proposed without +acceptance fields. New descriptor bytes require a reviewed exact successor +revision/version under canon section6; they do not inherit historical source +acknowledgment. Changed authentication/materialization/trust needs the prescribed +informed-decision acknowledgment. Do not insert a partial descriptor map or use +--allow-unimplemented preparation as ordinary case/native admission. The registry +helper reports restart_required; separately review quiet-window restart/readiness +and actual consumer verification rather than infer activation from registry CAS. +Exact composition: Custodian native-registration-r1/descriptor-composition.json. +Completion requires separate owner source review of exact machine descriptors, +artifact/source/config pins, normal type/version admission and independent +evidence-verification boundaries. Warden d97652a refreshes producer validator +pins for NetKingdom 015694ee; it does not attest T13's new adapter. Pinning any +machine descriptor into an acknowledged type is a separately reviewed source +change. Platform owns structural/mint/broker/delivery evidence; Apps owns actual +consumer login/group enforcement. Neither their assent nor native acceptance is +supplied by this task. + +2026-10-16 source checkpoint: T19 below extracts the completed per-attempt +executable snapshot implementation and reviewed fixture proof. T16 retains +native executable storage/custody, exact owner evidence/session callbacks, +complete descriptor composition and ordinary type/version admission. The +successor descriptor/source pins are proposed only; the earlier descriptor +history is preserved. T14 group provenance and T15 durable restart/recovery +remain independent open requirements. No native acceptance is counted here. + +2026-10-17 budget checkpoint (net-kingdom-worker): no source change made. The +T13 registration adapter alone is 217 source and 317 test lines plus an +independent review; six further owner descriptors/adapters cannot be completed +within one per-task token budget (soft 8000 / hard 10000), so the task was +stopped below 50% and is decomposed rather than pushed through. Proposed split, +one source-candidate task each with positive/refusal fixtures, canonical1.1 +default unchanged and no --allow-unimplemented admission: (a) ensure-groups, +bound to T14's journal/provenance seam; (b) mint-secret and (c) deliver-secret, +Platform-owned evidence seams; (d) bind-env and (e) deliver-consumer-secret; +(f) hand-over, Apps-owned consumer verification; then (g) descriptor composition +over the reviewed set. Each candidate needs its owner's source review before +pinning; none is authored here as owner-accepted. Status stays todo. + +2026-10-17 task refinement after coordinator r5: completed independent mint- +guard regression coverage is extracted as T32. T33 tracks the exact Platform +issuer-store source/reference correction; T34 tracks the Apps configuration +inverse reachable without secret or hand-over resource creation. T16 retains +final exact seven-descriptor composition, independent owner reviews and admission; +its status and existing dependencies are preserved. The kernel branch also needs +its own KR1-F1/F2 admission/dispatch corrections and independent review under +WARDEN-WP-0044-T03 before any 1.3 pin. Historical r4 acceptance does not supersede +r5's findings or establish native readiness. + +## T17 — Build and exercise the exact offline config-checker artifact + +```task +id: NK-WP-0048-T17 +status: done +priority: high +state_hub_task_id: "8c3e4baa-a218-5996-af9e-fe1153a46dac" +``` + +Completed 2026-10-16, isolated artifact/source fixture proof only. Built the +existing `key-cape/src/cmd/keycape` command from published clean source revision +`3c71226a1a7b8a2bdb710e9c497e1a1f7f8a3987`, source tree +`f0010b9cab17dbc3a787628a29657d98b900320f`, with cached Go 1.23.0, +CGO disabled, Linux/amd64 v1, trimmed paths, disabled VCS stamping and offline +read-only module selection. The full retained closure pins 108 source files, +9876 toolchain files and ten used module source directories. Three isolated +build locations produced identical 13633409-byte artifacts: +`sha256:be37738fe1c08259fb4f9ca0daf0f9301bfffc237e5bb38435525dfbf11ccff5`. + +Eight disposable checks use the actual executable and maintained helper/kernel, +with complete synthetic configs and a minimal environment: add/created-only +removal, normal pinned producer admission, rollback, lost-readback uncertainty, +stale revisions, substituted checker refusal, invalid startup config and missing +secret-env refusal. No checker stub or producer-pin override remains. The +artifact-bound descriptor proposal is +`sha256:76b1153d188062ece53fcd5e8d35290fdbfd02a69dd79d4b001fc42781afcdc3`. +Evidence: Custodian +`docs/changes/netkingdom-target-2026-10-16/browser-checker-artifact-next/`. +The first two build attempts succeeded but their closure collection failed; +both failures are retained, as is refusal of the older incomplete synthetic +fixture. No production config, credentials or cluster mutation was used. + +This extracts completed build/fixture scope from T16 without claiming KeyCape +owner endorsement, native checker selection/storage custody, acknowledged type +integration, native evidence/session callbacks or attended browser acceptance. +T16 retains exact owner review and composition/admission scope. The attended +registration procedure remains unexercised. + +## T18 — Make checker input and executable selection deterministic + +```task +id: NK-WP-0048-T18 +status: done +priority: high +state_hub_task_id: "9c45da71-fb4c-5f20-9065-90f448f37c64" +``` + +Completed 2026-10-16, isolated source correction and real-artifact fixture proof +only; independent integration/publication remain separately recorded. KeyCape +source review identified non-secret `KEYCAPE_ACCOUNT_PORTAL_URL` and +`KEYCAPE_BROWSER_LOGOUT_URL` overrides in config.Load. The original helper +inherited ambient environment, allowing valid supplied configuration to be +rejected or invalid supplied URL configuration to be admitted. A bare relative +checker filename could also be replaced by PATH lookup after the adapter had +hashed the selected local file. All three counterexamples are retained against +the unchanged predecessor. + +The existing helper now resolves the selected checker file to an absolute path +and passes an explicit empty subprocess environment. Three opt-in real-checker +regressions verify both URL-verdict directions and relative-file selection under +a non-checker PATH decoy. All 39 affected checks pass with the exact supplied +artifact, as do 11 real-artifact helper/kernel scenarios. Source evidence: +Custodian `docs/changes/netkingdom-target-2026-10-16/browser-checker-environment-r2/`. +Historical descriptors remain retained; the helper correction changes successor +source-composition pins and requires their separate review/admission. An absolute +path is not immutable custody and does not eliminate file replacement races; +T16 retains native executable storage/custody and callback/type admission. +No production configuration, credentials or cluster mutation was used. + +Source integration and publication: `916ba4b33e03179274d03b78bd898a4843e8653f`, following independent +Custodian source review in browser-checker-environment-r2/coordinator-review.json. +The ordinary protected projection pass assigned T17/T18 UUIDs with zero failures +and zero automation errors; generated-only brief commit refusal is retained. +The exact prior staged/working brief bytes were restored. Final source/publication/ +projection evidence is retained in Custodian browser-checker-environment-r2/ +canonical-integration.json. These source receipts do not admit native executable +custody, owner callbacks, acknowledged type changes or browser identities. + +## T19 — Bind checker execution to verified immutable attempt bytes + +```task +id: NK-WP-0048-T19 +status: done +priority: high +state_hub_task_id: "f50fdcd1-00be-5c3c-be0c-345aa2126032" +``` + +Completed 2026-10-16, independently reviewed source implementation and synthetic +fixture proof only. The registration adapter now opens a bounded regular checker +artifact without following its terminal symlink, verifies the reviewed digest, +and snapshots the exact bytes into a write/grow/shrink/seal-removal-sealed Linux +memfd before helper access. Live and proposed config checks execute that same fd +through procfs with the existing empty environment; the fd closes on completion +or failure. Unsupported snapshot/seal/exec policy refuses through the existing +parent-session revocation and uncertainty boundaries. The earlier unbounded +pre-snapshot path read was removed during independent review; actual adapter +symlink/FIFO/oversize refusal tests verify helper access never starts. + +Evidence: Custodian `docs/changes/netkingdom-target-2026-10-16/browser-resume-r1/`, +including exact base/preimages/postimages/patch, proposed descriptor, coordinator +source review and canonical integration receipt. Independent Custodian checks +passed 49 affected source tests and 12 actual Go checker/helper/kernel scenarios; +the sets overlap and are not separate acceptance counts. Existing published +Warden d97652a producer pins remain unchanged. The exact KeyCape artifact remains +`sha256:be37738fe1c08259fb4f9ca0daf0f9301bfffc237e5bb38435525dfbf11ccff5`. +The predecessor path-replacement refusal and the first expanded reopened-fd test +failure are retained; the latter fixture was corrected to restore write mode +before independently proving seals block reopened-fd writes and shared mmap. + +This source task adds no native owner custody, callback, evidence trust, type +acknowledgment, credential access, consumer sign-in or rollback acceptance. +Executable memfds, Linux seals and usable procfs are explicit runtime dependencies +and still require native admission. This is an extracted source outcome from +T16, not completion of T16; T12/T14/T15 and all external-owner gates remain open. + + +Publication verified by the coordinator after the source worker usage limit: +`8790dd18ae83c4651ef3d1d923312a118a5b436b` is the actual remote main revision. +All five canonical source hashes match the reviewed packet. Normal synchronization +completed with zero failures/automation errors; all 19 task statuses match the +projection. T19's assigned UUID is retained. The original staged and working brief +bytes were restored and independently compared. The exact source, projection and +publication checks are in Custodian browser-resume-r1/canonical-integration.json. +Native acceptance remains open under T16 and the other named owner tasks. + + +## T20 — Prepare durable group provenance and prove disposable LLDAP compatibility + +```task +id: NK-WP-0048-T20 +status: done +priority: high +state_hub_task_id: "863fcbdb-e246-51ba-87ca-a2f44d01d6f8" +``` + +Completed 2026-10-17, source candidate preparation and bounded synthetic +compatibility proof only. This extracts evidenced implementation/fixture scope +from T14; T14 retains native storage, writer isolation, protected callback and +owner-acceptance gates. Source integration/publication and State Hub projection +are pending the coordinator's separate receipts; no revision or task UUID is +invented here. + +The existing identity-provisioner group ensure/revoke interface now has a +reviewed candidate for a bounded owner-private SQLite journal, proven only in +local/disposable use. The initial storage instructions wrongly suggested a native +persistent-volume path; source review against accepted ADR-0015 corrected them +before publication. Production SQLite-on-a-PVC is not admitted: T14 retains +CNPG/rapp-postgres storage admission or a separately governed exception. +The local journal saves +complete prestate and intent before each attempted directory mutation, inventories +only confirmed new groups/memberships, binds original ensure idempotency and +correlation to the exact request digest, and refuses unsigned caller inventories +or legacy broad revoke requests. Replays never recreate removed state. Missing, +replaced or untrusted storage and missing intent rows stop execution; definite +before-mutation refusals retain evidence without stranding unrelated valid work. +Interrupted writes/readback remain unresolved across reopening. Revoke validates +all targeted native ids and complete member postimages before destructive work, +removes only confirmed creations and never deletes a preexisting empty group. +Server/configuration wiring, image-copy closure and owner storage/request +migration instructions are included. The existing endpoint's caller +authentication remains the existing boundary, not a new grant. + +The candidate was prepared against NetKingdom +`878abc93588e12b6da1bac055cfbb3abb8d7adf1`; exact source patch digest +`sha256:1f3f3ca94c46dadac8f24896053d4d23179de795e50a24298a0cfbcc46cca30e`. +The independent Custodian review passed all 65 provisioner checks. The earlier +62-check candidate, two independent-review findings and subsequent fixes remain +retained rather than overwritten. Seven additional fixture-plan checks exercise +the preparation boundary; these counts overlap the same candidate and do not +measure task-completion percentage or independent acceptance. + +A separately reviewed bounded fixture then exercised the exact candidate against +cached pinned disposable LLDAP and Python images, through actual GraphQL. All 14 +closed compatibility/containment oracles passed: exact creation inventory, +separate-process SQLite reopening/replay/revoke, complete preexisting directory +preservation including empty/shared groups and memberships, refusal of shared +membership drift, retained uncertainty after a lost response following a real +addition, and refusal of unknown/cross-case receipt bindings. The fixture used +only newly generated synthetic users/passwords/key material in private tmpfs, +with a network-none anchor and shared loopback helper, no host ports/mounts, +no image pulls and no production or actual credential contact. The coordinator's +safe receipt reports both exact owned containers absent after cleanup; final +independent cleanup verification remains the coordinator's integration duty. + +Evidence: Custodian +`docs/changes/netkingdom-target-2026-10-16/group-provenance-r1/`, including exact +source base/pre/postimages, patch/manifest, independent review and retained +refinements. Native fixture manifest +`sha256:8a0514f5f14137355972400cd4852f48c892316d28e3abd3c86635d7cb81eef2`; +retained safe result `native-fixture/execution-result.json` (original +`/tmp/nk48-groups-native-review-5pywb19z/result.json`). The source task supplies +no native owner assent, production rollout, protected session/evidence callbacks, +ordinary type admission or consumer login proof. LLDAP has no conditional +membership mutation or generation token here: exact readback cannot exclude +ABA or a concurrent external writer, so T14 remains open. + +2026-10-17 verified source follow-through: source commit `4f65e41` passed normal +local scanning and all 65 canonical provisioner tests. The actual disposable +LLDAP receipt passed all 14 oracles; independent verification on the pinned +daemon found both exact names and the run label absent. Source integration +includes the ADR-0015 documentation correction; the journal remains local-only. + +T14 source preparation is underway. Twenty-three pinned public sources identify +NetKingdom implementation/reconciliation, Platform custody/database governance +and rapp-postgres consumer provisioning; the inspected runtime binding does not +assign identity-provisioner a package. The existing User Engine runtime token +and pod route are not a protected-case caller lane. The native proposal preserves +the same journal seam on a dedicated CNPG consumer and requires authoritative +scope binding, admitted role/lease/placement/recovery and all LDAP writers fenced. +The declared exclusive maintenance window is not mechanical exclusion of WebUI, +helper or lifecycle writers. Capacity placement and EUR cost/risk remain owner +decisions. No package, database, credential, rollout or owner assent was created. +Custodian evidence: group-provenance-r1/native-storage-proposal-r1; final source, +projection and cleanup review: resume-integration-r1/publication-verification.json. + + +## T21 — Implement the PostgreSQL journal and prove disposable SQL compatibility + +```task +id: NK-WP-0048-T21 +status: done +depends_on: [NK-WP-0048-T20] +priority: high +state_hub_task_id: "afe78e4d-408a-516a-ab92-e212e8a2d11d" +``` + +Completed 2026-10-17: bounded source preparation and actual disposable PostgreSQL +compatibility only. T14 retains native storage, custody, callbacks, authorization, +all-writer isolation and production admission. Coordinator source publication and +projection remain separate follow-through; no source revision or task UUID is +invented here. T20 remains the historical local SQLite/actual LLDAP proof. + +The eight-path source candidate adds an injection-only PostgreSQL implementation +behind the existing owner journal seam, with explicit trusted tenant/directory/ +case/source/principal/epoch binding and independent continuity callbacks. It +persists bounded intent/created-only receipts, exact-one version updates, durable +active fences, and a content-bound journal chain. SQL commits precede independent +exact-marker acknowledgment; lost replies/checkpoints poison continuation and +retain uncertainty. A counter-only checkpoint is explicitly insufficient for +divergent clones. Migration does not create database/schema/credential, admit an +epoch or clear quarantine; production HTTP activation is absent pending trusted +callbacks. Pinned psycopg package/image-copy closure is included, without claiming +an image build or native dependency admission. + +Candidate base `6cb43c0cc55212363a5406b02db046e2384a7c40`; final eight-path patch +`sha256:5549d2982b61ff70aba4b3928fecd5639db0f1a732ab2f8630f678e7acf4d34d`. +All 82 source tests pass, alongside 13 fixture plan/diagnostic checks and two +source-backed snapshot regressions. Counts overlap the same source and fixture; +they are not additive native acceptance or completion percentages. + +The independently reviewed R5 fixture ran the actual SQL migration/backend and +existing provisioner path against cached pinned disposable PostgreSQL. Directory +operations deliberately used the existing synthetic LLDAP test double. All +manifest-declared phase oracles passed: creation-only inventory and DDL refusal; +separate-process replay/revoke with complete preexisting/shared visible-state +preservation; foreign-context refusal; concurrent exactly-once reservation and +stale version refusal; actual database fork with divergent bodies at the same +counter and fresh-object rejection of the losing exact checkpoint; retained +uncertainty after checkpoint/directory/SQL response loss; and actual pg_dump/ +pg_restore rollback refusal against the independently retained fixture checkpoint. +The checkpoint is fixture-only, not an admitted native owner evidence holder. + +Only generated credentials/state were used, with two nonroot/read-only containers +on private loopback, bounded tmpfs/CPU/memory/PIDs/lifetimes, no host ports/mounts, +no pulls and no production contact. The safe receipt reports both exact owned +containers absent, with no daemon uncertainty. Independent final pinned-daemon +cleanup verification remains coordinator follow-through. + +Failed R2/R3/R4 receipts and source revisions remain retained. R2's helper failure +subphase is unknown; R3 exposed a real backend bug where replacing nested receipt +objects lost the created ID held by ensure, fixed by acknowledging only the backend +version after commit/checkpoint. R4 verified that fix through actual SQL bootstrap, +then exposed fixture snapshot aliasing and empty deleted-group backing entries; +source-backed regressions corrected the snapshot without weakening complete +visible-prestate equality, and reject nonempty orphan membership. These refinements +belong to one trial, not extra native acceptance successes. + +Custodian evidence: `docs/changes/netkingdom-target-2026-10-16/group-postgres-r1/`; +exact source/pre/postimages and retained predecessors; native R5 manifest +`sha256:d51f90816616848b93bcdc482e10a98d07061deb01eeed9e50700fc6e68b6051`; +`final-integration-r1/typed-execution-evidence.json` and byte-exact safe receipt +(original `/tmp/nk48-postgres-native-review-sguj7t8p/result.json`). No CNPG native +storage/HA/backup/lease/TLS/capacity/restore admission, authoritative protected +callbacks, real LDAP writer fencing/CAS/ABA exclusion, owner assent or production +endpoint activation is claimed. Those remain live T14 work. + + +## T22 — Establish registration recovery source gaps and complete descriptor review inventory + +```task +id: NK-WP-0048-T22 +status: done +priority: high +state_hub_task_id: "ea50d93b-0d04-55d2-98c4-9b6c3fe925b1" +``` + +Completed 2026-10-17: source-only owner-contract review and complete review +inventory. Inspected the actual registration adapter, Warden Journal/Executor, +NetKingdom evidence compatibility checker and Audit Core custody/read surfaces. +No existing admitted holder/readback supplies authenticated original-attempt +recovery: payload/creation are process-local, persisted evidence references are +hashed, and reconcile receives a new attempt without original context. The exact +four-owner contract requirements and non-replayable missing/ambiguous cases are +recorded; no recovery callback, publisher, credential grant or native trust root +was installed. T15 retains implementation and actual acceptance. + +The complete source1.2 inventory maps all seven follow-through steps to their +existing owners, missing exact contracts and created-only rollback requirements. +The actual maintained adapter regenerated the sole existing register-client +machine descriptor against the delivered 13,633,409-byte checker and current +sealed-memfd snapshot source. The checker rehash and Warden Descriptor.decode +round-trip passed; six owner descriptors remain absent. Actual source commands +render canonical1.1 and explicitly selected1.2 with execution.permitted false; +ordinary1.2 case admission still refuses its acknowledged/unimplemented type. +Canonical type/case/acknowledgment bytes were hash-checked unchanged. No tests were +added or rerun, no native configuration or effect occurred, and no owner assent +or complete machine bundle is claimed. T16 retains native executable custody, +complete exact descriptors, independent source review and normal version/type +admission. This extracted outcome supplies no native gate completion credit. + +Evidence in the-custodian: +`docs/changes/netkingdom-target-2026-10-17/native-registration-r1/`, especially +original-attempt-review.json, descriptor-composition.json, source-verification.json, +source-manifest.json and manifest.json. The prior sixteen-gate audit remains +immutable. Publication, normal task UUID allocation and projection are separate +coordinator follow-through; no Hub UUID is assigned by this source record. + + +## T23 — Review native journal package assignment and storage integration boundaries + +```task +id: NK-WP-0048-T23 +status: done +priority: high +state_hub_task_id: "6e7cd483-c7af-53a2-9d0a-3d76b6fb49ee" +``` + +Completed 2026-10-17: source/package placement and protected integration review +only, separate from T21's completed PostgreSQL source and disposable-SQL proof. +The owner review recommends a dedicated independently rolled-back +`rapp-identity-provisioner`, owned by NetKingdom on rail-kubernetes, with a +schema-valid draft declaration and no reef binding. No repository, registry +entry, native package assignment or owner assent was created. Existing +rapp-user-engine ownership/token delivery does not assign it the provisioner. + +The complete current rapp-postgres catalog has six declared consumers: four on +platform-pg and two on platform-pg-2. The inert renderer-valid provisioner +candidate fits platform-pg-2 at three of four, requesting one migration plus +four runtime connections; placing it on the first cell is refused as a fifth. +Normative package schema and full-catalog capacity/render checks passed. The +four-consumer ceiling is estimated, not measured native headroom, and no current +SQL session, backup or restore acceptance is claimed. + +The exact review identifies remaining package/substrate, SQL role/lease/TLS, +network and backup/recovery contracts; independently authenticated protected +scope callbacks; an external content-bound checkpoint holder with atomic +advance/loss handling; and all-LDAP-writer exclusion or provider CAS/generation, +including lifecycle cascades and remove/readd ABA. Generic Audit custody is not +that checkpoint service. A database-only fence, final reread or one replica +cannot exclude foreign directory writers. The native acceptance/recovery matrix +keeps storage/lease admission separate from separately approved directory +mutation; the production activation remains disabled pending trusted integration. + +Evidence in the-custodian: +`docs/changes/netkingdom-target-2026-10-17/native-storage-r1/OWNER-REVIEW.md`, +source-validation.json and hash-bound source-references.json, plus the inert +proposed declaration/consumer and renderer outputs. No native resource, lease, +credential, deployment, destructive effect, independent holder or native owner +acceptance was delivered. T14 retains these live gates with its original UUID +and progress status. This completed review adds no duplicate T21 acceptance +credit; publication, task UUID allocation and projection remain coordinator +follow-through and no UUID is hand-assigned here. + +## T24 — Ensure-groups adapter source candidate + +```task +id: NK-WP-0048-T24 +status: done +priority: high +state_hub_task_id: "1a3931ee-3b5f-52fc-9295-4073804f4bd2" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): ensure-groups-adapter.py, 8 tests (commit 5a2e7d6). Binds to the T14 PostgreSQL journal and refuses SQLite or a foreign binding. It accepts only canonical 1.1 cases, so T30 must add an explicit 1.2 path. It calls the provisioner's private `_group_binding`; a public helper is the cleaner shared edit. + +Split from T16 on 2026-10-17 so each candidate fits one per-task budget. Bind group creation to T14's journal/provenance seam; refuse without trusted journal callbacks. +Source candidate only, modelled on the T13 registration adapter +(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and +`registration-adapter.md`). Requires positive and refusal fixtures and +created-only rollback. Keeps canonical 1.1 as the default and uses no +`--allow-unimplemented` admission. Done means the candidate and its tests are +committed. NetKingdom (directory) owner source review before pinning, and any native action, +stay separate gates and are not supplied by this task. + +## T25 — Mint-secret adapter source candidate + +```task +id: NK-WP-0048-T25 +status: done +priority: high +state_hub_task_id: "0de2a7d9-874d-578b-a076-f653fe42e30b" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): mint-secret-adapter.py, 9 tests (commit 558c5bc). Value-free receipts only. The receipt schema `railiance-platform.custody-mint.v1` and the mint/destroy capability interface are proposals that Platform must confirm. + +Split from T16 on 2026-10-17 so each candidate fits one per-task budget. Platform-owned custody evidence seam: the adapter records value-free mint evidence only and never handles secret values. +Source candidate only, modelled on the T13 registration adapter +(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and +`registration-adapter.md`). Requires positive and refusal fixtures and +created-only rollback. Keeps canonical 1.1 as the default and uses no +`--allow-unimplemented` admission. Done means the candidate and its tests are +committed. Platform owner source review before pinning, and any native action, +stay separate gates and are not supplied by this task. + +## T26 — Deliver-secret adapter source candidate + +```task +id: NK-WP-0048-T26 +status: done +priority: high +state_hub_task_id: "94b3cbf0-c908-5ac6-b0db-67385c3e021c" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): deliver-secret-adapter.py, 8 tests (commit 1b60f6e). The lane `platform-secret-delivery` and the schema `netkingdom.deliver-secret.v1` are proposals that Platform must confirm. + +Split from T16 on 2026-10-17 so each candidate fits one per-task budget. Platform-owned delivery evidence seam (ESO/custody), value-free. +Source candidate only, modelled on the T13 registration adapter +(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and +`registration-adapter.md`). Requires positive and refusal fixtures and +created-only rollback. Keeps canonical 1.1 as the default and uses no +`--allow-unimplemented` admission. Done means the candidate and its tests are +committed. Platform owner source review before pinning, and any native action, +stay separate gates and are not supplied by this task. + +## T27 — Bind-env adapter source candidate + +```task +id: NK-WP-0048-T27 +status: done +priority: high +state_hub_task_id: "e56365f8-29b5-5c89-a329-8115f9f3b2a7" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): bind-env-adapter.py, 5 tests (commit 6fbce73). Binds references only. To fit the budget it does not yet wrap a full Warden `Adapter` like T13 (source-digest pin, authenticated evidence, parent-session revocation); it returns `(reference, run, rollback)`. That wrapping is a prerequisite for T30. + +Split from T16 on 2026-10-17 so each candidate fits one per-task budget. Bind the issuer Deployment env reference to the delivered secret reference, without values. +Source candidate only, modelled on the T13 registration adapter +(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and +`registration-adapter.md`). Requires positive and refusal fixtures and +created-only rollback. Keeps canonical 1.1 as the default and uses no +`--allow-unimplemented` admission. Done means the candidate and its tests are +committed. NetKingdom runtime owner source review before pinning, and any native action, +stay separate gates and are not supplied by this task. + +## T28 — Deliver-consumer-secret adapter source candidate + +```task +id: NK-WP-0048-T28 +status: done +priority: high +state_hub_task_id: "6dab281c-ea9a-5a17-a83f-e8e9a4d4484f" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): deliver-consumer-secret-adapter.py, 5 tests (commit 650c1ac). Type 1.2 only. It assumes the owner has validated ESO TokenRequest and the controller-RBAC admission; that stays an owner gate. + +Split from T16 on 2026-10-17 so each candidate fits one per-task budget. Consumer-side delivery evidence seam, value-free. +Source candidate only, modelled on the T13 registration adapter +(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and +`registration-adapter.md`). Requires positive and refusal fixtures and +created-only rollback. Keeps canonical 1.1 as the default and uses no +`--allow-unimplemented` admission. Done means the candidate and its tests are +committed. Platform and Apps owner source review before pinning, and any native action, +stay separate gates and are not supplied by this task. + +## T29 — Hand-over adapter source candidate + +```task +id: NK-WP-0048-T29 +status: done +priority: high +state_hub_task_id: "129db41d-4560-5e20-9f3b-c3c91e0a5601" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): handover-adapter.py, 8 tests (commit ec63539). The owner `apps` and lane `apps-consumer-handover` are proposals that the Apps owner must confirm. It has no reconcile path; restart handling follows T15. + +Split from T16 on 2026-10-17 so each candidate fits one per-task budget. Apps-owned consumer verification and hand-over receipt seam. +Source candidate only, modelled on the T13 registration adapter +(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and +`registration-adapter.md`). Requires positive and refusal fixtures and +created-only rollback. Keeps canonical 1.1 as the default and uses no +`--allow-unimplemented` admission. Done means the candidate and its tests are +committed. Apps owner source review before pinning, and any native action, +stay separate gates and are not supplied by this task. + +## T30 — Compose the descriptor set over the reviewed candidates + +```task +id: NK-WP-0048-T30 +status: done +depends_on: [NK-WP-0048-T24, NK-WP-0048-T25, NK-WP-0048-T26, NK-WP-0048-T27, NK-WP-0048-T28, NK-WP-0048-T29] +priority: high +state_hub_task_id: "6f6073ac-52dd-5b70-8874-6d76d4c072e4" +``` + +2026-10-17 done: coordinator review r3 (the-custodian `nk48-adapters-1.2-review/coordinator-review-r3.json`, +fd07eaa) approved all nine files at ff8f597, with R2-F1..F3 resolved, an independent +reconstruction of 117 passing and no open findings. This is a source review only: +it grants no owner admission, pin or native authority. Owner review and admission +continue under T16, which also takes the r2 observations, including the +`warden_executes: false` steps that carry execution descriptors. + +2026-10-17 coordinator review r2 (the-custodian `nk48-adapters-1.2-review/coordinator-review-r2.json`) +approved five files and rejected three, plus the artifact. The fixes are merged in +2ee4ed0: R2-F1, mint-secret revokes the session and the broker grant +independently (eeed8e1); R2-F2, deliver-secret binds to the admitted case and +derives its Secret and custody path, with no caller reference (640292c); R2-F3, +bind-env pins its five-file closure, and a new closure-completeness test checks +every adapter (d16db30). Each regression test failed on 68f4bbb. The artifact is +regenerated, and the full suite including kernel admission passed 117. Next is +the r3 review. Open for owner review under T16: the type marks four steps +`warden_executes: false`, yet each has an execution descriptor. + +2026-10-17 founder confirmed every proposed lane and schema name for T25-T29 +as-is. The artifact records them as `names_confirmed` (by founder, 2026-10-17), +and no step is still `owner_confirmation_required`. Next: independent source +review of the seven adapters. + +2026-10-17 founder decision (supersedes the `agent` decision below): the +mint-secret actor is `human`. Coordinator review r1 (the-custodian +`docs/changes/netkingdom-target-2026-10-17/nk48-adapters-1.2-review/coordinator-review-r1.json`, +finding NK48-R1-F1) showed that unchanged kernel a45cf47 refuses every +`effect: mint` admission whose actor is not `human` ("agent mint is +forbidden"), so the `agent` descriptor could never be admitted. Adapter, tests +and composer comment updated and the artifact regenerated (mint-secret +descriptor digest now `sha256:8c40c2884f8ba15e8cb6c98f33419c395e54fce8ef33d6e1a34782cf3f1fcfa9`); +98 tests pass against a45cf47. The set needs an independent r2 review. + +2026-10-17 `test_admit_1_2_adapter_descriptors.py` runs Warden's own +`Executor.admit` on all seven composed 1.2 descriptors, with the real 1.2 +type, the 1.2 forgejo example case, the reviewed `BrowserInputs` validator and +fixture-only trust (stand-in adapters, nothing dispatched). It also pins that +an agent mint descriptor is refused ("agent mint is forbidden") and that a +wrong lane is refused for every step. Against the old `agent` revision +(582ab9f) it fails as expected. Combined suite: 102 passed on ops-warden a45cf47 +and on current ops-warden. Fixture admission is not owner admission or a pin. + +2026-10-17 founder decision: the mint-secret actor is `agent`, not `warden`. +The kernel is unchanged. The adapter, its test and the composer were updated and +the artifact regenerated; all seven steps pass `Descriptor.decode` and +`complete_executable_descriptor_set` is true (98 tests passed). The artifact +stays `not_an_admission`, with no owner review, admission or dispatch permission. + +2026-10-17 composer merged (60b41a1 via 3c62bec) with follow-up 376b44b (merged; +combined suite 98 passed). `compose-1.2-descriptors.py` loads 1.2 only through an +explicit version root and refuses partial, duplicate, unknown or mismatched maps. +`descriptor-composition-1.2.candidate.json` is a reviewable candidate, marked +`not_an_admission`, with canonical 1.1 unchanged. ensure-groups and hand-over now +carry and honour `parent-session` revocation, and the hand-over owner is +parameterised (`railiance-apps` for Forgejo). Six of the seven steps pass +`Descriptor.decode`. mint-secret is still refused: its actor `warden` is outside +the kernel's human/agent set, and Platform (with ops-warden, if the kernel should +change) must decide. Five steps carry `owner_confirmation_required` names. The +set is complete only after both. Owner review, the canon section 6 successor +revision and admission remain T16's. + +2026-10-17 prerequisites merged (65559ef; combined suite 78 passed): +ensure-groups accepts 1.2 via an explicit `version_root='1.2'` (531151b), with +1.1 still the default. bind-env is now a full Warden v1 `Adapter` (7442d21); its +kernel-level fixture tests like T13's are still missing, and its `build()` +signature changed. Composition is unblocked in source. Owner confirmation of +the proposed step, lane and schema names remains before pinning. + +2026-10-17 inputs from T24–T29 that composition must resolve: T24 accepts +only 1.1 while T28 accepts only 1.2, so add an explicit 1.2 ensure-groups path; +T27 still needs full Warden `Adapter` wrapping; the lane, schema and owner names +proposed in T25, T26 and T29 need owner confirmation before pinning. + +Split from T16 on 2026-10-17. Compose the seven-step source 1.2 descriptor set +through the existing explicit version-root selection over T13 and T24–T29. No +partial descriptor map, canonical 1.1 stays the default, and new descriptor bytes +need a reviewed successor revision under canon section 6. Owner review and +admission remain T16's. + +## T31 — Restart and lost-receipt fixtures against Warden's original-intent interface + +```task +id: NK-WP-0048-T31 +status: done +priority: high +state_hub_task_id: "ed1ccd87-89a0-5e69-885c-0eaf5ab0bb5a" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): test_registration_original_intent_reconcile.py, 8 tests (commit 74adcd7), runs against the unmodified ops-warden a45cf47. The holder is a test-only in-memory store. For real T15 support the T13 adapter's `build()` needs an injected holder/readback capability, a `reconcile_original` callback, and a `verify` that accepts reconcile results. Rollback after restart stays refused until there is an owner contract. + +Split from T15 on 2026-10-17. ops-warden published the original-intent +lookup/context interface in `src/warden/case_run.py` (`OriginalIntent`, +`Journal.original_intent`, `Adapter.reconcile_original`; commit `a45cf47`). +Build T15's restart/lost-receipt fixtures for the registration adapter against +it: the original intent is kept separate from the reconcile attempt; wrong +attempt/case/source/principal and unavailable-holder cases refuse; and a missing +or ambiguous record stays uncertain, with no add replay or undo. The interface +is Warden's own dispatch record, not owner-authenticated created-only provenance. +Selecting the actual authenticated holder stays a T15 owner gate. + + +## T32 — Restore independent agent-mint admission regression coverage + +```task +id: NK-WP-0048-T32 +status: done +priority: high +state_hub_task_id: "c6d0a27e-f6a7-5df0-95f6-3e7473d38bb5" +``` + +Completed 2026-10-17, source tests only. Extracted from T16's coordinator r5 +finding NK48-R5-F1; no earlier adapter outcome is counted again. The real 1.3 +admission test now separately checks the committed descriptor map, an in-memory +fixture map acknowledging the changed agent descriptor to reach the independent +mint prohibition, and the human descriptor under an explicit fixture agent +runtime marker. Only `sso-mfa/k8s/keycape/test_admit_1_3_type.py` changes. + +Against frozen Warden `117fec9241e7b985dfb187b992e173d0a5dc3700`, the new +suite passes 10 tests / 32 subtests. Independent mutation evidence removes only +the mint admission guard in scratch: both new guard tests fail while eight +controls pass; the old suite passed that mutant. Custodian independently reran +the canonical successor: 10 tests / 32 subtests pass. Evidence: +`the-custodian/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/` +(patch, frozen/mutant results and canonical-validation.json). No descriptor, +parameter pin, catalog, acknowledgment, kernel or native execution changed. +T16 still owns actual composition and admission; F2/F3 follow below. + +## T33 — Close the issuer-store reference finding with Platform source + +```task +id: NK-WP-0048-T33 +status: todo +priority: high +state_hub_task_id: "5ace28ff-a4cb-5647-a8f6-3cd54bb9d0a5" +``` + +Owner: Platform for the fixed store/template source and its delivery boundary; +NetKingdom for the 1.3 type reference. Split from T16's current coordinator r5 +finding NK48-R5-F2. Next action: independently review the prepared Platform +candidate and publish a concrete owner-controlled issuer store kind/name/scope +and template reference, then integrate only that exact proposed source into the +1.3 deliver-secret entry point and regenerate its composition artifact. Candidate +and current review evidence are under Custodian +`docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/` +and `nk48-r5-f2-review-r1/`. + +Completion requires exact committed source, a passing independent source review +and unchanged type parameter bytes. No consumer store may be inferred as issuer +store, and no source declaration supplies owner assent, installed ESO support, +CCR/grant changes or a real delivery capability. Those remain existing +RPF-1.3-T4 and T16 admission/native requirements. + +## T34 — Close the consumer configuration inverse finding + +```task +id: NK-WP-0048-T34 +status: todo +priority: high +state_hub_task_id: "37f43c40-d014-544e-aa0b-5219dd4a6b93" +``` + +Owner: Apps for consumer configuration and inverse; NetKingdom for exact type +placement. Split from T16's coordinator r5 finding NK48-R5-F3. Next action: +independently review and implement the owner transaction/inverse through the +existing RAPPS-WP-0016-T05 path, with authenticated durable before/after release +provenance, exclusive serialization of all release writers and a fresh authorized +attended session for every inverse attempt after the hand-over session is revoked. +The inverse must remain reachable for public clients, confidential NO_CHANGE +secret delivery and verification failure before a hand-over receipt exists. + +Completion requires real owner configuration/inverse source bindings and +independent regressions, then exact reviewed 1.3 entry/rollback text and composed +artifact. A synthetic wrapper with an injected backend alone is insufficient. +Keep hand-over receipt rollback receipt-only and secret deletion created-only; +preexisting or drifted resources must refuse unsafe inverse. Candidate evidence: +Custodian `docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/` +and `nk48-r5-f3-review-r1/`. Source review, owner acknowledgment and native proof +remain distinct. T16's final composition/admission and T06's native first case +are not completed by this refinement. + +## T35 — Align source admission fixtures with the authenticated catalog and attended-session API + +```task +id: NK-WP-0048-T35 +status: done +priority: high +state_hub_task_id: "cf1d6ab3-846e-53ea-8575-d6347b6f4769" +``` + +Completed 2026-10-17, source fixture compatibility only. Distinct from T32's +independent agent-mint regression repair: both remaining opaque catalog byte +placeholders now use one well-formed synthetic catalog whose exact bytes are +bound by the fixture evidence verifier. The fixture mirrors the six relevant +Warden lane shapes; four draft statuses are hypothetically active, while both +Apps establishing login references remain unresolved. No real catalog entry, +owner login choice or production parameter pin changes. + +Against the reviewed mode-aware Warden source now integrated at +`ae7bc73b4f4bac22454144959b870d161498e0af`, scratch validation passes 31 tests +and 53 subtests. Five resolvable 1.3 descriptors conditionally admit under +fixture-only acknowledgment and pinning; both Apps attended steps refuse before +execution. Changed catalog bytes refuse, all independent mint-guard tests still +reach their intended guard, and the registration fixture's actual kernel +run/rollback and uncertain-result paths pass with synthetic owner callbacks. +The registration fixture explicitly aligns its in-memory proposed type lane +with its current descriptor; canonical 1.1 is unchanged. + +The same tests against predecessor `117fec9241e7b985dfb187b992e173d0a5dc3700` +pass 31 tests and 51 subtests, with only the two new Apps refusal assertions +failing because that predecessor lacks the attended-session catalog check. +Tests target the reviewed current API; the parser or Apps gate is not weakened +for predecessor compatibility. Evidence and root source review: +`the-custodian/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-update-r1/` +and `nk48-catalog-fixture-publication-r1/`. Canonical validation/publication +receipts belong in the latter packet when performed. This records source +fixture work, not native T03, owner assent, exact-type acknowledgment, PIN-1, +composition/admission completion under T16 or the native first case under T06. +T33 and T34 retain their existing issuer-store and consumer-inverse work. diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/workplan.candidate.md b/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/workplan.candidate.md new file mode 100644 index 0000000..82f67d2 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/workplan.candidate.md @@ -0,0 +1,1647 @@ +--- +id: NK-WP-0048 +type: workplan +title: "Establish SecurityCaseTypes so that known security cases are light" +domain: infotech +repo: net-kingdom +status: active +flavor: implementation +owner: claude-code +topic_slug: netkingdom +created: "2026-10-08" +updated: "2026-10-17" +depends_on: [] +related_repos: + - ops-mason + - ops-warden + - key-cape + - railiance-platform + - informed-decision +state_hub_workstream_id: "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" +--- + +# NK-WP-0048 - Establish SecurityCaseTypes so that known security cases are light + +## Why + +The founder's direction (2026-10-08): setting up a security integration of a +kind that has been done before should be easy, meaning accept the case, then +ops-mason arranges it and ops-warden follows through. Setting up a new kind +needs ample documentation and tests and an informed-decision, because its shape +is part of the security architecture. + +Today every integration is hand-built: per-client helper scripts, per-lane +custody, founder checklists. The trigger was KEY-WP-0038 (Forgejo as an OIDC +client), whose next step was going to be yet another hand-written rollout +script. + +This plan continues the pattern work of NK-WP-0008 (pattern infospace), +NK-WP-0009 (tutorials) and NK-WP-0030 (scenario composition). It turns patterns +into **SecurityCaseTypes** that ops-mason and ops-warden can execute. Canon: +`canon/standards/security-case-types_v0.1.md` (proposed). + +Companion plans: ops-mason `MASON-WP-0010` (materialization) and ops-warden +`WARDEN-WP-0044` (follow-through). + +## Tasks + +```task +id: NK-WP-0048-T01 +status: done +priority: high +state_hub_task_id: "930ac3e9-20ec-5473-98cd-3fe8fddb730f" +``` + +### T01 — Accept the terminology and the canon + +Founder review of `canon/standards/security-case-types_v0.1.md`, including its +section 10 open questions. On acceptance the canon status goes from `proposed` +to `accepted`. + +2026-10-08: accepted by the founder (in session). Canon status `accepted`; +section 10's open questions are carried forward to the first type (T04, T05). + +```task +id: NK-WP-0048-T02 +status: done +priority: high +state_hub_task_id: "19c25d96-a55c-58f0-9168-3fdbc4955fc2" +``` + +### T02 — Inventory the de-facto types already built by hand + +Read-only. List every security integration built so far and group them by shape. +Known starting points: + +- browser OIDC relying parties on key-cape: informed-decision-approver, + coulomb-social, user-engine portal, openbao-admin, and Forgejo (pending) +- confidential service clients with `client_credentials`: secrets-engine + approval, requester and openbao clients, approval-engine operator +- workload KV read lanes on OpenBao: the railiance-platform credential lane + designs and CCRs, for example `railiance-backup-offsite-lane` +- short-lived SSH certificates through ops-warden (`warden sign`) +- tunnelled operator access (ops-bridge) + +For each: the repositories involved, the hand-built steps, attended human steps, +custody paths, verification evidence, and rollback (if any). Output +`docs/security-case-type-inventory.md`, ranking candidate types by how often the +shape recurs and how much manual work each instance costs today. + +2026-10-08: done, `docs/security-case-type-inventory.md`. 36 of 37 CCRs are +already `request_type: workload-kv-read`, a de-facto type reviewed each time as +if it were new. Browser clients: 5 hand-built plus Forgejo, each with its own +rollout script. Confidential service clients need a registration plus two or +three KV-read CCRs each. A forge read identity is needed in bulk by +ORGREF-WP-0002-T06. Recommendation: oidc-browser-relying-party first (unblocks +the forge), workload-kv-read second (largest payoff), then forge-read-identity. + +```task +id: NK-WP-0048-T03 +status: done +depends_on: [NK-WP-0048-T01] +priority: high +state_hub_task_id: "e487975b-73b1-516c-971d-da4cbbb93063" +``` + +### T03 — Schema, template and validator + +- `canon/schemas/security-case-type_v0.1.schema.json` and + `security-case_v0.1.schema.json` +- `security-case-types/_template/` with `type.yaml`, `README.md` and tests to copy +- a validator, extending `tools/security-scenario-composer/`, that checks a type, + checks a case against its type, and renders the case into the composer's + deterministic plan (still `execution.permitted: false`) + +Acceptance: the validator rejects missing sections, secret-shaped parameter +values, unpinned providers and cases against non-`implemented` types, with tests +for each. + +2026-10-08: done. `canon/schemas/security-case-type_v0.1.schema.json` and +`security-case_v0.1.schema.json`; `tools/security-case-types/security_case_validator.py` +(`validate-type`, `validate-case`, `render`, reusing the scenario composer); +`security-case-types/_template/` (a valid, runnable example type over +`net-kingdom.local-identity`, with README sections, an example case and tests that +travel with every copy). 31 tests pass, including the composer's existing suite. + +```task +id: NK-WP-0048-T04 +status: done +depends_on: [NK-WP-0048-T02, NK-WP-0048-T03] +priority: high +state_hub_task_id: "2fffe112-7d55-5680-9952-78d74f44f87f" +``` + +### T04 — Define the first type: browser OIDC relying party + +`security-case-types/oidc-browser-relying-party/`, version 1.0: + +- parameters: client id, display name, exact redirect URIs, scopes, client type + (confidential or public), MFA pin, the groups the consuming service maps, + custody path for the secret (confidential only), consuming service and its + owner +- composition: key-cape client registration (key-cape declares it as a playbook + capability), secret minting into custody (railiance-platform), LLDAP group + existence and membership (net-kingdom identity-provisioner), handover to the + consuming service +- materialization (ops-mason) and follow-through (ops-warden) sections +- verification: discovery lists the redirect URI; a test sign-in returns the + expected claims for one human; anonymous access to the custody path is denied +- rollback: remove exactly this client and its custody path +- README: threat model, MFA rationale, why redirect URIs are exact, which steps + still need an attended login and how that is minimised +- reference instances: informed-decision-approver, coulomb-social, user-engine + portal, openbao-admin + +Acceptance: complete per canon section 3, tests green, status `proposed`. + +2026-10-08: done. `security-case-types/oidc-browser-relying-party/` 1.0 (proposed): +`type.yaml` with 12 parameters (the custody reference is required only for +confidential clients), composition over key-cape's new +`key-cape.oidc-client-registration` declaration (contract PASS, `maturity: +planned`), materialization and follow-through, verification and rollback, all +conditional on client type where it matters. One attended step, +`register-client`, justified by the live registry being a Kubernetes Secret. +README with threat model, alternatives, an honest fit table (portal and vergabe +fit; Forgejo is the first confidential case; openbao-admin, coulomb.social and +informed-decision-approver do not fit 1.0), and known limits. Examples: Forgejo +(checked against the accepted request) and a public portal. The validator gained +`when` / `required_when` conditions and provider discovery. 43 tests pass. +ops-mason's and ops-warden's sections are drafted in the type. Their owners +review them in MASON-WP-0010-T01 and WARDEN-WP-0044-T01 before acknowledgment +(T05). + +```task +id: NK-WP-0048-T05 +status: done +depends_on: [NK-WP-0048-T04, MASON-WP-0010-T01, WARDEN-WP-0044-T01] +priority: high +state_hub_task_id: "3d987d3d-0797-538b-86e4-51185a327ae2" +``` + +### T05 — Acknowledge the type through informed-decision + +Prepare the Decision Memo from the type's README and `type.yaml` and submit it +through informed-decision's production intake. On acknowledgment the type +moves to `acknowledged`. + +2026-10-08: Decision Memo prepared. Hub decision `e418263e-a71f-4f01-a970-c87c9e187c09`. +`security-case-types/oidc-browser-relying-party/decision/`: `packet.md` (question, +one act, what acknowledging does not do, the shape, alternatives, attended +steps, limits, evidence), `build_intake.py` and the built `intake.json` (memo +class `decision-record`, review group `net-kingdom-admins`, resolves the hub +decision on answer). The envelope passes informed-decision's own +`validate_submission`. Queuing it is an attended operator step, a write inside +the production dispatcher pod: + + kubectl -n state-hub exec -i deploy/state-hub-memo-dispatch -- \ + python -m api.services.memo_dispatch --queue /data/private enqueue --envelope /dev/stdin \ + < security-case-types/oidc-browser-relying-party/decision/intake.json + +The answer then appears for review in informed-decision. + +2026-10-08: queued by the founder (`pending`, `queued`) after one refusal: the +first envelope's idempotency key was not State Hub's stable +`state-hub..` form, and the builder now validates against both +sides. The dispatcher delivered it to informed-decision (ledger 7 → 8 +delivered). T05 waits for the founder's answer in informed-decision, which +resolves hub decision `e418263e`. + +2026-10-08: ops-mason's review (MASON-WP-0010-T01) folded into the type, still +1.0 `proposed`: `secret_ref` is no longer a parameter, the type derives +`platform/workloads/keycape/{client_id}`; policies are +`workload-kv-read-keycape-{client_id}-eso` and +`workload-kv-read-keycape-{client_id}-{consuming_service}`, each with a templated +body (`read` on that one data path); new `openbao_role` +`external-secrets-keycape-{client_id}` (Kubernetes auth, ESO service account, +15m, no default policy); catalog entry `{consuming_service}-keycape-client`; +the two rollback steps on the KV path collapse into one metadata delete. The +schema and validator now require a `body` on policy and role items. 105 tests +pass. Two consequences: the delivered memo describes the type before these +changes (a case-set custody reference, two differently named policies, no ESO +role), so the founder answers on a superseded shape unless the memo is +re-sent; and Forgejo's custody path moves from the accepted request's proposed +`platform/workloads/forgejo/netkingdom-oidc-client` to +`platform/workloads/keycape/forgejo`, which key-cape (KEY-WP-0038) and +railiance-apps (RAPPS-WP-0016-T02) need to adopt in T06. ops-warden's review +(WARDEN-WP-0044-T01) is still in progress. + +2026-10-08: revised memo `nk-wp-0048-t05-oidc-browser-relying-party-r2` built +(`packet.md`, `intake.json`, packet `sha256:af6435e2…`); it states that it +supersedes the first memo and passes informed-decision's and the dispatcher's +validation. It needs a new memo id because the dispatcher refuses new content +under a delivered idempotency key (`idempotency_conflict`). Queuing it is the +same attended step as above. Both memos serve hub decision `e418263e`; the +first answer resolves it, so the first memo must not stay open: an accept on it +would acknowledge the superseded shape, and a decline would resolve the +decision against r2. Close it with `return` (a reviewer verb that requests a +new revision and does not resolve the hub decision; only accept and decline +do), commenting that r2 supersedes it, then answer r2. `withdraw` exists in +the vocabulary but is not offered to reviewers, and lifecycle `revise` only +applies to lifecycle-registered memos, not intake-delivered ones. + +2026-10-09: the founder reports r2 enqueued and the first memo returned. At +close, hub decision `e418263e` still showed `open` (not verified whether r2 +was answered or the answer is still in transit; the dispatcher ledger was not +read). ops-warden's review (WARDEN-WP-0044-T01, ops-warden +`history/2026-10-08-oidc-rp-follow-through-review.md`, d470f2e) arrived at +21:25, before r2 was built, and is not in r2. Required before WARDEN-WP-0044-T02: +R1 a per-step `lane` (missing: identity-provisioner caller credential, +railiance-platform broker grant), R2 canon meaning of `warden_executes: true` +(caller proxy, caller identity, no standing ops-warden token; ADR-0002), R3 a +per-step `value_handling`. Refinements R4 (one type-level catalog lane instead +of a per-case entry, which conflicts with ops-mason's item 5), R5 (ESO Ready gate +before bind-env), R6 (exact accepted memberships), R7 (mfa-pinned and +secret-delivered checks). Next: if r2 was accepted, fold R1-R3 and R5-R7 in as +a follow-up and settle R4 between ops-warden and ops-mason before T06; if not, +hold r2, fold them in and build r3. + key-cape and railiance-apps were told about Forgejo's new custody +path. + +2026-10-09: r2 was not answered (hub decision `e418263e` open; dispatcher 9 +delivered), so r2 is held and r3 replaces it. ops-warden withdrew R4 for R4′ +(message a66a9204): one catalog entry per case with its body templated in the +type. Folded in: + +- Canon section 3: a narrow meaning of `warden_executes: true` (caller proxy, + caller identity, no standing credential; ops-warden ADR-0002), plus per-step + `lane`, `value_handling` and an optional `ready_check`. A catalog entry body is + now required, like policy and role bodies. +- Schema and validator enforce all of these. +- The type: lanes `identity-provisioner-groups` and + `keycape-client-secret-generate` (both new) and `key-cape-oidc-login`; + `mint-secret` is `generated_in_custody`; a Ready gate on `deliver-secret`; + `ensure-groups` adds exactly the accepted case's memberships; new checks + `secret-delivered` and `mfa-pinned`; the catalog-entry body follows R4′. +- 110 tests pass. + +Memo r3 (`nk-wp-0048-t05-oidc-browser-relying-party-r3`, packet +`sha256:726dfc01…`) passes informed-decision's and the dispatcher's validation. +The founder queues it with the same command, returns r2 with a comment that r3 +supersedes it, then answers r3. + +Prerequisites for T06, not for T05: +- the two new lanes, each through its owner; +- a founder-approved rollout of an identity-provisioner image containing + 630ebfd (the deployed one is main-6c4fcaf from 2026-09-24, without + `/v1/groups/ensure`). + +ops-mason's request to create LLDAP group `fluid-telegram-operators` (message +259ec26d) was answered: not possible from here by that path; the founder creates +it in the LLDAP admin UI. + +2026-10-09: ops-warden finished its side of WARDEN-WP-0044-T01 (message +`58d33f40`, commit 0e7ceb8): the revised type conforms to `warden case +check-type`, with no further changes asked of the type. This acknowledges that +review. Its later addendum R8 (message `af274783`, ops-warden +`history/2026-10-08-oidc-rp-follow-through-review.md`) showed that lane +`identity-provisioner-groups` cannot exist as declared: the provisioner's only +HTTP credential is user-engine's runtime token, and its NetworkPolicy admits +only user-engine pods, so a caller-proxy lane would act as user-engine. Taken +option 1 (ops-warden's recommendation): `ensure-groups` and its rollback are +attended, in the `register-client` session on `key-cape-oidc-login`, run inside +the provisioner pod; no token is read. Option 2 (an operator credential of its +own on the provisioner) stays open for a later version. `warden case +check-type` now reports only `keycape-client-secret-generate` +(railiance-platform) as missing. 89 tests pass. + +Hub decision `e418263e` was still open, so memo r4 +(`nk-wp-0048-t05-oidc-browser-relying-party-r4`, packet `sha256:452ea421…`) +replaces r3; it passes informed-decision's and the dispatcher's validation. The +founder queues it with the same command, returns r2/r3 if still open with a +comment that r4 supersedes them, then answers r4. + +Prerequisites for T06 are now: lane `keycape-client-secret-generate` +(railiance-platform), and a founder-approved rollout of an identity-provisioner +image containing 630ebfd. + +2026-10-09: done. The founder queued r4 (`pending`, `queued`), returned r3 as +superseded and accepted r4 in informed-decision. informed-decision's hub +write-back is off in production (`INFD_HUB_WRITEBACK` unset), so net-kingdom +resolved hub decision `e418263e` from the founder's report, naming memo r4 in +the rationale. The type is `acknowledged`; it becomes `implemented` with T06. + +```task +id: NK-WP-0048-T06 +status: wait +depends_on: [NK-WP-0048-T05, NK-WP-0048-T08, NK-WP-0048-T09, NK-WP-0048-T12, NK-WP-0048-T13, NK-WP-0048-T14, NK-WP-0048-T15, NK-WP-0048-T16, NK-WP-0048-T17, NK-WP-0048-T18, NK-WP-0048-T20, MASON-WP-0010-T03, WARDEN-WP-0044-T03] +blocking_reason: "Type1.2 architecture has a confirmed source-only founder acknowledgment; canonical1.1 remains unchanged/default and the1.2 reference case stays proposed. Needs explicit version selection/reference-source integration without normal admission bypass, closed owner adapters plus global/native receipt verifier (WARDEN-WP-0044-T03), independently verified six-object structural materialization and CCR42 claims/created-only provenance, exact broker/case/source reconciliation, then separately authorized native mint/delivery and attended registry/consumer verification. Source acknowledgment, a bundle or local tests do not prove implemented/materialized/live status." +priority: high +state_hub_task_id: "d9b26bcf-a7c6-59a2-92a0-f913d76a203c" +``` + +### T06 — First KnownSecurityCase: Forgejo + +Write `security-cases/forgejo-netkingdom-oidc.yaml` from the parameters the +founder accepted in KEY-WP-0038-T01 (`key-cape/docs/forgejo-oidc-client-request.yaml`), +so that acceptance carries over as the case acceptance. ops-mason materializes it; +ops-warden follows through and verifies it. The type becomes `implemented` once +this reference case is live. + +This replaces the hand-built path proposed for KEY-WP-0038-T02. + +2026-10-09: case written, `security-cases/forgejo-netkingdom-oidc.yaml`, status +`accepted`: the example's parameters plus the founder's 2026-10-08 acceptance of +the key-cape request (owner and founder), with the custody-path derivation +covered by the type's acknowledgment (memo r4). It validates and renders (plan +`execution.permitted: false`; three attended steps on `key-cape-oidc-login`); +it needs `--allow-unimplemented` until it is live, since it is the case that +makes the type `implemented`. A test keeps it equal to the type's example. +Still open before it runs: ops-mason renders and builds it (MASON-WP-0010-T02, +T03, unblocked by T05), ops-warden plans and runs it (WARDEN-WP-0044-T02, T03), +railiance-platform's `keycape-client-secret-generate` lane (asked by ops-mason +`789feb39` and ops-warden `d9f889b1`, both unread), and a founder-approved +identity-provisioner rollout containing 630ebfd. + +2026-10-09: identity-provisioner rolled out, founder-approved in session: +`main-630ebfd` (`sha256:bafb4860…`, built by the image workflow) replaces +`main-6c4fcaf` (`sha256:ffacd5d7…`; only change: 630ebfd, additive). Applied +with `kubectl set image` on the Deployment only (generation 17 → 18, 1/1 Ready); +the file's Service, NetworkPolicies and Ingress were not reapplied. Verified in +the pod: `ensure_service_groups` and `revoke_service_groups` present, `/readyz` +200; no token used, no group changed. Rollback: `kubectl -n sso set image +deploy/identity-provisioner provisioner=forgejo.coulomb.social/coulomb/identity-provisioner@sha256:ffacd5d790a5b7800e0535f3aa95b7de52c7a68e4f575b9192c3e1622fa672c2`. +The pin in `sso-mfa/k8s/identity-provisioner/deployment.yaml` follows. + +2026-10-09: ops-warden closed WARDEN-WP-0044-T01 and shipped `warden case plan` +(T02, message `06303d1d`); the accepted Forgejo case is refused until ops-mason +materializes it, and a materialized copy plans cleanly once +`keycape-client-secret-generate` exists. Its observation that `bind-env` carries +a literal `KEYCAPE__CLIENT_SECRET`: the validator now renders a +`{param:env}` format spec (upper case, `-` as `_`, as `keycape-client.py` +derives the name) and rejects unknown specs, and follow-through and rollback +text is now checked for unknown placeholders too (patch-level, canon section 6). +The type keeps the literal until ops-warden's planner (`text.format`) renders the +spec as well; asked of ops-warden. 53 tests pass. + +2026-10-10: ops-warden rendered `{param:env}` in `warden case plan` +(23ba584, message `c1df14b7`; other specs and `!conversion` refused). The type +adopts it as a 1.0 patch (canon section 6): `bind-env` and its rollback now read +`KEYCAPE_{client_id:env}_CLIENT_SECRET`. ops-mason reports the Forgejo case +rendered and built (message `1ebddae9`; CCR drafts handed to railiance-platform), +so T06 now waits on the platform apply, the mint-secret lane and the attended +register-client session. + +2026-10-10: key-cape adopted the type's custody path (key-cape ab5cccd, message +`88a4559c`): `docs/forgejo-oidc-client-request.yaml` and KEY-WP-0038-T02 name +`platform/workloads/keycape/forgejo`, field `CLIENT_SECRET`, delivered as +`sso/keycape-forgejo-client` through `external-secrets-keycape-forgejo`, and the +`forgejo/netkingdom-oidc-client` proposal is withdrawn. This matches the type and +the Forgejo example; no change needed here. + +2026-10-10: key-cape's type-composition review (key-cape 6038483, message +`8e5ed5f6`) found the composition sound with two blocking findings, taken as a +1.0 patch (stricter validation and documentation, canon section 6): +R1, redirect URIs now refuse `?` and `#` in the type pattern and in +`keycape-client.py`'s `REDIRECT` (KeyCape refuses `?` at startup and the single +replica would stop); R2, `allowed_scopes` always includes `groups`, the renderer +refuses `access_groups` without it, and `access_groups` now says the consumer +enforces the gate because KeyCape has none; R3, `mfa_required` "false" means the +per-user privacyIDEA policy; R4, register-client's justification records the +restart for every client, the quiet window, and that the rendered registry must +pass KeyCape's client validation before it. The accepted Forgejo case and both +examples still validate; 116 tests pass. +Open, needs a decision: R2's verification step that the consumer enforces the +group gate is a new verification step, hence 1.1 (minor), and the validator pins +cases to the exact type version, so the accepted 1.0 Forgejo case would need +re-pinning. Also open with key-cape: a KeyCape entry point that validates a +rendered client registry without starting the server; until then +`keycape-client.py` mirrors the redirect rule. + +2026-10-10: founder go for 1.1, so no 1.0 run overlaps. The type adds +verification `consumer-enforces-groups`, and hand-over now requires the consumer +to enforce `access_groups` (minor, canon section 6; risk class unchanged, no new +acknowledgment; history in the type README). The accepted Forgejo case and both +examples are re-pinned to `oidc-browser-relying-party@1.1` before any apply; the +acknowledgment intake keeps `@1.0` as the record of what was acknowledged. +ops-mason's build and ops-warden's case-ref digest predate this and need a +re-render. + +2026-10-10: ops-mason re-rendered the Forgejo case from `@1.1` (message +`22c6bba6`; plan digest `af1f4f81…`, no collisions) and rebuilt the bundle. The +rendered OpenBao objects are identical to 1.0, so the already applied +CCR-2026-0041 stands; 1.1 changes only consumer-side steps. key-cape shipped +`keycape check-config` (c528daa, message `a7b0aeff`): KeyCape's startup checks +without secret values, including the new duplicate-clientId rule. +`keycape-client.py --check-config ` now runs it on the live and the +proposed registry, fed on stdin, and apply refuses a proposed registry that +fails; register-client's entry point names the option (1.1 patch). The deployed +image predates c528daa, so the binary is built from key-cape source for now. +Before the next KeyCape rollout (Forgejo register-client or the CCR-2026-0018 +window), the attended operator runs `inspect --check-config` so the live +registry is checked too. + +```task +id: NK-WP-0048-T07 +status: wait +depends_on: [NK-WP-0048-T06] +blocking_reason: "The next types follow once the first one is proven end to end." +priority: medium +state_hub_task_id: "c3c9c3ae-4f23-5ba7-9e6f-e63bcf29b9f7" +``` + +### T07 — Queue the next types + +From the T02 ranking, open the next type drafts (likely the confidential service +client and the workload KV read lane), each to be acknowledged on its own. +Record which existing hand-built instances get adopted as cases, without +rewriting them in place. + +```task +id: NK-WP-0048-T08 +status: done +priority: high +state_hub_task_id: "8e519082-dc8b-5e46-b660-03d03e55dcf6" +``` + +### T08 — identity-provisioner: ensure service-access groups + +The type's `ensure-groups` step calls `identity-provisioner groups ensure --groups + --members `. The provisioner already has idempotent +`_ensure_group` and `_add_group`, but exposes only tenant-scoped operations. Add +the general operation (create if absent, add the listed memberships, never +remove others), its rollback (remove only this case's memberships), tests, and +the ops-warden route for its service identity. + +2026-10-08: done. `identity-provisioner/provisioner.py` gained +`ensure_service_groups` (`POST /v1/groups/ensure`) and `revoke_service_groups` +(`POST /v1/groups/revoke`), behind the existing service-token check. Names +matching `tenant*`, `netkingdom-*` and `lldap_*` are reserved; members must +already exist; everything is validated before any change; memberships are read +back before success; nothing else is ever removed; groups are deleted only when +asked and empty. 7 new tests (39 in the provisioner suite). The type's +`ensure-groups` step and its rollback now name these endpoints. The ops-warden +route for the provisioner's service token belongs to WARDEN-WP-0044-T01. + +```task +id: NK-WP-0048-T09 +status: done +priority: high +state_hub_task_id: "0ee12dfc-8260-5038-bd39-cc7fb4113be0" +``` + +### T09 — The `keycape-client` entry point and its declaration + +Planned first as key-cape KEY-WP-0038-T04. Moved here because the machinery a +live registration needs already lives in net-kingdom, where KeyCape is deployed +(`sso-mfa/k8s/keycape/`): the contained kubectl, the cluster-identity pin and the +issuer pin. A copy in key-cape would drift. + +2026-10-08: done. `sso-mfa/k8s/keycape/keycape-client.py` derives the KeyCape +client record from a case (`client-from-case`). It adds or removes exactly one +client with the existing safeguards (byte-preserving merge, exact match or +refuse, shared-reference refusal, compare-and-swap, read-back, contained errors), +requires the Deployment env binding first for confidential clients, and requires +the case acceptance record for `apply` instead of a per-client founder decision +id. 15 tests, including a regression showing that the vergabe case reproduces +its hand-built registration exactly; all 71 helper tests pass. Declaration +`capabilities/playbooks/net-kingdom.keycape-client-registration.yaml` (contract +PASS, `maturity: implemented`) replaces key-cape's draft. The type now pins it, +and for confidential clients adds `deliver-secret` (delivery from custody into +the sso namespace) and `bind-env` (Deployment env), attended in the same session +as `register-client`. The per-client helpers stay until their clients are +adopted as cases. + +2026-10-10 — NetKingdom owner review of the execution seam, source only: +ops-warden/docs/security-case-execution-proposal.md SHA256 78ab1439e4c5beb205aeccf15b1b2e706511be55ab195c5a3aa682f326c7c77c +was reviewed against INTENT, SecurityCase canon, acknowledged type1.1, current +case and owner source. docs/security-case-execution-owner-review.md supports +the closed adapter/receipt direction with explicit versioned schema, structural +materialization provenance, unchanged caller/session boundaries, safe CAS +reconciliation and exact-created-object rollback requirements. This is owner +review, not approval. Supporting file digests are in the adjacent sources JSON. + +Current source exposes a circular wait: ops-mason T03 waits on mint while Warden +and the broker refuse a non-materialized case. Structural acceptance therefore +needs its own native pinned receipt before mint, not a successful mint before +materialized. The platform broker additionally pins type1.0 while Forgejo is +accepted at1.1; platform-owner alignment and tests are required, not an unchecked +version bypass. T06 remains wait. No case/type lifecycle status, grant, authority +or live runtime changed by this review. A proposed informed-decision would +adopt the source contract only; native materialization and execution acceptance +stay distinct. + +Final proposal re-review: staged Warden proposal SHA256 78ab1439e4c5beb205aeccf15b1b2e706511be55ab195c5a3aa682f326c7c77c +adds only the explicitly local reducer appendix, not an accepted wire protocol +or executor. The exact-final review and sources JSON supersede the earlier +1035f049... proposal review. Platform staged the exact type1.1 broker correction +with ten fake-backend subprocess checks (script SHA256 +9576cd211cfc06526ec4cc9bd0a5b06f0f59287a68931bb2d6e3592a951ddc7d). +The next source work is the versioned closed adapter/receipt/materialization +contract, not another relaxation of the broker's status gate. Native work still +requires installed broker provenance, structural reader receipts, exact new +materialized-case bytes, caller grant/session revocation and verified execution. +No installed/native acceptance is inferred from that staged correction. + +2026-10-11 — new source-only type1.2 architecture acknowledgment: +Confirmed native founder ACCEPT of infd-20261011-forgejo-type12-source-r1@1 +at06:47:39Z, approval8e9332c8-754a-420c-b8b7-445cb4fe6765. Exact frozen packet +0b377d647ca748cd03c92f9792396412048055d4bde9f7bb06ff6d413f7acd68 and submission +585cee731de30d2b37883a57abfd9eb32f023ee694e8d911989109d5caf80569; native receipt +retained in docs/evidence/2026-10-11-forgejo-type12-source-acknowledgment/. +The complete acknowledged1.2 source package is isolated under +security-case-type-revisions/1.2/oidc-browser-relying-party; default loader and +current accepted1.1 case remain untouched because load_type is single-version. +The new proposed1.2 case is NOT accepted/materialized/live and has no acceptance +fields. Actual reference-migration design approval is a separate Apps owner +receipt; no1.1 approval is inherited. Source-recording review/test proof and exact +next gates are in docs/forgejo-type12-source-acknowledgment-followthrough.md. +T06 remains wait and T07 remains its existing dependent task; type is NOT +implemented and no first-case/native grant or broader workplan finish is asserted. + +2026-10-11 — bounded explicit version selection source preparation for T06: +The validator now offers optional exact version_roots/--version-root VERSION=PATH +selection with duplicate/mismatch/traversal/symlink refusal and no fallback. The +default1.1 source and historicalacceptedcase hashes/ordinaryrefusal/preparation +render stayunchanged; actual1.1 is acknowledged, not implemented. Acknowledged1.2 +ordinaryadmission also refuses; optionalpreparation remains nonexecuting.43local +validatorchecks pass and original1.1 CLIrenderbytes match exactly. Makerproof: +docs/evidence/2026-10-11-explicit-type-version-selection/source-integration-validation.json. +FormalNetownerreview/reference-source integration and all existing native adapter, +provenance/broker/materialization/attendedproof gates remain; T06 stayswait, no +new residual or firstcase/typeimplementation/native grant is inferred. + + +2026-10-16 — isolated source preparation for T06 (canonical integration pending): +Added `check-evidence` to the existing validator CLI. It reuses exact version +selection and ordinary type/case/render admission, checks actual Mason structural +request source/inventory/projection pins and reads actual Warden journal rows +read-only. Successful rows require exact type-declared descriptors and attempt/ +revocation fingerprints. Existing acknowledged types without machine descriptors +remain plan-only. The compatibility result never claims authenticated native +completion: Request and journal fingerprints are not portable native evidence. +Owner authentication/status binding and actual native gates remain unresolved; +T06/T07 statuses and both type/case lifecycles remain unchanged. No dispatch, +credential access or authority expansion is introduced. See tool README for the +existing owner protocol boundaries and source integration tests. + +Concrete next Warden owner integration: current `Executor.admit` refuses every +case parameter whose sensitivity is not public/operational, so browser +`redirect_uris`, `allowed_scopes`, `client_type`, `mfa_required` (and 1.2 +`consumer_namespace`) fail after otherwise prepared admission. The sensitive +classification is a security-change review level, not secret content. The +Warden owner must validate through NetKingdom's existing exact-version case/type +validator, then project only descriptor-declared safe target metadata; do not +remove the credential/value refusal or copy complete parameters into journals. +Actual owner descriptor acknowledgment and independent evidence admission remain +prerequisites, with normal admission still refusing unimplemented types. + + +## T10 — Source compatibility checker for Mason and Warden evidence + +```task +id: NK-WP-0048-T10 +status: done +priority: high +state_hub_task_id: "4b06ca97-c1e4-5680-bf22-ab75ab1646a4" +``` + +Completed 2026-10-16 in published source `c31d21ba`. The read-only +`check-evidence` command checks the actual Mason structural request and Warden +journal source protocols against an exact version/render. Independent source +review passed 103 tests at that checkpoint. It always reports native completion +false and execution prohibited. Fixtures and local journal fingerprints do not +provide authenticated owner/native evidence. This records completed source scope +previously described under T06; it does not duplicate native acceptance credit. + +## T11 — Validate the optional machine-descriptor pin contract + +```task +id: NK-WP-0048-T11 +status: done +priority: high +state_hub_task_id: "ef2a0119-f59e-5e45-9519-115da9df7b85" +``` + +Completed 2026-10-16. Reproduced the JSON schema rejecting a valid +`execution_descriptors` map while the Python validator accepted malformed maps. +The schema now supports the existing Warden optional exact-byte pin map; semantic +validation binds keys to declared steps, bounds the map and checks digest shape. +The direct evidence API refuses malformed maps before accessing them. Twenty-one +new checks and all 127 affected source checks passed. No current type/case bytes, +status, owner descriptors or native acceptance changed. Source evidence and +publication receipt: Custodian +`docs/changes/netkingdom-target-2026-10-16/browser-acceptance-next/`. + +## T12 — Prepare NetKingdom-owned attended step adapters + +```task +id: NK-WP-0048-T12 +status: todo +priority: high +state_hub_task_id: "7ce4eee3-cd47-55a9-bfa5-4f1835d4d6fd" +``` + +Owner: NetKingdom. Retained scope: prepare bounded attended Deployment env, +config verification and restart/readiness steps around the existing helpers. +Next action: identify exact current entry points and their revision/readback +contracts, then implement fixed source-pinned callbacks or receipt-only attended +steps. Completion requires positive/refusal checks, exact target/source pins and +uncertainty behavior without acquiring user-engine runtime identity or exposing +registry/client secrets. Existing caller/session, cluster/issuer, CAS/readback +and status-only output boundaries remain mandatory. + +2026-10-16 task refinement: the original broad adapter scope is split into T13 +(completed registration source candidate), T14 (group creation provenance), T15 +(durable registration recovery) and T16 (descriptor/artifact selection and owner +review). This preserves T12's task ID and Hub UUID while giving each remaining +outcome an owner and completion gate. T06 now names every source dependency; +its native/reference lifecycle acceptance scope remains unchanged. Completion +of any source task supplies no Warden, Platform or Apps assent and no native +execution authority. + +## T13 — Prepare the fixed registration adapter source candidate + +```task +id: NK-WP-0048-T13 +status: done +priority: high +state_hub_task_id: "b264c10b-565c-5d29-b788-03cf5fbd2883" +``` + +Completed 2026-10-16, source implementation and synthetic fixture proof only. +Added an injection-only adapter around actual `keycape-client.run`: fixed +case/principal/human/lane/source/acceptance binding, existing CAS/readback, +metadata-only owner evidence and per-attempt parent-session revocation boundary. +The descriptor source composition pins the exact checker executable digest +alongside adapter/helper source. Creation-only rollback authenticates its +creation receipt and refuses changed postcreation UID/resourceVersion; +pre-existing clients confer no rollback authority. Missing readback, evidence, +revocation or process-local creation provenance cannot become retry-safe success. + +Evidence: Custodian `docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/` +R3 candidate, explicit patch and postimages, retained R1 rejected custody finding +and R2 revision, and 36 passing source checks (16 new, 20 existing). Three checks +use actual Warden kernel admission/dispatch/rollback with published Warden +`d97652a3bb5fa733b063d71a1b74068efcccf572` producer pins unchanged. Owner trust, +descriptor/type/case and Kubernetes objects remain synthetic fixtures. This +records a completed source deliverable, not independent review, publication, +type acknowledgment, exercised procedure, identity admission or native browser +acceptance. Independent root source review: Custodian browser-adapter-next/coordinator-review-r3.json. Source integration and normal publication/synchronization receipts: Custodian browser-adapter-next/canonical-integration.json. These receipts supply no other-owner assent or native authority. + +Source publication: `0775c2bfa41af236301155bb5a69bbf088bdf823`, independently +reviewed by the Custodian coordinator before integration. The required initial +projection and ordinary UUID writeback both completed with zero failures and +zero automation errors. Generated-only brief commits were refused by the normal +hook; exact pre-existing staged/working brief bytes were restored. The separate +canonical integration receipt retains those refusals and projection evidence. +Native admission and all other-owner acceptance remain open. + +## T14 — Admit governed group rollback against native owner boundaries + +```task +id: NK-WP-0048-T14 +status: progress +depends_on: [NK-WP-0048-T20, NK-WP-0048-T21] +priority: high +state_hub_task_id: "fce95440-a25b-5f27-8ef3-a3d182099113" +``` + +Owner: NetKingdom. Next action: bind the completed T21 PostgreSQL journal to +an admitted dedicated CNPG consumer through the existing `rapp-postgres` +database-per-consumer contract. Identify the actual deployment/package and +custody owner, admitted placement/capacity and role/renewable lease/TLS target; +implement the independently retained exact content checkpoint and authoritative +tenant/case/source/principal callbacks before enabling governed group operations. +The injection-only backend and fixture callbacks do not supply production scope +or authorization. Existing service tokens, caller-supplied JSON and provenance +receipts confer no new grant. The default production endpoint remains disabled +without the trusted integration. SQLite remains local/disposable only; any +production exception requires explicit scoped governance under ADR-0015. + +2026-10-17 owner-integration refinement: T23 records the completed source/package +placement review, distinct from T21's source/disposable-SQL proof. Its dedicated +`rapp-identity-provisioner` recommendation is draft and unbound, not an accepted +package or existing repository. The full six-consumer rapp-postgres renderer +accepts the inert candidate on platform-pg-2 as its third of four declared +consumers; first-cell placement is refused as a fifth. Neither the estimated +four-consumer ceiling nor source requests establish native headroom or backup. + +Next independent owner contracts: NetKingdom/package/substrate owners accept +exact deployment/rollback fate; rapp-postgres admits the dedicated consumer, +roles, five-connection budget and actual backup/restore; Platform admits exact +renewable runtime/migration delivery and TLS/CA/SAN; network owners scope SQL, +broker and checkpoint paths without assuming a narrow policy subtracts additive +same-namespace access. Protected caller owners must provide authoritative +GroupJournalBinding construction and current authorization; an independent +existing holder must authenticate and atomically advance the exact content +checkpoint, not a counter or ordinary Audit receipt. Directory/runtime owners +must select and prove either an exclusive all-writer maintenance barrier or +provider-supported CAS/generation, including established sessions, lifecycle +cascades and remove/readd ABA. No source review establishes those owner facts. + +The default PostgreSQL activation and destructive native rollback remain refused. +Storage/lease acceptance can be proposed separately from LDAP mutation only after +exact owner source/target/custody/verification/recovery and cost/risk review; +State Hub guard approval grants none of it. Source-only integration review and +native acceptance matrix: Custodian native-storage-r1/OWNER-REVIEW.md. T14's +existing status, UUID, dependencies and native completion gate are retained. + +2026-10-17 refinement: T20 retains the historical durable SQLite source and +actual disposable LLDAP proof. T21 extracts the completed PostgreSQL source and +bounded real disposable SQL compatibility proof. T14 remains `progress` for +native CNPG admission/recovery, protected owner integration and exclusion of all +external LDAP writers. Separate source publication/projection and independent +cleanup receipts belong to coordinator follow-through; no missing native assent +is represented as received. + +Completion still requires independently reviewed/admitted ADR-0015-conforming +CNPG storage (or an explicit governed exception), backup/lost-store recovery and +migration of legacy requests; a native +writer-isolation/fencing or provider CAS/version contract that closes external +writer races and remove/re-add ABA; exact case/source/principal and current +native object bindings for protected callbacks; and actual per-attempt evidence, +revocation and readback/refusal verification through the existing owner paths. +Only independently retained confirmed creations may be undone. Every +pre-existing/shared group and membership must remain preserved; unresolved +intents never expire into retry authority. The disposable fixture supplies none +of these native custody, authorization, owner-acceptance or production claims. + +## T15 — Define authenticated recovery for lost registration process state + +```task +id: NK-WP-0048-T15 +status: wait +depends_on: [NK-WP-0048-T31] +blocking_reason: "Source candidate 061461e is merged. Waiting on the owner to select the real authenticated original-attempt holder and native readback, and on independent re-review of the changed T13 adapter source." +priority: high +state_hub_task_id: "1b825705-4848-5edc-832f-b1622cd9bdb7" +``` + +2026-10-17 source part merged (061461e via 65559ef). `build()` takes optional +paired `holder`/`observe` capabilities and installs `reconcile_original`; the T31 +fixtures now run through the adapter's own path. Rollback after restart is still +refused, and there is no add replay. This changes `keycape-registration-adapter.py`, +so the revision reviewed under Custodian `browser-adapter-next/coordinator-review-r3.json` +is superseded. The new revision has no independent review yet and must not be +pinned on the strength of the old one. + +2026-10-17: T31 fixtures done (74adcd7). What remains for T15: the owner +selects the real authenticated original-attempt holder, and the T13 adapter +gains the holder capability, `reconcile_original` and reconcile-aware `verify` +described in T31. + +Owner: NetKingdom for registration state, with Warden owning kernel evidence +admission. T22 records the completed source review and exact existing-seam gaps; +T15 retains implementation and authenticated restart/lost-receipt acceptance. +Next action: select and admit the actual owner publisher/authenticator and bounded +original-attempt readback contract for the merged holder/observe adapter seam. +Warden's original-intent persistence/interface (`a45cf475`) and additive validation +(`117fec9241e7b985dfb187b992e173d0a5dc3700`) are now published and projected. +The local Journal retains admitted nonsecret original context, validates original +actor/attempt and metadata linkage to the original running receipt, and supplies +it separately from the new reconciliation attempt. This local consistency is not +independent original-holder authorization or creation provenance; neither the +journal nor a current matching registry entry reconstructs creation authority. + +The owner contract must bind the original Invocation, case/type/descriptor/catalog/ +acceptance pins, source, principal and admitted tenant/realm provenance, separately +from the new reconciliation attempt and its independently revoked parent session. +Use an existing authenticated holder/readback; Audit Core's custody API is a +candidate only until its actual registration source/class, load-bearing emission +contract and separate scoped reader are admitted. No new broker or receipt trust +root is proposed. A missing original record, ambiguous publication, NO_CHANGE, +changed native revision or lost readback stays uncertain; no add replay or undo. + +Completion requires restart/lost-receipt fixtures against the selected actual owner +contract: independently authenticated original created-only provenance, original +exact client entry and unchanged current postcreation UID/resourceVersion before +any undo, with wrong attempt/case/source/principal and unavailable-holder refusals. +The merged T13 adapter exposes reconciliation only through paired injected +holder/observe capabilities and still refuses post-restart rollback. Existing +source review, JSON receipts and synthetic trust do not satisfy independent +holder authorization/provenance or native admission, or authorize attended recovery. +Exact remaining owner facts: Custodian native-registration-r1/original-attempt-review.json. + +2026-10-17 check (net-kingdom-worker): Warden's original-intent lookup interface +is not published — ops-warden head is `d97652a`, with no WARDEN-WP-0044-T03 +commit, and the net-kingdom inbox holds no announcement. No restart/lost-receipt +fixture or readback seam was added this session; the work is deferred behind +T16's decomposition under the same token budget. Remains todo, gated on Warden +publishing the reviewed lookup/context interface and on owner selection of the +authenticated original-attempt holder. + +2026-10-17 current-source correction: the earlier same-day `d97652a` lookup +checkpoint and T31 adapter-next-action note above are historical and superseded +by the published Warden interface/validation and merged `061461e` adapter seam. +The 54 focused Warden checks establish source behavior only. Current admission +leaves tenant/realm empty; no admitted tenant/realm provenance is inferred. +T15 remains wait for actual independently authenticated owner-holder/readback +selection, independent review of the changed adapter, and native restart/lost- +receipt acceptance. Existing task metadata, owner gates and historical reviews +are unchanged. + +## T16 — Select and review the actual machine descriptor composition + +```task +id: NK-WP-0048-T16 +status: todo +depends_on: [NK-WP-0048-T24, NK-WP-0048-T25, NK-WP-0048-T26, NK-WP-0048-T27, NK-WP-0048-T28, NK-WP-0048-T29, NK-WP-0048-T30] +priority: high +state_hub_task_id: "200bf500-5084-5ae7-be26-3033fe1f296e" +``` + +2026-10-17 coordinator review r4 approved the 1.3 candidate at 1920b13 with no +findings (the-custodian `nk48-adapters-1.2-review/coordinator-review-r4.json`). The founder chose +option A of the T16 owner-review packet (`nk48-t16-owner-review/founder-option-decision.md`): +1.3 with execution descriptors for all seven steps. Owner reviews for A are open +in the-custodian `docs/changes/netkingdom-target-2026-10-17/nk48-1.3-owner-review/` (net-kingdom, +railiance-platform, railiance-apps, ops-warden, key-cape). The owner-review packet +supersedes the r1 composition pointer: T16's exact composition is now +`sso-mfa/k8s/keycape/descriptor-composition-1.3.candidate.json`. + +2026-10-17 successor candidate prepared (source only, `proposed`): +`security-case-type-revisions/1.3/oidc-browser-relying-party/` is the 1.2 package plus +an `execution_descriptors` map of the seven composed descriptors, with examples +pinned to `@1.3` and no acknowledgment. ensure-groups, hand-over, +deliver-consumer-secret and the composer accept 1.3 (835680c, merged acda26c). +`test_admit_1_3_type.py` runs Warden's real admission against the committed 1.3 type: +the proposed type is refused; an acknowledged copy is refused because ops-warden +pins parameter fingerprints only for 1.1 and 1.2; and with only a 1.3 pin (equal to +1.2's fingerprint) all seven are admitted. This is a major change under canon +section 6. Next: r4 source review of the changed adapters and the map, then owner +reviews, including the ops-warden 1.3 pin, the mint-secret warden_executes/human +question and the owner-run steps question, then the founder's Decision Memo. + +Owner: NetKingdom for descriptor/type source and exact checker artifact; Warden +independently owns global provenance/admission. T22 records the completed seven-step +source1.2 inventory and regenerated register-client descriptor using the delivered +checker and current sealed-snapshot source; T16 retains exact owner review, +complete descriptor composition and admission. Next action: bind actual native +publisher/authenticator, original/current session verification and revocation, +executable storage/custody and config pins, then obtain the six missing fixed +owner descriptors/adapters for ensure-groups, mint-secret, deliver-secret, +bind-env, deliver-consumer-secret and hand-over. The inventory identifies each +existing owner seam, exact missing contract and created-only rollback requirement; +it is not an executable bundle. T14 and T15 remain independent prerequisites. + +Review the dedicated consumer source1.2 composition only through the existing +explicit version-root selection. Keep canonical1.1/default and its accepted case +unchanged, and the source-only acknowledged1.2 reference case proposed without +acceptance fields. New descriptor bytes require a reviewed exact successor +revision/version under canon section6; they do not inherit historical source +acknowledgment. Changed authentication/materialization/trust needs the prescribed +informed-decision acknowledgment. Do not insert a partial descriptor map or use +--allow-unimplemented preparation as ordinary case/native admission. The registry +helper reports restart_required; separately review quiet-window restart/readiness +and actual consumer verification rather than infer activation from registry CAS. +Exact composition: Custodian native-registration-r1/descriptor-composition.json. +Completion requires separate owner source review of exact machine descriptors, +artifact/source/config pins, normal type/version admission and independent +evidence-verification boundaries. Warden d97652a refreshes producer validator +pins for NetKingdom 015694ee; it does not attest T13's new adapter. Pinning any +machine descriptor into an acknowledged type is a separately reviewed source +change. Platform owns structural/mint/broker/delivery evidence; Apps owns actual +consumer login/group enforcement. Neither their assent nor native acceptance is +supplied by this task. + +2026-10-16 source checkpoint: T19 below extracts the completed per-attempt +executable snapshot implementation and reviewed fixture proof. T16 retains +native executable storage/custody, exact owner evidence/session callbacks, +complete descriptor composition and ordinary type/version admission. The +successor descriptor/source pins are proposed only; the earlier descriptor +history is preserved. T14 group provenance and T15 durable restart/recovery +remain independent open requirements. No native acceptance is counted here. + +2026-10-17 budget checkpoint (net-kingdom-worker): no source change made. The +T13 registration adapter alone is 217 source and 317 test lines plus an +independent review; six further owner descriptors/adapters cannot be completed +within one per-task token budget (soft 8000 / hard 10000), so the task was +stopped below 50% and is decomposed rather than pushed through. Proposed split, +one source-candidate task each with positive/refusal fixtures, canonical1.1 +default unchanged and no --allow-unimplemented admission: (a) ensure-groups, +bound to T14's journal/provenance seam; (b) mint-secret and (c) deliver-secret, +Platform-owned evidence seams; (d) bind-env and (e) deliver-consumer-secret; +(f) hand-over, Apps-owned consumer verification; then (g) descriptor composition +over the reviewed set. Each candidate needs its owner's source review before +pinning; none is authored here as owner-accepted. Status stays todo. + +2026-10-17 task refinement after coordinator r5: completed independent mint- +guard regression coverage is extracted as T32. T33 tracks the exact Platform +issuer-store source/reference correction; T34 tracks the Apps configuration +inverse reachable without secret or hand-over resource creation. T16 retains +final exact seven-descriptor composition, independent owner reviews and admission; +its status and existing dependencies are preserved. The kernel branch also needs +its own KR1-F1/F2 admission/dispatch corrections and independent review under +WARDEN-WP-0044-T03 before any 1.3 pin. Historical r4 acceptance does not supersede +r5's findings or establish native readiness. + +## T17 — Build and exercise the exact offline config-checker artifact + +```task +id: NK-WP-0048-T17 +status: done +priority: high +state_hub_task_id: "8c3e4baa-a218-5996-af9e-fe1153a46dac" +``` + +Completed 2026-10-16, isolated artifact/source fixture proof only. Built the +existing `key-cape/src/cmd/keycape` command from published clean source revision +`3c71226a1a7b8a2bdb710e9c497e1a1f7f8a3987`, source tree +`f0010b9cab17dbc3a787628a29657d98b900320f`, with cached Go 1.23.0, +CGO disabled, Linux/amd64 v1, trimmed paths, disabled VCS stamping and offline +read-only module selection. The full retained closure pins 108 source files, +9876 toolchain files and ten used module source directories. Three isolated +build locations produced identical 13633409-byte artifacts: +`sha256:be37738fe1c08259fb4f9ca0daf0f9301bfffc237e5bb38435525dfbf11ccff5`. + +Eight disposable checks use the actual executable and maintained helper/kernel, +with complete synthetic configs and a minimal environment: add/created-only +removal, normal pinned producer admission, rollback, lost-readback uncertainty, +stale revisions, substituted checker refusal, invalid startup config and missing +secret-env refusal. No checker stub or producer-pin override remains. The +artifact-bound descriptor proposal is +`sha256:76b1153d188062ece53fcd5e8d35290fdbfd02a69dd79d4b001fc42781afcdc3`. +Evidence: Custodian +`docs/changes/netkingdom-target-2026-10-16/browser-checker-artifact-next/`. +The first two build attempts succeeded but their closure collection failed; +both failures are retained, as is refusal of the older incomplete synthetic +fixture. No production config, credentials or cluster mutation was used. + +This extracts completed build/fixture scope from T16 without claiming KeyCape +owner endorsement, native checker selection/storage custody, acknowledged type +integration, native evidence/session callbacks or attended browser acceptance. +T16 retains exact owner review and composition/admission scope. The attended +registration procedure remains unexercised. + +## T18 — Make checker input and executable selection deterministic + +```task +id: NK-WP-0048-T18 +status: done +priority: high +state_hub_task_id: "9c45da71-fb4c-5f20-9065-90f448f37c64" +``` + +Completed 2026-10-16, isolated source correction and real-artifact fixture proof +only; independent integration/publication remain separately recorded. KeyCape +source review identified non-secret `KEYCAPE_ACCOUNT_PORTAL_URL` and +`KEYCAPE_BROWSER_LOGOUT_URL` overrides in config.Load. The original helper +inherited ambient environment, allowing valid supplied configuration to be +rejected or invalid supplied URL configuration to be admitted. A bare relative +checker filename could also be replaced by PATH lookup after the adapter had +hashed the selected local file. All three counterexamples are retained against +the unchanged predecessor. + +The existing helper now resolves the selected checker file to an absolute path +and passes an explicit empty subprocess environment. Three opt-in real-checker +regressions verify both URL-verdict directions and relative-file selection under +a non-checker PATH decoy. All 39 affected checks pass with the exact supplied +artifact, as do 11 real-artifact helper/kernel scenarios. Source evidence: +Custodian `docs/changes/netkingdom-target-2026-10-16/browser-checker-environment-r2/`. +Historical descriptors remain retained; the helper correction changes successor +source-composition pins and requires their separate review/admission. An absolute +path is not immutable custody and does not eliminate file replacement races; +T16 retains native executable storage/custody and callback/type admission. +No production configuration, credentials or cluster mutation was used. + +Source integration and publication: `916ba4b33e03179274d03b78bd898a4843e8653f`, following independent +Custodian source review in browser-checker-environment-r2/coordinator-review.json. +The ordinary protected projection pass assigned T17/T18 UUIDs with zero failures +and zero automation errors; generated-only brief commit refusal is retained. +The exact prior staged/working brief bytes were restored. Final source/publication/ +projection evidence is retained in Custodian browser-checker-environment-r2/ +canonical-integration.json. These source receipts do not admit native executable +custody, owner callbacks, acknowledged type changes or browser identities. + +## T19 — Bind checker execution to verified immutable attempt bytes + +```task +id: NK-WP-0048-T19 +status: done +priority: high +state_hub_task_id: "f50fdcd1-00be-5c3c-be0c-345aa2126032" +``` + +Completed 2026-10-16, independently reviewed source implementation and synthetic +fixture proof only. The registration adapter now opens a bounded regular checker +artifact without following its terminal symlink, verifies the reviewed digest, +and snapshots the exact bytes into a write/grow/shrink/seal-removal-sealed Linux +memfd before helper access. Live and proposed config checks execute that same fd +through procfs with the existing empty environment; the fd closes on completion +or failure. Unsupported snapshot/seal/exec policy refuses through the existing +parent-session revocation and uncertainty boundaries. The earlier unbounded +pre-snapshot path read was removed during independent review; actual adapter +symlink/FIFO/oversize refusal tests verify helper access never starts. + +Evidence: Custodian `docs/changes/netkingdom-target-2026-10-16/browser-resume-r1/`, +including exact base/preimages/postimages/patch, proposed descriptor, coordinator +source review and canonical integration receipt. Independent Custodian checks +passed 49 affected source tests and 12 actual Go checker/helper/kernel scenarios; +the sets overlap and are not separate acceptance counts. Existing published +Warden d97652a producer pins remain unchanged. The exact KeyCape artifact remains +`sha256:be37738fe1c08259fb4f9ca0daf0f9301bfffc237e5bb38435525dfbf11ccff5`. +The predecessor path-replacement refusal and the first expanded reopened-fd test +failure are retained; the latter fixture was corrected to restore write mode +before independently proving seals block reopened-fd writes and shared mmap. + +This source task adds no native owner custody, callback, evidence trust, type +acknowledgment, credential access, consumer sign-in or rollback acceptance. +Executable memfds, Linux seals and usable procfs are explicit runtime dependencies +and still require native admission. This is an extracted source outcome from +T16, not completion of T16; T12/T14/T15 and all external-owner gates remain open. + + +Publication verified by the coordinator after the source worker usage limit: +`8790dd18ae83c4651ef3d1d923312a118a5b436b` is the actual remote main revision. +All five canonical source hashes match the reviewed packet. Normal synchronization +completed with zero failures/automation errors; all 19 task statuses match the +projection. T19's assigned UUID is retained. The original staged and working brief +bytes were restored and independently compared. The exact source, projection and +publication checks are in Custodian browser-resume-r1/canonical-integration.json. +Native acceptance remains open under T16 and the other named owner tasks. + + +## T20 — Prepare durable group provenance and prove disposable LLDAP compatibility + +```task +id: NK-WP-0048-T20 +status: done +priority: high +state_hub_task_id: "863fcbdb-e246-51ba-87ca-a2f44d01d6f8" +``` + +Completed 2026-10-17, source candidate preparation and bounded synthetic +compatibility proof only. This extracts evidenced implementation/fixture scope +from T14; T14 retains native storage, writer isolation, protected callback and +owner-acceptance gates. Source integration/publication and State Hub projection +are pending the coordinator's separate receipts; no revision or task UUID is +invented here. + +The existing identity-provisioner group ensure/revoke interface now has a +reviewed candidate for a bounded owner-private SQLite journal, proven only in +local/disposable use. The initial storage instructions wrongly suggested a native +persistent-volume path; source review against accepted ADR-0015 corrected them +before publication. Production SQLite-on-a-PVC is not admitted: T14 retains +CNPG/rapp-postgres storage admission or a separately governed exception. +The local journal saves +complete prestate and intent before each attempted directory mutation, inventories +only confirmed new groups/memberships, binds original ensure idempotency and +correlation to the exact request digest, and refuses unsigned caller inventories +or legacy broad revoke requests. Replays never recreate removed state. Missing, +replaced or untrusted storage and missing intent rows stop execution; definite +before-mutation refusals retain evidence without stranding unrelated valid work. +Interrupted writes/readback remain unresolved across reopening. Revoke validates +all targeted native ids and complete member postimages before destructive work, +removes only confirmed creations and never deletes a preexisting empty group. +Server/configuration wiring, image-copy closure and owner storage/request +migration instructions are included. The existing endpoint's caller +authentication remains the existing boundary, not a new grant. + +The candidate was prepared against NetKingdom +`878abc93588e12b6da1bac055cfbb3abb8d7adf1`; exact source patch digest +`sha256:1f3f3ca94c46dadac8f24896053d4d23179de795e50a24298a0cfbcc46cca30e`. +The independent Custodian review passed all 65 provisioner checks. The earlier +62-check candidate, two independent-review findings and subsequent fixes remain +retained rather than overwritten. Seven additional fixture-plan checks exercise +the preparation boundary; these counts overlap the same candidate and do not +measure task-completion percentage or independent acceptance. + +A separately reviewed bounded fixture then exercised the exact candidate against +cached pinned disposable LLDAP and Python images, through actual GraphQL. All 14 +closed compatibility/containment oracles passed: exact creation inventory, +separate-process SQLite reopening/replay/revoke, complete preexisting directory +preservation including empty/shared groups and memberships, refusal of shared +membership drift, retained uncertainty after a lost response following a real +addition, and refusal of unknown/cross-case receipt bindings. The fixture used +only newly generated synthetic users/passwords/key material in private tmpfs, +with a network-none anchor and shared loopback helper, no host ports/mounts, +no image pulls and no production or actual credential contact. The coordinator's +safe receipt reports both exact owned containers absent after cleanup; final +independent cleanup verification remains the coordinator's integration duty. + +Evidence: Custodian +`docs/changes/netkingdom-target-2026-10-16/group-provenance-r1/`, including exact +source base/pre/postimages, patch/manifest, independent review and retained +refinements. Native fixture manifest +`sha256:8a0514f5f14137355972400cd4852f48c892316d28e3abd3c86635d7cb81eef2`; +retained safe result `native-fixture/execution-result.json` (original +`/tmp/nk48-groups-native-review-5pywb19z/result.json`). The source task supplies +no native owner assent, production rollout, protected session/evidence callbacks, +ordinary type admission or consumer login proof. LLDAP has no conditional +membership mutation or generation token here: exact readback cannot exclude +ABA or a concurrent external writer, so T14 remains open. + +2026-10-17 verified source follow-through: source commit `4f65e41` passed normal +local scanning and all 65 canonical provisioner tests. The actual disposable +LLDAP receipt passed all 14 oracles; independent verification on the pinned +daemon found both exact names and the run label absent. Source integration +includes the ADR-0015 documentation correction; the journal remains local-only. + +T14 source preparation is underway. Twenty-three pinned public sources identify +NetKingdom implementation/reconciliation, Platform custody/database governance +and rapp-postgres consumer provisioning; the inspected runtime binding does not +assign identity-provisioner a package. The existing User Engine runtime token +and pod route are not a protected-case caller lane. The native proposal preserves +the same journal seam on a dedicated CNPG consumer and requires authoritative +scope binding, admitted role/lease/placement/recovery and all LDAP writers fenced. +The declared exclusive maintenance window is not mechanical exclusion of WebUI, +helper or lifecycle writers. Capacity placement and EUR cost/risk remain owner +decisions. No package, database, credential, rollout or owner assent was created. +Custodian evidence: group-provenance-r1/native-storage-proposal-r1; final source, +projection and cleanup review: resume-integration-r1/publication-verification.json. + + +## T21 — Implement the PostgreSQL journal and prove disposable SQL compatibility + +```task +id: NK-WP-0048-T21 +status: done +depends_on: [NK-WP-0048-T20] +priority: high +state_hub_task_id: "afe78e4d-408a-516a-ab92-e212e8a2d11d" +``` + +Completed 2026-10-17: bounded source preparation and actual disposable PostgreSQL +compatibility only. T14 retains native storage, custody, callbacks, authorization, +all-writer isolation and production admission. Coordinator source publication and +projection remain separate follow-through; no source revision or task UUID is +invented here. T20 remains the historical local SQLite/actual LLDAP proof. + +The eight-path source candidate adds an injection-only PostgreSQL implementation +behind the existing owner journal seam, with explicit trusted tenant/directory/ +case/source/principal/epoch binding and independent continuity callbacks. It +persists bounded intent/created-only receipts, exact-one version updates, durable +active fences, and a content-bound journal chain. SQL commits precede independent +exact-marker acknowledgment; lost replies/checkpoints poison continuation and +retain uncertainty. A counter-only checkpoint is explicitly insufficient for +divergent clones. Migration does not create database/schema/credential, admit an +epoch or clear quarantine; production HTTP activation is absent pending trusted +callbacks. Pinned psycopg package/image-copy closure is included, without claiming +an image build or native dependency admission. + +Candidate base `6cb43c0cc55212363a5406b02db046e2384a7c40`; final eight-path patch +`sha256:5549d2982b61ff70aba4b3928fecd5639db0f1a732ab2f8630f678e7acf4d34d`. +All 82 source tests pass, alongside 13 fixture plan/diagnostic checks and two +source-backed snapshot regressions. Counts overlap the same source and fixture; +they are not additive native acceptance or completion percentages. + +The independently reviewed R5 fixture ran the actual SQL migration/backend and +existing provisioner path against cached pinned disposable PostgreSQL. Directory +operations deliberately used the existing synthetic LLDAP test double. All +manifest-declared phase oracles passed: creation-only inventory and DDL refusal; +separate-process replay/revoke with complete preexisting/shared visible-state +preservation; foreign-context refusal; concurrent exactly-once reservation and +stale version refusal; actual database fork with divergent bodies at the same +counter and fresh-object rejection of the losing exact checkpoint; retained +uncertainty after checkpoint/directory/SQL response loss; and actual pg_dump/ +pg_restore rollback refusal against the independently retained fixture checkpoint. +The checkpoint is fixture-only, not an admitted native owner evidence holder. + +Only generated credentials/state were used, with two nonroot/read-only containers +on private loopback, bounded tmpfs/CPU/memory/PIDs/lifetimes, no host ports/mounts, +no pulls and no production contact. The safe receipt reports both exact owned +containers absent, with no daemon uncertainty. Independent final pinned-daemon +cleanup verification remains coordinator follow-through. + +Failed R2/R3/R4 receipts and source revisions remain retained. R2's helper failure +subphase is unknown; R3 exposed a real backend bug where replacing nested receipt +objects lost the created ID held by ensure, fixed by acknowledging only the backend +version after commit/checkpoint. R4 verified that fix through actual SQL bootstrap, +then exposed fixture snapshot aliasing and empty deleted-group backing entries; +source-backed regressions corrected the snapshot without weakening complete +visible-prestate equality, and reject nonempty orphan membership. These refinements +belong to one trial, not extra native acceptance successes. + +Custodian evidence: `docs/changes/netkingdom-target-2026-10-16/group-postgres-r1/`; +exact source/pre/postimages and retained predecessors; native R5 manifest +`sha256:d51f90816616848b93bcdc482e10a98d07061deb01eeed9e50700fc6e68b6051`; +`final-integration-r1/typed-execution-evidence.json` and byte-exact safe receipt +(original `/tmp/nk48-postgres-native-review-sguj7t8p/result.json`). No CNPG native +storage/HA/backup/lease/TLS/capacity/restore admission, authoritative protected +callbacks, real LDAP writer fencing/CAS/ABA exclusion, owner assent or production +endpoint activation is claimed. Those remain live T14 work. + + +## T22 — Establish registration recovery source gaps and complete descriptor review inventory + +```task +id: NK-WP-0048-T22 +status: done +priority: high +state_hub_task_id: "ea50d93b-0d04-55d2-98c4-9b6c3fe925b1" +``` + +Completed 2026-10-17: source-only owner-contract review and complete review +inventory. Inspected the actual registration adapter, Warden Journal/Executor, +NetKingdom evidence compatibility checker and Audit Core custody/read surfaces. +No existing admitted holder/readback supplies authenticated original-attempt +recovery: payload/creation are process-local, persisted evidence references are +hashed, and reconcile receives a new attempt without original context. The exact +four-owner contract requirements and non-replayable missing/ambiguous cases are +recorded; no recovery callback, publisher, credential grant or native trust root +was installed. T15 retains implementation and actual acceptance. + +The complete source1.2 inventory maps all seven follow-through steps to their +existing owners, missing exact contracts and created-only rollback requirements. +The actual maintained adapter regenerated the sole existing register-client +machine descriptor against the delivered 13,633,409-byte checker and current +sealed-memfd snapshot source. The checker rehash and Warden Descriptor.decode +round-trip passed; six owner descriptors remain absent. Actual source commands +render canonical1.1 and explicitly selected1.2 with execution.permitted false; +ordinary1.2 case admission still refuses its acknowledged/unimplemented type. +Canonical type/case/acknowledgment bytes were hash-checked unchanged. No tests were +added or rerun, no native configuration or effect occurred, and no owner assent +or complete machine bundle is claimed. T16 retains native executable custody, +complete exact descriptors, independent source review and normal version/type +admission. This extracted outcome supplies no native gate completion credit. + +Evidence in the-custodian: +`docs/changes/netkingdom-target-2026-10-17/native-registration-r1/`, especially +original-attempt-review.json, descriptor-composition.json, source-verification.json, +source-manifest.json and manifest.json. The prior sixteen-gate audit remains +immutable. Publication, normal task UUID allocation and projection are separate +coordinator follow-through; no Hub UUID is assigned by this source record. + + +## T23 — Review native journal package assignment and storage integration boundaries + +```task +id: NK-WP-0048-T23 +status: done +priority: high +state_hub_task_id: "6e7cd483-c7af-53a2-9d0a-3d76b6fb49ee" +``` + +Completed 2026-10-17: source/package placement and protected integration review +only, separate from T21's completed PostgreSQL source and disposable-SQL proof. +The owner review recommends a dedicated independently rolled-back +`rapp-identity-provisioner`, owned by NetKingdom on rail-kubernetes, with a +schema-valid draft declaration and no reef binding. No repository, registry +entry, native package assignment or owner assent was created. Existing +rapp-user-engine ownership/token delivery does not assign it the provisioner. + +The complete current rapp-postgres catalog has six declared consumers: four on +platform-pg and two on platform-pg-2. The inert renderer-valid provisioner +candidate fits platform-pg-2 at three of four, requesting one migration plus +four runtime connections; placing it on the first cell is refused as a fifth. +Normative package schema and full-catalog capacity/render checks passed. The +four-consumer ceiling is estimated, not measured native headroom, and no current +SQL session, backup or restore acceptance is claimed. + +The exact review identifies remaining package/substrate, SQL role/lease/TLS, +network and backup/recovery contracts; independently authenticated protected +scope callbacks; an external content-bound checkpoint holder with atomic +advance/loss handling; and all-LDAP-writer exclusion or provider CAS/generation, +including lifecycle cascades and remove/readd ABA. Generic Audit custody is not +that checkpoint service. A database-only fence, final reread or one replica +cannot exclude foreign directory writers. The native acceptance/recovery matrix +keeps storage/lease admission separate from separately approved directory +mutation; the production activation remains disabled pending trusted integration. + +Evidence in the-custodian: +`docs/changes/netkingdom-target-2026-10-17/native-storage-r1/OWNER-REVIEW.md`, +source-validation.json and hash-bound source-references.json, plus the inert +proposed declaration/consumer and renderer outputs. No native resource, lease, +credential, deployment, destructive effect, independent holder or native owner +acceptance was delivered. T14 retains these live gates with its original UUID +and progress status. This completed review adds no duplicate T21 acceptance +credit; publication, task UUID allocation and projection remain coordinator +follow-through and no UUID is hand-assigned here. + +## T24 — Ensure-groups adapter source candidate + +```task +id: NK-WP-0048-T24 +status: done +priority: high +state_hub_task_id: "1a3931ee-3b5f-52fc-9295-4073804f4bd2" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): ensure-groups-adapter.py, 8 tests (commit 5a2e7d6). Binds to the T14 PostgreSQL journal and refuses SQLite or a foreign binding. It accepts only canonical 1.1 cases, so T30 must add an explicit 1.2 path. It calls the provisioner's private `_group_binding`; a public helper is the cleaner shared edit. + +Split from T16 on 2026-10-17 so each candidate fits one per-task budget. Bind group creation to T14's journal/provenance seam; refuse without trusted journal callbacks. +Source candidate only, modelled on the T13 registration adapter +(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and +`registration-adapter.md`). Requires positive and refusal fixtures and +created-only rollback. Keeps canonical 1.1 as the default and uses no +`--allow-unimplemented` admission. Done means the candidate and its tests are +committed. NetKingdom (directory) owner source review before pinning, and any native action, +stay separate gates and are not supplied by this task. + +## T25 — Mint-secret adapter source candidate + +```task +id: NK-WP-0048-T25 +status: done +priority: high +state_hub_task_id: "0de2a7d9-874d-578b-a076-f653fe42e30b" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): mint-secret-adapter.py, 9 tests (commit 558c5bc). Value-free receipts only. The receipt schema `railiance-platform.custody-mint.v1` and the mint/destroy capability interface are proposals that Platform must confirm. + +Split from T16 on 2026-10-17 so each candidate fits one per-task budget. Platform-owned custody evidence seam: the adapter records value-free mint evidence only and never handles secret values. +Source candidate only, modelled on the T13 registration adapter +(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and +`registration-adapter.md`). Requires positive and refusal fixtures and +created-only rollback. Keeps canonical 1.1 as the default and uses no +`--allow-unimplemented` admission. Done means the candidate and its tests are +committed. Platform owner source review before pinning, and any native action, +stay separate gates and are not supplied by this task. + +## T26 — Deliver-secret adapter source candidate + +```task +id: NK-WP-0048-T26 +status: done +priority: high +state_hub_task_id: "94b3cbf0-c908-5ac6-b0db-67385c3e021c" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): deliver-secret-adapter.py, 8 tests (commit 1b60f6e). The lane `platform-secret-delivery` and the schema `netkingdom.deliver-secret.v1` are proposals that Platform must confirm. + +Split from T16 on 2026-10-17 so each candidate fits one per-task budget. Platform-owned delivery evidence seam (ESO/custody), value-free. +Source candidate only, modelled on the T13 registration adapter +(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and +`registration-adapter.md`). Requires positive and refusal fixtures and +created-only rollback. Keeps canonical 1.1 as the default and uses no +`--allow-unimplemented` admission. Done means the candidate and its tests are +committed. Platform owner source review before pinning, and any native action, +stay separate gates and are not supplied by this task. + +## T27 — Bind-env adapter source candidate + +```task +id: NK-WP-0048-T27 +status: done +priority: high +state_hub_task_id: "e56365f8-29b5-5c89-a329-8115f9f3b2a7" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): bind-env-adapter.py, 5 tests (commit 6fbce73). Binds references only. To fit the budget it does not yet wrap a full Warden `Adapter` like T13 (source-digest pin, authenticated evidence, parent-session revocation); it returns `(reference, run, rollback)`. That wrapping is a prerequisite for T30. + +Split from T16 on 2026-10-17 so each candidate fits one per-task budget. Bind the issuer Deployment env reference to the delivered secret reference, without values. +Source candidate only, modelled on the T13 registration adapter +(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and +`registration-adapter.md`). Requires positive and refusal fixtures and +created-only rollback. Keeps canonical 1.1 as the default and uses no +`--allow-unimplemented` admission. Done means the candidate and its tests are +committed. NetKingdom runtime owner source review before pinning, and any native action, +stay separate gates and are not supplied by this task. + +## T28 — Deliver-consumer-secret adapter source candidate + +```task +id: NK-WP-0048-T28 +status: done +priority: high +state_hub_task_id: "6dab281c-ea9a-5a17-a83f-e8e9a4d4484f" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): deliver-consumer-secret-adapter.py, 5 tests (commit 650c1ac). Type 1.2 only. It assumes the owner has validated ESO TokenRequest and the controller-RBAC admission; that stays an owner gate. + +Split from T16 on 2026-10-17 so each candidate fits one per-task budget. Consumer-side delivery evidence seam, value-free. +Source candidate only, modelled on the T13 registration adapter +(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and +`registration-adapter.md`). Requires positive and refusal fixtures and +created-only rollback. Keeps canonical 1.1 as the default and uses no +`--allow-unimplemented` admission. Done means the candidate and its tests are +committed. Platform and Apps owner source review before pinning, and any native action, +stay separate gates and are not supplied by this task. + +## T29 — Hand-over adapter source candidate + +```task +id: NK-WP-0048-T29 +status: done +priority: high +state_hub_task_id: "129db41d-4560-5e20-9f3b-c3c91e0a5601" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): handover-adapter.py, 8 tests (commit ec63539). The owner `apps` and lane `apps-consumer-handover` are proposals that the Apps owner must confirm. It has no reconcile path; restart handling follows T15. + +Split from T16 on 2026-10-17 so each candidate fits one per-task budget. Apps-owned consumer verification and hand-over receipt seam. +Source candidate only, modelled on the T13 registration adapter +(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and +`registration-adapter.md`). Requires positive and refusal fixtures and +created-only rollback. Keeps canonical 1.1 as the default and uses no +`--allow-unimplemented` admission. Done means the candidate and its tests are +committed. Apps owner source review before pinning, and any native action, +stay separate gates and are not supplied by this task. + +## T30 — Compose the descriptor set over the reviewed candidates + +```task +id: NK-WP-0048-T30 +status: done +depends_on: [NK-WP-0048-T24, NK-WP-0048-T25, NK-WP-0048-T26, NK-WP-0048-T27, NK-WP-0048-T28, NK-WP-0048-T29] +priority: high +state_hub_task_id: "6f6073ac-52dd-5b70-8874-6d76d4c072e4" +``` + +2026-10-17 done: coordinator review r3 (the-custodian `nk48-adapters-1.2-review/coordinator-review-r3.json`, +fd07eaa) approved all nine files at ff8f597, with R2-F1..F3 resolved, an independent +reconstruction of 117 passing and no open findings. This is a source review only: +it grants no owner admission, pin or native authority. Owner review and admission +continue under T16, which also takes the r2 observations, including the +`warden_executes: false` steps that carry execution descriptors. + +2026-10-17 coordinator review r2 (the-custodian `nk48-adapters-1.2-review/coordinator-review-r2.json`) +approved five files and rejected three, plus the artifact. The fixes are merged in +2ee4ed0: R2-F1, mint-secret revokes the session and the broker grant +independently (eeed8e1); R2-F2, deliver-secret binds to the admitted case and +derives its Secret and custody path, with no caller reference (640292c); R2-F3, +bind-env pins its five-file closure, and a new closure-completeness test checks +every adapter (d16db30). Each regression test failed on 68f4bbb. The artifact is +regenerated, and the full suite including kernel admission passed 117. Next is +the r3 review. Open for owner review under T16: the type marks four steps +`warden_executes: false`, yet each has an execution descriptor. + +2026-10-17 founder confirmed every proposed lane and schema name for T25-T29 +as-is. The artifact records them as `names_confirmed` (by founder, 2026-10-17), +and no step is still `owner_confirmation_required`. Next: independent source +review of the seven adapters. + +2026-10-17 founder decision (supersedes the `agent` decision below): the +mint-secret actor is `human`. Coordinator review r1 (the-custodian +`docs/changes/netkingdom-target-2026-10-17/nk48-adapters-1.2-review/coordinator-review-r1.json`, +finding NK48-R1-F1) showed that unchanged kernel a45cf47 refuses every +`effect: mint` admission whose actor is not `human` ("agent mint is +forbidden"), so the `agent` descriptor could never be admitted. Adapter, tests +and composer comment updated and the artifact regenerated (mint-secret +descriptor digest now `sha256:8c40c2884f8ba15e8cb6c98f33419c395e54fce8ef33d6e1a34782cf3f1fcfa9`); +98 tests pass against a45cf47. The set needs an independent r2 review. + +2026-10-17 `test_admit_1_2_adapter_descriptors.py` runs Warden's own +`Executor.admit` on all seven composed 1.2 descriptors, with the real 1.2 +type, the 1.2 forgejo example case, the reviewed `BrowserInputs` validator and +fixture-only trust (stand-in adapters, nothing dispatched). It also pins that +an agent mint descriptor is refused ("agent mint is forbidden") and that a +wrong lane is refused for every step. Against the old `agent` revision +(582ab9f) it fails as expected. Combined suite: 102 passed on ops-warden a45cf47 +and on current ops-warden. Fixture admission is not owner admission or a pin. + +2026-10-17 founder decision: the mint-secret actor is `agent`, not `warden`. +The kernel is unchanged. The adapter, its test and the composer were updated and +the artifact regenerated; all seven steps pass `Descriptor.decode` and +`complete_executable_descriptor_set` is true (98 tests passed). The artifact +stays `not_an_admission`, with no owner review, admission or dispatch permission. + +2026-10-17 composer merged (60b41a1 via 3c62bec) with follow-up 376b44b (merged; +combined suite 98 passed). `compose-1.2-descriptors.py` loads 1.2 only through an +explicit version root and refuses partial, duplicate, unknown or mismatched maps. +`descriptor-composition-1.2.candidate.json` is a reviewable candidate, marked +`not_an_admission`, with canonical 1.1 unchanged. ensure-groups and hand-over now +carry and honour `parent-session` revocation, and the hand-over owner is +parameterised (`railiance-apps` for Forgejo). Six of the seven steps pass +`Descriptor.decode`. mint-secret is still refused: its actor `warden` is outside +the kernel's human/agent set, and Platform (with ops-warden, if the kernel should +change) must decide. Five steps carry `owner_confirmation_required` names. The +set is complete only after both. Owner review, the canon section 6 successor +revision and admission remain T16's. + +2026-10-17 prerequisites merged (65559ef; combined suite 78 passed): +ensure-groups accepts 1.2 via an explicit `version_root='1.2'` (531151b), with +1.1 still the default. bind-env is now a full Warden v1 `Adapter` (7442d21); its +kernel-level fixture tests like T13's are still missing, and its `build()` +signature changed. Composition is unblocked in source. Owner confirmation of +the proposed step, lane and schema names remains before pinning. + +2026-10-17 inputs from T24–T29 that composition must resolve: T24 accepts +only 1.1 while T28 accepts only 1.2, so add an explicit 1.2 ensure-groups path; +T27 still needs full Warden `Adapter` wrapping; the lane, schema and owner names +proposed in T25, T26 and T29 need owner confirmation before pinning. + +Split from T16 on 2026-10-17. Compose the seven-step source 1.2 descriptor set +through the existing explicit version-root selection over T13 and T24–T29. No +partial descriptor map, canonical 1.1 stays the default, and new descriptor bytes +need a reviewed successor revision under canon section 6. Owner review and +admission remain T16's. + +## T31 — Restart and lost-receipt fixtures against Warden's original-intent interface + +```task +id: NK-WP-0048-T31 +status: done +priority: high +state_hub_task_id: "ed1ccd87-89a0-5e69-885c-0eaf5ab0bb5a" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): test_registration_original_intent_reconcile.py, 8 tests (commit 74adcd7), runs against the unmodified ops-warden a45cf47. The holder is a test-only in-memory store. For real T15 support the T13 adapter's `build()` needs an injected holder/readback capability, a `reconcile_original` callback, and a `verify` that accepts reconcile results. Rollback after restart stays refused until there is an owner contract. + +Split from T15 on 2026-10-17. ops-warden published the original-intent +lookup/context interface in `src/warden/case_run.py` (`OriginalIntent`, +`Journal.original_intent`, `Adapter.reconcile_original`; commit `a45cf47`). +Build T15's restart/lost-receipt fixtures for the registration adapter against +it: the original intent is kept separate from the reconcile attempt; wrong +attempt/case/source/principal and unavailable-holder cases refuse; and a missing +or ambiguous record stays uncertain, with no add replay or undo. The interface +is Warden's own dispatch record, not owner-authenticated created-only provenance. +Selecting the actual authenticated holder stays a T15 owner gate. + + +## T32 — Restore independent agent-mint admission regression coverage + +```task +id: NK-WP-0048-T32 +status: done +priority: high +state_hub_task_id: "c6d0a27e-f6a7-5df0-95f6-3e7473d38bb5" +``` + +Completed 2026-10-17, source tests only. Extracted from T16's coordinator r5 +finding NK48-R5-F1; no earlier adapter outcome is counted again. The real 1.3 +admission test now separately checks the committed descriptor map, an in-memory +fixture map acknowledging the changed agent descriptor to reach the independent +mint prohibition, and the human descriptor under an explicit fixture agent +runtime marker. Only `sso-mfa/k8s/keycape/test_admit_1_3_type.py` changes. + +Against frozen Warden `117fec9241e7b985dfb187b992e173d0a5dc3700`, the new +suite passes 10 tests / 32 subtests. Independent mutation evidence removes only +the mint admission guard in scratch: both new guard tests fail while eight +controls pass; the old suite passed that mutant. Custodian independently reran +the canonical successor: 10 tests / 32 subtests pass. Evidence: +`the-custodian/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/` +(patch, frozen/mutant results and canonical-validation.json). No descriptor, +parameter pin, catalog, acknowledgment, kernel or native execution changed. +T16 still owns actual composition and admission; F2/F3 follow below. + +## T33 — Close the issuer-store reference finding with Platform source + +```task +id: NK-WP-0048-T33 +status: todo +priority: high +state_hub_task_id: "5ace28ff-a4cb-5647-a8f6-3cd54bb9d0a5" +``` + +Owner: Platform for the fixed store/template source and its delivery boundary; +NetKingdom for the 1.3 type reference. Split from T16's current coordinator r5 +finding NK48-R5-F2. Next action: independently review the prepared Platform +candidate and publish a concrete owner-controlled issuer store kind/name/scope +and template reference, then integrate only that exact proposed source into the +1.3 deliver-secret entry point and regenerate its composition artifact. Candidate +and current review evidence are under Custodian +`docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/` +and `nk48-r5-f2-review-r1/`. + +Completion requires exact committed source, a passing independent source review +and unchanged type parameter bytes. No consumer store may be inferred as issuer +store, and no source declaration supplies owner assent, installed ESO support, +CCR/grant changes or a real delivery capability. Those remain existing +RPF-1.3-T4 and T16 admission/native requirements. + +## T34 — Close the consumer configuration inverse finding + +```task +id: NK-WP-0048-T34 +status: todo +priority: high +state_hub_task_id: "37f43c40-d014-544e-aa0b-5219dd4a6b93" +``` + +Owner: Apps for consumer configuration and inverse; NetKingdom for exact type +placement. Split from T16's coordinator r5 finding NK48-R5-F3. Next action: +independently review and implement the owner transaction/inverse through the +existing RAPPS-WP-0016-T05 path, with authenticated durable before/after release +provenance, exclusive serialization of all release writers and a fresh authorized +attended session for every inverse attempt after the hand-over session is revoked. +The inverse must remain reachable for public clients, confidential NO_CHANGE +secret delivery and verification failure before a hand-over receipt exists. + +Completion requires real owner configuration/inverse source bindings and +independent regressions, then exact reviewed 1.3 entry/rollback text and composed +artifact. A synthetic wrapper with an injected backend alone is insufficient. +Keep hand-over receipt rollback receipt-only and secret deletion created-only; +preexisting or drifted resources must refuse unsafe inverse. Candidate evidence: +Custodian `docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/` +and `nk48-r5-f3-review-r1/`. Source review, owner acknowledgment and native proof +remain distinct. T16's final composition/admission and T06's native first case +are not completed by this refinement. + +## T35 — Align source admission fixtures with the authenticated catalog and attended-session API + +```task +id: NK-WP-0048-T35 +status: done +priority: high +``` + +Completed 2026-10-17, source fixture compatibility only. Distinct from T32's +independent agent-mint regression repair: both remaining opaque catalog byte +placeholders now use one well-formed synthetic catalog whose exact bytes are +bound by the fixture evidence verifier. The fixture mirrors the six relevant +Warden lane shapes; four draft statuses are hypothetically active, while both +Apps establishing login references remain unresolved. No real catalog entry, +owner login choice or production parameter pin changes. + +Against the reviewed mode-aware Warden source now integrated at +`ae7bc73b4f4bac22454144959b870d161498e0af`, scratch validation passes 31 tests +and 53 subtests. Five resolvable 1.3 descriptors conditionally admit under +fixture-only acknowledgment and pinning; both Apps attended steps refuse before +execution. Changed catalog bytes refuse, all independent mint-guard tests still +reach their intended guard, and the registration fixture's actual kernel +run/rollback and uncertain-result paths pass with synthetic owner callbacks. +The registration fixture explicitly aligns its in-memory proposed type lane +with its current descriptor; canonical 1.1 is unchanged. + +The same tests against predecessor `117fec9241e7b985dfb187b992e173d0a5dc3700` +pass 31 tests and 51 subtests, with only the two new Apps refusal assertions +failing because that predecessor lacks the attended-session catalog check. +Tests target the reviewed current API; the parser or Apps gate is not weakened +for predecessor compatibility. Evidence and root source review: +`the-custodian/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-update-r1/` +and `nk48-catalog-fixture-publication-r1/`. Canonical validation/publication +receipts belong in the latter packet when performed. This records source +fixture work, not native T03, owner assent, exact-type acknowledgment, PIN-1, +composition/admission completion under T16 or the native first case under T06. +T33 and T34 retain their existing issuer-store and consumer-inverse work. diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/workplan.preimage.md b/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/workplan.preimage.md new file mode 100644 index 0000000..16914fc --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/workplan.preimage.md @@ -0,0 +1,1609 @@ +--- +id: NK-WP-0048 +type: workplan +title: "Establish SecurityCaseTypes so that known security cases are light" +domain: infotech +repo: net-kingdom +status: active +flavor: implementation +owner: claude-code +topic_slug: netkingdom +created: "2026-10-08" +updated: "2026-10-17" +depends_on: [] +related_repos: + - ops-mason + - ops-warden + - key-cape + - railiance-platform + - informed-decision +state_hub_workstream_id: "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" +--- + +# NK-WP-0048 - Establish SecurityCaseTypes so that known security cases are light + +## Why + +The founder's direction (2026-10-08): setting up a security integration of a +kind that has been done before should be easy, meaning accept the case, then +ops-mason arranges it and ops-warden follows through. Setting up a new kind +needs ample documentation and tests and an informed-decision, because its shape +is part of the security architecture. + +Today every integration is hand-built: per-client helper scripts, per-lane +custody, founder checklists. The trigger was KEY-WP-0038 (Forgejo as an OIDC +client), whose next step was going to be yet another hand-written rollout +script. + +This plan continues the pattern work of NK-WP-0008 (pattern infospace), +NK-WP-0009 (tutorials) and NK-WP-0030 (scenario composition). It turns patterns +into **SecurityCaseTypes** that ops-mason and ops-warden can execute. Canon: +`canon/standards/security-case-types_v0.1.md` (proposed). + +Companion plans: ops-mason `MASON-WP-0010` (materialization) and ops-warden +`WARDEN-WP-0044` (follow-through). + +## Tasks + +```task +id: NK-WP-0048-T01 +status: done +priority: high +state_hub_task_id: "930ac3e9-20ec-5473-98cd-3fe8fddb730f" +``` + +### T01 — Accept the terminology and the canon + +Founder review of `canon/standards/security-case-types_v0.1.md`, including its +section 10 open questions. On acceptance the canon status goes from `proposed` +to `accepted`. + +2026-10-08: accepted by the founder (in session). Canon status `accepted`; +section 10's open questions are carried forward to the first type (T04, T05). + +```task +id: NK-WP-0048-T02 +status: done +priority: high +state_hub_task_id: "19c25d96-a55c-58f0-9168-3fdbc4955fc2" +``` + +### T02 — Inventory the de-facto types already built by hand + +Read-only. List every security integration built so far and group them by shape. +Known starting points: + +- browser OIDC relying parties on key-cape: informed-decision-approver, + coulomb-social, user-engine portal, openbao-admin, and Forgejo (pending) +- confidential service clients with `client_credentials`: secrets-engine + approval, requester and openbao clients, approval-engine operator +- workload KV read lanes on OpenBao: the railiance-platform credential lane + designs and CCRs, for example `railiance-backup-offsite-lane` +- short-lived SSH certificates through ops-warden (`warden sign`) +- tunnelled operator access (ops-bridge) + +For each: the repositories involved, the hand-built steps, attended human steps, +custody paths, verification evidence, and rollback (if any). Output +`docs/security-case-type-inventory.md`, ranking candidate types by how often the +shape recurs and how much manual work each instance costs today. + +2026-10-08: done, `docs/security-case-type-inventory.md`. 36 of 37 CCRs are +already `request_type: workload-kv-read`, a de-facto type reviewed each time as +if it were new. Browser clients: 5 hand-built plus Forgejo, each with its own +rollout script. Confidential service clients need a registration plus two or +three KV-read CCRs each. A forge read identity is needed in bulk by +ORGREF-WP-0002-T06. Recommendation: oidc-browser-relying-party first (unblocks +the forge), workload-kv-read second (largest payoff), then forge-read-identity. + +```task +id: NK-WP-0048-T03 +status: done +depends_on: [NK-WP-0048-T01] +priority: high +state_hub_task_id: "e487975b-73b1-516c-971d-da4cbbb93063" +``` + +### T03 — Schema, template and validator + +- `canon/schemas/security-case-type_v0.1.schema.json` and + `security-case_v0.1.schema.json` +- `security-case-types/_template/` with `type.yaml`, `README.md` and tests to copy +- a validator, extending `tools/security-scenario-composer/`, that checks a type, + checks a case against its type, and renders the case into the composer's + deterministic plan (still `execution.permitted: false`) + +Acceptance: the validator rejects missing sections, secret-shaped parameter +values, unpinned providers and cases against non-`implemented` types, with tests +for each. + +2026-10-08: done. `canon/schemas/security-case-type_v0.1.schema.json` and +`security-case_v0.1.schema.json`; `tools/security-case-types/security_case_validator.py` +(`validate-type`, `validate-case`, `render`, reusing the scenario composer); +`security-case-types/_template/` (a valid, runnable example type over +`net-kingdom.local-identity`, with README sections, an example case and tests that +travel with every copy). 31 tests pass, including the composer's existing suite. + +```task +id: NK-WP-0048-T04 +status: done +depends_on: [NK-WP-0048-T02, NK-WP-0048-T03] +priority: high +state_hub_task_id: "2fffe112-7d55-5680-9952-78d74f44f87f" +``` + +### T04 — Define the first type: browser OIDC relying party + +`security-case-types/oidc-browser-relying-party/`, version 1.0: + +- parameters: client id, display name, exact redirect URIs, scopes, client type + (confidential or public), MFA pin, the groups the consuming service maps, + custody path for the secret (confidential only), consuming service and its + owner +- composition: key-cape client registration (key-cape declares it as a playbook + capability), secret minting into custody (railiance-platform), LLDAP group + existence and membership (net-kingdom identity-provisioner), handover to the + consuming service +- materialization (ops-mason) and follow-through (ops-warden) sections +- verification: discovery lists the redirect URI; a test sign-in returns the + expected claims for one human; anonymous access to the custody path is denied +- rollback: remove exactly this client and its custody path +- README: threat model, MFA rationale, why redirect URIs are exact, which steps + still need an attended login and how that is minimised +- reference instances: informed-decision-approver, coulomb-social, user-engine + portal, openbao-admin + +Acceptance: complete per canon section 3, tests green, status `proposed`. + +2026-10-08: done. `security-case-types/oidc-browser-relying-party/` 1.0 (proposed): +`type.yaml` with 12 parameters (the custody reference is required only for +confidential clients), composition over key-cape's new +`key-cape.oidc-client-registration` declaration (contract PASS, `maturity: +planned`), materialization and follow-through, verification and rollback, all +conditional on client type where it matters. One attended step, +`register-client`, justified by the live registry being a Kubernetes Secret. +README with threat model, alternatives, an honest fit table (portal and vergabe +fit; Forgejo is the first confidential case; openbao-admin, coulomb.social and +informed-decision-approver do not fit 1.0), and known limits. Examples: Forgejo +(checked against the accepted request) and a public portal. The validator gained +`when` / `required_when` conditions and provider discovery. 43 tests pass. +ops-mason's and ops-warden's sections are drafted in the type. Their owners +review them in MASON-WP-0010-T01 and WARDEN-WP-0044-T01 before acknowledgment +(T05). + +```task +id: NK-WP-0048-T05 +status: done +depends_on: [NK-WP-0048-T04, MASON-WP-0010-T01, WARDEN-WP-0044-T01] +priority: high +state_hub_task_id: "3d987d3d-0797-538b-86e4-51185a327ae2" +``` + +### T05 — Acknowledge the type through informed-decision + +Prepare the Decision Memo from the type's README and `type.yaml` and submit it +through informed-decision's production intake. On acknowledgment the type +moves to `acknowledged`. + +2026-10-08: Decision Memo prepared. Hub decision `e418263e-a71f-4f01-a970-c87c9e187c09`. +`security-case-types/oidc-browser-relying-party/decision/`: `packet.md` (question, +one act, what acknowledging does not do, the shape, alternatives, attended +steps, limits, evidence), `build_intake.py` and the built `intake.json` (memo +class `decision-record`, review group `net-kingdom-admins`, resolves the hub +decision on answer). The envelope passes informed-decision's own +`validate_submission`. Queuing it is an attended operator step, a write inside +the production dispatcher pod: + + kubectl -n state-hub exec -i deploy/state-hub-memo-dispatch -- \ + python -m api.services.memo_dispatch --queue /data/private enqueue --envelope /dev/stdin \ + < security-case-types/oidc-browser-relying-party/decision/intake.json + +The answer then appears for review in informed-decision. + +2026-10-08: queued by the founder (`pending`, `queued`) after one refusal: the +first envelope's idempotency key was not State Hub's stable +`state-hub..` form, and the builder now validates against both +sides. The dispatcher delivered it to informed-decision (ledger 7 → 8 +delivered). T05 waits for the founder's answer in informed-decision, which +resolves hub decision `e418263e`. + +2026-10-08: ops-mason's review (MASON-WP-0010-T01) folded into the type, still +1.0 `proposed`: `secret_ref` is no longer a parameter, the type derives +`platform/workloads/keycape/{client_id}`; policies are +`workload-kv-read-keycape-{client_id}-eso` and +`workload-kv-read-keycape-{client_id}-{consuming_service}`, each with a templated +body (`read` on that one data path); new `openbao_role` +`external-secrets-keycape-{client_id}` (Kubernetes auth, ESO service account, +15m, no default policy); catalog entry `{consuming_service}-keycape-client`; +the two rollback steps on the KV path collapse into one metadata delete. The +schema and validator now require a `body` on policy and role items. 105 tests +pass. Two consequences: the delivered memo describes the type before these +changes (a case-set custody reference, two differently named policies, no ESO +role), so the founder answers on a superseded shape unless the memo is +re-sent; and Forgejo's custody path moves from the accepted request's proposed +`platform/workloads/forgejo/netkingdom-oidc-client` to +`platform/workloads/keycape/forgejo`, which key-cape (KEY-WP-0038) and +railiance-apps (RAPPS-WP-0016-T02) need to adopt in T06. ops-warden's review +(WARDEN-WP-0044-T01) is still in progress. + +2026-10-08: revised memo `nk-wp-0048-t05-oidc-browser-relying-party-r2` built +(`packet.md`, `intake.json`, packet `sha256:af6435e2…`); it states that it +supersedes the first memo and passes informed-decision's and the dispatcher's +validation. It needs a new memo id because the dispatcher refuses new content +under a delivered idempotency key (`idempotency_conflict`). Queuing it is the +same attended step as above. Both memos serve hub decision `e418263e`; the +first answer resolves it, so the first memo must not stay open: an accept on it +would acknowledge the superseded shape, and a decline would resolve the +decision against r2. Close it with `return` (a reviewer verb that requests a +new revision and does not resolve the hub decision; only accept and decline +do), commenting that r2 supersedes it, then answer r2. `withdraw` exists in +the vocabulary but is not offered to reviewers, and lifecycle `revise` only +applies to lifecycle-registered memos, not intake-delivered ones. + +2026-10-09: the founder reports r2 enqueued and the first memo returned. At +close, hub decision `e418263e` still showed `open` (not verified whether r2 +was answered or the answer is still in transit; the dispatcher ledger was not +read). ops-warden's review (WARDEN-WP-0044-T01, ops-warden +`history/2026-10-08-oidc-rp-follow-through-review.md`, d470f2e) arrived at +21:25, before r2 was built, and is not in r2. Required before WARDEN-WP-0044-T02: +R1 a per-step `lane` (missing: identity-provisioner caller credential, +railiance-platform broker grant), R2 canon meaning of `warden_executes: true` +(caller proxy, caller identity, no standing ops-warden token; ADR-0002), R3 a +per-step `value_handling`. Refinements R4 (one type-level catalog lane instead +of a per-case entry, which conflicts with ops-mason's item 5), R5 (ESO Ready gate +before bind-env), R6 (exact accepted memberships), R7 (mfa-pinned and +secret-delivered checks). Next: if r2 was accepted, fold R1-R3 and R5-R7 in as +a follow-up and settle R4 between ops-warden and ops-mason before T06; if not, +hold r2, fold them in and build r3. + key-cape and railiance-apps were told about Forgejo's new custody +path. + +2026-10-09: r2 was not answered (hub decision `e418263e` open; dispatcher 9 +delivered), so r2 is held and r3 replaces it. ops-warden withdrew R4 for R4′ +(message a66a9204): one catalog entry per case with its body templated in the +type. Folded in: + +- Canon section 3: a narrow meaning of `warden_executes: true` (caller proxy, + caller identity, no standing credential; ops-warden ADR-0002), plus per-step + `lane`, `value_handling` and an optional `ready_check`. A catalog entry body is + now required, like policy and role bodies. +- Schema and validator enforce all of these. +- The type: lanes `identity-provisioner-groups` and + `keycape-client-secret-generate` (both new) and `key-cape-oidc-login`; + `mint-secret` is `generated_in_custody`; a Ready gate on `deliver-secret`; + `ensure-groups` adds exactly the accepted case's memberships; new checks + `secret-delivered` and `mfa-pinned`; the catalog-entry body follows R4′. +- 110 tests pass. + +Memo r3 (`nk-wp-0048-t05-oidc-browser-relying-party-r3`, packet +`sha256:726dfc01…`) passes informed-decision's and the dispatcher's validation. +The founder queues it with the same command, returns r2 with a comment that r3 +supersedes it, then answers r3. + +Prerequisites for T06, not for T05: +- the two new lanes, each through its owner; +- a founder-approved rollout of an identity-provisioner image containing + 630ebfd (the deployed one is main-6c4fcaf from 2026-09-24, without + `/v1/groups/ensure`). + +ops-mason's request to create LLDAP group `fluid-telegram-operators` (message +259ec26d) was answered: not possible from here by that path; the founder creates +it in the LLDAP admin UI. + +2026-10-09: ops-warden finished its side of WARDEN-WP-0044-T01 (message +`58d33f40`, commit 0e7ceb8): the revised type conforms to `warden case +check-type`, with no further changes asked of the type. This acknowledges that +review. Its later addendum R8 (message `af274783`, ops-warden +`history/2026-10-08-oidc-rp-follow-through-review.md`) showed that lane +`identity-provisioner-groups` cannot exist as declared: the provisioner's only +HTTP credential is user-engine's runtime token, and its NetworkPolicy admits +only user-engine pods, so a caller-proxy lane would act as user-engine. Taken +option 1 (ops-warden's recommendation): `ensure-groups` and its rollback are +attended, in the `register-client` session on `key-cape-oidc-login`, run inside +the provisioner pod; no token is read. Option 2 (an operator credential of its +own on the provisioner) stays open for a later version. `warden case +check-type` now reports only `keycape-client-secret-generate` +(railiance-platform) as missing. 89 tests pass. + +Hub decision `e418263e` was still open, so memo r4 +(`nk-wp-0048-t05-oidc-browser-relying-party-r4`, packet `sha256:452ea421…`) +replaces r3; it passes informed-decision's and the dispatcher's validation. The +founder queues it with the same command, returns r2/r3 if still open with a +comment that r4 supersedes them, then answers r4. + +Prerequisites for T06 are now: lane `keycape-client-secret-generate` +(railiance-platform), and a founder-approved rollout of an identity-provisioner +image containing 630ebfd. + +2026-10-09: done. The founder queued r4 (`pending`, `queued`), returned r3 as +superseded and accepted r4 in informed-decision. informed-decision's hub +write-back is off in production (`INFD_HUB_WRITEBACK` unset), so net-kingdom +resolved hub decision `e418263e` from the founder's report, naming memo r4 in +the rationale. The type is `acknowledged`; it becomes `implemented` with T06. + +```task +id: NK-WP-0048-T06 +status: wait +depends_on: [NK-WP-0048-T05, NK-WP-0048-T08, NK-WP-0048-T09, NK-WP-0048-T12, NK-WP-0048-T13, NK-WP-0048-T14, NK-WP-0048-T15, NK-WP-0048-T16, NK-WP-0048-T17, NK-WP-0048-T18, NK-WP-0048-T20, MASON-WP-0010-T03, WARDEN-WP-0044-T03] +blocking_reason: "Type1.2 architecture has a confirmed source-only founder acknowledgment; canonical1.1 remains unchanged/default and the1.2 reference case stays proposed. Needs explicit version selection/reference-source integration without normal admission bypass, closed owner adapters plus global/native receipt verifier (WARDEN-WP-0044-T03), independently verified six-object structural materialization and CCR42 claims/created-only provenance, exact broker/case/source reconciliation, then separately authorized native mint/delivery and attended registry/consumer verification. Source acknowledgment, a bundle or local tests do not prove implemented/materialized/live status." +priority: high +state_hub_task_id: "d9b26bcf-a7c6-59a2-92a0-f913d76a203c" +``` + +### T06 — First KnownSecurityCase: Forgejo + +Write `security-cases/forgejo-netkingdom-oidc.yaml` from the parameters the +founder accepted in KEY-WP-0038-T01 (`key-cape/docs/forgejo-oidc-client-request.yaml`), +so that acceptance carries over as the case acceptance. ops-mason materializes it; +ops-warden follows through and verifies it. The type becomes `implemented` once +this reference case is live. + +This replaces the hand-built path proposed for KEY-WP-0038-T02. + +2026-10-09: case written, `security-cases/forgejo-netkingdom-oidc.yaml`, status +`accepted`: the example's parameters plus the founder's 2026-10-08 acceptance of +the key-cape request (owner and founder), with the custody-path derivation +covered by the type's acknowledgment (memo r4). It validates and renders (plan +`execution.permitted: false`; three attended steps on `key-cape-oidc-login`); +it needs `--allow-unimplemented` until it is live, since it is the case that +makes the type `implemented`. A test keeps it equal to the type's example. +Still open before it runs: ops-mason renders and builds it (MASON-WP-0010-T02, +T03, unblocked by T05), ops-warden plans and runs it (WARDEN-WP-0044-T02, T03), +railiance-platform's `keycape-client-secret-generate` lane (asked by ops-mason +`789feb39` and ops-warden `d9f889b1`, both unread), and a founder-approved +identity-provisioner rollout containing 630ebfd. + +2026-10-09: identity-provisioner rolled out, founder-approved in session: +`main-630ebfd` (`sha256:bafb4860…`, built by the image workflow) replaces +`main-6c4fcaf` (`sha256:ffacd5d7…`; only change: 630ebfd, additive). Applied +with `kubectl set image` on the Deployment only (generation 17 → 18, 1/1 Ready); +the file's Service, NetworkPolicies and Ingress were not reapplied. Verified in +the pod: `ensure_service_groups` and `revoke_service_groups` present, `/readyz` +200; no token used, no group changed. Rollback: `kubectl -n sso set image +deploy/identity-provisioner provisioner=forgejo.coulomb.social/coulomb/identity-provisioner@sha256:ffacd5d790a5b7800e0535f3aa95b7de52c7a68e4f575b9192c3e1622fa672c2`. +The pin in `sso-mfa/k8s/identity-provisioner/deployment.yaml` follows. + +2026-10-09: ops-warden closed WARDEN-WP-0044-T01 and shipped `warden case plan` +(T02, message `06303d1d`); the accepted Forgejo case is refused until ops-mason +materializes it, and a materialized copy plans cleanly once +`keycape-client-secret-generate` exists. Its observation that `bind-env` carries +a literal `KEYCAPE__CLIENT_SECRET`: the validator now renders a +`{param:env}` format spec (upper case, `-` as `_`, as `keycape-client.py` +derives the name) and rejects unknown specs, and follow-through and rollback +text is now checked for unknown placeholders too (patch-level, canon section 6). +The type keeps the literal until ops-warden's planner (`text.format`) renders the +spec as well; asked of ops-warden. 53 tests pass. + +2026-10-10: ops-warden rendered `{param:env}` in `warden case plan` +(23ba584, message `c1df14b7`; other specs and `!conversion` refused). The type +adopts it as a 1.0 patch (canon section 6): `bind-env` and its rollback now read +`KEYCAPE_{client_id:env}_CLIENT_SECRET`. ops-mason reports the Forgejo case +rendered and built (message `1ebddae9`; CCR drafts handed to railiance-platform), +so T06 now waits on the platform apply, the mint-secret lane and the attended +register-client session. + +2026-10-10: key-cape adopted the type's custody path (key-cape ab5cccd, message +`88a4559c`): `docs/forgejo-oidc-client-request.yaml` and KEY-WP-0038-T02 name +`platform/workloads/keycape/forgejo`, field `CLIENT_SECRET`, delivered as +`sso/keycape-forgejo-client` through `external-secrets-keycape-forgejo`, and the +`forgejo/netkingdom-oidc-client` proposal is withdrawn. This matches the type and +the Forgejo example; no change needed here. + +2026-10-10: key-cape's type-composition review (key-cape 6038483, message +`8e5ed5f6`) found the composition sound with two blocking findings, taken as a +1.0 patch (stricter validation and documentation, canon section 6): +R1, redirect URIs now refuse `?` and `#` in the type pattern and in +`keycape-client.py`'s `REDIRECT` (KeyCape refuses `?` at startup and the single +replica would stop); R2, `allowed_scopes` always includes `groups`, the renderer +refuses `access_groups` without it, and `access_groups` now says the consumer +enforces the gate because KeyCape has none; R3, `mfa_required` "false" means the +per-user privacyIDEA policy; R4, register-client's justification records the +restart for every client, the quiet window, and that the rendered registry must +pass KeyCape's client validation before it. The accepted Forgejo case and both +examples still validate; 116 tests pass. +Open, needs a decision: R2's verification step that the consumer enforces the +group gate is a new verification step, hence 1.1 (minor), and the validator pins +cases to the exact type version, so the accepted 1.0 Forgejo case would need +re-pinning. Also open with key-cape: a KeyCape entry point that validates a +rendered client registry without starting the server; until then +`keycape-client.py` mirrors the redirect rule. + +2026-10-10: founder go for 1.1, so no 1.0 run overlaps. The type adds +verification `consumer-enforces-groups`, and hand-over now requires the consumer +to enforce `access_groups` (minor, canon section 6; risk class unchanged, no new +acknowledgment; history in the type README). The accepted Forgejo case and both +examples are re-pinned to `oidc-browser-relying-party@1.1` before any apply; the +acknowledgment intake keeps `@1.0` as the record of what was acknowledged. +ops-mason's build and ops-warden's case-ref digest predate this and need a +re-render. + +2026-10-10: ops-mason re-rendered the Forgejo case from `@1.1` (message +`22c6bba6`; plan digest `af1f4f81…`, no collisions) and rebuilt the bundle. The +rendered OpenBao objects are identical to 1.0, so the already applied +CCR-2026-0041 stands; 1.1 changes only consumer-side steps. key-cape shipped +`keycape check-config` (c528daa, message `a7b0aeff`): KeyCape's startup checks +without secret values, including the new duplicate-clientId rule. +`keycape-client.py --check-config ` now runs it on the live and the +proposed registry, fed on stdin, and apply refuses a proposed registry that +fails; register-client's entry point names the option (1.1 patch). The deployed +image predates c528daa, so the binary is built from key-cape source for now. +Before the next KeyCape rollout (Forgejo register-client or the CCR-2026-0018 +window), the attended operator runs `inspect --check-config` so the live +registry is checked too. + +```task +id: NK-WP-0048-T07 +status: wait +depends_on: [NK-WP-0048-T06] +blocking_reason: "The next types follow once the first one is proven end to end." +priority: medium +state_hub_task_id: "c3c9c3ae-4f23-5ba7-9e6f-e63bcf29b9f7" +``` + +### T07 — Queue the next types + +From the T02 ranking, open the next type drafts (likely the confidential service +client and the workload KV read lane), each to be acknowledged on its own. +Record which existing hand-built instances get adopted as cases, without +rewriting them in place. + +```task +id: NK-WP-0048-T08 +status: done +priority: high +state_hub_task_id: "8e519082-dc8b-5e46-b660-03d03e55dcf6" +``` + +### T08 — identity-provisioner: ensure service-access groups + +The type's `ensure-groups` step calls `identity-provisioner groups ensure --groups + --members `. The provisioner already has idempotent +`_ensure_group` and `_add_group`, but exposes only tenant-scoped operations. Add +the general operation (create if absent, add the listed memberships, never +remove others), its rollback (remove only this case's memberships), tests, and +the ops-warden route for its service identity. + +2026-10-08: done. `identity-provisioner/provisioner.py` gained +`ensure_service_groups` (`POST /v1/groups/ensure`) and `revoke_service_groups` +(`POST /v1/groups/revoke`), behind the existing service-token check. Names +matching `tenant*`, `netkingdom-*` and `lldap_*` are reserved; members must +already exist; everything is validated before any change; memberships are read +back before success; nothing else is ever removed; groups are deleted only when +asked and empty. 7 new tests (39 in the provisioner suite). The type's +`ensure-groups` step and its rollback now name these endpoints. The ops-warden +route for the provisioner's service token belongs to WARDEN-WP-0044-T01. + +```task +id: NK-WP-0048-T09 +status: done +priority: high +state_hub_task_id: "0ee12dfc-8260-5038-bd39-cc7fb4113be0" +``` + +### T09 — The `keycape-client` entry point and its declaration + +Planned first as key-cape KEY-WP-0038-T04. Moved here because the machinery a +live registration needs already lives in net-kingdom, where KeyCape is deployed +(`sso-mfa/k8s/keycape/`): the contained kubectl, the cluster-identity pin and the +issuer pin. A copy in key-cape would drift. + +2026-10-08: done. `sso-mfa/k8s/keycape/keycape-client.py` derives the KeyCape +client record from a case (`client-from-case`). It adds or removes exactly one +client with the existing safeguards (byte-preserving merge, exact match or +refuse, shared-reference refusal, compare-and-swap, read-back, contained errors), +requires the Deployment env binding first for confidential clients, and requires +the case acceptance record for `apply` instead of a per-client founder decision +id. 15 tests, including a regression showing that the vergabe case reproduces +its hand-built registration exactly; all 71 helper tests pass. Declaration +`capabilities/playbooks/net-kingdom.keycape-client-registration.yaml` (contract +PASS, `maturity: implemented`) replaces key-cape's draft. The type now pins it, +and for confidential clients adds `deliver-secret` (delivery from custody into +the sso namespace) and `bind-env` (Deployment env), attended in the same session +as `register-client`. The per-client helpers stay until their clients are +adopted as cases. + +2026-10-10 — NetKingdom owner review of the execution seam, source only: +ops-warden/docs/security-case-execution-proposal.md SHA256 78ab1439e4c5beb205aeccf15b1b2e706511be55ab195c5a3aa682f326c7c77c +was reviewed against INTENT, SecurityCase canon, acknowledged type1.1, current +case and owner source. docs/security-case-execution-owner-review.md supports +the closed adapter/receipt direction with explicit versioned schema, structural +materialization provenance, unchanged caller/session boundaries, safe CAS +reconciliation and exact-created-object rollback requirements. This is owner +review, not approval. Supporting file digests are in the adjacent sources JSON. + +Current source exposes a circular wait: ops-mason T03 waits on mint while Warden +and the broker refuse a non-materialized case. Structural acceptance therefore +needs its own native pinned receipt before mint, not a successful mint before +materialized. The platform broker additionally pins type1.0 while Forgejo is +accepted at1.1; platform-owner alignment and tests are required, not an unchecked +version bypass. T06 remains wait. No case/type lifecycle status, grant, authority +or live runtime changed by this review. A proposed informed-decision would +adopt the source contract only; native materialization and execution acceptance +stay distinct. + +Final proposal re-review: staged Warden proposal SHA256 78ab1439e4c5beb205aeccf15b1b2e706511be55ab195c5a3aa682f326c7c77c +adds only the explicitly local reducer appendix, not an accepted wire protocol +or executor. The exact-final review and sources JSON supersede the earlier +1035f049... proposal review. Platform staged the exact type1.1 broker correction +with ten fake-backend subprocess checks (script SHA256 +9576cd211cfc06526ec4cc9bd0a5b06f0f59287a68931bb2d6e3592a951ddc7d). +The next source work is the versioned closed adapter/receipt/materialization +contract, not another relaxation of the broker's status gate. Native work still +requires installed broker provenance, structural reader receipts, exact new +materialized-case bytes, caller grant/session revocation and verified execution. +No installed/native acceptance is inferred from that staged correction. + +2026-10-11 — new source-only type1.2 architecture acknowledgment: +Confirmed native founder ACCEPT of infd-20261011-forgejo-type12-source-r1@1 +at06:47:39Z, approval8e9332c8-754a-420c-b8b7-445cb4fe6765. Exact frozen packet +0b377d647ca748cd03c92f9792396412048055d4bde9f7bb06ff6d413f7acd68 and submission +585cee731de30d2b37883a57abfd9eb32f023ee694e8d911989109d5caf80569; native receipt +retained in docs/evidence/2026-10-11-forgejo-type12-source-acknowledgment/. +The complete acknowledged1.2 source package is isolated under +security-case-type-revisions/1.2/oidc-browser-relying-party; default loader and +current accepted1.1 case remain untouched because load_type is single-version. +The new proposed1.2 case is NOT accepted/materialized/live and has no acceptance +fields. Actual reference-migration design approval is a separate Apps owner +receipt; no1.1 approval is inherited. Source-recording review/test proof and exact +next gates are in docs/forgejo-type12-source-acknowledgment-followthrough.md. +T06 remains wait and T07 remains its existing dependent task; type is NOT +implemented and no first-case/native grant or broader workplan finish is asserted. + +2026-10-11 — bounded explicit version selection source preparation for T06: +The validator now offers optional exact version_roots/--version-root VERSION=PATH +selection with duplicate/mismatch/traversal/symlink refusal and no fallback. The +default1.1 source and historicalacceptedcase hashes/ordinaryrefusal/preparation +render stayunchanged; actual1.1 is acknowledged, not implemented. Acknowledged1.2 +ordinaryadmission also refuses; optionalpreparation remains nonexecuting.43local +validatorchecks pass and original1.1 CLIrenderbytes match exactly. Makerproof: +docs/evidence/2026-10-11-explicit-type-version-selection/source-integration-validation.json. +FormalNetownerreview/reference-source integration and all existing native adapter, +provenance/broker/materialization/attendedproof gates remain; T06 stayswait, no +new residual or firstcase/typeimplementation/native grant is inferred. + + +2026-10-16 — isolated source preparation for T06 (canonical integration pending): +Added `check-evidence` to the existing validator CLI. It reuses exact version +selection and ordinary type/case/render admission, checks actual Mason structural +request source/inventory/projection pins and reads actual Warden journal rows +read-only. Successful rows require exact type-declared descriptors and attempt/ +revocation fingerprints. Existing acknowledged types without machine descriptors +remain plan-only. The compatibility result never claims authenticated native +completion: Request and journal fingerprints are not portable native evidence. +Owner authentication/status binding and actual native gates remain unresolved; +T06/T07 statuses and both type/case lifecycles remain unchanged. No dispatch, +credential access or authority expansion is introduced. See tool README for the +existing owner protocol boundaries and source integration tests. + +Concrete next Warden owner integration: current `Executor.admit` refuses every +case parameter whose sensitivity is not public/operational, so browser +`redirect_uris`, `allowed_scopes`, `client_type`, `mfa_required` (and 1.2 +`consumer_namespace`) fail after otherwise prepared admission. The sensitive +classification is a security-change review level, not secret content. The +Warden owner must validate through NetKingdom's existing exact-version case/type +validator, then project only descriptor-declared safe target metadata; do not +remove the credential/value refusal or copy complete parameters into journals. +Actual owner descriptor acknowledgment and independent evidence admission remain +prerequisites, with normal admission still refusing unimplemented types. + + +## T10 — Source compatibility checker for Mason and Warden evidence + +```task +id: NK-WP-0048-T10 +status: done +priority: high +state_hub_task_id: "4b06ca97-c1e4-5680-bf22-ab75ab1646a4" +``` + +Completed 2026-10-16 in published source `c31d21ba`. The read-only +`check-evidence` command checks the actual Mason structural request and Warden +journal source protocols against an exact version/render. Independent source +review passed 103 tests at that checkpoint. It always reports native completion +false and execution prohibited. Fixtures and local journal fingerprints do not +provide authenticated owner/native evidence. This records completed source scope +previously described under T06; it does not duplicate native acceptance credit. + +## T11 — Validate the optional machine-descriptor pin contract + +```task +id: NK-WP-0048-T11 +status: done +priority: high +state_hub_task_id: "ef2a0119-f59e-5e45-9519-115da9df7b85" +``` + +Completed 2026-10-16. Reproduced the JSON schema rejecting a valid +`execution_descriptors` map while the Python validator accepted malformed maps. +The schema now supports the existing Warden optional exact-byte pin map; semantic +validation binds keys to declared steps, bounds the map and checks digest shape. +The direct evidence API refuses malformed maps before accessing them. Twenty-one +new checks and all 127 affected source checks passed. No current type/case bytes, +status, owner descriptors or native acceptance changed. Source evidence and +publication receipt: Custodian +`docs/changes/netkingdom-target-2026-10-16/browser-acceptance-next/`. + +## T12 — Prepare NetKingdom-owned attended step adapters + +```task +id: NK-WP-0048-T12 +status: todo +priority: high +state_hub_task_id: "7ce4eee3-cd47-55a9-bfa5-4f1835d4d6fd" +``` + +Owner: NetKingdom. Retained scope: prepare bounded attended Deployment env, +config verification and restart/readiness steps around the existing helpers. +Next action: identify exact current entry points and their revision/readback +contracts, then implement fixed source-pinned callbacks or receipt-only attended +steps. Completion requires positive/refusal checks, exact target/source pins and +uncertainty behavior without acquiring user-engine runtime identity or exposing +registry/client secrets. Existing caller/session, cluster/issuer, CAS/readback +and status-only output boundaries remain mandatory. + +2026-10-16 task refinement: the original broad adapter scope is split into T13 +(completed registration source candidate), T14 (group creation provenance), T15 +(durable registration recovery) and T16 (descriptor/artifact selection and owner +review). This preserves T12's task ID and Hub UUID while giving each remaining +outcome an owner and completion gate. T06 now names every source dependency; +its native/reference lifecycle acceptance scope remains unchanged. Completion +of any source task supplies no Warden, Platform or Apps assent and no native +execution authority. + +## T13 — Prepare the fixed registration adapter source candidate + +```task +id: NK-WP-0048-T13 +status: done +priority: high +state_hub_task_id: "b264c10b-565c-5d29-b788-03cf5fbd2883" +``` + +Completed 2026-10-16, source implementation and synthetic fixture proof only. +Added an injection-only adapter around actual `keycape-client.run`: fixed +case/principal/human/lane/source/acceptance binding, existing CAS/readback, +metadata-only owner evidence and per-attempt parent-session revocation boundary. +The descriptor source composition pins the exact checker executable digest +alongside adapter/helper source. Creation-only rollback authenticates its +creation receipt and refuses changed postcreation UID/resourceVersion; +pre-existing clients confer no rollback authority. Missing readback, evidence, +revocation or process-local creation provenance cannot become retry-safe success. + +Evidence: Custodian `docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/` +R3 candidate, explicit patch and postimages, retained R1 rejected custody finding +and R2 revision, and 36 passing source checks (16 new, 20 existing). Three checks +use actual Warden kernel admission/dispatch/rollback with published Warden +`d97652a3bb5fa733b063d71a1b74068efcccf572` producer pins unchanged. Owner trust, +descriptor/type/case and Kubernetes objects remain synthetic fixtures. This +records a completed source deliverable, not independent review, publication, +type acknowledgment, exercised procedure, identity admission or native browser +acceptance. Independent root source review: Custodian browser-adapter-next/coordinator-review-r3.json. Source integration and normal publication/synchronization receipts: Custodian browser-adapter-next/canonical-integration.json. These receipts supply no other-owner assent or native authority. + +Source publication: `0775c2bfa41af236301155bb5a69bbf088bdf823`, independently +reviewed by the Custodian coordinator before integration. The required initial +projection and ordinary UUID writeback both completed with zero failures and +zero automation errors. Generated-only brief commits were refused by the normal +hook; exact pre-existing staged/working brief bytes were restored. The separate +canonical integration receipt retains those refusals and projection evidence. +Native admission and all other-owner acceptance remain open. + +## T14 — Admit governed group rollback against native owner boundaries + +```task +id: NK-WP-0048-T14 +status: progress +depends_on: [NK-WP-0048-T20, NK-WP-0048-T21] +priority: high +state_hub_task_id: "fce95440-a25b-5f27-8ef3-a3d182099113" +``` + +Owner: NetKingdom. Next action: bind the completed T21 PostgreSQL journal to +an admitted dedicated CNPG consumer through the existing `rapp-postgres` +database-per-consumer contract. Identify the actual deployment/package and +custody owner, admitted placement/capacity and role/renewable lease/TLS target; +implement the independently retained exact content checkpoint and authoritative +tenant/case/source/principal callbacks before enabling governed group operations. +The injection-only backend and fixture callbacks do not supply production scope +or authorization. Existing service tokens, caller-supplied JSON and provenance +receipts confer no new grant. The default production endpoint remains disabled +without the trusted integration. SQLite remains local/disposable only; any +production exception requires explicit scoped governance under ADR-0015. + +2026-10-17 owner-integration refinement: T23 records the completed source/package +placement review, distinct from T21's source/disposable-SQL proof. Its dedicated +`rapp-identity-provisioner` recommendation is draft and unbound, not an accepted +package or existing repository. The full six-consumer rapp-postgres renderer +accepts the inert candidate on platform-pg-2 as its third of four declared +consumers; first-cell placement is refused as a fifth. Neither the estimated +four-consumer ceiling nor source requests establish native headroom or backup. + +Next independent owner contracts: NetKingdom/package/substrate owners accept +exact deployment/rollback fate; rapp-postgres admits the dedicated consumer, +roles, five-connection budget and actual backup/restore; Platform admits exact +renewable runtime/migration delivery and TLS/CA/SAN; network owners scope SQL, +broker and checkpoint paths without assuming a narrow policy subtracts additive +same-namespace access. Protected caller owners must provide authoritative +GroupJournalBinding construction and current authorization; an independent +existing holder must authenticate and atomically advance the exact content +checkpoint, not a counter or ordinary Audit receipt. Directory/runtime owners +must select and prove either an exclusive all-writer maintenance barrier or +provider-supported CAS/generation, including established sessions, lifecycle +cascades and remove/readd ABA. No source review establishes those owner facts. + +The default PostgreSQL activation and destructive native rollback remain refused. +Storage/lease acceptance can be proposed separately from LDAP mutation only after +exact owner source/target/custody/verification/recovery and cost/risk review; +State Hub guard approval grants none of it. Source-only integration review and +native acceptance matrix: Custodian native-storage-r1/OWNER-REVIEW.md. T14's +existing status, UUID, dependencies and native completion gate are retained. + +2026-10-17 refinement: T20 retains the historical durable SQLite source and +actual disposable LLDAP proof. T21 extracts the completed PostgreSQL source and +bounded real disposable SQL compatibility proof. T14 remains `progress` for +native CNPG admission/recovery, protected owner integration and exclusion of all +external LDAP writers. Separate source publication/projection and independent +cleanup receipts belong to coordinator follow-through; no missing native assent +is represented as received. + +Completion still requires independently reviewed/admitted ADR-0015-conforming +CNPG storage (or an explicit governed exception), backup/lost-store recovery and +migration of legacy requests; a native +writer-isolation/fencing or provider CAS/version contract that closes external +writer races and remove/re-add ABA; exact case/source/principal and current +native object bindings for protected callbacks; and actual per-attempt evidence, +revocation and readback/refusal verification through the existing owner paths. +Only independently retained confirmed creations may be undone. Every +pre-existing/shared group and membership must remain preserved; unresolved +intents never expire into retry authority. The disposable fixture supplies none +of these native custody, authorization, owner-acceptance or production claims. + +## T15 — Define authenticated recovery for lost registration process state + +```task +id: NK-WP-0048-T15 +status: wait +depends_on: [NK-WP-0048-T31] +blocking_reason: "Source candidate 061461e is merged. Waiting on the owner to select the real authenticated original-attempt holder and native readback, and on independent re-review of the changed T13 adapter source." +priority: high +state_hub_task_id: "1b825705-4848-5edc-832f-b1622cd9bdb7" +``` + +2026-10-17 source part merged (061461e via 65559ef). `build()` takes optional +paired `holder`/`observe` capabilities and installs `reconcile_original`; the T31 +fixtures now run through the adapter's own path. Rollback after restart is still +refused, and there is no add replay. This changes `keycape-registration-adapter.py`, +so the revision reviewed under Custodian `browser-adapter-next/coordinator-review-r3.json` +is superseded. The new revision has no independent review yet and must not be +pinned on the strength of the old one. + +2026-10-17: T31 fixtures done (74adcd7). What remains for T15: the owner +selects the real authenticated original-attempt holder, and the T13 adapter +gains the holder capability, `reconcile_original` and reconcile-aware `verify` +described in T31. + +Owner: NetKingdom for registration state, with Warden owning kernel evidence +admission. T22 records the completed source review and exact existing-seam gaps; +T15 retains implementation and authenticated restart/lost-receipt acceptance. +Next action: select and admit the actual owner publisher/authenticator and bounded +original-attempt readback contract for the merged holder/observe adapter seam. +Warden's original-intent persistence/interface (`a45cf475`) and additive validation +(`117fec9241e7b985dfb187b992e173d0a5dc3700`) are now published and projected. +The local Journal retains admitted nonsecret original context, validates original +actor/attempt and metadata linkage to the original running receipt, and supplies +it separately from the new reconciliation attempt. This local consistency is not +independent original-holder authorization or creation provenance; neither the +journal nor a current matching registry entry reconstructs creation authority. + +The owner contract must bind the original Invocation, case/type/descriptor/catalog/ +acceptance pins, source, principal and admitted tenant/realm provenance, separately +from the new reconciliation attempt and its independently revoked parent session. +Use an existing authenticated holder/readback; Audit Core's custody API is a +candidate only until its actual registration source/class, load-bearing emission +contract and separate scoped reader are admitted. No new broker or receipt trust +root is proposed. A missing original record, ambiguous publication, NO_CHANGE, +changed native revision or lost readback stays uncertain; no add replay or undo. + +Completion requires restart/lost-receipt fixtures against the selected actual owner +contract: independently authenticated original created-only provenance, original +exact client entry and unchanged current postcreation UID/resourceVersion before +any undo, with wrong attempt/case/source/principal and unavailable-holder refusals. +The merged T13 adapter exposes reconciliation only through paired injected +holder/observe capabilities and still refuses post-restart rollback. Existing +source review, JSON receipts and synthetic trust do not satisfy independent +holder authorization/provenance or native admission, or authorize attended recovery. +Exact remaining owner facts: Custodian native-registration-r1/original-attempt-review.json. + +2026-10-17 check (net-kingdom-worker): Warden's original-intent lookup interface +is not published — ops-warden head is `d97652a`, with no WARDEN-WP-0044-T03 +commit, and the net-kingdom inbox holds no announcement. No restart/lost-receipt +fixture or readback seam was added this session; the work is deferred behind +T16's decomposition under the same token budget. Remains todo, gated on Warden +publishing the reviewed lookup/context interface and on owner selection of the +authenticated original-attempt holder. + +2026-10-17 current-source correction: the earlier same-day `d97652a` lookup +checkpoint and T31 adapter-next-action note above are historical and superseded +by the published Warden interface/validation and merged `061461e` adapter seam. +The 54 focused Warden checks establish source behavior only. Current admission +leaves tenant/realm empty; no admitted tenant/realm provenance is inferred. +T15 remains wait for actual independently authenticated owner-holder/readback +selection, independent review of the changed adapter, and native restart/lost- +receipt acceptance. Existing task metadata, owner gates and historical reviews +are unchanged. + +## T16 — Select and review the actual machine descriptor composition + +```task +id: NK-WP-0048-T16 +status: todo +depends_on: [NK-WP-0048-T24, NK-WP-0048-T25, NK-WP-0048-T26, NK-WP-0048-T27, NK-WP-0048-T28, NK-WP-0048-T29, NK-WP-0048-T30] +priority: high +state_hub_task_id: "200bf500-5084-5ae7-be26-3033fe1f296e" +``` + +2026-10-17 coordinator review r4 approved the 1.3 candidate at 1920b13 with no +findings (the-custodian `nk48-adapters-1.2-review/coordinator-review-r4.json`). The founder chose +option A of the T16 owner-review packet (`nk48-t16-owner-review/founder-option-decision.md`): +1.3 with execution descriptors for all seven steps. Owner reviews for A are open +in the-custodian `docs/changes/netkingdom-target-2026-10-17/nk48-1.3-owner-review/` (net-kingdom, +railiance-platform, railiance-apps, ops-warden, key-cape). The owner-review packet +supersedes the r1 composition pointer: T16's exact composition is now +`sso-mfa/k8s/keycape/descriptor-composition-1.3.candidate.json`. + +2026-10-17 successor candidate prepared (source only, `proposed`): +`security-case-type-revisions/1.3/oidc-browser-relying-party/` is the 1.2 package plus +an `execution_descriptors` map of the seven composed descriptors, with examples +pinned to `@1.3` and no acknowledgment. ensure-groups, hand-over, +deliver-consumer-secret and the composer accept 1.3 (835680c, merged acda26c). +`test_admit_1_3_type.py` runs Warden's real admission against the committed 1.3 type: +the proposed type is refused; an acknowledged copy is refused because ops-warden +pins parameter fingerprints only for 1.1 and 1.2; and with only a 1.3 pin (equal to +1.2's fingerprint) all seven are admitted. This is a major change under canon +section 6. Next: r4 source review of the changed adapters and the map, then owner +reviews, including the ops-warden 1.3 pin, the mint-secret warden_executes/human +question and the owner-run steps question, then the founder's Decision Memo. + +Owner: NetKingdom for descriptor/type source and exact checker artifact; Warden +independently owns global provenance/admission. T22 records the completed seven-step +source1.2 inventory and regenerated register-client descriptor using the delivered +checker and current sealed-snapshot source; T16 retains exact owner review, +complete descriptor composition and admission. Next action: bind actual native +publisher/authenticator, original/current session verification and revocation, +executable storage/custody and config pins, then obtain the six missing fixed +owner descriptors/adapters for ensure-groups, mint-secret, deliver-secret, +bind-env, deliver-consumer-secret and hand-over. The inventory identifies each +existing owner seam, exact missing contract and created-only rollback requirement; +it is not an executable bundle. T14 and T15 remain independent prerequisites. + +Review the dedicated consumer source1.2 composition only through the existing +explicit version-root selection. Keep canonical1.1/default and its accepted case +unchanged, and the source-only acknowledged1.2 reference case proposed without +acceptance fields. New descriptor bytes require a reviewed exact successor +revision/version under canon section6; they do not inherit historical source +acknowledgment. Changed authentication/materialization/trust needs the prescribed +informed-decision acknowledgment. Do not insert a partial descriptor map or use +--allow-unimplemented preparation as ordinary case/native admission. The registry +helper reports restart_required; separately review quiet-window restart/readiness +and actual consumer verification rather than infer activation from registry CAS. +Exact composition: Custodian native-registration-r1/descriptor-composition.json. +Completion requires separate owner source review of exact machine descriptors, +artifact/source/config pins, normal type/version admission and independent +evidence-verification boundaries. Warden d97652a refreshes producer validator +pins for NetKingdom 015694ee; it does not attest T13's new adapter. Pinning any +machine descriptor into an acknowledged type is a separately reviewed source +change. Platform owns structural/mint/broker/delivery evidence; Apps owns actual +consumer login/group enforcement. Neither their assent nor native acceptance is +supplied by this task. + +2026-10-16 source checkpoint: T19 below extracts the completed per-attempt +executable snapshot implementation and reviewed fixture proof. T16 retains +native executable storage/custody, exact owner evidence/session callbacks, +complete descriptor composition and ordinary type/version admission. The +successor descriptor/source pins are proposed only; the earlier descriptor +history is preserved. T14 group provenance and T15 durable restart/recovery +remain independent open requirements. No native acceptance is counted here. + +2026-10-17 budget checkpoint (net-kingdom-worker): no source change made. The +T13 registration adapter alone is 217 source and 317 test lines plus an +independent review; six further owner descriptors/adapters cannot be completed +within one per-task token budget (soft 8000 / hard 10000), so the task was +stopped below 50% and is decomposed rather than pushed through. Proposed split, +one source-candidate task each with positive/refusal fixtures, canonical1.1 +default unchanged and no --allow-unimplemented admission: (a) ensure-groups, +bound to T14's journal/provenance seam; (b) mint-secret and (c) deliver-secret, +Platform-owned evidence seams; (d) bind-env and (e) deliver-consumer-secret; +(f) hand-over, Apps-owned consumer verification; then (g) descriptor composition +over the reviewed set. Each candidate needs its owner's source review before +pinning; none is authored here as owner-accepted. Status stays todo. + +2026-10-17 task refinement after coordinator r5: completed independent mint- +guard regression coverage is extracted as T32. T33 tracks the exact Platform +issuer-store source/reference correction; T34 tracks the Apps configuration +inverse reachable without secret or hand-over resource creation. T16 retains +final exact seven-descriptor composition, independent owner reviews and admission; +its status and existing dependencies are preserved. The kernel branch also needs +its own KR1-F1/F2 admission/dispatch corrections and independent review under +WARDEN-WP-0044-T03 before any 1.3 pin. Historical r4 acceptance does not supersede +r5's findings or establish native readiness. + +## T17 — Build and exercise the exact offline config-checker artifact + +```task +id: NK-WP-0048-T17 +status: done +priority: high +state_hub_task_id: "8c3e4baa-a218-5996-af9e-fe1153a46dac" +``` + +Completed 2026-10-16, isolated artifact/source fixture proof only. Built the +existing `key-cape/src/cmd/keycape` command from published clean source revision +`3c71226a1a7b8a2bdb710e9c497e1a1f7f8a3987`, source tree +`f0010b9cab17dbc3a787628a29657d98b900320f`, with cached Go 1.23.0, +CGO disabled, Linux/amd64 v1, trimmed paths, disabled VCS stamping and offline +read-only module selection. The full retained closure pins 108 source files, +9876 toolchain files and ten used module source directories. Three isolated +build locations produced identical 13633409-byte artifacts: +`sha256:be37738fe1c08259fb4f9ca0daf0f9301bfffc237e5bb38435525dfbf11ccff5`. + +Eight disposable checks use the actual executable and maintained helper/kernel, +with complete synthetic configs and a minimal environment: add/created-only +removal, normal pinned producer admission, rollback, lost-readback uncertainty, +stale revisions, substituted checker refusal, invalid startup config and missing +secret-env refusal. No checker stub or producer-pin override remains. The +artifact-bound descriptor proposal is +`sha256:76b1153d188062ece53fcd5e8d35290fdbfd02a69dd79d4b001fc42781afcdc3`. +Evidence: Custodian +`docs/changes/netkingdom-target-2026-10-16/browser-checker-artifact-next/`. +The first two build attempts succeeded but their closure collection failed; +both failures are retained, as is refusal of the older incomplete synthetic +fixture. No production config, credentials or cluster mutation was used. + +This extracts completed build/fixture scope from T16 without claiming KeyCape +owner endorsement, native checker selection/storage custody, acknowledged type +integration, native evidence/session callbacks or attended browser acceptance. +T16 retains exact owner review and composition/admission scope. The attended +registration procedure remains unexercised. + +## T18 — Make checker input and executable selection deterministic + +```task +id: NK-WP-0048-T18 +status: done +priority: high +state_hub_task_id: "9c45da71-fb4c-5f20-9065-90f448f37c64" +``` + +Completed 2026-10-16, isolated source correction and real-artifact fixture proof +only; independent integration/publication remain separately recorded. KeyCape +source review identified non-secret `KEYCAPE_ACCOUNT_PORTAL_URL` and +`KEYCAPE_BROWSER_LOGOUT_URL` overrides in config.Load. The original helper +inherited ambient environment, allowing valid supplied configuration to be +rejected or invalid supplied URL configuration to be admitted. A bare relative +checker filename could also be replaced by PATH lookup after the adapter had +hashed the selected local file. All three counterexamples are retained against +the unchanged predecessor. + +The existing helper now resolves the selected checker file to an absolute path +and passes an explicit empty subprocess environment. Three opt-in real-checker +regressions verify both URL-verdict directions and relative-file selection under +a non-checker PATH decoy. All 39 affected checks pass with the exact supplied +artifact, as do 11 real-artifact helper/kernel scenarios. Source evidence: +Custodian `docs/changes/netkingdom-target-2026-10-16/browser-checker-environment-r2/`. +Historical descriptors remain retained; the helper correction changes successor +source-composition pins and requires their separate review/admission. An absolute +path is not immutable custody and does not eliminate file replacement races; +T16 retains native executable storage/custody and callback/type admission. +No production configuration, credentials or cluster mutation was used. + +Source integration and publication: `916ba4b33e03179274d03b78bd898a4843e8653f`, following independent +Custodian source review in browser-checker-environment-r2/coordinator-review.json. +The ordinary protected projection pass assigned T17/T18 UUIDs with zero failures +and zero automation errors; generated-only brief commit refusal is retained. +The exact prior staged/working brief bytes were restored. Final source/publication/ +projection evidence is retained in Custodian browser-checker-environment-r2/ +canonical-integration.json. These source receipts do not admit native executable +custody, owner callbacks, acknowledged type changes or browser identities. + +## T19 — Bind checker execution to verified immutable attempt bytes + +```task +id: NK-WP-0048-T19 +status: done +priority: high +state_hub_task_id: "f50fdcd1-00be-5c3c-be0c-345aa2126032" +``` + +Completed 2026-10-16, independently reviewed source implementation and synthetic +fixture proof only. The registration adapter now opens a bounded regular checker +artifact without following its terminal symlink, verifies the reviewed digest, +and snapshots the exact bytes into a write/grow/shrink/seal-removal-sealed Linux +memfd before helper access. Live and proposed config checks execute that same fd +through procfs with the existing empty environment; the fd closes on completion +or failure. Unsupported snapshot/seal/exec policy refuses through the existing +parent-session revocation and uncertainty boundaries. The earlier unbounded +pre-snapshot path read was removed during independent review; actual adapter +symlink/FIFO/oversize refusal tests verify helper access never starts. + +Evidence: Custodian `docs/changes/netkingdom-target-2026-10-16/browser-resume-r1/`, +including exact base/preimages/postimages/patch, proposed descriptor, coordinator +source review and canonical integration receipt. Independent Custodian checks +passed 49 affected source tests and 12 actual Go checker/helper/kernel scenarios; +the sets overlap and are not separate acceptance counts. Existing published +Warden d97652a producer pins remain unchanged. The exact KeyCape artifact remains +`sha256:be37738fe1c08259fb4f9ca0daf0f9301bfffc237e5bb38435525dfbf11ccff5`. +The predecessor path-replacement refusal and the first expanded reopened-fd test +failure are retained; the latter fixture was corrected to restore write mode +before independently proving seals block reopened-fd writes and shared mmap. + +This source task adds no native owner custody, callback, evidence trust, type +acknowledgment, credential access, consumer sign-in or rollback acceptance. +Executable memfds, Linux seals and usable procfs are explicit runtime dependencies +and still require native admission. This is an extracted source outcome from +T16, not completion of T16; T12/T14/T15 and all external-owner gates remain open. + + +Publication verified by the coordinator after the source worker usage limit: +`8790dd18ae83c4651ef3d1d923312a118a5b436b` is the actual remote main revision. +All five canonical source hashes match the reviewed packet. Normal synchronization +completed with zero failures/automation errors; all 19 task statuses match the +projection. T19's assigned UUID is retained. The original staged and working brief +bytes were restored and independently compared. The exact source, projection and +publication checks are in Custodian browser-resume-r1/canonical-integration.json. +Native acceptance remains open under T16 and the other named owner tasks. + + +## T20 — Prepare durable group provenance and prove disposable LLDAP compatibility + +```task +id: NK-WP-0048-T20 +status: done +priority: high +state_hub_task_id: "863fcbdb-e246-51ba-87ca-a2f44d01d6f8" +``` + +Completed 2026-10-17, source candidate preparation and bounded synthetic +compatibility proof only. This extracts evidenced implementation/fixture scope +from T14; T14 retains native storage, writer isolation, protected callback and +owner-acceptance gates. Source integration/publication and State Hub projection +are pending the coordinator's separate receipts; no revision or task UUID is +invented here. + +The existing identity-provisioner group ensure/revoke interface now has a +reviewed candidate for a bounded owner-private SQLite journal, proven only in +local/disposable use. The initial storage instructions wrongly suggested a native +persistent-volume path; source review against accepted ADR-0015 corrected them +before publication. Production SQLite-on-a-PVC is not admitted: T14 retains +CNPG/rapp-postgres storage admission or a separately governed exception. +The local journal saves +complete prestate and intent before each attempted directory mutation, inventories +only confirmed new groups/memberships, binds original ensure idempotency and +correlation to the exact request digest, and refuses unsigned caller inventories +or legacy broad revoke requests. Replays never recreate removed state. Missing, +replaced or untrusted storage and missing intent rows stop execution; definite +before-mutation refusals retain evidence without stranding unrelated valid work. +Interrupted writes/readback remain unresolved across reopening. Revoke validates +all targeted native ids and complete member postimages before destructive work, +removes only confirmed creations and never deletes a preexisting empty group. +Server/configuration wiring, image-copy closure and owner storage/request +migration instructions are included. The existing endpoint's caller +authentication remains the existing boundary, not a new grant. + +The candidate was prepared against NetKingdom +`878abc93588e12b6da1bac055cfbb3abb8d7adf1`; exact source patch digest +`sha256:1f3f3ca94c46dadac8f24896053d4d23179de795e50a24298a0cfbcc46cca30e`. +The independent Custodian review passed all 65 provisioner checks. The earlier +62-check candidate, two independent-review findings and subsequent fixes remain +retained rather than overwritten. Seven additional fixture-plan checks exercise +the preparation boundary; these counts overlap the same candidate and do not +measure task-completion percentage or independent acceptance. + +A separately reviewed bounded fixture then exercised the exact candidate against +cached pinned disposable LLDAP and Python images, through actual GraphQL. All 14 +closed compatibility/containment oracles passed: exact creation inventory, +separate-process SQLite reopening/replay/revoke, complete preexisting directory +preservation including empty/shared groups and memberships, refusal of shared +membership drift, retained uncertainty after a lost response following a real +addition, and refusal of unknown/cross-case receipt bindings. The fixture used +only newly generated synthetic users/passwords/key material in private tmpfs, +with a network-none anchor and shared loopback helper, no host ports/mounts, +no image pulls and no production or actual credential contact. The coordinator's +safe receipt reports both exact owned containers absent after cleanup; final +independent cleanup verification remains the coordinator's integration duty. + +Evidence: Custodian +`docs/changes/netkingdom-target-2026-10-16/group-provenance-r1/`, including exact +source base/pre/postimages, patch/manifest, independent review and retained +refinements. Native fixture manifest +`sha256:8a0514f5f14137355972400cd4852f48c892316d28e3abd3c86635d7cb81eef2`; +retained safe result `native-fixture/execution-result.json` (original +`/tmp/nk48-groups-native-review-5pywb19z/result.json`). The source task supplies +no native owner assent, production rollout, protected session/evidence callbacks, +ordinary type admission or consumer login proof. LLDAP has no conditional +membership mutation or generation token here: exact readback cannot exclude +ABA or a concurrent external writer, so T14 remains open. + +2026-10-17 verified source follow-through: source commit `4f65e41` passed normal +local scanning and all 65 canonical provisioner tests. The actual disposable +LLDAP receipt passed all 14 oracles; independent verification on the pinned +daemon found both exact names and the run label absent. Source integration +includes the ADR-0015 documentation correction; the journal remains local-only. + +T14 source preparation is underway. Twenty-three pinned public sources identify +NetKingdom implementation/reconciliation, Platform custody/database governance +and rapp-postgres consumer provisioning; the inspected runtime binding does not +assign identity-provisioner a package. The existing User Engine runtime token +and pod route are not a protected-case caller lane. The native proposal preserves +the same journal seam on a dedicated CNPG consumer and requires authoritative +scope binding, admitted role/lease/placement/recovery and all LDAP writers fenced. +The declared exclusive maintenance window is not mechanical exclusion of WebUI, +helper or lifecycle writers. Capacity placement and EUR cost/risk remain owner +decisions. No package, database, credential, rollout or owner assent was created. +Custodian evidence: group-provenance-r1/native-storage-proposal-r1; final source, +projection and cleanup review: resume-integration-r1/publication-verification.json. + + +## T21 — Implement the PostgreSQL journal and prove disposable SQL compatibility + +```task +id: NK-WP-0048-T21 +status: done +depends_on: [NK-WP-0048-T20] +priority: high +state_hub_task_id: "afe78e4d-408a-516a-ab92-e212e8a2d11d" +``` + +Completed 2026-10-17: bounded source preparation and actual disposable PostgreSQL +compatibility only. T14 retains native storage, custody, callbacks, authorization, +all-writer isolation and production admission. Coordinator source publication and +projection remain separate follow-through; no source revision or task UUID is +invented here. T20 remains the historical local SQLite/actual LLDAP proof. + +The eight-path source candidate adds an injection-only PostgreSQL implementation +behind the existing owner journal seam, with explicit trusted tenant/directory/ +case/source/principal/epoch binding and independent continuity callbacks. It +persists bounded intent/created-only receipts, exact-one version updates, durable +active fences, and a content-bound journal chain. SQL commits precede independent +exact-marker acknowledgment; lost replies/checkpoints poison continuation and +retain uncertainty. A counter-only checkpoint is explicitly insufficient for +divergent clones. Migration does not create database/schema/credential, admit an +epoch or clear quarantine; production HTTP activation is absent pending trusted +callbacks. Pinned psycopg package/image-copy closure is included, without claiming +an image build or native dependency admission. + +Candidate base `6cb43c0cc55212363a5406b02db046e2384a7c40`; final eight-path patch +`sha256:5549d2982b61ff70aba4b3928fecd5639db0f1a732ab2f8630f678e7acf4d34d`. +All 82 source tests pass, alongside 13 fixture plan/diagnostic checks and two +source-backed snapshot regressions. Counts overlap the same source and fixture; +they are not additive native acceptance or completion percentages. + +The independently reviewed R5 fixture ran the actual SQL migration/backend and +existing provisioner path against cached pinned disposable PostgreSQL. Directory +operations deliberately used the existing synthetic LLDAP test double. All +manifest-declared phase oracles passed: creation-only inventory and DDL refusal; +separate-process replay/revoke with complete preexisting/shared visible-state +preservation; foreign-context refusal; concurrent exactly-once reservation and +stale version refusal; actual database fork with divergent bodies at the same +counter and fresh-object rejection of the losing exact checkpoint; retained +uncertainty after checkpoint/directory/SQL response loss; and actual pg_dump/ +pg_restore rollback refusal against the independently retained fixture checkpoint. +The checkpoint is fixture-only, not an admitted native owner evidence holder. + +Only generated credentials/state were used, with two nonroot/read-only containers +on private loopback, bounded tmpfs/CPU/memory/PIDs/lifetimes, no host ports/mounts, +no pulls and no production contact. The safe receipt reports both exact owned +containers absent, with no daemon uncertainty. Independent final pinned-daemon +cleanup verification remains coordinator follow-through. + +Failed R2/R3/R4 receipts and source revisions remain retained. R2's helper failure +subphase is unknown; R3 exposed a real backend bug where replacing nested receipt +objects lost the created ID held by ensure, fixed by acknowledging only the backend +version after commit/checkpoint. R4 verified that fix through actual SQL bootstrap, +then exposed fixture snapshot aliasing and empty deleted-group backing entries; +source-backed regressions corrected the snapshot without weakening complete +visible-prestate equality, and reject nonempty orphan membership. These refinements +belong to one trial, not extra native acceptance successes. + +Custodian evidence: `docs/changes/netkingdom-target-2026-10-16/group-postgres-r1/`; +exact source/pre/postimages and retained predecessors; native R5 manifest +`sha256:d51f90816616848b93bcdc482e10a98d07061deb01eeed9e50700fc6e68b6051`; +`final-integration-r1/typed-execution-evidence.json` and byte-exact safe receipt +(original `/tmp/nk48-postgres-native-review-sguj7t8p/result.json`). No CNPG native +storage/HA/backup/lease/TLS/capacity/restore admission, authoritative protected +callbacks, real LDAP writer fencing/CAS/ABA exclusion, owner assent or production +endpoint activation is claimed. Those remain live T14 work. + + +## T22 — Establish registration recovery source gaps and complete descriptor review inventory + +```task +id: NK-WP-0048-T22 +status: done +priority: high +state_hub_task_id: "ea50d93b-0d04-55d2-98c4-9b6c3fe925b1" +``` + +Completed 2026-10-17: source-only owner-contract review and complete review +inventory. Inspected the actual registration adapter, Warden Journal/Executor, +NetKingdom evidence compatibility checker and Audit Core custody/read surfaces. +No existing admitted holder/readback supplies authenticated original-attempt +recovery: payload/creation are process-local, persisted evidence references are +hashed, and reconcile receives a new attempt without original context. The exact +four-owner contract requirements and non-replayable missing/ambiguous cases are +recorded; no recovery callback, publisher, credential grant or native trust root +was installed. T15 retains implementation and actual acceptance. + +The complete source1.2 inventory maps all seven follow-through steps to their +existing owners, missing exact contracts and created-only rollback requirements. +The actual maintained adapter regenerated the sole existing register-client +machine descriptor against the delivered 13,633,409-byte checker and current +sealed-memfd snapshot source. The checker rehash and Warden Descriptor.decode +round-trip passed; six owner descriptors remain absent. Actual source commands +render canonical1.1 and explicitly selected1.2 with execution.permitted false; +ordinary1.2 case admission still refuses its acknowledged/unimplemented type. +Canonical type/case/acknowledgment bytes were hash-checked unchanged. No tests were +added or rerun, no native configuration or effect occurred, and no owner assent +or complete machine bundle is claimed. T16 retains native executable custody, +complete exact descriptors, independent source review and normal version/type +admission. This extracted outcome supplies no native gate completion credit. + +Evidence in the-custodian: +`docs/changes/netkingdom-target-2026-10-17/native-registration-r1/`, especially +original-attempt-review.json, descriptor-composition.json, source-verification.json, +source-manifest.json and manifest.json. The prior sixteen-gate audit remains +immutable. Publication, normal task UUID allocation and projection are separate +coordinator follow-through; no Hub UUID is assigned by this source record. + + +## T23 — Review native journal package assignment and storage integration boundaries + +```task +id: NK-WP-0048-T23 +status: done +priority: high +state_hub_task_id: "6e7cd483-c7af-53a2-9d0a-3d76b6fb49ee" +``` + +Completed 2026-10-17: source/package placement and protected integration review +only, separate from T21's completed PostgreSQL source and disposable-SQL proof. +The owner review recommends a dedicated independently rolled-back +`rapp-identity-provisioner`, owned by NetKingdom on rail-kubernetes, with a +schema-valid draft declaration and no reef binding. No repository, registry +entry, native package assignment or owner assent was created. Existing +rapp-user-engine ownership/token delivery does not assign it the provisioner. + +The complete current rapp-postgres catalog has six declared consumers: four on +platform-pg and two on platform-pg-2. The inert renderer-valid provisioner +candidate fits platform-pg-2 at three of four, requesting one migration plus +four runtime connections; placing it on the first cell is refused as a fifth. +Normative package schema and full-catalog capacity/render checks passed. The +four-consumer ceiling is estimated, not measured native headroom, and no current +SQL session, backup or restore acceptance is claimed. + +The exact review identifies remaining package/substrate, SQL role/lease/TLS, +network and backup/recovery contracts; independently authenticated protected +scope callbacks; an external content-bound checkpoint holder with atomic +advance/loss handling; and all-LDAP-writer exclusion or provider CAS/generation, +including lifecycle cascades and remove/readd ABA. Generic Audit custody is not +that checkpoint service. A database-only fence, final reread or one replica +cannot exclude foreign directory writers. The native acceptance/recovery matrix +keeps storage/lease admission separate from separately approved directory +mutation; the production activation remains disabled pending trusted integration. + +Evidence in the-custodian: +`docs/changes/netkingdom-target-2026-10-17/native-storage-r1/OWNER-REVIEW.md`, +source-validation.json and hash-bound source-references.json, plus the inert +proposed declaration/consumer and renderer outputs. No native resource, lease, +credential, deployment, destructive effect, independent holder or native owner +acceptance was delivered. T14 retains these live gates with its original UUID +and progress status. This completed review adds no duplicate T21 acceptance +credit; publication, task UUID allocation and projection remain coordinator +follow-through and no UUID is hand-assigned here. + +## T24 — Ensure-groups adapter source candidate + +```task +id: NK-WP-0048-T24 +status: done +priority: high +state_hub_task_id: "1a3931ee-3b5f-52fc-9295-4073804f4bd2" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): ensure-groups-adapter.py, 8 tests (commit 5a2e7d6). Binds to the T14 PostgreSQL journal and refuses SQLite or a foreign binding. It accepts only canonical 1.1 cases, so T30 must add an explicit 1.2 path. It calls the provisioner's private `_group_binding`; a public helper is the cleaner shared edit. + +Split from T16 on 2026-10-17 so each candidate fits one per-task budget. Bind group creation to T14's journal/provenance seam; refuse without trusted journal callbacks. +Source candidate only, modelled on the T13 registration adapter +(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and +`registration-adapter.md`). Requires positive and refusal fixtures and +created-only rollback. Keeps canonical 1.1 as the default and uses no +`--allow-unimplemented` admission. Done means the candidate and its tests are +committed. NetKingdom (directory) owner source review before pinning, and any native action, +stay separate gates and are not supplied by this task. + +## T25 — Mint-secret adapter source candidate + +```task +id: NK-WP-0048-T25 +status: done +priority: high +state_hub_task_id: "0de2a7d9-874d-578b-a076-f653fe42e30b" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): mint-secret-adapter.py, 9 tests (commit 558c5bc). Value-free receipts only. The receipt schema `railiance-platform.custody-mint.v1` and the mint/destroy capability interface are proposals that Platform must confirm. + +Split from T16 on 2026-10-17 so each candidate fits one per-task budget. Platform-owned custody evidence seam: the adapter records value-free mint evidence only and never handles secret values. +Source candidate only, modelled on the T13 registration adapter +(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and +`registration-adapter.md`). Requires positive and refusal fixtures and +created-only rollback. Keeps canonical 1.1 as the default and uses no +`--allow-unimplemented` admission. Done means the candidate and its tests are +committed. Platform owner source review before pinning, and any native action, +stay separate gates and are not supplied by this task. + +## T26 — Deliver-secret adapter source candidate + +```task +id: NK-WP-0048-T26 +status: done +priority: high +state_hub_task_id: "94b3cbf0-c908-5ac6-b0db-67385c3e021c" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): deliver-secret-adapter.py, 8 tests (commit 1b60f6e). The lane `platform-secret-delivery` and the schema `netkingdom.deliver-secret.v1` are proposals that Platform must confirm. + +Split from T16 on 2026-10-17 so each candidate fits one per-task budget. Platform-owned delivery evidence seam (ESO/custody), value-free. +Source candidate only, modelled on the T13 registration adapter +(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and +`registration-adapter.md`). Requires positive and refusal fixtures and +created-only rollback. Keeps canonical 1.1 as the default and uses no +`--allow-unimplemented` admission. Done means the candidate and its tests are +committed. Platform owner source review before pinning, and any native action, +stay separate gates and are not supplied by this task. + +## T27 — Bind-env adapter source candidate + +```task +id: NK-WP-0048-T27 +status: done +priority: high +state_hub_task_id: "e56365f8-29b5-5c89-a329-8115f9f3b2a7" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): bind-env-adapter.py, 5 tests (commit 6fbce73). Binds references only. To fit the budget it does not yet wrap a full Warden `Adapter` like T13 (source-digest pin, authenticated evidence, parent-session revocation); it returns `(reference, run, rollback)`. That wrapping is a prerequisite for T30. + +Split from T16 on 2026-10-17 so each candidate fits one per-task budget. Bind the issuer Deployment env reference to the delivered secret reference, without values. +Source candidate only, modelled on the T13 registration adapter +(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and +`registration-adapter.md`). Requires positive and refusal fixtures and +created-only rollback. Keeps canonical 1.1 as the default and uses no +`--allow-unimplemented` admission. Done means the candidate and its tests are +committed. NetKingdom runtime owner source review before pinning, and any native action, +stay separate gates and are not supplied by this task. + +## T28 — Deliver-consumer-secret adapter source candidate + +```task +id: NK-WP-0048-T28 +status: done +priority: high +state_hub_task_id: "6dab281c-ea9a-5a17-a83f-e8e9a4d4484f" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): deliver-consumer-secret-adapter.py, 5 tests (commit 650c1ac). Type 1.2 only. It assumes the owner has validated ESO TokenRequest and the controller-RBAC admission; that stays an owner gate. + +Split from T16 on 2026-10-17 so each candidate fits one per-task budget. Consumer-side delivery evidence seam, value-free. +Source candidate only, modelled on the T13 registration adapter +(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and +`registration-adapter.md`). Requires positive and refusal fixtures and +created-only rollback. Keeps canonical 1.1 as the default and uses no +`--allow-unimplemented` admission. Done means the candidate and its tests are +committed. Platform and Apps owner source review before pinning, and any native action, +stay separate gates and are not supplied by this task. + +## T29 — Hand-over adapter source candidate + +```task +id: NK-WP-0048-T29 +status: done +priority: high +state_hub_task_id: "129db41d-4560-5e20-9f3b-c3c91e0a5601" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): handover-adapter.py, 8 tests (commit ec63539). The owner `apps` and lane `apps-consumer-handover` are proposals that the Apps owner must confirm. It has no reconcile path; restart handling follows T15. + +Split from T16 on 2026-10-17 so each candidate fits one per-task budget. Apps-owned consumer verification and hand-over receipt seam. +Source candidate only, modelled on the T13 registration adapter +(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and +`registration-adapter.md`). Requires positive and refusal fixtures and +created-only rollback. Keeps canonical 1.1 as the default and uses no +`--allow-unimplemented` admission. Done means the candidate and its tests are +committed. Apps owner source review before pinning, and any native action, +stay separate gates and are not supplied by this task. + +## T30 — Compose the descriptor set over the reviewed candidates + +```task +id: NK-WP-0048-T30 +status: done +depends_on: [NK-WP-0048-T24, NK-WP-0048-T25, NK-WP-0048-T26, NK-WP-0048-T27, NK-WP-0048-T28, NK-WP-0048-T29] +priority: high +state_hub_task_id: "6f6073ac-52dd-5b70-8874-6d76d4c072e4" +``` + +2026-10-17 done: coordinator review r3 (the-custodian `nk48-adapters-1.2-review/coordinator-review-r3.json`, +fd07eaa) approved all nine files at ff8f597, with R2-F1..F3 resolved, an independent +reconstruction of 117 passing and no open findings. This is a source review only: +it grants no owner admission, pin or native authority. Owner review and admission +continue under T16, which also takes the r2 observations, including the +`warden_executes: false` steps that carry execution descriptors. + +2026-10-17 coordinator review r2 (the-custodian `nk48-adapters-1.2-review/coordinator-review-r2.json`) +approved five files and rejected three, plus the artifact. The fixes are merged in +2ee4ed0: R2-F1, mint-secret revokes the session and the broker grant +independently (eeed8e1); R2-F2, deliver-secret binds to the admitted case and +derives its Secret and custody path, with no caller reference (640292c); R2-F3, +bind-env pins its five-file closure, and a new closure-completeness test checks +every adapter (d16db30). Each regression test failed on 68f4bbb. The artifact is +regenerated, and the full suite including kernel admission passed 117. Next is +the r3 review. Open for owner review under T16: the type marks four steps +`warden_executes: false`, yet each has an execution descriptor. + +2026-10-17 founder confirmed every proposed lane and schema name for T25-T29 +as-is. The artifact records them as `names_confirmed` (by founder, 2026-10-17), +and no step is still `owner_confirmation_required`. Next: independent source +review of the seven adapters. + +2026-10-17 founder decision (supersedes the `agent` decision below): the +mint-secret actor is `human`. Coordinator review r1 (the-custodian +`docs/changes/netkingdom-target-2026-10-17/nk48-adapters-1.2-review/coordinator-review-r1.json`, +finding NK48-R1-F1) showed that unchanged kernel a45cf47 refuses every +`effect: mint` admission whose actor is not `human` ("agent mint is +forbidden"), so the `agent` descriptor could never be admitted. Adapter, tests +and composer comment updated and the artifact regenerated (mint-secret +descriptor digest now `sha256:8c40c2884f8ba15e8cb6c98f33419c395e54fce8ef33d6e1a34782cf3f1fcfa9`); +98 tests pass against a45cf47. The set needs an independent r2 review. + +2026-10-17 `test_admit_1_2_adapter_descriptors.py` runs Warden's own +`Executor.admit` on all seven composed 1.2 descriptors, with the real 1.2 +type, the 1.2 forgejo example case, the reviewed `BrowserInputs` validator and +fixture-only trust (stand-in adapters, nothing dispatched). It also pins that +an agent mint descriptor is refused ("agent mint is forbidden") and that a +wrong lane is refused for every step. Against the old `agent` revision +(582ab9f) it fails as expected. Combined suite: 102 passed on ops-warden a45cf47 +and on current ops-warden. Fixture admission is not owner admission or a pin. + +2026-10-17 founder decision: the mint-secret actor is `agent`, not `warden`. +The kernel is unchanged. The adapter, its test and the composer were updated and +the artifact regenerated; all seven steps pass `Descriptor.decode` and +`complete_executable_descriptor_set` is true (98 tests passed). The artifact +stays `not_an_admission`, with no owner review, admission or dispatch permission. + +2026-10-17 composer merged (60b41a1 via 3c62bec) with follow-up 376b44b (merged; +combined suite 98 passed). `compose-1.2-descriptors.py` loads 1.2 only through an +explicit version root and refuses partial, duplicate, unknown or mismatched maps. +`descriptor-composition-1.2.candidate.json` is a reviewable candidate, marked +`not_an_admission`, with canonical 1.1 unchanged. ensure-groups and hand-over now +carry and honour `parent-session` revocation, and the hand-over owner is +parameterised (`railiance-apps` for Forgejo). Six of the seven steps pass +`Descriptor.decode`. mint-secret is still refused: its actor `warden` is outside +the kernel's human/agent set, and Platform (with ops-warden, if the kernel should +change) must decide. Five steps carry `owner_confirmation_required` names. The +set is complete only after both. Owner review, the canon section 6 successor +revision and admission remain T16's. + +2026-10-17 prerequisites merged (65559ef; combined suite 78 passed): +ensure-groups accepts 1.2 via an explicit `version_root='1.2'` (531151b), with +1.1 still the default. bind-env is now a full Warden v1 `Adapter` (7442d21); its +kernel-level fixture tests like T13's are still missing, and its `build()` +signature changed. Composition is unblocked in source. Owner confirmation of +the proposed step, lane and schema names remains before pinning. + +2026-10-17 inputs from T24–T29 that composition must resolve: T24 accepts +only 1.1 while T28 accepts only 1.2, so add an explicit 1.2 ensure-groups path; +T27 still needs full Warden `Adapter` wrapping; the lane, schema and owner names +proposed in T25, T26 and T29 need owner confirmation before pinning. + +Split from T16 on 2026-10-17. Compose the seven-step source 1.2 descriptor set +through the existing explicit version-root selection over T13 and T24–T29. No +partial descriptor map, canonical 1.1 stays the default, and new descriptor bytes +need a reviewed successor revision under canon section 6. Owner review and +admission remain T16's. + +## T31 — Restart and lost-receipt fixtures against Warden's original-intent interface + +```task +id: NK-WP-0048-T31 +status: done +priority: high +state_hub_task_id: "ed1ccd87-89a0-5e69-885c-0eaf5ab0bb5a" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): test_registration_original_intent_reconcile.py, 8 tests (commit 74adcd7), runs against the unmodified ops-warden a45cf47. The holder is a test-only in-memory store. For real T15 support the T13 adapter's `build()` needs an injected holder/readback capability, a `reconcile_original` callback, and a `verify` that accepts reconcile results. Rollback after restart stays refused until there is an owner contract. + +Split from T15 on 2026-10-17. ops-warden published the original-intent +lookup/context interface in `src/warden/case_run.py` (`OriginalIntent`, +`Journal.original_intent`, `Adapter.reconcile_original`; commit `a45cf47`). +Build T15's restart/lost-receipt fixtures for the registration adapter against +it: the original intent is kept separate from the reconcile attempt; wrong +attempt/case/source/principal and unavailable-holder cases refuse; and a missing +or ambiguous record stays uncertain, with no add replay or undo. The interface +is Warden's own dispatch record, not owner-authenticated created-only provenance. +Selecting the actual authenticated holder stays a T15 owner gate. + + +## T32 — Restore independent agent-mint admission regression coverage + +```task +id: NK-WP-0048-T32 +status: done +priority: high +state_hub_task_id: "c6d0a27e-f6a7-5df0-95f6-3e7473d38bb5" +``` + +Completed 2026-10-17, source tests only. Extracted from T16's coordinator r5 +finding NK48-R5-F1; no earlier adapter outcome is counted again. The real 1.3 +admission test now separately checks the committed descriptor map, an in-memory +fixture map acknowledging the changed agent descriptor to reach the independent +mint prohibition, and the human descriptor under an explicit fixture agent +runtime marker. Only `sso-mfa/k8s/keycape/test_admit_1_3_type.py` changes. + +Against frozen Warden `117fec9241e7b985dfb187b992e173d0a5dc3700`, the new +suite passes 10 tests / 32 subtests. Independent mutation evidence removes only +the mint admission guard in scratch: both new guard tests fail while eight +controls pass; the old suite passed that mutant. Custodian independently reran +the canonical successor: 10 tests / 32 subtests pass. Evidence: +`the-custodian/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/` +(patch, frozen/mutant results and canonical-validation.json). No descriptor, +parameter pin, catalog, acknowledgment, kernel or native execution changed. +T16 still owns actual composition and admission; F2/F3 follow below. + +## T33 — Close the issuer-store reference finding with Platform source + +```task +id: NK-WP-0048-T33 +status: todo +priority: high +state_hub_task_id: "5ace28ff-a4cb-5647-a8f6-3cd54bb9d0a5" +``` + +Owner: Platform for the fixed store/template source and its delivery boundary; +NetKingdom for the 1.3 type reference. Split from T16's current coordinator r5 +finding NK48-R5-F2. Next action: independently review the prepared Platform +candidate and publish a concrete owner-controlled issuer store kind/name/scope +and template reference, then integrate only that exact proposed source into the +1.3 deliver-secret entry point and regenerate its composition artifact. Candidate +and current review evidence are under Custodian +`docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/` +and `nk48-r5-f2-review-r1/`. + +Completion requires exact committed source, a passing independent source review +and unchanged type parameter bytes. No consumer store may be inferred as issuer +store, and no source declaration supplies owner assent, installed ESO support, +CCR/grant changes or a real delivery capability. Those remain existing +RPF-1.3-T4 and T16 admission/native requirements. + +## T34 — Close the consumer configuration inverse finding + +```task +id: NK-WP-0048-T34 +status: todo +priority: high +state_hub_task_id: "37f43c40-d014-544e-aa0b-5219dd4a6b93" +``` + +Owner: Apps for consumer configuration and inverse; NetKingdom for exact type +placement. Split from T16's coordinator r5 finding NK48-R5-F3. Next action: +independently review and implement the owner transaction/inverse through the +existing RAPPS-WP-0016-T05 path, with authenticated durable before/after release +provenance, exclusive serialization of all release writers and a fresh authorized +attended session for every inverse attempt after the hand-over session is revoked. +The inverse must remain reachable for public clients, confidential NO_CHANGE +secret delivery and verification failure before a hand-over receipt exists. + +Completion requires real owner configuration/inverse source bindings and +independent regressions, then exact reviewed 1.3 entry/rollback text and composed +artifact. A synthetic wrapper with an injected backend alone is insufficient. +Keep hand-over receipt rollback receipt-only and secret deletion created-only; +preexisting or drifted resources must refuse unsafe inverse. Candidate evidence: +Custodian `docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/` +and `nk48-r5-f3-review-r1/`. Source review, owner acknowledgment and native proof +remain distinct. T16's final composition/admission and T06's native first case +are not completed by this refinement. diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-update-r1/candidate.patch b/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-update-r1/candidate.patch new file mode 100644 index 0000000..671f3e2 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-update-r1/candidate.patch @@ -0,0 +1,243 @@ +diff --git a/sso-mfa/k8s/keycape/test_admit_1_3_type.py b/sso-mfa/k8s/keycape/test_admit_1_3_type.py +--- a/sso-mfa/k8s/keycape/test_admit_1_3_type.py ++++ b/sso-mfa/k8s/keycape/test_admit_1_3_type.py +@@ -8,7 +8,9 @@ + acknowledged 1.3 is still refused until ops-warden adds a 1.3 pin. That is an + ops-warden owner change, and it is not made here. The tests show that 1.3's + parameters carry 1.2's pinned fingerprint, and that with only that one pin added in +-memory the committed map admits exactly the composed descriptors and nothing else. ++memory the five resolvable steps admit. The two Apps attended steps remain refused ++because their establishing login is unresolved in the synthetic catalog. Four real ++draft lane shapes are hypothetically active in that fixture; no catalog is promoted. + Trust is fixture-only, nothing is dispatched, and none of this is owner admission + or an acknowledgment. + """ +@@ -37,6 +39,8 @@ + TYPE_DIR = REPO / 'security-case-type-revisions/1.3/oidc-browser-relying-party' + TYPE_BYTES = (TYPE_DIR / 'type.yaml').read_bytes() + ARTIFACT = json.loads((ROOT / 'descriptor-composition-1.3.candidate.json').read_text()) ++CATALOG = (ROOT / 'kernel-catalog.fixture.yaml').read_bytes() ++UNRESOLVED_APPS = {'deliver-consumer-secret', 'hand-over'} + EVIDENCE = {'fixture-acceptance', 'fixture-materialization', 'fixture-descriptor', 'fixture-session'} + + +@@ -74,12 +78,13 @@ + ARTIFACT['params']['consumer_owner_repo'], + version='1.3')) + +- def admit(self, desc, type_source, *, actor=None, lane=None, agent_id=None): ++ def admit(self, desc, type_source, *, actor=None, lane=None, agent_id=None, catalog=CATALOG): + desc_bytes = c.encode(desc) + adapter = Adapter(desc.adapter, desc.adapter_source, unused, unused) + executor = Executor(Journal(Path(self.temp.name) / desc.step), + {adapter.reference: adapter}, +- lambda purpose, source, evidence: evidence in EVIDENCE, ++ lambda purpose, source, evidence: (evidence in EVIDENCE and ++ (purpose != 'catalog-routing-acceptance' or source == digest(CATALOG))), + browser_inputs=inputs.BrowserInputs(REPO)) + with mock.patch.dict(os.environ): + if agent_id is None: +@@ -87,7 +92,7 @@ + else: + os.environ['WARDEN_AGENT_ID'] = agent_id + return executor.admit(self.case_source, type_source, desc_bytes, +- b'fixture-only-catalog', principal='founder', actor=actor or desc.actor, ++ catalog, principal='founder', actor=actor or desc.actor, + lane=lane or desc.lane, + acceptance_evidence='fixture-acceptance', + materialization_evidence='fixture-materialization', +@@ -112,18 +117,33 @@ + self.assertEqual(parameter_fingerprint(TYPE_BYTES), inputs.PARAMETER_PINS['1.2']) + acknowledged = with_status('acknowledged') + for step, desc in self.descriptors.items(): ++ if step in UNRESOLVED_APPS: ++ continue # The attended session gate correctly precedes parameter validation. + with self.subTest(step=step), self.assertRaisesRegex( + Refused, 'failed exact-version validation'): + self.admit(desc, acknowledged) + +- def test_committed_map_admits_every_descriptor_once_acknowledged_and_pinned(self): ++ def test_committed_map_conditionally_admits_five_resolvable_steps(self): + acknowledged = with_status('acknowledged') + for step, desc in self.descriptors.items(): ++ if step in UNRESOLVED_APPS: ++ continue + with self.subTest(step=step), pinned_13(): + admitted, invocation, _ = self.admit(desc, acknowledged) + self.assertEqual(admitted, desc) + self.assertEqual((invocation.step, invocation.actor, invocation.target), + (desc.step, desc.actor, 'forgejo')) ++ ++ def test_unresolved_apps_sessions_refused_even_once_acknowledged_and_pinned(self): ++ for step in sorted(UNRESOLVED_APPS): ++ with self.subTest(step=step), pinned_13(), self.assertRaisesRegex( ++ Refused, 'does not resolve to an active, reviewed login lane'): ++ self.admit(self.descriptors[step], with_status('acknowledged')) ++ ++ def test_catalog_evidence_is_bound_to_exact_fixture_bytes(self): ++ mint = self.descriptors['mint-secret'] ++ with pinned_13(), self.assertRaisesRegex(Refused, 'independent owner evidence not verified'): ++ self.admit(mint, with_status('acknowledged'), catalog=CATALOG + b'\n# changed source\n') + + def test_descriptor_outside_the_map_is_refused(self): + acknowledged = with_status('acknowledged') +diff --git a/sso-mfa/k8s/keycape/test_keycape_registration_adapter.py b/sso-mfa/k8s/keycape/test_keycape_registration_adapter.py +--- a/sso-mfa/k8s/keycape/test_keycape_registration_adapter.py ++++ b/sso-mfa/k8s/keycape/test_keycape_registration_adapter.py +@@ -259,8 +259,13 @@ + case_source = yaml.safe_dump(case).encode() + typ = yaml.safe_load((root / 'security-case-types/oidc-browser-relying-party/type.yaml').read_bytes()) + typ['spec']['execution_descriptors'] = {'register-client': digest(self.desc_bytes)} ++ # Fixture-only successor lane for the adapter's current descriptor. ++ # Canonical 1.1 still names the earlier login lane and is not edited. ++ typ['spec']['follow_through'] = [ ++ dict(step, lane=self.admitted.lane) if step['id'] == 'register-client' else step ++ for step in typ['spec']['follow_through']] + type_source = yaml.safe_dump(typ).encode() +- catalog = b'fixture-only-catalog' ++ catalog = (ROOT / 'kernel-catalog.fixture.yaml').read_bytes() + sheet = json.dumps(['register-client', 'net-kingdom', 'forgejo', 'human', + 'founder', 'keycape-attended-case-change', 'fixture-acceptance', 'fixture-materialization', + 'fixture-descriptor', 'fixture-session'], separators=(',', ':')).encode() +@@ -270,6 +275,8 @@ + adapter = a.build(**dict(self.kw, admitted=invocation, case_source=case_source, + case_acceptance='fixture-acceptance')) + def fixture_verifier(purpose, source, evidence): ++ if purpose == 'catalog-routing-acceptance': ++ return source == digest(catalog) and evidence == 'fixture-descriptor' + if purpose.startswith('session-revocation:'): + _, lane, operation, attempt = purpose.split(':') + return source == digest(case_source) and lane == invocation.lane and evidence == ( +diff --git a/sso-mfa/k8s/keycape/kernel-catalog.fixture.yaml b/sso-mfa/k8s/keycape/kernel-catalog.fixture.yaml +new file mode 100644 +--- /dev/null ++++ b/sso-mfa/k8s/keycape/kernel-catalog.fixture.yaml +@@ -0,0 +1,128 @@ ++# TEST ONLY: synthetic subset shaped after ops-warden 5abde817 catalog. ++# Four draft entries are hypothetically active; Apps session_lane remains unresolved. ++# Not an authenticated owner catalog, promotion, login choice or native authority. ++entries: ++- id: key-cape-oidc-login ++ risk: standard ++ owner_repo: key-cape ++ warden_executes: false ++ reviewed: '2026-06-27' ++ status: active ++ delegation: ++ mode: interim ++ intended_owner: key-cape ++ reviewed: '2026-09-08' ++ blocked_on: fixture-only; no native authority ++ lane: login ++ title: Synthetic key-cape-oidc-login ++ need_keywords: ++ - key-cape-oidc-login ++ subsystem: synthetic source test ++ wiki_ref: fixture-only ++ canon_ref: fixture-only ++ workload_ref: ++ applicability: not-applicable ++ reason: Synthetic source test; no workload or native authority. ++- id: keycape-client-secret-generate ++ risk: high ++ owner_repo: railiance-platform ++ warden_executes: false ++ reviewed: '2026-10-10' ++ status: active ++ delegation: ++ mode: native ++ intended_owner: railiance-platform ++ reviewed: '2026-10-10' ++ lane: secret ++ title: Synthetic keycape-client-secret-generate ++ need_keywords: ++ - keycape-client-secret-generate ++ subsystem: synthetic source test ++ wiki_ref: fixture-only ++ canon_ref: fixture-only ++ workload_ref: ++ applicability: not-applicable ++ reason: Synthetic source test; no workload or native authority. ++- id: keycape-attended-case-change ++ lane: attended-change ++ session_lane: key-cape-oidc-login ++ risk: high ++ owner_repo: key-cape/net-kingdom ++ warden_executes: false ++ reviewed: '2026-10-17' ++ status: active ++ delegation: ++ mode: interim ++ intended_owner: key-cape ++ reviewed: '2026-10-17' ++ blocked_on: fixture-only; no native authority ++ title: Synthetic keycape-attended-case-change ++ need_keywords: ++ - keycape-attended-case-change ++ subsystem: synthetic source test ++ wiki_ref: fixture-only ++ canon_ref: fixture-only ++ workload_ref: ++ applicability: not-applicable ++ reason: Synthetic source test; no workload or native authority. ++- id: platform-secret-delivery ++ risk: high ++ owner_repo: railiance-platform ++ warden_executes: false ++ reviewed: '2026-10-17' ++ status: active ++ delegation: ++ mode: native ++ intended_owner: railiance-platform ++ reviewed: '2026-10-17' ++ title: Synthetic platform-secret-delivery ++ need_keywords: ++ - platform-secret-delivery ++ subsystem: synthetic source test ++ wiki_ref: fixture-only ++ canon_ref: fixture-only ++ workload_ref: ++ applicability: not-applicable ++ reason: Synthetic source test; no workload or native authority. ++- id: consumer-owner-deployment ++ lane: attended-change ++ session_lane: pending-owner-confirmation ++ risk: high ++ owner_repo: railiance-apps ++ warden_executes: false ++ reviewed: '2026-10-17' ++ status: active ++ delegation: ++ mode: native ++ intended_owner: railiance-apps ++ reviewed: '2026-10-17' ++ title: Synthetic consumer-owner-deployment ++ need_keywords: ++ - consumer-owner-deployment ++ subsystem: synthetic source test ++ wiki_ref: fixture-only ++ canon_ref: fixture-only ++ workload_ref: ++ applicability: not-applicable ++ reason: Synthetic source test; no workload or native authority. ++- id: apps-consumer-handover ++ lane: attended-change ++ session_lane: pending-owner-confirmation ++ risk: high ++ owner_repo: railiance-apps ++ warden_executes: false ++ reviewed: '2026-10-17' ++ status: active ++ delegation: ++ mode: native ++ intended_owner: railiance-apps ++ reviewed: '2026-10-17' ++ title: Synthetic apps-consumer-handover ++ need_keywords: ++ - apps-consumer-handover ++ subsystem: synthetic source test ++ wiki_ref: fixture-only ++ canon_ref: fixture-only ++ workload_ref: ++ applicability: not-applicable ++ reason: Synthetic source test; no workload or native authority. diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-update-r1/edit.py b/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-update-r1/edit.py new file mode 100644 index 0000000..8765080 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-update-r1/edit.py @@ -0,0 +1,46 @@ +from pathlib import Path +import yaml +r=Path('/tmp/nk48-catalog-fixture-update-r1/net-kingdom/sso-mfa/k8s/keycape') +original=Path('/tmp/warden-kernel-review-r2/candidate/registry/routing/catalog.yaml') +entries=yaml.safe_load(original.read_bytes())['entries'] +ids={'keycape-attended-case-change','keycape-client-secret-generate','key-cape-oidc-login','platform-secret-delivery','consumer-owner-deployment','apps-consumer-handover'} +keep=('id','lane','session_lane','owner_repo','risk','status','reviewed','warden_executes','delegation') +rows=[] +for entry in entries: + if entry['id'] not in ids:continue + item={k:v for k,v in entry.items() if k in keep} + item.update(title='Synthetic '+entry['id'],need_keywords=[entry['id']],subsystem='synthetic source test',wiki_ref='fixture-only',canon_ref='fixture-only',workload_ref={'applicability':'not-applicable','reason':'Synthetic source test; no workload or native authority.'}) + if item['status']=='draft':item['status']='active' + # Keep delegation mode/owner; do not copy actual runtime commands or claim owner acceptance. + item['delegation']={k:v for k,v in item['delegation'].items() if k in ('mode','intended_owner','reviewed')} + if item['delegation']['mode']=='interim':item['delegation']['blocked_on']='fixture-only; no native authority' + rows.append(item) +assert len(rows)==6 +(r/'kernel-catalog.fixture.yaml').write_text('# TEST ONLY: synthetic subset shaped after ops-warden 5abde817 catalog.\n# Four draft entries are hypothetically active; Apps session_lane remains unresolved.\n# Not an authenticated owner catalog, promotion, login choice or native authority.\n'+yaml.safe_dump({'entries':rows},sort_keys=False)) +p=r/'test_admit_1_3_type.py';s=p.read_text() +s=s.replace("memory the committed map admits exactly the composed descriptors and nothing else.","memory the five resolvable steps admit. The two Apps attended steps remain refused\nbecause their establishing login is unresolved in the synthetic catalog. Four real\ndraft lane shapes are hypothetically active in that fixture; no catalog is promoted.") +s=s.replace("EVIDENCE =", "CATALOG = (ROOT / 'kernel-catalog.fixture.yaml').read_bytes()\nUNRESOLVED_APPS = {'deliver-consumer-secret', 'hand-over'}\nEVIDENCE =") +s=s.replace("def admit(self, desc, type_source, *, actor=None, lane=None, agent_id=None):","def admit(self, desc, type_source, *, actor=None, lane=None, agent_id=None, catalog=CATALOG):") +s=s.replace("lambda purpose, source, evidence: evidence in EVIDENCE,", "lambda purpose, source, evidence: (evidence in EVIDENCE and\n (purpose != 'catalog-routing-acceptance' or source == digest(CATALOG))),") +s=s.replace("b'fixture-only-catalog', principal=", "catalog, principal=") +s=s.replace("for step, desc in self.descriptors.items():\n with self.subTest(step=step), self.assertRaisesRegex(\n Refused, 'failed exact-version validation'):","for step, desc in self.descriptors.items():\n if step in UNRESOLVED_APPS:\n continue # The attended session gate correctly precedes parameter validation.\n with self.subTest(step=step), self.assertRaisesRegex(\n Refused, 'failed exact-version validation'):") +s=s.replace("def test_committed_map_admits_every_descriptor_once_acknowledged_and_pinned(self):", "def test_committed_map_conditionally_admits_five_resolvable_steps(self):") +s=s.replace("with self.subTest(step=step), pinned_13():\n admitted, invocation, _ =", "with self.subTest(step=step), pinned_13():\n if step in UNRESOLVED_APPS:\n continue\n admitted, invocation, _ =") +pos=s.index(' def test_descriptor_outside_the_map_is_refused') +s=s[:pos]+''' def test_unresolved_apps_sessions_refused_even_once_acknowledged_and_pinned(self): + for step in sorted(UNRESOLVED_APPS): + with self.subTest(step=step), pinned_13(), self.assertRaisesRegex( + Refused, 'does not resolve to an active, reviewed login lane'): + self.admit(self.descriptors[step], with_status('acknowledged')) + + def test_catalog_evidence_is_bound_to_exact_fixture_bytes(self): + mint = self.descriptors['mint-secret'] + with pinned_13(), self.assertRaisesRegex(Refused, 'independent owner evidence not verified'): + self.admit(mint, with_status('acknowledged'), catalog=CATALOG + b'\\n# changed source\\n') + +'''+s[pos:] +p.write_text(s) +p=r/'test_keycape_registration_adapter.py';s=p.read_text() +s=s.replace(" catalog = b'fixture-only-catalog'", " # Fixture-only successor lane for the adapter's current descriptor.\n # Canonical 1.1 still names the earlier login lane and is not edited.\n typ['spec']['follow_through'] = [\n dict(step, lane=self.admitted.lane) if step['id'] == 'register-client' else step\n for step in typ['spec']['follow_through']]\n type_source = yaml.safe_dump(typ).encode()\n catalog = (ROOT / 'kernel-catalog.fixture.yaml').read_bytes()") +s=s.replace(" def fixture_verifier(purpose, source, evidence):\n", " def fixture_verifier(purpose, source, evidence):\n if purpose == 'catalog-routing-acceptance':\n return source == digest(catalog) and evidence == 'fixture-descriptor'\n") +p.write_text(s) diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-update-r1/implementation-r1.json b/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-update-r1/implementation-r1.json new file mode 100644 index 0000000..284df3e --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-update-r1/implementation-r1.json @@ -0,0 +1,75 @@ +{ + "schema": "custodian.nk48-catalog-fixture-update.v1", + "date": "2026-10-17", + "role": "implementation proposal; this successor task is not independent review", + "status": "candidate prepared in scratch only; no canonical edits/commits", + "base_net_kingdom": "342a6f25af5cdc602841948f42759ea52ef1c8f5", + "kernel_current_candidate": "5abde817775d6304e3cf5a46948717a1606981bf plus reviewed candidate.patch SHA2561a3530a1e469fc03c2c29f1b66fa3ba1904e56bf2abcfb242c7a2ed015fb3e74", + "kernel_predecessor": "117fec9241e7b985dfb187b992e173d0a5dc3700", + "patch": { + "file": "candidate.patch", + "sha256": "sha256:df69fb2ec9f1e1cf0dd94194e3c046c995ed8a7deabf65a8e9bec34662adb501", + "check": "git apply --check passed against fresh frozen342a6f25 archive" + }, + "files": { + "sso-mfa/k8s/keycape/test_admit_1_3_type.py": { + "base": "sha256:c512ba00513d066fd1ca8af08fd6bf106fbd7a132981b1dcc5af80a745ab03f0", + "candidate": "sha256:2186eab00b000a534ab1452f14fa7b1a7ed352d5b27c400687c1c8ec7c4c3019" + }, + "sso-mfa/k8s/keycape/test_keycape_registration_adapter.py": { + "base": "sha256:4d0126416522e54eec1ae4c83b5488e09ff11ffb97ec1ee6d234548780c60f63", + "candidate": "sha256:86268e082763c388528e5175184fd32362167c3441716436e4b1cab150c3e79a" + }, + "sso-mfa/k8s/keycape/kernel-catalog.fixture.yaml": { + "base": null, + "candidate": "sha256:7885b8c380c03ccb0198e540532ce80c791c8e86698521d7173642e0c7a56e53" + } + }, + "changes": [ + "Replace both remaining fixture-only-catalog byte placeholders with one static well-formed synthetic catalog shaped after Warden5abde817. Four draft entries hypothetically active; no real catalog change. Preserve real owner/risk/delegation/lane/session kinds, including unresolved Apps sessions. Runtime commands and real workload paths are omitted.", + "Bind catalog-routing-acceptance fixture evidence to digest of the exact catalog bytes in both fixtures; add changed-source refusal test. Fixture trust remains synthetic and supplies no owner authentication.", + "For the current kernel expect five resolvable1.3 descriptors to admit under simulated acknowledgment/pin; expect both Apps attended descriptors to refuse unresolved establishing login. Missing-pin tests exercise the five descriptors that can reach parameter validation.", + "Keep every committed-map and mint-guard test: wrong actor, agent-actor descriptor after exact fixture-map acknowledgment, and agent runtime all reach their intended guard.", + "Registration fixture explicitly supplies its proposed current-descriptor lane in its in-memory type step; canonical1.1 remains unchanged. This fixes the previously unexamined canonical lane/descriptor disagreement for the mode-aware API. Existing synthetic kernel dispatch, rollback, lost-readback and external-revision tests pass." + ], + "path_correction": "test_admit_1_2_adapter_descriptors.py does not exist at342a6f25; its corrected mint tests already moved into test_admit_1_3_type.py. The second remaining fake catalog is test_keycape_registration_adapter.py, which is included.", + "validation": { + "command": "python3 /tmp/nk48-catalog-fixture-update-r1/run.py final", + "candidate": "31 passed,53 subtests passed (see candidate-final.log)", + "frozen117": "2 failed subtests,31 passed,51 subtests passed (see frozen117-final.log). Only unresolved Apps refusal assertions fail; predecessor117 lacks the attended lane/session enforcement added after it.", + "compatibility": "Selected tests now target the reviewed mode-aware catalog API.117 remains useful as a differential predecessor; no version-based skipping, expected-failure marking or weakened production parser is proposed. All common mint guard and registration execution tests still pass with117.", + "isolation": "Empty synthetic HOME; allowlisted environment. Python socket audit guard denies all network. Native subprocesses denied except exact pinned BrowserInputs offline validator child, launched with empty environment and injected child socket/process/ambient-home-read audit guards. Registration Kubernetes/checker owner calls are synthetic mocks. No ambient tokens, native credentials, live owner calls or owner acceptance.", + "full_suite": "Not run; focused scope only.", + "packaging_initial_attempt": "First generated diff omitted new file mode for synthetic YAML and git apply --check refused dev/null; corrected new-file metadata, no source change or canonical apply." + }, + "limitations": [ + "Four source draft lane statuses, unresolved Apps login choice, founder exact-type acknowledgment,1.3 PARAMETER_PINS, native T03 and downstream acceptance remain separate gates.", + "Synthetic catalog is deliberately hypothetical data; its fixture evidence and bytes do not imply authentic owner approval.", + "This implementation packet requires source review before canonical application.", + "No workplan edits or State Hub changes; parent handles task/progress records." + ], + "boundary": { + "canonical_edits": false, + "new_commits": false, + "catalog_promotion": false, + "parameter_pin_added": false, + "native_actions": false, + "credential_access": false, + "external_owner_assent": false + }, + "knowledge_orientation": { + "package": "0.2.6", + "digest": "8d1d6a9d4d07fec7b619618de7dfe2be2254c1790f85f4198c7fe19db9751c02", + "status": "start withheld/orientation-only; actual owner AGENTS.md, scoped source and source contract reviewed directly; no inferred action authority" + }, + "packet_hashes": { + "edit.py": "sha256:da1fef62073b92b59307e9f52115fa4fa784b260b5202db9d28fc62938982040", + "candidate-tests.log": "sha256:dbc8706c1b29c6fb08821670d1e4a923e1b0a8db56b039937e346bd7581090c1", + "frozen117-final.log": "sha256:81185ef66ed4ed5f836dc214899911af3a7a8b9f53b25b12130d8d8b6d8eafcf", + "run.py": "sha256:8ad707bd9bb509e4b27503c284cee8aff3a7205882ca87febdace0a050c5f749", + "frozen117-tests.log": "sha256:bd012a45cfeb9bba992b79a89a12a1397eea22169740aca2830d136d4a410ea2", + "candidate-final.log": "sha256:2669471008859e15ce9b3bd92baf4e759672e96048ef221f90b19daf84dc3a28", + "candidate.patch": "sha256:df69fb2ec9f1e1cf0dd94194e3c046c995ed8a7deabf65a8e9bec34662adb501", + "test_runner.py": "sha256:d6f4e8f0f5c34557bf678543ae2c69202dc321f1226c2e1a2d462b2d81efc9c5" + } +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-update-r1/run.py b/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-update-r1/run.py new file mode 100644 index 0000000..84f1c7e --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-update-r1/run.py @@ -0,0 +1,8 @@ +from pathlib import Path +import os,subprocess,sys +r=Path('/tmp/nk48-catalog-fixture-update-r1');(r/'home').mkdir(exist_ok=True) +kernel='/tmp/warden-kernel-review-r2/candidate/src' if sys.argv[1]=='candidate' else str(r/'warden117/src') +env={'HOME':str(r/'home'),'PATH':'/usr/bin:/bin','PYTHONPATH':'/tmp/warden-kernel-review-r2/guard:'+kernel,'PYTHONDONTWRITEBYTECODE':'1','LANG':'C.UTF-8'} +with (r/(sys.argv[1]+'-'+sys.argv[2]+'.log')).open('w') as log: + result=subprocess.run(['/tmp/warden-kernel-review-r1/final/.venv/bin/python',str(r/'test_runner.py')],cwd=r/'net-kingdom',env=env,stdout=log,stderr=subprocess.STDOUT) +print(result.returncode);sys.exit(result.returncode) diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-update-r1/test_runner.py b/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-update-r1/test_runner.py new file mode 100644 index 0000000..61c8a10 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-update-r1/test_runner.py @@ -0,0 +1,18 @@ +"""Invoke only pinned, offline validator child; keep socket/native denial in child.""" +from pathlib import Path +import sys +import subprocess +import warden.case_inputs as inputs +import pytest + +PREAMBLE = '''import sys as _review_sys\ndef _review_guard(event,args):\n if event.startswith('socket.') or event in {'subprocess.Popen','os.system','os.exec','os.posix_spawn'}:\n raise RuntimeError('review validator: network/native disabled')\n if event=='open' and isinstance(args[0],str) and (args[0].startswith('/home/') or args[0].startswith('/root/')):\n raise RuntimeError('review validator: ambient home denied')\n_review_sys.addaudithook(_review_guard)\n''' +old_run = subprocess.run + +def run_pinned(args,*pos,**kw): + if (type(args) is not list or len(args)!=5 or args[:3]!=[sys.executable,'-I','-c'] + or args[3]!=inputs._VALIDATE or not str(args[4]).startswith('/tmp/warden-case-validator-') + or kw.get('env')!={}): + raise RuntimeError('review validator: unexpected process refused') + return old_run([*args[:3],PREAMBLE+args[3],args[4]],*pos,**kw) +inputs.subprocess.run=run_pinned +raise SystemExit(pytest.main(['-q','sso-mfa/k8s/keycape/test_admit_1_3_type.py','sso-mfa/k8s/keycape/test_keycape_registration_adapter.py'])) diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-current-source-publication-r3/canonical-validation.json b/docs/changes/netkingdom-target-2026-10-17/nk48-current-source-publication-r3/canonical-validation.json new file mode 100644 index 0000000..eddeb77 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-current-source-publication-r3/canonical-validation.json @@ -0,0 +1,15 @@ +{ + "executed": true, + "counts_verified": true, + "returncode": 0, + "warden_revision": "922c26da0f3fcb0182352ea83e6ae06f29878580", + "net_kingdom_head": "342a6f25af5cdc602841948f42759ea52ef1c8f5", + "verified_public_source_files": 105, + "tests": [ + "sso-mfa/k8s/keycape/test_admit_1_3_type.py", + "sso-mfa/k8s/keycape/test_keycape_registration_adapter.py" + ], + "log_sha256": "sha256:7bb78da51b547a05c66ffeb1e592c23317be29f6705d3e359c2ae370b86aedd4", + "scratch": "/tmp/nk48-catalog-canonical-validation-5ghz6p1c", + "limitations": "Source fixture tests only; no native owner execution, acceptance, catalog promotion or pin." +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-current-source-publication-r3/consistency.json b/docs/changes/netkingdom-target-2026-10-17/nk48-current-source-publication-r3/consistency.json new file mode 100644 index 0000000..89ee1d9 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-current-source-publication-r3/consistency.json @@ -0,0 +1,365 @@ +{ + "repo_slug": "net-kingdom", + "repo_path": "/home/worsch/net-kingdom", + "source_revision": "befb0bc68cd25b625804fce559476eefd702521d", + "source_revision_reason": null, + "issues": [ + { + "severity": "WARN", + "check_id": "C-24", + "message": "Repo classification advisory: capability_tag 'security' is not a recommended family tag (allowed, check for synonym)", + "file_path": "", + "db_id": "", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-35", + "message": "Repo Manager warning: workplans/: repository uses more than one workplan prefix: ['NET-WP', 'NK-WP']", + "file_path": "workplans/", + "db_id": "", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-33", + "message": "WORK-RECORDS.md is missing or stale \u2014 regenerate via fix-consistency (CUST-WP-0061-T04 generated work-record index)", + "file_path": "WORK-RECORDS.md", + "db_id": "", + "file_value": "", + "db_value": "", + "fixable": true + }, + { + "severity": "WARN", + "check_id": "C-37", + "message": "Residual workplan 'nk-wp-0011' has no origin/origin_ref provenance (not invented)", + "file_path": "workplans/NK-WP-0011-enterprise-federation-saml.md", + "db_id": "1075448f-d533-5f9e-94b7-c3adfe151a07", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-20", + "message": "Workplan dependency target 'USER-WP-0020' is not linked to State Hub", + "file_path": "workplans/NK-WP-0022-railiance01-identity-cutover-and-coulombcore-retirement.md", + "db_id": "", + "file_value": "USER-WP-0020", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-20", + "message": "Workplan dependency target 'KEY-WP-0004' is not linked to State Hub", + "file_path": "workplans/NK-WP-0022-railiance01-identity-cutover-and-coulombcore-retirement.md", + "db_id": "", + "file_value": "KEY-WP-0004", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-20", + "message": "Workplan dependency target 'USER-WP-0020' is not linked to State Hub", + "file_path": "workplans/NK-WP-0023-user-engine-portal-platform-integration.md", + "db_id": "", + "file_value": "USER-WP-0020", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-20", + "message": "Workplan dependency target 'KEY-WP-0004' is not linked to State Hub", + "file_path": "workplans/NK-WP-0023-user-engine-portal-platform-integration.md", + "db_id": "", + "file_value": "KEY-WP-0004", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-20", + "message": "Workplan dependency target 'USER-WP-0021' is not linked to State Hub", + "file_path": "workplans/NK-WP-0024-user-engine-portal-integration-expansion.md", + "db_id": "", + "file_value": "USER-WP-0021", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-20", + "message": "Workplan dependency target 'FLEX-WP-0009' is not linked to State Hub", + "file_path": "workplans/NK-WP-0024-user-engine-portal-integration-expansion.md", + "db_id": "", + "file_value": "FLEX-WP-0009", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-20", + "message": "Workplan dependency target 'AUDIT-WP-0003' is not linked to State Hub", + "file_path": "workplans/NK-WP-0024-user-engine-portal-integration-expansion.md", + "db_id": "", + "file_value": "AUDIT-WP-0003", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-20", + "message": "Workplan dependency target 'EMAIL-WP-0004' is not linked to State Hub", + "file_path": "workplans/NK-WP-0024-user-engine-portal-integration-expansion.md", + "db_id": "", + "file_value": "EMAIL-WP-0004", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-20", + "message": "Workplan dependency target 'USER-WP-0022' is not linked to State Hub", + "file_path": "workplans/NK-WP-0025-public-self-registration-and-application-jit.md", + "db_id": "", + "file_value": "USER-WP-0022", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-20", + "message": "Workplan dependency target 'KEY-WP-0008' is not linked to State Hub", + "file_path": "workplans/NK-WP-0025-public-self-registration-and-application-jit.md", + "db_id": "", + "file_value": "KEY-WP-0008", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-20", + "message": "Workplan dependency target 'CSOC-WP-0003' is not linked to State Hub", + "file_path": "workplans/NK-WP-0025-public-self-registration-and-application-jit.md", + "db_id": "", + "file_value": "CSOC-WP-0003", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-37", + "message": "Residual workplan 'nk-wp-0027' has no origin/origin_ref provenance (not invented)", + "file_path": "workplans/NK-WP-0027-reef-placement-reconciliation.md", + "db_id": "965ad365-6b81-50a1-a2a3-2d0c1fcce0b4", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-37", + "message": "Residual workplan 'nk-wp-0031' has no origin/origin_ref provenance (not invented)", + "file_path": "workplans/NK-WP-0031-deterministic-posture-feedback.md", + "db_id": "9d7b04f9-3803-5613-b7a5-8bd606c77f5a", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-37", + "message": "Residual workplan 'nk-wp-0040' has no origin/origin_ref provenance (not invented)", + "file_path": "workplans/NK-WP-0040-execution-attribution-receipt.md", + "db_id": "e2533f3a-aa43-59b3-bff3-8e64b6149487", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-40", + "message": "blocker satisfied, task still wait: 'NK-WP-0048-T15' in workplan 'NK-WP-0048' \u2014 every depends_on target is terminal (NK-WP-0048-T31)", + "file_path": "workplans/NK-WP-0048-security-case-types.md#NK-WP-0048-T15", + "db_id": "", + "file_value": "NK-WP-0048-T31", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-10", + "message": "Task status drift 'NK-WP-0048-T33': file='done' db='todo' (file wins)", + "file_path": "workplans/NK-WP-0048-security-case-types.md#NK-WP-0048-T33", + "db_id": "5ace28ff-a4cb-5647-a8f6-3cd54bb9d0a5", + "file_value": "done", + "db_value": "todo", + "fixable": true + }, + { + "severity": "WARN", + "check_id": "C-10", + "message": "Task status drift 'NK-WP-0048-T34': file='progress' db='todo' (file wins)", + "file_path": "workplans/NK-WP-0048-security-case-types.md#NK-WP-0048-T34", + "db_id": "37f43c40-d014-544e-aa0b-5219dd4a6b93", + "file_value": "progress", + "db_value": "todo", + "fixable": true + }, + { + "severity": "WARN", + "check_id": "C-11", + "message": "Derived task 'NK-WP-0048-T35' is absent from this projection", + "file_path": "workplans/NK-WP-0048-security-case-types.md#NK-WP-0048-T35", + "db_id": "cf1d6ab3-846e-53ea-8575-d6347b6f4769", + "file_value": "", + "db_value": "", + "fixable": true + }, + { + "severity": "WARN", + "check_id": "C-20", + "message": "Workplan dependency target 'RAIL-PL-WP-0002' is not linked to State Hub", + "file_path": "workplans/archived/260702-NET-WP-0017-it-security-readiness-for-user-onboarding.md", + "db_id": "", + "file_value": "RAIL-PL-WP-0002", + "db_value": "", + "fixable": false + }, + { + "severity": "INFO", + "check_id": "C-08", + "message": "Retired DB workstream 'nk-wp-0035@retired-20260908' (id=064c5e8b\u2026, status=archived) has no backing workplan file \u2014 projection tombstone, not live work", + "file_path": "", + "db_id": "064c5e8b-7852-4d7f-a5fa-e26e406ab8e0", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "INFO", + "check_id": "C-08", + "message": "Retired DB workstream 'adhoc-2026-08-14@retired-20260827' (id=a5097121\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work", + "file_path": "", + "db_id": "a5097121-1461-59f8-81fa-e6ecef527337", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "INFO", + "check_id": "C-08", + "message": "Retired DB workstream 'adhoc-2026-08-23@retired-20260827' (id=e25ba5fa\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work", + "file_path": "", + "db_id": "e25ba5fa-eaae-5039-8d93-a69c6d837fc4", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "INFO", + "check_id": "C-08", + "message": "Retired DB workstream 'net-kingdom-adhoc-2026-07-02@retired-20260827' (id=67c1c7ac\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work", + "file_path": "", + "db_id": "67c1c7ac-d8b1-41dc-a81a-4e43f1afd068", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "INFO", + "check_id": "C-08", + "message": "Retired DB workstream 'net-kingdom-adhoc-2026-06-14@retired-20260827' (id=c6f3d6bf\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work", + "file_path": "", + "db_id": "c6f3d6bf-4916-490d-96ce-092d2776d7e7", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-26", + "message": "Workplan id 'NET-WP-0020' uses prefix 'NET-WP' but repo canonical prefix is 'NK-WP' \u2014 new plans must conform", + "file_path": "workplans/NET-WP-0020-openbao-unseal-custody-and-ssh-automation.md", + "db_id": "", + "file_value": "NET-WP-0020", + "db_value": "NK-WP", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-26", + "message": "Workplan id 'NET-WP-0015' uses prefix 'NET-WP' but repo canonical prefix is 'NK-WP' \u2014 new plans must conform", + "file_path": "workplans/archived/260603-NET-WP-0015-platform-root-custody-and-openbao-identity-bootstrap.md", + "db_id": "", + "file_value": "NET-WP-0015", + "db_value": "NK-WP", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-26", + "message": "Workplan id 'NET-WP-0016' uses prefix 'NET-WP' but repo canonical prefix is 'NK-WP' \u2014 new plans must conform", + "file_path": "workplans/archived/260603-NET-WP-0016-guided-security-bootstrap-experience.md", + "db_id": "", + "file_value": "NET-WP-0016", + "db_value": "NK-WP", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-26", + "message": "Workplan id 'NET-WP-0018' uses prefix 'NET-WP' but repo canonical prefix is 'NK-WP' \u2014 new plans must conform", + "file_path": "workplans/archived/260604-NET-WP-0018-bootstrap-automation-and-rebuild-readiness.md", + "db_id": "", + "file_value": "NET-WP-0018", + "db_value": "NK-WP", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-26", + "message": "Workplan id 'NET-WP-0017' uses prefix 'NET-WP' but repo canonical prefix is 'NK-WP' \u2014 new plans must conform", + "file_path": "workplans/archived/260702-NET-WP-0017-it-security-readiness-for-user-onboarding.md", + "db_id": "", + "file_value": "NET-WP-0017", + "db_value": "NK-WP", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-26", + "message": "Workplan id 'NET-WP-0019' uses prefix 'NET-WP' but repo canonical prefix is 'NK-WP' \u2014 new plans must conform", + "file_path": "workplans/archived/260702-NET-WP-0019-t06-adjacent-user-lifecycle-dry-run-polish.md", + "db_id": "", + "file_value": "NET-WP-0019", + "db_value": "NK-WP", + "fixable": false + } + ], + "fixes_applied": [ + "bindings: synced 64/64", + "C-17 cleared: pushed 1 backlogged commit(s)", + "C-33 fixed: regenerated WORK-RECORDS.md", + "C-10 fixed: task 5ace28ff\u2026 status \u2192 'done'", + "C-10 fixed: task 37f43c40\u2026 status \u2192 'progress'", + "C-11 fixed: task 'NK-WP-0048-T35' \u2192 cf1d6ab3\u2026", + "brief: .custodian-brief.md updated", + "push: pushed" + ], + "summary": { + "fail": 0, + "automation_error": 0, + "warn": 29, + "info": 5 + }, + "result": "warn" +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-current-source-publication-r3/consistency.stderr.txt b/docs/changes/netkingdom-target-2026-10-17/nk48-current-source-publication-r3/consistency.stderr.txt new file mode 100644 index 0000000..3345563 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-current-source-publication-r3/consistency.stderr.txt @@ -0,0 +1 @@ +WARN: git commit failed for writeback: Generated-only commits rejected: staged diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-current-source-publication-r3/projection.json b/docs/changes/netkingdom-target-2026-10-17/nk48-current-source-publication-r3/projection.json new file mode 100644 index 0000000..c404fee --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-current-source-publication-r3/projection.json @@ -0,0 +1,989 @@ +{ + "ok": true, + "status": "applied", + "repo_slug": "net-kingdom", + "commit": "befb0bc68cd25b625804fce559476eefd702521d", + "requests": 2, + "elapsed_ms": 7743.0, + "git": { + "upstream": "origin/main", + "behind": 0, + "ahead": 0 + }, + "identifiers": { + "schema": "repo-manager.identifier-assignment.v1", + "ok": true, + "executed": true, + "repo": "net-kingdom", + "namespace": "helixforge", + "assignments": [], + "historical_invalid_identifiers": [], + "historical_identity_collisions": [], + "files_changed": [] + }, + "receipt": { + "schema": "state-hub.repository-projection-reconcile.v1", + "instance_role": "primary", + "instance_label": "railiance01", + "expected_commit": "befb0bc68cd25b625804fce559476eefd702521d", + "derived_commit": "befb0bc68cd25b625804fce559476eefd702521d", + "outcome": { + "schema": "state-hub.projection-reset.v1", + "repo_slug": "net-kingdom", + "commit": "befb0bc68cd25b625804fce559476eefd702521d", + "status": "applied", + "counts": { + "created": 0, + "updated": 64, + "retired": 0, + "refused": 0, + "released": 0, + "created_tasks": 0, + "updated_tasks": 1, + "cancelled_tasks": 0, + "created_edges": 0, + "removed_edges": 0, + "unresolved_edges": 1, + "external_edges": 85 + }, + "created": [], + "updated": [ + "NET-WP-0015", + "NET-WP-0016", + "NET-WP-0017", + "NET-WP-0018", + "NET-WP-0019", + "NET-WP-0020", + "NK-WP-0001", + "NK-WP-0002", + "NK-WP-0003", + "NK-WP-0004", + "NK-WP-0005", + "NK-WP-0006", + "NK-WP-0007", + "NK-WP-0008", + "NK-WP-0009", + "NK-WP-0010", + "NK-WP-0011", + "NK-WP-0012", + "NK-WP-0013", + "NK-WP-0014", + "NK-WP-0015", + "NK-WP-0016", + "NK-WP-0017", + "NK-WP-0018", + "NK-WP-0019", + "NK-WP-0021", + "NK-WP-0022", + "NK-WP-0023", + "NK-WP-0024", + "NK-WP-0025", + "NK-WP-0026", + "NK-WP-0027", + "NK-WP-0028", + "NK-WP-0029", + "NK-WP-0030", + "NK-WP-0031", + "NK-WP-0032", + "NK-WP-0033", + "NK-WP-0034", + "NK-WP-0035", + "NK-WP-0036", + "NK-WP-0037", + "NK-WP-0038", + "NK-WP-0039", + "NK-WP-0040", + "NK-WP-0041", + "NK-WP-0042", + "NK-WP-0043", + "NK-WP-0044", + "NK-WP-0045", + "NK-WP-0046", + "NK-WP-0047", + "NK-WP-0048", + "NK-WP-0049", + "NK-WP-0050", + "NK-WP-0051", + "NK-WP-ADHOC-2026-06-14", + "NK-WP-ADHOC-2026-07-02", + "NK-WP-ADHOC-2026-08-14", + "NK-WP-ADHOC-2026-08-23", + "NK-WP-ADHOC-2026-10-03", + "NK-WP-ADHOC-2026-10-10", + "NK-WP-ADHOC-2026-10-11", + "NK-WP-ADHOC-2026-10-16" + ], + "retired": [], + "released": [], + "created_tasks": [], + "updated_tasks": [ + "NK-WP-0048-T35" + ], + "cancelled_tasks": [], + "created_edges": [], + "removed_edges": [], + "unresolved_edges": [ + { + "workplan": "NK-WP-0006", + "from": "NK-WP-0006", + "target": "NK-WP-0001", + "reason": "missing" + } + ], + "external_edges": [ + { + "workplan": "NK-WP-0008", + "from": "NK-WP-0008", + "target": "NK-WP-0006", + "relationship_type": "blocks", + "from_workplan_id": "053c6d96-9396-40c9-a2e5-c36531e7810d", + "from_task_id": null, + "to_workplan_id": "2eb8a5e0-4e33-4ed3-8996-a2eec3aad862", + "to_task_id": null + }, + { + "workplan": "NK-WP-0050", + "from": "NK-WP-0050-T03", + "target": "RPF-WP-0056-T03", + "relationship_type": "starts_after", + "from_workplan_id": "06ba98d5-609e-5aaa-9bf7-4c4f4460e377", + "from_task_id": "5a3aaf57-de5c-5a03-bc99-9bcf2fc5d95e", + "to_workplan_id": null, + "to_task_id": "a82762eb-bb6d-5600-89ca-a81ada2eba9f" + }, + { + "workplan": "NK-WP-0050", + "from": "NK-WP-0050-T03", + "target": "NK-WP-0050-T02", + "relationship_type": "starts_after", + "from_workplan_id": "06ba98d5-609e-5aaa-9bf7-4c4f4460e377", + "from_task_id": "5a3aaf57-de5c-5a03-bc99-9bcf2fc5d95e", + "to_workplan_id": null, + "to_task_id": "a9dadd07-f136-5dc7-9229-7a9dbb941f0d" + }, + { + "workplan": "NK-WP-0050", + "from": "NK-WP-0050-T02", + "target": "NK-WP-0050-T01", + "relationship_type": "starts_after", + "from_workplan_id": "06ba98d5-609e-5aaa-9bf7-4c4f4460e377", + "from_task_id": "a9dadd07-f136-5dc7-9229-7a9dbb941f0d", + "to_workplan_id": null, + "to_task_id": "64893d94-9717-5812-b8e3-7cabe095fcdc" + }, + { + "workplan": "NK-WP-0017", + "from": "NK-WP-0017", + "target": "NK-WP-0015", + "relationship_type": "blocks", + "from_workplan_id": "08398d26-cadf-44bc-97ee-67da790040e6", + "from_task_id": null, + "to_workplan_id": "343e035d-41e1-4fc4-b209-6872e4a5fdc8", + "to_task_id": null + }, + { + "workplan": "NK-WP-0011", + "from": "NK-WP-0011", + "target": "NK-WP-0006", + "relationship_type": "blocks", + "from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07", + "from_task_id": null, + "to_workplan_id": "2eb8a5e0-4e33-4ed3-8996-a2eec3aad862", + "to_task_id": null + }, + { + "workplan": "NK-WP-0011", + "from": "NK-WP-0011", + "target": "NK-WP-0004", + "relationship_type": "blocks", + "from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07", + "from_task_id": null, + "to_workplan_id": "d9cf7c4b-886b-4cd1-ad7b-99c4e1929c9e", + "to_task_id": null + }, + { + "workplan": "NK-WP-0011", + "from": "NK-WP-0011", + "target": "NK-WP-0003", + "relationship_type": "blocks", + "from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07", + "from_task_id": null, + "to_workplan_id": "f24cefd4-a09b-4fa1-9b25-94bf783b425e", + "to_task_id": null + }, + { + "workplan": "NK-WP-0011", + "from": "NK-WP-0011-T03", + "target": "NK-WP-0011-T02", + "relationship_type": "starts_after", + "from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07", + "from_task_id": "32d1411b-10a4-5a8b-8f43-99ac46d83908", + "to_workplan_id": null, + "to_task_id": "2fe6f100-3a5d-563e-b033-7d7b846ae487" + }, + { + "workplan": "NK-WP-0011", + "from": "NK-WP-0011-T07", + "target": "NK-WP-0011-T03", + "relationship_type": "starts_after", + "from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07", + "from_task_id": "38998c68-29bf-50d2-8c8d-0c75184d5833", + "to_workplan_id": null, + "to_task_id": "32d1411b-10a4-5a8b-8f43-99ac46d83908" + }, + { + "workplan": "NK-WP-0011", + "from": "NK-WP-0011-T07", + "target": "NK-WP-0011-T04", + "relationship_type": "starts_after", + "from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07", + "from_task_id": "38998c68-29bf-50d2-8c8d-0c75184d5833", + "to_workplan_id": null, + "to_task_id": "6697a994-7343-5ea8-8b37-bc3b921e5a9b" + }, + { + "workplan": "NK-WP-0011", + "from": "NK-WP-0011-T07", + "target": "NK-WP-0011-T05", + "relationship_type": "starts_after", + "from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07", + "from_task_id": "38998c68-29bf-50d2-8c8d-0c75184d5833", + "to_workplan_id": null, + "to_task_id": "d845380d-3dbc-5e59-8f2b-5a4d1b32d89d" + }, + { + "workplan": "NK-WP-0011", + "from": "NK-WP-0011-T04", + "target": "NK-WP-0011-T03", + "relationship_type": "starts_after", + "from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07", + "from_task_id": "6697a994-7343-5ea8-8b37-bc3b921e5a9b", + "to_workplan_id": null, + "to_task_id": "32d1411b-10a4-5a8b-8f43-99ac46d83908" + }, + { + "workplan": "NK-WP-0011", + "from": "NK-WP-0011-T06", + "target": "NK-WP-0011-T03", + "relationship_type": "starts_after", + "from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07", + "from_task_id": "9cb7fd93-c16e-5102-83ce-195b3aa87446", + "to_workplan_id": null, + "to_task_id": "32d1411b-10a4-5a8b-8f43-99ac46d83908" + }, + { + "workplan": "NK-WP-0011", + "from": "NK-WP-0011-T06", + "target": "NK-WP-0011-T04", + "relationship_type": "starts_after", + "from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07", + "from_task_id": "9cb7fd93-c16e-5102-83ce-195b3aa87446", + "to_workplan_id": null, + "to_task_id": "6697a994-7343-5ea8-8b37-bc3b921e5a9b" + }, + { + "workplan": "NK-WP-0011", + "from": "NK-WP-0011-T06", + "target": "NK-WP-0011-T05", + "relationship_type": "starts_after", + "from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07", + "from_task_id": "9cb7fd93-c16e-5102-83ce-195b3aa87446", + "to_workplan_id": null, + "to_task_id": "d845380d-3dbc-5e59-8f2b-5a4d1b32d89d" + }, + { + "workplan": "NK-WP-0011", + "from": "NK-WP-0011-T05", + "target": "NK-WP-0011-T04", + "relationship_type": "starts_after", + "from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07", + "from_task_id": "d845380d-3dbc-5e59-8f2b-5a4d1b32d89d", + "to_workplan_id": null, + "to_task_id": "6697a994-7343-5ea8-8b37-bc3b921e5a9b" + }, + { + "workplan": "NK-WP-0011", + "from": "NK-WP-0011-T09", + "target": "NK-WP-0011-T03", + "relationship_type": "starts_after", + "from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07", + "from_task_id": "d9dc4292-6b66-5797-9ff9-a1a4a556278e", + "to_workplan_id": null, + "to_task_id": "32d1411b-10a4-5a8b-8f43-99ac46d83908" + }, + { + "workplan": "NK-WP-0011", + "from": "NK-WP-0011-T09", + "target": "NK-WP-0011-T07", + "relationship_type": "starts_after", + "from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07", + "from_task_id": "d9dc4292-6b66-5797-9ff9-a1a4a556278e", + "to_workplan_id": null, + "to_task_id": "38998c68-29bf-50d2-8c8d-0c75184d5833" + }, + { + "workplan": "NK-WP-0011", + "from": "NK-WP-0011-T09", + "target": "NK-WP-0011-T08", + "relationship_type": "starts_after", + "from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07", + "from_task_id": "d9dc4292-6b66-5797-9ff9-a1a4a556278e", + "to_workplan_id": null, + "to_task_id": "e5f44ddc-9645-5f61-b84c-da5ab9cccb6d" + }, + { + "workplan": "NK-WP-0011", + "from": "NK-WP-0011-T08", + "target": "NK-WP-0011-T02", + "relationship_type": "starts_after", + "from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07", + "from_task_id": "e5f44ddc-9645-5f61-b84c-da5ab9cccb6d", + "to_workplan_id": null, + "to_task_id": "2fe6f100-3a5d-563e-b033-7d7b846ae487" + }, + { + "workplan": "NK-WP-0011", + "from": "NK-WP-0011-T08", + "target": "NK-WP-0011-T03", + "relationship_type": "starts_after", + "from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07", + "from_task_id": "e5f44ddc-9645-5f61-b84c-da5ab9cccb6d", + "to_workplan_id": null, + "to_task_id": "32d1411b-10a4-5a8b-8f43-99ac46d83908" + }, + { + "workplan": "NET-WP-0016", + "from": "NET-WP-0016", + "target": "NET-WP-0015", + "relationship_type": "blocks", + "from_workplan_id": "16069174-6698-4855-ad9e-5092c8571f38", + "from_task_id": null, + "to_workplan_id": "6b9c25e4-1008-429a-8de6-54361872c0dd", + "to_task_id": null + }, + { + "workplan": "NET-WP-0016", + "from": "NET-WP-0016", + "target": "NK-WP-0012", + "relationship_type": "blocks", + "from_workplan_id": "16069174-6698-4855-ad9e-5092c8571f38", + "from_task_id": null, + "to_workplan_id": "9b8e4afc-eb71-47d9-8750-799a082b320a", + "to_task_id": null + }, + { + "workplan": "NK-WP-0049", + "from": "NK-WP-0049-T18", + "target": "NK-WP-0049-T33", + "relationship_type": "starts_after", + "from_workplan_id": "17d315f0-829e-57c7-85cc-3a7914152804", + "from_task_id": "141dd773-c1fa-56c1-a852-bbe2372e5125", + "to_workplan_id": null, + "to_task_id": "56c8d469-7a10-5d50-a30e-95cbf3cd4756" + }, + { + "workplan": "NK-WP-0049", + "from": "NK-WP-0049-T18", + "target": "NK-WP-0049-T32", + "relationship_type": "starts_after", + "from_workplan_id": "17d315f0-829e-57c7-85cc-3a7914152804", + "from_task_id": "141dd773-c1fa-56c1-a852-bbe2372e5125", + "to_workplan_id": null, + "to_task_id": "cd78dbb7-a8f2-5754-8788-6eb1b7d97741" + }, + { + "workplan": "NK-WP-0049", + "from": "NK-WP-0049-T35", + "target": "NK-WP-0049-T34", + "relationship_type": "starts_after", + "from_workplan_id": "17d315f0-829e-57c7-85cc-3a7914152804", + "from_task_id": "1bfd1703-589f-5511-bad5-812d860e4a71", + "to_workplan_id": null, + "to_task_id": "821b3a56-6f36-5812-abc9-5a00e37cb2e7" + }, + { + "workplan": "NK-WP-0049", + "from": "NK-WP-0049-T33", + "target": "NK-WP-0049-T35", + "relationship_type": "starts_after", + "from_workplan_id": "17d315f0-829e-57c7-85cc-3a7914152804", + "from_task_id": "56c8d469-7a10-5d50-a30e-95cbf3cd4756", + "to_workplan_id": null, + "to_task_id": "1bfd1703-589f-5511-bad5-812d860e4a71" + }, + { + "workplan": "NK-WP-0049", + "from": "NK-WP-0049-T33", + "target": "NK-WP-0049-T34", + "relationship_type": "starts_after", + "from_workplan_id": "17d315f0-829e-57c7-85cc-3a7914152804", + "from_task_id": "56c8d469-7a10-5d50-a30e-95cbf3cd4756", + "to_workplan_id": null, + "to_task_id": "821b3a56-6f36-5812-abc9-5a00e37cb2e7" + }, + { + "workplan": "NK-WP-0049", + "from": "NK-WP-0049-T33", + "target": "NK-WP-0049-T36", + "relationship_type": "starts_after", + "from_workplan_id": "17d315f0-829e-57c7-85cc-3a7914152804", + "from_task_id": "56c8d469-7a10-5d50-a30e-95cbf3cd4756", + "to_workplan_id": null, + "to_task_id": "8b72334b-fc27-5707-a189-bad345eaf3bc" + }, + { + "workplan": "NK-WP-0049", + "from": "NK-WP-0049-T33", + "target": "NK-WP-0049-T32", + "relationship_type": "starts_after", + "from_workplan_id": "17d315f0-829e-57c7-85cc-3a7914152804", + "from_task_id": "56c8d469-7a10-5d50-a30e-95cbf3cd4756", + "to_workplan_id": null, + "to_task_id": "cd78dbb7-a8f2-5754-8788-6eb1b7d97741" + }, + { + "workplan": "NK-WP-0049", + "from": "NK-WP-0049-T36", + "target": "NK-WP-0049-T34", + "relationship_type": "starts_after", + "from_workplan_id": "17d315f0-829e-57c7-85cc-3a7914152804", + "from_task_id": "8b72334b-fc27-5707-a189-bad345eaf3bc", + "to_workplan_id": null, + "to_task_id": "821b3a56-6f36-5812-abc9-5a00e37cb2e7" + }, + { + "workplan": "NK-WP-0049", + "from": "NK-WP-0049-T03", + "target": "NK-WP-0049-T02", + "relationship_type": "starts_after", + "from_workplan_id": "17d315f0-829e-57c7-85cc-3a7914152804", + "from_task_id": "d68cd4d3-1ba3-530e-88f9-26ebb8dd6eac", + "to_workplan_id": null, + "to_task_id": "479d6970-6516-50f5-9436-bb5b56d58aad" + }, + { + "workplan": "NK-WP-0025", + "from": "NK-WP-0025", + "target": "NK-WP-0024", + "relationship_type": "blocks", + "from_workplan_id": "2412a251-d6b5-5f05-8082-3eb7697c51dc", + "from_task_id": null, + "to_workplan_id": "635a73d4-b3cb-5c38-af22-56a6a66beb0e", + "to_task_id": null + }, + { + "workplan": "NK-WP-0016", + "from": "NK-WP-0016", + "target": "NK-WP-0015", + "relationship_type": "blocks", + "from_workplan_id": "2d592e18-e63d-4856-97a1-f8c3e019e150", + "from_task_id": null, + "to_workplan_id": "343e035d-41e1-4fc4-b209-6872e4a5fdc8", + "to_task_id": null + }, + { + "workplan": "NK-WP-0006", + "from": "NK-WP-0006", + "target": "NK-WP-0005", + "relationship_type": "blocks", + "from_workplan_id": "2eb8a5e0-4e33-4ed3-8996-a2eec3aad862", + "from_task_id": null, + "to_workplan_id": "75bc472b-cc0a-48f2-afb6-62b896f7cc19", + "to_task_id": null + }, + { + "workplan": "NK-WP-0006", + "from": "NK-WP-0006", + "target": "NK-WP-0004", + "relationship_type": "blocks", + "from_workplan_id": "2eb8a5e0-4e33-4ed3-8996-a2eec3aad862", + "from_task_id": null, + "to_workplan_id": "d9cf7c4b-886b-4cd1-ad7b-99c4e1929c9e", + "to_task_id": null + }, + { + "workplan": "NK-WP-0013", + "from": "NK-WP-0013", + "target": "NK-WP-0006", + "relationship_type": "blocks", + "from_workplan_id": "32a54d8e-8633-42a6-8ec1-104842c581c1", + "from_task_id": null, + "to_workplan_id": "2eb8a5e0-4e33-4ed3-8996-a2eec3aad862", + "to_task_id": null + }, + { + "workplan": "NK-WP-0015", + "from": "NK-WP-0015", + "target": "NK-WP-0014", + "relationship_type": "blocks", + "from_workplan_id": "343e035d-41e1-4fc4-b209-6872e4a5fdc8", + "from_task_id": null, + "to_workplan_id": "b9bf56bc-7ef6-43eb-badc-fa0f4896c63c", + "to_task_id": null + }, + { + "workplan": "NET-WP-0017", + "from": "NET-WP-0017", + "target": "NET-WP-0016", + "relationship_type": "blocks", + "from_workplan_id": "385de708-fd59-4bab-a4f4-28c1c476b3ea", + "from_task_id": null, + "to_workplan_id": "16069174-6698-4855-ad9e-5092c8571f38", + "to_task_id": null + }, + { + "workplan": "NET-WP-0017", + "from": "NET-WP-0017", + "target": "NET-WP-0015", + "relationship_type": "blocks", + "from_workplan_id": "385de708-fd59-4bab-a4f4-28c1c476b3ea", + "from_task_id": null, + "to_workplan_id": "6b9c25e4-1008-429a-8de6-54361872c0dd", + "to_task_id": null + }, + { + "workplan": "NK-WP-0007", + "from": "NK-WP-0007", + "target": "NK-WP-0006", + "relationship_type": "blocks", + "from_workplan_id": "3cbc81ec-7ad5-46cf-a4a0-fc5fe9873695", + "from_task_id": null, + "to_workplan_id": "2eb8a5e0-4e33-4ed3-8996-a2eec3aad862", + "to_task_id": null + }, + { + "workplan": "NK-WP-0007", + "from": "NK-WP-0007", + "target": "NK-WP-0005", + "relationship_type": "blocks", + "from_workplan_id": "3cbc81ec-7ad5-46cf-a4a0-fc5fe9873695", + "from_task_id": null, + "to_workplan_id": "75bc472b-cc0a-48f2-afb6-62b896f7cc19", + "to_task_id": null + }, + { + "workplan": "NK-WP-0007", + "from": "NK-WP-0007", + "target": "NK-WP-0004", + "relationship_type": "blocks", + "from_workplan_id": "3cbc81ec-7ad5-46cf-a4a0-fc5fe9873695", + "from_task_id": null, + "to_workplan_id": "d9cf7c4b-886b-4cd1-ad7b-99c4e1929c9e", + "to_task_id": null + }, + { + "workplan": "NK-WP-0024", + "from": "NK-WP-0024", + "target": "NK-WP-0023", + "relationship_type": "blocks", + "from_workplan_id": "635a73d4-b3cb-5c38-af22-56a6a66beb0e", + "from_task_id": null, + "to_workplan_id": "fdb3deee-06c3-5d12-91ce-9c50b2ac0618", + "to_task_id": null + }, + { + "workplan": "NET-WP-0015", + "from": "NET-WP-0015", + "target": "NK-WP-0006", + "relationship_type": "blocks", + "from_workplan_id": "6b9c25e4-1008-429a-8de6-54361872c0dd", + "from_task_id": null, + "to_workplan_id": "2eb8a5e0-4e33-4ed3-8996-a2eec3aad862", + "to_task_id": null + }, + { + "workplan": "NET-WP-0015", + "from": "NET-WP-0015", + "target": "NK-WP-0012", + "relationship_type": "blocks", + "from_workplan_id": "6b9c25e4-1008-429a-8de6-54361872c0dd", + "from_task_id": null, + "to_workplan_id": "9b8e4afc-eb71-47d9-8750-799a082b320a", + "to_task_id": null + }, + { + "workplan": "NK-WP-0018", + "from": "NK-WP-0018", + "target": "NK-WP-0017", + "relationship_type": "blocks", + "from_workplan_id": "6f75035a-e056-4eab-8fdb-00a18bacdf87", + "from_task_id": null, + "to_workplan_id": "08398d26-cadf-44bc-97ee-67da790040e6", + "to_task_id": null + }, + { + "workplan": "NK-WP-0018", + "from": "NK-WP-0018", + "target": "NK-WP-0016", + "relationship_type": "blocks", + "from_workplan_id": "6f75035a-e056-4eab-8fdb-00a18bacdf87", + "from_task_id": null, + "to_workplan_id": "2d592e18-e63d-4856-97a1-f8c3e019e150", + "to_task_id": null + }, + { + "workplan": "NK-WP-0005", + "from": "NK-WP-0005", + "target": "NK-WP-0004", + "relationship_type": "blocks", + "from_workplan_id": "75bc472b-cc0a-48f2-afb6-62b896f7cc19", + "from_task_id": null, + "to_workplan_id": "d9cf7c4b-886b-4cd1-ad7b-99c4e1929c9e", + "to_task_id": null + }, + { + "workplan": "NET-WP-0019", + "from": "NET-WP-0019", + "target": "NET-WP-0017", + "relationship_type": "blocks", + "from_workplan_id": "75d388b6-7ec1-4e1b-8c87-6ff44f953210", + "from_task_id": null, + "to_workplan_id": "385de708-fd59-4bab-a4f4-28c1c476b3ea", + "to_task_id": null + }, + { + "workplan": "NET-WP-0019", + "from": "NET-WP-0019", + "target": "NET-WP-0018", + "relationship_type": "blocks", + "from_workplan_id": "75d388b6-7ec1-4e1b-8c87-6ff44f953210", + "from_task_id": null, + "to_workplan_id": "800f9f16-bc44-4bbf-a771-58a630a3b698", + "to_task_id": null + }, + { + "workplan": "NET-WP-0018", + "from": "NET-WP-0018", + "target": "NET-WP-0017", + "relationship_type": "blocks", + "from_workplan_id": "800f9f16-bc44-4bbf-a771-58a630a3b698", + "from_task_id": null, + "to_workplan_id": "385de708-fd59-4bab-a4f4-28c1c476b3ea", + "to_task_id": null + }, + { + "workplan": "NET-WP-0018", + "from": "NET-WP-0018", + "target": "NET-WP-0015", + "relationship_type": "blocks", + "from_workplan_id": "800f9f16-bc44-4bbf-a771-58a630a3b698", + "from_task_id": null, + "to_workplan_id": "6b9c25e4-1008-429a-8de6-54361872c0dd", + "to_task_id": null + }, + { + "workplan": "NK-WP-0027", + "from": "NK-WP-0027-T03", + "target": "NK-WP-0027-T02", + "relationship_type": "starts_after", + "from_workplan_id": "965ad365-6b81-50a1-a2a3-2d0c1fcce0b4", + "from_task_id": "e3f0bb0f-0c85-5cb9-a5ab-75f668cd3447", + "to_workplan_id": null, + "to_task_id": "5b35b646-a525-55da-852a-6005613d42ea" + }, + { + "workplan": "NK-WP-0012", + "from": "NK-WP-0012", + "target": "NK-WP-0006", + "relationship_type": "blocks", + "from_workplan_id": "9b8e4afc-eb71-47d9-8750-799a082b320a", + "from_task_id": null, + "to_workplan_id": "2eb8a5e0-4e33-4ed3-8996-a2eec3aad862", + "to_task_id": null + }, + { + "workplan": "NK-WP-0014", + "from": "NK-WP-0014", + "target": "NK-WP-0013", + "relationship_type": "blocks", + "from_workplan_id": "b9bf56bc-7ef6-43eb-badc-fa0f4896c63c", + "from_task_id": null, + "to_workplan_id": "32a54d8e-8633-42a6-8ec1-104842c581c1", + "to_task_id": null + }, + { + "workplan": "NK-WP-0014", + "from": "NK-WP-0014", + "target": "NK-WP-0012", + "relationship_type": "blocks", + "from_workplan_id": "b9bf56bc-7ef6-43eb-badc-fa0f4896c63c", + "from_task_id": null, + "to_workplan_id": "9b8e4afc-eb71-47d9-8750-799a082b320a", + "to_task_id": null + }, + { + "workplan": "NK-WP-0019", + "from": "NK-WP-0019", + "target": "NK-WP-0018", + "relationship_type": "blocks", + "from_workplan_id": "d2daa8b4-8ecf-4377-b382-492e653735f7", + "from_task_id": null, + "to_workplan_id": "6f75035a-e056-4eab-8fdb-00a18bacdf87", + "to_task_id": null + }, + { + "workplan": "NK-WP-0009", + "from": "NK-WP-0009", + "target": "NK-WP-0008", + "relationship_type": "blocks", + "from_workplan_id": "d4d02dbf-3974-502d-8b87-b776fc63e17e", + "from_task_id": null, + "to_workplan_id": "053c6d96-9396-40c9-a2e5-c36531e7810d", + "to_task_id": null + }, + { + "workplan": "NK-WP-0022", + "from": "NK-WP-0022", + "target": "NK-WP-0023", + "relationship_type": "blocks", + "from_workplan_id": "d76ddccc-00c8-548a-b141-2cd660fa38da", + "from_task_id": null, + "to_workplan_id": "fdb3deee-06c3-5d12-91ce-9c50b2ac0618", + "to_task_id": null + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T15", + "target": "NK-WP-0048-T31", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "1b825705-4848-5edc-832f-b1622cd9bdb7", + "to_workplan_id": null, + "to_task_id": "ed1ccd87-89a0-5e69-885c-0eaf5ab0bb5a" + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T16", + "target": "NK-WP-0048-T25", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "200bf500-5084-5ae7-be26-3033fe1f296e", + "to_workplan_id": null, + "to_task_id": "0de2a7d9-874d-578b-a076-f653fe42e30b" + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T16", + "target": "NK-WP-0048-T29", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "200bf500-5084-5ae7-be26-3033fe1f296e", + "to_workplan_id": null, + "to_task_id": "129db41d-4560-5e20-9f3b-c3c91e0a5601" + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T16", + "target": "NK-WP-0048-T24", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "200bf500-5084-5ae7-be26-3033fe1f296e", + "to_workplan_id": null, + "to_task_id": "1a3931ee-3b5f-52fc-9295-4073804f4bd2" + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T16", + "target": "NK-WP-0048-T28", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "200bf500-5084-5ae7-be26-3033fe1f296e", + "to_workplan_id": null, + "to_task_id": "6dab281c-ea9a-5a17-a83f-e8e9a4d4484f" + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T16", + "target": "NK-WP-0048-T30", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "200bf500-5084-5ae7-be26-3033fe1f296e", + "to_workplan_id": null, + "to_task_id": "6f6073ac-52dd-5b70-8874-6d76d4c072e4" + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T16", + "target": "NK-WP-0048-T26", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "200bf500-5084-5ae7-be26-3033fe1f296e", + "to_workplan_id": null, + "to_task_id": "94b3cbf0-c908-5ac6-b0db-67385c3e021c" + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T16", + "target": "NK-WP-0048-T27", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "200bf500-5084-5ae7-be26-3033fe1f296e", + "to_workplan_id": null, + "to_task_id": "e56365f8-29b5-5c89-a329-8115f9f3b2a7" + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T07", + "target": "NK-WP-0048-T06", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "c3c9c3ae-4f23-5ba7-9e6f-e63bcf29b9f7", + "to_workplan_id": null, + "to_task_id": "d9b26bcf-a7c6-59a2-92a0-f913d76a203c" + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T06", + "target": "NK-WP-0048-T09", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "d9b26bcf-a7c6-59a2-92a0-f913d76a203c", + "to_workplan_id": null, + "to_task_id": "0ee12dfc-8260-5038-bd39-cc7fb4113be0" + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T06", + "target": "NK-WP-0048-T15", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "d9b26bcf-a7c6-59a2-92a0-f913d76a203c", + "to_workplan_id": null, + "to_task_id": "1b825705-4848-5edc-832f-b1622cd9bdb7" + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T06", + "target": "NK-WP-0048-T16", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "d9b26bcf-a7c6-59a2-92a0-f913d76a203c", + "to_workplan_id": null, + "to_task_id": "200bf500-5084-5ae7-be26-3033fe1f296e" + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T06", + "target": "NK-WP-0048-T05", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "d9b26bcf-a7c6-59a2-92a0-f913d76a203c", + "to_workplan_id": null, + "to_task_id": "3d987d3d-0797-538b-86e4-51185a327ae2" + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T06", + "target": "MASON-WP-0010-T03", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "d9b26bcf-a7c6-59a2-92a0-f913d76a203c", + "to_workplan_id": null, + "to_task_id": "56cea6be-c613-586c-80cd-125340b374da" + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T06", + "target": "NK-WP-0048-T12", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "d9b26bcf-a7c6-59a2-92a0-f913d76a203c", + "to_workplan_id": null, + "to_task_id": "7ce4eee3-cd47-55a9-bfa5-4f1835d4d6fd" + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T06", + "target": "NK-WP-0048-T20", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "d9b26bcf-a7c6-59a2-92a0-f913d76a203c", + "to_workplan_id": null, + "to_task_id": "863fcbdb-e246-51ba-87ca-a2f44d01d6f8" + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T06", + "target": "NK-WP-0048-T17", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "d9b26bcf-a7c6-59a2-92a0-f913d76a203c", + "to_workplan_id": null, + "to_task_id": "8c3e4baa-a218-5996-af9e-fe1153a46dac" + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T06", + "target": "NK-WP-0048-T08", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "d9b26bcf-a7c6-59a2-92a0-f913d76a203c", + "to_workplan_id": null, + "to_task_id": "8e519082-dc8b-5e46-b660-03d03e55dcf6" + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T06", + "target": "NK-WP-0048-T18", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "d9b26bcf-a7c6-59a2-92a0-f913d76a203c", + "to_workplan_id": null, + "to_task_id": "9c45da71-fb4c-5f20-9065-90f448f37c64" + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T06", + "target": "NK-WP-0048-T13", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "d9b26bcf-a7c6-59a2-92a0-f913d76a203c", + "to_workplan_id": null, + "to_task_id": "b264c10b-565c-5d29-b788-03cf5fbd2883" + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T06", + "target": "WARDEN-WP-0044-T03", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "d9b26bcf-a7c6-59a2-92a0-f913d76a203c", + "to_workplan_id": null, + "to_task_id": "bcbf6c91-2c04-5c16-ae8c-5d6c06fd6b89" + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T06", + "target": "NK-WP-0048-T14", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "d9b26bcf-a7c6-59a2-92a0-f913d76a203c", + "to_workplan_id": null, + "to_task_id": "fce95440-a25b-5f27-8ef3-a3d182099113" + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T14", + "target": "NK-WP-0048-T20", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "fce95440-a25b-5f27-8ef3-a3d182099113", + "to_workplan_id": null, + "to_task_id": "863fcbdb-e246-51ba-87ca-a2f44d01d6f8" + }, + { + "workplan": "NK-WP-0010", + "from": "NK-WP-0010", + "target": "NK-WP-0008", + "relationship_type": "blocks", + "from_workplan_id": "f4faf8b4-ae57-40cf-a881-6fe66ca6ad74", + "from_task_id": null, + "to_workplan_id": "053c6d96-9396-40c9-a2e5-c36531e7810d", + "to_task_id": null + } + ], + "refused": [], + "notes": [] + } + } +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-current-source-publication-r3/projection.stderr.txt b/docs/changes/netkingdom-target-2026-10-17/nk48-current-source-publication-r3/projection.stderr.txt new file mode 100644 index 0000000..e69de29 diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-current-source-publication-r3/publication-commit.json b/docs/changes/netkingdom-target-2026-10-17/nk48-current-source-publication-r3/publication-commit.json new file mode 100644 index 0000000..c62e0c0 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-current-source-publication-r3/publication-commit.json @@ -0,0 +1,29 @@ +{ + "exit_code": 0, + "commit": "befb0bc68cd25b625804fce559476eefd702521d", + "base": "342a6f25af5cdc602841948f42759ea52ef1c8f5", + "files": [ + { + "path": "sso-mfa/k8s/keycape/test_admit_1_3_type.py", + "sha256": "2186eab00b000a534ab1452f14fa7b1a7ed352d5b27c400687c1c8ec7c4c3019" + }, + { + "path": "sso-mfa/k8s/keycape/test_keycape_registration_adapter.py", + "sha256": "86268e082763c388528e5175184fd32362167c3441716436e4b1cab150c3e79a" + }, + { + "path": "sso-mfa/k8s/keycape/kernel-catalog.fixture.yaml", + "sha256": "7885b8c380c03ccb0198e540532ce80c791c8e86698521d7173642e0c7a56e53" + }, + { + "path": "workplans/NK-WP-0048-security-case-types.md", + "sha256": "80279bbdf6d36e2321ecfeb5a3f996a8345e6da2ceb9b26167e52b3668ba7e85" + }, + { + "path": "security-case-type-revisions/1.3/oidc-browser-relying-party/type.yaml", + "sha256": "ef02ddae3167bdd1558193a5f6db0379fb8cc84207531706d5ef35caabc3684f" + } + ], + "stdout": "[main befb0bc] fix: bind issuer template exactly and align catalog admission fixtures\n 5 files changed, 209 insertions(+), 10 deletions(-)\n create mode 100644 sso-mfa/k8s/keycape/kernel-catalog.fixture.yaml\n", + "stderr": "{\"blocked_blob_ids\": [], \"scanned_blobs\": 5, \"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/nk48-current-source-publication-r3/publication-inputs.final.json b/docs/changes/netkingdom-target-2026-10-17/nk48-current-source-publication-r3/publication-inputs.final.json new file mode 100644 index 0000000..b39b864 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-current-source-publication-r3/publication-inputs.final.json @@ -0,0 +1,27 @@ +{ + "owner": "net-kingdom", + "base_revision": "342a6f25af5cdc602841948f42759ea52ef1c8f5", + "message": "fix: bind issuer template exactly and align catalog admission fixtures", + "files": [ + { + "path": "sso-mfa/k8s/keycape/test_admit_1_3_type.py", + "sha256": "2186eab00b000a534ab1452f14fa7b1a7ed352d5b27c400687c1c8ec7c4c3019" + }, + { + "path": "sso-mfa/k8s/keycape/test_keycape_registration_adapter.py", + "sha256": "86268e082763c388528e5175184fd32362167c3441716436e4b1cab150c3e79a" + }, + { + "path": "sso-mfa/k8s/keycape/kernel-catalog.fixture.yaml", + "sha256": "7885b8c380c03ccb0198e540532ce80c791c8e86698521d7173642e0c7a56e53" + }, + { + "path": "workplans/NK-WP-0048-security-case-types.md", + "sha256": "80279bbdf6d36e2321ecfeb5a3f996a8345e6da2ceb9b26167e52b3668ba7e85" + }, + { + "path": "security-case-type-revisions/1.3/oidc-browser-relying-party/type.yaml", + "sha256": "ef02ddae3167bdd1558193a5f6db0379fb8cc84207531706d5ef35caabc3684f" + } + ] +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-current-source-publication-r3/publication-inputs.json b/docs/changes/netkingdom-target-2026-10-17/nk48-current-source-publication-r3/publication-inputs.json new file mode 100644 index 0000000..251ed01 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-current-source-publication-r3/publication-inputs.json @@ -0,0 +1,176 @@ +{ + "schema": "custodian.nk48-catalog-fixture-publication-inputs.v1", + "prepared_only": true, + "owner": "net-kingdom", + "base_revision": "342a6f25af5cdc602841948f42759ea52ef1c8f5", + "warden_revision": "922c26da0f3fcb0182352ea83e6ae06f29878580", + "message": "test: align security case fixtures with authenticated catalog admission", + "workplan": "workplans/NK-WP-0048-security-case-types.md", + "workplan_preimage_sha256": "sha256:f92962c5aa21aade0fc2f6b4f8d9a95932fe85b107413cdd2824b6ebe72c36d9", + "workplan_candidate_sha256": "sha256:3bce122c5b83d9044b142fc2cabc48de5558ab0d65dc0223a8ed0325f219e981", + "task_addition_sha256": "sha256:7d14d261ffbab2a930c4a3ff962cec786060cad6177280773cf1c0b0f006127f", + "patch_sha256": "sha256:df69fb2ec9f1e1cf0dd94194e3c046c995ed8a7deabf65a8e9bec34662adb501", + "files": [ + { + "path": "sso-mfa/k8s/keycape/test_admit_1_3_type.py", + "preimage_sha256": "sha256:c512ba00513d066fd1ca8af08fd6bf106fbd7a132981b1dcc5af80a745ab03f0", + "sha256": "sha256:2186eab00b000a534ab1452f14fa7b1a7ed352d5b27c400687c1c8ec7c4c3019" + }, + { + "path": "sso-mfa/k8s/keycape/test_keycape_registration_adapter.py", + "preimage_sha256": "sha256:4d0126416522e54eec1ae4c83b5488e09ff11ffb97ec1ee6d234548780c60f63", + "sha256": "sha256:86268e082763c388528e5175184fd32362167c3441716436e4b1cab150c3e79a" + }, + { + "path": "sso-mfa/k8s/keycape/kernel-catalog.fixture.yaml", + "preimage_sha256": null, + "sha256": "sha256:7885b8c380c03ccb0198e540532ce80c791c8e86698521d7173642e0c7a56e53" + } + ], + "allocation": { + "method": "repo_manager.identifiers.ensure_missing_work_record_identifiers", + "manual_uuid": false, + "preexisting_assignments": [], + "expected_new_record_ids": [ + "NK-WP-0048-T35" + ], + "expected_files_changed": [ + "workplans/NK-WP-0048-security-case-types.md" + ], + "expected_task_count": 35, + "concrete_postallocation_manifest": "publication-inputs.allocated.json" + }, + "protected_files": { + ".custodian-brief.md": "sha256:3eb7f91f2443090c9b859a716d83711310364897b6144dcf10d3254342fec3d1", + "WORK-RECORDS.md": "sha256:a1fec217a6e380f528778d781dbea1fc5a836fdefa1dd4dd0308100fbe08112a" + }, + "validation": { + "tests": [ + "sso-mfa/k8s/keycape/test_admit_1_3_type.py", + "sso-mfa/k8s/keycape/test_keycape_registration_adapter.py" + ], + "expected_result": "31 passed,53 subtests passed; no skipped or deselected tests", + "python": "/tmp/warden-kernel-review-r1/final/.venv/bin/python", + "source_hashes": { + "net-kingdom": { + "sso-mfa/k8s/keycape/test_vergabe_client_rollout.py": "sha256:f9b7defc312b0fea24ded92ef479dcc8aef063f7a66a8245760918bcf44c4af1", + "sso-mfa/k8s/keycape/test_compose_1_2_descriptors.py": "sha256:9a4b9fb0141221d6e5bcb72b3656613afda54d16ede300bffa8f71956ae6fd2c", + "sso-mfa/k8s/keycape/test_keycape_client.py": "sha256:9572044679feaedfe732e15a2b6f8ef6b41a8fe2e4430257f309a3e83e97736c", + "sso-mfa/k8s/keycape/openbao-negative-node-wrapper.py": "sha256:76ac013d1a676a0dfa58aea1ce87b0c3e72483ccd21f586a22c5475e9132f6c1", + "sso-mfa/k8s/keycape/portal-client-rollout.py": "sha256:158e1d36eb881cdd0a2eda91b472adea5e8fde5061e00d26dda368580aeb871f", + "sso-mfa/k8s/keycape/test_playwright_activation.py": "sha256:6fc22d091f27a76c32d2fb0a7fc3c6e18e54ff98d242699c518b2f9dc9fd4cbd", + "sso-mfa/k8s/keycape/exercise-approval-clients.py": "sha256:e2d3dd741ac52af28dce752344227a4443b78fedd4cfcc06ba902e679dfc9351", + "sso-mfa/k8s/keycape/keycape-client.py": "sha256:4b63161cf3365d34dd684a3116e2c935baaf7f6aa4b30983f6883546db3d6591", + "sso-mfa/k8s/keycape/test_openbao_negative_session.py": "sha256:01e23f2b4fc77824a161a57acb3a52d19aad846b3b6c0fd3abd81afa1dc2985c", + "sso-mfa/k8s/keycape/authentication-policy-rollout.py": "sha256:2c1bd23fded4855bae1eb3a08dbc4510d7f60f2bc4d625d60e471ab78f6f0251", + "sso-mfa/k8s/keycape/handover-adapter.py": "sha256:bad7c1132258f2140b0d83548031ac86eecea4f4019f34adedc1e9e3774a3832", + "sso-mfa/k8s/keycape/openbao-signin-diagnosis-rollout.py": "sha256:a57b957d6eed9e8fb09d2a7aa7405a39deeafd9aaf57a9e6dc907afabd380371", + "sso-mfa/k8s/keycape/test_handover_adapter.py": "sha256:cd1fb0eb64a777699221ebdfcd6fea96bafb9b27ec388e95dcfe66189eac8ca2", + "sso-mfa/k8s/keycape/test_authentication_policy_rollout.py": "sha256:11c10039d99a34d2819b09146a966c8b7cba6095e7a8b51c055a01e7167e3ed5", + "sso-mfa/k8s/keycape/test_deliver_consumer_secret_adapter.py": "sha256:f4e6c6acd75a36f675007d2f62a7c7c4f7bfe2951cbb5e3e4cdde8b53f17f473", + "sso-mfa/k8s/keycape/bind-env-adapter.py": "sha256:fa17fae21776b5c620ee9782adf050649c3a28719a6687d2524ff67ca7ad1925", + "sso-mfa/k8s/keycape/test_keycape_checker_snapshot.py": "sha256:9271f25c760ad732c257d59d259b5c767436a2dc2d5ba24fca259bc57761d6d1", + "sso-mfa/k8s/keycape/test_openbao_signin_diagnosis_rollout.py": "sha256:25d46fe7caca2ad4c262bd2a25188c91847304b6b051c4f3b3705c69f2803f01", + "sso-mfa/k8s/keycape/test_openbao_private_ready.py": "sha256:8a7a033b1c509ba21397d1d74580c44f24592045b2b9de50fe87638607224277", + "sso-mfa/k8s/keycape/test_mint_secret_adapter.py": "sha256:8110b6a8abd8e9a1f5e1feae11ec09c4e28cb7486c759b0e8459da1fb1b7144d", + "sso-mfa/k8s/keycape/playwright-activation.py": "sha256:5ac683b3324b00e6253f6040f4c73057027b1f98f7071160a6153794ba522757", + "sso-mfa/k8s/keycape/test_admit_1_3_type.py": "sha256:2186eab00b000a534ab1452f14fa7b1a7ed352d5b27c400687c1c8ec7c4c3019", + "sso-mfa/k8s/keycape/test_openbao_node_route.py": "sha256:1c57053be6e8472174e4034fdfbf34b3e1729f5e239b3a9af89d9e59a08d6cb6", + "sso-mfa/k8s/keycape/test_secrets_engine_openbao_client_rollout.py": "sha256:e6f86fb7712c1b9c01d9b684f26d5cc949649b823d2732eced132d59acd3fe01", + "sso-mfa/k8s/keycape/openbao-client-config.py": "sha256:1b908b4c36134212d78f9fbe7a2a201eb3ced06d31bb1dcea4a59d80689d84b1", + "sso-mfa/k8s/keycape/test_registration_original_intent_reconcile.py": "sha256:e2897b8200ec97445fc4a26c55ad82651fcb1a4d22efe7fe59d0fcfc5cd8ec71", + "sso-mfa/k8s/keycape/test_ensure_groups_adapter.py": "sha256:c8754a2ba735116edc6fe257aa993d0530cea9ca9f954ea5a621e38694906be2", + "sso-mfa/k8s/keycape/test_keycape_checker_environment.py": "sha256:35f316eed168d3df588498faf24ab9377733a7d857ddfa5e0e4322a6287c93ff", + "sso-mfa/k8s/keycape/test_bind_env_adapter.py": "sha256:a1e5e7a631a4843f9a0d2310a3942b26936103d786aeae47a142ff7c7b2fdbdb", + "sso-mfa/k8s/keycape/create-secrets.py": "sha256:5da672fff36eb94101741ad99e2a1a96ae81fea15ed1871304c40e63ce27c787", + "sso-mfa/k8s/keycape/approval-clients-rollout.py": "sha256:488d5cc59e081993dd2b5fc6d9e46bbddcaec4f5170b8f7a5ad0aae3a211a790", + "sso-mfa/k8s/keycape/vergabe-client-rollout.py": "sha256:8cee6cc5ff3e3e8617304055263d04b393d0e005c34d9af12cb7e8bc685bac1a", + "sso-mfa/k8s/keycape/keycape-registration-adapter.py": "sha256:a53beea4dbbe373ce7a5b84d55e79be8407ab9688037c7ee057afcc7be576047", + "sso-mfa/k8s/keycape/compose-1.2-descriptors.py": "sha256:39021cac900b11a59d00c5a1d5491b1ee4c639955cde07d07854c3db8bc04958", + "sso-mfa/k8s/keycape/test_approval_clients_rollout.py": "sha256:745316d42c3aba6b59b562d3bbcd4b2cb92871fb5e51a954e2254e2e550bb408", + "sso-mfa/k8s/keycape/register-user-engine-portal.py": "sha256:67173edc44f5457e7b6a3e0fb2321b398159cfce5dea8ead01eabf835fb38d76", + "sso-mfa/k8s/keycape/test_issuer_pin.py": "sha256:e471ed815af246633c0b21ac2cf5cae94d0dbdeff9e6ce801b4ddd033097dc20", + "sso-mfa/k8s/keycape/openbao-negative-session.py": "sha256:24b7b803e4eb7e6e7c0700a069dad0ec315ee7eb5538a88b9e56147f8fab5cdd", + "sso-mfa/k8s/keycape/test_keycape_registration_adapter.py": "sha256:86268e082763c388528e5175184fd32362167c3441716436e4b1cab150c3e79a", + "sso-mfa/k8s/keycape/deliver-consumer-secret-adapter.py": "sha256:65db5386cdf08a1aa932886c046abed20330a36c96e63d4267adb8341b23da42", + "sso-mfa/k8s/keycape/test_portal_client_rollout.py": "sha256:a16cd4bf278970c10de2e1b1025597b3872c9515713dd947ac6b50cb78feea1f", + "sso-mfa/k8s/keycape/test_deliver_secret_adapter.py": "sha256:24cedec29071b62fc2c2458950ed244feeff542bc20a7d480667dd99cda16b03", + "sso-mfa/k8s/keycape/deliver-secret-adapter.py": "sha256:cd1a515a1ae2def2093660ed2a810a9c66d1d4cf7126f573ee7e9c4c42afb98d", + "sso-mfa/k8s/keycape/test_openbao_private_input.py": "sha256:84277edb5bd7858885173dea802b6bdc4f0f0c7785d6e200ee9a98d8553891a1", + "sso-mfa/k8s/keycape/register-coulomb-social.py": "sha256:b40ff4bdba71b797f8f393e79cb3c9d73b790045a0f7df6ecea35803369793c7", + "sso-mfa/k8s/keycape/mint-secret-adapter.py": "sha256:246323c7243ded18229ab063956577e151d56d9be485d3bd44fc38e5af0edf3c", + "sso-mfa/k8s/keycape/test_adapter_source_closure.py": "sha256:63cb442636d996fcf1c36b7b746ed860106a7da35dbaeb1c5129a41420ca4155", + "sso-mfa/k8s/keycape/secrets-engine-openbao-client-rollout.py": "sha256:8b834e5f0353a5e11797637420609e06e7bf7de47777c7e78b774f74d4a6afbd", + "sso-mfa/k8s/keycape/test_openbao_client_config.py": "sha256:7104a73fb936aca4829ef8046e39497d0b7347d3e74810f672e4d4ddc3346457", + "sso-mfa/k8s/keycape/ensure-groups-adapter.py": "sha256:6cba5b07a9262a8e0bf445dc4095ba03b31ddc15e2efbfe374e93f4f78b6aed4", + "sso-mfa/k8s/keycape/verify-user-engine-portal.py": "sha256:ccd5106395bd5d7b4f3dbbcbad449e3793aff217d4429b5d5af9e52ab9a58103", + "sso-mfa/k8s/keycape/test_playwright_registration.py": "sha256:b97ea8619ac768f520e4058aeb50b0b1e8ef6d2e9a1a0fff932a151a3d4b61ae", + "sso-mfa/k8s/keycape/playwright-registration.py": "sha256:bf2d6b83d93629df8ddaf6c31d6c7408ae3866969d63895714df0e08a570da99", + "identity-provisioner/group_provenance_postgres.py": "sha256:2a0111e83ad7b611076a1b45634e7f698b3d2e9acc473320b4a8ba009ebd0932", + "identity-provisioner/password_setup.py": "sha256:53012fc708384fc9cd0fff4f3fa5f204ab85866db91d726457dd130799e88ca0", + "identity-provisioner/provisioner.py": "sha256:19d1b837cbafeeacb1aa815856cd68091e6702281d72b1d860230fa320e1725b", + "identity-provisioner/copy-client-secret.py": "sha256:0f1907a9f16688148aede9fceb53a55cfd0504bcb8552f158d0fa77e9ad960c8", + "identity-provisioner/group_provenance.py": "sha256:405e08eaaab7c21d618dff4db5970607806f32ab6c461effb81c737e32182b10", + "identity-provisioner/server.py": "sha256:b61c83da6c7ce20fa2790d5bb00aeca6a91dccd133526bc559ed26562f90eb29", + "security-case-types/oidc-browser-relying-party/type.yaml": "sha256:f705c64e4875e9b8e18a912fb51c135e77898926d6a451f5cb74ab3c0cbe4d1c", + "security-case-type-revisions/1.2/oidc-browser-relying-party/type.yaml": "sha256:9e60ff569fb0d860a2bbde7bbb0991a54d4b0688f62b56df0fa5d161fd624578", + "security-case-type-revisions/1.3/oidc-browser-relying-party/type.yaml": "sha256:ef02ddae3167bdd1558193a5f6db0379fb8cc84207531706d5ef35caabc3684f", + "security-case-type-revisions/1.3/oidc-browser-relying-party/examples/forgejo.yaml": "sha256:546aad5cf26842c484fc6ba99940d56697b85531afea11872556761008923c94", + "security-cases/forgejo-netkingdom-oidc.yaml": "sha256:bcc2c6d5bae0d28242b3cdb71e74d8ca297c2afe04766b55d53597f86893351d", + "sso-mfa/k8s/keycape/descriptor-composition-1.3.candidate.json": "sha256:773daa1df72cc7cf6a6d8aa082208181ca2438cdecc9179b1bc5b1c3a09099c2", + "sso-mfa/k8s/keycape/kernel-catalog.fixture.yaml": "sha256:7885b8c380c03ccb0198e540532ce80c791c8e86698521d7173642e0c7a56e53", + "tools/security-case-types/security_case_evidence.py": "sha256:da8efa59c52ad73283397ebe639ae259d0cb9c6348b5cb829135c24230dcdc75", + "tools/security-case-types/security_case_validator.py": "sha256:1bcc62e9bc374c711363295549f3cd5bb0b32952671ed4b08fddc7f74742959b", + "tools/security-scenario-composer/security_scenario_composer.py": "sha256:9f0cc69930aa8ee2cc3fb92236e6a99728df17b8c0fe36e1250241c38fc01c4f", + "tools/playbook-capability-contract/playbook_contract_validator.py": "sha256:2e1e0c809d4bc9d64c2d974cc23fecd1c5636785154c2554edd9191852fd926d" + }, + "ops-warden": { + "src/warden/ca.py": "sha256:d5282819c5d29386fd59a7dc46d1abdc01e99fc6838a1a6637f90868389fe557", + "src/warden/inventory.py": "sha256:9401db92d7cbf3bea962b97e83ef2c252a19c69ab94ce4836ba58f68638cb311", + "src/warden/memory.py": "sha256:ecf5f7ccabb669b56bfca3a109b10bbe8437bdd2ea30a47a4c08570f97d7be2a", + "src/warden/case_inputs.py": "sha256:1334b0cd9897e763cfea2d306e997c659f3f284ecc3f9612140c2104835da7c7", + "src/warden/audit.py": "sha256:6b5b8fe053064d3905294bfde1b691fcca8a0806c889e52a49494470d29c24a0", + "src/warden/case_type.py": "sha256:7b659b8d682863cd28df24025be4b10dd7ca3df724350a22d360c6af9615bca7", + "src/warden/vault.py": "sha256:860b8c9f498b3955b363a74dd74087b6d0c399599ef22b951a076ee33c95929f", + "src/warden/taint.py": "sha256:8c3d161d18d429d026aad715ac01e2d33e0f4e84ac03fbaea3cea15017ad39a1", + "src/warden/case_run.py": "sha256:1bc73e30ac718fd75bbaff2a739495fc051aeaec73802ac71f0583cb2d53ab0a", + "src/warden/vault_hints.py": "sha256:0d158ffcc3a93d5442df5fbe13d4c1e3b3159dd60301530bbdae6db9bc9c709e", + "src/warden/access.py": "sha256:258d45d2f84b54b2464373ad4d7d6df6ff95b8e8d60f5e85ceb2d962fcfa8a61", + "src/warden/clock_attribution.py": "sha256:5fcf07a2525a6a250442e14b6f6229f7db2a6ca02fc6aac23cdaa4c1a78c63eb", + "src/warden/case_plan.py": "sha256:9352ae8683d1181980c4ff38524582e4be448d33c85b558f60582c1e5bb5bbaa", + "src/warden/caller_identity.py": "sha256:747edfaceecd470981949253bda0c3bd2f487d40a9a2f77c2cc1eff3f34f04f7", + "src/warden/policy.py": "sha256:aac46f538e986731cd841581c91efe004b8c1940966b954ef635780a719dada4", + "src/warden/case_receipt_state.py": "sha256:a2cda7f187e77bd76d4d209505638de44a587fae0bc033ec75c23ee9ea9f73bc", + "src/warden/config.py": "sha256:8a857faa3811b00303d4d5c904d69255ebdaff3239e8516f97e6606e53b46924", + "src/warden/openbao_caller.py": "sha256:7e6d07827d00eae7de3b2367c97bdcda1a13d6559627201a4ba09a0e42d46a17", + "src/warden/doubles.py": "sha256:5460e07f5bd0f51a81d3f3c0ac84efde65ca9130b1039f94ee22ca484976a9de", + "src/warden/posture.py": "sha256:1bb60c4dd4634ea42cb130ae1325d7415adf43096906bde2515396c368256e33", + "src/warden/cli.py": "sha256:196cd829d3dbfbd2333598a983687bde1f9bcdfadef95b2fd1d237c4a5f060a9", + "src/warden/proxy.py": "sha256:69649bcf8979e4ed7f9104e927b458827a355c4846143367f4cd49c8b89b96da", + "src/warden/plan.py": "sha256:001b3197e644221344d52e6b1594eb8eae78d10e73d18ed31273505909f3916b", + "src/warden/worker.py": "sha256:d7c2b391b36033d44d8c6841b9bce18a05c74b41ef2910b91689820b6a1cb44e", + "src/warden/mask.py": "sha256:0876412d453fa84109dc8ee0468449f40cf4d3421b056f30fc205f0f59ec494b", + "src/warden/models.py": "sha256:f4100b02c5d96d537980e00d4b874159cd5822ba8219f1f533a2516912b7bd85", + "src/warden/__init__.py": "sha256:047e0feba479474235f0707a207655ecfddfb33cd4f54069134419b8ca4ea24a", + "src/warden/scorecard.py": "sha256:0fa5aef34aeb004fa7fcfabc287759cc47421ca5244c1f31fc467b22a653353c", + "src/warden/desk.py": "sha256:2499120392a5a1c0fd8f423d5b5c787c75a4efb9a986a1b7bcb85eae13423e85", + "src/warden/routing/scaffold.py": "sha256:5079c8e51f8d487eae3f322a66324bd70cefb637bb28f46c861032de8ec54aa2", + "src/warden/routing/models.py": "sha256:b85372b2572a49af62e9534f26a33963b894633c7cd43dd2a28416b8959cba3e", + "src/warden/routing/__init__.py": "sha256:e9124811ebe11ad32c98ab462b2eed634d4ddca08792981d0f353b223a239902", + "src/warden/routing/catalog.py": "sha256:5b95810df9e6102a33d3a116a096cd75292c8572bad721fe6baa772caacc9bc3", + "src/warden/scripts/ops_ssh_wrapper.py": "sha256:e8c289882f2ad439d2eb32f9a871967420fefaffe745c9271c1a2e948fbdfe50", + "src/warden/scripts/__init__.py": "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + } + }, + "repo_manager_identifiers_sha256": "sha256:4e35fcd009c20822574fd33171176bd14a1ff2dd5af8b0bb2820954974593a2a", + "authorization": "Root accepted the exact source fixture patch for source integration. This packet adds no owner/native authority or catalog/pin promotion. No prepared script has been executed.", + "publishable": false, + "requires_allocated_manifest": true, + "publication_file_paths": [ + "sso-mfa/k8s/keycape/test_admit_1_3_type.py", + "sso-mfa/k8s/keycape/test_keycape_registration_adapter.py", + "sso-mfa/k8s/keycape/kernel-catalog.fixture.yaml", + "workplans/NK-WP-0048-security-case-types.md" + ] +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-current-source-publication-r3/run_validation.py b/docs/changes/netkingdom-target-2026-10-17/nk48-current-source-publication-r3/run_validation.py new file mode 100644 index 0000000..12f783d --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-current-source-publication-r3/run_validation.py @@ -0,0 +1,73 @@ +"""Prepared canonical source validation; --execute launches only the two scoped tests.""" +from pathlib import Path +import argparse +import hashlib +import json +import re +import shutil +import subprocess +import tempfile + +PACKET = Path(__file__).resolve().parent +ROOTS = {'net-kingdom': Path('/home/worsch/net-kingdom'), 'ops-warden': Path('/home/worsch/ops-warden')} + +def sha(data): + return 'sha256:' + hashlib.sha256(data).hexdigest() + +def sources(manifest): + allowed = [] + for owner, rows in manifest['validation']['source_hashes'].items(): + for relative, expected in rows.items(): + path = ROOTS[owner] / relative + assert not path.is_symlink(), str(path) + assert sha(path.read_bytes()) == expected, str(path) + allowed.append(str(path.resolve())) + return allowed + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--execute', action='store_true') + args = parser.parse_args() + manifest = json.loads((PACKET / 'publication-inputs.json').read_text()) + allowed = sources(manifest) + revision = subprocess.check_output(['git', '-C', str(ROOTS['ops-warden']), 'rev-parse', 'HEAD'], text=True).strip() + assert revision == manifest['warden_revision'] + if not args.execute: + print(json.dumps({'prepared_only': True, 'public_source_files_verified': len(allowed), + 'tests': manifest['validation']['tests']})) + return + protected = {name: sha((ROOTS['net-kingdom'] / name).read_bytes()) for name in manifest['protected_files']} + scratch = Path(tempfile.mkdtemp(prefix='nk48-catalog-canonical-validation-')) + for name in ['home', 'tmp', 'guard', 'pycache']: + (scratch / name).mkdir() + shutil.copyfile(PACKET / 'validation-guard.py', scratch / 'guard/sitecustomize.py') + shutil.copyfile(PACKET / 'validation-child.py', scratch / 'child.py') + (scratch / 'allowed.json').write_text(json.dumps(allowed)) + (scratch / 'pytest.ini').write_text('[pytest]\n') + environment = { + 'HOME': str(scratch / 'home'), 'TMPDIR': str(scratch / 'tmp'), 'PATH': '/usr/bin:/bin', + 'PYTHONPATH': str(scratch / 'guard') + ':' + str(ROOTS['ops-warden'] / 'src'), + 'PYTHONDONTWRITEBYTECODE': '1', 'PYTHONPYCACHEPREFIX': str(scratch / 'pycache'), + 'LANG': 'C.UTF-8', 'WARDEN_REVIEW_ALLOWED_SOURCE_LIST': str(scratch / 'allowed.json'), + 'WARDEN_REVIEW_VALIDATOR_PREFIX': str(scratch / 'tmp/warden-case-validator-'), + } + with (PACKET / 'canonical-validation.log').open('w') as log: + result = subprocess.run([manifest['validation']['python'], str(scratch / 'child.py')], + cwd=ROOTS['net-kingdom'], env=environment, + stdout=log, stderr=subprocess.STDOUT) + sources(manifest) + assert protected == {name: sha((ROOTS['net-kingdom'] / name).read_bytes()) for name in manifest['protected_files']} + log_text = (PACKET / 'canonical-validation.log').read_text() + counts_verified = bool(re.search(r'\b31 passed, 53 subtests passed\b', log_text)) + counts_verified = counts_verified and not re.search(r'\b[1-9][0-9]* (?:skipped|deselected)\b', log_text) + receipt = {'executed': True, 'counts_verified': counts_verified, 'returncode': result.returncode, 'warden_revision': revision, + 'net_kingdom_head': subprocess.check_output(['git', '-C', str(ROOTS['net-kingdom']), 'rev-parse', 'HEAD'], text=True).strip(), + 'verified_public_source_files': len(allowed), 'tests': manifest['validation']['tests'], + 'log_sha256': sha((PACKET / 'canonical-validation.log').read_bytes()), 'scratch': str(scratch), + 'limitations': 'Source fixture tests only; no native owner execution, acceptance, catalog promotion or pin.'} + (PACKET / 'canonical-validation.json').write_text(json.dumps(receipt, indent=2) + '\n') + print(json.dumps(receipt, indent=2)) + raise SystemExit(result.returncode or (0 if counts_verified else 1)) + +if __name__ == '__main__': + main() diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-current-source-publication-r3/validation-child.py b/docs/changes/netkingdom-target-2026-10-17/nk48-current-source-publication-r3/validation-child.py new file mode 100644 index 0000000..7e659ae --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-current-source-publication-r3/validation-child.py @@ -0,0 +1,43 @@ +"""Focused canonical-source test process. Network/native/estate-read guard required.""" +from pathlib import Path +import os +import socket +import subprocess +import sys +import sitecustomize + +assert sitecustomize.ACTIVE is True +assert not {'BAO_TOKEN', 'VAULT_TOKEN', 'WARDEN_AGENT_ID'} & os.environ.keys() +assert list(Path.home().iterdir()) == [] +try: + socket.socket() +except RuntimeError as error: + assert str(error) == 'fixture validation: network disabled' +else: + raise AssertionError('network guard inactive') + +import warden.case_inputs as inputs +import pytest + +assert Path(inputs.__file__).resolve() == Path('/home/worsch/ops-warden/src/warden/case_inputs.py') +PREAMBLE = '''import sys as _review_sys\ndef _review_guard(event,args):\n if event.startswith('socket.') or event in {'subprocess.Popen','os.system','os.exec','os.posix_spawn'}:\n raise RuntimeError('review validator: network/native disabled')\n if event=='open' and isinstance(args[0],str) and (args[0].startswith('/home/') or args[0].startswith('/root/')):\n raise RuntimeError('review validator: ambient home denied')\n_review_sys.addaudithook(_review_guard)\n''' +old_run = subprocess.run + +def run_pinned(arguments, *positional, **keywords): + if (type(arguments) is not list or len(arguments) != 5 + or arguments[:3] != [sys.executable, '-I', '-c'] + or arguments[3] != inputs._VALIDATE + or not str(arguments[4]).startswith(sitecustomize.VALIDATOR_PREFIX) + or keywords.get('env') != {}): + raise RuntimeError('fixture validation: unexpected process refused') + return old_run([*arguments[:3], PREAMBLE + arguments[3], arguments[4]], + *positional, **keywords) + +inputs.subprocess.run = run_pinned +raise SystemExit(pytest.main([ + '-q', '-p', 'no:cacheprovider', '--noconftest', + '-c', str(Path(os.environ['TMPDIR']).parent / 'pytest.ini'), + '--rootdir=/home/worsch/net-kingdom', + '/home/worsch/net-kingdom/sso-mfa/k8s/keycape/test_admit_1_3_type.py', + '/home/worsch/net-kingdom/sso-mfa/k8s/keycape/test_keycape_registration_adapter.py', +])) diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-current-source-publication-r3/validation-guard.py b/docs/changes/netkingdom-target-2026-10-17/nk48-current-source-publication-r3/validation-guard.py new file mode 100644 index 0000000..05f7294 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-current-source-publication-r3/validation-guard.py @@ -0,0 +1,35 @@ +"""Copied as sitecustomize.py into a fresh scratch directory by run_validation.""" +import json +import os +from pathlib import Path +import sys + +ALLOWED = frozenset(json.loads(Path(os.environ['WARDEN_REVIEW_ALLOWED_SOURCE_LIST']).read_text())) +VALIDATOR_PREFIX = os.environ['WARDEN_REVIEW_VALIDATOR_PREFIX'] + +def guard(event, args): + if event.startswith('socket.'): + raise RuntimeError('fixture validation: network disabled') + if event == 'subprocess.Popen': + command = args[1] + if (type(command) is list and len(command) == 5 + and command[:3] == [sys.executable, '-I', '-c'] + and command[3].startswith('import sys as _review_sys\ndef _review_guard(') + and str(command[4]).startswith(VALIDATOR_PREFIX) and args[3] == {}): + return + raise RuntimeError('fixture validation: native process disabled') + if event in {'os.system', 'os.exec', 'os.posix_spawn'}: + raise RuntimeError('fixture validation: native process disabled') + if event == 'open' and isinstance(args[0], str): + path = str(Path(args[0]).absolute()) + if path.startswith(('/home/', '/root/')): + # Only exact prehashed public source may be read from the estate. + if str(Path(path).resolve()) not in ALLOWED: + raise PermissionError('fixture validation: unlisted estate file disabled') + mode, flags = args[1:3] + if ((isinstance(mode, str) and any(c in mode for c in 'wax+')) + or (flags & (os.O_WRONLY | os.O_RDWR | os.O_CREAT | os.O_TRUNC | os.O_APPEND))): + raise PermissionError('fixture validation: canonical source writes disabled') + +sys.addaudithook(guard) +ACTIVE = True diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-current-source-publication-r3/validation-inputs-refresh.json b/docs/changes/netkingdom-target-2026-10-17/nk48-current-source-publication-r3/validation-inputs-refresh.json new file mode 100644 index 0000000..79f1714 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-current-source-publication-r3/validation-inputs-refresh.json @@ -0,0 +1,12 @@ +{ + "previous": "docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2", + "only_source_pin_deltas": [ + { + "repo": "net-kingdom", + "path": "security-case-type-revisions/1.3/oidc-browser-relying-party/type.yaml", + "before": "sha256:e75fe94110ef9fb7fb18072fe2856941b33507c99cb406d36576046481485d0a", + "after": "sha256:ef02ddae3167bdd1558193a5f6db0379fb8cc84207531706d5ef35caabc3684f" + } + ], + "reason": "Independently reviewed exact issuer-template entry text integrated; descriptors, parameters and artifact unchanged." +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-current-source-publication-r3/verified-task-bindings.json b/docs/changes/netkingdom-target-2026-10-17/nk48-current-source-publication-r3/verified-task-bindings.json new file mode 100644 index 0000000..48c7229 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-current-source-publication-r3/verified-task-bindings.json @@ -0,0 +1,30 @@ +{ + "status": "verified", + "commit": "befb0bc68cd25b625804fce559476eefd702521d", + "source_and_primary_task_count": 35, + "original31bindings_unchanged": true, + "new_records": [ + { + "record_id": "NK-WP-0048-T32", + "status": "done", + "uuid": "c6d0a27e-f6a7-5df0-95f6-3e7473d38bb5" + }, + { + "record_id": "NK-WP-0048-T33", + "status": "done", + "uuid": "5ace28ff-a4cb-5647-a8f6-3cd54bb9d0a5" + }, + { + "record_id": "NK-WP-0048-T34", + "status": "progress", + "uuid": "37f43c40-d014-544e-aa0b-5219dd4a6b93" + }, + { + "record_id": "NK-WP-0048-T35", + "status": "done", + "uuid": "cf1d6ab3-846e-53ea-8575-d6347b6f4769" + } + ], + "native_gates_closed": 0, + "source_corrections_only": true +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-issuer-reference-integration-r1/apply.py b/docs/changes/netkingdom-target-2026-10-17/nk48-issuer-reference-integration-r1/apply.py new file mode 100644 index 0000000..28e22f0 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-issuer-reference-integration-r1/apply.py @@ -0,0 +1,57 @@ +from pathlib import Path +import hashlib,json,subprocess,copy,yaml,re,importlib.util,sys +p=Path(__file__).resolve().parent;r=Path('/home/worsch/net-kingdom');base='342a6f25af5cdc602841948f42759ea52ef1c8f5' +def git(root,*a):return subprocess.check_output(['git','-C',str(root),*a],text=True).strip() +assert git(r,'rev-parse','HEAD')==base +assert git(Path('/home/worsch/railiance-platform'),'rev-parse','HEAD')=='104ed90f42a9a86bf9fda343b49b65a85c9ee4b9' +template=Path('/home/worsch/railiance-platform/manifests/templates/keycape-issuer-eso.yaml') +assert hashlib.sha256(template.read_bytes()).hexdigest()=='e17498bd8a4c820041a4105e4efe9c60c6a50953db8f621ff54887080065c743' +name='security-case-type-revisions/1.3/oidc-browser-relying-party/type.yaml';old=(r/name).read_bytes();old_doc=yaml.safe_load(old) +assert hashlib.sha256(old).hexdigest()=='e75fe94110ef9fb7fb18072fe2856941b33507c99cb406d36576046481485d0a' +(p/'type.preimage.yaml').write_bytes(old) +patch=p.parent/'nk48-r5-f2-publication-r1/netkingdom-entry-point.conditional.patch' +assert hashlib.sha256(patch.read_bytes()).hexdigest()=='6784a21fbd10bd62712be2e0f4929034601c94629aa74ccaa2e93fc815eecd61' +subprocess.run(['git','apply','--check',str(patch.resolve())],cwd=r,check=True) +subprocess.run(['git','apply',str(patch.resolve())],cwd=r,check=True) +new=(r/name).read_bytes();new_doc=yaml.safe_load(new) +a=copy.deepcopy(old_doc);b=copy.deepcopy(new_doc) +for doc in [a,b]: + step=next(x for x in doc['spec']['follow_through'] if x['id']=='deliver-secret');step['entry_point']='same-entry-removed-for-comparison' +assert a==b,'only entry_point may change' +assert old_doc['spec']['parameters']==new_doc['spec']['parameters'] +assert old_doc['spec']['execution_descriptors']==new_doc['spec']['execution_descriptors'] +assert new_doc.get('metadata',{}).get('status')==old_doc.get('metadata',{}).get('status') +# Compose by importing the read-only seven-adapter composer, never dispatch. +sys.path.insert(0,'/home/worsch/ops-warden/src');sys.dont_write_bytecode=True +module=r/'sso-mfa/k8s/keycape/compose-1.2-descriptors.py' +spec=importlib.util.spec_from_file_location('nk48_composer',module);c=importlib.util.module_from_spec(spec);spec.loader.exec_module(c) +artifact=r/'sso-mfa/k8s/keycape/descriptor-composition-1.3.candidate.json';before=artifact.read_bytes();doc=json.loads(before) +regenerated=(json.dumps(c.artifact(doc['check_config_digest'],doc['params'],'1.3'),indent=2,sort_keys=True)+'\n').encode() +assert regenerated==before,'Unexpected descriptor/artifact delta; inspect before writing' +(p/'composition-regenerated.json').write_bytes(regenerated) +wp=r/'workplans/NK-WP-0048-security-case-types.md';text=wp.read_text();(p/'workplan.preimage.md').write_text(text) +for task,status in [('NK-WP-0048-T33','done'),('NK-WP-0048-T34','progress')]: + match=re.search(r'```task\nid: '+task+r'\n.*?```',text,re.S);assert match + before_block=match.group();assert 'status: todo\n' in before_block + text=text[:match.start()]+before_block.replace('status: todo\n','status: '+status+'\n')+text[match.end():] +needle='## T34 — Close the consumer configuration inverse finding' +completion="""2026-10-17 completed exact proposed-source reference correction: Platform +published template, metadata helper and eight focused tests in +`104ed90f42a9a86bf9fda343b49b65a85c9ee4b9`; independent source review +`nk48-r5-f2-review-r1` found no blocking source-fit defect. The proposed 1.3 +entry now names ClusterSecretStore `openbao-keycape-{client_id}-issuer`, cluster +scope restricted to `sso`, and the exact Platform template digest. Only that +entry text changes; parameters and all seven descriptors are unchanged, and +regeneration produces the identical not-an-admission composition artifact. +This closes the coordinator's vague-reference source finding only. It supplies +no formal owner assent, existing live store, installed ESO compatibility, +real delivery/removal/session capability, grant or native acceptance; existing +RPF-1.3-T4 and T16 keep those obligations. F3 is independent and remains open. +Evidence: Custodian `nk48-r5-f2-publication-r1/` and +`nk48-issuer-reference-integration-r1/` under the existing 17 October packet. + +""" +assert text.count(needle)==1;text=text.replace('and `nk48-catalog-fixture-publication-r1/`. Canonical validation/publication\nreceipts belong in the latter packet when performed.','and `nk48-catalog-fixture-publication-r2/`. Root canonical validation passes\n31 tests / 53 subtests against Warden `922c26d` (the same reviewed code plus\none normal allocator-only source UUID commit). Publication receipts belong\nin that packet.') +wp.write_text(text) +(p/'verification.json').write_text(json.dumps({'status':'integrated-proposed-source-only','only_type_delta':'deliver-secret.entry_point','type_before_sha256':hashlib.sha256(old).hexdigest(),'type_after_sha256':hashlib.sha256(new).hexdigest(),'parameters_unchanged':True,'seven_descriptors_unchanged':True,'composition_byte_identical':True,'T33':'done: exact source reference only','T34':'progress: real owner backend absent','owner_assent':False,'native_action':False},indent=2)+'\n') +print('Applied exact issuer reference, verified unchanged descriptors/parameters/artifact; recorded bounded T33 completion and T34 progress.') diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-issuer-reference-integration-r1/composition-regenerated.json b/docs/changes/netkingdom-target-2026-10-17/nk48-issuer-reference-integration-r1/composition-regenerated.json new file mode 100644 index 0000000..d18b649 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-issuer-reference-integration-r1/composition-regenerated.json @@ -0,0 +1,254 @@ +{ + "admitted": false, + "canonical_default": { + "type": "oidc-browser-relying-party@1.1", + "type_yaml_digest": "sha256:f705c64e4875e9b8e18a912fb51c135e77898926d6a451f5cb74ab3c0cbe4d1c", + "unchanged": true + }, + "check_config_digest": "sha256:be37738fe1c08259fb4f9ca0daf0f9301bfffc237e5bb38435525dfbf11ccff5", + "complete_executable_descriptor_set": true, + "dispatch_permitted": false, + "execution_descriptors": { + "bind-env": "sha256:d5fcb20cf15bf7454890d2529180c582218e3df12584a99162e4b664b899caa0", + "deliver-consumer-secret": "sha256:36281df8139fb1ec64b226d94f80eb592de5addab7063ff54c691c641e275fc7", + "deliver-secret": "sha256:75aaa7346aebdea2a0eb0ba993995515b5248ffefc34caf8e713525e9b58da85", + "ensure-groups": "sha256:ebc9d5b9167d18e6015f4bbeb9699e8d396d64fbfaf195142781f84f4f47a70a", + "hand-over": "sha256:1bce07a3229b7d0c7c730f35ce5b40fe471009a82de8dd53788ed970a9be1224", + "mint-secret": "sha256:c92957227235db04953809d92b260d32c8b947472a8e8fd17a258ade3ae8da70", + "register-client": "sha256:4ecc769f75433b66c7161f13c0fe7e5599633cf466d46cb6d8031f0456f426e7" + }, + "owner_review": false, + "params": { + "consumer_owner_repo": "railiance-apps" + }, + "refusal": null, + "review_requirement": "successor candidate only: new descriptor bytes need a reviewed successor revision under canon section 6 and owner review (NK-WP-0048-T16) before pinning", + "schema": "netkingdom.descriptor-composition-candidate.v1", + "selection": "explicit version root 1.3=security-case-type-revisions/1.3; default root unchanged", + "status": "not_an_admission", + "steps": [ + { + "candidate_descriptor_digest": "sha256:ebc9d5b9167d18e6015f4bbeb9699e8d396d64fbfaf195142781f84f4f47a70a", + "descriptor": { + "actor": "human", + "adapter": "net-kingdom/ensure-groups@v1", + "adapter_source": "sha256:f93e68e1932c7ca293c9e69e2d0e1843d58d06a8d4892158fe7d1e0393a21ba0", + "allowed_created": [ + "service-group", + "service-group-membership" + ], + "effect": "change", + "lane": "keycape-attended-case-change", + "owner": "net-kingdom", + "retry_safe": false, + "revocation_scopes": [ + "parent-session" + ], + "step": "ensure-groups", + "target_parameter": "client_id", + "version": "warden.case.execution.v1" + }, + "findings": [], + "names_confirmed": null, + "owner_confirmation_required": null, + "step": "ensure-groups", + "type_lane": "keycape-attended-case-change", + "type_owner": "net-kingdom" + }, + { + "candidate_descriptor_digest": "sha256:c92957227235db04953809d92b260d32c8b947472a8e8fd17a258ade3ae8da70", + "descriptor": { + "actor": "human", + "adapter": "net-kingdom/keycape-mint-secret@v1", + "adapter_source": "sha256:34f79944204a2cf5b2a22baecbee4bde77cbdc0e3f9fc87bc98fa12a9c2c0bc1", + "allowed_created": [ + "custody-secret-version" + ], + "effect": "mint", + "lane": "keycape-client-secret-generate", + "owner": "railiance-platform", + "retry_safe": false, + "revocation_scopes": [ + "parent-session", + "broker-grant" + ], + "step": "mint-secret", + "target_parameter": "client_id", + "version": "warden.case.execution.v1" + }, + "findings": [], + "names_confirmed": { + "by": "founder", + "on": "2026-10-17", + "schema": "railiance-platform.custody-mint.v1", + "task": "NK-WP-0048-T25" + }, + "owner_confirmation_required": null, + "step": "mint-secret", + "type_lane": "keycape-client-secret-generate", + "type_owner": "railiance-platform" + }, + { + "candidate_descriptor_digest": "sha256:75aaa7346aebdea2a0eb0ba993995515b5248ffefc34caf8e713525e9b58da85", + "descriptor": { + "actor": "human", + "adapter": "net-kingdom/deliver-secret@v1", + "adapter_source": "sha256:df609bb93ebdfffb12cde213381a77eb7ac82670f814ff73623c1d98262cc90e", + "allowed_created": [ + "delivered-secret" + ], + "effect": "change", + "lane": "platform-secret-delivery", + "owner": "railiance-platform", + "retry_safe": false, + "revocation_scopes": [ + "parent-session" + ], + "step": "deliver-secret", + "target_parameter": "client_id", + "version": "warden.case.execution.v1" + }, + "findings": [], + "names_confirmed": { + "by": "founder", + "lane": "platform-secret-delivery", + "on": "2026-10-17", + "schema": "netkingdom.deliver-secret.v1", + "task": "NK-WP-0048-T26" + }, + "owner_confirmation_required": null, + "step": "deliver-secret", + "type_lane": "platform-secret-delivery", + "type_owner": "railiance-platform" + }, + { + "candidate_descriptor_digest": "sha256:d5fcb20cf15bf7454890d2529180c582218e3df12584a99162e4b664b899caa0", + "descriptor": { + "actor": "human", + "adapter": "net-kingdom/keycape-bind-env@v1", + "adapter_source": "sha256:dcd43f5ce1c4e829e1198faffc6bfb9cc610509f5176658f42c133accc0a7305", + "allowed_created": [ + "keycape-env-binding" + ], + "effect": "change", + "lane": "keycape-attended-case-change", + "owner": "net-kingdom", + "retry_safe": false, + "revocation_scopes": [ + "parent-session" + ], + "step": "bind-env", + "target_parameter": "client_id", + "version": "warden.case.execution.v1" + }, + "findings": [], + "names_confirmed": { + "by": "founder", + "on": "2026-10-17", + "schema": "netkingdom.keycape-bind-env.attempt.v1", + "task": "NK-WP-0048-T27" + }, + "owner_confirmation_required": null, + "step": "bind-env", + "type_lane": "keycape-attended-case-change", + "type_owner": "net-kingdom" + }, + { + "candidate_descriptor_digest": "sha256:4ecc769f75433b66c7161f13c0fe7e5599633cf466d46cb6d8031f0456f426e7", + "descriptor": { + "actor": "human", + "adapter": "net-kingdom/keycape-registration@v1", + "adapter_source": "sha256:b30f7abfbb5bf439c5ce067325b01c8b711076a38a6fb8484f55d5c8912921f3", + "allowed_created": [ + "keycape-client" + ], + "effect": "change", + "lane": "keycape-attended-case-change", + "owner": "net-kingdom", + "retry_safe": false, + "revocation_scopes": [ + "parent-session" + ], + "step": "register-client", + "target_parameter": "client_id", + "version": "warden.case.execution.v1" + }, + "findings": [], + "names_confirmed": null, + "owner_confirmation_required": null, + "step": "register-client", + "type_lane": "keycape-attended-case-change", + "type_owner": "net-kingdom" + }, + { + "candidate_descriptor_digest": "sha256:36281df8139fb1ec64b226d94f80eb592de5addab7063ff54c691c641e275fc7", + "descriptor": { + "actor": "human", + "adapter": "net-kingdom/deliver-consumer-secret@v1", + "adapter_source": "sha256:66f16dcb665216d09ef63a5ca654283a0292005ba4fb4d79c27eeb54b886a385", + "allowed_created": [ + "consumer-eso-sa", + "consumer-secretstore", + "consumer-externalsecret" + ], + "effect": "change", + "lane": "consumer-owner-deployment", + "owner": "railiance-apps", + "retry_safe": false, + "revocation_scopes": [ + "parent-session" + ], + "step": "deliver-consumer-secret", + "target_parameter": "client_id", + "version": "warden.case.execution.v1" + }, + "findings": [], + "names_confirmed": { + "by": "founder", + "lane": "consumer-owner-deployment", + "on": "2026-10-17", + "schema": "netkingdom.consumer-secret-delivery.v1", + "task": "NK-WP-0048-T28" + }, + "owner_confirmation_required": null, + "step": "deliver-consumer-secret", + "type_lane": "consumer-owner-deployment", + "type_owner": "{consumer_owner_repo}" + }, + { + "candidate_descriptor_digest": "sha256:1bce07a3229b7d0c7c730f35ce5b40fe471009a82de8dd53788ed970a9be1224", + "descriptor": { + "actor": "human", + "adapter": "net-kingdom/apps-handover@v1", + "adapter_source": "sha256:4439305b3edcd5d51c73e6438744d8895d69c1ccb58854aac79bb489d868cf48", + "allowed_created": [ + "handover-receipt" + ], + "effect": "change", + "lane": "apps-consumer-handover", + "owner": "railiance-apps", + "retry_safe": false, + "revocation_scopes": [ + "parent-session" + ], + "step": "hand-over", + "target_parameter": "client_id", + "version": "warden.case.execution.v1" + }, + "findings": [], + "names_confirmed": { + "by": "founder", + "lane": "apps-consumer-handover", + "on": "2026-10-17", + "schema": "netkingdom.apps-handover.attempt.v1", + "task": "NK-WP-0048-T29" + }, + "owner_confirmation_required": null, + "step": "hand-over", + "type_lane": "apps-consumer-handover", + "type_owner": "{consumer_owner_repo}" + } + ], + "task": "NK-WP-0048-T30", + "type": "oidc-browser-relying-party@1.3" +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-issuer-reference-integration-r1/initial-syntax-failure.py.txt b/docs/changes/netkingdom-target-2026-10-17/nk48-issuer-reference-integration-r1/initial-syntax-failure.py.txt new file mode 100644 index 0000000..3fb32af --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-issuer-reference-integration-r1/initial-syntax-failure.py.txt @@ -0,0 +1,79 @@ +from pathlib import Path +import hashlib,json,subprocess,copy,yaml,re,importlib.util,sys +p=Path(__file__).resolve().parent;r=Path('/home/worsch/net-kingdom');base='342a6f25af5cdc602841948f42759ea52ef1c8f5' +def git(root,*a):return subprocess.check_output(['git','-C',str(root),*a],text=True).strip() +assert git(r,'rev-parse','HEAD')==base +assert git(Path('/home/worsch/railiance-platform'),'rev-parse','HEAD')=='104ed90f42a9a86bf9fda343b49b65a85c9ee4b9' +template=Path('/home/worsch/railiance-platform/manifests/templates/keycape-issuer-eso.yaml') +assert hashlib.sha256(template.read_bytes()).hexdigest()=='e17498bd8a4c820041a4105e4efe9c60c6a50953db8f621ff54887080065c743' +name='security-case-type-revisions/1.3/oidc-browser-relying-party/type.yaml';old=(r/name).read_bytes();old_doc=yaml.safe_load(old) +assert hashlib.sha256(old).hexdigest()=='e75fe94110ef9fb7fb18072fe2856941b33507c99cb406d36576046481485d0a' +(p/'type.preimage.yaml').write_bytes(old) +patch=p.parent/'nk48-r5-f2-publication-r1/netkingdom-entry-point.conditional.patch' +assert hashlib.sha256(patch.read_bytes()).hexdigest()=='6784a21fbd10bd62712be2e0f4929034601c94629aa74ccaa2e93fc815eecd61' +subprocess.run(['git','apply','--check',str(patch.resolve())],cwd=r,check=True) +subprocess.run(['git','apply',str(patch.resolve())],cwd=r,check=True) +new=(r/name).read_bytes();new_doc=yaml.safe_load(new) +a=copy.deepcopy(old_doc);b=copy.deepcopy(new_doc) +for doc in [a,b]: + step=next(x for x in doc['spec']['follow_through'] if x['id']=='deliver-secret');step['entry_point']='same-entry-removed-for-comparison' +assert a==b,'only entry_point may change' +assert old_doc['spec']['parameters']==new_doc['spec']['parameters'] +assert old_doc['spec']['execution_descriptors']==new_doc['spec']['execution_descriptors'] +assert new_doc.get('metadata',{}).get('status')==old_doc.get('metadata',{}).get('status') +# Compose by importing the read-only seven-adapter composer, never dispatch. +sys.path.insert(0,'/home/worsch/ops-warden/src');sys.dont_write_bytecode=True +module=r/'sso-mfa/k8s/keycape/compose-1.2-descriptors.py' +spec=importlib.util.spec_from_file_location('nk48_composer',module);c=importlib.util.module_from_spec(spec);spec.loader.exec_module(c) +artifact=r/'sso-mfa/k8s/keycape/descriptor-composition-1.3.candidate.json';before=artifact.read_bytes();doc=json.loads(before) +regenerated=(json.dumps(c.artifact(doc['check_config_digest'],doc['params'],'1.3'),indent=2,sort_keys=True)+'\n').encode() +assert regenerated==before,'Unexpected descriptor/artifact delta; inspect before writing' +(p/'composition-regenerated.json').write_bytes(regenerated) +wp=r/'workplans/NK-WP-0048-security-case-types.md';text=wp.read_text();(p/'workplan.preimage.md').write_text(text) +for task,status in [('NK-WP-0048-T33','done'),('NK-WP-0048-T34','progress')]: + match=re.search(r'```task\nid: '+task+r'\n.*?```',text,re.S);assert match + before_block=match.group();assert 'status: todo\n' in before_block + text=text[:match.start()]+before_block.replace('status: todo\n','status: '+status+'\n')+text[match.end():] +needle='## T34 — Close the consumer configuration inverse finding' +completion="""2026-10-17 completed exact proposed-source reference correction: Platform +published template, metadata helper and eight focused tests in +`104ed90f42a9a86bf9fda343b49b65a85c9ee4b9`; independent source review +`nk48-r5-f2-review-r1` found no blocking source-fit defect. The proposed 1.3 +entry now names ClusterSecretStore `openbao-keycape-{client_id}-issuer`, cluster +scope restricted to `sso`, and the exact Platform template digest. Only that +entry text changes; parameters and all seven descriptors are unchanged, and +regeneration produces the identical not-an-admission composition artifact. +This closes the coordinator's vague-reference source finding only. It supplies +no formal owner assent, existing live store, installed ESO compatibility, +real delivery/removal/session capability, grant or native acceptance; existing +RPF-1.3-T4 and T16 keep those obligations. F3 is independent and remains open. +Evidence: Custodian `nk48-r5-f2-publication-r1/` and +`nk48-issuer-reference-integration-r1/` under the existing 17 October packet. + +""" +assert text.count(needle)==1;text=text.replace(needle,completion+needle) +needle='## T35 — Align source admission fixtures with the authenticated catalog and attended-session API' +note="""2026-10-17 implementation progress: the fresh independent Apps review +reproduced a missing fresh inverse-session boundary. Three new regressions fail +the original wrapper. Corrected r2 requires a separately authenticated inverse +session before the release-writer lock and revokes it on every exit; 19 tests / +33 subtests pass, and removing the session guard is detected. This remains a +source candidate: direct Apps source inspection found no real owner transaction, +durable journal, all-release-writer exclusion or inverse-session backend to bind. +The four exact missing owner facilities and smallest existing T05 implementation +are retained in Custodian `nk48-r5-f3-implementation-r2/owner-backend-next.md`. +T34 progresses for this correction and investigation; it does not claim the real +backend, accepted type placement, owner assent or F3 closure. The unsafe original +candidate/review remain frozen. Do not integrate its type prose as an implemented +capability or reuse the revoked hand-over session. + +""" +assert text.count(needle)==1;text=text.replace(needle,note+needle) +text=text.replace('and `nk48-catalog-fixture-publication-r1/`. Canonical validation/publication +receipts belong in the latter packet when performed.','and `nk48-catalog-fixture-publication-r2/`. Root canonical validation passes +31 tests / 53 subtests against Warden `922c26d` (the same reviewed code plus +one normal allocator-only source UUID commit). Publication receipts belong +in that packet.') +wp.write_text(text) +(p/'verification.json').write_text(json.dumps({'status':'integrated-proposed-source-only','only_type_delta':'deliver-secret.entry_point','type_before_sha256':hashlib.sha256(old).hexdigest(),'type_after_sha256':hashlib.sha256(new).hexdigest(),'parameters_unchanged':True,'seven_descriptors_unchanged':True,'composition_byte_identical':True,'T33':'done: exact source reference only','T34':'progress: real owner backend absent','owner_assent':False,'native_action':False},indent=2)+'\n') +print('Applied exact issuer reference, verified unchanged descriptors/parameters/artifact; recorded bounded T33 completion and T34 progress.') diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-issuer-reference-integration-r1/type.preimage.yaml b/docs/changes/netkingdom-target-2026-10-17/nk48-issuer-reference-integration-r1/type.preimage.yaml new file mode 100644 index 0000000..6ec7560 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-issuer-reference-integration-r1/type.preimage.yaml @@ -0,0 +1,495 @@ +apiVersion: netkingdom.io/security-case-type/v0.1 +kind: SecurityCaseType +spec: + id: oidc-browser-relying-party + version: '1.3' + status: proposed + title: Browser OIDC relying party for an internal service + purpose: 'Let an internal web service sign its human users in through NetKingdom + (KeyCape): an exact-redirect OIDC client, optional MFA pin, the LLDAP groups the + service maps to its own roles, and, for confidential clients, a client secret + minted straight into OpenBao custody. Replaces the per-client rollout scripts + in sso-mfa/k8s/keycape. First case: Forgejo (KEY-WP-0038).' + parameters: + - name: client_id + type: string + case_settable: true + constraints: + pattern: ^[a-z0-9][a-z0-9-]{1,62}$ + sensitivity: operational + description: OAuth2 client_id, unique in KeyCape. + - name: display_name + type: string + case_settable: true + constraints: + pattern: ^[^\n\r]{1,80}$ + sensitivity: public + description: Name shown on the sign-in page. + - name: redirect_uris + type: string + case_settable: true + constraints: + pattern: ^https://[^*?#\s,]+(,https://[^*?#\s,]+)*$ + sensitivity: security_sensitive + description: 'Comma-separated exact https redirect URIs. No wildcards, no plain + http, no query and no fragment: KeyCape refuses ''*'' and ''?'' at startup, + and OAuth 2.0 forbids fragments (key-cape type review R1).' + - name: allowed_scopes + type: string + case_settable: true + constraints: + enum: + - openid,profile,email,groups + - openid,email,groups + - openid,profile,groups + - openid,groups + sensitivity: security_sensitive + description: Scopes the client may request, openid first. Always includes groups, + because access_groups is required and KeyCape's UserInfo returns groups only + for that scope (key-cape type review R2). + - name: client_type + type: string + case_settable: true + constraints: + enum: + - confidential + - public + sensitivity: security_sensitive + description: confidential for server-side services that can hold a secret; public + for SPAs and native apps (PKCE only). + - name: mfa_required + type: string + case_settable: true + constraints: + enum: + - 'true' + - 'false' + sensitivity: security_sensitive + description: '"true" pins MFA on this client; required when the service grants + administrative or owner rights. "false" is not "no MFA": the client then carries + no mfaRequired field and KeyCape applies the per-user privacyIDEA policy. A + renderer must never emit an explicit mfaRequired false, which would skip that + policy (key-cape type review R3).' + - name: access_groups + type: string + case_settable: true + constraints: + pattern: ^[a-z][a-z0-9-]{1,62}(,[a-z][a-z0-9-]{1,62})*$ + sensitivity: operational + description: 'Comma-separated LLDAP groups that the service maps to its roles. + Created if absent. KeyCape has no per-client group gate: it issues tokens to + any authenticated user and only carries the groups claim, so the consuming service + enforces the gate itself (for Forgejo, its required-claim setting; key-cape + type review R2).' + - name: initial_members + type: string + required: false + case_settable: true + constraints: + pattern: ^[a-z][a-z0-9-]*=[a-z][a-z0-9._-]*(\+[a-z][a-z0-9._-]*)*(;[a-z][a-z0-9-]*=[a-z][a-z0-9._-]*(\+[a-z][a-z0-9._-]*)*)*$ + sensitivity: operational + description: Initial memberships as group=user+user;group=user. Usernames are + directory usernames, never secrets. + - name: consuming_service + type: string + case_settable: true + constraints: + pattern: ^[a-z0-9][a-z0-9-]{0,62}$ + sensitivity: operational + description: The service that signs users in with this client. + - name: consumer_owner_repo + type: string + case_settable: true + constraints: + pattern: ^[a-z0-9][a-z0-9-]*$ + sensitivity: operational + description: The repository that owns the consuming service's configuration. + - name: consumer_namespace + type: string + case_settable: true + constraints: + pattern: ^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$ + sensitivity: security_sensitive + description: Consumer namespace for namespaced ESO TokenRequest reader; actual + workload owner must confirm; never infer from service name. + - name: issuer + type: string + case_settable: false + default: https://kc.coulomb.social + sensitivity: security_sensitive + description: The NetKingdom issuer; fixed by the type. + composition: + authority: netkingdom + initial_trust: + - runtime_secret_trust + - runtime_identity_trust + capabilities: + - c1.lightweight-sso + providers: + c1.lightweight-sso: net-kingdom.keycape-client-registration + parameter_bindings: + net-kingdom.keycape-client-registration.client_id: client_id + net-kingdom.keycape-client-registration.display_name: display_name + net-kingdom.keycape-client-registration.redirect_uris: redirect_uris + net-kingdom.keycape-client-registration.allowed_scopes: allowed_scopes + net-kingdom.keycape-client-registration.client_type: client_type + net-kingdom.keycape-client-registration.mfa_required: mfa_required + materialization: + - id: client-secret-path + kind: kv_path + when: client_type == confidential + template: platform/workloads/keycape/{client_id} + body: field CLIENT_SECRET; minted by the credential broker, never written by hand + - id: keycape-reader-policy + kind: openbao_policy + when: client_type == confidential + template: workload-kv-read-keycape-{client_id}-eso + body: "path \"platform/data/workloads/keycape/{client_id}\" {{\n capabilities\ + \ = [\"read\"]\n}}\n" + description: KeyCape reads its client secret only through ESO. No metadata, list, + parent or sibling access. + - id: consumer-reader-policy + kind: openbao_policy + when: client_type == confidential + template: workload-kv-read-keycape-{client_id}-{consuming_service} + body: "path \"platform/data/workloads/keycape/{client_id}\" {{\n capabilities\ + \ = [\"read\"]\n}}\n" + description: The consuming service reads the same secret. No metadata, list, parent + or sibling access. + - id: eso-role + kind: openbao_role + when: client_type == confidential + template: external-secrets-keycape-{client_id} + body: 'auth: kubernetes + + bound_service_account_names: [external-secrets] + + bound_service_account_namespaces: [external-secrets] + + token_policies: [workload-kv-read-keycape-{client_id}-eso] + + token_no_default_policy: true + + token_ttl: 15m + + token_max_ttl: 15m + + ' + description: The Kubernetes-auth role deliver-secret uses; bound to the ESO service + account only. + - id: consumer-eso-role + kind: openbao_role + when: client_type == confidential + template: external-secrets-keycape-{client_id}-{consuming_service} + body: 'auth: kubernetes + + bound_service_account_names: [{consuming_service}-keycape-{client_id}-eso] + + bound_service_account_namespaces: [{consumer_namespace}] + + audience: openbao + + token_policies: [workload-kv-read-keycape-{client_id}-{consuming_service}] + + token_no_default_policy: true + + token_ttl: 15m + + token_max_ttl: 15m + + ' + description: Consumer ESO requests a token for this separate reference identity. + Common ESO controller remains trusted and can request tokens; no cryptographic + controller isolation is claimed. + - id: catalog-entry + kind: catalog_entry + template: '{consuming_service}-keycape-client' + body: 'id: {consuming_service}-keycape-client + + owner_repo: key-cape + + warden_executes: false + + risk: high + + status: active once verification passes + + workload_ref: applicable, name {consuming_service}, declaration_ref of the consumer''s + published declaration or the explicit unknown form, never inferred + + path_template: platform/workloads/keycape/{client_id} + + field: CLIENT_SECRET + + delegation: native, intended_owner key-cape + + rotation: mint-secret then verification; automatable false until a run has been + proven + + ' + description: One entry per case (ops-mason item 5, ops-warden R4'). It carries + the consumer's workload reference, which ADR-0009 needs for zone selection. + follow_through: + - id: ensure-groups + owner: net-kingdom + entry_point: reviewed adapter net-kingdom/keycape-ensure-groups, dispatched by Warden's + case kernel inside the attended session; it calls identity-provisioner + ensure_service_groups in the provisioner pod through the session's contained + cluster access (groups {access_groups}; members exactly {initial_members} as + recorded in the accepted case, nothing else) + auth: the attended human session on lane keycape-attended-case-change (actor human, + parent-session revoked per attempt); the in-pod call uses the provisioner's own + directory binding, and no provisioner or user-engine token is read or sent + lane: keycape-attended-case-change + warden_executes: false + attended: true + value_handling: none + attended_justification: The provisioner's only HTTP credential is user-engine's + runtime token, and its NetworkPolicy admits only user-engine pods. A proxy lane + would act as user-engine (ops-warden review R8; canon section 3; ops-warden + ADR-0002). Running it in the session register-client already needs adds no attended + session. It can become unattended once the provisioner has an operator credential + of its own and an admitted path. Warden's case kernel dispatches the reviewed adapter only inside the one attended human session (descriptor actor human, lane keycape-attended-case-change, parent-session revoked per attempt); the human session is the authority and the kernel is only the dispatcher. + - id: mint-secret + owner: railiance-platform + when: client_type == confidential + entry_point: 'railiance-platform credential broker: generate a client secret inside + custody at bao:platform/workloads/keycape/{client_id}, through reviewed adapter + railiance-platform/keycape-mint-secret' + auth: a human caller (net-kingdom-admins, aal2) starts it; Warden runs it unattended + through the broker lane under the caller's session, with the caller's broker grant + for the case's KV path; agents and root are refused, and the value is generated + in custody and never returned + lane: keycape-client-secret-generate + warden_executes: true + attended: false + value_handling: generated_in_custody + - id: deliver-secret + owner: railiance-platform + when: client_type == confidential + entry_point: reviewed adapter railiance-platform/keycape-deliver-secret, dispatched + by Warden's case kernel; Platform's deliver capability applies ExternalSecret + sso/keycape-{client_id}-client (key client-secret) through the ESO store for + role external-secrets-keycape-{client_id}, from bao:platform/workloads/keycape/{client_id} + auth: Platform's own delivery path on lane platform-secret-delivery, inside the human + aal2 session; ESO reads custody through role external-secrets-keycape-{client_id}; + Warden holds no Platform credential, and no value leaves custody + lane: platform-secret-delivery + warden_executes: false + attended: false + value_handling: none + ready_check: the delivery for sso/keycape-{client_id}-client reports Ready=True + (status only, no value read) + - id: bind-env + owner: net-kingdom + when: client_type == confidential + entry_point: 'reviewed adapter net-kingdom/keycape-bind-env, dispatched by Warden''s + case kernel inside the attended session: patch Deployment sso/keycape env + KEYCAPE_{client_id:env}_CLIENT_SECRET from sso/keycape-{client_id}-client, by + reference only' + auth: the attended human session on lane keycape-attended-case-change, with operator + kube credentials for namespace sso (actor human, parent-session revoked per attempt) + lane: keycape-attended-case-change + warden_executes: false + attended: true + value_handling: none + attended_justification: A change to the live identity provider's Deployment. It + runs in the same attended operator session as register-client, before it, because + KeyCape resolves a confidential client's secret at start and fails closed if + the binding is missing. Warden's case kernel dispatches the reviewed adapter only inside the one attended human session (descriptor actor human, lane keycape-attended-case-change, parent-session revoked per attempt); the human session is the authority and the kernel is only the dispatcher. + - id: register-client + owner: net-kingdom + entry_point: reviewed adapter net-kingdom/keycape-register-client, dispatched by Warden's + case kernel inside the attended session; it runs sso-mfa/k8s/keycape/keycape-client.py + apply --client-file --case-acceptance --check-config + (net-kingdom.keycape-client-registration) + auth: the attended human session on lane keycape-attended-case-change, with operator + write access to the live KeyCape client registry in namespace sso (actor human, + parent-session revoked per attempt) + lane: keycape-attended-case-change + warden_executes: false + attended: true + value_handling: none + attended_justification: 'The live KeyCape client registry is a Kubernetes Secret, + and agents do not read or write Secrets. Until KeyCape loads clients from a + non-secret registry with secrets referenced from custody, one attended operator + session applies the rendered registration. ops-warden groups all attended steps + of a run into that one session. KeyCape loads clients at startup and keeps login, + authorization and handoff state in memory, so the restart drops in-flight logins + and sessions for every client; runs go in a quiet window (key-cape type review + R4). The rendered registry must pass KeyCape''s own startup checks (keycape + check-config, the pinned checker artifact sha256:be37738fe1c08259fb4f9ca0daf0f9301bfffc237e5bb38435525dfbf11ccff5 + built from key-cape 3c71226, which keycape-client.py runs before it patches) + before the restart: a refused entry stops the single replica and every client + loses sign-in (R1). Warden''s case kernel dispatches the reviewed adapter only inside the one attended human session (descriptor actor human, lane keycape-attended-case-change, parent-session revoked per attempt); the human session is the authority and the kernel is only the dispatcher.' + - id: deliver-consumer-secret + owner: '{consumer_owner_repo}' + when: client_type == confidential + entry_point: Reviewed adapter net-kingdom/keycape-deliver-consumer-secret, dispatched + by Warden's case kernel inside the attended consumer-owner session; the owner's + apply capability creates dedicated ServiceAccount {consumer_namespace}/{consuming_service}-keycape-{client_id}-eso + (automount=false, no legacy tokens), namespace SecretStore openbao-keycape-{client_id}-consumer + with serviceAccountRef and audiences [openbao], and ExternalSecret {consuming_service}-netkingdom-oidc-client + with Owner target, Retain remote deletion and only CLIENT_SECRET from workloads/keycape/{client_id}. + Admission validates actual installed ESO TokenRequest support and common controller + RBAC before any owner-approved apply. + auth: the attended consumer-owner session on lane consumer-owner-deployment (actor + human, the consumer owner's operator deployment authority, on the founder's go + for the change); ESO authenticates to OpenBao role external-secrets-keycape-{client_id}-{consuming_service} + with a TokenRequest for the dedicated namespaced serviceAccountRef; controller + remains trusted + lane: consumer-owner-deployment + warden_executes: false + attended: true + value_handling: none + attended_justification: The consumer owner's only deployment authority in the consumer + namespace is its operator credential, and production applies need the founder's + go per change. No contained agent deployer identity exists, and the owner does + not want a standing credential an agent could trigger. It runs in one attended + consumer-owner session together with hand-over. Warden's case kernel dispatches + the reviewed adapter only inside that session (actor human, parent-session + revoked per attempt); the human session is the authority and the kernel is only + the dispatcher. + ready_check: Namespace SecretStore and ExternalSecret both report Ready=True; + dedicated SA has no legacy token refs; status and fixed metadata only, never + Secret values. + - id: hand-over + owner: '{consumer_owner_repo}' + entry_point: The consumer owner first makes its own Helm change configuring {consuming_service} + with issuer {issuer}, client {client_id} and custody bao:platform/workloads/keycape/{client_id}, + requiring one of {access_groups} in the groups claim before sign-in completes. + Then reviewed adapter net-kingdom/keycape-hand-over, dispatched by Warden's case + kernel inside the attended consumer-owner session, verifies the consumer with + four value-free probes (member-login, non-member-refused, membership-add-grants, + membership-remove-revokes) and records the hand-over receipt. + auth: the attended consumer-owner session on lane apps-consumer-handover (actor human, + parent-session revoked per attempt); the consumer configuration itself is the + owner's own deployment path + lane: apps-consumer-handover + warden_executes: false + attended: true + value_handling: none + attended_justification: The verification needs a real member login and a refused + non-member login, which only an attended session can perform, and it follows + the owner's own Helm change in the same consumer-owner session as + deliver-consumer-secret. Warden's case kernel dispatches the reviewed adapter + only inside that session (actor human, parent-session revoked per attempt); the + human session is the authority and the kernel is only the dispatcher. + verification: + - id: authorize-accepts-redirects + check: GET {issuer}/authorize with client {client_id} and each of {redirect_uris} + returns the login page + expect: present + reads_secret_value: false + - id: authorize-refuses-other-redirect + check: GET {issuer}/authorize with client {client_id} and an unregistered redirect + returns an OAuth error + expect: deny + reads_secret_value: false + - id: custody-denied-to-default + when: client_type == confidential + check: a default-only OpenBao identity has no read capability on bao:platform/workloads/keycape/{client_id} + expect: deny + reads_secret_value: false + - id: custody-allowed-to-consumer + when: client_type == confidential + check: policy workload-kv-read-keycape-{client_id}-{consuming_service} grants + read on bao:platform/workloads/keycape/{client_id} (capabilities check only) + expect: allow + reads_secret_value: false + - id: groups-present + check: LLDAP groups {access_groups} exist, with exactly the memberships of the + accepted case added + expect: present + reads_secret_value: false + - id: consumer-enforces-groups + check: '{consuming_service}''s OIDC source for {client_id} (owner {consumer_owner_repo}) + requires one of {access_groups} in the groups claim (configuration read, no + secret value)' + expect: present + reads_secret_value: false + - id: secret-delivered + when: client_type == confidential + check: the delivery for sso/keycape-{client_id}-client reports Ready=True (status + only) + expect: present + reads_secret_value: false + - id: mfa-pinned + when: mfa_required == true + check: keycape-client.py inspect for {client_id} shows the MFA pin on the registered + client (non-secret client view) + expect: present + reads_secret_value: false + - id: consumer-secret-delivered + when: client_type == confidential + check: '{consumer_namespace} SecretStore openbao-keycape-{client_id}-consumer + and ExternalSecret {consuming_service}-netkingdom-oidc-client Ready=True, exact + dedicated SA/audience role binding, fixed CLIENT_SECRET mapping; no Secret value + read' + expect: present + reads_secret_value: false + rollback: + - reverses: hand-over + action: the adapter removes only its hand-over receipt. Removing the OIDC source + for {client_id} from {consuming_service} is the owner's Helm rollback (owner + {consumer_owner_repo}), done inside deliver-consumer-secret's owner_remove + before any consumer object is removed + - reverses: deliver-consumer-secret + when: client_type == confidential + action: After reverting consumer secret reference, delete only authenticated created + ExternalSecret/owned target by UID and ownerReference, then SecretStore and + dedicated SA; preserve preexisting-matched resources and never delete a shared + target. Retain audit and ownership receipts; removing SecretStore/SA alone is + not immediate OpenBao token revocation. + - reverses: register-client + action: keycape-client.py apply --operation remove for {client_id} + - reverses: bind-env + when: client_type == confidential + action: remove env KEYCAPE_{client_id:env}_CLIENT_SECRET from Deployment sso/keycape, + after register-client is reversed + - reverses: deliver-secret + when: client_type == confidential + action: delete ExternalSecret sso/keycape-{client_id}-client and its target + - reverses: mint-secret + when: client_type == confidential + action: 'covered by client-secret-path: one metadata delete, after deliver-secret + is reversed' + - reverses: ensure-groups + action: identity-provisioner revoke_service_groups inside the provisioner pod + (attended, as ensure-groups) with the case's memberships; delete_if_empty only + if no other case uses {access_groups} + - reverses: client-secret-path + when: client_type == confidential + action: delete metadata and all versions at platform/workloads/keycape/{client_id}, + after the ExternalSecret is gone (this also reverses mint-secret) + - reverses: eso-role + when: client_type == confidential + action: delete Kubernetes-auth role external-secrets-keycape-{client_id} + - reverses: keycape-reader-policy + when: client_type == confidential + action: delete policy workload-kv-read-keycape-{client_id}-eso, after eso-role + - reverses: consumer-eso-role + when: client_type == confidential + action: Delete only this case-created role external-secrets-keycape-{client_id}-{consuming_service}, + before deleting consumer-reader-policy; revoke case-issued OpenBao tokens or + prove expiration before declaring deactivation. + - reverses: consumer-reader-policy + when: client_type == confidential + action: delete policy workload-kv-read-keycape-{client_id}-{consuming_service} + - reverses: catalog-entry + action: remove catalog entry {consuming_service}-keycape-client + acceptance: + accepted_by: + - owner + - founder + risk_class: medium + # Reviewed machine descriptors (NK-WP-0048-T16 successor candidate). Digests are + # generated by sso-mfa/k8s/keycape/compose-1.2-descriptors.py --version 1.3. + execution_descriptors: + bind-env: sha256:d5fcb20cf15bf7454890d2529180c582218e3df12584a99162e4b664b899caa0 + deliver-consumer-secret: sha256:36281df8139fb1ec64b226d94f80eb592de5addab7063ff54c691c641e275fc7 + deliver-secret: sha256:75aaa7346aebdea2a0eb0ba993995515b5248ffefc34caf8e713525e9b58da85 + ensure-groups: sha256:ebc9d5b9167d18e6015f4bbeb9699e8d396d64fbfaf195142781f84f4f47a70a + hand-over: sha256:1bce07a3229b7d0c7c730f35ce5b40fe471009a82de8dd53788ed970a9be1224 + mint-secret: sha256:c92957227235db04953809d92b260d32c8b947472a8e8fd17a258ade3ae8da70 + register-client: sha256:4ecc769f75433b66c7161f13c0fe7e5599633cf466d46cb6d8031f0456f426e7 diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-issuer-reference-integration-r1/verification.json b/docs/changes/netkingdom-target-2026-10-17/nk48-issuer-reference-integration-r1/verification.json new file mode 100644 index 0000000..b098063 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-issuer-reference-integration-r1/verification.json @@ -0,0 +1,13 @@ +{ + "status": "integrated-proposed-source-only", + "only_type_delta": "deliver-secret.entry_point", + "type_before_sha256": "e75fe94110ef9fb7fb18072fe2856941b33507c99cb406d36576046481485d0a", + "type_after_sha256": "ef02ddae3167bdd1558193a5f6db0379fb8cc84207531706d5ef35caabc3684f", + "parameters_unchanged": true, + "seven_descriptors_unchanged": true, + "composition_byte_identical": true, + "T33": "done: exact source reference only", + "T34": "progress: real owner backend absent", + "owner_assent": false, + "native_action": false +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-issuer-reference-integration-r1/workplan.preimage.md b/docs/changes/netkingdom-target-2026-10-17/nk48-issuer-reference-integration-r1/workplan.preimage.md new file mode 100644 index 0000000..4589b2b --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-issuer-reference-integration-r1/workplan.preimage.md @@ -0,0 +1,1648 @@ +--- +id: NK-WP-0048 +type: workplan +title: "Establish SecurityCaseTypes so that known security cases are light" +domain: infotech +repo: net-kingdom +status: active +flavor: implementation +owner: claude-code +topic_slug: netkingdom +created: "2026-10-08" +updated: "2026-10-17" +depends_on: [] +related_repos: + - ops-mason + - ops-warden + - key-cape + - railiance-platform + - informed-decision +state_hub_workstream_id: "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" +--- + +# NK-WP-0048 - Establish SecurityCaseTypes so that known security cases are light + +## Why + +The founder's direction (2026-10-08): setting up a security integration of a +kind that has been done before should be easy, meaning accept the case, then +ops-mason arranges it and ops-warden follows through. Setting up a new kind +needs ample documentation and tests and an informed-decision, because its shape +is part of the security architecture. + +Today every integration is hand-built: per-client helper scripts, per-lane +custody, founder checklists. The trigger was KEY-WP-0038 (Forgejo as an OIDC +client), whose next step was going to be yet another hand-written rollout +script. + +This plan continues the pattern work of NK-WP-0008 (pattern infospace), +NK-WP-0009 (tutorials) and NK-WP-0030 (scenario composition). It turns patterns +into **SecurityCaseTypes** that ops-mason and ops-warden can execute. Canon: +`canon/standards/security-case-types_v0.1.md` (proposed). + +Companion plans: ops-mason `MASON-WP-0010` (materialization) and ops-warden +`WARDEN-WP-0044` (follow-through). + +## Tasks + +```task +id: NK-WP-0048-T01 +status: done +priority: high +state_hub_task_id: "930ac3e9-20ec-5473-98cd-3fe8fddb730f" +``` + +### T01 — Accept the terminology and the canon + +Founder review of `canon/standards/security-case-types_v0.1.md`, including its +section 10 open questions. On acceptance the canon status goes from `proposed` +to `accepted`. + +2026-10-08: accepted by the founder (in session). Canon status `accepted`; +section 10's open questions are carried forward to the first type (T04, T05). + +```task +id: NK-WP-0048-T02 +status: done +priority: high +state_hub_task_id: "19c25d96-a55c-58f0-9168-3fdbc4955fc2" +``` + +### T02 — Inventory the de-facto types already built by hand + +Read-only. List every security integration built so far and group them by shape. +Known starting points: + +- browser OIDC relying parties on key-cape: informed-decision-approver, + coulomb-social, user-engine portal, openbao-admin, and Forgejo (pending) +- confidential service clients with `client_credentials`: secrets-engine + approval, requester and openbao clients, approval-engine operator +- workload KV read lanes on OpenBao: the railiance-platform credential lane + designs and CCRs, for example `railiance-backup-offsite-lane` +- short-lived SSH certificates through ops-warden (`warden sign`) +- tunnelled operator access (ops-bridge) + +For each: the repositories involved, the hand-built steps, attended human steps, +custody paths, verification evidence, and rollback (if any). Output +`docs/security-case-type-inventory.md`, ranking candidate types by how often the +shape recurs and how much manual work each instance costs today. + +2026-10-08: done, `docs/security-case-type-inventory.md`. 36 of 37 CCRs are +already `request_type: workload-kv-read`, a de-facto type reviewed each time as +if it were new. Browser clients: 5 hand-built plus Forgejo, each with its own +rollout script. Confidential service clients need a registration plus two or +three KV-read CCRs each. A forge read identity is needed in bulk by +ORGREF-WP-0002-T06. Recommendation: oidc-browser-relying-party first (unblocks +the forge), workload-kv-read second (largest payoff), then forge-read-identity. + +```task +id: NK-WP-0048-T03 +status: done +depends_on: [NK-WP-0048-T01] +priority: high +state_hub_task_id: "e487975b-73b1-516c-971d-da4cbbb93063" +``` + +### T03 — Schema, template and validator + +- `canon/schemas/security-case-type_v0.1.schema.json` and + `security-case_v0.1.schema.json` +- `security-case-types/_template/` with `type.yaml`, `README.md` and tests to copy +- a validator, extending `tools/security-scenario-composer/`, that checks a type, + checks a case against its type, and renders the case into the composer's + deterministic plan (still `execution.permitted: false`) + +Acceptance: the validator rejects missing sections, secret-shaped parameter +values, unpinned providers and cases against non-`implemented` types, with tests +for each. + +2026-10-08: done. `canon/schemas/security-case-type_v0.1.schema.json` and +`security-case_v0.1.schema.json`; `tools/security-case-types/security_case_validator.py` +(`validate-type`, `validate-case`, `render`, reusing the scenario composer); +`security-case-types/_template/` (a valid, runnable example type over +`net-kingdom.local-identity`, with README sections, an example case and tests that +travel with every copy). 31 tests pass, including the composer's existing suite. + +```task +id: NK-WP-0048-T04 +status: done +depends_on: [NK-WP-0048-T02, NK-WP-0048-T03] +priority: high +state_hub_task_id: "2fffe112-7d55-5680-9952-78d74f44f87f" +``` + +### T04 — Define the first type: browser OIDC relying party + +`security-case-types/oidc-browser-relying-party/`, version 1.0: + +- parameters: client id, display name, exact redirect URIs, scopes, client type + (confidential or public), MFA pin, the groups the consuming service maps, + custody path for the secret (confidential only), consuming service and its + owner +- composition: key-cape client registration (key-cape declares it as a playbook + capability), secret minting into custody (railiance-platform), LLDAP group + existence and membership (net-kingdom identity-provisioner), handover to the + consuming service +- materialization (ops-mason) and follow-through (ops-warden) sections +- verification: discovery lists the redirect URI; a test sign-in returns the + expected claims for one human; anonymous access to the custody path is denied +- rollback: remove exactly this client and its custody path +- README: threat model, MFA rationale, why redirect URIs are exact, which steps + still need an attended login and how that is minimised +- reference instances: informed-decision-approver, coulomb-social, user-engine + portal, openbao-admin + +Acceptance: complete per canon section 3, tests green, status `proposed`. + +2026-10-08: done. `security-case-types/oidc-browser-relying-party/` 1.0 (proposed): +`type.yaml` with 12 parameters (the custody reference is required only for +confidential clients), composition over key-cape's new +`key-cape.oidc-client-registration` declaration (contract PASS, `maturity: +planned`), materialization and follow-through, verification and rollback, all +conditional on client type where it matters. One attended step, +`register-client`, justified by the live registry being a Kubernetes Secret. +README with threat model, alternatives, an honest fit table (portal and vergabe +fit; Forgejo is the first confidential case; openbao-admin, coulomb.social and +informed-decision-approver do not fit 1.0), and known limits. Examples: Forgejo +(checked against the accepted request) and a public portal. The validator gained +`when` / `required_when` conditions and provider discovery. 43 tests pass. +ops-mason's and ops-warden's sections are drafted in the type. Their owners +review them in MASON-WP-0010-T01 and WARDEN-WP-0044-T01 before acknowledgment +(T05). + +```task +id: NK-WP-0048-T05 +status: done +depends_on: [NK-WP-0048-T04, MASON-WP-0010-T01, WARDEN-WP-0044-T01] +priority: high +state_hub_task_id: "3d987d3d-0797-538b-86e4-51185a327ae2" +``` + +### T05 — Acknowledge the type through informed-decision + +Prepare the Decision Memo from the type's README and `type.yaml` and submit it +through informed-decision's production intake. On acknowledgment the type +moves to `acknowledged`. + +2026-10-08: Decision Memo prepared. Hub decision `e418263e-a71f-4f01-a970-c87c9e187c09`. +`security-case-types/oidc-browser-relying-party/decision/`: `packet.md` (question, +one act, what acknowledging does not do, the shape, alternatives, attended +steps, limits, evidence), `build_intake.py` and the built `intake.json` (memo +class `decision-record`, review group `net-kingdom-admins`, resolves the hub +decision on answer). The envelope passes informed-decision's own +`validate_submission`. Queuing it is an attended operator step, a write inside +the production dispatcher pod: + + kubectl -n state-hub exec -i deploy/state-hub-memo-dispatch -- \ + python -m api.services.memo_dispatch --queue /data/private enqueue --envelope /dev/stdin \ + < security-case-types/oidc-browser-relying-party/decision/intake.json + +The answer then appears for review in informed-decision. + +2026-10-08: queued by the founder (`pending`, `queued`) after one refusal: the +first envelope's idempotency key was not State Hub's stable +`state-hub..` form, and the builder now validates against both +sides. The dispatcher delivered it to informed-decision (ledger 7 → 8 +delivered). T05 waits for the founder's answer in informed-decision, which +resolves hub decision `e418263e`. + +2026-10-08: ops-mason's review (MASON-WP-0010-T01) folded into the type, still +1.0 `proposed`: `secret_ref` is no longer a parameter, the type derives +`platform/workloads/keycape/{client_id}`; policies are +`workload-kv-read-keycape-{client_id}-eso` and +`workload-kv-read-keycape-{client_id}-{consuming_service}`, each with a templated +body (`read` on that one data path); new `openbao_role` +`external-secrets-keycape-{client_id}` (Kubernetes auth, ESO service account, +15m, no default policy); catalog entry `{consuming_service}-keycape-client`; +the two rollback steps on the KV path collapse into one metadata delete. The +schema and validator now require a `body` on policy and role items. 105 tests +pass. Two consequences: the delivered memo describes the type before these +changes (a case-set custody reference, two differently named policies, no ESO +role), so the founder answers on a superseded shape unless the memo is +re-sent; and Forgejo's custody path moves from the accepted request's proposed +`platform/workloads/forgejo/netkingdom-oidc-client` to +`platform/workloads/keycape/forgejo`, which key-cape (KEY-WP-0038) and +railiance-apps (RAPPS-WP-0016-T02) need to adopt in T06. ops-warden's review +(WARDEN-WP-0044-T01) is still in progress. + +2026-10-08: revised memo `nk-wp-0048-t05-oidc-browser-relying-party-r2` built +(`packet.md`, `intake.json`, packet `sha256:af6435e2…`); it states that it +supersedes the first memo and passes informed-decision's and the dispatcher's +validation. It needs a new memo id because the dispatcher refuses new content +under a delivered idempotency key (`idempotency_conflict`). Queuing it is the +same attended step as above. Both memos serve hub decision `e418263e`; the +first answer resolves it, so the first memo must not stay open: an accept on it +would acknowledge the superseded shape, and a decline would resolve the +decision against r2. Close it with `return` (a reviewer verb that requests a +new revision and does not resolve the hub decision; only accept and decline +do), commenting that r2 supersedes it, then answer r2. `withdraw` exists in +the vocabulary but is not offered to reviewers, and lifecycle `revise` only +applies to lifecycle-registered memos, not intake-delivered ones. + +2026-10-09: the founder reports r2 enqueued and the first memo returned. At +close, hub decision `e418263e` still showed `open` (not verified whether r2 +was answered or the answer is still in transit; the dispatcher ledger was not +read). ops-warden's review (WARDEN-WP-0044-T01, ops-warden +`history/2026-10-08-oidc-rp-follow-through-review.md`, d470f2e) arrived at +21:25, before r2 was built, and is not in r2. Required before WARDEN-WP-0044-T02: +R1 a per-step `lane` (missing: identity-provisioner caller credential, +railiance-platform broker grant), R2 canon meaning of `warden_executes: true` +(caller proxy, caller identity, no standing ops-warden token; ADR-0002), R3 a +per-step `value_handling`. Refinements R4 (one type-level catalog lane instead +of a per-case entry, which conflicts with ops-mason's item 5), R5 (ESO Ready gate +before bind-env), R6 (exact accepted memberships), R7 (mfa-pinned and +secret-delivered checks). Next: if r2 was accepted, fold R1-R3 and R5-R7 in as +a follow-up and settle R4 between ops-warden and ops-mason before T06; if not, +hold r2, fold them in and build r3. + key-cape and railiance-apps were told about Forgejo's new custody +path. + +2026-10-09: r2 was not answered (hub decision `e418263e` open; dispatcher 9 +delivered), so r2 is held and r3 replaces it. ops-warden withdrew R4 for R4′ +(message a66a9204): one catalog entry per case with its body templated in the +type. Folded in: + +- Canon section 3: a narrow meaning of `warden_executes: true` (caller proxy, + caller identity, no standing credential; ops-warden ADR-0002), plus per-step + `lane`, `value_handling` and an optional `ready_check`. A catalog entry body is + now required, like policy and role bodies. +- Schema and validator enforce all of these. +- The type: lanes `identity-provisioner-groups` and + `keycape-client-secret-generate` (both new) and `key-cape-oidc-login`; + `mint-secret` is `generated_in_custody`; a Ready gate on `deliver-secret`; + `ensure-groups` adds exactly the accepted case's memberships; new checks + `secret-delivered` and `mfa-pinned`; the catalog-entry body follows R4′. +- 110 tests pass. + +Memo r3 (`nk-wp-0048-t05-oidc-browser-relying-party-r3`, packet +`sha256:726dfc01…`) passes informed-decision's and the dispatcher's validation. +The founder queues it with the same command, returns r2 with a comment that r3 +supersedes it, then answers r3. + +Prerequisites for T06, not for T05: +- the two new lanes, each through its owner; +- a founder-approved rollout of an identity-provisioner image containing + 630ebfd (the deployed one is main-6c4fcaf from 2026-09-24, without + `/v1/groups/ensure`). + +ops-mason's request to create LLDAP group `fluid-telegram-operators` (message +259ec26d) was answered: not possible from here by that path; the founder creates +it in the LLDAP admin UI. + +2026-10-09: ops-warden finished its side of WARDEN-WP-0044-T01 (message +`58d33f40`, commit 0e7ceb8): the revised type conforms to `warden case +check-type`, with no further changes asked of the type. This acknowledges that +review. Its later addendum R8 (message `af274783`, ops-warden +`history/2026-10-08-oidc-rp-follow-through-review.md`) showed that lane +`identity-provisioner-groups` cannot exist as declared: the provisioner's only +HTTP credential is user-engine's runtime token, and its NetworkPolicy admits +only user-engine pods, so a caller-proxy lane would act as user-engine. Taken +option 1 (ops-warden's recommendation): `ensure-groups` and its rollback are +attended, in the `register-client` session on `key-cape-oidc-login`, run inside +the provisioner pod; no token is read. Option 2 (an operator credential of its +own on the provisioner) stays open for a later version. `warden case +check-type` now reports only `keycape-client-secret-generate` +(railiance-platform) as missing. 89 tests pass. + +Hub decision `e418263e` was still open, so memo r4 +(`nk-wp-0048-t05-oidc-browser-relying-party-r4`, packet `sha256:452ea421…`) +replaces r3; it passes informed-decision's and the dispatcher's validation. The +founder queues it with the same command, returns r2/r3 if still open with a +comment that r4 supersedes them, then answers r4. + +Prerequisites for T06 are now: lane `keycape-client-secret-generate` +(railiance-platform), and a founder-approved rollout of an identity-provisioner +image containing 630ebfd. + +2026-10-09: done. The founder queued r4 (`pending`, `queued`), returned r3 as +superseded and accepted r4 in informed-decision. informed-decision's hub +write-back is off in production (`INFD_HUB_WRITEBACK` unset), so net-kingdom +resolved hub decision `e418263e` from the founder's report, naming memo r4 in +the rationale. The type is `acknowledged`; it becomes `implemented` with T06. + +```task +id: NK-WP-0048-T06 +status: wait +depends_on: [NK-WP-0048-T05, NK-WP-0048-T08, NK-WP-0048-T09, NK-WP-0048-T12, NK-WP-0048-T13, NK-WP-0048-T14, NK-WP-0048-T15, NK-WP-0048-T16, NK-WP-0048-T17, NK-WP-0048-T18, NK-WP-0048-T20, MASON-WP-0010-T03, WARDEN-WP-0044-T03] +blocking_reason: "Type1.2 architecture has a confirmed source-only founder acknowledgment; canonical1.1 remains unchanged/default and the1.2 reference case stays proposed. Needs explicit version selection/reference-source integration without normal admission bypass, closed owner adapters plus global/native receipt verifier (WARDEN-WP-0044-T03), independently verified six-object structural materialization and CCR42 claims/created-only provenance, exact broker/case/source reconciliation, then separately authorized native mint/delivery and attended registry/consumer verification. Source acknowledgment, a bundle or local tests do not prove implemented/materialized/live status." +priority: high +state_hub_task_id: "d9b26bcf-a7c6-59a2-92a0-f913d76a203c" +``` + +### T06 — First KnownSecurityCase: Forgejo + +Write `security-cases/forgejo-netkingdom-oidc.yaml` from the parameters the +founder accepted in KEY-WP-0038-T01 (`key-cape/docs/forgejo-oidc-client-request.yaml`), +so that acceptance carries over as the case acceptance. ops-mason materializes it; +ops-warden follows through and verifies it. The type becomes `implemented` once +this reference case is live. + +This replaces the hand-built path proposed for KEY-WP-0038-T02. + +2026-10-09: case written, `security-cases/forgejo-netkingdom-oidc.yaml`, status +`accepted`: the example's parameters plus the founder's 2026-10-08 acceptance of +the key-cape request (owner and founder), with the custody-path derivation +covered by the type's acknowledgment (memo r4). It validates and renders (plan +`execution.permitted: false`; three attended steps on `key-cape-oidc-login`); +it needs `--allow-unimplemented` until it is live, since it is the case that +makes the type `implemented`. A test keeps it equal to the type's example. +Still open before it runs: ops-mason renders and builds it (MASON-WP-0010-T02, +T03, unblocked by T05), ops-warden plans and runs it (WARDEN-WP-0044-T02, T03), +railiance-platform's `keycape-client-secret-generate` lane (asked by ops-mason +`789feb39` and ops-warden `d9f889b1`, both unread), and a founder-approved +identity-provisioner rollout containing 630ebfd. + +2026-10-09: identity-provisioner rolled out, founder-approved in session: +`main-630ebfd` (`sha256:bafb4860…`, built by the image workflow) replaces +`main-6c4fcaf` (`sha256:ffacd5d7…`; only change: 630ebfd, additive). Applied +with `kubectl set image` on the Deployment only (generation 17 → 18, 1/1 Ready); +the file's Service, NetworkPolicies and Ingress were not reapplied. Verified in +the pod: `ensure_service_groups` and `revoke_service_groups` present, `/readyz` +200; no token used, no group changed. Rollback: `kubectl -n sso set image +deploy/identity-provisioner provisioner=forgejo.coulomb.social/coulomb/identity-provisioner@sha256:ffacd5d790a5b7800e0535f3aa95b7de52c7a68e4f575b9192c3e1622fa672c2`. +The pin in `sso-mfa/k8s/identity-provisioner/deployment.yaml` follows. + +2026-10-09: ops-warden closed WARDEN-WP-0044-T01 and shipped `warden case plan` +(T02, message `06303d1d`); the accepted Forgejo case is refused until ops-mason +materializes it, and a materialized copy plans cleanly once +`keycape-client-secret-generate` exists. Its observation that `bind-env` carries +a literal `KEYCAPE__CLIENT_SECRET`: the validator now renders a +`{param:env}` format spec (upper case, `-` as `_`, as `keycape-client.py` +derives the name) and rejects unknown specs, and follow-through and rollback +text is now checked for unknown placeholders too (patch-level, canon section 6). +The type keeps the literal until ops-warden's planner (`text.format`) renders the +spec as well; asked of ops-warden. 53 tests pass. + +2026-10-10: ops-warden rendered `{param:env}` in `warden case plan` +(23ba584, message `c1df14b7`; other specs and `!conversion` refused). The type +adopts it as a 1.0 patch (canon section 6): `bind-env` and its rollback now read +`KEYCAPE_{client_id:env}_CLIENT_SECRET`. ops-mason reports the Forgejo case +rendered and built (message `1ebddae9`; CCR drafts handed to railiance-platform), +so T06 now waits on the platform apply, the mint-secret lane and the attended +register-client session. + +2026-10-10: key-cape adopted the type's custody path (key-cape ab5cccd, message +`88a4559c`): `docs/forgejo-oidc-client-request.yaml` and KEY-WP-0038-T02 name +`platform/workloads/keycape/forgejo`, field `CLIENT_SECRET`, delivered as +`sso/keycape-forgejo-client` through `external-secrets-keycape-forgejo`, and the +`forgejo/netkingdom-oidc-client` proposal is withdrawn. This matches the type and +the Forgejo example; no change needed here. + +2026-10-10: key-cape's type-composition review (key-cape 6038483, message +`8e5ed5f6`) found the composition sound with two blocking findings, taken as a +1.0 patch (stricter validation and documentation, canon section 6): +R1, redirect URIs now refuse `?` and `#` in the type pattern and in +`keycape-client.py`'s `REDIRECT` (KeyCape refuses `?` at startup and the single +replica would stop); R2, `allowed_scopes` always includes `groups`, the renderer +refuses `access_groups` without it, and `access_groups` now says the consumer +enforces the gate because KeyCape has none; R3, `mfa_required` "false" means the +per-user privacyIDEA policy; R4, register-client's justification records the +restart for every client, the quiet window, and that the rendered registry must +pass KeyCape's client validation before it. The accepted Forgejo case and both +examples still validate; 116 tests pass. +Open, needs a decision: R2's verification step that the consumer enforces the +group gate is a new verification step, hence 1.1 (minor), and the validator pins +cases to the exact type version, so the accepted 1.0 Forgejo case would need +re-pinning. Also open with key-cape: a KeyCape entry point that validates a +rendered client registry without starting the server; until then +`keycape-client.py` mirrors the redirect rule. + +2026-10-10: founder go for 1.1, so no 1.0 run overlaps. The type adds +verification `consumer-enforces-groups`, and hand-over now requires the consumer +to enforce `access_groups` (minor, canon section 6; risk class unchanged, no new +acknowledgment; history in the type README). The accepted Forgejo case and both +examples are re-pinned to `oidc-browser-relying-party@1.1` before any apply; the +acknowledgment intake keeps `@1.0` as the record of what was acknowledged. +ops-mason's build and ops-warden's case-ref digest predate this and need a +re-render. + +2026-10-10: ops-mason re-rendered the Forgejo case from `@1.1` (message +`22c6bba6`; plan digest `af1f4f81…`, no collisions) and rebuilt the bundle. The +rendered OpenBao objects are identical to 1.0, so the already applied +CCR-2026-0041 stands; 1.1 changes only consumer-side steps. key-cape shipped +`keycape check-config` (c528daa, message `a7b0aeff`): KeyCape's startup checks +without secret values, including the new duplicate-clientId rule. +`keycape-client.py --check-config ` now runs it on the live and the +proposed registry, fed on stdin, and apply refuses a proposed registry that +fails; register-client's entry point names the option (1.1 patch). The deployed +image predates c528daa, so the binary is built from key-cape source for now. +Before the next KeyCape rollout (Forgejo register-client or the CCR-2026-0018 +window), the attended operator runs `inspect --check-config` so the live +registry is checked too. + +```task +id: NK-WP-0048-T07 +status: wait +depends_on: [NK-WP-0048-T06] +blocking_reason: "The next types follow once the first one is proven end to end." +priority: medium +state_hub_task_id: "c3c9c3ae-4f23-5ba7-9e6f-e63bcf29b9f7" +``` + +### T07 — Queue the next types + +From the T02 ranking, open the next type drafts (likely the confidential service +client and the workload KV read lane), each to be acknowledged on its own. +Record which existing hand-built instances get adopted as cases, without +rewriting them in place. + +```task +id: NK-WP-0048-T08 +status: done +priority: high +state_hub_task_id: "8e519082-dc8b-5e46-b660-03d03e55dcf6" +``` + +### T08 — identity-provisioner: ensure service-access groups + +The type's `ensure-groups` step calls `identity-provisioner groups ensure --groups + --members `. The provisioner already has idempotent +`_ensure_group` and `_add_group`, but exposes only tenant-scoped operations. Add +the general operation (create if absent, add the listed memberships, never +remove others), its rollback (remove only this case's memberships), tests, and +the ops-warden route for its service identity. + +2026-10-08: done. `identity-provisioner/provisioner.py` gained +`ensure_service_groups` (`POST /v1/groups/ensure`) and `revoke_service_groups` +(`POST /v1/groups/revoke`), behind the existing service-token check. Names +matching `tenant*`, `netkingdom-*` and `lldap_*` are reserved; members must +already exist; everything is validated before any change; memberships are read +back before success; nothing else is ever removed; groups are deleted only when +asked and empty. 7 new tests (39 in the provisioner suite). The type's +`ensure-groups` step and its rollback now name these endpoints. The ops-warden +route for the provisioner's service token belongs to WARDEN-WP-0044-T01. + +```task +id: NK-WP-0048-T09 +status: done +priority: high +state_hub_task_id: "0ee12dfc-8260-5038-bd39-cc7fb4113be0" +``` + +### T09 — The `keycape-client` entry point and its declaration + +Planned first as key-cape KEY-WP-0038-T04. Moved here because the machinery a +live registration needs already lives in net-kingdom, where KeyCape is deployed +(`sso-mfa/k8s/keycape/`): the contained kubectl, the cluster-identity pin and the +issuer pin. A copy in key-cape would drift. + +2026-10-08: done. `sso-mfa/k8s/keycape/keycape-client.py` derives the KeyCape +client record from a case (`client-from-case`). It adds or removes exactly one +client with the existing safeguards (byte-preserving merge, exact match or +refuse, shared-reference refusal, compare-and-swap, read-back, contained errors), +requires the Deployment env binding first for confidential clients, and requires +the case acceptance record for `apply` instead of a per-client founder decision +id. 15 tests, including a regression showing that the vergabe case reproduces +its hand-built registration exactly; all 71 helper tests pass. Declaration +`capabilities/playbooks/net-kingdom.keycape-client-registration.yaml` (contract +PASS, `maturity: implemented`) replaces key-cape's draft. The type now pins it, +and for confidential clients adds `deliver-secret` (delivery from custody into +the sso namespace) and `bind-env` (Deployment env), attended in the same session +as `register-client`. The per-client helpers stay until their clients are +adopted as cases. + +2026-10-10 — NetKingdom owner review of the execution seam, source only: +ops-warden/docs/security-case-execution-proposal.md SHA256 78ab1439e4c5beb205aeccf15b1b2e706511be55ab195c5a3aa682f326c7c77c +was reviewed against INTENT, SecurityCase canon, acknowledged type1.1, current +case and owner source. docs/security-case-execution-owner-review.md supports +the closed adapter/receipt direction with explicit versioned schema, structural +materialization provenance, unchanged caller/session boundaries, safe CAS +reconciliation and exact-created-object rollback requirements. This is owner +review, not approval. Supporting file digests are in the adjacent sources JSON. + +Current source exposes a circular wait: ops-mason T03 waits on mint while Warden +and the broker refuse a non-materialized case. Structural acceptance therefore +needs its own native pinned receipt before mint, not a successful mint before +materialized. The platform broker additionally pins type1.0 while Forgejo is +accepted at1.1; platform-owner alignment and tests are required, not an unchecked +version bypass. T06 remains wait. No case/type lifecycle status, grant, authority +or live runtime changed by this review. A proposed informed-decision would +adopt the source contract only; native materialization and execution acceptance +stay distinct. + +Final proposal re-review: staged Warden proposal SHA256 78ab1439e4c5beb205aeccf15b1b2e706511be55ab195c5a3aa682f326c7c77c +adds only the explicitly local reducer appendix, not an accepted wire protocol +or executor. The exact-final review and sources JSON supersede the earlier +1035f049... proposal review. Platform staged the exact type1.1 broker correction +with ten fake-backend subprocess checks (script SHA256 +9576cd211cfc06526ec4cc9bd0a5b06f0f59287a68931bb2d6e3592a951ddc7d). +The next source work is the versioned closed adapter/receipt/materialization +contract, not another relaxation of the broker's status gate. Native work still +requires installed broker provenance, structural reader receipts, exact new +materialized-case bytes, caller grant/session revocation and verified execution. +No installed/native acceptance is inferred from that staged correction. + +2026-10-11 — new source-only type1.2 architecture acknowledgment: +Confirmed native founder ACCEPT of infd-20261011-forgejo-type12-source-r1@1 +at06:47:39Z, approval8e9332c8-754a-420c-b8b7-445cb4fe6765. Exact frozen packet +0b377d647ca748cd03c92f9792396412048055d4bde9f7bb06ff6d413f7acd68 and submission +585cee731de30d2b37883a57abfd9eb32f023ee694e8d911989109d5caf80569; native receipt +retained in docs/evidence/2026-10-11-forgejo-type12-source-acknowledgment/. +The complete acknowledged1.2 source package is isolated under +security-case-type-revisions/1.2/oidc-browser-relying-party; default loader and +current accepted1.1 case remain untouched because load_type is single-version. +The new proposed1.2 case is NOT accepted/materialized/live and has no acceptance +fields. Actual reference-migration design approval is a separate Apps owner +receipt; no1.1 approval is inherited. Source-recording review/test proof and exact +next gates are in docs/forgejo-type12-source-acknowledgment-followthrough.md. +T06 remains wait and T07 remains its existing dependent task; type is NOT +implemented and no first-case/native grant or broader workplan finish is asserted. + +2026-10-11 — bounded explicit version selection source preparation for T06: +The validator now offers optional exact version_roots/--version-root VERSION=PATH +selection with duplicate/mismatch/traversal/symlink refusal and no fallback. The +default1.1 source and historicalacceptedcase hashes/ordinaryrefusal/preparation +render stayunchanged; actual1.1 is acknowledged, not implemented. Acknowledged1.2 +ordinaryadmission also refuses; optionalpreparation remains nonexecuting.43local +validatorchecks pass and original1.1 CLIrenderbytes match exactly. Makerproof: +docs/evidence/2026-10-11-explicit-type-version-selection/source-integration-validation.json. +FormalNetownerreview/reference-source integration and all existing native adapter, +provenance/broker/materialization/attendedproof gates remain; T06 stayswait, no +new residual or firstcase/typeimplementation/native grant is inferred. + + +2026-10-16 — isolated source preparation for T06 (canonical integration pending): +Added `check-evidence` to the existing validator CLI. It reuses exact version +selection and ordinary type/case/render admission, checks actual Mason structural +request source/inventory/projection pins and reads actual Warden journal rows +read-only. Successful rows require exact type-declared descriptors and attempt/ +revocation fingerprints. Existing acknowledged types without machine descriptors +remain plan-only. The compatibility result never claims authenticated native +completion: Request and journal fingerprints are not portable native evidence. +Owner authentication/status binding and actual native gates remain unresolved; +T06/T07 statuses and both type/case lifecycles remain unchanged. No dispatch, +credential access or authority expansion is introduced. See tool README for the +existing owner protocol boundaries and source integration tests. + +Concrete next Warden owner integration: current `Executor.admit` refuses every +case parameter whose sensitivity is not public/operational, so browser +`redirect_uris`, `allowed_scopes`, `client_type`, `mfa_required` (and 1.2 +`consumer_namespace`) fail after otherwise prepared admission. The sensitive +classification is a security-change review level, not secret content. The +Warden owner must validate through NetKingdom's existing exact-version case/type +validator, then project only descriptor-declared safe target metadata; do not +remove the credential/value refusal or copy complete parameters into journals. +Actual owner descriptor acknowledgment and independent evidence admission remain +prerequisites, with normal admission still refusing unimplemented types. + + +## T10 — Source compatibility checker for Mason and Warden evidence + +```task +id: NK-WP-0048-T10 +status: done +priority: high +state_hub_task_id: "4b06ca97-c1e4-5680-bf22-ab75ab1646a4" +``` + +Completed 2026-10-16 in published source `c31d21ba`. The read-only +`check-evidence` command checks the actual Mason structural request and Warden +journal source protocols against an exact version/render. Independent source +review passed 103 tests at that checkpoint. It always reports native completion +false and execution prohibited. Fixtures and local journal fingerprints do not +provide authenticated owner/native evidence. This records completed source scope +previously described under T06; it does not duplicate native acceptance credit. + +## T11 — Validate the optional machine-descriptor pin contract + +```task +id: NK-WP-0048-T11 +status: done +priority: high +state_hub_task_id: "ef2a0119-f59e-5e45-9519-115da9df7b85" +``` + +Completed 2026-10-16. Reproduced the JSON schema rejecting a valid +`execution_descriptors` map while the Python validator accepted malformed maps. +The schema now supports the existing Warden optional exact-byte pin map; semantic +validation binds keys to declared steps, bounds the map and checks digest shape. +The direct evidence API refuses malformed maps before accessing them. Twenty-one +new checks and all 127 affected source checks passed. No current type/case bytes, +status, owner descriptors or native acceptance changed. Source evidence and +publication receipt: Custodian +`docs/changes/netkingdom-target-2026-10-16/browser-acceptance-next/`. + +## T12 — Prepare NetKingdom-owned attended step adapters + +```task +id: NK-WP-0048-T12 +status: todo +priority: high +state_hub_task_id: "7ce4eee3-cd47-55a9-bfa5-4f1835d4d6fd" +``` + +Owner: NetKingdom. Retained scope: prepare bounded attended Deployment env, +config verification and restart/readiness steps around the existing helpers. +Next action: identify exact current entry points and their revision/readback +contracts, then implement fixed source-pinned callbacks or receipt-only attended +steps. Completion requires positive/refusal checks, exact target/source pins and +uncertainty behavior without acquiring user-engine runtime identity or exposing +registry/client secrets. Existing caller/session, cluster/issuer, CAS/readback +and status-only output boundaries remain mandatory. + +2026-10-16 task refinement: the original broad adapter scope is split into T13 +(completed registration source candidate), T14 (group creation provenance), T15 +(durable registration recovery) and T16 (descriptor/artifact selection and owner +review). This preserves T12's task ID and Hub UUID while giving each remaining +outcome an owner and completion gate. T06 now names every source dependency; +its native/reference lifecycle acceptance scope remains unchanged. Completion +of any source task supplies no Warden, Platform or Apps assent and no native +execution authority. + +## T13 — Prepare the fixed registration adapter source candidate + +```task +id: NK-WP-0048-T13 +status: done +priority: high +state_hub_task_id: "b264c10b-565c-5d29-b788-03cf5fbd2883" +``` + +Completed 2026-10-16, source implementation and synthetic fixture proof only. +Added an injection-only adapter around actual `keycape-client.run`: fixed +case/principal/human/lane/source/acceptance binding, existing CAS/readback, +metadata-only owner evidence and per-attempt parent-session revocation boundary. +The descriptor source composition pins the exact checker executable digest +alongside adapter/helper source. Creation-only rollback authenticates its +creation receipt and refuses changed postcreation UID/resourceVersion; +pre-existing clients confer no rollback authority. Missing readback, evidence, +revocation or process-local creation provenance cannot become retry-safe success. + +Evidence: Custodian `docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/` +R3 candidate, explicit patch and postimages, retained R1 rejected custody finding +and R2 revision, and 36 passing source checks (16 new, 20 existing). Three checks +use actual Warden kernel admission/dispatch/rollback with published Warden +`d97652a3bb5fa733b063d71a1b74068efcccf572` producer pins unchanged. Owner trust, +descriptor/type/case and Kubernetes objects remain synthetic fixtures. This +records a completed source deliverable, not independent review, publication, +type acknowledgment, exercised procedure, identity admission or native browser +acceptance. Independent root source review: Custodian browser-adapter-next/coordinator-review-r3.json. Source integration and normal publication/synchronization receipts: Custodian browser-adapter-next/canonical-integration.json. These receipts supply no other-owner assent or native authority. + +Source publication: `0775c2bfa41af236301155bb5a69bbf088bdf823`, independently +reviewed by the Custodian coordinator before integration. The required initial +projection and ordinary UUID writeback both completed with zero failures and +zero automation errors. Generated-only brief commits were refused by the normal +hook; exact pre-existing staged/working brief bytes were restored. The separate +canonical integration receipt retains those refusals and projection evidence. +Native admission and all other-owner acceptance remain open. + +## T14 — Admit governed group rollback against native owner boundaries + +```task +id: NK-WP-0048-T14 +status: progress +depends_on: [NK-WP-0048-T20, NK-WP-0048-T21] +priority: high +state_hub_task_id: "fce95440-a25b-5f27-8ef3-a3d182099113" +``` + +Owner: NetKingdom. Next action: bind the completed T21 PostgreSQL journal to +an admitted dedicated CNPG consumer through the existing `rapp-postgres` +database-per-consumer contract. Identify the actual deployment/package and +custody owner, admitted placement/capacity and role/renewable lease/TLS target; +implement the independently retained exact content checkpoint and authoritative +tenant/case/source/principal callbacks before enabling governed group operations. +The injection-only backend and fixture callbacks do not supply production scope +or authorization. Existing service tokens, caller-supplied JSON and provenance +receipts confer no new grant. The default production endpoint remains disabled +without the trusted integration. SQLite remains local/disposable only; any +production exception requires explicit scoped governance under ADR-0015. + +2026-10-17 owner-integration refinement: T23 records the completed source/package +placement review, distinct from T21's source/disposable-SQL proof. Its dedicated +`rapp-identity-provisioner` recommendation is draft and unbound, not an accepted +package or existing repository. The full six-consumer rapp-postgres renderer +accepts the inert candidate on platform-pg-2 as its third of four declared +consumers; first-cell placement is refused as a fifth. Neither the estimated +four-consumer ceiling nor source requests establish native headroom or backup. + +Next independent owner contracts: NetKingdom/package/substrate owners accept +exact deployment/rollback fate; rapp-postgres admits the dedicated consumer, +roles, five-connection budget and actual backup/restore; Platform admits exact +renewable runtime/migration delivery and TLS/CA/SAN; network owners scope SQL, +broker and checkpoint paths without assuming a narrow policy subtracts additive +same-namespace access. Protected caller owners must provide authoritative +GroupJournalBinding construction and current authorization; an independent +existing holder must authenticate and atomically advance the exact content +checkpoint, not a counter or ordinary Audit receipt. Directory/runtime owners +must select and prove either an exclusive all-writer maintenance barrier or +provider-supported CAS/generation, including established sessions, lifecycle +cascades and remove/readd ABA. No source review establishes those owner facts. + +The default PostgreSQL activation and destructive native rollback remain refused. +Storage/lease acceptance can be proposed separately from LDAP mutation only after +exact owner source/target/custody/verification/recovery and cost/risk review; +State Hub guard approval grants none of it. Source-only integration review and +native acceptance matrix: Custodian native-storage-r1/OWNER-REVIEW.md. T14's +existing status, UUID, dependencies and native completion gate are retained. + +2026-10-17 refinement: T20 retains the historical durable SQLite source and +actual disposable LLDAP proof. T21 extracts the completed PostgreSQL source and +bounded real disposable SQL compatibility proof. T14 remains `progress` for +native CNPG admission/recovery, protected owner integration and exclusion of all +external LDAP writers. Separate source publication/projection and independent +cleanup receipts belong to coordinator follow-through; no missing native assent +is represented as received. + +Completion still requires independently reviewed/admitted ADR-0015-conforming +CNPG storage (or an explicit governed exception), backup/lost-store recovery and +migration of legacy requests; a native +writer-isolation/fencing or provider CAS/version contract that closes external +writer races and remove/re-add ABA; exact case/source/principal and current +native object bindings for protected callbacks; and actual per-attempt evidence, +revocation and readback/refusal verification through the existing owner paths. +Only independently retained confirmed creations may be undone. Every +pre-existing/shared group and membership must remain preserved; unresolved +intents never expire into retry authority. The disposable fixture supplies none +of these native custody, authorization, owner-acceptance or production claims. + +## T15 — Define authenticated recovery for lost registration process state + +```task +id: NK-WP-0048-T15 +status: wait +depends_on: [NK-WP-0048-T31] +blocking_reason: "Source candidate 061461e is merged. Waiting on the owner to select the real authenticated original-attempt holder and native readback, and on independent re-review of the changed T13 adapter source." +priority: high +state_hub_task_id: "1b825705-4848-5edc-832f-b1622cd9bdb7" +``` + +2026-10-17 source part merged (061461e via 65559ef). `build()` takes optional +paired `holder`/`observe` capabilities and installs `reconcile_original`; the T31 +fixtures now run through the adapter's own path. Rollback after restart is still +refused, and there is no add replay. This changes `keycape-registration-adapter.py`, +so the revision reviewed under Custodian `browser-adapter-next/coordinator-review-r3.json` +is superseded. The new revision has no independent review yet and must not be +pinned on the strength of the old one. + +2026-10-17: T31 fixtures done (74adcd7). What remains for T15: the owner +selects the real authenticated original-attempt holder, and the T13 adapter +gains the holder capability, `reconcile_original` and reconcile-aware `verify` +described in T31. + +Owner: NetKingdom for registration state, with Warden owning kernel evidence +admission. T22 records the completed source review and exact existing-seam gaps; +T15 retains implementation and authenticated restart/lost-receipt acceptance. +Next action: select and admit the actual owner publisher/authenticator and bounded +original-attempt readback contract for the merged holder/observe adapter seam. +Warden's original-intent persistence/interface (`a45cf475`) and additive validation +(`117fec9241e7b985dfb187b992e173d0a5dc3700`) are now published and projected. +The local Journal retains admitted nonsecret original context, validates original +actor/attempt and metadata linkage to the original running receipt, and supplies +it separately from the new reconciliation attempt. This local consistency is not +independent original-holder authorization or creation provenance; neither the +journal nor a current matching registry entry reconstructs creation authority. + +The owner contract must bind the original Invocation, case/type/descriptor/catalog/ +acceptance pins, source, principal and admitted tenant/realm provenance, separately +from the new reconciliation attempt and its independently revoked parent session. +Use an existing authenticated holder/readback; Audit Core's custody API is a +candidate only until its actual registration source/class, load-bearing emission +contract and separate scoped reader are admitted. No new broker or receipt trust +root is proposed. A missing original record, ambiguous publication, NO_CHANGE, +changed native revision or lost readback stays uncertain; no add replay or undo. + +Completion requires restart/lost-receipt fixtures against the selected actual owner +contract: independently authenticated original created-only provenance, original +exact client entry and unchanged current postcreation UID/resourceVersion before +any undo, with wrong attempt/case/source/principal and unavailable-holder refusals. +The merged T13 adapter exposes reconciliation only through paired injected +holder/observe capabilities and still refuses post-restart rollback. Existing +source review, JSON receipts and synthetic trust do not satisfy independent +holder authorization/provenance or native admission, or authorize attended recovery. +Exact remaining owner facts: Custodian native-registration-r1/original-attempt-review.json. + +2026-10-17 check (net-kingdom-worker): Warden's original-intent lookup interface +is not published — ops-warden head is `d97652a`, with no WARDEN-WP-0044-T03 +commit, and the net-kingdom inbox holds no announcement. No restart/lost-receipt +fixture or readback seam was added this session; the work is deferred behind +T16's decomposition under the same token budget. Remains todo, gated on Warden +publishing the reviewed lookup/context interface and on owner selection of the +authenticated original-attempt holder. + +2026-10-17 current-source correction: the earlier same-day `d97652a` lookup +checkpoint and T31 adapter-next-action note above are historical and superseded +by the published Warden interface/validation and merged `061461e` adapter seam. +The 54 focused Warden checks establish source behavior only. Current admission +leaves tenant/realm empty; no admitted tenant/realm provenance is inferred. +T15 remains wait for actual independently authenticated owner-holder/readback +selection, independent review of the changed adapter, and native restart/lost- +receipt acceptance. Existing task metadata, owner gates and historical reviews +are unchanged. + +## T16 — Select and review the actual machine descriptor composition + +```task +id: NK-WP-0048-T16 +status: todo +depends_on: [NK-WP-0048-T24, NK-WP-0048-T25, NK-WP-0048-T26, NK-WP-0048-T27, NK-WP-0048-T28, NK-WP-0048-T29, NK-WP-0048-T30] +priority: high +state_hub_task_id: "200bf500-5084-5ae7-be26-3033fe1f296e" +``` + +2026-10-17 coordinator review r4 approved the 1.3 candidate at 1920b13 with no +findings (the-custodian `nk48-adapters-1.2-review/coordinator-review-r4.json`). The founder chose +option A of the T16 owner-review packet (`nk48-t16-owner-review/founder-option-decision.md`): +1.3 with execution descriptors for all seven steps. Owner reviews for A are open +in the-custodian `docs/changes/netkingdom-target-2026-10-17/nk48-1.3-owner-review/` (net-kingdom, +railiance-platform, railiance-apps, ops-warden, key-cape). The owner-review packet +supersedes the r1 composition pointer: T16's exact composition is now +`sso-mfa/k8s/keycape/descriptor-composition-1.3.candidate.json`. + +2026-10-17 successor candidate prepared (source only, `proposed`): +`security-case-type-revisions/1.3/oidc-browser-relying-party/` is the 1.2 package plus +an `execution_descriptors` map of the seven composed descriptors, with examples +pinned to `@1.3` and no acknowledgment. ensure-groups, hand-over, +deliver-consumer-secret and the composer accept 1.3 (835680c, merged acda26c). +`test_admit_1_3_type.py` runs Warden's real admission against the committed 1.3 type: +the proposed type is refused; an acknowledged copy is refused because ops-warden +pins parameter fingerprints only for 1.1 and 1.2; and with only a 1.3 pin (equal to +1.2's fingerprint) all seven are admitted. This is a major change under canon +section 6. Next: r4 source review of the changed adapters and the map, then owner +reviews, including the ops-warden 1.3 pin, the mint-secret warden_executes/human +question and the owner-run steps question, then the founder's Decision Memo. + +Owner: NetKingdom for descriptor/type source and exact checker artifact; Warden +independently owns global provenance/admission. T22 records the completed seven-step +source1.2 inventory and regenerated register-client descriptor using the delivered +checker and current sealed-snapshot source; T16 retains exact owner review, +complete descriptor composition and admission. Next action: bind actual native +publisher/authenticator, original/current session verification and revocation, +executable storage/custody and config pins, then obtain the six missing fixed +owner descriptors/adapters for ensure-groups, mint-secret, deliver-secret, +bind-env, deliver-consumer-secret and hand-over. The inventory identifies each +existing owner seam, exact missing contract and created-only rollback requirement; +it is not an executable bundle. T14 and T15 remain independent prerequisites. + +Review the dedicated consumer source1.2 composition only through the existing +explicit version-root selection. Keep canonical1.1/default and its accepted case +unchanged, and the source-only acknowledged1.2 reference case proposed without +acceptance fields. New descriptor bytes require a reviewed exact successor +revision/version under canon section6; they do not inherit historical source +acknowledgment. Changed authentication/materialization/trust needs the prescribed +informed-decision acknowledgment. Do not insert a partial descriptor map or use +--allow-unimplemented preparation as ordinary case/native admission. The registry +helper reports restart_required; separately review quiet-window restart/readiness +and actual consumer verification rather than infer activation from registry CAS. +Exact composition: Custodian native-registration-r1/descriptor-composition.json. +Completion requires separate owner source review of exact machine descriptors, +artifact/source/config pins, normal type/version admission and independent +evidence-verification boundaries. Warden d97652a refreshes producer validator +pins for NetKingdom 015694ee; it does not attest T13's new adapter. Pinning any +machine descriptor into an acknowledged type is a separately reviewed source +change. Platform owns structural/mint/broker/delivery evidence; Apps owns actual +consumer login/group enforcement. Neither their assent nor native acceptance is +supplied by this task. + +2026-10-16 source checkpoint: T19 below extracts the completed per-attempt +executable snapshot implementation and reviewed fixture proof. T16 retains +native executable storage/custody, exact owner evidence/session callbacks, +complete descriptor composition and ordinary type/version admission. The +successor descriptor/source pins are proposed only; the earlier descriptor +history is preserved. T14 group provenance and T15 durable restart/recovery +remain independent open requirements. No native acceptance is counted here. + +2026-10-17 budget checkpoint (net-kingdom-worker): no source change made. The +T13 registration adapter alone is 217 source and 317 test lines plus an +independent review; six further owner descriptors/adapters cannot be completed +within one per-task token budget (soft 8000 / hard 10000), so the task was +stopped below 50% and is decomposed rather than pushed through. Proposed split, +one source-candidate task each with positive/refusal fixtures, canonical1.1 +default unchanged and no --allow-unimplemented admission: (a) ensure-groups, +bound to T14's journal/provenance seam; (b) mint-secret and (c) deliver-secret, +Platform-owned evidence seams; (d) bind-env and (e) deliver-consumer-secret; +(f) hand-over, Apps-owned consumer verification; then (g) descriptor composition +over the reviewed set. Each candidate needs its owner's source review before +pinning; none is authored here as owner-accepted. Status stays todo. + +2026-10-17 task refinement after coordinator r5: completed independent mint- +guard regression coverage is extracted as T32. T33 tracks the exact Platform +issuer-store source/reference correction; T34 tracks the Apps configuration +inverse reachable without secret or hand-over resource creation. T16 retains +final exact seven-descriptor composition, independent owner reviews and admission; +its status and existing dependencies are preserved. The kernel branch also needs +its own KR1-F1/F2 admission/dispatch corrections and independent review under +WARDEN-WP-0044-T03 before any 1.3 pin. Historical r4 acceptance does not supersede +r5's findings or establish native readiness. + +## T17 — Build and exercise the exact offline config-checker artifact + +```task +id: NK-WP-0048-T17 +status: done +priority: high +state_hub_task_id: "8c3e4baa-a218-5996-af9e-fe1153a46dac" +``` + +Completed 2026-10-16, isolated artifact/source fixture proof only. Built the +existing `key-cape/src/cmd/keycape` command from published clean source revision +`3c71226a1a7b8a2bdb710e9c497e1a1f7f8a3987`, source tree +`f0010b9cab17dbc3a787628a29657d98b900320f`, with cached Go 1.23.0, +CGO disabled, Linux/amd64 v1, trimmed paths, disabled VCS stamping and offline +read-only module selection. The full retained closure pins 108 source files, +9876 toolchain files and ten used module source directories. Three isolated +build locations produced identical 13633409-byte artifacts: +`sha256:be37738fe1c08259fb4f9ca0daf0f9301bfffc237e5bb38435525dfbf11ccff5`. + +Eight disposable checks use the actual executable and maintained helper/kernel, +with complete synthetic configs and a minimal environment: add/created-only +removal, normal pinned producer admission, rollback, lost-readback uncertainty, +stale revisions, substituted checker refusal, invalid startup config and missing +secret-env refusal. No checker stub or producer-pin override remains. The +artifact-bound descriptor proposal is +`sha256:76b1153d188062ece53fcd5e8d35290fdbfd02a69dd79d4b001fc42781afcdc3`. +Evidence: Custodian +`docs/changes/netkingdom-target-2026-10-16/browser-checker-artifact-next/`. +The first two build attempts succeeded but their closure collection failed; +both failures are retained, as is refusal of the older incomplete synthetic +fixture. No production config, credentials or cluster mutation was used. + +This extracts completed build/fixture scope from T16 without claiming KeyCape +owner endorsement, native checker selection/storage custody, acknowledged type +integration, native evidence/session callbacks or attended browser acceptance. +T16 retains exact owner review and composition/admission scope. The attended +registration procedure remains unexercised. + +## T18 — Make checker input and executable selection deterministic + +```task +id: NK-WP-0048-T18 +status: done +priority: high +state_hub_task_id: "9c45da71-fb4c-5f20-9065-90f448f37c64" +``` + +Completed 2026-10-16, isolated source correction and real-artifact fixture proof +only; independent integration/publication remain separately recorded. KeyCape +source review identified non-secret `KEYCAPE_ACCOUNT_PORTAL_URL` and +`KEYCAPE_BROWSER_LOGOUT_URL` overrides in config.Load. The original helper +inherited ambient environment, allowing valid supplied configuration to be +rejected or invalid supplied URL configuration to be admitted. A bare relative +checker filename could also be replaced by PATH lookup after the adapter had +hashed the selected local file. All three counterexamples are retained against +the unchanged predecessor. + +The existing helper now resolves the selected checker file to an absolute path +and passes an explicit empty subprocess environment. Three opt-in real-checker +regressions verify both URL-verdict directions and relative-file selection under +a non-checker PATH decoy. All 39 affected checks pass with the exact supplied +artifact, as do 11 real-artifact helper/kernel scenarios. Source evidence: +Custodian `docs/changes/netkingdom-target-2026-10-16/browser-checker-environment-r2/`. +Historical descriptors remain retained; the helper correction changes successor +source-composition pins and requires their separate review/admission. An absolute +path is not immutable custody and does not eliminate file replacement races; +T16 retains native executable storage/custody and callback/type admission. +No production configuration, credentials or cluster mutation was used. + +Source integration and publication: `916ba4b33e03179274d03b78bd898a4843e8653f`, following independent +Custodian source review in browser-checker-environment-r2/coordinator-review.json. +The ordinary protected projection pass assigned T17/T18 UUIDs with zero failures +and zero automation errors; generated-only brief commit refusal is retained. +The exact prior staged/working brief bytes were restored. Final source/publication/ +projection evidence is retained in Custodian browser-checker-environment-r2/ +canonical-integration.json. These source receipts do not admit native executable +custody, owner callbacks, acknowledged type changes or browser identities. + +## T19 — Bind checker execution to verified immutable attempt bytes + +```task +id: NK-WP-0048-T19 +status: done +priority: high +state_hub_task_id: "f50fdcd1-00be-5c3c-be0c-345aa2126032" +``` + +Completed 2026-10-16, independently reviewed source implementation and synthetic +fixture proof only. The registration adapter now opens a bounded regular checker +artifact without following its terminal symlink, verifies the reviewed digest, +and snapshots the exact bytes into a write/grow/shrink/seal-removal-sealed Linux +memfd before helper access. Live and proposed config checks execute that same fd +through procfs with the existing empty environment; the fd closes on completion +or failure. Unsupported snapshot/seal/exec policy refuses through the existing +parent-session revocation and uncertainty boundaries. The earlier unbounded +pre-snapshot path read was removed during independent review; actual adapter +symlink/FIFO/oversize refusal tests verify helper access never starts. + +Evidence: Custodian `docs/changes/netkingdom-target-2026-10-16/browser-resume-r1/`, +including exact base/preimages/postimages/patch, proposed descriptor, coordinator +source review and canonical integration receipt. Independent Custodian checks +passed 49 affected source tests and 12 actual Go checker/helper/kernel scenarios; +the sets overlap and are not separate acceptance counts. Existing published +Warden d97652a producer pins remain unchanged. The exact KeyCape artifact remains +`sha256:be37738fe1c08259fb4f9ca0daf0f9301bfffc237e5bb38435525dfbf11ccff5`. +The predecessor path-replacement refusal and the first expanded reopened-fd test +failure are retained; the latter fixture was corrected to restore write mode +before independently proving seals block reopened-fd writes and shared mmap. + +This source task adds no native owner custody, callback, evidence trust, type +acknowledgment, credential access, consumer sign-in or rollback acceptance. +Executable memfds, Linux seals and usable procfs are explicit runtime dependencies +and still require native admission. This is an extracted source outcome from +T16, not completion of T16; T12/T14/T15 and all external-owner gates remain open. + + +Publication verified by the coordinator after the source worker usage limit: +`8790dd18ae83c4651ef3d1d923312a118a5b436b` is the actual remote main revision. +All five canonical source hashes match the reviewed packet. Normal synchronization +completed with zero failures/automation errors; all 19 task statuses match the +projection. T19's assigned UUID is retained. The original staged and working brief +bytes were restored and independently compared. The exact source, projection and +publication checks are in Custodian browser-resume-r1/canonical-integration.json. +Native acceptance remains open under T16 and the other named owner tasks. + + +## T20 — Prepare durable group provenance and prove disposable LLDAP compatibility + +```task +id: NK-WP-0048-T20 +status: done +priority: high +state_hub_task_id: "863fcbdb-e246-51ba-87ca-a2f44d01d6f8" +``` + +Completed 2026-10-17, source candidate preparation and bounded synthetic +compatibility proof only. This extracts evidenced implementation/fixture scope +from T14; T14 retains native storage, writer isolation, protected callback and +owner-acceptance gates. Source integration/publication and State Hub projection +are pending the coordinator's separate receipts; no revision or task UUID is +invented here. + +The existing identity-provisioner group ensure/revoke interface now has a +reviewed candidate for a bounded owner-private SQLite journal, proven only in +local/disposable use. The initial storage instructions wrongly suggested a native +persistent-volume path; source review against accepted ADR-0015 corrected them +before publication. Production SQLite-on-a-PVC is not admitted: T14 retains +CNPG/rapp-postgres storage admission or a separately governed exception. +The local journal saves +complete prestate and intent before each attempted directory mutation, inventories +only confirmed new groups/memberships, binds original ensure idempotency and +correlation to the exact request digest, and refuses unsigned caller inventories +or legacy broad revoke requests. Replays never recreate removed state. Missing, +replaced or untrusted storage and missing intent rows stop execution; definite +before-mutation refusals retain evidence without stranding unrelated valid work. +Interrupted writes/readback remain unresolved across reopening. Revoke validates +all targeted native ids and complete member postimages before destructive work, +removes only confirmed creations and never deletes a preexisting empty group. +Server/configuration wiring, image-copy closure and owner storage/request +migration instructions are included. The existing endpoint's caller +authentication remains the existing boundary, not a new grant. + +The candidate was prepared against NetKingdom +`878abc93588e12b6da1bac055cfbb3abb8d7adf1`; exact source patch digest +`sha256:1f3f3ca94c46dadac8f24896053d4d23179de795e50a24298a0cfbcc46cca30e`. +The independent Custodian review passed all 65 provisioner checks. The earlier +62-check candidate, two independent-review findings and subsequent fixes remain +retained rather than overwritten. Seven additional fixture-plan checks exercise +the preparation boundary; these counts overlap the same candidate and do not +measure task-completion percentage or independent acceptance. + +A separately reviewed bounded fixture then exercised the exact candidate against +cached pinned disposable LLDAP and Python images, through actual GraphQL. All 14 +closed compatibility/containment oracles passed: exact creation inventory, +separate-process SQLite reopening/replay/revoke, complete preexisting directory +preservation including empty/shared groups and memberships, refusal of shared +membership drift, retained uncertainty after a lost response following a real +addition, and refusal of unknown/cross-case receipt bindings. The fixture used +only newly generated synthetic users/passwords/key material in private tmpfs, +with a network-none anchor and shared loopback helper, no host ports/mounts, +no image pulls and no production or actual credential contact. The coordinator's +safe receipt reports both exact owned containers absent after cleanup; final +independent cleanup verification remains the coordinator's integration duty. + +Evidence: Custodian +`docs/changes/netkingdom-target-2026-10-16/group-provenance-r1/`, including exact +source base/pre/postimages, patch/manifest, independent review and retained +refinements. Native fixture manifest +`sha256:8a0514f5f14137355972400cd4852f48c892316d28e3abd3c86635d7cb81eef2`; +retained safe result `native-fixture/execution-result.json` (original +`/tmp/nk48-groups-native-review-5pywb19z/result.json`). The source task supplies +no native owner assent, production rollout, protected session/evidence callbacks, +ordinary type admission or consumer login proof. LLDAP has no conditional +membership mutation or generation token here: exact readback cannot exclude +ABA or a concurrent external writer, so T14 remains open. + +2026-10-17 verified source follow-through: source commit `4f65e41` passed normal +local scanning and all 65 canonical provisioner tests. The actual disposable +LLDAP receipt passed all 14 oracles; independent verification on the pinned +daemon found both exact names and the run label absent. Source integration +includes the ADR-0015 documentation correction; the journal remains local-only. + +T14 source preparation is underway. Twenty-three pinned public sources identify +NetKingdom implementation/reconciliation, Platform custody/database governance +and rapp-postgres consumer provisioning; the inspected runtime binding does not +assign identity-provisioner a package. The existing User Engine runtime token +and pod route are not a protected-case caller lane. The native proposal preserves +the same journal seam on a dedicated CNPG consumer and requires authoritative +scope binding, admitted role/lease/placement/recovery and all LDAP writers fenced. +The declared exclusive maintenance window is not mechanical exclusion of WebUI, +helper or lifecycle writers. Capacity placement and EUR cost/risk remain owner +decisions. No package, database, credential, rollout or owner assent was created. +Custodian evidence: group-provenance-r1/native-storage-proposal-r1; final source, +projection and cleanup review: resume-integration-r1/publication-verification.json. + + +## T21 — Implement the PostgreSQL journal and prove disposable SQL compatibility + +```task +id: NK-WP-0048-T21 +status: done +depends_on: [NK-WP-0048-T20] +priority: high +state_hub_task_id: "afe78e4d-408a-516a-ab92-e212e8a2d11d" +``` + +Completed 2026-10-17: bounded source preparation and actual disposable PostgreSQL +compatibility only. T14 retains native storage, custody, callbacks, authorization, +all-writer isolation and production admission. Coordinator source publication and +projection remain separate follow-through; no source revision or task UUID is +invented here. T20 remains the historical local SQLite/actual LLDAP proof. + +The eight-path source candidate adds an injection-only PostgreSQL implementation +behind the existing owner journal seam, with explicit trusted tenant/directory/ +case/source/principal/epoch binding and independent continuity callbacks. It +persists bounded intent/created-only receipts, exact-one version updates, durable +active fences, and a content-bound journal chain. SQL commits precede independent +exact-marker acknowledgment; lost replies/checkpoints poison continuation and +retain uncertainty. A counter-only checkpoint is explicitly insufficient for +divergent clones. Migration does not create database/schema/credential, admit an +epoch or clear quarantine; production HTTP activation is absent pending trusted +callbacks. Pinned psycopg package/image-copy closure is included, without claiming +an image build or native dependency admission. + +Candidate base `6cb43c0cc55212363a5406b02db046e2384a7c40`; final eight-path patch +`sha256:5549d2982b61ff70aba4b3928fecd5639db0f1a732ab2f8630f678e7acf4d34d`. +All 82 source tests pass, alongside 13 fixture plan/diagnostic checks and two +source-backed snapshot regressions. Counts overlap the same source and fixture; +they are not additive native acceptance or completion percentages. + +The independently reviewed R5 fixture ran the actual SQL migration/backend and +existing provisioner path against cached pinned disposable PostgreSQL. Directory +operations deliberately used the existing synthetic LLDAP test double. All +manifest-declared phase oracles passed: creation-only inventory and DDL refusal; +separate-process replay/revoke with complete preexisting/shared visible-state +preservation; foreign-context refusal; concurrent exactly-once reservation and +stale version refusal; actual database fork with divergent bodies at the same +counter and fresh-object rejection of the losing exact checkpoint; retained +uncertainty after checkpoint/directory/SQL response loss; and actual pg_dump/ +pg_restore rollback refusal against the independently retained fixture checkpoint. +The checkpoint is fixture-only, not an admitted native owner evidence holder. + +Only generated credentials/state were used, with two nonroot/read-only containers +on private loopback, bounded tmpfs/CPU/memory/PIDs/lifetimes, no host ports/mounts, +no pulls and no production contact. The safe receipt reports both exact owned +containers absent, with no daemon uncertainty. Independent final pinned-daemon +cleanup verification remains coordinator follow-through. + +Failed R2/R3/R4 receipts and source revisions remain retained. R2's helper failure +subphase is unknown; R3 exposed a real backend bug where replacing nested receipt +objects lost the created ID held by ensure, fixed by acknowledging only the backend +version after commit/checkpoint. R4 verified that fix through actual SQL bootstrap, +then exposed fixture snapshot aliasing and empty deleted-group backing entries; +source-backed regressions corrected the snapshot without weakening complete +visible-prestate equality, and reject nonempty orphan membership. These refinements +belong to one trial, not extra native acceptance successes. + +Custodian evidence: `docs/changes/netkingdom-target-2026-10-16/group-postgres-r1/`; +exact source/pre/postimages and retained predecessors; native R5 manifest +`sha256:d51f90816616848b93bcdc482e10a98d07061deb01eeed9e50700fc6e68b6051`; +`final-integration-r1/typed-execution-evidence.json` and byte-exact safe receipt +(original `/tmp/nk48-postgres-native-review-sguj7t8p/result.json`). No CNPG native +storage/HA/backup/lease/TLS/capacity/restore admission, authoritative protected +callbacks, real LDAP writer fencing/CAS/ABA exclusion, owner assent or production +endpoint activation is claimed. Those remain live T14 work. + + +## T22 — Establish registration recovery source gaps and complete descriptor review inventory + +```task +id: NK-WP-0048-T22 +status: done +priority: high +state_hub_task_id: "ea50d93b-0d04-55d2-98c4-9b6c3fe925b1" +``` + +Completed 2026-10-17: source-only owner-contract review and complete review +inventory. Inspected the actual registration adapter, Warden Journal/Executor, +NetKingdom evidence compatibility checker and Audit Core custody/read surfaces. +No existing admitted holder/readback supplies authenticated original-attempt +recovery: payload/creation are process-local, persisted evidence references are +hashed, and reconcile receives a new attempt without original context. The exact +four-owner contract requirements and non-replayable missing/ambiguous cases are +recorded; no recovery callback, publisher, credential grant or native trust root +was installed. T15 retains implementation and actual acceptance. + +The complete source1.2 inventory maps all seven follow-through steps to their +existing owners, missing exact contracts and created-only rollback requirements. +The actual maintained adapter regenerated the sole existing register-client +machine descriptor against the delivered 13,633,409-byte checker and current +sealed-memfd snapshot source. The checker rehash and Warden Descriptor.decode +round-trip passed; six owner descriptors remain absent. Actual source commands +render canonical1.1 and explicitly selected1.2 with execution.permitted false; +ordinary1.2 case admission still refuses its acknowledged/unimplemented type. +Canonical type/case/acknowledgment bytes were hash-checked unchanged. No tests were +added or rerun, no native configuration or effect occurred, and no owner assent +or complete machine bundle is claimed. T16 retains native executable custody, +complete exact descriptors, independent source review and normal version/type +admission. This extracted outcome supplies no native gate completion credit. + +Evidence in the-custodian: +`docs/changes/netkingdom-target-2026-10-17/native-registration-r1/`, especially +original-attempt-review.json, descriptor-composition.json, source-verification.json, +source-manifest.json and manifest.json. The prior sixteen-gate audit remains +immutable. Publication, normal task UUID allocation and projection are separate +coordinator follow-through; no Hub UUID is assigned by this source record. + + +## T23 — Review native journal package assignment and storage integration boundaries + +```task +id: NK-WP-0048-T23 +status: done +priority: high +state_hub_task_id: "6e7cd483-c7af-53a2-9d0a-3d76b6fb49ee" +``` + +Completed 2026-10-17: source/package placement and protected integration review +only, separate from T21's completed PostgreSQL source and disposable-SQL proof. +The owner review recommends a dedicated independently rolled-back +`rapp-identity-provisioner`, owned by NetKingdom on rail-kubernetes, with a +schema-valid draft declaration and no reef binding. No repository, registry +entry, native package assignment or owner assent was created. Existing +rapp-user-engine ownership/token delivery does not assign it the provisioner. + +The complete current rapp-postgres catalog has six declared consumers: four on +platform-pg and two on platform-pg-2. The inert renderer-valid provisioner +candidate fits platform-pg-2 at three of four, requesting one migration plus +four runtime connections; placing it on the first cell is refused as a fifth. +Normative package schema and full-catalog capacity/render checks passed. The +four-consumer ceiling is estimated, not measured native headroom, and no current +SQL session, backup or restore acceptance is claimed. + +The exact review identifies remaining package/substrate, SQL role/lease/TLS, +network and backup/recovery contracts; independently authenticated protected +scope callbacks; an external content-bound checkpoint holder with atomic +advance/loss handling; and all-LDAP-writer exclusion or provider CAS/generation, +including lifecycle cascades and remove/readd ABA. Generic Audit custody is not +that checkpoint service. A database-only fence, final reread or one replica +cannot exclude foreign directory writers. The native acceptance/recovery matrix +keeps storage/lease admission separate from separately approved directory +mutation; the production activation remains disabled pending trusted integration. + +Evidence in the-custodian: +`docs/changes/netkingdom-target-2026-10-17/native-storage-r1/OWNER-REVIEW.md`, +source-validation.json and hash-bound source-references.json, plus the inert +proposed declaration/consumer and renderer outputs. No native resource, lease, +credential, deployment, destructive effect, independent holder or native owner +acceptance was delivered. T14 retains these live gates with its original UUID +and progress status. This completed review adds no duplicate T21 acceptance +credit; publication, task UUID allocation and projection remain coordinator +follow-through and no UUID is hand-assigned here. + +## T24 — Ensure-groups adapter source candidate + +```task +id: NK-WP-0048-T24 +status: done +priority: high +state_hub_task_id: "1a3931ee-3b5f-52fc-9295-4073804f4bd2" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): ensure-groups-adapter.py, 8 tests (commit 5a2e7d6). Binds to the T14 PostgreSQL journal and refuses SQLite or a foreign binding. It accepts only canonical 1.1 cases, so T30 must add an explicit 1.2 path. It calls the provisioner's private `_group_binding`; a public helper is the cleaner shared edit. + +Split from T16 on 2026-10-17 so each candidate fits one per-task budget. Bind group creation to T14's journal/provenance seam; refuse without trusted journal callbacks. +Source candidate only, modelled on the T13 registration adapter +(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and +`registration-adapter.md`). Requires positive and refusal fixtures and +created-only rollback. Keeps canonical 1.1 as the default and uses no +`--allow-unimplemented` admission. Done means the candidate and its tests are +committed. NetKingdom (directory) owner source review before pinning, and any native action, +stay separate gates and are not supplied by this task. + +## T25 — Mint-secret adapter source candidate + +```task +id: NK-WP-0048-T25 +status: done +priority: high +state_hub_task_id: "0de2a7d9-874d-578b-a076-f653fe42e30b" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): mint-secret-adapter.py, 9 tests (commit 558c5bc). Value-free receipts only. The receipt schema `railiance-platform.custody-mint.v1` and the mint/destroy capability interface are proposals that Platform must confirm. + +Split from T16 on 2026-10-17 so each candidate fits one per-task budget. Platform-owned custody evidence seam: the adapter records value-free mint evidence only and never handles secret values. +Source candidate only, modelled on the T13 registration adapter +(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and +`registration-adapter.md`). Requires positive and refusal fixtures and +created-only rollback. Keeps canonical 1.1 as the default and uses no +`--allow-unimplemented` admission. Done means the candidate and its tests are +committed. Platform owner source review before pinning, and any native action, +stay separate gates and are not supplied by this task. + +## T26 — Deliver-secret adapter source candidate + +```task +id: NK-WP-0048-T26 +status: done +priority: high +state_hub_task_id: "94b3cbf0-c908-5ac6-b0db-67385c3e021c" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): deliver-secret-adapter.py, 8 tests (commit 1b60f6e). The lane `platform-secret-delivery` and the schema `netkingdom.deliver-secret.v1` are proposals that Platform must confirm. + +Split from T16 on 2026-10-17 so each candidate fits one per-task budget. Platform-owned delivery evidence seam (ESO/custody), value-free. +Source candidate only, modelled on the T13 registration adapter +(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and +`registration-adapter.md`). Requires positive and refusal fixtures and +created-only rollback. Keeps canonical 1.1 as the default and uses no +`--allow-unimplemented` admission. Done means the candidate and its tests are +committed. Platform owner source review before pinning, and any native action, +stay separate gates and are not supplied by this task. + +## T27 — Bind-env adapter source candidate + +```task +id: NK-WP-0048-T27 +status: done +priority: high +state_hub_task_id: "e56365f8-29b5-5c89-a329-8115f9f3b2a7" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): bind-env-adapter.py, 5 tests (commit 6fbce73). Binds references only. To fit the budget it does not yet wrap a full Warden `Adapter` like T13 (source-digest pin, authenticated evidence, parent-session revocation); it returns `(reference, run, rollback)`. That wrapping is a prerequisite for T30. + +Split from T16 on 2026-10-17 so each candidate fits one per-task budget. Bind the issuer Deployment env reference to the delivered secret reference, without values. +Source candidate only, modelled on the T13 registration adapter +(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and +`registration-adapter.md`). Requires positive and refusal fixtures and +created-only rollback. Keeps canonical 1.1 as the default and uses no +`--allow-unimplemented` admission. Done means the candidate and its tests are +committed. NetKingdom runtime owner source review before pinning, and any native action, +stay separate gates and are not supplied by this task. + +## T28 — Deliver-consumer-secret adapter source candidate + +```task +id: NK-WP-0048-T28 +status: done +priority: high +state_hub_task_id: "6dab281c-ea9a-5a17-a83f-e8e9a4d4484f" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): deliver-consumer-secret-adapter.py, 5 tests (commit 650c1ac). Type 1.2 only. It assumes the owner has validated ESO TokenRequest and the controller-RBAC admission; that stays an owner gate. + +Split from T16 on 2026-10-17 so each candidate fits one per-task budget. Consumer-side delivery evidence seam, value-free. +Source candidate only, modelled on the T13 registration adapter +(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and +`registration-adapter.md`). Requires positive and refusal fixtures and +created-only rollback. Keeps canonical 1.1 as the default and uses no +`--allow-unimplemented` admission. Done means the candidate and its tests are +committed. Platform and Apps owner source review before pinning, and any native action, +stay separate gates and are not supplied by this task. + +## T29 — Hand-over adapter source candidate + +```task +id: NK-WP-0048-T29 +status: done +priority: high +state_hub_task_id: "129db41d-4560-5e20-9f3b-c3c91e0a5601" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): handover-adapter.py, 8 tests (commit ec63539). The owner `apps` and lane `apps-consumer-handover` are proposals that the Apps owner must confirm. It has no reconcile path; restart handling follows T15. + +Split from T16 on 2026-10-17 so each candidate fits one per-task budget. Apps-owned consumer verification and hand-over receipt seam. +Source candidate only, modelled on the T13 registration adapter +(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and +`registration-adapter.md`). Requires positive and refusal fixtures and +created-only rollback. Keeps canonical 1.1 as the default and uses no +`--allow-unimplemented` admission. Done means the candidate and its tests are +committed. Apps owner source review before pinning, and any native action, +stay separate gates and are not supplied by this task. + +## T30 — Compose the descriptor set over the reviewed candidates + +```task +id: NK-WP-0048-T30 +status: done +depends_on: [NK-WP-0048-T24, NK-WP-0048-T25, NK-WP-0048-T26, NK-WP-0048-T27, NK-WP-0048-T28, NK-WP-0048-T29] +priority: high +state_hub_task_id: "6f6073ac-52dd-5b70-8874-6d76d4c072e4" +``` + +2026-10-17 done: coordinator review r3 (the-custodian `nk48-adapters-1.2-review/coordinator-review-r3.json`, +fd07eaa) approved all nine files at ff8f597, with R2-F1..F3 resolved, an independent +reconstruction of 117 passing and no open findings. This is a source review only: +it grants no owner admission, pin or native authority. Owner review and admission +continue under T16, which also takes the r2 observations, including the +`warden_executes: false` steps that carry execution descriptors. + +2026-10-17 coordinator review r2 (the-custodian `nk48-adapters-1.2-review/coordinator-review-r2.json`) +approved five files and rejected three, plus the artifact. The fixes are merged in +2ee4ed0: R2-F1, mint-secret revokes the session and the broker grant +independently (eeed8e1); R2-F2, deliver-secret binds to the admitted case and +derives its Secret and custody path, with no caller reference (640292c); R2-F3, +bind-env pins its five-file closure, and a new closure-completeness test checks +every adapter (d16db30). Each regression test failed on 68f4bbb. The artifact is +regenerated, and the full suite including kernel admission passed 117. Next is +the r3 review. Open for owner review under T16: the type marks four steps +`warden_executes: false`, yet each has an execution descriptor. + +2026-10-17 founder confirmed every proposed lane and schema name for T25-T29 +as-is. The artifact records them as `names_confirmed` (by founder, 2026-10-17), +and no step is still `owner_confirmation_required`. Next: independent source +review of the seven adapters. + +2026-10-17 founder decision (supersedes the `agent` decision below): the +mint-secret actor is `human`. Coordinator review r1 (the-custodian +`docs/changes/netkingdom-target-2026-10-17/nk48-adapters-1.2-review/coordinator-review-r1.json`, +finding NK48-R1-F1) showed that unchanged kernel a45cf47 refuses every +`effect: mint` admission whose actor is not `human` ("agent mint is +forbidden"), so the `agent` descriptor could never be admitted. Adapter, tests +and composer comment updated and the artifact regenerated (mint-secret +descriptor digest now `sha256:8c40c2884f8ba15e8cb6c98f33419c395e54fce8ef33d6e1a34782cf3f1fcfa9`); +98 tests pass against a45cf47. The set needs an independent r2 review. + +2026-10-17 `test_admit_1_2_adapter_descriptors.py` runs Warden's own +`Executor.admit` on all seven composed 1.2 descriptors, with the real 1.2 +type, the 1.2 forgejo example case, the reviewed `BrowserInputs` validator and +fixture-only trust (stand-in adapters, nothing dispatched). It also pins that +an agent mint descriptor is refused ("agent mint is forbidden") and that a +wrong lane is refused for every step. Against the old `agent` revision +(582ab9f) it fails as expected. Combined suite: 102 passed on ops-warden a45cf47 +and on current ops-warden. Fixture admission is not owner admission or a pin. + +2026-10-17 founder decision: the mint-secret actor is `agent`, not `warden`. +The kernel is unchanged. The adapter, its test and the composer were updated and +the artifact regenerated; all seven steps pass `Descriptor.decode` and +`complete_executable_descriptor_set` is true (98 tests passed). The artifact +stays `not_an_admission`, with no owner review, admission or dispatch permission. + +2026-10-17 composer merged (60b41a1 via 3c62bec) with follow-up 376b44b (merged; +combined suite 98 passed). `compose-1.2-descriptors.py` loads 1.2 only through an +explicit version root and refuses partial, duplicate, unknown or mismatched maps. +`descriptor-composition-1.2.candidate.json` is a reviewable candidate, marked +`not_an_admission`, with canonical 1.1 unchanged. ensure-groups and hand-over now +carry and honour `parent-session` revocation, and the hand-over owner is +parameterised (`railiance-apps` for Forgejo). Six of the seven steps pass +`Descriptor.decode`. mint-secret is still refused: its actor `warden` is outside +the kernel's human/agent set, and Platform (with ops-warden, if the kernel should +change) must decide. Five steps carry `owner_confirmation_required` names. The +set is complete only after both. Owner review, the canon section 6 successor +revision and admission remain T16's. + +2026-10-17 prerequisites merged (65559ef; combined suite 78 passed): +ensure-groups accepts 1.2 via an explicit `version_root='1.2'` (531151b), with +1.1 still the default. bind-env is now a full Warden v1 `Adapter` (7442d21); its +kernel-level fixture tests like T13's are still missing, and its `build()` +signature changed. Composition is unblocked in source. Owner confirmation of +the proposed step, lane and schema names remains before pinning. + +2026-10-17 inputs from T24–T29 that composition must resolve: T24 accepts +only 1.1 while T28 accepts only 1.2, so add an explicit 1.2 ensure-groups path; +T27 still needs full Warden `Adapter` wrapping; the lane, schema and owner names +proposed in T25, T26 and T29 need owner confirmation before pinning. + +Split from T16 on 2026-10-17. Compose the seven-step source 1.2 descriptor set +through the existing explicit version-root selection over T13 and T24–T29. No +partial descriptor map, canonical 1.1 stays the default, and new descriptor bytes +need a reviewed successor revision under canon section 6. Owner review and +admission remain T16's. + +## T31 — Restart and lost-receipt fixtures against Warden's original-intent interface + +```task +id: NK-WP-0048-T31 +status: done +priority: high +state_hub_task_id: "ed1ccd87-89a0-5e69-885c-0eaf5ab0bb5a" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): test_registration_original_intent_reconcile.py, 8 tests (commit 74adcd7), runs against the unmodified ops-warden a45cf47. The holder is a test-only in-memory store. For real T15 support the T13 adapter's `build()` needs an injected holder/readback capability, a `reconcile_original` callback, and a `verify` that accepts reconcile results. Rollback after restart stays refused until there is an owner contract. + +Split from T15 on 2026-10-17. ops-warden published the original-intent +lookup/context interface in `src/warden/case_run.py` (`OriginalIntent`, +`Journal.original_intent`, `Adapter.reconcile_original`; commit `a45cf47`). +Build T15's restart/lost-receipt fixtures for the registration adapter against +it: the original intent is kept separate from the reconcile attempt; wrong +attempt/case/source/principal and unavailable-holder cases refuse; and a missing +or ambiguous record stays uncertain, with no add replay or undo. The interface +is Warden's own dispatch record, not owner-authenticated created-only provenance. +Selecting the actual authenticated holder stays a T15 owner gate. + + +## T32 — Restore independent agent-mint admission regression coverage + +```task +id: NK-WP-0048-T32 +status: done +priority: high +state_hub_task_id: "c6d0a27e-f6a7-5df0-95f6-3e7473d38bb5" +``` + +Completed 2026-10-17, source tests only. Extracted from T16's coordinator r5 +finding NK48-R5-F1; no earlier adapter outcome is counted again. The real 1.3 +admission test now separately checks the committed descriptor map, an in-memory +fixture map acknowledging the changed agent descriptor to reach the independent +mint prohibition, and the human descriptor under an explicit fixture agent +runtime marker. Only `sso-mfa/k8s/keycape/test_admit_1_3_type.py` changes. + +Against frozen Warden `117fec9241e7b985dfb187b992e173d0a5dc3700`, the new +suite passes 10 tests / 32 subtests. Independent mutation evidence removes only +the mint admission guard in scratch: both new guard tests fail while eight +controls pass; the old suite passed that mutant. Custodian independently reran +the canonical successor: 10 tests / 32 subtests pass. Evidence: +`the-custodian/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/` +(patch, frozen/mutant results and canonical-validation.json). No descriptor, +parameter pin, catalog, acknowledgment, kernel or native execution changed. +T16 still owns actual composition and admission; F2/F3 follow below. + +## T33 — Close the issuer-store reference finding with Platform source + +```task +id: NK-WP-0048-T33 +status: todo +priority: high +state_hub_task_id: "5ace28ff-a4cb-5647-a8f6-3cd54bb9d0a5" +``` + +Owner: Platform for the fixed store/template source and its delivery boundary; +NetKingdom for the 1.3 type reference. Split from T16's current coordinator r5 +finding NK48-R5-F2. Next action: independently review the prepared Platform +candidate and publish a concrete owner-controlled issuer store kind/name/scope +and template reference, then integrate only that exact proposed source into the +1.3 deliver-secret entry point and regenerate its composition artifact. Candidate +and current review evidence are under Custodian +`docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/` +and `nk48-r5-f2-review-r1/`. + +Completion requires exact committed source, a passing independent source review +and unchanged type parameter bytes. No consumer store may be inferred as issuer +store, and no source declaration supplies owner assent, installed ESO support, +CCR/grant changes or a real delivery capability. Those remain existing +RPF-1.3-T4 and T16 admission/native requirements. + +## T34 — Close the consumer configuration inverse finding + +```task +id: NK-WP-0048-T34 +status: todo +priority: high +state_hub_task_id: "37f43c40-d014-544e-aa0b-5219dd4a6b93" +``` + +Owner: Apps for consumer configuration and inverse; NetKingdom for exact type +placement. Split from T16's coordinator r5 finding NK48-R5-F3. Next action: +independently review and implement the owner transaction/inverse through the +existing RAPPS-WP-0016-T05 path, with authenticated durable before/after release +provenance, exclusive serialization of all release writers and a fresh authorized +attended session for every inverse attempt after the hand-over session is revoked. +The inverse must remain reachable for public clients, confidential NO_CHANGE +secret delivery and verification failure before a hand-over receipt exists. + +Completion requires real owner configuration/inverse source bindings and +independent regressions, then exact reviewed 1.3 entry/rollback text and composed +artifact. A synthetic wrapper with an injected backend alone is insufficient. +Keep hand-over receipt rollback receipt-only and secret deletion created-only; +preexisting or drifted resources must refuse unsafe inverse. Candidate evidence: +Custodian `docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/` +and `nk48-r5-f3-review-r1/`. Source review, owner acknowledgment and native proof +remain distinct. T16's final composition/admission and T06's native first case +are not completed by this refinement. + +## T35 — Align source admission fixtures with the authenticated catalog and attended-session API + +```task +id: NK-WP-0048-T35 +status: done +priority: high +state_hub_task_id: "cf1d6ab3-846e-53ea-8575-d6347b6f4769" +``` + +Completed 2026-10-17, source fixture compatibility only. Distinct from T32's +independent agent-mint regression repair: both remaining opaque catalog byte +placeholders now use one well-formed synthetic catalog whose exact bytes are +bound by the fixture evidence verifier. The fixture mirrors the six relevant +Warden lane shapes; four draft statuses are hypothetically active, while both +Apps establishing login references remain unresolved. No real catalog entry, +owner login choice or production parameter pin changes. + +Against the reviewed mode-aware Warden source now integrated at +`ae7bc73b4f4bac22454144959b870d161498e0af`, scratch validation passes 31 tests +and 53 subtests. Five resolvable 1.3 descriptors conditionally admit under +fixture-only acknowledgment and pinning; both Apps attended steps refuse before +execution. Changed catalog bytes refuse, all independent mint-guard tests still +reach their intended guard, and the registration fixture's actual kernel +run/rollback and uncertain-result paths pass with synthetic owner callbacks. +The registration fixture explicitly aligns its in-memory proposed type lane +with its current descriptor; canonical 1.1 is unchanged. + +The same tests against predecessor `117fec9241e7b985dfb187b992e173d0a5dc3700` +pass 31 tests and 51 subtests, with only the two new Apps refusal assertions +failing because that predecessor lacks the attended-session catalog check. +Tests target the reviewed current API; the parser or Apps gate is not weakened +for predecessor compatibility. Evidence and root source review: +`the-custodian/docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-update-r1/` +and `nk48-catalog-fixture-publication-r1/`. Canonical validation/publication +receipts belong in the latter packet when performed. This records source +fixture work, not native T03, owner assent, exact-type acknowledgment, PIN-1, +composition/admission completion under T16 or the native first case under T06. +T33 and T34 retain their existing issuer-store and consumer-inverse work. diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/README.md b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/README.md new file mode 100644 index 0000000..597dad8 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/README.md @@ -0,0 +1,11 @@ +# NK48-R5-F1 successor coverage, prepared only + +The patch changes only `sso-mfa/k8s/keycape/test_admit_1_3_type.py`; it has not been applied to the owner checkout, committed or published. Source base: `3236d5000e52e0643f33906a26a7825aa0b67465`. Kernel dependency: frozen `117fec9241e7b985dfb187b992e173d0a5dc3700`. + +The committed-map refusal now explicitly includes an agent-actor mint descriptor. A separate fixture-map test acknowledges precisely that changed descriptor digest, allowing admission to reach the independent `agent mint is forbidden` guard. The human descriptor is also tested with an explicit fixture `WARDEN_AGENT_ID`; the helper sets it for that call instead of clearing it. Existing changed-caller coverage remains. + +Focused results: original tests pass against both frozen and scratch guard-removed kernels (8 tests / 31 subtests each). Successor tests pass against the real frozen kernel (10 tests / 32 subtests). Removing only its admission agent-mint guard in a separate scratch kernel makes exactly both new guard tests fail (2 failed, 8 passed / 32 subtests). The dispatch-time guard remains present in the mutant; no adapter is dispatched. + +`test-admit-1-3-agent-mint-coverage.patch` is the proposed owner patch. `scratch-kernel-mutation.patch` is negative-control evidence only and MUST NOT be applied to any canonical owner source. Preimage, successor, exact commands/results and hashes are retained beside this note. The runner requires the exported scratch trees named in scratch.json; it refuses a mismatched guard and modifies only those scratch trees. + +These are fixture admission checks. Type acknowledgment, the 1.3 parameter pin and the changed descriptor-map entry exist only in memory inside tests. No actual type acknowledgment, catalog/pin change, kernel acceptance, native execution or grant is established. R5 F2/F3 and owner/kernel follow-through are unchanged. Root review/integration is the next step; no canonical task refinement is authored in this preparation-only packet. diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/canonical-application.json b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/canonical-application.json new file mode 100644 index 0000000..ad0abb5 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/canonical-application.json @@ -0,0 +1,8 @@ +{ + "status": "applied_test_only", + "path": "/home/worsch/net-kingdom/sso-mfa/k8s/keycape/test_admit_1_3_type.py", + "preimage_sha256": "sha256:f4f6a35cfbada462c939fe357e51fa5d03efc9d20c17a511f224c1359ca0faa5", + "after_sha256": "sha256:c512ba00513d066fd1ca8af08fd6bf106fbd7a132981b1dcc5af80a745ab03f0", + "native_actions": 0, + "kernel_or_type_pin_changes": 0 +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/canonical-net-kingdom-status-after.txt b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/canonical-net-kingdom-status-after.txt new file mode 100644 index 0000000..8f7c1ab --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/canonical-net-kingdom-status-after.txt @@ -0,0 +1,6 @@ + M .custodian-brief.md + M WORK-RECORDS.md +?? docs/evidence/2026-10-15-cadence-adoption/hourly-observation-capture/ +?? docs/evidence/2026-10-15-cadence-adoption/hourly-observation-inventory.jsonl +?? docs/evidence/2026-10-15-cadence-adoption/hourly-observation-monitor.json +?? intakes/NET-IN-0005-single-use-setup-link-mail-handoff.md diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/canonical-validation.json b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/canonical-validation.json new file mode 100644 index 0000000..4619718 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/canonical-validation.json @@ -0,0 +1,8 @@ +{ + "command": "env PYTHONDONTWRITEBYTECODE=1 PYTHONPATH=/home/worsch/ops-warden/src /home/worsch/ops-warden/.venv/bin/python -B -m pytest -q -p no:cacheprovider test_admit_1_3_type.py", + "working_directory": "/home/worsch/net-kingdom/sso-mfa/k8s/keycape", + "result": "10 passed, 32 subtests passed in 5.05s", + "exit_code": 0, + "source_sha256": "c512ba00513d066fd1ca8af08fd6bf106fbd7a132981b1dcc5af80a745ab03f0", + "publication_status": "not committed; remaining source implementation work retained" +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/implementation.json b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/implementation.json new file mode 100644 index 0000000..cef00a9 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/implementation.json @@ -0,0 +1,112 @@ +{ + "schema": "custodian.nk48-r5-f1-source-implementation.v1", + "status": "prepared-tested-not-applied", + "finding": "NK48-R5-F1", + "source_base": "3236d5000e52e0643f33906a26a7825aa0b67465", + "reviewed_code_base": "001a38ca2aa85155ce4840c4da22aca6e2346171", + "kernel_base": "117fec9241e7b985dfb187b992e173d0a5dc3700", + "target": "sso-mfa/k8s/keycape/test_admit_1_3_type.py", + "preimage_sha256": "f4f6a35cfbada462c939fe357e51fa5d03efc9d20c17a511f224c1359ca0faa5", + "successor_sha256": "c512ba00513d066fd1ca8af08fd6bf106fbd7a132981b1dcc5af80a745ab03f0", + "frozen_r5_review_sha256": "e3016352c58178894ccbfc600b960d3f9e2b5d534a05b8df2c5b79737cb0127a", + "canonical_test_still_matches_preimage": true, + "canonical_edits": false, + "canonical_commit_or_publish": false, + "kernel_mutation_scratch_only": true, + "mutant_changed_files": [ + "src/warden/case_run.py" + ], + "native_actions": false, + "grants": false, + "knowledge_orientation": { + "package": "0.2.6", + "digest": "8d1d6a9d4d07fec7b619618de7dfe2254c1790f85f4198c7fe19db9751c02", + "cards": { + "start": "withheld-source-changed", + "identity": "withheld-source-changed" + }, + "direct_source_review": [ + "net-kingdom/AGENTS.md", + "net-kingdom/INTENT.md", + "net-kingdom/SECURITY-COMPANION.md", + "ops-warden/AGENTS.md", + "ops-warden/INTENT.md", + "ops-warden frozen case_run admission guard" + ] + }, + "scope": "Only successor test_admit_1_3_type.py coverage. Fixture type status, map and parameter pin are memory-only test setup. No acknowledgment, pin, catalog, canonical kernel, native capability or owner acceptance change. F2/F3 and independent kernel/catalog/owner reviews remain open.", + "results": [ + { + "name": "preimage-frozen", + "command": [ + "/home/worsch/ops-warden/.venv/bin/python", + "-m", + "pytest", + "-q", + "-p", + "no:cacheprovider", + "test_admit_1_3_type.py" + ], + "cwd": "/tmp/nk48-r5-f1-ahudwokt/net-kingdom/sso-mfa/k8s/keycape", + "pythonpath": "/tmp/nk48-r5-f1-ahudwokt/warden-frozen/src", + "test_sha256": "f4f6a35cfbada462c939fe357e51fa5d03efc9d20c17a511f224c1359ca0faa5", + "kernel_case_run_sha256": "77e8117a1fb4e039815b555797778f7180f11e30f64cebe51b350b3eb7e46768", + "exit_code": 0, + "expected_exit_code": 0 + }, + { + "name": "preimage-mutant", + "command": [ + "/home/worsch/ops-warden/.venv/bin/python", + "-m", + "pytest", + "-q", + "-p", + "no:cacheprovider", + "test_admit_1_3_type.py" + ], + "cwd": "/tmp/nk48-r5-f1-ahudwokt/net-kingdom/sso-mfa/k8s/keycape", + "pythonpath": "/tmp/nk48-r5-f1-ahudwokt/warden-admission-guard-removed/src", + "test_sha256": "f4f6a35cfbada462c939fe357e51fa5d03efc9d20c17a511f224c1359ca0faa5", + "kernel_case_run_sha256": "6b0d61fb0da4737f305bb959524540752fbcf0f2993a7969bd7e368bdd03ea5a", + "exit_code": 0, + "expected_exit_code": 0 + }, + { + "name": "successor-frozen", + "command": [ + "/home/worsch/ops-warden/.venv/bin/python", + "-m", + "pytest", + "-q", + "-p", + "no:cacheprovider", + "test_admit_1_3_type.py" + ], + "cwd": "/tmp/nk48-r5-f1-ahudwokt/net-kingdom/sso-mfa/k8s/keycape", + "pythonpath": "/tmp/nk48-r5-f1-ahudwokt/warden-frozen/src", + "test_sha256": "c512ba00513d066fd1ca8af08fd6bf106fbd7a132981b1dcc5af80a745ab03f0", + "kernel_case_run_sha256": "77e8117a1fb4e039815b555797778f7180f11e30f64cebe51b350b3eb7e46768", + "exit_code": 0, + "expected_exit_code": 0 + }, + { + "name": "successor-mutant", + "command": [ + "/home/worsch/ops-warden/.venv/bin/python", + "-m", + "pytest", + "-q", + "-p", + "no:cacheprovider", + "test_admit_1_3_type.py" + ], + "cwd": "/tmp/nk48-r5-f1-ahudwokt/net-kingdom/sso-mfa/k8s/keycape", + "pythonpath": "/tmp/nk48-r5-f1-ahudwokt/warden-admission-guard-removed/src", + "test_sha256": "c512ba00513d066fd1ca8af08fd6bf106fbd7a132981b1dcc5af80a745ab03f0", + "kernel_case_run_sha256": "6b0d61fb0da4737f305bb959524540752fbcf0f2993a7969bd7e368bdd03ea5a", + "exit_code": 1, + "expected_exit_code": 1 + } + ] +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/manifest.json b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/manifest.json new file mode 100644 index 0000000..cd3a3c0 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/manifest.json @@ -0,0 +1,19 @@ +{ + "status": "prepared-tested-not-applied", + "files": { + "implementation.json": "33d51784fe3ab7d9307292a265b0e684a8bc2fc77564ba58099833e41c849306", + "reproduce-scratch-checks.py": "8f0b01e606c03b3b27b178df60eab664a758df6fa3f9b1bc62ef8b58122c2cd8", + "successor-mutant.txt": "9f03083de5863b3855e533b94671cfbab6fd0dd565e0a0b8c385d51d9510d97c", + "README.md": "78a4dbef0088f18db2b9c1dbe701b3e8a2c20516ae6d2039420a82b8d6b1fe66", + "preimage-frozen.txt": "63dd3d754896c0e8578d82f98ae6f8e460a6ac43f3937a95fa61f65f8aa4accf", + "test_admit_1_3_type.after.py": "c512ba00513d066fd1ca8af08fd6bf106fbd7a132981b1dcc5af80a745ab03f0", + "scratch.json": "7e329154c5c60528d9f4387f0bbc9cfb4167c79decd49b14497e67d59e21e469", + "successor-frozen.txt": "275324dcf33eb852eb467832ff18fe9dd09f09d64d6ac37b43bf01766ea745d3", + "canonical-net-kingdom-status-after.txt": "354b3dd65c711df0855c22ba053a4ca295d5491941c5a64c1157a0b7d43cf4cb", + "scratch-kernel-mutation.patch": "dd12c63bd331b8f127149c402077e49fc66d62a452f48044486f58e79e0e29f7", + "test-results.json": "2aab986ed96fb9fa683992051ffe6ec7b2a9327d816542b908dcc66f9dd8df23", + "preimage-mutant.txt": "6c823ed279c355a34169d7753817256890d56fe4a1996ea59aa01fb19e5a5e1f", + "test-admit-1-3-agent-mint-coverage.patch": "5bc1da176836eea65195cfe5fae965284c1f38809a502a0be58c231b60111a75", + "test_admit_1_3_type.before.py": "f4f6a35cfbada462c939fe357e51fa5d03efc9d20c17a511f224c1359ca0faa5" + } +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/preimage-frozen.txt b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/preimage-frozen.txt new file mode 100644 index 0000000..2cb22b5 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/preimage-frozen.txt @@ -0,0 +1,2 @@ +........ [100%] +8 passed, 31 subtests passed in 4.98s diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/preimage-mutant.txt b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/preimage-mutant.txt new file mode 100644 index 0000000..708c86a --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/preimage-mutant.txt @@ -0,0 +1,2 @@ +........ [100%] +8 passed, 31 subtests passed in 4.90s diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/reproduce-scratch-checks.py b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/reproduce-scratch-checks.py new file mode 100644 index 0000000..7087821 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/reproduce-scratch-checks.py @@ -0,0 +1,30 @@ +from pathlib import Path +import os,json,subprocess,shutil,hashlib,difflib +packet=Path('/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1') +s=json.loads((packet/'scratch.json').read_text());scratch=Path(s['scratch_root']);nk=scratch/'net-kingdom';frozen=scratch/'warden-frozen';mutant=scratch/'warden-admission-guard-removed';shutil.copytree(frozen,mutant) +p=mutant/'src/warden/case_run.py';before=p.read_text() +guard=''' if descriptor.effect == "mint" and ( + actor != "human" or os.getenv("WARDEN_AGENT_ID") + ): + raise Refused("agent mint is forbidden") +''' +assert before.count(guard)==1 +p.write_text(before.replace(guard,'')) +(packet/'scratch-kernel-mutation.patch').write_text(''.join(difflib.unified_diff(before.splitlines(True),p.read_text().splitlines(True),fromfile='frozen/src/warden/case_run.py',tofile='scratch-mutant/src/warden/case_run.py'))) +test=nk/s['target'];successor=test.read_bytes();original=(packet/'test_admit_1_3_type.before.py').read_bytes() +results=[] +for name,kernel,source,expected in [('preimage-frozen',frozen,original,0),('preimage-mutant',mutant,original,0),('successor-frozen',frozen,successor,0),('successor-mutant',mutant,successor,1)]: + test.write_bytes(source) + env=os.environ.copy();env['PYTHONDONTWRITEBYTECODE']='1';env['PYTHONPATH']=str(kernel/'src') + cmd=['/home/worsch/ops-warden/.venv/bin/python','-m','pytest','-q','-p','no:cacheprovider',test.name] + run=subprocess.run(cmd,cwd=test.parent,env=env,capture_output=True,text=True) + (packet/(name+'.txt')).write_text(run.stdout+run.stderr) + results.append({'name':name,'command':cmd,'cwd':str(test.parent),'pythonpath':env['PYTHONPATH'],'test_sha256':hashlib.sha256(source).hexdigest(),'kernel_case_run_sha256':hashlib.sha256((kernel/'src/warden/case_run.py').read_bytes()).hexdigest(),'exit_code':run.returncode,'expected_exit_code':expected}) + print(name,run.returncode,run.stdout.splitlines()[-1],flush=True) + assert run.returncode==expected + if name=='successor-mutant': + assert 'test_agent_mint_is_refused_after_fixture_map_acknowledges_descriptor' in run.stdout + assert 'test_human_mint_is_refused_with_agent_runtime_identity' in run.stdout + assert '2 failed, 8 passed' in run.stdout + test.write_bytes(successor) +(packet/'test-results.json').write_text(json.dumps(results,indent=2)+'\n') diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/scratch-kernel-mutation.patch b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/scratch-kernel-mutation.patch new file mode 100644 index 0000000..0661651 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/scratch-kernel-mutation.patch @@ -0,0 +1,13 @@ +--- frozen/src/warden/case_run.py ++++ scratch-mutant/src/warden/case_run.py +@@ -646,10 +646,6 @@ + or lane != descriptor.lane + ): + raise Refused("principal actor or lane mismatch") +- if descriptor.effect == "mint" and ( +- actor != "human" or os.getenv("WARDEN_AGENT_ID") +- ): +- raise Refused("agent mint is forbidden") + parameters = case.get("parameters", {}) + declared = { + item["name"]: item diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/scratch.json b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/scratch.json new file mode 100644 index 0000000..aaa0ba2 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/scratch.json @@ -0,0 +1,6 @@ +{ + "scratch_root": "/tmp/nk48-r5-f1-ahudwokt", + "target": "sso-mfa/k8s/keycape/test_admit_1_3_type.py", + "net_kingdom_commit": "3236d5000e52e0643f33906a26a7825aa0b67465", + "warden_commit": "117fec9241e7b985dfb187b992e173d0a5dc3700" +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/successor-frozen.txt b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/successor-frozen.txt new file mode 100644 index 0000000..7f3e7ca --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/successor-frozen.txt @@ -0,0 +1,2 @@ +.......... [100%] +10 passed, 32 subtests passed in 5.33s diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/successor-mutant.txt b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/successor-mutant.txt new file mode 100644 index 0000000..962f8ef --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/successor-mutant.txt @@ -0,0 +1,30 @@ +F....F.... [100%] +=================================== FAILURES =================================== +_ Admit13TypeTests.test_agent_mint_is_refused_after_fixture_map_acknowledges_descriptor _ + +self = + + def test_agent_mint_is_refused_after_fixture_map_acknowledges_descriptor(self): + # A committed-map mismatch cannot exercise the independent mint guard. + mint = replace(self.descriptors['mint-secret'], actor='agent') + typ = yaml.safe_load(with_status('acknowledged')) + typ['spec']['execution_descriptors']['mint-secret'] = digest(c.encode(mint)) +> with pinned_13(), self.assertRaisesRegex(Refused, 'agent mint is forbidden'): +E AssertionError: Refused not raised + +test_admit_1_3_type.py:150: AssertionError +___ Admit13TypeTests.test_human_mint_is_refused_with_agent_runtime_identity ____ + +self = + + def test_human_mint_is_refused_with_agent_runtime_identity(self): + mint = self.descriptors['mint-secret'] + self.assertEqual(mint.actor, 'human') +> with pinned_13(), self.assertRaisesRegex(Refused, 'agent mint is forbidden'): +E AssertionError: Refused not raised + +test_admit_1_3_type.py:156: AssertionError +=========================== short test summary info ============================ +FAILED test_admit_1_3_type.py::Admit13TypeTests::test_agent_mint_is_refused_after_fixture_map_acknowledges_descriptor +FAILED test_admit_1_3_type.py::Admit13TypeTests::test_human_mint_is_refused_with_agent_runtime_identity +2 failed, 8 passed, 32 subtests passed in 5.72s diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/test-admit-1-3-agent-mint-coverage.patch b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/test-admit-1-3-agent-mint-coverage.patch new file mode 100644 index 0000000..ec72686 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/test-admit-1-3-agent-mint-coverage.patch @@ -0,0 +1,68 @@ +--- a/sso-mfa/k8s/keycape/test_admit_1_3_type.py ++++ b/sso-mfa/k8s/keycape/test_admit_1_3_type.py +@@ -1,9 +1,9 @@ + """Kernel admission against the real 1.3 successor type (NK-WP-0048-T16 candidate). + +-Unlike test_admit_1_2_adapter_descriptors.py, nothing is patched into the type's +-execution_descriptors: the map tested here is the one committed in +-security-case-type-revisions/1.3. The type is `proposed`, so the kernel must refuse +-it as it stands. Warden's BrowserInputs also pins each type version's parameter ++The committed-map tests use security-case-type-revisions/1.3 unchanged. One ++separate fixture-map test acknowledges only an agent-actor mint descriptor digest ++to reach the independent kernel mint prohibition after map validation. The type ++is `proposed`, so the kernel must refuse it as it stands. Warden's BrowserInputs also pins each type version's parameter + fingerprint and knows only 1.1 and 1.2 (ops-warden case_inputs.PARAMETER_PINS), so an + acknowledged 1.3 is still refused until ops-warden adds a 1.3 pin. That is an + ops-warden owner change, and it is not made here. The tests show that 1.3's +@@ -74,7 +74,7 @@ + ARTIFACT['params']['consumer_owner_repo'], + version='1.3')) + +- def admit(self, desc, type_source, *, actor=None, lane=None): ++ def admit(self, desc, type_source, *, actor=None, lane=None, agent_id=None): + desc_bytes = c.encode(desc) + adapter = Adapter(desc.adapter, desc.adapter_source, unused, unused) + executor = Executor(Journal(Path(self.temp.name) / desc.step), +@@ -82,7 +82,10 @@ + lambda purpose, source, evidence: evidence in EVIDENCE, + browser_inputs=inputs.BrowserInputs(REPO)) + with mock.patch.dict(os.environ): +- os.environ.pop('WARDEN_AGENT_ID', None) ++ if agent_id is None: ++ os.environ.pop('WARDEN_AGENT_ID', None) ++ else: ++ os.environ['WARDEN_AGENT_ID'] = agent_id + return executor.admit(self.case_source, type_source, desc_bytes, + b'fixture-only-catalog', principal='founder', actor=actor or desc.actor, + lane=lane or desc.lane, +@@ -125,7 +128,8 @@ + def test_descriptor_outside_the_map_is_refused(self): + acknowledged = with_status('acknowledged') + mint = self.descriptors['mint-secret'] +- for changed in (replace(mint, lane='other-lane'), replace(mint, retry_safe=True)): ++ for changed in (replace(mint, lane='other-lane'), replace(mint, retry_safe=True), ++ replace(mint, actor='agent')): + with self.subTest(changed=changed), pinned_13(), self.assertRaisesRegex( + Refused, 'no exact acknowledged machine descriptor'): + self.admit(changed, acknowledged) +@@ -137,6 +141,20 @@ + self.assertEqual((mint.effect, mint.actor), ('mint', 'human')) + with pinned_13(), self.assertRaisesRegex(Refused, 'principal actor or lane mismatch'): + self.admit(mint, acknowledged, actor='agent') ++ ++ def test_agent_mint_is_refused_after_fixture_map_acknowledges_descriptor(self): ++ # A committed-map mismatch cannot exercise the independent mint guard. ++ mint = replace(self.descriptors['mint-secret'], actor='agent') ++ typ = yaml.safe_load(with_status('acknowledged')) ++ typ['spec']['execution_descriptors']['mint-secret'] = digest(c.encode(mint)) ++ with pinned_13(), self.assertRaisesRegex(Refused, 'agent mint is forbidden'): ++ self.admit(mint, yaml.safe_dump(typ).encode()) ++ ++ def test_human_mint_is_refused_with_agent_runtime_identity(self): ++ mint = self.descriptors['mint-secret'] ++ self.assertEqual(mint.actor, 'human') ++ with pinned_13(), self.assertRaisesRegex(Refused, 'agent mint is forbidden'): ++ self.admit(mint, with_status('acknowledged'), agent_id='fixture-agent') + + def test_wrong_lane_is_refused(self): + # Moved from test_admit_1_2_adapter_descriptors.py in r5. diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/test-results.json b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/test-results.json new file mode 100644 index 0000000..032f7f3 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/test-results.json @@ -0,0 +1,74 @@ +[ + { + "name": "preimage-frozen", + "command": [ + "/home/worsch/ops-warden/.venv/bin/python", + "-m", + "pytest", + "-q", + "-p", + "no:cacheprovider", + "test_admit_1_3_type.py" + ], + "cwd": "/tmp/nk48-r5-f1-ahudwokt/net-kingdom/sso-mfa/k8s/keycape", + "pythonpath": "/tmp/nk48-r5-f1-ahudwokt/warden-frozen/src", + "test_sha256": "f4f6a35cfbada462c939fe357e51fa5d03efc9d20c17a511f224c1359ca0faa5", + "kernel_case_run_sha256": "77e8117a1fb4e039815b555797778f7180f11e30f64cebe51b350b3eb7e46768", + "exit_code": 0, + "expected_exit_code": 0 + }, + { + "name": "preimage-mutant", + "command": [ + "/home/worsch/ops-warden/.venv/bin/python", + "-m", + "pytest", + "-q", + "-p", + "no:cacheprovider", + "test_admit_1_3_type.py" + ], + "cwd": "/tmp/nk48-r5-f1-ahudwokt/net-kingdom/sso-mfa/k8s/keycape", + "pythonpath": "/tmp/nk48-r5-f1-ahudwokt/warden-admission-guard-removed/src", + "test_sha256": "f4f6a35cfbada462c939fe357e51fa5d03efc9d20c17a511f224c1359ca0faa5", + "kernel_case_run_sha256": "6b0d61fb0da4737f305bb959524540752fbcf0f2993a7969bd7e368bdd03ea5a", + "exit_code": 0, + "expected_exit_code": 0 + }, + { + "name": "successor-frozen", + "command": [ + "/home/worsch/ops-warden/.venv/bin/python", + "-m", + "pytest", + "-q", + "-p", + "no:cacheprovider", + "test_admit_1_3_type.py" + ], + "cwd": "/tmp/nk48-r5-f1-ahudwokt/net-kingdom/sso-mfa/k8s/keycape", + "pythonpath": "/tmp/nk48-r5-f1-ahudwokt/warden-frozen/src", + "test_sha256": "c512ba00513d066fd1ca8af08fd6bf106fbd7a132981b1dcc5af80a745ab03f0", + "kernel_case_run_sha256": "77e8117a1fb4e039815b555797778f7180f11e30f64cebe51b350b3eb7e46768", + "exit_code": 0, + "expected_exit_code": 0 + }, + { + "name": "successor-mutant", + "command": [ + "/home/worsch/ops-warden/.venv/bin/python", + "-m", + "pytest", + "-q", + "-p", + "no:cacheprovider", + "test_admit_1_3_type.py" + ], + "cwd": "/tmp/nk48-r5-f1-ahudwokt/net-kingdom/sso-mfa/k8s/keycape", + "pythonpath": "/tmp/nk48-r5-f1-ahudwokt/warden-admission-guard-removed/src", + "test_sha256": "c512ba00513d066fd1ca8af08fd6bf106fbd7a132981b1dcc5af80a745ab03f0", + "kernel_case_run_sha256": "6b0d61fb0da4737f305bb959524540752fbcf0f2993a7969bd7e368bdd03ea5a", + "exit_code": 1, + "expected_exit_code": 1 + } +] diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/test_admit_1_3_type.after.py b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/test_admit_1_3_type.after.py new file mode 100644 index 0000000..1b3e537 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/test_admit_1_3_type.after.py @@ -0,0 +1,175 @@ +"""Kernel admission against the real 1.3 successor type (NK-WP-0048-T16 candidate). + +The committed-map tests use security-case-type-revisions/1.3 unchanged. One +separate fixture-map test acknowledges only an agent-actor mint descriptor digest +to reach the independent kernel mint prohibition after map validation. The type +is `proposed`, so the kernel must refuse it as it stands. Warden's BrowserInputs also pins each type version's parameter +fingerprint and knows only 1.1 and 1.2 (ops-warden case_inputs.PARAMETER_PINS), so an +acknowledged 1.3 is still refused until ops-warden adds a 1.3 pin. That is an +ops-warden owner change, and it is not made here. The tests show that 1.3's +parameters carry 1.2's pinned fingerprint, and that with only that one pin added in +memory the committed map admits exactly the composed descriptors and nothing else. +Trust is fixture-only, nothing is dispatched, and none of this is owner admission +or an acknowledgment. +""" +from dataclasses import replace +import hashlib +import importlib.util +import json +import os +from pathlib import Path +import tempfile +import unittest +from unittest import mock + +import yaml + +from warden.case_run import Adapter, Executor, Journal, digest +from warden.case_receipt_state import Refused +import warden.case_inputs as inputs + +ROOT = Path(__file__).parent +REPO = ROOT.parents[2] +spec = importlib.util.spec_from_file_location('compose_13_admit', ROOT / 'compose-1.2-descriptors.py') +c = importlib.util.module_from_spec(spec) +spec.loader.exec_module(c) + +TYPE_DIR = REPO / 'security-case-type-revisions/1.3/oidc-browser-relying-party' +TYPE_BYTES = (TYPE_DIR / 'type.yaml').read_bytes() +ARTIFACT = json.loads((ROOT / 'descriptor-composition-1.3.candidate.json').read_text()) +EVIDENCE = {'fixture-acceptance', 'fixture-materialization', 'fixture-descriptor', 'fixture-session'} + + +def unused(*_): + raise AssertionError('admission must not dispatch') + + +def parameter_fingerprint(type_bytes): + params = yaml.safe_load(type_bytes)['spec']['parameters'] + return hashlib.sha256(json.dumps(params, sort_keys=True, separators=(',', ':')).encode()).hexdigest() + + +def pinned_13(): + """Warden's parameter pins plus the one 1.3 entry ops-warden would have to add.""" + return mock.patch.dict(inputs.PARAMETER_PINS, {'1.3': parameter_fingerprint(TYPE_BYTES)}) + + +def with_status(status): + """The committed 1.3 type with only its status changed.""" + typ = yaml.safe_load(TYPE_BYTES) + typ['spec']['status'] = status + return yaml.safe_dump(typ).encode() + + +class Admit13TypeTests(unittest.TestCase): + def setUp(self): + self.temp = tempfile.TemporaryDirectory() + self.addCleanup(self.temp.cleanup) + case = yaml.safe_load((TYPE_DIR / 'examples/forgejo.yaml').read_bytes()) + case['spec'].update(status='materialized', acceptance={ + 'at': '2026-10-17', 'record': 'fixture-only acceptance, not an owner record', + 'by': [{'name': 'fixture', 'role': 'owner'}, {'name': 'fixture', 'role': 'founder'}]}) + self.case_source = yaml.safe_dump(case).encode() + self.descriptors = dict(c.adapter_descriptors(ARTIFACT['check_config_digest'], + ARTIFACT['params']['consumer_owner_repo'], + version='1.3')) + + def admit(self, desc, type_source, *, actor=None, lane=None, agent_id=None): + desc_bytes = c.encode(desc) + adapter = Adapter(desc.adapter, desc.adapter_source, unused, unused) + executor = Executor(Journal(Path(self.temp.name) / desc.step), + {adapter.reference: adapter}, + lambda purpose, source, evidence: evidence in EVIDENCE, + browser_inputs=inputs.BrowserInputs(REPO)) + with mock.patch.dict(os.environ): + if agent_id is None: + os.environ.pop('WARDEN_AGENT_ID', None) + else: + os.environ['WARDEN_AGENT_ID'] = agent_id + return executor.admit(self.case_source, type_source, desc_bytes, + b'fixture-only-catalog', principal='founder', actor=actor or desc.actor, + lane=lane or desc.lane, + acceptance_evidence='fixture-acceptance', + materialization_evidence='fixture-materialization', + descriptor_evidence='fixture-descriptor', session_evidence='fixture-session') + + def test_committed_map_is_exactly_the_composed_set(self): + committed = yaml.safe_load(TYPE_BYTES)['spec']['execution_descriptors'] + composed = {s: digest(c.encode(d)) for s, d in self.descriptors.items()} + self.assertEqual(len(composed), 7) + self.assertEqual(committed, composed) + self.assertEqual(committed, ARTIFACT['execution_descriptors']) + + def test_proposed_type_is_refused_as_it_stands(self): + self.assertEqual(yaml.safe_load(TYPE_BYTES)['spec']['status'], 'proposed') + for step, desc in self.descriptors.items(): + with self.subTest(step=step), self.assertRaisesRegex(Refused, 'type is not acknowledged'): + self.admit(desc, TYPE_BYTES) + + def test_warden_has_no_1_3_parameter_pin_yet(self): + self.assertNotIn('1.3', inputs.PARAMETER_PINS) + # 1.3 keeps 1.2's parameters, so the missing pin is 1.2's fingerprint. + self.assertEqual(parameter_fingerprint(TYPE_BYTES), inputs.PARAMETER_PINS['1.2']) + acknowledged = with_status('acknowledged') + for step, desc in self.descriptors.items(): + with self.subTest(step=step), self.assertRaisesRegex( + Refused, 'failed exact-version validation'): + self.admit(desc, acknowledged) + + def test_committed_map_admits_every_descriptor_once_acknowledged_and_pinned(self): + acknowledged = with_status('acknowledged') + for step, desc in self.descriptors.items(): + with self.subTest(step=step), pinned_13(): + admitted, invocation, _ = self.admit(desc, acknowledged) + self.assertEqual(admitted, desc) + self.assertEqual((invocation.step, invocation.actor, invocation.target), + (desc.step, desc.actor, 'forgejo')) + + def test_descriptor_outside_the_map_is_refused(self): + acknowledged = with_status('acknowledged') + mint = self.descriptors['mint-secret'] + for changed in (replace(mint, lane='other-lane'), replace(mint, retry_safe=True), + replace(mint, actor='agent')): + with self.subTest(changed=changed), pinned_13(), self.assertRaisesRegex( + Refused, 'no exact acknowledged machine descriptor'): + self.admit(changed, acknowledged) + + def test_mint_secret_admits_only_a_human_actor(self): + # Moved from test_admit_1_2_adapter_descriptors.py (NK48-R1-F1) in r5. + acknowledged = with_status('acknowledged') + mint = self.descriptors['mint-secret'] + self.assertEqual((mint.effect, mint.actor), ('mint', 'human')) + with pinned_13(), self.assertRaisesRegex(Refused, 'principal actor or lane mismatch'): + self.admit(mint, acknowledged, actor='agent') + + def test_agent_mint_is_refused_after_fixture_map_acknowledges_descriptor(self): + # A committed-map mismatch cannot exercise the independent mint guard. + mint = replace(self.descriptors['mint-secret'], actor='agent') + typ = yaml.safe_load(with_status('acknowledged')) + typ['spec']['execution_descriptors']['mint-secret'] = digest(c.encode(mint)) + with pinned_13(), self.assertRaisesRegex(Refused, 'agent mint is forbidden'): + self.admit(mint, yaml.safe_dump(typ).encode()) + + def test_human_mint_is_refused_with_agent_runtime_identity(self): + mint = self.descriptors['mint-secret'] + self.assertEqual(mint.actor, 'human') + with pinned_13(), self.assertRaisesRegex(Refused, 'agent mint is forbidden'): + self.admit(mint, with_status('acknowledged'), agent_id='fixture-agent') + + def test_wrong_lane_is_refused(self): + # Moved from test_admit_1_2_adapter_descriptors.py in r5. + acknowledged = with_status('acknowledged') + for step, desc in self.descriptors.items(): + with self.subTest(step=step), pinned_13(), self.assertRaisesRegex( + Refused, 'principal actor or lane mismatch'): + self.admit(desc, acknowledged, lane='wrong-lane') + + def test_earlier_versions_carry_no_execution_map(self): + for version in ('1.2',): + path = REPO / f'security-case-type-revisions/{version}/oidc-browser-relying-party/type.yaml' + with self.subTest(version=version): + self.assertNotIn('execution_descriptors', yaml.safe_load(path.read_bytes())['spec']) + + +if __name__ == '__main__': + unittest.main() diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/test_admit_1_3_type.before.py b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/test_admit_1_3_type.before.py new file mode 100644 index 0000000..084df94 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/test_admit_1_3_type.before.py @@ -0,0 +1,157 @@ +"""Kernel admission against the real 1.3 successor type (NK-WP-0048-T16 candidate). + +Unlike test_admit_1_2_adapter_descriptors.py, nothing is patched into the type's +execution_descriptors: the map tested here is the one committed in +security-case-type-revisions/1.3. The type is `proposed`, so the kernel must refuse +it as it stands. Warden's BrowserInputs also pins each type version's parameter +fingerprint and knows only 1.1 and 1.2 (ops-warden case_inputs.PARAMETER_PINS), so an +acknowledged 1.3 is still refused until ops-warden adds a 1.3 pin. That is an +ops-warden owner change, and it is not made here. The tests show that 1.3's +parameters carry 1.2's pinned fingerprint, and that with only that one pin added in +memory the committed map admits exactly the composed descriptors and nothing else. +Trust is fixture-only, nothing is dispatched, and none of this is owner admission +or an acknowledgment. +""" +from dataclasses import replace +import hashlib +import importlib.util +import json +import os +from pathlib import Path +import tempfile +import unittest +from unittest import mock + +import yaml + +from warden.case_run import Adapter, Executor, Journal, digest +from warden.case_receipt_state import Refused +import warden.case_inputs as inputs + +ROOT = Path(__file__).parent +REPO = ROOT.parents[2] +spec = importlib.util.spec_from_file_location('compose_13_admit', ROOT / 'compose-1.2-descriptors.py') +c = importlib.util.module_from_spec(spec) +spec.loader.exec_module(c) + +TYPE_DIR = REPO / 'security-case-type-revisions/1.3/oidc-browser-relying-party' +TYPE_BYTES = (TYPE_DIR / 'type.yaml').read_bytes() +ARTIFACT = json.loads((ROOT / 'descriptor-composition-1.3.candidate.json').read_text()) +EVIDENCE = {'fixture-acceptance', 'fixture-materialization', 'fixture-descriptor', 'fixture-session'} + + +def unused(*_): + raise AssertionError('admission must not dispatch') + + +def parameter_fingerprint(type_bytes): + params = yaml.safe_load(type_bytes)['spec']['parameters'] + return hashlib.sha256(json.dumps(params, sort_keys=True, separators=(',', ':')).encode()).hexdigest() + + +def pinned_13(): + """Warden's parameter pins plus the one 1.3 entry ops-warden would have to add.""" + return mock.patch.dict(inputs.PARAMETER_PINS, {'1.3': parameter_fingerprint(TYPE_BYTES)}) + + +def with_status(status): + """The committed 1.3 type with only its status changed.""" + typ = yaml.safe_load(TYPE_BYTES) + typ['spec']['status'] = status + return yaml.safe_dump(typ).encode() + + +class Admit13TypeTests(unittest.TestCase): + def setUp(self): + self.temp = tempfile.TemporaryDirectory() + self.addCleanup(self.temp.cleanup) + case = yaml.safe_load((TYPE_DIR / 'examples/forgejo.yaml').read_bytes()) + case['spec'].update(status='materialized', acceptance={ + 'at': '2026-10-17', 'record': 'fixture-only acceptance, not an owner record', + 'by': [{'name': 'fixture', 'role': 'owner'}, {'name': 'fixture', 'role': 'founder'}]}) + self.case_source = yaml.safe_dump(case).encode() + self.descriptors = dict(c.adapter_descriptors(ARTIFACT['check_config_digest'], + ARTIFACT['params']['consumer_owner_repo'], + version='1.3')) + + def admit(self, desc, type_source, *, actor=None, lane=None): + desc_bytes = c.encode(desc) + adapter = Adapter(desc.adapter, desc.adapter_source, unused, unused) + executor = Executor(Journal(Path(self.temp.name) / desc.step), + {adapter.reference: adapter}, + lambda purpose, source, evidence: evidence in EVIDENCE, + browser_inputs=inputs.BrowserInputs(REPO)) + with mock.patch.dict(os.environ): + os.environ.pop('WARDEN_AGENT_ID', None) + return executor.admit(self.case_source, type_source, desc_bytes, + b'fixture-only-catalog', principal='founder', actor=actor or desc.actor, + lane=lane or desc.lane, + acceptance_evidence='fixture-acceptance', + materialization_evidence='fixture-materialization', + descriptor_evidence='fixture-descriptor', session_evidence='fixture-session') + + def test_committed_map_is_exactly_the_composed_set(self): + committed = yaml.safe_load(TYPE_BYTES)['spec']['execution_descriptors'] + composed = {s: digest(c.encode(d)) for s, d in self.descriptors.items()} + self.assertEqual(len(composed), 7) + self.assertEqual(committed, composed) + self.assertEqual(committed, ARTIFACT['execution_descriptors']) + + def test_proposed_type_is_refused_as_it_stands(self): + self.assertEqual(yaml.safe_load(TYPE_BYTES)['spec']['status'], 'proposed') + for step, desc in self.descriptors.items(): + with self.subTest(step=step), self.assertRaisesRegex(Refused, 'type is not acknowledged'): + self.admit(desc, TYPE_BYTES) + + def test_warden_has_no_1_3_parameter_pin_yet(self): + self.assertNotIn('1.3', inputs.PARAMETER_PINS) + # 1.3 keeps 1.2's parameters, so the missing pin is 1.2's fingerprint. + self.assertEqual(parameter_fingerprint(TYPE_BYTES), inputs.PARAMETER_PINS['1.2']) + acknowledged = with_status('acknowledged') + for step, desc in self.descriptors.items(): + with self.subTest(step=step), self.assertRaisesRegex( + Refused, 'failed exact-version validation'): + self.admit(desc, acknowledged) + + def test_committed_map_admits_every_descriptor_once_acknowledged_and_pinned(self): + acknowledged = with_status('acknowledged') + for step, desc in self.descriptors.items(): + with self.subTest(step=step), pinned_13(): + admitted, invocation, _ = self.admit(desc, acknowledged) + self.assertEqual(admitted, desc) + self.assertEqual((invocation.step, invocation.actor, invocation.target), + (desc.step, desc.actor, 'forgejo')) + + def test_descriptor_outside_the_map_is_refused(self): + acknowledged = with_status('acknowledged') + mint = self.descriptors['mint-secret'] + for changed in (replace(mint, lane='other-lane'), replace(mint, retry_safe=True)): + with self.subTest(changed=changed), pinned_13(), self.assertRaisesRegex( + Refused, 'no exact acknowledged machine descriptor'): + self.admit(changed, acknowledged) + + def test_mint_secret_admits_only_a_human_actor(self): + # Moved from test_admit_1_2_adapter_descriptors.py (NK48-R1-F1) in r5. + acknowledged = with_status('acknowledged') + mint = self.descriptors['mint-secret'] + self.assertEqual((mint.effect, mint.actor), ('mint', 'human')) + with pinned_13(), self.assertRaisesRegex(Refused, 'principal actor or lane mismatch'): + self.admit(mint, acknowledged, actor='agent') + + def test_wrong_lane_is_refused(self): + # Moved from test_admit_1_2_adapter_descriptors.py in r5. + acknowledged = with_status('acknowledged') + for step, desc in self.descriptors.items(): + with self.subTest(step=step), pinned_13(), self.assertRaisesRegex( + Refused, 'principal actor or lane mismatch'): + self.admit(desc, acknowledged, lane='wrong-lane') + + def test_earlier_versions_carry_no_execution_map(self): + for version in ('1.2',): + path = REPO / f'security-case-type-revisions/{version}/oidc-browser-relying-party/type.yaml' + with self.subTest(version=version): + self.assertNotIn('execution_descriptors', yaml.safe_load(path.read_bytes())['spec']) + + +if __name__ == '__main__': + unittest.main() diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/OWNER-REVIEW.md b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/OWNER-REVIEW.md new file mode 100644 index 0000000..34708c5 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/OWNER-REVIEW.md @@ -0,0 +1,69 @@ +# NK48-R5-F2 issuer store implementation candidate + +The missing issuer store is now a concrete **proposed Platform source contract**, +not a discovered existing grant. Neither patch has been applied to a canonical +checkout. Independent Platform review remains necessary before the conditional +NetKingdom patch can be integrated; F2 is not declared closed here. + +The proposed reference is cluster-scoped `ClusterSecretStore +openbao-keycape-{client_id}-issuer`, with no metadata namespace and use restricted +to `sso`. For Forgejo, it binds role `external-secrets-keycape-forgejo` to the +existing CCR0041 identity `external-secrets/external-secrets`, audience `openbao`. +The exact store/provider/ExternalSecret/key/property/target source is in +`platform-candidate/manifests/templates/keycape-issuer-eso.yaml`; its hash is +recorded in `review.json` and the conditional type text. The new `-issuer` name +is an implementation proposal: it does not reuse the Apps consumer SecretStore +or claim that CCR0041 admitted this store name. It preserves type IDs ending in +`-` without changing parameters. ClusterSecretStore names use DNS subdomain +names; the existing adapter's 63-character target reference bound is retained. + +`platform.candidate.patch` adds only that explicit template, a pure metadata +render/validation helper, eight focused fixture tests and an owner contract doc. +It contains no Kubernetes/OpenBao API client, apply/delete command, credentials, +receipt issuer or catalog grant. Store provisioning is separate owner work; the +delivery lane gets only the ExternalSecret document. The template is multi-doc +review source and must not be applied through the delivery lane. Its strict spec +comparison intentionally refuses drift; installed ESO schema/defaulting and +trusted observation authenticity still belong to the real owner binding. + +`netkingdom-entry-point.conditional.patch` changes **only** the issuer +`deliver-secret.entry_point` at NetKingdom 3236d500. The prior type is byte-equal +to r5's e75fe941 reviewed type. It names the proposed exact kind/name/scope and +owner template digest. Parameters, descriptors, modes, lanes, other follow-through, +materialization, verification, acceptance, and every rollback entry are unchanged. +The root coordinator can combine this text with F3 after Platform source review, +then regenerate the existing combined artifact. No source owner acknowledgment, +case/type pin, catalog mutation or default revision switch has occurred. + +Eight tests / 42 negative subcases passed using public synthetic metadata: +independently fixed Forgejo mapping; wrong kind/name/namespace; role, server, +mount, SA/audience or allowed-namespace drift; consumer-store substitution; +remoteRef/property/target drift; malformed/unready observations; rejected caller +store/path overrides; detached ExternalSecret-only render; type-compatible trailing +dash. Both exact patches passed `git apply --check` in disposable scratch. +See `focused-tests.*` and `structural-and-patch-checks.json`. These checks are not +native delivery, signed evidence, API defaulting, issuer auth or isolation proof. + +The source basis is pinned in `source-pins.json`: r5 F2 at coordinator review +lines 429–439; Platform RPF-1.3-C1 explicitly requires a named store and the +lane definition explicitly calls for a fixed client-derived Platform constant; +CCR0041 grants the Forgejo role/SA/audience but declares no store name; +Platform's historical issuer `eso.draft.yaml` supplies ClusterSecretStore, +namespace restriction, serviceAccountRef.audiences and fixed target/remoteRef +conventions; the scaffold also renders ClusterSecretStore and mount-relative +remoteRefs but requires a caller-supplied owner config store_name. The scaffold's +historical audience location differs: the candidate follows the issuer draft, +and makes no claim about the actual installed schema. + +Bounded source search: Platform, NetKingdom and Mason public YAML/Python/JSON/MD +for `openbao-keycape-forgejo` and `external-secrets-keycape-forgejo`, plus Platform +issuer/store/scaffold files. Existing hits name the separate consumer store, +role/policy or old type text. No fixed Forgejo issuer store declaration was found. +This is a public-source finding, not a live-cluster absence claim. No Kubernetes, +SSH, Bao, credential route, Secret or private archive was accessed. + +The next source step is independent Platform review of the exact new declaration, +then coordinator integration of the accepted text with F3. RPF-1.3-T4's actual +capabilities, authentic metadata receipts, per-attempt revocation, guarded +created-only rollback and Warden catalog routing remain its existing owner work. +No new task, UUID, protocol, policy gate or human approval request is created. diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/canonical-source-readonly-state.json b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/canonical-source-readonly-state.json new file mode 100644 index 0000000..f087170 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/canonical-source-readonly-state.json @@ -0,0 +1,12 @@ +{ + "net-kingdom": { + "HEAD": "3236d5000e52e0643f33906a26a7825aa0b67465", + "status_porcelain_v1": " M .custodian-brief.md\n M WORK-RECORDS.md\n?? docs/evidence/2026-10-15-cadence-adoption/hourly-observation-capture/\n?? docs/evidence/2026-10-15-cadence-adoption/hourly-observation-inventory.jsonl\n?? docs/evidence/2026-10-15-cadence-adoption/hourly-observation-monitor.json\n?? intakes/NET-IN-0005-single-use-setup-link-mail-handoff.md\n", + "index_sha256": "47bcf3291b9613706337c2493c33a065ab089c0c5bbbe545de5f462b67b007a3" + }, + "railiance-platform": { + "HEAD": "0a9617a62d4696a8070798923b459e8405c3dbf2", + "status_porcelain_v1": "M .custodian-brief.md\n M WORK-RECORDS.md\n", + "index_sha256": "40f766948f7774c6215ab562e50b4154e9bbdcfbf3a14488625c95afc9cf6fe6" + } +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/focused-tests.json b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/focused-tests.json new file mode 100644 index 0000000..403ab1f --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/focused-tests.json @@ -0,0 +1,10 @@ +{ + "schema": "custodian.source-test-result.v1", + "scope": "synthetic public metadata only; no API, native, credential, receipt-authenticity or isolation proof", + "command": "python3 -B platform-candidate/tests/test_keycape_issuer_eso.py", + "tests": 8, + "subtests": 42, + "passed": true, + "python": "3.12.3", + "pyyaml": "6.0.1" +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/focused-tests.txt b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/focused-tests.txt new file mode 100644 index 0000000..013b472 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/focused-tests.txt @@ -0,0 +1,13 @@ +test_delivery_document_is_only_external_and_detached (fixture.FixedIssuerContract.test_delivery_document_is_only_external_and_detached) ... ok +test_external_drift_refuses (fixture.FixedIssuerContract.test_external_drift_refuses) ... ok +test_independent_forgejo_binding (fixture.FixedIssuerContract.test_independent_forgejo_binding) ... ok +test_missing_or_unready_refuses (fixture.FixedIssuerContract.test_missing_or_unready_refuses) ... ok +test_no_caller_store_or_path_override (fixture.FixedIssuerContract.test_no_caller_store_or_path_override) ... ok +test_store_drift_refuses (fixture.FixedIssuerContract.test_store_drift_refuses) ... ok +test_type_admitted_trailing_dash_is_preserved (fixture.FixedIssuerContract.test_type_admitted_trailing_dash_is_preserved) ... ok +test_valid_ready_metadata_allows_extra_controller_metadata (fixture.FixedIssuerContract.test_valid_ready_metadata_allows_extra_controller_metadata) ... ok + +---------------------------------------------------------------------- +Ran 8 tests in 0.247s + +OK diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/manifest.json b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/manifest.json new file mode 100644 index 0000000..432c3f5 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/manifest.json @@ -0,0 +1,20 @@ +{ + "schema": "custodian.source-packet-manifest.v1", + "sha256": { + "OWNER-REVIEW.md": "sha256:7983fd841f09bc7584047426912c33d568c97b4c1126776f99aac42b34d2b742", + "canonical-source-readonly-state.json": "sha256:129d6926bb996e510ab4b41d30e738096b53b71de1fb2dd470fe0ac94848cbec", + "focused-tests.json": "sha256:8de5862a8d752dce0b3b96a9275e42952a99415a18d3f67c06bf9f5b6008d0e3", + "focused-tests.txt": "sha256:aab46c51d87198ad4be6dd3078f61286e2faf1ef4d76fa14298698388f78ab24", + "netkingdom-entry-point.conditional.patch": "sha256:6784a21fbd10bd62712be2e0f4929034601c94629aa74ccaa2e93fc815eecd61", + "platform-candidate/docs/keycape-issuer-eso-contract.md": "sha256:7f59e59b17c58252982931ac61fe50dd7a07340add6c437fe48af36971eafe68", + "platform-candidate/manifests/templates/keycape-issuer-eso.yaml": "sha256:e17498bd8a4c820041a4105e4efe9c60c6a50953db8f621ff54887080065c743", + "platform-candidate/scripts/keycape_issuer_eso.py": "sha256:21c5797280c2a7825dbda8ac970d34a0cdef4c6458f68bce0b1c12407f122a87", + "platform-candidate/tests/test_keycape_issuer_eso.py": "sha256:223d4c5b349062b9f93e6af79e8d5feda44377ba41a11726f6f2b762691e8308", + "platform.candidate.patch": "sha256:a490df7dee41e15351da8d2906e1342edebb533984befe3d9ae74d952dbb091b", + "review.json": "sha256:d99db9ab4f326308cde464129bbbb7573434e500fd03c130a37a590bac3643a4", + "source-pins.json": "sha256:6e896b6196a0aca01ddaf433b4d14cc041fc0296c5dd6404c3a698b941b61bd8", + "structural-and-patch-checks.json": "sha256:a3dc4cd405c412d4369986cc6736cd2d9eed02d4b86d95d62d5a9914106fdb14", + "type.before.yaml": "sha256:e75fe94110ef9fb7fb18072fe2856941b33507c99cb406d36576046481485d0a", + "type.candidate.yaml": "sha256:ef02ddae3167bdd1558193a5f6db0379fb8cc84207531706d5ef35caabc3684f" + } +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/netkingdom-entry-point.conditional.patch b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/netkingdom-entry-point.conditional.patch new file mode 100644 index 0000000..6a725fe --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/netkingdom-entry-point.conditional.patch @@ -0,0 +1,16 @@ +--- a/security-case-type-revisions/1.3/oidc-browser-relying-party/type.yaml ++++ b/security-case-type-revisions/1.3/oidc-browser-relying-party/type.yaml +@@ -269,8 +269,11 @@ + when: client_type == confidential + entry_point: reviewed adapter railiance-platform/keycape-deliver-secret, dispatched + by Warden's case kernel; Platform's deliver capability applies ExternalSecret +- sso/keycape-{client_id}-client (key client-secret) through the ESO store for +- role external-secrets-keycape-{client_id}, from bao:platform/workloads/keycape/{client_id} ++ sso/keycape-{client_id}-client (key client-secret) through fixed ClusterSecretStore ++ openbao-keycape-{client_id}-issuer (cluster-scoped, restricted to namespace sso), ++ defined by Platform-controlled manifests/templates/keycape-issuer-eso.yaml ++ (sha256:e17498bd8a4c820041a4105e4efe9c60c6a50953db8f621ff54887080065c743), ++ using role external-secrets-keycape-{client_id}, from bao:platform/workloads/keycape/{client_id} + auth: Platform's own delivery path on lane platform-secret-delivery, inside the human + aal2 session; ESO reads custody through role external-secrets-keycape-{client_id}; + Warden holds no Platform credential, and no value leaves custody diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/platform-candidate/docs/keycape-issuer-eso-contract.md b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/platform-candidate/docs/keycape-issuer-eso-contract.md new file mode 100644 index 0000000..56cfa1e --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/platform-candidate/docs/keycape-issuer-eso-contract.md @@ -0,0 +1,68 @@ +# 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. diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/platform-candidate/manifests/templates/keycape-issuer-eso.yaml b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/platform-candidate/manifests/templates/keycape-issuer-eso.yaml new file mode 100644 index 0000000..406a29c --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/platform-candidate/manifests/templates/keycape-issuer-eso.yaml @@ -0,0 +1,46 @@ +# PROPOSED SOURCE ONLY — RPF-1.3-T4 / NK48-R5-F2; not admitted or applied. +# Platform controls this template. Only admitted client_id is substituted. +# Provision the store through Platform's separate reviewed owner path; the +# platform-secret-delivery lane applies ONLY the ExternalSecret document. +apiVersion: external-secrets.io/v1 +kind: ClusterSecretStore +metadata: + name: openbao-keycape-{client_id}-issuer +spec: + provider: + vault: + server: http://openbao.openbao.svc:8200 + path: platform + version: v2 + auth: + kubernetes: + mountPath: kubernetes + role: external-secrets-keycape-{client_id} + serviceAccountRef: + name: external-secrets + namespace: external-secrets + audiences: + - openbao + conditions: + - namespaces: + - sso +--- +apiVersion: external-secrets.io/v1 +kind: ExternalSecret +metadata: + name: keycape-{client_id}-client + namespace: sso +spec: + refreshInterval: 5m + secretStoreRef: + kind: ClusterSecretStore + name: openbao-keycape-{client_id}-issuer + target: + name: keycape-{client_id}-client + creationPolicy: Owner + deletionPolicy: Retain + data: + - secretKey: client-secret + remoteRef: + key: workloads/keycape/{client_id} + property: CLIENT_SECRET diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/platform-candidate/scripts/keycape_issuer_eso.py b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/platform-candidate/scripts/keycape_issuer_eso.py new file mode 100644 index 0000000..8acace3 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/platform-candidate/scripts/keycape_issuer_eso.py @@ -0,0 +1,70 @@ +"""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 _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 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/nk48-r5-f2-implementation-r1/platform-candidate/tests/test_keycape_issuer_eso.py b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/platform-candidate/tests/test_keycape_issuer_eso.py new file mode 100644 index 0000000..fd6e75d --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/platform-candidate/tests/test_keycape_issuer_eso.py @@ -0,0 +1,129 @@ +"""Public metadata fixtures only: no API, credentials, or native calls.""" +import copy +import sys +import unittest +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "scripts")) +import keycape_issuer_eso as contract + + +class FixedIssuerContract(unittest.TestCase): + def observations(self): + resources = contract.documents("forgejo") + for resource in resources: + resource["metadata"].update(uid="public-fixture-uid", resourceVersion="7") + resource["status"] = {"conditions": [{"type": "Ready", "status": "True"}]} + return resources + + def test_independent_forgejo_binding(self): + # Independent owner-source example: CCR0041 role/SA/audience and the + # existing issuer ESO draft's provider/target/relative key convention. + store, external = contract.documents("forgejo") + self.assertEqual(store["kind"], "ClusterSecretStore") + self.assertEqual(store["metadata"], {"name": "openbao-keycape-forgejo-issuer"}) + self.assertEqual(store["spec"], { + "provider": {"vault": { + "server": "http://openbao.openbao.svc:8200", "path": "platform", "version": "v2", + "auth": {"kubernetes": {"mountPath": "kubernetes", + "role": "external-secrets-keycape-forgejo", + "serviceAccountRef": {"name": "external-secrets", "namespace": "external-secrets", + "audiences": ["openbao"]}}}}}, + "conditions": [{"namespaces": ["sso"]}]}) + self.assertEqual(external["metadata"], {"name": "keycape-forgejo-client", "namespace": "sso"}) + self.assertEqual(external["spec"], { + "refreshInterval": "5m", + "secretStoreRef": {"kind": "ClusterSecretStore", "name": "openbao-keycape-forgejo-issuer"}, + "target": {"name": "keycape-forgejo-client", "creationPolicy": "Owner", "deletionPolicy": "Retain"}, + "data": [{"secretKey": "client-secret", "remoteRef": { + "key": "workloads/keycape/forgejo", "property": "CLIENT_SECRET"}}]}) + + def test_valid_ready_metadata_allows_extra_controller_metadata(self): + store, external = self.observations() + contract.require_fixed_store(store, "forgejo") + contract.require_ready_delivery(external, "forgejo") + + def test_store_drift_refuses(self): + changes = [ + ("kind", "SecretStore"), + ("metadata.namespace", "sso"), + ("metadata.name", "caller-store"), + ("spec.provider.vault.server", "https://wrong.invalid"), + ("spec.provider.vault.path", "tenant"), + ("spec.provider.vault.auth.kubernetes.role", "external-secrets-keycape-sibling"), + ("spec.provider.vault.auth.kubernetes.serviceAccountRef.audiences", ["wrong"]), + ("spec.provider.vault.auth.kubernetes.serviceAccountRef.namespace", "sso"), + ("spec.conditions", [{"namespaces": ["sso", "other"]}]), + ] + for path, value in changes: + with self.subTest(path=path): + store = self.observations()[0] + mutate(store, path, value) + with self.assertRaises(contract.Refused): + contract.require_fixed_store(store, "forgejo") + + def test_external_drift_refuses(self): + changes = [ + ("metadata.namespace", ""), ("metadata.namespace", None), ("metadata.namespace", "other"), + ("metadata.name", "keycape-sibling-client"), + ("spec.secretStoreRef.kind", "SecretStore"), + ("spec.secretStoreRef.name", "openbao-keycape-forgejo-consumer"), + ("spec.target.name", "other"), ("spec.target.creationPolicy", "Merge"), + ("spec.target.deletionPolicy", "Delete"), + ("spec.data", [{"secretKey": "client-secret", "remoteRef": { + "key": "platform/workloads/keycape/forgejo", "property": "CLIENT_SECRET"}}]), + ("spec.data", [{"secretKey": "wrong", "remoteRef": { + "key": "workloads/keycape/forgejo", "property": "CLIENT_SECRET"}}]), + ("spec.data", [{"secretKey": "client-secret", "remoteRef": { + "key": "workloads/keycape/forgejo", "property": "wrong"}}]), + ] + for path, value in changes: + with self.subTest(path=path, value=value): + external = self.observations()[1] + mutate(external, path, value) + with self.assertRaises(contract.Refused): + contract.require_ready_delivery(external, "forgejo") + + def test_missing_or_unready_refuses(self): + for check, index in [(contract.require_fixed_store, 0), (contract.require_ready_delivery, 1)]: + for status in [None, {}, [], "bad", {"conditions": None}, + {"conditions": [{"type": "Ready", "status": "False"}]}]: + with self.subTest(check=check.__name__, status=status): + observed = self.observations()[index] + observed["status"] = status + with self.assertRaises(contract.Refused): + check(observed, "forgejo") + for malformed in [None, [], {}, {"metadata": None}]: + with self.assertRaises(contract.Refused): + check(malformed, "forgejo") + + def test_no_caller_store_or_path_override(self): + for invalid in ["", "f", "../forgejo", "forgejo/sibling", "Forgejo", "forgejo\n", "-forgejo", "f" * 49, None]: + with self.subTest(invalid=invalid), self.assertRaises(contract.Refused): + contract.documents(invalid) + with self.assertRaises(TypeError): + contract.external_secret("forgejo", store_name="caller-store") + + def test_type_admitted_trailing_dash_is_preserved(self): + self.assertEqual(contract.external_secret("forgejo-")["metadata"]["name"], + "keycape-forgejo--client") + self.assertEqual(contract.documents("forgejo-")[0]["metadata"]["name"], + "openbao-keycape-forgejo--issuer") + + def test_delivery_document_is_only_external_and_detached(self): + external = contract.external_secret("forgejo") + self.assertEqual(external["kind"], "ExternalSecret") + external["spec"]["secretStoreRef"]["name"] = "tampered" + self.assertEqual(contract.external_secret("forgejo")["spec"]["secretStoreRef"]["name"], + "openbao-keycape-forgejo-issuer") + + +def mutate(resource, path, value): + parts = path.split(".") + for key in parts[:-1]: + resource = resource[key] + resource[parts[-1]] = copy.deepcopy(value) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/platform.candidate.patch b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/platform.candidate.patch new file mode 100644 index 0000000..8d47282 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/platform.candidate.patch @@ -0,0 +1,325 @@ +--- /dev/null ++++ b/docs/keycape-issuer-eso-contract.md +@@ -0,0 +1,68 @@ ++# 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. +--- /dev/null ++++ b/manifests/templates/keycape-issuer-eso.yaml +@@ -0,0 +1,46 @@ ++# PROPOSED SOURCE ONLY — RPF-1.3-T4 / NK48-R5-F2; not admitted or applied. ++# Platform controls this template. Only admitted client_id is substituted. ++# Provision the store through Platform's separate reviewed owner path; the ++# platform-secret-delivery lane applies ONLY the ExternalSecret document. ++apiVersion: external-secrets.io/v1 ++kind: ClusterSecretStore ++metadata: ++ name: openbao-keycape-{client_id}-issuer ++spec: ++ provider: ++ vault: ++ server: http://openbao.openbao.svc:8200 ++ path: platform ++ version: v2 ++ auth: ++ kubernetes: ++ mountPath: kubernetes ++ role: external-secrets-keycape-{client_id} ++ serviceAccountRef: ++ name: external-secrets ++ namespace: external-secrets ++ audiences: ++ - openbao ++ conditions: ++ - namespaces: ++ - sso ++--- ++apiVersion: external-secrets.io/v1 ++kind: ExternalSecret ++metadata: ++ name: keycape-{client_id}-client ++ namespace: sso ++spec: ++ refreshInterval: 5m ++ secretStoreRef: ++ kind: ClusterSecretStore ++ name: openbao-keycape-{client_id}-issuer ++ target: ++ name: keycape-{client_id}-client ++ creationPolicy: Owner ++ deletionPolicy: Retain ++ data: ++ - secretKey: client-secret ++ remoteRef: ++ key: workloads/keycape/{client_id} ++ property: CLIENT_SECRET +--- /dev/null ++++ b/scripts/keycape_issuer_eso.py +@@ -0,0 +1,70 @@ ++"""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 _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 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]) +--- /dev/null ++++ b/tests/test_keycape_issuer_eso.py +@@ -0,0 +1,129 @@ ++"""Public metadata fixtures only: no API, credentials, or native calls.""" ++import copy ++import sys ++import unittest ++from pathlib import Path ++ ++sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "scripts")) ++import keycape_issuer_eso as contract ++ ++ ++class FixedIssuerContract(unittest.TestCase): ++ def observations(self): ++ resources = contract.documents("forgejo") ++ for resource in resources: ++ resource["metadata"].update(uid="public-fixture-uid", resourceVersion="7") ++ resource["status"] = {"conditions": [{"type": "Ready", "status": "True"}]} ++ return resources ++ ++ def test_independent_forgejo_binding(self): ++ # Independent owner-source example: CCR0041 role/SA/audience and the ++ # existing issuer ESO draft's provider/target/relative key convention. ++ store, external = contract.documents("forgejo") ++ self.assertEqual(store["kind"], "ClusterSecretStore") ++ self.assertEqual(store["metadata"], {"name": "openbao-keycape-forgejo-issuer"}) ++ self.assertEqual(store["spec"], { ++ "provider": {"vault": { ++ "server": "http://openbao.openbao.svc:8200", "path": "platform", "version": "v2", ++ "auth": {"kubernetes": {"mountPath": "kubernetes", ++ "role": "external-secrets-keycape-forgejo", ++ "serviceAccountRef": {"name": "external-secrets", "namespace": "external-secrets", ++ "audiences": ["openbao"]}}}}}, ++ "conditions": [{"namespaces": ["sso"]}]}) ++ self.assertEqual(external["metadata"], {"name": "keycape-forgejo-client", "namespace": "sso"}) ++ self.assertEqual(external["spec"], { ++ "refreshInterval": "5m", ++ "secretStoreRef": {"kind": "ClusterSecretStore", "name": "openbao-keycape-forgejo-issuer"}, ++ "target": {"name": "keycape-forgejo-client", "creationPolicy": "Owner", "deletionPolicy": "Retain"}, ++ "data": [{"secretKey": "client-secret", "remoteRef": { ++ "key": "workloads/keycape/forgejo", "property": "CLIENT_SECRET"}}]}) ++ ++ def test_valid_ready_metadata_allows_extra_controller_metadata(self): ++ store, external = self.observations() ++ contract.require_fixed_store(store, "forgejo") ++ contract.require_ready_delivery(external, "forgejo") ++ ++ def test_store_drift_refuses(self): ++ changes = [ ++ ("kind", "SecretStore"), ++ ("metadata.namespace", "sso"), ++ ("metadata.name", "caller-store"), ++ ("spec.provider.vault.server", "https://wrong.invalid"), ++ ("spec.provider.vault.path", "tenant"), ++ ("spec.provider.vault.auth.kubernetes.role", "external-secrets-keycape-sibling"), ++ ("spec.provider.vault.auth.kubernetes.serviceAccountRef.audiences", ["wrong"]), ++ ("spec.provider.vault.auth.kubernetes.serviceAccountRef.namespace", "sso"), ++ ("spec.conditions", [{"namespaces": ["sso", "other"]}]), ++ ] ++ for path, value in changes: ++ with self.subTest(path=path): ++ store = self.observations()[0] ++ mutate(store, path, value) ++ with self.assertRaises(contract.Refused): ++ contract.require_fixed_store(store, "forgejo") ++ ++ def test_external_drift_refuses(self): ++ changes = [ ++ ("metadata.namespace", ""), ("metadata.namespace", None), ("metadata.namespace", "other"), ++ ("metadata.name", "keycape-sibling-client"), ++ ("spec.secretStoreRef.kind", "SecretStore"), ++ ("spec.secretStoreRef.name", "openbao-keycape-forgejo-consumer"), ++ ("spec.target.name", "other"), ("spec.target.creationPolicy", "Merge"), ++ ("spec.target.deletionPolicy", "Delete"), ++ ("spec.data", [{"secretKey": "client-secret", "remoteRef": { ++ "key": "platform/workloads/keycape/forgejo", "property": "CLIENT_SECRET"}}]), ++ ("spec.data", [{"secretKey": "wrong", "remoteRef": { ++ "key": "workloads/keycape/forgejo", "property": "CLIENT_SECRET"}}]), ++ ("spec.data", [{"secretKey": "client-secret", "remoteRef": { ++ "key": "workloads/keycape/forgejo", "property": "wrong"}}]), ++ ] ++ for path, value in changes: ++ with self.subTest(path=path, value=value): ++ external = self.observations()[1] ++ mutate(external, path, value) ++ with self.assertRaises(contract.Refused): ++ contract.require_ready_delivery(external, "forgejo") ++ ++ def test_missing_or_unready_refuses(self): ++ for check, index in [(contract.require_fixed_store, 0), (contract.require_ready_delivery, 1)]: ++ for status in [None, {}, [], "bad", {"conditions": None}, ++ {"conditions": [{"type": "Ready", "status": "False"}]}]: ++ with self.subTest(check=check.__name__, status=status): ++ observed = self.observations()[index] ++ observed["status"] = status ++ with self.assertRaises(contract.Refused): ++ check(observed, "forgejo") ++ for malformed in [None, [], {}, {"metadata": None}]: ++ with self.assertRaises(contract.Refused): ++ check(malformed, "forgejo") ++ ++ def test_no_caller_store_or_path_override(self): ++ for invalid in ["", "f", "../forgejo", "forgejo/sibling", "Forgejo", "forgejo\n", "-forgejo", "f" * 49, None]: ++ with self.subTest(invalid=invalid), self.assertRaises(contract.Refused): ++ contract.documents(invalid) ++ with self.assertRaises(TypeError): ++ contract.external_secret("forgejo", store_name="caller-store") ++ ++ def test_type_admitted_trailing_dash_is_preserved(self): ++ self.assertEqual(contract.external_secret("forgejo-")["metadata"]["name"], ++ "keycape-forgejo--client") ++ self.assertEqual(contract.documents("forgejo-")[0]["metadata"]["name"], ++ "openbao-keycape-forgejo--issuer") ++ ++ def test_delivery_document_is_only_external_and_detached(self): ++ external = contract.external_secret("forgejo") ++ self.assertEqual(external["kind"], "ExternalSecret") ++ external["spec"]["secretStoreRef"]["name"] = "tampered" ++ self.assertEqual(contract.external_secret("forgejo")["spec"]["secretStoreRef"]["name"], ++ "openbao-keycape-forgejo-issuer") ++ ++ ++def mutate(resource, path, value): ++ parts = path.split(".") ++ for key in parts[:-1]: ++ resource = resource[key] ++ resource[parts[-1]] = copy.deepcopy(value) ++ ++ ++if __name__ == "__main__": ++ unittest.main(verbosity=2) diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/review.json b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/review.json new file mode 100644 index 0000000..7b81699 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/review.json @@ -0,0 +1,66 @@ +{ + "schema": "custodian.nk48-r5-f2-implementation-candidate.v1", + "status": "proposed-source-not-admitted", + "date": "2026-10-17", + "scope": "NK-WP-0048-T16; existing RPF-1.3-T4 implementation preparation only", + "source_owner_assent_received": false, + "finding": "NK48-R5-F2", + "owner_requirement": "RPF-1.3-C1", + "netkingdom_base": "3236d5000e52e0643f33906a26a7825aa0b67465", + "reviewed_code": "001a38ca2aa85155ce4840c4da22aca6e2346171", + "platform_base": "0a9617a62d4696a8070798923b459e8405c3dbf2", + "discovery": "No existing Forgejo issuer store declaration found in bounded public source search. No claim about live absence.", + "new_proposed_binding": { + "kind": "ClusterSecretStore", + "name": "openbao-keycape-{client_id}-issuer", + "namespace": null, + "allowed_external_secret_namespace": "sso", + "role": "external-secrets-keycape-{client_id}", + "service_account_ref": "external-secrets/external-secrets", + "audiences": [ + "openbao" + ], + "template_path": "manifests/templates/keycape-issuer-eso.yaml", + "template_sha256": "e17498bd8a4c820041a4105e4efe9c60c6a50953db8f621ff54887080065c743", + "external_secret": "sso/keycape-{client_id}-client", + "secret_key": "client-secret", + "provider_mount": "platform", + "remote_ref_key": "workloads/keycape/{client_id}", + "remote_ref_property": "CLIENT_SECRET", + "target_creation_policy": "Owner", + "remote_deletion_policy": "Retain" + }, + "candidate_patches": { + "platform.candidate.patch": "a490df7dee41e15351da8d2906e1342edebb533984befe3d9ae74d952dbb091b", + "netkingdom-entry-point.conditional.patch": "6784a21fbd10bd62712be2e0f4929034601c94629aa74ccaa2e93fc815eecd61" + }, + "checks": { + "tests_passed": 8, + "subtests_passed": 42, + "patch_dryruns_passed": true, + "only_type_structural_delta": "spec.follow_through[id=deliver-secret].entry_point", + "parameters_bytes_unchanged": true + }, + "remaining_existing_owner_work": [ + "Independent Platform review/adoption of explicit template and metadata helper under RPF-1.3-T4; no assent inferred.", + "After that review, integrate the conditional NetKingdom text with F3 and regenerate combined composition artifact through existing owner path.", + "RPF-1.3-T4 real authenticated deliver/remove/evidence/session capabilities and catalog routing acceptance remain separate existing requirements.", + "Actual approved owner execution must establish installed ESO compatibility, authentic resource observation, fixed preexisting store and receipt/rollback semantics; source fixtures prove none of these." + ], + "changes_not_made": [ + "canonical checkouts", + "workplans/tasks/UUIDs", + "descriptors", + "catalog", + "CCR0041/grants", + "pins/acknowledgments", + "default 1.1 or revision 1.2", + "native resources", + "credentials/Secret values" + ], + "knowledge": { + "version": "0.2.6", + "digest": "8d1d6a9d4d07fec7b619618de7dfe2be2254c1790f85f4198c7fe19db9751c02", + "status": "withheld-orientation-only; current source SECURITY-COMPANION and actual owner contracts reviewed; no authority inferred" + } +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/source-pins.json b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/source-pins.json new file mode 100644 index 0000000..ed00c61 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/source-pins.json @@ -0,0 +1,108 @@ +[ + { + "repo": "net-kingdom", + "commit": "3236d5000e52e0643f33906a26a7825aa0b67465", + "path": "security-case-type-revisions/1.3/oidc-browser-relying-party/type.yaml", + "sha256": "e75fe94110ef9fb7fb18072fe2856941b33507c99cb406d36576046481485d0a", + "working_tree_equals_commit": true, + "line_anchors": [ + 14, + 17, + 269, + 270, + 273 + ] + }, + { + "repo": "net-kingdom", + "commit": "3236d5000e52e0643f33906a26a7825aa0b67465", + "path": "sso-mfa/k8s/keycape/deliver-secret-adapter.py", + "sha256": "cd1a515a1ae2def2093660ed2a810a9c66d1d4cf7126f573ee7e9c4c42afb98d", + "working_tree_equals_commit": true, + "line_anchors": [ + 47, + 60, + 94, + 98 + ] + }, + { + "repo": "railiance-platform", + "commit": "0a9617a62d4696a8070798923b459e8405c3dbf2", + "path": "credential-change-requests/CCR-2026-0041-forgejo-netkingdom-oidc-keycape-eso-reader.yaml", + "sha256": "e69077ad29166770e4a57b4c4105615e272822d8fde11df591d0c127b158ac67", + "working_tree_equals_commit": true, + "line_anchors": [ + 40, + 44, + 52, + 56, + 66, + 89 + ] + }, + { + "repo": "railiance-platform", + "commit": "0a9617a62d4696a8070798923b459e8405c3dbf2", + "path": "docs/provisioning-windows/2026-10-04-1000/keycape/eso.draft.yaml", + "sha256": "c8c334fae9d7e7e2f74ac30095d2266d626e857f7c5a85ad4e20b28543678c5d", + "working_tree_equals_commit": true, + "line_anchors": [ + 3, + 5, + 16, + 18, + 19, + 22, + 26, + 33, + 43, + 55 + ] + }, + { + "repo": "railiance-platform", + "commit": "0a9617a62d4696a8070798923b459e8405c3dbf2", + "path": "docs/credential-lane-designs/keycape-secrets-engine-openbao-issuer-input.yaml", + "sha256": "ade2e10b0f65341051b955900c68931bdd56fd3a81bf452ae81229cc2390ad63", + "working_tree_equals_commit": true, + "line_anchors": [ + 44, + 56 + ] + }, + { + "repo": "railiance-platform", + "commit": "0a9617a62d4696a8070798923b459e8405c3dbf2", + "path": "scripts/catalog_lane_scaffold.py", + "sha256": "96d60cc47aa78728f5ba96ce1fd63033adf23c05641de93ebbdda3902082d2d1", + "working_tree_equals_commit": true, + "line_anchors": [ + 137, + 144, + 145, + 149, + 154 + ] + }, + { + "repo": "railiance-platform", + "commit": "0a9617a62d4696a8070798923b459e8405c3dbf2", + "path": "argocd/platform-addons/openbao-secretstore/openbao-keycape-approval-clients.clustersecretstore.yaml", + "sha256": "9ce6533e652db5428248c37319b6096a5f8f4af82138141473de4d4ca809e692", + "working_tree_equals_commit": true, + "line_anchors": [ + 1 + ] + }, + { + "repo": "the-custodian", + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-adapters-1.2-review/coordinator-review-r5.json", + "sha256": "e3016352c58178894ccbfc600b960d3f9e2b5d534a05b8df2c5b79737cb0127a" + }, + { + "repo": "the-custodian", + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-1.3-owner-review/owner-review-railiance-platform.json", + "sha256": "d3f523b60924b530376b7aac4459cecda5da183848dd6e35889c1013c73439d9" + } +] diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/structural-and-patch-checks.json b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/structural-and-patch-checks.json new file mode 100644 index 0000000..1e5cc2b --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/structural-and-patch-checks.json @@ -0,0 +1,8 @@ +{ + "only_structural_delta": "spec.follow_through[id=deliver-secret].entry_point", + "parameters_raw_bytes_identical": true, + "descriptor_changes": false, + "canonical_1_1_1_2_changes": false, + "base": "3236d5000e52e0643f33906a26a7825aa0b67465", + "both_patch_dryruns_exit": 0 +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/type.before.yaml b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/type.before.yaml new file mode 100644 index 0000000..6ec7560 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/type.before.yaml @@ -0,0 +1,495 @@ +apiVersion: netkingdom.io/security-case-type/v0.1 +kind: SecurityCaseType +spec: + id: oidc-browser-relying-party + version: '1.3' + status: proposed + title: Browser OIDC relying party for an internal service + purpose: 'Let an internal web service sign its human users in through NetKingdom + (KeyCape): an exact-redirect OIDC client, optional MFA pin, the LLDAP groups the + service maps to its own roles, and, for confidential clients, a client secret + minted straight into OpenBao custody. Replaces the per-client rollout scripts + in sso-mfa/k8s/keycape. First case: Forgejo (KEY-WP-0038).' + parameters: + - name: client_id + type: string + case_settable: true + constraints: + pattern: ^[a-z0-9][a-z0-9-]{1,62}$ + sensitivity: operational + description: OAuth2 client_id, unique in KeyCape. + - name: display_name + type: string + case_settable: true + constraints: + pattern: ^[^\n\r]{1,80}$ + sensitivity: public + description: Name shown on the sign-in page. + - name: redirect_uris + type: string + case_settable: true + constraints: + pattern: ^https://[^*?#\s,]+(,https://[^*?#\s,]+)*$ + sensitivity: security_sensitive + description: 'Comma-separated exact https redirect URIs. No wildcards, no plain + http, no query and no fragment: KeyCape refuses ''*'' and ''?'' at startup, + and OAuth 2.0 forbids fragments (key-cape type review R1).' + - name: allowed_scopes + type: string + case_settable: true + constraints: + enum: + - openid,profile,email,groups + - openid,email,groups + - openid,profile,groups + - openid,groups + sensitivity: security_sensitive + description: Scopes the client may request, openid first. Always includes groups, + because access_groups is required and KeyCape's UserInfo returns groups only + for that scope (key-cape type review R2). + - name: client_type + type: string + case_settable: true + constraints: + enum: + - confidential + - public + sensitivity: security_sensitive + description: confidential for server-side services that can hold a secret; public + for SPAs and native apps (PKCE only). + - name: mfa_required + type: string + case_settable: true + constraints: + enum: + - 'true' + - 'false' + sensitivity: security_sensitive + description: '"true" pins MFA on this client; required when the service grants + administrative or owner rights. "false" is not "no MFA": the client then carries + no mfaRequired field and KeyCape applies the per-user privacyIDEA policy. A + renderer must never emit an explicit mfaRequired false, which would skip that + policy (key-cape type review R3).' + - name: access_groups + type: string + case_settable: true + constraints: + pattern: ^[a-z][a-z0-9-]{1,62}(,[a-z][a-z0-9-]{1,62})*$ + sensitivity: operational + description: 'Comma-separated LLDAP groups that the service maps to its roles. + Created if absent. KeyCape has no per-client group gate: it issues tokens to + any authenticated user and only carries the groups claim, so the consuming service + enforces the gate itself (for Forgejo, its required-claim setting; key-cape + type review R2).' + - name: initial_members + type: string + required: false + case_settable: true + constraints: + pattern: ^[a-z][a-z0-9-]*=[a-z][a-z0-9._-]*(\+[a-z][a-z0-9._-]*)*(;[a-z][a-z0-9-]*=[a-z][a-z0-9._-]*(\+[a-z][a-z0-9._-]*)*)*$ + sensitivity: operational + description: Initial memberships as group=user+user;group=user. Usernames are + directory usernames, never secrets. + - name: consuming_service + type: string + case_settable: true + constraints: + pattern: ^[a-z0-9][a-z0-9-]{0,62}$ + sensitivity: operational + description: The service that signs users in with this client. + - name: consumer_owner_repo + type: string + case_settable: true + constraints: + pattern: ^[a-z0-9][a-z0-9-]*$ + sensitivity: operational + description: The repository that owns the consuming service's configuration. + - name: consumer_namespace + type: string + case_settable: true + constraints: + pattern: ^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$ + sensitivity: security_sensitive + description: Consumer namespace for namespaced ESO TokenRequest reader; actual + workload owner must confirm; never infer from service name. + - name: issuer + type: string + case_settable: false + default: https://kc.coulomb.social + sensitivity: security_sensitive + description: The NetKingdom issuer; fixed by the type. + composition: + authority: netkingdom + initial_trust: + - runtime_secret_trust + - runtime_identity_trust + capabilities: + - c1.lightweight-sso + providers: + c1.lightweight-sso: net-kingdom.keycape-client-registration + parameter_bindings: + net-kingdom.keycape-client-registration.client_id: client_id + net-kingdom.keycape-client-registration.display_name: display_name + net-kingdom.keycape-client-registration.redirect_uris: redirect_uris + net-kingdom.keycape-client-registration.allowed_scopes: allowed_scopes + net-kingdom.keycape-client-registration.client_type: client_type + net-kingdom.keycape-client-registration.mfa_required: mfa_required + materialization: + - id: client-secret-path + kind: kv_path + when: client_type == confidential + template: platform/workloads/keycape/{client_id} + body: field CLIENT_SECRET; minted by the credential broker, never written by hand + - id: keycape-reader-policy + kind: openbao_policy + when: client_type == confidential + template: workload-kv-read-keycape-{client_id}-eso + body: "path \"platform/data/workloads/keycape/{client_id}\" {{\n capabilities\ + \ = [\"read\"]\n}}\n" + description: KeyCape reads its client secret only through ESO. No metadata, list, + parent or sibling access. + - id: consumer-reader-policy + kind: openbao_policy + when: client_type == confidential + template: workload-kv-read-keycape-{client_id}-{consuming_service} + body: "path \"platform/data/workloads/keycape/{client_id}\" {{\n capabilities\ + \ = [\"read\"]\n}}\n" + description: The consuming service reads the same secret. No metadata, list, parent + or sibling access. + - id: eso-role + kind: openbao_role + when: client_type == confidential + template: external-secrets-keycape-{client_id} + body: 'auth: kubernetes + + bound_service_account_names: [external-secrets] + + bound_service_account_namespaces: [external-secrets] + + token_policies: [workload-kv-read-keycape-{client_id}-eso] + + token_no_default_policy: true + + token_ttl: 15m + + token_max_ttl: 15m + + ' + description: The Kubernetes-auth role deliver-secret uses; bound to the ESO service + account only. + - id: consumer-eso-role + kind: openbao_role + when: client_type == confidential + template: external-secrets-keycape-{client_id}-{consuming_service} + body: 'auth: kubernetes + + bound_service_account_names: [{consuming_service}-keycape-{client_id}-eso] + + bound_service_account_namespaces: [{consumer_namespace}] + + audience: openbao + + token_policies: [workload-kv-read-keycape-{client_id}-{consuming_service}] + + token_no_default_policy: true + + token_ttl: 15m + + token_max_ttl: 15m + + ' + description: Consumer ESO requests a token for this separate reference identity. + Common ESO controller remains trusted and can request tokens; no cryptographic + controller isolation is claimed. + - id: catalog-entry + kind: catalog_entry + template: '{consuming_service}-keycape-client' + body: 'id: {consuming_service}-keycape-client + + owner_repo: key-cape + + warden_executes: false + + risk: high + + status: active once verification passes + + workload_ref: applicable, name {consuming_service}, declaration_ref of the consumer''s + published declaration or the explicit unknown form, never inferred + + path_template: platform/workloads/keycape/{client_id} + + field: CLIENT_SECRET + + delegation: native, intended_owner key-cape + + rotation: mint-secret then verification; automatable false until a run has been + proven + + ' + description: One entry per case (ops-mason item 5, ops-warden R4'). It carries + the consumer's workload reference, which ADR-0009 needs for zone selection. + follow_through: + - id: ensure-groups + owner: net-kingdom + entry_point: reviewed adapter net-kingdom/keycape-ensure-groups, dispatched by Warden's + case kernel inside the attended session; it calls identity-provisioner + ensure_service_groups in the provisioner pod through the session's contained + cluster access (groups {access_groups}; members exactly {initial_members} as + recorded in the accepted case, nothing else) + auth: the attended human session on lane keycape-attended-case-change (actor human, + parent-session revoked per attempt); the in-pod call uses the provisioner's own + directory binding, and no provisioner or user-engine token is read or sent + lane: keycape-attended-case-change + warden_executes: false + attended: true + value_handling: none + attended_justification: The provisioner's only HTTP credential is user-engine's + runtime token, and its NetworkPolicy admits only user-engine pods. A proxy lane + would act as user-engine (ops-warden review R8; canon section 3; ops-warden + ADR-0002). Running it in the session register-client already needs adds no attended + session. It can become unattended once the provisioner has an operator credential + of its own and an admitted path. Warden's case kernel dispatches the reviewed adapter only inside the one attended human session (descriptor actor human, lane keycape-attended-case-change, parent-session revoked per attempt); the human session is the authority and the kernel is only the dispatcher. + - id: mint-secret + owner: railiance-platform + when: client_type == confidential + entry_point: 'railiance-platform credential broker: generate a client secret inside + custody at bao:platform/workloads/keycape/{client_id}, through reviewed adapter + railiance-platform/keycape-mint-secret' + auth: a human caller (net-kingdom-admins, aal2) starts it; Warden runs it unattended + through the broker lane under the caller's session, with the caller's broker grant + for the case's KV path; agents and root are refused, and the value is generated + in custody and never returned + lane: keycape-client-secret-generate + warden_executes: true + attended: false + value_handling: generated_in_custody + - id: deliver-secret + owner: railiance-platform + when: client_type == confidential + entry_point: reviewed adapter railiance-platform/keycape-deliver-secret, dispatched + by Warden's case kernel; Platform's deliver capability applies ExternalSecret + sso/keycape-{client_id}-client (key client-secret) through the ESO store for + role external-secrets-keycape-{client_id}, from bao:platform/workloads/keycape/{client_id} + auth: Platform's own delivery path on lane platform-secret-delivery, inside the human + aal2 session; ESO reads custody through role external-secrets-keycape-{client_id}; + Warden holds no Platform credential, and no value leaves custody + lane: platform-secret-delivery + warden_executes: false + attended: false + value_handling: none + ready_check: the delivery for sso/keycape-{client_id}-client reports Ready=True + (status only, no value read) + - id: bind-env + owner: net-kingdom + when: client_type == confidential + entry_point: 'reviewed adapter net-kingdom/keycape-bind-env, dispatched by Warden''s + case kernel inside the attended session: patch Deployment sso/keycape env + KEYCAPE_{client_id:env}_CLIENT_SECRET from sso/keycape-{client_id}-client, by + reference only' + auth: the attended human session on lane keycape-attended-case-change, with operator + kube credentials for namespace sso (actor human, parent-session revoked per attempt) + lane: keycape-attended-case-change + warden_executes: false + attended: true + value_handling: none + attended_justification: A change to the live identity provider's Deployment. It + runs in the same attended operator session as register-client, before it, because + KeyCape resolves a confidential client's secret at start and fails closed if + the binding is missing. Warden's case kernel dispatches the reviewed adapter only inside the one attended human session (descriptor actor human, lane keycape-attended-case-change, parent-session revoked per attempt); the human session is the authority and the kernel is only the dispatcher. + - id: register-client + owner: net-kingdom + entry_point: reviewed adapter net-kingdom/keycape-register-client, dispatched by Warden's + case kernel inside the attended session; it runs sso-mfa/k8s/keycape/keycape-client.py + apply --client-file --case-acceptance --check-config + (net-kingdom.keycape-client-registration) + auth: the attended human session on lane keycape-attended-case-change, with operator + write access to the live KeyCape client registry in namespace sso (actor human, + parent-session revoked per attempt) + lane: keycape-attended-case-change + warden_executes: false + attended: true + value_handling: none + attended_justification: 'The live KeyCape client registry is a Kubernetes Secret, + and agents do not read or write Secrets. Until KeyCape loads clients from a + non-secret registry with secrets referenced from custody, one attended operator + session applies the rendered registration. ops-warden groups all attended steps + of a run into that one session. KeyCape loads clients at startup and keeps login, + authorization and handoff state in memory, so the restart drops in-flight logins + and sessions for every client; runs go in a quiet window (key-cape type review + R4). The rendered registry must pass KeyCape''s own startup checks (keycape + check-config, the pinned checker artifact sha256:be37738fe1c08259fb4f9ca0daf0f9301bfffc237e5bb38435525dfbf11ccff5 + built from key-cape 3c71226, which keycape-client.py runs before it patches) + before the restart: a refused entry stops the single replica and every client + loses sign-in (R1). Warden''s case kernel dispatches the reviewed adapter only inside the one attended human session (descriptor actor human, lane keycape-attended-case-change, parent-session revoked per attempt); the human session is the authority and the kernel is only the dispatcher.' + - id: deliver-consumer-secret + owner: '{consumer_owner_repo}' + when: client_type == confidential + entry_point: Reviewed adapter net-kingdom/keycape-deliver-consumer-secret, dispatched + by Warden's case kernel inside the attended consumer-owner session; the owner's + apply capability creates dedicated ServiceAccount {consumer_namespace}/{consuming_service}-keycape-{client_id}-eso + (automount=false, no legacy tokens), namespace SecretStore openbao-keycape-{client_id}-consumer + with serviceAccountRef and audiences [openbao], and ExternalSecret {consuming_service}-netkingdom-oidc-client + with Owner target, Retain remote deletion and only CLIENT_SECRET from workloads/keycape/{client_id}. + Admission validates actual installed ESO TokenRequest support and common controller + RBAC before any owner-approved apply. + auth: the attended consumer-owner session on lane consumer-owner-deployment (actor + human, the consumer owner's operator deployment authority, on the founder's go + for the change); ESO authenticates to OpenBao role external-secrets-keycape-{client_id}-{consuming_service} + with a TokenRequest for the dedicated namespaced serviceAccountRef; controller + remains trusted + lane: consumer-owner-deployment + warden_executes: false + attended: true + value_handling: none + attended_justification: The consumer owner's only deployment authority in the consumer + namespace is its operator credential, and production applies need the founder's + go per change. No contained agent deployer identity exists, and the owner does + not want a standing credential an agent could trigger. It runs in one attended + consumer-owner session together with hand-over. Warden's case kernel dispatches + the reviewed adapter only inside that session (actor human, parent-session + revoked per attempt); the human session is the authority and the kernel is only + the dispatcher. + ready_check: Namespace SecretStore and ExternalSecret both report Ready=True; + dedicated SA has no legacy token refs; status and fixed metadata only, never + Secret values. + - id: hand-over + owner: '{consumer_owner_repo}' + entry_point: The consumer owner first makes its own Helm change configuring {consuming_service} + with issuer {issuer}, client {client_id} and custody bao:platform/workloads/keycape/{client_id}, + requiring one of {access_groups} in the groups claim before sign-in completes. + Then reviewed adapter net-kingdom/keycape-hand-over, dispatched by Warden's case + kernel inside the attended consumer-owner session, verifies the consumer with + four value-free probes (member-login, non-member-refused, membership-add-grants, + membership-remove-revokes) and records the hand-over receipt. + auth: the attended consumer-owner session on lane apps-consumer-handover (actor human, + parent-session revoked per attempt); the consumer configuration itself is the + owner's own deployment path + lane: apps-consumer-handover + warden_executes: false + attended: true + value_handling: none + attended_justification: The verification needs a real member login and a refused + non-member login, which only an attended session can perform, and it follows + the owner's own Helm change in the same consumer-owner session as + deliver-consumer-secret. Warden's case kernel dispatches the reviewed adapter + only inside that session (actor human, parent-session revoked per attempt); the + human session is the authority and the kernel is only the dispatcher. + verification: + - id: authorize-accepts-redirects + check: GET {issuer}/authorize with client {client_id} and each of {redirect_uris} + returns the login page + expect: present + reads_secret_value: false + - id: authorize-refuses-other-redirect + check: GET {issuer}/authorize with client {client_id} and an unregistered redirect + returns an OAuth error + expect: deny + reads_secret_value: false + - id: custody-denied-to-default + when: client_type == confidential + check: a default-only OpenBao identity has no read capability on bao:platform/workloads/keycape/{client_id} + expect: deny + reads_secret_value: false + - id: custody-allowed-to-consumer + when: client_type == confidential + check: policy workload-kv-read-keycape-{client_id}-{consuming_service} grants + read on bao:platform/workloads/keycape/{client_id} (capabilities check only) + expect: allow + reads_secret_value: false + - id: groups-present + check: LLDAP groups {access_groups} exist, with exactly the memberships of the + accepted case added + expect: present + reads_secret_value: false + - id: consumer-enforces-groups + check: '{consuming_service}''s OIDC source for {client_id} (owner {consumer_owner_repo}) + requires one of {access_groups} in the groups claim (configuration read, no + secret value)' + expect: present + reads_secret_value: false + - id: secret-delivered + when: client_type == confidential + check: the delivery for sso/keycape-{client_id}-client reports Ready=True (status + only) + expect: present + reads_secret_value: false + - id: mfa-pinned + when: mfa_required == true + check: keycape-client.py inspect for {client_id} shows the MFA pin on the registered + client (non-secret client view) + expect: present + reads_secret_value: false + - id: consumer-secret-delivered + when: client_type == confidential + check: '{consumer_namespace} SecretStore openbao-keycape-{client_id}-consumer + and ExternalSecret {consuming_service}-netkingdom-oidc-client Ready=True, exact + dedicated SA/audience role binding, fixed CLIENT_SECRET mapping; no Secret value + read' + expect: present + reads_secret_value: false + rollback: + - reverses: hand-over + action: the adapter removes only its hand-over receipt. Removing the OIDC source + for {client_id} from {consuming_service} is the owner's Helm rollback (owner + {consumer_owner_repo}), done inside deliver-consumer-secret's owner_remove + before any consumer object is removed + - reverses: deliver-consumer-secret + when: client_type == confidential + action: After reverting consumer secret reference, delete only authenticated created + ExternalSecret/owned target by UID and ownerReference, then SecretStore and + dedicated SA; preserve preexisting-matched resources and never delete a shared + target. Retain audit and ownership receipts; removing SecretStore/SA alone is + not immediate OpenBao token revocation. + - reverses: register-client + action: keycape-client.py apply --operation remove for {client_id} + - reverses: bind-env + when: client_type == confidential + action: remove env KEYCAPE_{client_id:env}_CLIENT_SECRET from Deployment sso/keycape, + after register-client is reversed + - reverses: deliver-secret + when: client_type == confidential + action: delete ExternalSecret sso/keycape-{client_id}-client and its target + - reverses: mint-secret + when: client_type == confidential + action: 'covered by client-secret-path: one metadata delete, after deliver-secret + is reversed' + - reverses: ensure-groups + action: identity-provisioner revoke_service_groups inside the provisioner pod + (attended, as ensure-groups) with the case's memberships; delete_if_empty only + if no other case uses {access_groups} + - reverses: client-secret-path + when: client_type == confidential + action: delete metadata and all versions at platform/workloads/keycape/{client_id}, + after the ExternalSecret is gone (this also reverses mint-secret) + - reverses: eso-role + when: client_type == confidential + action: delete Kubernetes-auth role external-secrets-keycape-{client_id} + - reverses: keycape-reader-policy + when: client_type == confidential + action: delete policy workload-kv-read-keycape-{client_id}-eso, after eso-role + - reverses: consumer-eso-role + when: client_type == confidential + action: Delete only this case-created role external-secrets-keycape-{client_id}-{consuming_service}, + before deleting consumer-reader-policy; revoke case-issued OpenBao tokens or + prove expiration before declaring deactivation. + - reverses: consumer-reader-policy + when: client_type == confidential + action: delete policy workload-kv-read-keycape-{client_id}-{consuming_service} + - reverses: catalog-entry + action: remove catalog entry {consuming_service}-keycape-client + acceptance: + accepted_by: + - owner + - founder + risk_class: medium + # Reviewed machine descriptors (NK-WP-0048-T16 successor candidate). Digests are + # generated by sso-mfa/k8s/keycape/compose-1.2-descriptors.py --version 1.3. + execution_descriptors: + bind-env: sha256:d5fcb20cf15bf7454890d2529180c582218e3df12584a99162e4b664b899caa0 + deliver-consumer-secret: sha256:36281df8139fb1ec64b226d94f80eb592de5addab7063ff54c691c641e275fc7 + deliver-secret: sha256:75aaa7346aebdea2a0eb0ba993995515b5248ffefc34caf8e713525e9b58da85 + ensure-groups: sha256:ebc9d5b9167d18e6015f4bbeb9699e8d396d64fbfaf195142781f84f4f47a70a + hand-over: sha256:1bce07a3229b7d0c7c730f35ce5b40fe471009a82de8dd53788ed970a9be1224 + mint-secret: sha256:c92957227235db04953809d92b260d32c8b947472a8e8fd17a258ade3ae8da70 + register-client: sha256:4ecc769f75433b66c7161f13c0fe7e5599633cf466d46cb6d8031f0456f426e7 diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/type.candidate.yaml b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/type.candidate.yaml new file mode 100644 index 0000000..7479022 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/type.candidate.yaml @@ -0,0 +1,498 @@ +apiVersion: netkingdom.io/security-case-type/v0.1 +kind: SecurityCaseType +spec: + id: oidc-browser-relying-party + version: '1.3' + status: proposed + title: Browser OIDC relying party for an internal service + purpose: 'Let an internal web service sign its human users in through NetKingdom + (KeyCape): an exact-redirect OIDC client, optional MFA pin, the LLDAP groups the + service maps to its own roles, and, for confidential clients, a client secret + minted straight into OpenBao custody. Replaces the per-client rollout scripts + in sso-mfa/k8s/keycape. First case: Forgejo (KEY-WP-0038).' + parameters: + - name: client_id + type: string + case_settable: true + constraints: + pattern: ^[a-z0-9][a-z0-9-]{1,62}$ + sensitivity: operational + description: OAuth2 client_id, unique in KeyCape. + - name: display_name + type: string + case_settable: true + constraints: + pattern: ^[^\n\r]{1,80}$ + sensitivity: public + description: Name shown on the sign-in page. + - name: redirect_uris + type: string + case_settable: true + constraints: + pattern: ^https://[^*?#\s,]+(,https://[^*?#\s,]+)*$ + sensitivity: security_sensitive + description: 'Comma-separated exact https redirect URIs. No wildcards, no plain + http, no query and no fragment: KeyCape refuses ''*'' and ''?'' at startup, + and OAuth 2.0 forbids fragments (key-cape type review R1).' + - name: allowed_scopes + type: string + case_settable: true + constraints: + enum: + - openid,profile,email,groups + - openid,email,groups + - openid,profile,groups + - openid,groups + sensitivity: security_sensitive + description: Scopes the client may request, openid first. Always includes groups, + because access_groups is required and KeyCape's UserInfo returns groups only + for that scope (key-cape type review R2). + - name: client_type + type: string + case_settable: true + constraints: + enum: + - confidential + - public + sensitivity: security_sensitive + description: confidential for server-side services that can hold a secret; public + for SPAs and native apps (PKCE only). + - name: mfa_required + type: string + case_settable: true + constraints: + enum: + - 'true' + - 'false' + sensitivity: security_sensitive + description: '"true" pins MFA on this client; required when the service grants + administrative or owner rights. "false" is not "no MFA": the client then carries + no mfaRequired field and KeyCape applies the per-user privacyIDEA policy. A + renderer must never emit an explicit mfaRequired false, which would skip that + policy (key-cape type review R3).' + - name: access_groups + type: string + case_settable: true + constraints: + pattern: ^[a-z][a-z0-9-]{1,62}(,[a-z][a-z0-9-]{1,62})*$ + sensitivity: operational + description: 'Comma-separated LLDAP groups that the service maps to its roles. + Created if absent. KeyCape has no per-client group gate: it issues tokens to + any authenticated user and only carries the groups claim, so the consuming service + enforces the gate itself (for Forgejo, its required-claim setting; key-cape + type review R2).' + - name: initial_members + type: string + required: false + case_settable: true + constraints: + pattern: ^[a-z][a-z0-9-]*=[a-z][a-z0-9._-]*(\+[a-z][a-z0-9._-]*)*(;[a-z][a-z0-9-]*=[a-z][a-z0-9._-]*(\+[a-z][a-z0-9._-]*)*)*$ + sensitivity: operational + description: Initial memberships as group=user+user;group=user. Usernames are + directory usernames, never secrets. + - name: consuming_service + type: string + case_settable: true + constraints: + pattern: ^[a-z0-9][a-z0-9-]{0,62}$ + sensitivity: operational + description: The service that signs users in with this client. + - name: consumer_owner_repo + type: string + case_settable: true + constraints: + pattern: ^[a-z0-9][a-z0-9-]*$ + sensitivity: operational + description: The repository that owns the consuming service's configuration. + - name: consumer_namespace + type: string + case_settable: true + constraints: + pattern: ^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$ + sensitivity: security_sensitive + description: Consumer namespace for namespaced ESO TokenRequest reader; actual + workload owner must confirm; never infer from service name. + - name: issuer + type: string + case_settable: false + default: https://kc.coulomb.social + sensitivity: security_sensitive + description: The NetKingdom issuer; fixed by the type. + composition: + authority: netkingdom + initial_trust: + - runtime_secret_trust + - runtime_identity_trust + capabilities: + - c1.lightweight-sso + providers: + c1.lightweight-sso: net-kingdom.keycape-client-registration + parameter_bindings: + net-kingdom.keycape-client-registration.client_id: client_id + net-kingdom.keycape-client-registration.display_name: display_name + net-kingdom.keycape-client-registration.redirect_uris: redirect_uris + net-kingdom.keycape-client-registration.allowed_scopes: allowed_scopes + net-kingdom.keycape-client-registration.client_type: client_type + net-kingdom.keycape-client-registration.mfa_required: mfa_required + materialization: + - id: client-secret-path + kind: kv_path + when: client_type == confidential + template: platform/workloads/keycape/{client_id} + body: field CLIENT_SECRET; minted by the credential broker, never written by hand + - id: keycape-reader-policy + kind: openbao_policy + when: client_type == confidential + template: workload-kv-read-keycape-{client_id}-eso + body: "path \"platform/data/workloads/keycape/{client_id}\" {{\n capabilities\ + \ = [\"read\"]\n}}\n" + description: KeyCape reads its client secret only through ESO. No metadata, list, + parent or sibling access. + - id: consumer-reader-policy + kind: openbao_policy + when: client_type == confidential + template: workload-kv-read-keycape-{client_id}-{consuming_service} + body: "path \"platform/data/workloads/keycape/{client_id}\" {{\n capabilities\ + \ = [\"read\"]\n}}\n" + description: The consuming service reads the same secret. No metadata, list, parent + or sibling access. + - id: eso-role + kind: openbao_role + when: client_type == confidential + template: external-secrets-keycape-{client_id} + body: 'auth: kubernetes + + bound_service_account_names: [external-secrets] + + bound_service_account_namespaces: [external-secrets] + + token_policies: [workload-kv-read-keycape-{client_id}-eso] + + token_no_default_policy: true + + token_ttl: 15m + + token_max_ttl: 15m + + ' + description: The Kubernetes-auth role deliver-secret uses; bound to the ESO service + account only. + - id: consumer-eso-role + kind: openbao_role + when: client_type == confidential + template: external-secrets-keycape-{client_id}-{consuming_service} + body: 'auth: kubernetes + + bound_service_account_names: [{consuming_service}-keycape-{client_id}-eso] + + bound_service_account_namespaces: [{consumer_namespace}] + + audience: openbao + + token_policies: [workload-kv-read-keycape-{client_id}-{consuming_service}] + + token_no_default_policy: true + + token_ttl: 15m + + token_max_ttl: 15m + + ' + description: Consumer ESO requests a token for this separate reference identity. + Common ESO controller remains trusted and can request tokens; no cryptographic + controller isolation is claimed. + - id: catalog-entry + kind: catalog_entry + template: '{consuming_service}-keycape-client' + body: 'id: {consuming_service}-keycape-client + + owner_repo: key-cape + + warden_executes: false + + risk: high + + status: active once verification passes + + workload_ref: applicable, name {consuming_service}, declaration_ref of the consumer''s + published declaration or the explicit unknown form, never inferred + + path_template: platform/workloads/keycape/{client_id} + + field: CLIENT_SECRET + + delegation: native, intended_owner key-cape + + rotation: mint-secret then verification; automatable false until a run has been + proven + + ' + description: One entry per case (ops-mason item 5, ops-warden R4'). It carries + the consumer's workload reference, which ADR-0009 needs for zone selection. + follow_through: + - id: ensure-groups + owner: net-kingdom + entry_point: reviewed adapter net-kingdom/keycape-ensure-groups, dispatched by Warden's + case kernel inside the attended session; it calls identity-provisioner + ensure_service_groups in the provisioner pod through the session's contained + cluster access (groups {access_groups}; members exactly {initial_members} as + recorded in the accepted case, nothing else) + auth: the attended human session on lane keycape-attended-case-change (actor human, + parent-session revoked per attempt); the in-pod call uses the provisioner's own + directory binding, and no provisioner or user-engine token is read or sent + lane: keycape-attended-case-change + warden_executes: false + attended: true + value_handling: none + attended_justification: The provisioner's only HTTP credential is user-engine's + runtime token, and its NetworkPolicy admits only user-engine pods. A proxy lane + would act as user-engine (ops-warden review R8; canon section 3; ops-warden + ADR-0002). Running it in the session register-client already needs adds no attended + session. It can become unattended once the provisioner has an operator credential + of its own and an admitted path. Warden's case kernel dispatches the reviewed adapter only inside the one attended human session (descriptor actor human, lane keycape-attended-case-change, parent-session revoked per attempt); the human session is the authority and the kernel is only the dispatcher. + - id: mint-secret + owner: railiance-platform + when: client_type == confidential + entry_point: 'railiance-platform credential broker: generate a client secret inside + custody at bao:platform/workloads/keycape/{client_id}, through reviewed adapter + railiance-platform/keycape-mint-secret' + auth: a human caller (net-kingdom-admins, aal2) starts it; Warden runs it unattended + through the broker lane under the caller's session, with the caller's broker grant + for the case's KV path; agents and root are refused, and the value is generated + in custody and never returned + lane: keycape-client-secret-generate + warden_executes: true + attended: false + value_handling: generated_in_custody + - id: deliver-secret + owner: railiance-platform + when: client_type == confidential + entry_point: reviewed adapter railiance-platform/keycape-deliver-secret, dispatched + by Warden's case kernel; Platform's deliver capability applies ExternalSecret + sso/keycape-{client_id}-client (key client-secret) through fixed ClusterSecretStore + openbao-keycape-{client_id}-issuer (cluster-scoped, restricted to namespace sso), + defined by Platform-controlled manifests/templates/keycape-issuer-eso.yaml + (sha256:e17498bd8a4c820041a4105e4efe9c60c6a50953db8f621ff54887080065c743), + using role external-secrets-keycape-{client_id}, from bao:platform/workloads/keycape/{client_id} + auth: Platform's own delivery path on lane platform-secret-delivery, inside the human + aal2 session; ESO reads custody through role external-secrets-keycape-{client_id}; + Warden holds no Platform credential, and no value leaves custody + lane: platform-secret-delivery + warden_executes: false + attended: false + value_handling: none + ready_check: the delivery for sso/keycape-{client_id}-client reports Ready=True + (status only, no value read) + - id: bind-env + owner: net-kingdom + when: client_type == confidential + entry_point: 'reviewed adapter net-kingdom/keycape-bind-env, dispatched by Warden''s + case kernel inside the attended session: patch Deployment sso/keycape env + KEYCAPE_{client_id:env}_CLIENT_SECRET from sso/keycape-{client_id}-client, by + reference only' + auth: the attended human session on lane keycape-attended-case-change, with operator + kube credentials for namespace sso (actor human, parent-session revoked per attempt) + lane: keycape-attended-case-change + warden_executes: false + attended: true + value_handling: none + attended_justification: A change to the live identity provider's Deployment. It + runs in the same attended operator session as register-client, before it, because + KeyCape resolves a confidential client's secret at start and fails closed if + the binding is missing. Warden's case kernel dispatches the reviewed adapter only inside the one attended human session (descriptor actor human, lane keycape-attended-case-change, parent-session revoked per attempt); the human session is the authority and the kernel is only the dispatcher. + - id: register-client + owner: net-kingdom + entry_point: reviewed adapter net-kingdom/keycape-register-client, dispatched by Warden's + case kernel inside the attended session; it runs sso-mfa/k8s/keycape/keycape-client.py + apply --client-file --case-acceptance --check-config + (net-kingdom.keycape-client-registration) + auth: the attended human session on lane keycape-attended-case-change, with operator + write access to the live KeyCape client registry in namespace sso (actor human, + parent-session revoked per attempt) + lane: keycape-attended-case-change + warden_executes: false + attended: true + value_handling: none + attended_justification: 'The live KeyCape client registry is a Kubernetes Secret, + and agents do not read or write Secrets. Until KeyCape loads clients from a + non-secret registry with secrets referenced from custody, one attended operator + session applies the rendered registration. ops-warden groups all attended steps + of a run into that one session. KeyCape loads clients at startup and keeps login, + authorization and handoff state in memory, so the restart drops in-flight logins + and sessions for every client; runs go in a quiet window (key-cape type review + R4). The rendered registry must pass KeyCape''s own startup checks (keycape + check-config, the pinned checker artifact sha256:be37738fe1c08259fb4f9ca0daf0f9301bfffc237e5bb38435525dfbf11ccff5 + built from key-cape 3c71226, which keycape-client.py runs before it patches) + before the restart: a refused entry stops the single replica and every client + loses sign-in (R1). Warden''s case kernel dispatches the reviewed adapter only inside the one attended human session (descriptor actor human, lane keycape-attended-case-change, parent-session revoked per attempt); the human session is the authority and the kernel is only the dispatcher.' + - id: deliver-consumer-secret + owner: '{consumer_owner_repo}' + when: client_type == confidential + entry_point: Reviewed adapter net-kingdom/keycape-deliver-consumer-secret, dispatched + by Warden's case kernel inside the attended consumer-owner session; the owner's + apply capability creates dedicated ServiceAccount {consumer_namespace}/{consuming_service}-keycape-{client_id}-eso + (automount=false, no legacy tokens), namespace SecretStore openbao-keycape-{client_id}-consumer + with serviceAccountRef and audiences [openbao], and ExternalSecret {consuming_service}-netkingdom-oidc-client + with Owner target, Retain remote deletion and only CLIENT_SECRET from workloads/keycape/{client_id}. + Admission validates actual installed ESO TokenRequest support and common controller + RBAC before any owner-approved apply. + auth: the attended consumer-owner session on lane consumer-owner-deployment (actor + human, the consumer owner's operator deployment authority, on the founder's go + for the change); ESO authenticates to OpenBao role external-secrets-keycape-{client_id}-{consuming_service} + with a TokenRequest for the dedicated namespaced serviceAccountRef; controller + remains trusted + lane: consumer-owner-deployment + warden_executes: false + attended: true + value_handling: none + attended_justification: The consumer owner's only deployment authority in the consumer + namespace is its operator credential, and production applies need the founder's + go per change. No contained agent deployer identity exists, and the owner does + not want a standing credential an agent could trigger. It runs in one attended + consumer-owner session together with hand-over. Warden's case kernel dispatches + the reviewed adapter only inside that session (actor human, parent-session + revoked per attempt); the human session is the authority and the kernel is only + the dispatcher. + ready_check: Namespace SecretStore and ExternalSecret both report Ready=True; + dedicated SA has no legacy token refs; status and fixed metadata only, never + Secret values. + - id: hand-over + owner: '{consumer_owner_repo}' + entry_point: The consumer owner first makes its own Helm change configuring {consuming_service} + with issuer {issuer}, client {client_id} and custody bao:platform/workloads/keycape/{client_id}, + requiring one of {access_groups} in the groups claim before sign-in completes. + Then reviewed adapter net-kingdom/keycape-hand-over, dispatched by Warden's case + kernel inside the attended consumer-owner session, verifies the consumer with + four value-free probes (member-login, non-member-refused, membership-add-grants, + membership-remove-revokes) and records the hand-over receipt. + auth: the attended consumer-owner session on lane apps-consumer-handover (actor human, + parent-session revoked per attempt); the consumer configuration itself is the + owner's own deployment path + lane: apps-consumer-handover + warden_executes: false + attended: true + value_handling: none + attended_justification: The verification needs a real member login and a refused + non-member login, which only an attended session can perform, and it follows + the owner's own Helm change in the same consumer-owner session as + deliver-consumer-secret. Warden's case kernel dispatches the reviewed adapter + only inside that session (actor human, parent-session revoked per attempt); the + human session is the authority and the kernel is only the dispatcher. + verification: + - id: authorize-accepts-redirects + check: GET {issuer}/authorize with client {client_id} and each of {redirect_uris} + returns the login page + expect: present + reads_secret_value: false + - id: authorize-refuses-other-redirect + check: GET {issuer}/authorize with client {client_id} and an unregistered redirect + returns an OAuth error + expect: deny + reads_secret_value: false + - id: custody-denied-to-default + when: client_type == confidential + check: a default-only OpenBao identity has no read capability on bao:platform/workloads/keycape/{client_id} + expect: deny + reads_secret_value: false + - id: custody-allowed-to-consumer + when: client_type == confidential + check: policy workload-kv-read-keycape-{client_id}-{consuming_service} grants + read on bao:platform/workloads/keycape/{client_id} (capabilities check only) + expect: allow + reads_secret_value: false + - id: groups-present + check: LLDAP groups {access_groups} exist, with exactly the memberships of the + accepted case added + expect: present + reads_secret_value: false + - id: consumer-enforces-groups + check: '{consuming_service}''s OIDC source for {client_id} (owner {consumer_owner_repo}) + requires one of {access_groups} in the groups claim (configuration read, no + secret value)' + expect: present + reads_secret_value: false + - id: secret-delivered + when: client_type == confidential + check: the delivery for sso/keycape-{client_id}-client reports Ready=True (status + only) + expect: present + reads_secret_value: false + - id: mfa-pinned + when: mfa_required == true + check: keycape-client.py inspect for {client_id} shows the MFA pin on the registered + client (non-secret client view) + expect: present + reads_secret_value: false + - id: consumer-secret-delivered + when: client_type == confidential + check: '{consumer_namespace} SecretStore openbao-keycape-{client_id}-consumer + and ExternalSecret {consuming_service}-netkingdom-oidc-client Ready=True, exact + dedicated SA/audience role binding, fixed CLIENT_SECRET mapping; no Secret value + read' + expect: present + reads_secret_value: false + rollback: + - reverses: hand-over + action: the adapter removes only its hand-over receipt. Removing the OIDC source + for {client_id} from {consuming_service} is the owner's Helm rollback (owner + {consumer_owner_repo}), done inside deliver-consumer-secret's owner_remove + before any consumer object is removed + - reverses: deliver-consumer-secret + when: client_type == confidential + action: After reverting consumer secret reference, delete only authenticated created + ExternalSecret/owned target by UID and ownerReference, then SecretStore and + dedicated SA; preserve preexisting-matched resources and never delete a shared + target. Retain audit and ownership receipts; removing SecretStore/SA alone is + not immediate OpenBao token revocation. + - reverses: register-client + action: keycape-client.py apply --operation remove for {client_id} + - reverses: bind-env + when: client_type == confidential + action: remove env KEYCAPE_{client_id:env}_CLIENT_SECRET from Deployment sso/keycape, + after register-client is reversed + - reverses: deliver-secret + when: client_type == confidential + action: delete ExternalSecret sso/keycape-{client_id}-client and its target + - reverses: mint-secret + when: client_type == confidential + action: 'covered by client-secret-path: one metadata delete, after deliver-secret + is reversed' + - reverses: ensure-groups + action: identity-provisioner revoke_service_groups inside the provisioner pod + (attended, as ensure-groups) with the case's memberships; delete_if_empty only + if no other case uses {access_groups} + - reverses: client-secret-path + when: client_type == confidential + action: delete metadata and all versions at platform/workloads/keycape/{client_id}, + after the ExternalSecret is gone (this also reverses mint-secret) + - reverses: eso-role + when: client_type == confidential + action: delete Kubernetes-auth role external-secrets-keycape-{client_id} + - reverses: keycape-reader-policy + when: client_type == confidential + action: delete policy workload-kv-read-keycape-{client_id}-eso, after eso-role + - reverses: consumer-eso-role + when: client_type == confidential + action: Delete only this case-created role external-secrets-keycape-{client_id}-{consuming_service}, + before deleting consumer-reader-policy; revoke case-issued OpenBao tokens or + prove expiration before declaring deactivation. + - reverses: consumer-reader-policy + when: client_type == confidential + action: delete policy workload-kv-read-keycape-{client_id}-{consuming_service} + - reverses: catalog-entry + action: remove catalog entry {consuming_service}-keycape-client + acceptance: + accepted_by: + - owner + - founder + risk_class: medium + # Reviewed machine descriptors (NK-WP-0048-T16 successor candidate). Digests are + # generated by sso-mfa/k8s/keycape/compose-1.2-descriptors.py --version 1.3. + execution_descriptors: + bind-env: sha256:d5fcb20cf15bf7454890d2529180c582218e3df12584a99162e4b664b899caa0 + deliver-consumer-secret: sha256:36281df8139fb1ec64b226d94f80eb592de5addab7063ff54c691c641e275fc7 + deliver-secret: sha256:75aaa7346aebdea2a0eb0ba993995515b5248ffefc34caf8e713525e9b58da85 + ensure-groups: sha256:ebc9d5b9167d18e6015f4bbeb9699e8d396d64fbfaf195142781f84f4f47a70a + hand-over: sha256:1bce07a3229b7d0c7c730f35ce5b40fe471009a82de8dd53788ed970a9be1224 + mint-secret: sha256:c92957227235db04953809d92b260d32c8b947472a8e8fd17a258ade3ae8da70 + register-client: sha256:4ecc769f75433b66c7161f13c0fe7e5599633cf466d46cb6d8031f0456f426e7 diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/README.md b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/README.md new file mode 100644 index 0000000..9667238 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/README.md @@ -0,0 +1,43 @@ +# F2 proposed source publication packet + +Prepared local integration only; not executed against either canonical checkout. +`manifest.json` lists exact source hashes and absent preimages. Platform HEAD was +`0a9617a62d4696a8070798923b459e8405c3dbf2`; staged `.custodian-brief.md` and +unstaged `WORK-RECORDS.md` already existed. Preserve them. + +Check, then apply the four reviewed files: + +```bash +python3 /home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/integrate_platform.py +python3 /home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/integrate_platform.py --apply +``` + +The script checks the exact HEAD, absent tracked/worktree preimages, hashes and +symlink safety; it writes only the four manifest paths. It never stages, commits, +syncs, applies native resources or edits NetKingdom. The source remains explicitly +proposed/source-only, with no inferred owner assent. It verifies that unrelated +Git status and index bytes are unchanged. A concurrent failure may leave a +partial set of newly created files; inspect it rather than reset unrelated dirt. + +Run the focused test in Platform, then stage only these paths. To keep the +preexisting staged brief out of the source commit, use a path-scoped commit: + +```bash +git add -- docs/keycape-issuer-eso-contract.md manifests/templates/keycape-issuer-eso.yaml scripts/keycape_issuer_eso.py tests/test_keycape_issuer_eso.py +git commit --only -m "Prepare proposed fixed KeyCape issuer ESO source contract" -- docs/keycape-issuer-eso-contract.md manifests/templates/keycape-issuer-eso.yaml scripts/keycape_issuer_eso.py tests/test_keycape_issuer_eso.py +``` + +Root handles the already-authorized normal repository sync and its exact-commit +receipt. No task is marked done. `progress-paragraph.md` explains the completed +source-fit work and remaining real implementation. No legitimate Platform +workplan task was found: `RPF-1.3-T4` lives in the original owner-review JSON only. +Do not append the paragraph to an unrelated finished workplan or create a new one. + +`netkingdom-entry-point.conditional.patch` is the unchanged exact proposed-1.3 +entry text from the reviewed packet. Keep it unapplied until F3 is combined and +the existing combined artifact regenerated. It edits only the issuer delivery +entry point, pins the exact template digest and grants no adoption or activation. + +`integration-checks.json` records the isolated scratch application, preservation +of synthetic staged/unstaged dirt, rejection of non-absent preimages, and eight +passing focused tests. Tests used a minimal environment and local Git only. diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/canonical-validation.json b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/canonical-validation.json new file mode 100644 index 0000000..f539981 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/canonical-validation.json @@ -0,0 +1,5 @@ +{ + "first_attempt": "missing /home/worsch/railiance-platform/.venv/bin/python; exit127; tests did not run", + "rerun": "env PYTHONDONTWRITEBYTECODE=1 HOME=/tmp PATH=/usr/bin:/bin python3 -B -m unittest discover -s tests -p test_keycape_issuer_eso.py -q", + "result": "8 tests passed; fixture-only metadata, no native/API calls" +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/consistency.json b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/consistency.json new file mode 100644 index 0000000..08e3dfd --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/consistency.json @@ -0,0 +1,604 @@ +{ + "repo_slug": "railiance-platform", + "repo_path": "/home/worsch/railiance-platform", + "source_revision": "104ed90f42a9a86bf9fda343b49b65a85c9ee4b9", + "source_revision_reason": null, + "issues": [ + { + "severity": "WARN", + "check_id": "C-35", + "message": "Repo Manager missing: README.md: README.md is required for flavor tooling", + "file_path": "README.md", + "db_id": "", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-40", + "message": "blocker satisfied, task still wait: 'RPF-WP-0015-T03' in workplan 'RPF-WP-0015' \u2014 decision 2fd5849c-240e-4d0a-bf07-5ebf8dfce3fd is resolved", + "file_path": "workplans/RPF-WP-0015-audit-core-custody-and-recovery-coordination.md#RPF-WP-0015-T03", + "db_id": "", + "file_value": "2fd5849c-240e-4d0a-bf07-5ebf8dfce3fd", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-40", + "message": "blocker satisfied, task still wait: 'RPF-WP-0035-T02' in workplan 'RPF-WP-0035' \u2014 decision 62aa95e4-3ccc-4867-8806-5e3db005b483 is resolved", + "file_path": "workplans/RPF-WP-0035-credential-lane-implementation.md#RPF-WP-0035-T02", + "db_id": "", + "file_value": "62aa95e4-3ccc-4867-8806-5e3db005b483", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-40", + "message": "blocker satisfied, task still wait: 'RPF-WP-0035-T03' in workplan 'RPF-WP-0035' \u2014 decision 62aa95e4-3ccc-4867-8806-5e3db005b483 is resolved", + "file_path": "workplans/RPF-WP-0035-credential-lane-implementation.md#RPF-WP-0035-T03", + "db_id": "", + "file_value": "62aa95e4-3ccc-4867-8806-5e3db005b483", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-40", + "message": "blocker satisfied, task still wait: 'RPF-WP-0035-T07' in workplan 'RPF-WP-0035' \u2014 decision 1591daa1-6e85-4dfd-aadd-5a9fe1ef47fa is resolved", + "file_path": "workplans/RPF-WP-0035-credential-lane-implementation.md#RPF-WP-0035-T07", + "db_id": "", + "file_value": "1591daa1-6e85-4dfd-aadd-5a9fe1ef47fa", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-40", + "message": "blocker satisfied, task still wait: 'RPF-WP-0035-T08' in workplan 'RPF-WP-0035' \u2014 decision ab85ae8a-12a0-4945-ae64-1d3d86d5b26f is resolved", + "file_path": "workplans/RPF-WP-0035-credential-lane-implementation.md#RPF-WP-0035-T08", + "db_id": "", + "file_value": "ab85ae8a-12a0-4945-ae64-1d3d86d5b26f", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-40", + "message": "blocker satisfied, task still wait: 'RPF-WP-0036-T03' in workplan 'RPF-WP-0036' \u2014 decision 89f1058f-33bc-47a8-8a49-0b44d426c67e is resolved", + "file_path": "workplans/RPF-WP-0036-platform-service-assurance.md#RPF-WP-0036-T03", + "db_id": "", + "file_value": "89f1058f-33bc-47a8-8a49-0b44d426c67e", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-39", + "message": "Wait task 'RPF-WP-0038-T04' in workplan 'RPF-WP-0038' has neither depends_on, needs_human: true, blocked_on: message-from:\u2026 nor wait_until \u2014 unqualified wait", + "file_path": "workplans/RPF-WP-0038-forgejo-scaleway-primary-coverage.md#RPF-WP-0038-T04", + "db_id": "", + "file_value": "wait", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-40", + "message": "blocker satisfied, task still wait: 'RPF-WP-0043-T03' in workplan 'RPF-WP-0043' \u2014 decision 89f1058f-33bc-47a8-8a49-0b44d426c67e is resolved", + "file_path": "workplans/RPF-WP-0043-policy-nexus-argocd-onboarding.md#RPF-WP-0043-T03", + "db_id": "", + "file_value": "89f1058f-33bc-47a8-8a49-0b44d426c67e", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-40", + "message": "blocker satisfied, task still wait: 'RPF-WP-0043-T04' in workplan 'RPF-WP-0043' \u2014 decision 89f1058f-33bc-47a8-8a49-0b44d426c67e is resolved", + "file_path": "workplans/RPF-WP-0043-policy-nexus-argocd-onboarding.md#RPF-WP-0043-T04", + "db_id": "", + "file_value": "89f1058f-33bc-47a8-8a49-0b44d426c67e", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-40", + "message": "blocker satisfied, task still wait: 'RPF-WP-0044-T04' in workplan 'RPF-WP-0044' \u2014 decision 89f1058f-33bc-47a8-8a49-0b44d426c67e is resolved", + "file_path": "workplans/RPF-WP-0044-argocd-phase-b-adopt-existing-applications.md#RPF-WP-0044-T04", + "db_id": "", + "file_value": "89f1058f-33bc-47a8-8a49-0b44d426c67e", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-40", + "message": "blocker satisfied, task still wait: 'RPF-WP-0044-T06' in workplan 'RPF-WP-0044' \u2014 decision 89f1058f-33bc-47a8-8a49-0b44d426c67e is resolved", + "file_path": "workplans/RPF-WP-0044-argocd-phase-b-adopt-existing-applications.md#RPF-WP-0044-T06", + "db_id": "", + "file_value": "89f1058f-33bc-47a8-8a49-0b44d426c67e", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-40", + "message": "blocker satisfied, task still wait: 'RPF-WP-0044-T07' in workplan 'RPF-WP-0044' \u2014 decision 89f1058f-33bc-47a8-8a49-0b44d426c67e is resolved", + "file_path": "workplans/RPF-WP-0044-argocd-phase-b-adopt-existing-applications.md#RPF-WP-0044-T07", + "db_id": "", + "file_value": "89f1058f-33bc-47a8-8a49-0b44d426c67e", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-40", + "message": "blocker satisfied, task still wait: 'RPF-WP-0048-T02' in workplan 'RPF-WP-0048' \u2014 decision 89f1058f-33bc-47a8-8a49-0b44d426c67e is resolved", + "file_path": "workplans/RPF-WP-0048-activity-core-gitops-adoption.md#RPF-WP-0048-T02", + "db_id": "", + "file_value": "89f1058f-33bc-47a8-8a49-0b44d426c67e", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-23", + "message": "Lifecycle drift in 'rpf-wp-0053': workplan status 'proposed' has an active task \u2014 report only (ADR-007: do not auto-promote proposed)", + "file_path": "workplans/RPF-WP-0053-coulomb-paired-backup-admission.md", + "db_id": "cb55e474-c7a5-5c6f-bd72-ce65f853e3e3", + "file_value": "proposed", + "db_value": "proposed", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-39", + "message": "Wait task 'RPF-WP-0058-T05' in workplan 'RPF-WP-0058' has neither depends_on, needs_human: true, blocked_on: message-from:\u2026 nor wait_until \u2014 unqualified wait", + "file_path": "workplans/RPF-WP-0058-reuse-private-source-reader.md#RPF-WP-0058-T05", + "db_id": "", + "file_value": "wait", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-20", + "message": "Workplan dependency target 'RAILIANCE-WP-0004' is not linked to State Hub", + "file_path": "workplans/archived/260702-RPF-WP-0023-workload-kv-access-lanes.md", + "db_id": "", + "file_value": "RAILIANCE-WP-0004", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-20", + "message": "Workplan dependency target 'RAILIANCE-WP-0005' is not linked to State Hub", + "file_path": "workplans/archived/260702-RPF-WP-0024-credential-change-approval-workflow.md", + "db_id": "", + "file_value": "RAILIANCE-WP-0005", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-20", + "message": "Workplan dependency target 'RAILIANCE-WP-0006' is not linked to State Hub", + "file_path": "workplans/archived/260702-RPF-WP-0024-credential-change-approval-workflow.md", + "db_id": "", + "file_value": "RAILIANCE-WP-0006", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-20", + "message": "Workplan dependency target 'RAILIANCE-WP-0005' is not linked to State Hub", + "file_path": "workplans/archived/260905-RPF-WP-0002-openbao-approved-automation-delegation.md", + "db_id": "", + "file_value": "RAILIANCE-WP-0005", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-20", + "message": "Workplan dependency target 'RAILIANCE-WP-0006' is not linked to State Hub", + "file_path": "workplans/archived/260905-RPF-WP-0002-openbao-approved-automation-delegation.md", + "db_id": "", + "file_value": "RAILIANCE-WP-0006", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-20", + "message": "Workplan dependency target 'RAILIANCE-WP-0007' is not linked to State Hub", + "file_path": "workplans/archived/260905-RPF-WP-0002-openbao-approved-automation-delegation.md", + "db_id": "", + "file_value": "RAILIANCE-WP-0007", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-20", + "message": "Workplan dependency target 'RAILIANCE-WP-0004' is not linked to State Hub", + "file_path": "workplans/archived/260905-RPF-WP-0003-issue-core-runtime-ingestion-key-lane.md", + "db_id": "", + "file_value": "RAILIANCE-WP-0004", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-20", + "message": "Workplan dependency target 'RAILIANCE-WP-0007' is not linked to State Hub", + "file_path": "workplans/archived/260905-RPF-WP-0003-issue-core-runtime-ingestion-key-lane.md", + "db_id": "", + "file_value": "RAILIANCE-WP-0007", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-20", + "message": "Workplan dependency target 'RAILIANCE-WP-0008' is not linked to State Hub", + "file_path": "workplans/archived/260905-RPF-WP-0003-issue-core-runtime-ingestion-key-lane.md", + "db_id": "", + "file_value": "RAILIANCE-WP-0008", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-20", + "message": "Workplan dependency target 'RAILIANCE-WP-0004' is not linked to State Hub", + "file_path": "workplans/archived/260905-RPF-WP-0004-llm-connect-openrouter-provider-key-lane.md", + "db_id": "", + "file_value": "RAILIANCE-WP-0004", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-20", + "message": "Workplan dependency target 'RAILIANCE-WP-0007' is not linked to State Hub", + "file_path": "workplans/archived/260905-RPF-WP-0004-llm-connect-openrouter-provider-key-lane.md", + "db_id": "", + "file_value": "RAILIANCE-WP-0007", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-20", + "message": "Workplan dependency target 'RAILIANCE-WP-0008' is not linked to State Hub", + "file_path": "workplans/archived/260905-RPF-WP-0004-llm-connect-openrouter-provider-key-lane.md", + "db_id": "", + "file_value": "RAILIANCE-WP-0008", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-20", + "message": "Workplan dependency target 'RAILIANCE-WP-0007' is not linked to State Hub", + "file_path": "workplans/archived/260905-RPF-WP-0005-reuse-surface-runtime-secrets-openbao-lane.md", + "db_id": "", + "file_value": "RAILIANCE-WP-0007", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-20", + "message": "Workplan dependency target 'RAILIANCE-WP-0008' is not linked to State Hub", + "file_path": "workplans/archived/260905-RPF-WP-0005-reuse-surface-runtime-secrets-openbao-lane.md", + "db_id": "", + "file_value": "RAILIANCE-WP-0008", + "db_value": "", + "fixable": false + }, + { + "severity": "INFO", + "check_id": "C-08", + "message": "Retired DB workstream 'railiance-wp-0003@retired-20260826' (id=665b3b9b\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work", + "file_path": "", + "db_id": "665b3b9b-608a-4be4-84b6-dcb8261ff57b", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "INFO", + "check_id": "C-08", + "message": "Retired DB workstream 'railiance-platform-railiance-wp-0004@retired-20260826' (id=e57e487b\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work", + "file_path": "", + "db_id": "e57e487b-8557-439d-8093-0457c73ede93", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "INFO", + "check_id": "C-08", + "message": "Retired DB workstream 'railiance-platform-railiance-wp-0005@retired-20260826' (id=2731fece\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work", + "file_path": "", + "db_id": "2731fece-6c49-45b8-ab8a-4ea6c04ac603", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "INFO", + "check_id": "C-08", + "message": "Retired DB workstream 'railiance-platform-railiance-wp-0006@retired-20260826' (id=96c8a93d\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work", + "file_path": "", + "db_id": "96c8a93d-7a5a-4fa9-8f7b-865119551da3", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "INFO", + "check_id": "C-08", + "message": "Retired DB workstream 'railiance-platform-railiance-wp-0007@retired-20260826' (id=4d7ce243\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work", + "file_path": "", + "db_id": "4d7ce243-f40a-4249-a46a-a24f75d6fe4c", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "INFO", + "check_id": "C-08", + "message": "Retired DB workstream 'railiance-platform-railiance-wp-0008@retired-20260826' (id=671898ef\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work", + "file_path": "", + "db_id": "671898ef-2378-4814-b8f6-066148cdad46", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "INFO", + "check_id": "C-08", + "message": "Retired DB workstream 'railiance-platform-railiance-wp-0009@retired-20260826' (id=b059c81d\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work", + "file_path": "", + "db_id": "b059c81d-96f1-451f-896f-a05cd73744a1", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "INFO", + "check_id": "C-08", + "message": "Retired DB workstream 'railiance-platform-railiance-wp-0010@retired-20260826' (id=f364d405\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work", + "file_path": "", + "db_id": "f364d405-a85d-4b89-b600-1964ab436cad", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "INFO", + "check_id": "C-08", + "message": "Retired DB workstream 'railiance-wp-0029@retired-20260826' (id=038bc3c0\u2026, status=blocked) has no backing workplan file \u2014 projection tombstone, not live work", + "file_path": "", + "db_id": "038bc3c0-4492-5b91-95eb-ae515ca205df", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "INFO", + "check_id": "C-08", + "message": "Retired DB workstream 'railiance-wp-0024@retired-20260826' (id=88c4ef7f\u2026, status=blocked) has no backing workplan file \u2014 projection tombstone, not live work", + "file_path": "", + "db_id": "88c4ef7f-0af8-580e-90dc-a2bae2675a4d", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "INFO", + "check_id": "C-08", + "message": "Retired DB workstream 'railiance-platform-railiance-wp-0011@retired-20260826' (id=3f9fd8de\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work", + "file_path": "", + "db_id": "3f9fd8de-7235-4486-aaa7-634dba2bb6fa", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "INFO", + "check_id": "C-08", + "message": "Retired DB workstream 'railiance-wp-0027@retired-20260826' (id=6f8a6cbc\u2026, status=blocked) has no backing workplan file \u2014 projection tombstone, not live work", + "file_path": "", + "db_id": "6f8a6cbc-c076-5f0a-ade2-281a7ec71360", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "INFO", + "check_id": "C-08", + "message": "Retired DB workstream 'railiance-wp-0017@retired-20260826' (id=16e8f234\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work", + "file_path": "", + "db_id": "16e8f234-9305-5ff3-af82-4639215a0c54", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "INFO", + "check_id": "C-08", + "message": "Retired DB workstream 'railiance-wp-0028@retired-20260826' (id=022dda1f\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work", + "file_path": "", + "db_id": "022dda1f-8772-57ad-862e-672924455f11", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "INFO", + "check_id": "C-08", + "message": "Retired DB workstream 'railiance-wp-0015@retired-20260826' (id=1c73af72\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work", + "file_path": "", + "db_id": "1c73af72-fd4b-5ab6-8ff7-a3c302bf55a5", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "INFO", + "check_id": "C-08", + "message": "Retired DB workstream 'railiance-wp-0022@retired-20260826' (id=a7977cbf\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work", + "file_path": "", + "db_id": "a7977cbf-ab43-56b7-b7a9-ceb52382b341", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "INFO", + "check_id": "C-08", + "message": "Retired DB workstream 'railiance-wp-0025@retired-20260826' (id=2422d4e7\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work", + "file_path": "", + "db_id": "2422d4e7-2125-591e-8e95-80ecc6e09da0", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "INFO", + "check_id": "C-08", + "message": "Retired DB workstream 'railiance-wp-0016@retired-20260826' (id=857ca302\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work", + "file_path": "", + "db_id": "857ca302-bb94-5d41-a649-1aa966368df4", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "INFO", + "check_id": "C-08", + "message": "Retired DB workstream 'railiance-wp-0026@retired-20260826' (id=73878f8d\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work", + "file_path": "", + "db_id": "73878f8d-d8bb-5845-8a5c-91cdd83c1066", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "INFO", + "check_id": "C-08", + "message": "Retired DB workstream 'railiance-wp-0023@retired-20260826' (id=ec52fbe6\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work", + "file_path": "", + "db_id": "ec52fbe6-571b-570e-884b-c7a32ff7bcc8", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-25", + "message": "Owner-return task 'RPF-WP-0035-T08' waiting on audit-core has 9 reply/replies after its last dated note (or unread when undated) \u2014 owner action needed: 570ca050-0316-4e38-9c16-be41057c2330, 2de1c6e7-9602-48e5-868d-0048fbbef150, e7c36b17-d8f7-4d86-af09-02111289d1f5, 5fe769a7-d35b-4e37-a671-673d13cf8948, b3c564a6-93f9-4c07-872b-7feb22cccb73, ec4995b5-e1b2-4c66-9f8d-5ac8d6ceb149, 7c4019f2-348d-475e-9c96-e1ff6d0d27f5, ff43fd2c-7633-4b34-9ae6-2483ba03a271, 0d0da0d2-36a1-4ffc-b7bf-7896fad691f6", + "file_path": "workplans/RPF-WP-0035-credential-lane-implementation.md#RPF-WP-0035-T08", + "db_id": "", + "file_value": "message-from:audit-core", + "db_value": "", + "fixable": false, + "repo_slug": "railiance-platform", + "source_revision": "104ed90f42a9a86bf9fda343b49b65a85c9ee4b9", + "source_revision_reason": null, + "owner_return": { + "task_record_id": "RPF-WP-0035-T08", + "message_ids": [ + "570ca050-0316-4e38-9c16-be41057c2330", + "2de1c6e7-9602-48e5-868d-0048fbbef150", + "e7c36b17-d8f7-4d86-af09-02111289d1f5", + "5fe769a7-d35b-4e37-a671-673d13cf8948", + "b3c564a6-93f9-4c07-872b-7feb22cccb73", + "ec4995b5-e1b2-4c66-9f8d-5ac8d6ceb149", + "7c4019f2-348d-475e-9c96-e1ff6d0d27f5", + "ff43fd2c-7633-4b34-9ae6-2483ba03a271", + "0d0da0d2-36a1-4ffc-b7bf-7896fad691f6" + ] + } + }, + { + "severity": "WARN", + "check_id": "C-25", + "message": "Owner-return task 'RPF-WP-0056-T03' waiting on railiance-cluster has 9 reply/replies after its last dated note (or unread when undated) \u2014 owner action needed: 4e9d4e58-71b3-497b-8954-cedc30740529, 13acb774-8fbe-4ca3-b44d-89a4742138bd, c9435491-8545-4d48-a57a-1773f2789092, fb8f25df-57ce-40e5-ae67-0c0b392746e8, 4bb0d7af-b644-44f3-acc8-69c7321e9824, 5301d4fb-0631-4ac5-8471-531e5362b2ba, 2087b9ac-c980-4881-89f1-6b1088132f5b, 78b89fb2-ced6-45f1-9718-0670b8257d45, 110e8dea-ce81-40ff-8161-325ddb89cd67", + "file_path": "workplans/RPF-WP-0056-playwright-consumer-admission.md#RPF-WP-0056-T03", + "db_id": "", + "file_value": "message-from:railiance-cluster", + "db_value": "", + "fixable": false, + "repo_slug": "railiance-platform", + "source_revision": "104ed90f42a9a86bf9fda343b49b65a85c9ee4b9", + "source_revision_reason": null, + "owner_return": { + "task_record_id": "RPF-WP-0056-T03", + "message_ids": [ + "4e9d4e58-71b3-497b-8954-cedc30740529", + "13acb774-8fbe-4ca3-b44d-89a4742138bd", + "c9435491-8545-4d48-a57a-1773f2789092", + "fb8f25df-57ce-40e5-ae67-0c0b392746e8", + "4bb0d7af-b644-44f3-acc8-69c7321e9824", + "5301d4fb-0631-4ac5-8471-531e5362b2ba", + "2087b9ac-c980-4881-89f1-6b1088132f5b", + "78b89fb2-ced6-45f1-9718-0670b8257d45", + "110e8dea-ce81-40ff-8161-325ddb89cd67" + ] + } + }, + { + "severity": "WARN", + "check_id": "C-28", + "message": "14 unread message(s) lack thread_id for supersession tracking: the-custodian:836a8502, the-custodian:72c6a148, audit-core:0d0da0d2, rapp-postgres:6d047a08, railiance-platform:c4a80a60", + "file_path": "", + "db_id": "", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-26", + "message": "Workplan id 'RAIL-PL-WP-0002' uses prefix 'RAIL-PL-WP' but repo canonical prefix is 'RPF-WP' \u2014 new plans must conform", + "file_path": "workplans/archived/260702-RAIL-PL-WP-0002-openbao-platform-secrets-service.md", + "db_id": "", + "file_value": "RAIL-PL-WP-0002", + "db_value": "RPF-WP", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-26", + "message": "Workplan id 'RAIL-PL-WP-0001' uses prefix 'RAIL-PL-WP' but repo canonical prefix is 'RPF-WP' \u2014 new plans must conform", + "file_path": "workplans/archived/RAIL-PL-WP-0001-platform-baseline.md", + "db_id": "", + "file_value": "RAIL-PL-WP-0001", + "db_value": "RPF-WP", + "fixable": false + } + ], + "fixes_applied": [ + "bindings: synced 64/64", + "C-17 cleared: pushed 1 backlogged commit(s)", + "push: pushed" + ], + "summary": { + "fail": 0, + "automation_error": 0, + "warn": 35, + "info": 20 + }, + "result": "warn" +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/consistency.stderr.txt b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/consistency.stderr.txt new file mode 100644 index 0000000..e69de29 diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/integrate_platform.py b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/integrate_platform.py new file mode 100644 index 0000000..f05f204 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/integrate_platform.py @@ -0,0 +1,84 @@ +"""Four-file local source integration only. Default check; --apply writes files. + +No staging, commit, sync, native call, credentials or NetKingdom mutation. +""" +import argparse +import hashlib +import json +import os +from pathlib import Path +import subprocess + +HERE = Path(__file__).resolve().parent +BASE = '0a9617a62d4696a8070798923b459e8405c3dbf2' +FILES = ('docs/keycape-issuer-eso-contract.md', + 'manifests/templates/keycape-issuer-eso.yaml', + 'scripts/keycape_issuer_eso.py', 'tests/test_keycape_issuer_eso.py') +def sha(data): + return hashlib.sha256(data).hexdigest() +def main(): + parser = argparse.ArgumentParser(__doc__) + parser.add_argument('--repo', type=Path, default=Path('/home/worsch/railiance-platform')) + parser.add_argument('--apply', action='store_true') + args = parser.parse_args() + repo = args.repo.resolve() + env = {'PATH': '/usr/local/bin:/usr/bin:/bin', 'HOME': '/tmp', 'GIT_OPTIONAL_LOCKS': '0'} + def git(*words): + result = subprocess.run(['git', '-C', str(repo), *words], env=env, + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + if result.returncode: + raise SystemExit('Refused: local Git check failed; no command output emitted') + return result.stdout + if git('rev-parse', 'HEAD').decode().strip() != BASE: + raise SystemExit('Refused: Platform HEAD changed; rebase/review exact inputs first') + if Path(git('rev-parse', '--show-toplevel').decode().strip()).resolve() != repo: + raise SystemExit('Refused: repository root differs') + manifest = json.loads((HERE / 'manifest.json').read_text()) + entries = {x['path']: x for x in manifest['files']} + if set(entries) != set(FILES) or len(manifest['files']) != 4: + raise SystemExit('Refused: four-file scope differs') + prepared = {} + for name in FILES: + target = repo / name + for parent in (target, *target.parents): + if parent == repo.parent: + break + if parent.is_symlink(): + raise SystemExit('Refused: target or parent is a symlink') + if target.exists() or git('ls-files', '--', name): + raise SystemExit('Refused: expected absent preimage differs: ' + name) + data = (HERE / 'platform-source' / name).read_bytes() + if sha(data) != entries[name]['sha256']: + raise SystemExit('Refused: prepared source hash differs: ' + name) + prepared[name] = data + # Git status gets no lock; do not stage, refresh, reset or replace the index. + excluded = [':(exclude)' + name for name in FILES] + def state(): + index_name = git('rev-parse', '--git-path', 'index').decode().strip() + index = Path(index_name) + if not index.is_absolute(): + index = repo / index + return {'other_status_sha256': sha(git('status', '--porcelain=v1', '-z', + '--untracked-files=all', '--', '.', *excluded)), + 'index_sha256': sha(index.read_bytes()) if index.exists() else None} + before = state() + if not args.apply: + print(json.dumps({'outcome': 'checked-no-write', 'files': list(FILES), + 'preserved_state': before}, indent=2)) + return + # Exclusive creation independently refuses racing target creation. + for name, data in prepared.items(): + target = repo / name + target.parent.mkdir(parents=True, exist_ok=True) + with target.open('xb') as stream: + stream.write(data) + after = state() + if before != after: + raise SystemExit('Refused: unrelated status or index changed during integration; inspect concurrency') + if any(sha((repo / name).read_bytes()) != entries[name]['sha256'] for name in FILES): + raise SystemExit('Refused: post-write verification failed') + print(json.dumps({'outcome': 'applied-source-only', 'files': list(FILES), + 'before': before, 'after': after, 'workplan_changed': False, + 'source_owner_assent_inferred': False}, indent=2)) +if __name__ == '__main__': + main() diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/integration-checks.json b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/integration-checks.json new file mode 100644 index 0000000..2162356 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/integration-checks.json @@ -0,0 +1,39 @@ +{ + "check": { + "outcome": "checked-no-write", + "files": [ + "docs/keycape-issuer-eso-contract.md", + "manifests/templates/keycape-issuer-eso.yaml", + "scripts/keycape_issuer_eso.py", + "tests/test_keycape_issuer_eso.py" + ], + "preserved_state": { + "other_status_sha256": "b274ecee79380c3670afa2ff40790064bdbe0565ecea4cfc1eec586deb409990", + "index_sha256": "9c08aefaf66ea19285e143e7c8f0de6402b340307391d7433ba2772b5dd02cf7" + } + }, + "applied": { + "outcome": "applied-source-only", + "files": [ + "docs/keycape-issuer-eso-contract.md", + "manifests/templates/keycape-issuer-eso.yaml", + "scripts/keycape_issuer_eso.py", + "tests/test_keycape_issuer_eso.py" + ], + "before": { + "other_status_sha256": "b274ecee79380c3670afa2ff40790064bdbe0565ecea4cfc1eec586deb409990", + "index_sha256": "9c08aefaf66ea19285e143e7c8f0de6402b340307391d7433ba2772b5dd02cf7" + }, + "after": { + "other_status_sha256": "b274ecee79380c3670afa2ff40790064bdbe0565ecea4cfc1eec586deb409990", + "index_sha256": "9c08aefaf66ea19285e143e7c8f0de6402b340307391d7433ba2772b5dd02cf7" + }, + "workplan_changed": false, + "source_owner_assent_inferred": false + }, + "existing_preimage_refused": true, + "tests_returncode": 0, + "test_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.248s\n\nOK\n", + "canonical_mutation": false, + "native_calls": false +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/manifest.json b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/manifest.json new file mode 100644 index 0000000..49240b7 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/manifest.json @@ -0,0 +1,40 @@ +{ + "schema": "custodian.nk48-r5-f2-source-publication.v1", + "status": "prepared-not-executed", + "platform_base": "0a9617a62d4696a8070798923b459e8405c3dbf2", + "platform_source_status": "proposed-source-only", + "source_owner_assent_received": false, + "files": [ + { + "path": "docs/keycape-issuer-eso-contract.md", + "preimage": "absent", + "sha256": "7f59e59b17c58252982931ac61fe50dd7a07340add6c437fe48af36971eafe68" + }, + { + "path": "manifests/templates/keycape-issuer-eso.yaml", + "preimage": "absent", + "sha256": "e17498bd8a4c820041a4105e4efe9c60c6a50953db8f621ff54887080065c743" + }, + { + "path": "scripts/keycape_issuer_eso.py", + "preimage": "absent", + "sha256": "21c5797280c2a7825dbda8ac970d34a0cdef4c6458f68bce0b1c12407f122a87" + }, + { + "path": "tests/test_keycape_issuer_eso.py", + "preimage": "absent", + "sha256": "223d4c5b349062b9f93e6af79e8d5feda44377ba41a11726f6f2b762691e8308" + } + ], + "canonical_workplan_delta": null, + "workplan_discovery": "RPF-1.3-T4 exists only in the original Platform owner-review JSON, not in a Platform workplan. No workplan created or repurposed.", + "conditional_netkingdom": { + "base": "3236d5000e52e0643f33906a26a7825aa0b67465", + "patch_sha256": "6784a21fbd10bd62712be2e0f4929034601c94629aa74ccaa2e93fc815eecd61", + "status": "prepared-only; await F3 combined regeneration before application" + }, + "observed_dirt": [ + "M .custodian-brief.md", + " M WORK-RECORDS.md" + ] +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/netkingdom-entry-point.conditional.patch b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/netkingdom-entry-point.conditional.patch new file mode 100644 index 0000000..6a725fe --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/netkingdom-entry-point.conditional.patch @@ -0,0 +1,16 @@ +--- a/security-case-type-revisions/1.3/oidc-browser-relying-party/type.yaml ++++ b/security-case-type-revisions/1.3/oidc-browser-relying-party/type.yaml +@@ -269,8 +269,11 @@ + when: client_type == confidential + entry_point: reviewed adapter railiance-platform/keycape-deliver-secret, dispatched + by Warden's case kernel; Platform's deliver capability applies ExternalSecret +- sso/keycape-{client_id}-client (key client-secret) through the ESO store for +- role external-secrets-keycape-{client_id}, from bao:platform/workloads/keycape/{client_id} ++ sso/keycape-{client_id}-client (key client-secret) through fixed ClusterSecretStore ++ openbao-keycape-{client_id}-issuer (cluster-scoped, restricted to namespace sso), ++ defined by Platform-controlled manifests/templates/keycape-issuer-eso.yaml ++ (sha256:e17498bd8a4c820041a4105e4efe9c60c6a50953db8f621ff54887080065c743), ++ using role external-secrets-keycape-{client_id}, from bao:platform/workloads/keycape/{client_id} + auth: Platform's own delivery path on lane platform-secret-delivery, inside the human + aal2 session; ESO reads custody through role external-secrets-keycape-{client_id}; + Warden holds no Platform credential, and no value leaves custody diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/packet-hashes.json b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/packet-hashes.json new file mode 100644 index 0000000..b9e65dc --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/packet-hashes.json @@ -0,0 +1,12 @@ +{ + "README.md": "sha256:522392bd35d2e0b22fd097509361d1476200a361c2ced22603d43e5758aa691d", + "integrate_platform.py": "sha256:ef8a1647e54013c0c6f2c11c8f74ef43fc97ceaca99e8f8caa89ee16ff80efba", + "integration-checks.json": "sha256:0ab8b058414dde1be9c7fd6ee2cb3f246e4deef2036d17472e846fae23bba4b9", + "manifest.json": "sha256:c52692fb154cd46f1bfa62e7a0cda73241716b1024d992449e24e87e18750bbb", + "netkingdom-entry-point.conditional.patch": "sha256:6784a21fbd10bd62712be2e0f4929034601c94629aa74ccaa2e93fc815eecd61", + "platform-source/docs/keycape-issuer-eso-contract.md": "sha256:7f59e59b17c58252982931ac61fe50dd7a07340add6c437fe48af36971eafe68", + "platform-source/manifests/templates/keycape-issuer-eso.yaml": "sha256:e17498bd8a4c820041a4105e4efe9c60c6a50953db8f621ff54887080065c743", + "platform-source/scripts/keycape_issuer_eso.py": "sha256:21c5797280c2a7825dbda8ac970d34a0cdef4c6458f68bce0b1c12407f122a87", + "platform-source/tests/test_keycape_issuer_eso.py": "sha256:223d4c5b349062b9f93e6af79e8d5feda44377ba41a11726f6f2b762691e8308", + "progress-paragraph.md": "sha256:151199597ba37225e5d46922ae90453f7338c5602ff81b3d870b579278bef2d4" +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/platform-source/docs/keycape-issuer-eso-contract.md b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/platform-source/docs/keycape-issuer-eso-contract.md new file mode 100644 index 0000000..56cfa1e --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/platform-source/docs/keycape-issuer-eso-contract.md @@ -0,0 +1,68 @@ +# 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. diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/platform-source/manifests/templates/keycape-issuer-eso.yaml b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/platform-source/manifests/templates/keycape-issuer-eso.yaml new file mode 100644 index 0000000..406a29c --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/platform-source/manifests/templates/keycape-issuer-eso.yaml @@ -0,0 +1,46 @@ +# PROPOSED SOURCE ONLY — RPF-1.3-T4 / NK48-R5-F2; not admitted or applied. +# Platform controls this template. Only admitted client_id is substituted. +# Provision the store through Platform's separate reviewed owner path; the +# platform-secret-delivery lane applies ONLY the ExternalSecret document. +apiVersion: external-secrets.io/v1 +kind: ClusterSecretStore +metadata: + name: openbao-keycape-{client_id}-issuer +spec: + provider: + vault: + server: http://openbao.openbao.svc:8200 + path: platform + version: v2 + auth: + kubernetes: + mountPath: kubernetes + role: external-secrets-keycape-{client_id} + serviceAccountRef: + name: external-secrets + namespace: external-secrets + audiences: + - openbao + conditions: + - namespaces: + - sso +--- +apiVersion: external-secrets.io/v1 +kind: ExternalSecret +metadata: + name: keycape-{client_id}-client + namespace: sso +spec: + refreshInterval: 5m + secretStoreRef: + kind: ClusterSecretStore + name: openbao-keycape-{client_id}-issuer + target: + name: keycape-{client_id}-client + creationPolicy: Owner + deletionPolicy: Retain + data: + - secretKey: client-secret + remoteRef: + key: workloads/keycape/{client_id} + property: CLIENT_SECRET diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/platform-source/scripts/keycape_issuer_eso.py b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/platform-source/scripts/keycape_issuer_eso.py new file mode 100644 index 0000000..8acace3 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/platform-source/scripts/keycape_issuer_eso.py @@ -0,0 +1,70 @@ +"""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 _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 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/nk48-r5-f2-publication-r1/platform-source/tests/test_keycape_issuer_eso.py b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/platform-source/tests/test_keycape_issuer_eso.py new file mode 100644 index 0000000..fd6e75d --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/platform-source/tests/test_keycape_issuer_eso.py @@ -0,0 +1,129 @@ +"""Public metadata fixtures only: no API, credentials, or native calls.""" +import copy +import sys +import unittest +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "scripts")) +import keycape_issuer_eso as contract + + +class FixedIssuerContract(unittest.TestCase): + def observations(self): + resources = contract.documents("forgejo") + for resource in resources: + resource["metadata"].update(uid="public-fixture-uid", resourceVersion="7") + resource["status"] = {"conditions": [{"type": "Ready", "status": "True"}]} + return resources + + def test_independent_forgejo_binding(self): + # Independent owner-source example: CCR0041 role/SA/audience and the + # existing issuer ESO draft's provider/target/relative key convention. + store, external = contract.documents("forgejo") + self.assertEqual(store["kind"], "ClusterSecretStore") + self.assertEqual(store["metadata"], {"name": "openbao-keycape-forgejo-issuer"}) + self.assertEqual(store["spec"], { + "provider": {"vault": { + "server": "http://openbao.openbao.svc:8200", "path": "platform", "version": "v2", + "auth": {"kubernetes": {"mountPath": "kubernetes", + "role": "external-secrets-keycape-forgejo", + "serviceAccountRef": {"name": "external-secrets", "namespace": "external-secrets", + "audiences": ["openbao"]}}}}}, + "conditions": [{"namespaces": ["sso"]}]}) + self.assertEqual(external["metadata"], {"name": "keycape-forgejo-client", "namespace": "sso"}) + self.assertEqual(external["spec"], { + "refreshInterval": "5m", + "secretStoreRef": {"kind": "ClusterSecretStore", "name": "openbao-keycape-forgejo-issuer"}, + "target": {"name": "keycape-forgejo-client", "creationPolicy": "Owner", "deletionPolicy": "Retain"}, + "data": [{"secretKey": "client-secret", "remoteRef": { + "key": "workloads/keycape/forgejo", "property": "CLIENT_SECRET"}}]}) + + def test_valid_ready_metadata_allows_extra_controller_metadata(self): + store, external = self.observations() + contract.require_fixed_store(store, "forgejo") + contract.require_ready_delivery(external, "forgejo") + + def test_store_drift_refuses(self): + changes = [ + ("kind", "SecretStore"), + ("metadata.namespace", "sso"), + ("metadata.name", "caller-store"), + ("spec.provider.vault.server", "https://wrong.invalid"), + ("spec.provider.vault.path", "tenant"), + ("spec.provider.vault.auth.kubernetes.role", "external-secrets-keycape-sibling"), + ("spec.provider.vault.auth.kubernetes.serviceAccountRef.audiences", ["wrong"]), + ("spec.provider.vault.auth.kubernetes.serviceAccountRef.namespace", "sso"), + ("spec.conditions", [{"namespaces": ["sso", "other"]}]), + ] + for path, value in changes: + with self.subTest(path=path): + store = self.observations()[0] + mutate(store, path, value) + with self.assertRaises(contract.Refused): + contract.require_fixed_store(store, "forgejo") + + def test_external_drift_refuses(self): + changes = [ + ("metadata.namespace", ""), ("metadata.namespace", None), ("metadata.namespace", "other"), + ("metadata.name", "keycape-sibling-client"), + ("spec.secretStoreRef.kind", "SecretStore"), + ("spec.secretStoreRef.name", "openbao-keycape-forgejo-consumer"), + ("spec.target.name", "other"), ("spec.target.creationPolicy", "Merge"), + ("spec.target.deletionPolicy", "Delete"), + ("spec.data", [{"secretKey": "client-secret", "remoteRef": { + "key": "platform/workloads/keycape/forgejo", "property": "CLIENT_SECRET"}}]), + ("spec.data", [{"secretKey": "wrong", "remoteRef": { + "key": "workloads/keycape/forgejo", "property": "CLIENT_SECRET"}}]), + ("spec.data", [{"secretKey": "client-secret", "remoteRef": { + "key": "workloads/keycape/forgejo", "property": "wrong"}}]), + ] + for path, value in changes: + with self.subTest(path=path, value=value): + external = self.observations()[1] + mutate(external, path, value) + with self.assertRaises(contract.Refused): + contract.require_ready_delivery(external, "forgejo") + + def test_missing_or_unready_refuses(self): + for check, index in [(contract.require_fixed_store, 0), (contract.require_ready_delivery, 1)]: + for status in [None, {}, [], "bad", {"conditions": None}, + {"conditions": [{"type": "Ready", "status": "False"}]}]: + with self.subTest(check=check.__name__, status=status): + observed = self.observations()[index] + observed["status"] = status + with self.assertRaises(contract.Refused): + check(observed, "forgejo") + for malformed in [None, [], {}, {"metadata": None}]: + with self.assertRaises(contract.Refused): + check(malformed, "forgejo") + + def test_no_caller_store_or_path_override(self): + for invalid in ["", "f", "../forgejo", "forgejo/sibling", "Forgejo", "forgejo\n", "-forgejo", "f" * 49, None]: + with self.subTest(invalid=invalid), self.assertRaises(contract.Refused): + contract.documents(invalid) + with self.assertRaises(TypeError): + contract.external_secret("forgejo", store_name="caller-store") + + def test_type_admitted_trailing_dash_is_preserved(self): + self.assertEqual(contract.external_secret("forgejo-")["metadata"]["name"], + "keycape-forgejo--client") + self.assertEqual(contract.documents("forgejo-")[0]["metadata"]["name"], + "openbao-keycape-forgejo--issuer") + + def test_delivery_document_is_only_external_and_detached(self): + external = contract.external_secret("forgejo") + self.assertEqual(external["kind"], "ExternalSecret") + external["spec"]["secretStoreRef"]["name"] = "tampered" + self.assertEqual(contract.external_secret("forgejo")["spec"]["secretStoreRef"]["name"], + "openbao-keycape-forgejo-issuer") + + +def mutate(resource, path, value): + parts = path.split(".") + for key in parts[:-1]: + resource = resource[key] + resource[parts[-1]] = copy.deepcopy(value) + + +if __name__ == "__main__": + unittest.main(verbosity=2) diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/progress-paragraph.md b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/progress-paragraph.md new file mode 100644 index 0000000..ab1af2a --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/progress-paragraph.md @@ -0,0 +1,19 @@ +2026-10-17 source preparation: the proposed fixed issuer ESO template +`manifests/templates/keycape-issuer-eso.yaml`, pure metadata helper +`scripts/keycape_issuer_eso.py`, focused fixtures and owner contract doc passed +independent Platform-scoped source-fit review (Custodian +`docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-review-r1/REVIEW.md`). This +supplies reviewable source for the exact-store-reference gap NK48-R5-F2 under +the existing owner-review obligation RPF-1.3-T4; it establishes no owner assent, +deployment, grant or lane activation. Real authenticated delivery/remove, +attempt-session revocation, authentic before/after provenance and metadata +receipts, preexisting/adoption protection, guarded created-only rollback, +installed ESO compatibility and Warden catalog routing remain incomplete. Do +not mark the delivery obligation done on the strength of template/helper source. + +Placement: reference material only. No legitimate Platform workplan/task for +RPF-1.3-T4 exists in the reviewed current source. The identifier originates in +`nk48-1.3-owner-review/owner-review-railiance-platform.json`, whose review explicitly +created no workplan. This paragraph is retained here for the existing coordinator +work record; it must not be appended to an unrelated finished Platform task or +used to invent a task, status, dependency or owner assent. diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/projection.json b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/projection.json new file mode 100644 index 0000000..9186ff9 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/projection.json @@ -0,0 +1,443 @@ +{ + "ok": true, + "status": "applied", + "repo_slug": "railiance-platform", + "commit": "104ed90f42a9a86bf9fda343b49b65a85c9ee4b9", + "requests": 2, + "elapsed_ms": 5105.9, + "git": { + "upstream": "origin/main", + "behind": 0, + "ahead": 0 + }, + "identifiers": { + "schema": "repo-manager.identifier-assignment.v1", + "ok": true, + "executed": true, + "repo": "railiance-platform", + "namespace": "helixforge", + "assignments": [], + "historical_invalid_identifiers": [], + "historical_identity_collisions": [], + "files_changed": [] + }, + "receipt": { + "schema": "state-hub.repository-projection-reconcile.v1", + "instance_role": "primary", + "instance_label": "railiance01", + "expected_commit": "104ed90f42a9a86bf9fda343b49b65a85c9ee4b9", + "derived_commit": "104ed90f42a9a86bf9fda343b49b65a85c9ee4b9", + "outcome": { + "schema": "state-hub.projection-reset.v1", + "repo_slug": "railiance-platform", + "commit": "104ed90f42a9a86bf9fda343b49b65a85c9ee4b9", + "status": "applied", + "counts": { + "created": 0, + "updated": 64, + "retired": 0, + "refused": 0, + "released": 0, + "created_tasks": 0, + "updated_tasks": 0, + "cancelled_tasks": 0, + "created_edges": 0, + "removed_edges": 0, + "unresolved_edges": 12, + "external_edges": 24 + }, + "created": [], + "updated": [ + "RAIL-PL-WP-0001", + "RAIL-PL-WP-0002", + "RPF-WP-0001", + "RPF-WP-0002", + "RPF-WP-0003", + "RPF-WP-0004", + "RPF-WP-0005", + "RPF-WP-0006", + "RPF-WP-0007", + "RPF-WP-0008", + "RPF-WP-0009", + "RPF-WP-0010", + "RPF-WP-0011", + "RPF-WP-0012", + "RPF-WP-0013", + "RPF-WP-0014", + "RPF-WP-0015", + "RPF-WP-0016", + "RPF-WP-0017", + "RPF-WP-0018", + "RPF-WP-0019", + "RPF-WP-0020", + "RPF-WP-0021", + "RPF-WP-0022", + "RPF-WP-0023", + "RPF-WP-0024", + "RPF-WP-0025", + "RPF-WP-0026", + "RPF-WP-0027", + "RPF-WP-0028", + "RPF-WP-0029", + "RPF-WP-0030", + "RPF-WP-0031", + "RPF-WP-0032", + "RPF-WP-0033", + "RPF-WP-0034", + "RPF-WP-0035", + "RPF-WP-0036", + "RPF-WP-0037", + "RPF-WP-0038", + "RPF-WP-0039", + "RPF-WP-0040", + "RPF-WP-0041", + "RPF-WP-0042", + "RPF-WP-0043", + "RPF-WP-0044", + "RPF-WP-0045", + "RPF-WP-0046", + "RPF-WP-0047", + "RPF-WP-0048", + "RPF-WP-0049", + "RPF-WP-0050", + "RPF-WP-0051", + "RPF-WP-0052", + "RPF-WP-0053", + "RPF-WP-0054", + "RPF-WP-0055", + "RPF-WP-0056", + "RPF-WP-0057", + "RPF-WP-0058", + "RPF-WP-0059", + "RPF-WP-ADHOC-2026-08-23", + "RPF-WP-ADHOC-2026-10-10", + "RPF-WP-ADHOC-2026-10-11" + ], + "retired": [], + "released": [], + "created_tasks": [], + "updated_tasks": [], + "cancelled_tasks": [], + "created_edges": [], + "removed_edges": [], + "unresolved_edges": [ + { + "workplan": "RAIL-PL-WP-0002", + "from": "RAIL-PL-WP-0002", + "target": "RAIL-PL-WP-0001", + "reason": "missing" + }, + { + "workplan": "RPF-WP-0002", + "from": "RPF-WP-0002", + "target": "RAILIANCE-WP-0005", + "reason": "missing" + }, + { + "workplan": "RPF-WP-0002", + "from": "RPF-WP-0002", + "target": "RAILIANCE-WP-0006", + "reason": "missing" + }, + { + "workplan": "RPF-WP-0002", + "from": "RPF-WP-0002", + "target": "RAILIANCE-WP-0007", + "reason": "missing" + }, + { + "workplan": "RPF-WP-0003", + "from": "RPF-WP-0003", + "target": "RAILIANCE-WP-0007", + "reason": "missing" + }, + { + "workplan": "RPF-WP-0003", + "from": "RPF-WP-0003", + "target": "RAILIANCE-WP-0008", + "reason": "missing" + }, + { + "workplan": "RPF-WP-0004", + "from": "RPF-WP-0004", + "target": "RAILIANCE-WP-0007", + "reason": "missing" + }, + { + "workplan": "RPF-WP-0004", + "from": "RPF-WP-0004", + "target": "RAILIANCE-WP-0008", + "reason": "missing" + }, + { + "workplan": "RPF-WP-0005", + "from": "RPF-WP-0005", + "target": "RAILIANCE-WP-0007", + "reason": "missing" + }, + { + "workplan": "RPF-WP-0005", + "from": "RPF-WP-0005", + "target": "RAILIANCE-WP-0008", + "reason": "missing" + }, + { + "workplan": "RPF-WP-0024", + "from": "RPF-WP-0024", + "target": "RAILIANCE-WP-0005", + "reason": "missing" + }, + { + "workplan": "RPF-WP-0024", + "from": "RPF-WP-0024", + "target": "RAILIANCE-WP-0006", + "reason": "missing" + } + ], + "external_edges": [ + { + "workplan": "RPF-WP-0057", + "from": "RPF-WP-0057-T04", + "target": "RPF-WP-0057-T03", + "relationship_type": "starts_after", + "from_workplan_id": "04f7c4aa-0a24-5d61-994d-2487ca6bfb91", + "from_task_id": "94cdfd1c-c639-5c5e-9bd6-7db823ab0265", + "to_workplan_id": null, + "to_task_id": "afa14a91-1ebf-5a45-a28b-380f9a933336" + }, + { + "workplan": "RPF-WP-0003", + "from": "RPF-WP-0003", + "target": "RAIL-PL-WP-0002", + "relationship_type": "blocks", + "from_workplan_id": "5fb6b6a7-3a40-5658-9804-21c0928c6dd9", + "from_task_id": null, + "to_workplan_id": "fd1c045a-01d4-43be-980f-acbda6c64e6c", + "to_task_id": null + }, + { + "workplan": "RPF-WP-0024", + "from": "RPF-WP-0024", + "target": "RAIL-PL-WP-0002", + "relationship_type": "blocks", + "from_workplan_id": "6c2e61cf-7aa9-58ee-bb22-3514b9692eb5", + "from_task_id": null, + "to_workplan_id": "fd1c045a-01d4-43be-980f-acbda6c64e6c", + "to_task_id": null + }, + { + "workplan": "RPF-WP-0035", + "from": "RPF-WP-0035-T07", + "target": "SECRETS-WP-0006-T06", + "relationship_type": "starts_after", + "from_workplan_id": "975db491-5412-5e27-8e34-14a2417bb039", + "from_task_id": "0dd7c9a5-65a0-53fc-b6c3-5ad96ee56f7b", + "to_workplan_id": null, + "to_task_id": "1431edae-5791-5892-8c4b-829419b537d2" + }, + { + "workplan": "RPF-WP-0035", + "from": "RPF-WP-0035-T12", + "target": "RPF-WP-0035-T11", + "relationship_type": "starts_after", + "from_workplan_id": "975db491-5412-5e27-8e34-14a2417bb039", + "from_task_id": "914bf383-c13f-5247-9af0-91d7e3a4784a", + "to_workplan_id": null, + "to_task_id": "7952c8cd-c066-5b72-a025-78d24341c1ab" + }, + { + "workplan": "RPF-WP-0035", + "from": "RPF-WP-0035-T02", + "target": "KEY-WP-0039-T02", + "relationship_type": "starts_after", + "from_workplan_id": "975db491-5412-5e27-8e34-14a2417bb039", + "from_task_id": "e0c82ea9-05a5-537e-8fbf-67534263572e", + "to_workplan_id": null, + "to_task_id": "03da4c38-1dac-53b1-8b0b-e697df48e9d3" + }, + { + "workplan": "RPF-WP-0035", + "from": "RPF-WP-0035-T02", + "target": "MASON-WP-0009-T05", + "relationship_type": "starts_after", + "from_workplan_id": "975db491-5412-5e27-8e34-14a2417bb039", + "from_task_id": "e0c82ea9-05a5-537e-8fbf-67534263572e", + "to_workplan_id": null, + "to_task_id": "1b1a0566-add7-59c1-94b0-6bb81d2d1537" + }, + { + "workplan": "RPF-WP-0035", + "from": "RPF-WP-0035-T03", + "target": "MASON-WP-0005-T05", + "relationship_type": "starts_after", + "from_workplan_id": "975db491-5412-5e27-8e34-14a2417bb039", + "from_task_id": "f47e5bc5-6f3c-54bb-aa35-260ad03892ba", + "to_workplan_id": null, + "to_task_id": "4c205be7-4f1f-5dd1-aafa-fc112fdd640e" + }, + { + "workplan": "RPF-WP-0035", + "from": "RPF-WP-0035-T03", + "target": "FT-WP-0002", + "relationship_type": "blocks", + "from_workplan_id": "975db491-5412-5e27-8e34-14a2417bb039", + "from_task_id": "f47e5bc5-6f3c-54bb-aa35-260ad03892ba", + "to_workplan_id": "f2373858-c932-5a4d-81b6-db9a3595135a", + "to_task_id": null + }, + { + "workplan": "RPF-WP-0044", + "from": "RPF-WP-0044-T06", + "target": "RPF-WP-0044-T04", + "relationship_type": "starts_after", + "from_workplan_id": "98140775-3b9a-5cf9-9af6-722502d487dc", + "from_task_id": "4ca54868-c31f-5e3d-b7d3-a15b84c15f80", + "to_workplan_id": null, + "to_task_id": "0e619658-0453-51ed-b9a0-bed28b5544fd" + }, + { + "workplan": "RPF-WP-0044", + "from": "RPF-WP-0044-T06", + "target": "RPF-WP-0044-T05", + "relationship_type": "starts_after", + "from_workplan_id": "98140775-3b9a-5cf9-9af6-722502d487dc", + "from_task_id": "4ca54868-c31f-5e3d-b7d3-a15b84c15f80", + "to_workplan_id": null, + "to_task_id": "d418068a-6fb0-5416-aac5-d23c93924d9c" + }, + { + "workplan": "RPF-WP-0044", + "from": "RPF-WP-0044-T07", + "target": "RPF-WP-0044-T04", + "relationship_type": "starts_after", + "from_workplan_id": "98140775-3b9a-5cf9-9af6-722502d487dc", + "from_task_id": "844ff115-def2-55ca-ab01-413ba7e6857d", + "to_workplan_id": null, + "to_task_id": "0e619658-0453-51ed-b9a0-bed28b5544fd" + }, + { + "workplan": "RPF-WP-0044", + "from": "RPF-WP-0044-T07", + "target": "RPF-WP-0044-T06", + "relationship_type": "starts_after", + "from_workplan_id": "98140775-3b9a-5cf9-9af6-722502d487dc", + "from_task_id": "844ff115-def2-55ca-ab01-413ba7e6857d", + "to_workplan_id": null, + "to_task_id": "4ca54868-c31f-5e3d-b7d3-a15b84c15f80" + }, + { + "workplan": "RPF-WP-0044", + "from": "RPF-WP-0044-T07", + "target": "RPF-WP-0044-T03", + "relationship_type": "starts_after", + "from_workplan_id": "98140775-3b9a-5cf9-9af6-722502d487dc", + "from_task_id": "844ff115-def2-55ca-ab01-413ba7e6857d", + "to_workplan_id": null, + "to_task_id": "6d5fc801-e361-579b-bcd8-6fe719a82e94" + }, + { + "workplan": "RPF-WP-0044", + "from": "RPF-WP-0044-T07", + "target": "RPF-WP-0044-T05", + "relationship_type": "starts_after", + "from_workplan_id": "98140775-3b9a-5cf9-9af6-722502d487dc", + "from_task_id": "844ff115-def2-55ca-ab01-413ba7e6857d", + "to_workplan_id": null, + "to_task_id": "d418068a-6fb0-5416-aac5-d23c93924d9c" + }, + { + "workplan": "RPF-WP-0004", + "from": "RPF-WP-0004", + "target": "RAIL-PL-WP-0002", + "relationship_type": "blocks", + "from_workplan_id": "afc66064-7ba4-5f11-b61f-d76890244b7e", + "from_task_id": null, + "to_workplan_id": "fd1c045a-01d4-43be-980f-acbda6c64e6c", + "to_task_id": null + }, + { + "workplan": "RPF-WP-0023", + "from": "RPF-WP-0023", + "target": "RAIL-PL-WP-0002", + "relationship_type": "blocks", + "from_workplan_id": "b07930f7-ff0e-5fda-b7b9-3b56467173b1", + "from_task_id": null, + "to_workplan_id": "fd1c045a-01d4-43be-980f-acbda6c64e6c", + "to_task_id": null + }, + { + "workplan": "RPF-WP-0001", + "from": "RPF-WP-0001", + "target": "RAIL-PL-WP-0002", + "relationship_type": "blocks", + "from_workplan_id": "bcc11cf9-d008-5e42-8fff-e2b33a1d0ff2", + "from_task_id": null, + "to_workplan_id": "fd1c045a-01d4-43be-980f-acbda6c64e6c", + "to_task_id": null + }, + { + "workplan": "RPF-WP-0002", + "from": "RPF-WP-0002", + "target": "RAIL-PL-WP-0002", + "relationship_type": "blocks", + "from_workplan_id": "c69cb4e7-15c8-5858-aebd-73a6e46f0f21", + "from_task_id": null, + "to_workplan_id": "fd1c045a-01d4-43be-980f-acbda6c64e6c", + "to_task_id": null + }, + { + "workplan": "RPF-WP-0036", + "from": "RPF-WP-0036-T04", + "target": "RTEL-WP-0002", + "relationship_type": "blocks", + "from_workplan_id": "ca639c3d-3a87-5fa4-ad13-6f2e014b0c84", + "from_task_id": "5351e0e4-6263-58f0-afb2-7c78c4cd6f68", + "to_workplan_id": "08a5db92-7293-50d3-b589-55287b9850b3", + "to_task_id": null + }, + { + "workplan": "RPF-WP-0036", + "from": "RPF-WP-0036-T03", + "target": "ACTIVITY-WP-0042", + "relationship_type": "blocks", + "from_workplan_id": "ca639c3d-3a87-5fa4-ad13-6f2e014b0c84", + "from_task_id": "d64446fb-870b-5ae9-9628-f1fb9d06c5a4", + "to_workplan_id": "e1bfb5a4-e647-592d-883f-fe8728ad2f5f", + "to_task_id": null + }, + { + "workplan": "RPF-WP-0043", + "from": "RPF-WP-0043-T03", + "target": "RAPP-POLICY-NEXUS-WP-0002-T02", + "relationship_type": "starts_after", + "from_workplan_id": "ec41a4bd-df18-5b07-9b63-ccb80d9f001c", + "from_task_id": "2afb1aac-66dd-5e1e-92e1-fc020a59a927", + "to_workplan_id": null, + "to_task_id": "18b26ecc-4fb8-516b-8d97-62ff39a85f24" + }, + { + "workplan": "RPF-WP-0043", + "from": "RPF-WP-0043-T04", + "target": "RPF-WP-0043-T03", + "relationship_type": "starts_after", + "from_workplan_id": "ec41a4bd-df18-5b07-9b63-ccb80d9f001c", + "from_task_id": "8638fa73-439d-5dcd-85c7-81aa125170e1", + "to_workplan_id": null, + "to_task_id": "2afb1aac-66dd-5e1e-92e1-fc020a59a927" + }, + { + "workplan": "RPF-WP-0043", + "from": "RPF-WP-0043-T04", + "target": "RPF-WP-0044-T07", + "relationship_type": "starts_after", + "from_workplan_id": "ec41a4bd-df18-5b07-9b63-ccb80d9f001c", + "from_task_id": "8638fa73-439d-5dcd-85c7-81aa125170e1", + "to_workplan_id": null, + "to_task_id": "844ff115-def2-55ca-ab01-413ba7e6857d" + } + ], + "refused": [], + "notes": [] + } + } +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/projection.stderr.txt b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/projection.stderr.txt new file mode 100644 index 0000000..e69de29 diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/publication-commit.json b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/publication-commit.json new file mode 100644 index 0000000..50060d0 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/publication-commit.json @@ -0,0 +1,25 @@ +{ + "exit_code": 0, + "commit": "104ed90f42a9a86bf9fda343b49b65a85c9ee4b9", + "base": "0a9617a62d4696a8070798923b459e8405c3dbf2", + "files": [ + { + "path": "docs/keycape-issuer-eso-contract.md", + "sha256": "7f59e59b17c58252982931ac61fe50dd7a07340add6c437fe48af36971eafe68" + }, + { + "path": "manifests/templates/keycape-issuer-eso.yaml", + "sha256": "e17498bd8a4c820041a4105e4efe9c60c6a50953db8f621ff54887080065c743" + }, + { + "path": "scripts/keycape_issuer_eso.py", + "sha256": "21c5797280c2a7825dbda8ac970d34a0cdef4c6458f68bce0b1c12407f122a87" + }, + { + "path": "tests/test_keycape_issuer_eso.py", + "sha256": "223d4c5b349062b9f93e6af79e8d5feda44377ba41a11726f6f2b762691e8308" + } + ], + "stdout": "[main 104ed90] feat: declare proposed fixed KeyCape issuer ESO source contract\n 4 files changed, 313 insertions(+)\n create mode 100644 docs/keycape-issuer-eso-contract.md\n create mode 100644 manifests/templates/keycape-issuer-eso.yaml\n create mode 100644 scripts/keycape_issuer_eso.py\n create mode 100644 tests/test_keycape_issuer_eso.py\n", + "stderr": "{\"blocked_blob_ids\": [], \"scanned_blobs\": 4, \"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/nk48-r5-f2-publication-r1/publication-inputs.json b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/publication-inputs.json new file mode 100644 index 0000000..067d121 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/publication-inputs.json @@ -0,0 +1,23 @@ +{ + "owner": "railiance-platform", + "base_revision": "0a9617a62d4696a8070798923b459e8405c3dbf2", + "files": [ + { + "path": "docs/keycape-issuer-eso-contract.md", + "sha256": "7f59e59b17c58252982931ac61fe50dd7a07340add6c437fe48af36971eafe68" + }, + { + "path": "manifests/templates/keycape-issuer-eso.yaml", + "sha256": "e17498bd8a4c820041a4105e4efe9c60c6a50953db8f621ff54887080065c743" + }, + { + "path": "scripts/keycape_issuer_eso.py", + "sha256": "21c5797280c2a7825dbda8ac970d34a0cdef4c6458f68bce0b1c12407f122a87" + }, + { + "path": "tests/test_keycape_issuer_eso.py", + "sha256": "223d4c5b349062b9f93e6af79e8d5feda44377ba41a11726f6f2b762691e8308" + } + ], + "message": "feat: declare proposed fixed KeyCape issuer ESO source contract" +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-review-r1/REVIEW.md b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-review-r1/REVIEW.md new file mode 100644 index 0000000..86517ba --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-review-r1/REVIEW.md @@ -0,0 +1,70 @@ +# Independent Platform-scoped source review: NK48-R5-F2 + +Decision: **accept the exact Platform candidate as reviewable source**. No blocking +source-fit defect was reproduced. This is an independent reviewer decision, not +Platform owner assent, source adoption, descriptor acceptance or execution authority. +The conditional NetKingdom patch may be incorporated into the coordinator's +combined candidate; report F2's exact-store-reference requirement closed only +after the Platform source is actually adopted. No canonical files were edited. + +The proposed `ClusterSecretStore openbao-keycape-{client_id}-issuer` is a new, +explicit owner-controlled declaration. It is not a store name granted by +CCR-2026-0041. For Forgejo, its role, Kubernetes auth mount, service account, +audience and exact KV path agree with that CCR. The `platform` provider mount +and `workloads/keycape/forgejo` remoteRef correctly form the custody path without +double-prefixing it. The `sso` condition and cluster scope agree with Platform's +historical issuer draft. The consumer namespace SecretStore is never substituted. +No role, policy, grant, catalog or caller-selectable store/path is added. + +The pure helper is appropriate preparation under existing RPF-1.3-T4. It checks +fixed identity, complete spec equality and Ready status; it exposes only the +ExternalSecret through its delivery-facing function. It neither provisions the +store nor issues receipts. Admitted IDs ending in `-` remain representable because +the fixed suffix completes the resource name. The adapter's effective bound is +client ID length 48: `keycape-` + ID + `-client` is at most 63 characters. The +store reaches 71 characters and is a DNS subdomain resource name, so a separate +63-character store label bound would incorrectly narrow the type. + +Both patches applied to clean archives of the exact specified commits, all nine +source-pins matched, the eight supplied tests passed, and independent boundary +checks accepted IDs of length 2, 47 and 48 while rejecting 1, 49 and 63. Parsing +the patched type proved that only +`spec.follow_through[id=deliver-secret].entry_point` changed. The pinned template +hash matches its exact content. See `evidence.json`, `focused-tests.txt` and the +replayable `reproduce.py`. Archive subprocesses and tests received a minimal +environment with no ambient credentials; no native infrastructure calls occurred. + +These source checks establish neither the installed ESO CRD shape/defaulting nor +trusted live resource observations. Strict spec equality may reject harmless API +defaults; the actual owner implementation must handle them deliberately without +allowing semantic drift. The audience placement follows the historical issuer +draft, not the older scaffold's differing placement. No installed schema proof is +claimed. The `sso` condition limits store users by namespace; the shared ESO +controller and writers in that namespace remain within the documented trust +boundary. + +The concrete next implementation is the existing RPF-1.3-T4 binding: authenticate +the admitted case/client and attempt session; retrieve and validate this exact +preexisting store through a contained owner path; create the exact ExternalSecret +or refuse a differing one; issue NO_CHANGE for an exact preexisting match only +after Ready readback; and publish/authenticate the closed metadata receipt. Its +before/after UID and resourceVersion must come from that trusted operation, not +from caller-supplied fixture dictionaries. Preserve the preexisting/adoption guard: +these helpers confer no ownership on a preexisting ExternalSecret or target Secret, +and no created receipt may be fabricated from matching spec or Ready alone. The +real binding must handle preexisting target collisions through the owner's +containment/adoption policy, without broad Secret reads or inferred adoption. + +Implement remove using authenticated case-created provenance plus exact UID and +resourceVersion delete preconditions, allow owned target garbage collection, +revoke the parent session per attempt, and route drift or uncertain ownership to +owner reconciliation. Catalog routing remains the existing Warden owner work. +These are carried implementation obligations, not new tasks or permission gates. + +Inputs: Platform `0a9617a62d4696a8070798923b459e8405c3dbf2`, NetKingdom +`3236d5000e52e0643f33906a26a7825aa0b67465`, and the adjacent +`nk48-r5-f2-implementation-r1` packet. Exact packet file hashes and archive hashes +are in `evidence.json`. Review also read Platform AGENTS/INTENT, the original +Platform owner contract and CCR0041, issuer ESO draft, lane design and scaffold, +plus r5's F2 finding and the NetKingdom security companion. No separate fix was +needed and the author's record was not rewritten. diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-review-r1/evidence.json b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-review-r1/evidence.json new file mode 100644 index 0000000..9ac2fad --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-review-r1/evidence.json @@ -0,0 +1,39 @@ +{ + "source_only": true, + "ambient_credentials_passed": false, + "native_calls": false, + "bases": { + "railiance-platform": "0a9617a62d4696a8070798923b459e8405c3dbf2", + "net-kingdom": "3236d5000e52e0643f33906a26a7825aa0b67465" + }, + "input_hashes": { + "OWNER-REVIEW.md": "sha256:7983fd841f09bc7584047426912c33d568c97b4c1126776f99aac42b34d2b742", + "canonical-source-readonly-state.json": "sha256:129d6926bb996e510ab4b41d30e738096b53b71de1fb2dd470fe0ac94848cbec", + "focused-tests.json": "sha256:8de5862a8d752dce0b3b96a9275e42952a99415a18d3f67c06bf9f5b6008d0e3", + "focused-tests.txt": "sha256:aab46c51d87198ad4be6dd3078f61286e2faf1ef4d76fa14298698388f78ab24", + "manifest.json": "sha256:754531fbef468bc0e432bd26d4dbed992294c34d57e23fe7c3b7f829f7a24ba4", + "netkingdom-entry-point.conditional.patch": "sha256:6784a21fbd10bd62712be2e0f4929034601c94629aa74ccaa2e93fc815eecd61", + "platform-candidate/docs/keycape-issuer-eso-contract.md": "sha256:7f59e59b17c58252982931ac61fe50dd7a07340add6c437fe48af36971eafe68", + "platform-candidate/manifests/templates/keycape-issuer-eso.yaml": "sha256:e17498bd8a4c820041a4105e4efe9c60c6a50953db8f621ff54887080065c743", + "platform-candidate/scripts/keycape_issuer_eso.py": "sha256:21c5797280c2a7825dbda8ac970d34a0cdef4c6458f68bce0b1c12407f122a87", + "platform-candidate/tests/test_keycape_issuer_eso.py": "sha256:223d4c5b349062b9f93e6af79e8d5feda44377ba41a11726f6f2b762691e8308", + "platform.candidate.patch": "sha256:a490df7dee41e15351da8d2906e1342edebb533984befe3d9ae74d952dbb091b", + "review.json": "sha256:d99db9ab4f326308cde464129bbbb7573434e500fd03c130a37a590bac3643a4", + "source-pins.json": "sha256:6e896b6196a0aca01ddaf433b4d14cc041fc0296c5dd6404c3a698b941b61bd8", + "structural-and-patch-checks.json": "sha256:a3dc4cd405c412d4369986cc6736cd2d9eed02d4b86d95d62d5a9914106fdb14", + "type.before.yaml": "sha256:e75fe94110ef9fb7fb18072fe2856941b33507c99cb406d36576046481485d0a", + "type.candidate.yaml": "sha256:ef02ddae3167bdd1558193a5f6db0379fb8cc84207531706d5ef35caabc3684f" + }, + "checks": { + "all_source_pins_verified": true, + "both_patches_apply_to_exact_archives": true, + "focused_tests": "8 tests passed", + "only_type_delta": "spec.follow_through[id=deliver-secret].entry_point", + "independent_length_boundaries": "2,47,48 accepted; 1,49,63 refused", + "template_hash_matches_type": true + }, + "archive_sha256": { + "railiance-platform": "sha256:0798af8b0171a5925a10b520269c45b83b03eaa97650831e17c2aaaeb696bbd9", + "net-kingdom": "sha256:e2852f444ed3c1501107b3cc1aef088e7d30f953284ca310149cc46424da12b3" + } +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-review-r1/focused-tests.txt b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-review-r1/focused-tests.txt new file mode 100644 index 0000000..9706316 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-review-r1/focused-tests.txt @@ -0,0 +1,13 @@ +test_delivery_document_is_only_external_and_detached (test_keycape_issuer_eso.FixedIssuerContract.test_delivery_document_is_only_external_and_detached) ... ok +test_external_drift_refuses (test_keycape_issuer_eso.FixedIssuerContract.test_external_drift_refuses) ... ok +test_independent_forgejo_binding (test_keycape_issuer_eso.FixedIssuerContract.test_independent_forgejo_binding) ... ok +test_missing_or_unready_refuses (test_keycape_issuer_eso.FixedIssuerContract.test_missing_or_unready_refuses) ... ok +test_no_caller_store_or_path_override (test_keycape_issuer_eso.FixedIssuerContract.test_no_caller_store_or_path_override) ... ok +test_store_drift_refuses (test_keycape_issuer_eso.FixedIssuerContract.test_store_drift_refuses) ... ok +test_type_admitted_trailing_dash_is_preserved (test_keycape_issuer_eso.FixedIssuerContract.test_type_admitted_trailing_dash_is_preserved) ... ok +test_valid_ready_metadata_allows_extra_controller_metadata (test_keycape_issuer_eso.FixedIssuerContract.test_valid_ready_metadata_allows_extra_controller_metadata) ... ok + +---------------------------------------------------------------------- +Ran 8 tests in 0.294s + +OK diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-review-r1/reproduce.py b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-review-r1/reproduce.py new file mode 100644 index 0000000..90d2a10 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-review-r1/reproduce.py @@ -0,0 +1,87 @@ +"""Independent source-only review; archived trees and public synthetic fixtures.""" +import hashlib +import importlib.util +import io +import json +import os +from pathlib import Path +import subprocess +import tarfile +import tempfile +import yaml + +OUT = Path(__file__).resolve().parent +PACKET = OUT.parent / 'nk48-r5-f2-implementation-r1' +BASES = {'railiance-platform': '0a9617a62d4696a8070798923b459e8405c3dbf2', + 'net-kingdom': '3236d5000e52e0643f33906a26a7825aa0b67465'} +ENV = {'PATH': '/usr/local/bin:/usr/bin:/bin', 'HOME': '/tmp', 'PYTHONDONTWRITEBYTECODE': '1'} +def run(args, cwd): + return subprocess.run(args, cwd=cwd, env=ENV, capture_output=True, text=True, check=True).stdout +def sha(data): + return hashlib.sha256(data).hexdigest() + +result = {'source_only': True, 'ambient_credentials_passed': False, 'native_calls': False, + 'bases': BASES, 'input_hashes': {}, 'checks': {}} +for p in sorted(PACKET.rglob('*')): + if p.is_file(): + result['input_hashes'][str(p.relative_to(PACKET))] = sha(p.read_bytes()) +with tempfile.TemporaryDirectory(prefix='platform-f2-independent-') as scratch: + roots = {} + for repo, commit in BASES.items(): + dest = Path(scratch) / repo + dest.mkdir() + archive = subprocess.run(['git', '-C', '/home/worsch/' + repo, 'archive', commit], + env=ENV, capture_output=True, check=True).stdout + with tarfile.open(fileobj=io.BytesIO(archive)) as tf: + tf.extractall(dest, filter='data') + roots[repo] = dest + result.setdefault('archive_sha256', {})[repo] = sha(archive) + for pin in json.loads((PACKET / 'source-pins.json').read_text()): + repo = pin['repo'] + root = roots.get(repo, Path('/home/worsch/the-custodian')) + actual = sha((root / pin['path']).read_bytes()) + assert actual == pin['sha256'], pin['path'] + result['checks']['all_source_pins_verified'] = True + for repo, patch in [('railiance-platform', 'platform.candidate.patch'), + ('net-kingdom', 'netkingdom-entry-point.conditional.patch')]: + run(['git', 'apply', '--check', str(PACKET / patch)], roots[repo]) + run(['git', 'apply', str(PACKET / patch)], roots[repo]) + result['checks']['both_patches_apply_to_exact_archives'] = True + platform = roots['railiance-platform'] + tests = subprocess.run(['/usr/bin/python3', '-m', 'unittest', 'discover', '-s', 'tests', + '-p', 'test_keycape_issuer_eso.py', '-v'], cwd=platform, + env=ENV, capture_output=True, text=True) + (OUT / 'focused-tests.txt').write_text(tests.stdout + tests.stderr) + assert tests.returncode == 0 + result['checks']['focused_tests'] = '8 tests passed' + type_path = roots['net-kingdom'] / 'security-case-type-revisions/1.3/oidc-browser-relying-party/type.yaml' + before = yaml.safe_load((PACKET / 'type.before.yaml').read_text()) + after = yaml.safe_load(type_path.read_text()) + before_step = next(s for s in before['spec']['follow_through'] if s['id'] == 'deliver-secret') + after_step = next(s for s in after['spec']['follow_through'] if s['id'] == 'deliver-secret') + assert before_step['entry_point'] != after_step['entry_point'] + after_step['entry_point'] = before_step['entry_point'] + assert before == after + result['checks']['only_type_delta'] = 'spec.follow_through[id=deliver-secret].entry_point' + spec = importlib.util.spec_from_file_location('issuer', platform / 'scripts/keycape_issuer_eso.py') + issuer = importlib.util.module_from_spec(spec) + spec.loader.exec_module(issuer) + for n in (2, 47, 48): + store, external = issuer.documents('a' * n) + assert len(external['metadata']['name']) <= 63 + assert len(store['metadata']['name']) <= 253 + for n in (1, 49, 63): + try: + issuer.documents('a' * n) + except issuer.Refused: + pass + else: + raise AssertionError(n) + result['checks']['independent_length_boundaries'] = '2,47,48 accepted; 1,49,63 refused' + result['checks']['template_hash_matches_type'] = ( + sha((platform / 'manifests/templates/keycape-issuer-eso.yaml').read_bytes()) + in yaml.safe_load(type_path.read_text())['spec']['follow_through'][ + next(i for i,s in enumerate(before['spec']['follow_through']) if s['id']=='deliver-secret')]['entry_point']) + assert result['checks']['template_hash_matches_type'] +(OUT / 'evidence.json').write_text(json.dumps(result, indent=2) + '\n') +print(json.dumps(result['checks'], indent=2)) diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-review-r1/review.json b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-review-r1/review.json new file mode 100644 index 0000000..a571cf6 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-review-r1/review.json @@ -0,0 +1,23 @@ +{ + "schema": "custodian.nk48-r5-f2-independent-source-review.v1", + "date": "2026-10-17", + "reviewer": "independent Platform-scoped Codex subagent", + "finding": "NK48-R5-F2", + "decision": "accept-for-reviewable-source", + "blocking_source_findings": [], + "source_owner_assent_received": false, + "source_adoption_performed": false, + "f2_closed": false, + "f2_closure_condition": "Actual Platform adoption of the explicit source declaration followed by integration of its exact conditional entry_point into the successor candidate.", + "platform_base": "0a9617a62d4696a8070798923b459e8405c3dbf2", + "netkingdom_base": "3236d5000e52e0643f33906a26a7825aa0b67465", + "accepted_platform_patch_sha256": "a490df7dee41e15351da8d2906e1342edebb533984befe3d9ae74d952dbb091b", + "conditional_netkingdom_patch_sha256": "6784a21fbd10bd62712be2e0f4929034601c94629aa74ccaa2e93fc815eecd61", + "template_sha256": "e17498bd8a4c820041a4105e4efe9c60c6a50953db8f621ff54887080065c743", + "evidence": ["evidence.json", "focused-tests.txt", "reproduce.py", "REVIEW.md"], + "next_implementation": "Existing RPF-1.3-T4 authenticated deliver/remove, trusted metadata readback and closed receipts, attempt-session revocation, created-only rollback and catalog routing; preserve preexisting/adoption guards and confirm installed ESO schema/defaulting.", + "native_execution_performed": false, + "canonical_edits_or_commits": false, + "new_grants_or_workplans": false, + "candidate_fix_needed": false +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/README.md b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/README.md new file mode 100644 index 0000000..e549052 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/README.md @@ -0,0 +1,15 @@ +# NK48-R5-F3 owner configuration inverse — candidate only + +Prepared for root and independent Apps review; no canonical files changed, no commit/push/sync, no new owner assent or native action. The type remains proposed. R5 and all prior receipts are untouched. + +The source gap is concrete: public clients skip delivery; confidential preexisting-matched delivery returns NO_CHANGE and cannot call owner_remove. The unchanged hand-over Adapter rolls back only its receipt. A reproduced public flow therefore leaves the Helm configuration after receipt rollback. T02 already names the owner Helm rollback mechanism, but T05 has not implemented its real capabilities or an authenticated configuration transaction. + +`type-owner-configuration-inverse.candidate.patch` changes only the hand-over entry text and hand-over/delivery rollback text. It proposes an owner-run transaction around the **already external** deployment plus existing hand-over, with a separately reachable owner inverse. Seven kernel descriptors, their bytes/maps, parameters, earlier versions, type status and all adapter guards stay unchanged. No eighth kernel step or new service is proposed. + +`owner_configuration_contract.py` is the unadopted executable control-flow candidate, not an installed Helm backend. `owner-contract-candidate.md` specifies exact authority, durability, scope and required owner decisions. The owner entry invokes the real unchanged hand-over seam in fixtures; the inverse is reached on public and NO_CHANGE-delivery case unwind and after failures before receipt creation. It does not borrow receipt or secret-object creation authority. All real deployment/journal/authentication/exclusion capabilities remain Apps T05 work. + +Focused synthetic evidence: **13 tests and 18 subtests pass** against the real frozen hand-over/delivery seams and Warden117fec9 imports. Covers the original counterexample, public/client delivery-skipped path, confidential delivery NO_CHANGE, preexisting configuration and receipt, exact admission/owner/source/release binding, changed post revision, missing release-writer exclusion, verification/recording failure before receipt, publication ambiguity after receipt, lost configuration confirmation, conflicting configuration, and inverse failure with original error context retained. Two separate scratch negative controls detect removing the post-revision guard and omitting the wrapper's failure inverse. Existing owner type validator accepts the candidate shape and both examples under explicit allow-unimplemented preparation; this is not admission. + +**Unresolved owner choice:** Apps must adopt and publish the external wrapper entry/inverse, the durable authenticated configuration journal and accepted source pins, and a real boundary excluding all release writers across revision check plus Helm rollback. The current Make target supplies none of these provenance/serialization guarantees. Helm revision equality checked before an unguarded command is not an atomic guard. The synthetic backend does not establish any real guarantee. If Apps declines that owner path, the current seven-step execution contract cannot express the missing inverse; owners must choose a structural execution/receipt change instead. Do not mark F3 or T05 accepted on this packet alone. + +Receipt rollback remains receipt-only. Delivery removal remains created-only and requires independently proven reference deactivation; preexisting configuration NO_CHANGE alone never authorizes deleting referenced objects. A configuration inverse may succeed while a hand-over receipt remains uncertain after publication failure: retain that uncertainty rather than report whole-case rollback success. No credentials, Secret values, release manifests or native resources were read or changed. diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/candidate-validation.json b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/candidate-validation.json new file mode 100644 index 0000000..c81ce1d --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/candidate-validation.json @@ -0,0 +1,29 @@ +{ + "status": "passed", + "checks": [ + { + "check": "existing_owner_validate_type", + "errors": [] + }, + { + "check": "existing_owner_validate_case/forgejo.yaml", + "errors": [], + "allow_unimplemented": true + }, + { + "check": "existing_owner_validate_case/example-public-portal.yaml", + "errors": [], + "allow_unimplemented": true + }, + { + "check": "bounded_structural_diff", + "changed": [ + "follow_through[hand-over].entry_point", + "rollback[hand-over].action", + "rollback[deliver-consumer-secret].action" + ], + "seven_descriptors_parameters_and_proposed_status_preserved": true + } + ], + "native_or_owner_acceptance": false +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/failure_inverse_omitted.txt b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/failure_inverse_omitted.txt new file mode 100644 index 0000000..aa24204 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/failure_inverse_omitted.txt @@ -0,0 +1,34 @@ +. [100%] +=================================== FAILURES =================================== +_ OwnerFlowTests.test_failure_before_receipt_still_reaches_inverse (failure='fail_verify') _ + +self = + + def test_failure_before_receipt_still_reaches_inverse(self): + for failure in ('fail_verify', 'fail_record'): + with self.subTest(failure=failure): + self.setup_flow(); setattr(self, failure, True) + with self.assertRaises(Refused): self.flow() + self.assertEqual(self.receipts, set()) +> self.assertIsNone(self.owner.current['configuration']['oidc']) +E AssertionError: {'issuer': 'https://fixture-issuer.invalid', 'client': 'example-portal', 'groups': 'example-portal-users'} is not None + +test_owner_configuration_contract.py:251: AssertionError +_ OwnerFlowTests.test_failure_before_receipt_still_reaches_inverse (failure='fail_record') _ + +self = + + def test_failure_before_receipt_still_reaches_inverse(self): + for failure in ('fail_verify', 'fail_record'): + with self.subTest(failure=failure): + self.setup_flow(); setattr(self, failure, True) + with self.assertRaises(Refused): self.flow() + self.assertEqual(self.receipts, set()) +> self.assertIsNone(self.owner.current['configuration']['oidc']) +E AssertionError: {'issuer': 'https://fixture-issuer.invalid', 'client': 'example-portal', 'groups': 'example-portal-users'} is not None + +test_owner_configuration_contract.py:251: AssertionError +=========================== short test summary info ============================ +SUBFAILED(failure='fail_verify') test_owner_configuration_contract.py::OwnerFlowTests::test_failure_before_receipt_still_reaches_inverse +SUBFAILED(failure='fail_record') test_owner_configuration_contract.py::OwnerFlowTests::test_failure_before_receipt_still_reaches_inverse +2 failed, 1 passed, 12 deselected in 0.11s diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/focused-tests-before-admission-binding.json b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/focused-tests-before-admission-binding.json new file mode 100644 index 0000000..c222f45 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/focused-tests-before-admission-binding.json @@ -0,0 +1,19 @@ +{ + "command": [ + "/home/worsch/ops-warden/.venv/bin/python", + "-m", + "pytest", + "-q", + "-p", + "no:cacheprovider", + "/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/test_owner_configuration_contract.py" + ], + "exit_code": 0, + "environment_overrides": { + "PYTHONDONTWRITEBYTECODE": "1", + "PYTHONPATH": "/tmp/nk48-r5-f1-ahudwokt/warden-frozen/src", + "NK_FROZEN_ROOT": "/tmp/nk48-r5-f1-ahudwokt/net-kingdom" + }, + "scope": "synthetic candidate owner wrapper through unchanged real adapter seams, not kernel admission or actual owner authority", + "initial_fixture_correction": "Initial 1 failed/11 passed/13 subtests: delivery contained_output sanitizes refusal to owner callback failed; changed only over-specific message assertion to Refused while keeping zero owner_remove calls asserted. No adapter change." +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/focused-tests-before-admission-binding.txt b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/focused-tests-before-admission-binding.txt new file mode 100644 index 0000000..01b0cae --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/focused-tests-before-admission-binding.txt @@ -0,0 +1,2 @@ +............ [100%] +12 passed, 13 subtests passed in 0.27s diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/focused-tests.json b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/focused-tests.json new file mode 100644 index 0000000..5f8d55a --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/focused-tests.json @@ -0,0 +1,18 @@ +{ + "command": [ + "/home/worsch/ops-warden/.venv/bin/python", + "-m", + "pytest", + "-q", + "-p", + "no:cacheprovider", + "/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/test_owner_configuration_contract.py" + ], + "exit_code": 0, + "environment_overrides": { + "PYTHONDONTWRITEBYTECODE": "1", + "PYTHONPATH": "/tmp/nk48-r5-f1-ahudwokt/warden-frozen/src", + "NK_FROZEN_ROOT": "/tmp/nk48-r5-f1-ahudwokt/net-kingdom" + }, + "scope": "synthetic candidate owner wrapper through unchanged real adapter seams; explicit admission binding added before owner mutation, no kernel admission or actual owner authority" +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/focused-tests.txt b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/focused-tests.txt new file mode 100644 index 0000000..ec94c0a --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/focused-tests.txt @@ -0,0 +1,2 @@ +............. [100%] +13 passed, 18 subtests passed in 0.18s diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/implementation.json b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/implementation.json new file mode 100644 index 0000000..4f0f082 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/implementation.json @@ -0,0 +1,143 @@ +{ + "schema": "custodian.nk48-r5-f3-candidate.v1", + "status": "prepared-synthetic-tested-owner-review-required", + "finding": "NK48-R5-F3", + "canonical_edits": false, + "native_actions": false, + "owner_assent": false, + "source_pins": [ + { + "repo": "net-kingdom", + "head": "3236d5000e52e0643f33906a26a7825aa0b67465", + "path": "AGENTS.md", + "sha256": "95158b1d5c15e6cf8be980f5c0e3b48aeef252654846273887f40516b3fa6a78", + "working_bytes_equal_commit": true + }, + { + "repo": "net-kingdom", + "head": "3236d5000e52e0643f33906a26a7825aa0b67465", + "path": "INTENT.md", + "sha256": "fd2ba8dff9dfd5583ec6d8b0348f273bfe00c59923b5d5d47cd22b1fb4658c54", + "working_bytes_equal_commit": true + }, + { + "repo": "net-kingdom", + "head": "3236d5000e52e0643f33906a26a7825aa0b67465", + "path": "SECURITY-COMPANION.md", + "sha256": "06374445ca888549f1bf79527d1345d94918d7a7ec363160d8427d0e7a31d643", + "working_bytes_equal_commit": true + }, + { + "repo": "net-kingdom", + "head": "3236d5000e52e0643f33906a26a7825aa0b67465", + "path": "sso-mfa/k8s/keycape/handover-adapter.py", + "sha256": "bad7c1132258f2140b0d83548031ac86eecea4f4019f34adedc1e9e3774a3832", + "working_bytes_equal_commit": true + }, + { + "repo": "net-kingdom", + "head": "3236d5000e52e0643f33906a26a7825aa0b67465", + "path": "sso-mfa/k8s/keycape/deliver-consumer-secret-adapter.py", + "sha256": "65db5386cdf08a1aa932886c046abed20330a36c96e63d4267adb8341b23da42", + "working_bytes_equal_commit": true + }, + { + "repo": "net-kingdom", + "head": "3236d5000e52e0643f33906a26a7825aa0b67465", + "path": "security-case-type-revisions/1.3/oidc-browser-relying-party/type.yaml", + "sha256": "e75fe94110ef9fb7fb18072fe2856941b33507c99cb406d36576046481485d0a", + "working_bytes_equal_commit": true + }, + { + "repo": "net-kingdom", + "head": "3236d5000e52e0643f33906a26a7825aa0b67465", + "path": "security-case-type-revisions/1.3/oidc-browser-relying-party/examples/example-public-portal.yaml", + "sha256": "2b30b9403ba124514e3ab8777b187a225099e2f7683fdd41d0ad48ef3d9649a7", + "working_bytes_equal_commit": true + }, + { + "repo": "net-kingdom", + "head": "3236d5000e52e0643f33906a26a7825aa0b67465", + "path": "security-case-type-revisions/1.3/oidc-browser-relying-party/examples/forgejo.yaml", + "sha256": "546aad5cf26842c484fc6ba99940d56697b85531afea11872556761008923c94", + "working_bytes_equal_commit": true + }, + { + "repo": "railiance-apps", + "head": "944b30ad63ab054cc24617e374e177d34cd572da", + "path": "AGENTS.md", + "sha256": "2a366a5fbb69ba235df97ee117153cbfa1af11ab579372751b4fb5dbb30c31a7", + "working_bytes_equal_commit": true + }, + { + "repo": "railiance-apps", + "head": "944b30ad63ab054cc24617e374e177d34cd572da", + "path": "INTENT.md", + "sha256": "31060dc1738aa4540f78735019359a276ece39dad8afbf1be740713bebbe65e5", + "working_bytes_equal_commit": true + }, + { + "repo": "railiance-apps", + "head": "944b30ad63ab054cc24617e374e177d34cd572da", + "path": "Makefile", + "sha256": "a319e264e6f04378f6b3d2c7781860fc4bac1abdb248cffa8c26065e08f2f290", + "working_bytes_equal_commit": true + }, + { + "repo": "railiance-apps", + "head": "944b30ad63ab054cc24617e374e177d34cd572da", + "path": "workplans/RAPPS-WP-0016-forgejo-netkingdom-login.md", + "sha256": "1077e9de358433bf1316e3db1459dd271f82875b9590bf3d7b5b22c0df42bbda", + "working_bytes_equal_commit": true + }, + { + "repo": "railiance-apps", + "head": "944b30ad63ab054cc24617e374e177d34cd572da", + "path": "docs/evidence/nk48-t16-owner-review/owner-review-railiance-apps.json", + "sha256": "67e9ded8d37462ba04a0a224bac03a840d0c94326235474e1d3371c8ff61865d", + "working_bytes_equal_commit": true + }, + { + "repo": "railiance-apps", + "head": "944b30ad63ab054cc24617e374e177d34cd572da", + "path": "docs/proposals/2026-10-11-forgejo-consumer-eso/owner-design.md", + "sha256": "c523949454e4c6eb58e8caee382380a561f561704136de62bf5783c592f962f2", + "working_bytes_equal_commit": true + }, + { + "repo": "the-custodian", + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-adapters-1.2-review/coordinator-review-r5.json", + "sha256": "e3016352c58178894ccbfc600b960d3f9e2b5d534a05b8df2c5b79737cb0127a", + "historical_receipt_untouched": true + }, + { + "repo": "the-custodian", + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-1.3-owner-review/owner-review-railiance-apps.json", + "sha256": "aedca85f3a742f6510c45aa17ade7375b93c46069cf4e5a7119bec9765efbd7d", + "historical_receipt_untouched": true + } + ], + "kernel": "117fec9241e7b985dfb187b992e173d0a5dc3700", + "candidate_type_before_sha256": "e75fe94110ef9fb7fb18072fe2856941b33507c99cb406d36576046481485d0a", + "candidate_type_after_sha256": "739c927ab494f4e0f6618e6e55394dd8dc4b720db428cd4973f7a81524e1ecd9", + "candidate_wrapper_sha256": "62752f518b78fa7713bdb4d47e68decbf3aa89ecbc7950549ffdc3d08e01c554", + "tests": { + "passed": 13, + "subtests_passed": 18, + "negative_controls_detected": [ + "missing current-post revision guard", + "missing failure-before-receipt inverse" + ] + }, + "blocking_owner_work": "RAPPS-WP-0016-T05 must adopt/publish real entry path, exact authenticated durable configuration journal/source binding and release-writer exclusion; independent Apps review must supersede original C3 placement. Current seven adapters alone cannot do this inverse.", + "proposed_type_structural_scope": [ + "follow_through[hand-over].entry_point", + "rollback[hand-over].action", + "rollback[deliver-consumer-secret].action" + ], + "knowledge_orientation": { + "package": "0.2.6", + "digest": "8d1d6a9d4d07fec7b619618de7dfe2254c1790f85f4198c7fe19db9751c02", + "start_and_identity": "withheld-source-changed; owner AGENTS/INTENT and direct seam/owner sources reviewed" + } +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/manifest.json b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/manifest.json new file mode 100644 index 0000000..1890e91 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/manifest.json @@ -0,0 +1,23 @@ +{ + "status": "prepared-not-applied", + "files": { + "implementation.json": "fce244e7a8bd1f1b9911c632de4831de291f358375183ff7959dc5615560f6d0", + "net-kingdom-unchanged-owner-status.txt": "354b3dd65c711df0855c22ba053a4ca295d5491941c5a64c1157a0b7d43cf4cb", + "railiance-apps-unchanged-owner-status.txt": "251b0eb219198e4ad334a76e0b39f23848242354105e97f3f61701168817cdd3", + "README.md": "bff0eb23ce4920da6058bf71a3dace209baca69a97c94bd9db4f765f71044eb2", + "focused-tests.txt": "99ddd55245e57dd9047793bd206158c66b723a7fe79cc8e7b785f8183f1e4654", + "candidate-validation.json": "17ed30550f7330beb49b5f866c634e78fbcbf98088fcc3e0ce92faa7b9042524", + "focused-tests-before-admission-binding.json": "c11b15e97b3da1c9b4819fec539b17823f98f4e6f39ece9bc5c8422325ddbc8e", + "focused-tests.json": "8b1f7ecf75fedee9c03d1421efc6a6e9dd89eb99da70df5e8e4d47027b5fe331", + "type.before.yaml": "e75fe94110ef9fb7fb18072fe2856941b33507c99cb406d36576046481485d0a", + "test_owner_configuration_contract.py": "51482fc5bad6af5c52d736ded7e7031291b18bc4949b9ff43d7dea78f3a8ad94", + "focused-tests-before-admission-binding.txt": "9fb7e13b0e58942d2dc1921fd303ad372b1385841e3df88f5a23365a4a4b95bf", + "negative-controls.json": "d13a0cf8b22abef1cba502b3fd3fd10c9f40955a3ffa8fee473d6189bfb25783", + "failure_inverse_omitted.txt": "543906e345b1b5daa28f0f3cfc620b3e06253cfb65c3a861586630dc63b57608", + "owner-contract-candidate.md": "dfdc6573df76647790aabb26e4b228b04047dffd032fc33821024f8bbc9ba941", + "type-owner-configuration-inverse.candidate.patch": "6e23967d768759fc10203bcc25d81cb32c3d9222ed6fcb4a510a7cfe90e579bf", + "type.candidate.yaml": "739c927ab494f4e0f6618e6e55394dd8dc4b720db428cd4973f7a81524e1ecd9", + "post_revision_guard_removed.txt": "e9e4a134ece9095af5dbf790858b6e3ed44ae9388dde98cbb9a25b4183a16f50", + "owner_configuration_contract.py": "62752f518b78fa7713bdb4d47e68decbf3aa89ecbc7950549ffdc3d08e01c554" + } +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/negative-controls.json b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/negative-controls.json new file mode 100644 index 0000000..d2552f5 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/negative-controls.json @@ -0,0 +1,42 @@ +{ + "scratch_root": "/tmp/nk48-f3-negative-01b8uuu9", + "results": [ + { + "mutation": "post_revision_guard_removed", + "scratch_only": true, + "cwd": "/tmp/nk48-f3-negative-01b8uuu9/post_revision_guard_removed", + "command": [ + "/home/worsch/ops-warden/.venv/bin/python", + "-m", + "pytest", + "-q", + "-p", + "no:cacheprovider", + "test_owner_configuration_contract.py", + "-k", + "changed_revision" + ], + "exit_code": 1, + "detects_missing_candidate_behavior": true + }, + { + "mutation": "failure_inverse_omitted", + "scratch_only": true, + "cwd": "/tmp/nk48-f3-negative-01b8uuu9/failure_inverse_omitted", + "command": [ + "/home/worsch/ops-warden/.venv/bin/python", + "-m", + "pytest", + "-q", + "-p", + "no:cacheprovider", + "test_owner_configuration_contract.py", + "-k", + "failure_before_receipt" + ], + "exit_code": 1, + "detects_missing_candidate_behavior": true + } + ], + "canonical_or_kernel_mutation": false +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/net-kingdom-unchanged-owner-status.txt b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/net-kingdom-unchanged-owner-status.txt new file mode 100644 index 0000000..8f7c1ab --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/net-kingdom-unchanged-owner-status.txt @@ -0,0 +1,6 @@ + M .custodian-brief.md + M WORK-RECORDS.md +?? docs/evidence/2026-10-15-cadence-adoption/hourly-observation-capture/ +?? docs/evidence/2026-10-15-cadence-adoption/hourly-observation-inventory.jsonl +?? docs/evidence/2026-10-15-cadence-adoption/hourly-observation-monitor.json +?? intakes/NET-IN-0005-single-use-setup-link-mail-handoff.md diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/owner-contract-candidate.md b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/owner-contract-candidate.md new file mode 100644 index 0000000..73c3ef4 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/owner-contract-candidate.md @@ -0,0 +1,29 @@ +# Consumer configuration transaction candidate for Apps T05 + +This is a coordinator-authored proposal for independent Apps review under RAPPS-WP-0016-T05, not Apps assent or an implemented owner capability. T02 already owns the Helm change and rollback to its noted previous revision. Keep that deployment mechanism and the seven kernel descriptors. Do not move Helm side effects into hand-over's verification, receipt callbacks, or Adapter.rollback. Do not add an eighth kernel step, a new service, a standing credential, or a new catalog grant. + +The existing seven kernel adapters alone do not express this inverse: hand-over can authenticate only a newly created receipt, and confidential delivery can authenticate only its case-created objects. Public delivery is skipped; matched delivery returns NO_CHANGE. No such creation can grant authority over a Helm configuration. The reproducible direct-seam counterexample in the fixtures removes the public client's receipt and leaves the consumer configuration present. Merely adding an unused rollback helper would not fix it. + +## Proposed reachable owner entry and inverse + +The existing external owner deployment before hand-over becomes **one reviewed owner-run entry path**: begin configuration transaction, perform/confirm the T02 Helm change, then invoke the admitted hand-over step in the same attended owner session. `owner_configuration_contract.run` is an executable control-flow candidate for that path, exercised by the fixtures through the unchanged real hand-over adapter. It is not installed or callable by production. The owner must actually implement and publish its backend and bind its entry path under T05 before this text can establish readiness. + +Before any mutation, bind an owner-authenticated durable intent to the exact case bytes/digest, case acceptance, owner, human principal, cluster identity, namespace/release, client, configuration source and reviewed wrapper/helper source digests, hand-over descriptor, and distinct owner operation/attempt ID. The wrapper also compares the case/acceptance/principal/owner/client/descriptor against the exact admitted hand-over Invocation. Bind the selected configuration to the case (issuer/client/groups and the confidential reference where applicable), not just to an arbitrary revision. Never store Helm values, rendered Secrets, login cookies or credential material in this record. Full values/chart material may be sensitive: retain only the owner-reviewed non-secret projection/digests and provenance references needed for the inverse. The real owner journal must authenticate the entire record; the fixture's dictionary and literal source pin provide no durable/native proof. + +Under the owner's exclusive release-writer boundary, observe the before revision and its configuration identity, persist intent, perform the source-reviewed deployment, and persist authenticated post revision/configuration identity and a changed/no-change classification **before hand-over verification or receipt publication**. Confirm the operation provenance and that the allowed change was exactly this case's new OIDC configuration; do not infer ownership merely because a revision incremented. A deployment affecting unrelated chart/configuration changes is outside this inverse. Match an identical preexisting configuration as NO_CHANGE and give it no rollback authority. Refuse a conflicting preexisting source rather than overwrite it. Preserve local sign-in, unrelated configuration, data and preexisting resources. + +`owner_configuration_contract.inverse` is an owner-run unwind entry on the same durable transaction, independent of secret-delivery outcomes and hand-over receipt creation. It is reached (a) by the wrapper on verification, receipt-recording or publication failure after confirmed configuration and (b) by the owner case-unwind path after a successful hand-over, before removing any delivery object. It applies to public and confidential cases. A failed hand-over is not an excuse to wait for a successful kernel receipt before invoking the owner inverse. A configuration NO_CHANGE is preserved; no Helm action is authorized. An authenticated, already completed inverse is idempotent evidence, not permission to run Helm again. + +For a changed configuration, authenticate the transaction and exact scope, require the current release still equals the recorded case-created post revision/configuration, and hold the owner's release-writer exclusion continuously across that check and the normal Helm rollback to the noted before revision. Confirm a new post-rollback revision restoring the exact approved before configuration; retain inverse evidence and transaction history. Current revision or configuration drift stops the inverse without touching other work. Helm's rollback command is not a revision CAS: the owner must identify a real exclusion mechanism covering *all* writers to this release, including manual and reconciliation paths, or retain an attended per-release freeze/owner-reconciliation gate. A local mutex that other writers ignore is insufficient. No such real exclusion is supplied by this packet or the existing forgejo-deploy Make target. + +An intent/configuration/post-record persistence gap yields an ambiguous configuration outcome: keep the durable pending intent and stop for owner reconciliation using the exact operation/history/source binding. Do not replay, assert NO_CHANGE, or infer mutation ownership from an untrusted local record. Process loss does not confer an automatic inverse; T05 must supply authenticated durable readback/reconciliation before resuming. The fixture demonstrates this refusal; it does not implement the real journal/recovery backend. Failed inverse remains pending for owner reconciliation and retains the original hand-over failure as context. + +The adapter's rollback remains exactly receipt-only and created-only. The independent configuration inverse must not fabricate or extend its `allowed_created`. If publication failed after receipt creation, configuration can be restored by its separately authenticated transaction while that receipt remains uncertain for Apps reconciliation; never report whole-case rollback complete from the configuration inverse alone. + +For delivery cleanup, `owner_remove` must first verify the authenticated configuration inverse/no remaining consumer reference; it may reuse the completed inverse evidence, not run a second unguarded Helm rollback. It then removes only its authenticated case-created UIDs, in the existing reverse order, and reports `consumer_reference_reverted=true` only when actually proven. Preserve all preexisting-matched objects. If configuration itself was preexisting and still references a delivery object, NO_CHANGE is not proof of deactivation: refuse removing the referenced object until the owner resolves that dependency. Role/store deletion alone remains no token-revocation proof. + +## Exact owner choice still required + +Apps must adopt this external wrapper/transaction entry and inverse in T05, name the real authenticated journal, accepted configuration/source binding, release identity and writer-exclusion mechanism, and re-review the type's revised C3 wording. Its previous C3 expressly placed the Helm inverse inside owner_remove; this proposal changes that placement and cannot be treated as prior assent. The existing Makefile invokes normal Helm upgrade with no transaction journal or all-writer revision guard. T02's operational rollback intention is an existing legitimate mechanism, not an implemented proof boundary. + +If Apps does not adopt an owner-run external transaction around the existing pre-hand-over deployment, there is no reachable complete inverse in the current seven-step contract. Then the owners must choose a structural execution/receipt contract change before accepting this type; do not silently expand either Adapter's object scope. The seven descriptors and their pins remain untouched in this proposal. Owner backend implementation, independent review, exact source publication, kernel/catalog acceptance and separately authorized native proof all remain outstanding. diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/owner_configuration_contract.py b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/owner_configuration_contract.py new file mode 100644 index 0000000..da41d96 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/owner_configuration_contract.py @@ -0,0 +1,67 @@ +"""Unadopted Apps T05 control-flow candidate; no CLI, Helm or native capability. + +The owner backend is mandatory, including durable authenticated records and an +exclusive release-writer boundary. Tests supply a deliberately synthetic backend. +This wraps the existing owner deployment + hand-over entry path, not an eighth +kernel step or a replacement Adapter. No execution authority is provided here. +""" +from warden.case_run import Invocation +from warden.case_receipt_state import Refused + + +def inverse(binding, transaction, owner): + """Owner unwind entry, reachable without any delivery/hand-over creation.""" + with owner.exclusive(binding): + record = owner.authenticated_record(transaction) + if record['binding'] != binding: + raise Refused('configuration transaction binding differs') + if record['state'] == 'restored': + return 'already-restored' + if record['state'] != 'confirmed': + raise Refused('configuration outcome requires owner reconciliation') + if not record['changed']: + return 'preexisting-configuration-preserved' + # The owner must serialize ALL release writers across this read + inverse. + # Checking revision before an unguarded Helm command would leave a race. + if owner.observe(binding) != record['after']: + raise Refused('current release differs from case-created post revision') + restored = owner.rollback_to(binding, record['before']['revision']) + if (restored['configuration'] != record['before']['configuration'] + or restored['revision'] <= record['after']['revision']): + raise Refused('owner Helm inverse readback differs') + owner.confirm_inverse(transaction, restored) + return 'case-configuration-restored' + + +def run(binding, owner, admitted, handover): + """One owner entry: record/configure/confirm, then invoke real hand-over. + + The owner must persist begin BEFORE configure and confirm BEFORE hand-over. + Configuration/publication ambiguity stays pending for owner reconciliation. + A failure after confirmed configuration reaches the same owner inverse even + when verification fails before a receipt exists. A successful call returns a + durable transaction identifier for a subsequent owner-run inverse request. + """ + if (type(admitted) is not Invocation or admitted.operation != 'admission' + or admitted.attempt_id or admitted.step != 'hand-over' + or admitted.actor != 'human' or admitted.lane != 'apps-consumer-handover'): + raise Refused('owner wrapper requires exact hand-over admission') + expected = {'case': admitted.pins.case, 'acceptance': admitted.pins.acceptance, + 'principal': admitted.principal, 'owner': admitted.owner, + 'client': admitted.target, 'handover_descriptor': admitted.pins.adapter} + if any(binding.get(key) != value for key, value in expected.items()): + raise Refused('owner wrapper differs from hand-over admission') + with owner.exclusive(binding): + before = owner.observe(binding) + transaction = owner.begin(binding, before) + # begin/configure/confirm exceptions deliberately do not infer rollback + # authority: a mutation may have occurred before its proof was committed. + after, changed = owner.configure(binding, before) + owner.confirm(transaction, after, changed) + try: + result = handover(admitted) + except BaseException: + # Failure to unwind propagates with the original error as its context. + inverse(binding, transaction, owner) + raise + return transaction, result diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/post_revision_guard_removed.txt b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/post_revision_guard_removed.txt new file mode 100644 index 0000000..630d27a --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/post_revision_guard_removed.txt @@ -0,0 +1,19 @@ +F [100%] +=================================== FAILURES =================================== +_ OwnerFlowTests.test_changed_revision_refuses_inverse_and_preserves_other_work _ + +self = + + def test_changed_revision_refuses_inverse_and_preserves_other_work(self): + self.setup_flow() + transaction, _ = self.flow() + self.owner.current['revision'] += 1 + self.owner.current['configuration']['other_work'] = True + before = deepcopy(self.owner.current) +> with self.assertRaisesRegex(Refused, 'current release differs'): +E AssertionError: Refused not raised + +test_owner_configuration_contract.py:240: AssertionError +=========================== short test summary info ============================ +FAILED test_owner_configuration_contract.py::OwnerFlowTests::test_changed_revision_refuses_inverse_and_preserves_other_work +1 failed, 12 deselected in 0.09s diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/railiance-apps-unchanged-owner-status.txt b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/railiance-apps-unchanged-owner-status.txt new file mode 100644 index 0000000..e1402a9 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/railiance-apps-unchanged-owner-status.txt @@ -0,0 +1 @@ +M .custodian-brief.md diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/test_owner_configuration_contract.py b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/test_owner_configuration_contract.py new file mode 100644 index 0000000..9dc04dd --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/test_owner_configuration_contract.py @@ -0,0 +1,310 @@ +"""Synthetic owner flow through the unchanged frozen hand-over/delivery seams. + +No owner approval, trusted journal, real Helm, kernel admission or native proof. +""" +from contextlib import contextmanager +from copy import deepcopy +from dataclasses import asdict, replace +import importlib.util +import json +import os +from pathlib import Path +import unittest + +import yaml +from warden.case_run import Invocation, digest +from warden.case_receipt_state import Outcome, Pins, Refused +from owner_configuration_contract import run, inverse + +ROOT = Path(os.environ['NK_FROZEN_ROOT']) +ADAPTERS = ROOT / 'sso-mfa/k8s/keycape' +def load(name, filename): + spec = importlib.util.spec_from_file_location(name, ADAPTERS / filename) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module +h = load('f3_frozen_handover', 'handover-adapter.py') +d = load('f3_frozen_delivery', 'deliver-consumer-secret-adapter.py') + + +class SyntheticOwner: + """In-memory stand-in for the *required*, currently absent Apps T05 backend.""" + def __init__(self, binding, desired): + self.binding, self.desired = deepcopy(binding), desired + self.current = {'revision': 7, 'configuration': {'local_signin': True, 'oidc': None}} + self.history = {7: deepcopy(self.current['configuration'])} + self.records, self.calls = {}, [] + self.exclusive_available = True + self.locked = False + self.fail_confirm = self.fail_inverse = False + + @contextmanager + def exclusive(self, binding): + if not self.exclusive_available or binding != self.binding: + raise Refused('owner release-writer boundary or source/principal binding absent') + assert not self.locked + self.locked = True + try: + yield + finally: + self.locked = False + + def observe(self, binding): + assert self.locked and binding == self.binding + return deepcopy(self.current) + + def begin(self, binding, before): + assert self.locked + ident = 'fixture-transaction:' + str(len(self.records)) + self.records[ident] = {'binding': deepcopy(binding), 'before': deepcopy(before), 'state': 'pending'} + self.calls.append('durable-intent') + return ident + + def configure(self, binding, before): + assert self.locked and binding == self.binding and before == self.current + if self.current['configuration']['oidc'] == self.desired: + self.calls.append('matched-configuration') + return deepcopy(self.current), False + if self.current['configuration']['oidc'] is not None: + raise Refused('preexisting configuration conflicts') + self.current['revision'] += 1 + self.current['configuration']['oidc'] = self.desired + self.history[self.current['revision']] = deepcopy(self.current['configuration']) + self.calls.append('helm-configure') + return deepcopy(self.current), True + + def confirm(self, ident, after, changed): + assert self.locked + if self.fail_confirm: + raise Refused('configuration receipt persistence failed') + self.records[ident].update(after=deepcopy(after), changed=changed, state='confirmed') + self.calls.append('durable-post-revision') + + def authenticated_record(self, ident): + assert self.locked + if ident not in self.records: + raise Refused('owner transaction not authenticated') + return deepcopy(self.records[ident]) + + def rollback_to(self, binding, revision): + assert self.locked and binding == self.binding + self.calls.append(('helm-rollback', revision)) + if self.fail_inverse: + raise Refused('owner rollback failed') + self.current = {'revision': self.current['revision'] + 1, + 'configuration': deepcopy(self.history[revision])} + return deepcopy(self.current) + + def confirm_inverse(self, ident, restored): + assert self.locked + self.records[ident].update(state='restored', inverse=deepcopy(restored)) + self.calls.append('durable-inverse') + + +class OwnerFlowTests(unittest.TestCase): + def setup_flow(self, confidential=False): + name = 'forgejo.yaml' if confidential else 'example-public-portal.yaml' + source = (ROOT / 'security-case-type-revisions/1.3/oidc-browser-relying-party/examples' / name).read_bytes() + self.case = source + params = yaml.safe_load(source)['spec']['parameters'] + self.params = params + self.owner_repo, self.client = params['consumer_owner_repo'], params['client_id'] + desc = h.descriptor(self.owner_repo) + encoded = json.dumps(asdict(desc), sort_keys=True, separators=(',', ':')).encode() + pins = Pins(digest(source), digest(b'fixture-type'), digest(encoded), digest(b'fixture-catalog'), + digest(b'fixture-acceptance'), digest(b'fixture-run-sheet')) + self.admitted = Invocation('hand-over', self.owner_repo, self.client, 'human', 'fixture-founder', + 'apps-consumer-handover', pins) + self.inv = replace(self.admitted, attempt_id='fixture-run', operation='run') + self.rb = replace(self.admitted, attempt_id='fixture-rollback', operation='rollback') + self.binding = {'case': pins.case, 'acceptance': pins.acceptance, 'principal': self.admitted.principal, + 'owner': self.owner_repo, 'source': digest(b'fixture-approved-owner-config-source'), + 'cluster': 'fixture-cluster-uid', 'namespace': params['consumer_namespace'], + 'release': params['consuming_service'], 'client': self.client, + 'handover_descriptor': pins.adapter, 'attempt': 'fixture-owner-attempt'} + desired = {'issuer': params.get('issuer', 'https://fixture-issuer.invalid'), 'client': self.client, + 'groups': params['access_groups']} + self.owner = SyntheticOwner(self.binding, desired) + self.receipts, self.evidence, self.revoked = set(), {}, [] + self.fail_verify = self.fail_record = self.fail_publish = False + def verify(client, groups): + self.owner.calls.append('handover-verification') + return {'client_id': client, 'groups': list(groups), 'enforces_groups': True, + 'login_verified': not self.fail_verify, + 'observations': [{'probe': p, 'evidence': 'fixture-probe:' + p} for p in sorted(h.PROBES)]} + def record(client, observed): + self.owner.calls.append('handover-receipt') + if self.fail_record: + raise Refused('fixture receipt failure') + ref = 'fixture-handover:' + client + created = ref not in self.receipts + self.receipts.add(ref) + return ref, created + def publish(payload): + if self.fail_publish: + raise Refused('fixture publication failed') + ident = 'fixture-evidence:' + str(len(self.evidence)) + self.evidence[ident] = payload + return ident + def remove(ref): + self.receipts.remove(ref) + return True + def revoke(inv): + self.revoked.append(inv) + return 'fixture-revoked:' + inv.attempt_id + self.adapter = h.build(admitted=self.admitted, case_source=source, case_acceptance='fixture-acceptance', + verify_consumer=verify, record_handover=record, remove_handover=remove, publish=publish, + authenticate=lambda ref, payload: self.evidence.get(ref) == payload, + revoke_session=revoke, version_root='1.3') + + def flow(self): + # Reachable candidate owner entry: configuration encloses the REAL seam. + return run(self.binding, self.owner, self.admitted, + lambda admitted: self.adapter.invoke(replace(admitted, attempt_id=self.inv.attempt_id, operation="run"))) + + def delivery_no_change(self): + encoded = json.dumps(asdict(d.descriptor(self.owner_repo)), sort_keys=True, separators=(',', ':')).encode() + admitted = replace(self.admitted, step='deliver-consumer-secret', lane='consumer-owner-deployment', + pins=replace(self.admitted.pins, adapter=digest(encoded))) + self.delivery_removals = [] + evidence = {} + def apply(inv, want): + return {'schema': 'netkingdom.consumer-secret-delivery.v1', 'operation': 'apply', + 'values_emitted': False, 'consumer_reference_reverted': False, + 'objects': {name: {'uid': 'preexisting-' + name, 'created': False, 'metadata': metadata} + for name, metadata in want.items()}} + def remove(inv, created): + self.delivery_removals.append(created) + raise AssertionError('matched delivery cannot authorize removal') + def publish(payload): + ident = 'fixture-delivery:' + str(len(evidence)); evidence[ident] = payload; return ident + adapter = d.build(admitted=admitted, case_source=self.case, owner_apply=apply, owner_remove=remove, + publish=publish, authenticate=lambda ref, payload: evidence.get(ref) == payload, + revoke_session=lambda inv: 'fixture-revoked:' + inv.attempt_id) + result = adapter.invoke(replace(admitted, attempt_id='fixture-delivery', operation='run')) + self.assertEqual((result.outcome, result.created), (Outcome.NO_CHANGE, ())) + return adapter, replace(admitted, attempt_id='fixture-delivery-inverse', operation='rollback') + + def test_existing_public_flow_counterexample_receipt_rollback_leaves_helm_change(self): + self.setup_flow() + self.assertEqual(self.params['client_type'], 'public') + # Reproduce existing text: owner's own configuration, then receipt seam. + self.owner.current['configuration']['oidc'] = self.owner.desired + result = self.adapter.invoke(self.inv) + self.adapter.rollback(self.rb, result.created) + self.assertEqual(self.receipts, set()) + self.assertEqual(self.owner.current['configuration']['oidc'], self.owner.desired) + + def test_candidate_public_flow_reaches_independent_inverse(self): + self.setup_flow() + transaction, result = self.flow() + self.assertTrue(self.adapter.verify(self.inv, result)) + self.assertLess(self.owner.calls.index('durable-post-revision'), self.owner.calls.index('handover-verification')) + self.adapter.rollback(self.rb, result.created) # unchanged receipt-only path + self.assertEqual(self.owner.current['configuration']['oidc'], self.owner.desired) + self.assertEqual(inverse(self.binding, transaction, self.owner), 'case-configuration-restored') + self.assertEqual(self.owner.current['configuration'], {'local_signin': True, 'oidc': None}) + self.assertEqual(inverse(self.binding, transaction, self.owner), 'already-restored') + + def test_confidential_no_change_delivery_cannot_block_configuration_inverse(self): + self.setup_flow(confidential=True) + adapter, rb = self.delivery_no_change() + transaction, result = self.flow() + inverse(self.binding, transaction, self.owner) + with self.assertRaises(Refused): # contained owner path sanitizes refusal text + adapter.rollback(rb, ('consumer-externalsecret',)) + self.adapter.rollback(self.rb, result.created) + self.assertEqual(self.delivery_removals, []) + self.assertIsNone(self.owner.current['configuration']['oidc']) + + def test_preexisting_configuration_and_receipt_preserved(self): + self.setup_flow() + self.owner.current['configuration']['oidc'] = self.owner.desired + before = deepcopy(self.owner.current) + self.receipts.add('fixture-handover:' + self.client) + transaction, result = self.flow() + self.assertEqual(result.outcome, Outcome.NO_CHANGE) + self.assertEqual(inverse(self.binding, transaction, self.owner), 'preexisting-configuration-preserved') + with self.assertRaises(Refused): + self.adapter.rollback(self.rb, (h.RECEIPT,)) + self.assertEqual(self.owner.current, before) + self.assertEqual(len(self.receipts), 1) + self.assertFalse(any(isinstance(c, tuple) for c in self.owner.calls)) + + def test_changed_revision_refuses_inverse_and_preserves_other_work(self): + self.setup_flow() + transaction, _ = self.flow() + self.owner.current['revision'] += 1 + self.owner.current['configuration']['other_work'] = True + before = deepcopy(self.owner.current) + with self.assertRaisesRegex(Refused, 'current release differs'): + inverse(self.binding, transaction, self.owner) + self.assertEqual(self.owner.current, before) + self.assertEqual(self.owner.records[transaction]['state'], 'confirmed') + + def test_failure_before_receipt_still_reaches_inverse(self): + for failure in ('fail_verify', 'fail_record'): + with self.subTest(failure=failure): + self.setup_flow(); setattr(self, failure, True) + with self.assertRaises(Refused): self.flow() + self.assertEqual(self.receipts, set()) + self.assertIsNone(self.owner.current['configuration']['oidc']) + self.assertEqual(next(iter(self.owner.records.values()))['state'], 'restored') + self.assertEqual(self.revoked, [self.inv]) + + def test_receipt_publication_failure_restores_config_without_claiming_receipt_removal(self): + self.setup_flow(); self.fail_publish = True + with self.assertRaises(Refused): self.flow() + self.assertIsNone(self.owner.current['configuration']['oidc']) + self.assertEqual(len(self.receipts), 1) # uncertain receipt must be reconciled + with self.assertRaises(Refused): self.adapter.rollback(self.rb, (h.RECEIPT,)) + + def test_configuration_confirmation_failure_requires_owner_reconciliation(self): + self.setup_flow(); self.owner.fail_confirm = True + with self.assertRaises(Refused): self.flow() + self.assertEqual(next(iter(self.owner.records.values()))['state'], 'pending') + self.assertEqual(self.owner.current['configuration']['oidc'], self.owner.desired) + self.assertNotIn('handover-verification', self.owner.calls) + with self.assertRaisesRegex(Refused, 'requires owner reconciliation'): + inverse(self.binding, 'fixture-transaction:0', self.owner) + + def test_exact_binding_mismatches_refused(self): + self.setup_flow(); transaction, _ = self.flow() + before = deepcopy(self.owner.current) + for field in self.binding: + with self.subTest(field=field), self.assertRaises(Refused): + inverse({**self.binding, field: 'different'}, transaction, self.owner) + self.assertEqual(self.owner.current, before) + + def test_wrapper_admission_binding_refused_before_owner_mutation(self): + self.setup_flow() + for field, value in [('principal', 'other'), ('target', 'other'), ('owner', 'other'), + ('actor', 'agent'), ('lane', 'other')]: + with self.subTest(field=field), self.assertRaises(Refused): + run(self.binding, self.owner, replace(self.admitted, **{field: value}), + lambda admitted: self.fail('must not reach hand-over')) + self.assertEqual(self.owner.records, {}) + + def test_missing_release_writer_guard_refuses_before_configuration(self): + self.setup_flow(); self.owner.exclusive_available = False + with self.assertRaises(Refused): self.flow() + self.assertEqual(self.owner.records, {}) + self.assertIsNone(self.owner.current['configuration']['oidc']) + + def test_preexisting_conflicting_configuration_not_overwritten(self): + self.setup_flow(); self.owner.current['configuration']['oidc'] = {'client': 'unrelated'} + before = deepcopy(self.owner.current) + with self.assertRaisesRegex(Refused, 'configuration conflicts'): self.flow() + self.assertEqual(self.owner.current, before) + self.assertNotIn('handover-verification', self.owner.calls) + + def test_inverse_failure_retains_original_failure_and_pending_unwind(self): + self.setup_flow(); self.fail_verify = True; self.owner.fail_inverse = True + with self.assertRaisesRegex(Refused, 'owner rollback failed') as caught: + self.flow() + self.assertIsInstance(caught.exception.__context__, Refused) + self.assertEqual(next(iter(self.owner.records.values()))['state'], 'confirmed') + self.assertEqual(self.owner.current['configuration']['oidc'], self.owner.desired) + +if __name__ == '__main__': + unittest.main() diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/type-owner-configuration-inverse.candidate.patch b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/type-owner-configuration-inverse.candidate.patch new file mode 100644 index 0000000..5b74e03 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/type-owner-configuration-inverse.candidate.patch @@ -0,0 +1,61 @@ +--- a/security-case-type-revisions/1.3/oidc-browser-relying-party/type.yaml ++++ b/security-case-type-revisions/1.3/oidc-browser-relying-party/type.yaml +@@ -355,13 +355,23 @@ + Secret values. + - id: hand-over + owner: '{consumer_owner_repo}' +- entry_point: The consumer owner first makes its own Helm change configuring {consuming_service} +- with issuer {issuer}, client {client_id} and custody bao:platform/workloads/keycape/{client_id}, +- requiring one of {access_groups} in the groups claim before sign-in completes. +- Then reviewed adapter net-kingdom/keycape-hand-over, dispatched by Warden's case +- kernel inside the attended consumer-owner session, verifies the consumer with +- four value-free probes (member-login, non-member-refused, membership-add-grants, +- membership-remove-revokes) and records the hand-over receipt. ++ entry_point: The consumer owner's reviewed deployment entry wraps its own Helm ++ configuration and the admitted hand-over in one owner configuration transaction, ++ for public clients as well as confidential clients even when delivery is NO_CHANGE. ++ Before changing {consuming_service}, durably bind exact case, acceptance, human ++ principal, owner configuration source, cluster/release and before revision; ++ authenticate the case-created post revision under the owner's release-writer ++ exclusion before verification. Configure issuer {issuer}, client {client_id} ++ and, only for confidential clients, custody bao:platform/workloads/keycape/{client_id}, ++ requiring one of {access_groups} before sign-in completes. Preserve identical ++ preexisting configuration as NO_CHANGE and refuse conflicts. Then the reviewed ++ adapter net-kingdom/keycape-hand-over, dispatched by Warden's case kernel inside ++ the attended consumer-owner session, verifies the four value-free probes ++ (member-login, non-member-refused, membership-add-grants, membership-remove-revokes) ++ and records the receipt. The owner wrapper's separate configuration inverse is ++ reachable on failure before a receipt exists and on later owner case unwind; ++ it does not depend on delivery-created objects or receipt creation. Missing or ++ ambiguous configuration provenance requires owner reconciliation, never replay. + auth: the attended consumer-owner session on lane apps-consumer-handover (actor human, + parent-session revoked per attempt); the consumer configuration itself is the + owner's own deployment path +@@ -430,13 +440,22 @@ + reads_secret_value: false + rollback: + - reverses: hand-over +- action: the adapter removes only its hand-over receipt. Removing the OIDC source +- for {client_id} from {consuming_service} is the owner's Helm rollback (owner +- {consumer_owner_repo}), done inside deliver-consumer-secret's owner_remove +- before any consumer object is removed ++ action: the adapter removes only its authenticated case-created hand-over receipt. ++ Independently, the consumer owner's configuration transaction inverse restores ++ only this case's configuration change through the noted before Helm revision, ++ after authenticating exact case/principal/source/release binding and checking ++ the unchanged post revision under exclusion of all release writers. It runs ++ on owner unwind for public and confidential cases, including skipped or NO_CHANGE ++ delivery and failures before receipt creation. Preserve preexisting configuration; ++ drift or ambiguous provenance requires owner reconciliation. This owner inverse ++ is outside the receipt adapter and precedes any delivery-object removal; ++ receipt removal alone never proves configuration rollback. + - reverses: deliver-consumer-secret + when: client_type == confidential +- action: After reverting consumer secret reference, delete only authenticated created ++ action: First verify the owner configuration transaction inverse and absence of ++ remaining consumer secret references; NO_CHANGE configuration alone is not proof of ++ deactivation. Do not repeat an unguarded Helm rollback inside owner_remove. ++ Then delete only authenticated created + ExternalSecret/owned target by UID and ownerReference, then SecretStore and + dedicated SA; preserve preexisting-matched resources and never delete a shared + target. Retain audit and ownership receipts; removing SecretStore/SA alone is diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/type.before.yaml b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/type.before.yaml new file mode 100644 index 0000000..6ec7560 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/type.before.yaml @@ -0,0 +1,495 @@ +apiVersion: netkingdom.io/security-case-type/v0.1 +kind: SecurityCaseType +spec: + id: oidc-browser-relying-party + version: '1.3' + status: proposed + title: Browser OIDC relying party for an internal service + purpose: 'Let an internal web service sign its human users in through NetKingdom + (KeyCape): an exact-redirect OIDC client, optional MFA pin, the LLDAP groups the + service maps to its own roles, and, for confidential clients, a client secret + minted straight into OpenBao custody. Replaces the per-client rollout scripts + in sso-mfa/k8s/keycape. First case: Forgejo (KEY-WP-0038).' + parameters: + - name: client_id + type: string + case_settable: true + constraints: + pattern: ^[a-z0-9][a-z0-9-]{1,62}$ + sensitivity: operational + description: OAuth2 client_id, unique in KeyCape. + - name: display_name + type: string + case_settable: true + constraints: + pattern: ^[^\n\r]{1,80}$ + sensitivity: public + description: Name shown on the sign-in page. + - name: redirect_uris + type: string + case_settable: true + constraints: + pattern: ^https://[^*?#\s,]+(,https://[^*?#\s,]+)*$ + sensitivity: security_sensitive + description: 'Comma-separated exact https redirect URIs. No wildcards, no plain + http, no query and no fragment: KeyCape refuses ''*'' and ''?'' at startup, + and OAuth 2.0 forbids fragments (key-cape type review R1).' + - name: allowed_scopes + type: string + case_settable: true + constraints: + enum: + - openid,profile,email,groups + - openid,email,groups + - openid,profile,groups + - openid,groups + sensitivity: security_sensitive + description: Scopes the client may request, openid first. Always includes groups, + because access_groups is required and KeyCape's UserInfo returns groups only + for that scope (key-cape type review R2). + - name: client_type + type: string + case_settable: true + constraints: + enum: + - confidential + - public + sensitivity: security_sensitive + description: confidential for server-side services that can hold a secret; public + for SPAs and native apps (PKCE only). + - name: mfa_required + type: string + case_settable: true + constraints: + enum: + - 'true' + - 'false' + sensitivity: security_sensitive + description: '"true" pins MFA on this client; required when the service grants + administrative or owner rights. "false" is not "no MFA": the client then carries + no mfaRequired field and KeyCape applies the per-user privacyIDEA policy. A + renderer must never emit an explicit mfaRequired false, which would skip that + policy (key-cape type review R3).' + - name: access_groups + type: string + case_settable: true + constraints: + pattern: ^[a-z][a-z0-9-]{1,62}(,[a-z][a-z0-9-]{1,62})*$ + sensitivity: operational + description: 'Comma-separated LLDAP groups that the service maps to its roles. + Created if absent. KeyCape has no per-client group gate: it issues tokens to + any authenticated user and only carries the groups claim, so the consuming service + enforces the gate itself (for Forgejo, its required-claim setting; key-cape + type review R2).' + - name: initial_members + type: string + required: false + case_settable: true + constraints: + pattern: ^[a-z][a-z0-9-]*=[a-z][a-z0-9._-]*(\+[a-z][a-z0-9._-]*)*(;[a-z][a-z0-9-]*=[a-z][a-z0-9._-]*(\+[a-z][a-z0-9._-]*)*)*$ + sensitivity: operational + description: Initial memberships as group=user+user;group=user. Usernames are + directory usernames, never secrets. + - name: consuming_service + type: string + case_settable: true + constraints: + pattern: ^[a-z0-9][a-z0-9-]{0,62}$ + sensitivity: operational + description: The service that signs users in with this client. + - name: consumer_owner_repo + type: string + case_settable: true + constraints: + pattern: ^[a-z0-9][a-z0-9-]*$ + sensitivity: operational + description: The repository that owns the consuming service's configuration. + - name: consumer_namespace + type: string + case_settable: true + constraints: + pattern: ^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$ + sensitivity: security_sensitive + description: Consumer namespace for namespaced ESO TokenRequest reader; actual + workload owner must confirm; never infer from service name. + - name: issuer + type: string + case_settable: false + default: https://kc.coulomb.social + sensitivity: security_sensitive + description: The NetKingdom issuer; fixed by the type. + composition: + authority: netkingdom + initial_trust: + - runtime_secret_trust + - runtime_identity_trust + capabilities: + - c1.lightweight-sso + providers: + c1.lightweight-sso: net-kingdom.keycape-client-registration + parameter_bindings: + net-kingdom.keycape-client-registration.client_id: client_id + net-kingdom.keycape-client-registration.display_name: display_name + net-kingdom.keycape-client-registration.redirect_uris: redirect_uris + net-kingdom.keycape-client-registration.allowed_scopes: allowed_scopes + net-kingdom.keycape-client-registration.client_type: client_type + net-kingdom.keycape-client-registration.mfa_required: mfa_required + materialization: + - id: client-secret-path + kind: kv_path + when: client_type == confidential + template: platform/workloads/keycape/{client_id} + body: field CLIENT_SECRET; minted by the credential broker, never written by hand + - id: keycape-reader-policy + kind: openbao_policy + when: client_type == confidential + template: workload-kv-read-keycape-{client_id}-eso + body: "path \"platform/data/workloads/keycape/{client_id}\" {{\n capabilities\ + \ = [\"read\"]\n}}\n" + description: KeyCape reads its client secret only through ESO. No metadata, list, + parent or sibling access. + - id: consumer-reader-policy + kind: openbao_policy + when: client_type == confidential + template: workload-kv-read-keycape-{client_id}-{consuming_service} + body: "path \"platform/data/workloads/keycape/{client_id}\" {{\n capabilities\ + \ = [\"read\"]\n}}\n" + description: The consuming service reads the same secret. No metadata, list, parent + or sibling access. + - id: eso-role + kind: openbao_role + when: client_type == confidential + template: external-secrets-keycape-{client_id} + body: 'auth: kubernetes + + bound_service_account_names: [external-secrets] + + bound_service_account_namespaces: [external-secrets] + + token_policies: [workload-kv-read-keycape-{client_id}-eso] + + token_no_default_policy: true + + token_ttl: 15m + + token_max_ttl: 15m + + ' + description: The Kubernetes-auth role deliver-secret uses; bound to the ESO service + account only. + - id: consumer-eso-role + kind: openbao_role + when: client_type == confidential + template: external-secrets-keycape-{client_id}-{consuming_service} + body: 'auth: kubernetes + + bound_service_account_names: [{consuming_service}-keycape-{client_id}-eso] + + bound_service_account_namespaces: [{consumer_namespace}] + + audience: openbao + + token_policies: [workload-kv-read-keycape-{client_id}-{consuming_service}] + + token_no_default_policy: true + + token_ttl: 15m + + token_max_ttl: 15m + + ' + description: Consumer ESO requests a token for this separate reference identity. + Common ESO controller remains trusted and can request tokens; no cryptographic + controller isolation is claimed. + - id: catalog-entry + kind: catalog_entry + template: '{consuming_service}-keycape-client' + body: 'id: {consuming_service}-keycape-client + + owner_repo: key-cape + + warden_executes: false + + risk: high + + status: active once verification passes + + workload_ref: applicable, name {consuming_service}, declaration_ref of the consumer''s + published declaration or the explicit unknown form, never inferred + + path_template: platform/workloads/keycape/{client_id} + + field: CLIENT_SECRET + + delegation: native, intended_owner key-cape + + rotation: mint-secret then verification; automatable false until a run has been + proven + + ' + description: One entry per case (ops-mason item 5, ops-warden R4'). It carries + the consumer's workload reference, which ADR-0009 needs for zone selection. + follow_through: + - id: ensure-groups + owner: net-kingdom + entry_point: reviewed adapter net-kingdom/keycape-ensure-groups, dispatched by Warden's + case kernel inside the attended session; it calls identity-provisioner + ensure_service_groups in the provisioner pod through the session's contained + cluster access (groups {access_groups}; members exactly {initial_members} as + recorded in the accepted case, nothing else) + auth: the attended human session on lane keycape-attended-case-change (actor human, + parent-session revoked per attempt); the in-pod call uses the provisioner's own + directory binding, and no provisioner or user-engine token is read or sent + lane: keycape-attended-case-change + warden_executes: false + attended: true + value_handling: none + attended_justification: The provisioner's only HTTP credential is user-engine's + runtime token, and its NetworkPolicy admits only user-engine pods. A proxy lane + would act as user-engine (ops-warden review R8; canon section 3; ops-warden + ADR-0002). Running it in the session register-client already needs adds no attended + session. It can become unattended once the provisioner has an operator credential + of its own and an admitted path. Warden's case kernel dispatches the reviewed adapter only inside the one attended human session (descriptor actor human, lane keycape-attended-case-change, parent-session revoked per attempt); the human session is the authority and the kernel is only the dispatcher. + - id: mint-secret + owner: railiance-platform + when: client_type == confidential + entry_point: 'railiance-platform credential broker: generate a client secret inside + custody at bao:platform/workloads/keycape/{client_id}, through reviewed adapter + railiance-platform/keycape-mint-secret' + auth: a human caller (net-kingdom-admins, aal2) starts it; Warden runs it unattended + through the broker lane under the caller's session, with the caller's broker grant + for the case's KV path; agents and root are refused, and the value is generated + in custody and never returned + lane: keycape-client-secret-generate + warden_executes: true + attended: false + value_handling: generated_in_custody + - id: deliver-secret + owner: railiance-platform + when: client_type == confidential + entry_point: reviewed adapter railiance-platform/keycape-deliver-secret, dispatched + by Warden's case kernel; Platform's deliver capability applies ExternalSecret + sso/keycape-{client_id}-client (key client-secret) through the ESO store for + role external-secrets-keycape-{client_id}, from bao:platform/workloads/keycape/{client_id} + auth: Platform's own delivery path on lane platform-secret-delivery, inside the human + aal2 session; ESO reads custody through role external-secrets-keycape-{client_id}; + Warden holds no Platform credential, and no value leaves custody + lane: platform-secret-delivery + warden_executes: false + attended: false + value_handling: none + ready_check: the delivery for sso/keycape-{client_id}-client reports Ready=True + (status only, no value read) + - id: bind-env + owner: net-kingdom + when: client_type == confidential + entry_point: 'reviewed adapter net-kingdom/keycape-bind-env, dispatched by Warden''s + case kernel inside the attended session: patch Deployment sso/keycape env + KEYCAPE_{client_id:env}_CLIENT_SECRET from sso/keycape-{client_id}-client, by + reference only' + auth: the attended human session on lane keycape-attended-case-change, with operator + kube credentials for namespace sso (actor human, parent-session revoked per attempt) + lane: keycape-attended-case-change + warden_executes: false + attended: true + value_handling: none + attended_justification: A change to the live identity provider's Deployment. It + runs in the same attended operator session as register-client, before it, because + KeyCape resolves a confidential client's secret at start and fails closed if + the binding is missing. Warden's case kernel dispatches the reviewed adapter only inside the one attended human session (descriptor actor human, lane keycape-attended-case-change, parent-session revoked per attempt); the human session is the authority and the kernel is only the dispatcher. + - id: register-client + owner: net-kingdom + entry_point: reviewed adapter net-kingdom/keycape-register-client, dispatched by Warden's + case kernel inside the attended session; it runs sso-mfa/k8s/keycape/keycape-client.py + apply --client-file --case-acceptance --check-config + (net-kingdom.keycape-client-registration) + auth: the attended human session on lane keycape-attended-case-change, with operator + write access to the live KeyCape client registry in namespace sso (actor human, + parent-session revoked per attempt) + lane: keycape-attended-case-change + warden_executes: false + attended: true + value_handling: none + attended_justification: 'The live KeyCape client registry is a Kubernetes Secret, + and agents do not read or write Secrets. Until KeyCape loads clients from a + non-secret registry with secrets referenced from custody, one attended operator + session applies the rendered registration. ops-warden groups all attended steps + of a run into that one session. KeyCape loads clients at startup and keeps login, + authorization and handoff state in memory, so the restart drops in-flight logins + and sessions for every client; runs go in a quiet window (key-cape type review + R4). The rendered registry must pass KeyCape''s own startup checks (keycape + check-config, the pinned checker artifact sha256:be37738fe1c08259fb4f9ca0daf0f9301bfffc237e5bb38435525dfbf11ccff5 + built from key-cape 3c71226, which keycape-client.py runs before it patches) + before the restart: a refused entry stops the single replica and every client + loses sign-in (R1). Warden''s case kernel dispatches the reviewed adapter only inside the one attended human session (descriptor actor human, lane keycape-attended-case-change, parent-session revoked per attempt); the human session is the authority and the kernel is only the dispatcher.' + - id: deliver-consumer-secret + owner: '{consumer_owner_repo}' + when: client_type == confidential + entry_point: Reviewed adapter net-kingdom/keycape-deliver-consumer-secret, dispatched + by Warden's case kernel inside the attended consumer-owner session; the owner's + apply capability creates dedicated ServiceAccount {consumer_namespace}/{consuming_service}-keycape-{client_id}-eso + (automount=false, no legacy tokens), namespace SecretStore openbao-keycape-{client_id}-consumer + with serviceAccountRef and audiences [openbao], and ExternalSecret {consuming_service}-netkingdom-oidc-client + with Owner target, Retain remote deletion and only CLIENT_SECRET from workloads/keycape/{client_id}. + Admission validates actual installed ESO TokenRequest support and common controller + RBAC before any owner-approved apply. + auth: the attended consumer-owner session on lane consumer-owner-deployment (actor + human, the consumer owner's operator deployment authority, on the founder's go + for the change); ESO authenticates to OpenBao role external-secrets-keycape-{client_id}-{consuming_service} + with a TokenRequest for the dedicated namespaced serviceAccountRef; controller + remains trusted + lane: consumer-owner-deployment + warden_executes: false + attended: true + value_handling: none + attended_justification: The consumer owner's only deployment authority in the consumer + namespace is its operator credential, and production applies need the founder's + go per change. No contained agent deployer identity exists, and the owner does + not want a standing credential an agent could trigger. It runs in one attended + consumer-owner session together with hand-over. Warden's case kernel dispatches + the reviewed adapter only inside that session (actor human, parent-session + revoked per attempt); the human session is the authority and the kernel is only + the dispatcher. + ready_check: Namespace SecretStore and ExternalSecret both report Ready=True; + dedicated SA has no legacy token refs; status and fixed metadata only, never + Secret values. + - id: hand-over + owner: '{consumer_owner_repo}' + entry_point: The consumer owner first makes its own Helm change configuring {consuming_service} + with issuer {issuer}, client {client_id} and custody bao:platform/workloads/keycape/{client_id}, + requiring one of {access_groups} in the groups claim before sign-in completes. + Then reviewed adapter net-kingdom/keycape-hand-over, dispatched by Warden's case + kernel inside the attended consumer-owner session, verifies the consumer with + four value-free probes (member-login, non-member-refused, membership-add-grants, + membership-remove-revokes) and records the hand-over receipt. + auth: the attended consumer-owner session on lane apps-consumer-handover (actor human, + parent-session revoked per attempt); the consumer configuration itself is the + owner's own deployment path + lane: apps-consumer-handover + warden_executes: false + attended: true + value_handling: none + attended_justification: The verification needs a real member login and a refused + non-member login, which only an attended session can perform, and it follows + the owner's own Helm change in the same consumer-owner session as + deliver-consumer-secret. Warden's case kernel dispatches the reviewed adapter + only inside that session (actor human, parent-session revoked per attempt); the + human session is the authority and the kernel is only the dispatcher. + verification: + - id: authorize-accepts-redirects + check: GET {issuer}/authorize with client {client_id} and each of {redirect_uris} + returns the login page + expect: present + reads_secret_value: false + - id: authorize-refuses-other-redirect + check: GET {issuer}/authorize with client {client_id} and an unregistered redirect + returns an OAuth error + expect: deny + reads_secret_value: false + - id: custody-denied-to-default + when: client_type == confidential + check: a default-only OpenBao identity has no read capability on bao:platform/workloads/keycape/{client_id} + expect: deny + reads_secret_value: false + - id: custody-allowed-to-consumer + when: client_type == confidential + check: policy workload-kv-read-keycape-{client_id}-{consuming_service} grants + read on bao:platform/workloads/keycape/{client_id} (capabilities check only) + expect: allow + reads_secret_value: false + - id: groups-present + check: LLDAP groups {access_groups} exist, with exactly the memberships of the + accepted case added + expect: present + reads_secret_value: false + - id: consumer-enforces-groups + check: '{consuming_service}''s OIDC source for {client_id} (owner {consumer_owner_repo}) + requires one of {access_groups} in the groups claim (configuration read, no + secret value)' + expect: present + reads_secret_value: false + - id: secret-delivered + when: client_type == confidential + check: the delivery for sso/keycape-{client_id}-client reports Ready=True (status + only) + expect: present + reads_secret_value: false + - id: mfa-pinned + when: mfa_required == true + check: keycape-client.py inspect for {client_id} shows the MFA pin on the registered + client (non-secret client view) + expect: present + reads_secret_value: false + - id: consumer-secret-delivered + when: client_type == confidential + check: '{consumer_namespace} SecretStore openbao-keycape-{client_id}-consumer + and ExternalSecret {consuming_service}-netkingdom-oidc-client Ready=True, exact + dedicated SA/audience role binding, fixed CLIENT_SECRET mapping; no Secret value + read' + expect: present + reads_secret_value: false + rollback: + - reverses: hand-over + action: the adapter removes only its hand-over receipt. Removing the OIDC source + for {client_id} from {consuming_service} is the owner's Helm rollback (owner + {consumer_owner_repo}), done inside deliver-consumer-secret's owner_remove + before any consumer object is removed + - reverses: deliver-consumer-secret + when: client_type == confidential + action: After reverting consumer secret reference, delete only authenticated created + ExternalSecret/owned target by UID and ownerReference, then SecretStore and + dedicated SA; preserve preexisting-matched resources and never delete a shared + target. Retain audit and ownership receipts; removing SecretStore/SA alone is + not immediate OpenBao token revocation. + - reverses: register-client + action: keycape-client.py apply --operation remove for {client_id} + - reverses: bind-env + when: client_type == confidential + action: remove env KEYCAPE_{client_id:env}_CLIENT_SECRET from Deployment sso/keycape, + after register-client is reversed + - reverses: deliver-secret + when: client_type == confidential + action: delete ExternalSecret sso/keycape-{client_id}-client and its target + - reverses: mint-secret + when: client_type == confidential + action: 'covered by client-secret-path: one metadata delete, after deliver-secret + is reversed' + - reverses: ensure-groups + action: identity-provisioner revoke_service_groups inside the provisioner pod + (attended, as ensure-groups) with the case's memberships; delete_if_empty only + if no other case uses {access_groups} + - reverses: client-secret-path + when: client_type == confidential + action: delete metadata and all versions at platform/workloads/keycape/{client_id}, + after the ExternalSecret is gone (this also reverses mint-secret) + - reverses: eso-role + when: client_type == confidential + action: delete Kubernetes-auth role external-secrets-keycape-{client_id} + - reverses: keycape-reader-policy + when: client_type == confidential + action: delete policy workload-kv-read-keycape-{client_id}-eso, after eso-role + - reverses: consumer-eso-role + when: client_type == confidential + action: Delete only this case-created role external-secrets-keycape-{client_id}-{consuming_service}, + before deleting consumer-reader-policy; revoke case-issued OpenBao tokens or + prove expiration before declaring deactivation. + - reverses: consumer-reader-policy + when: client_type == confidential + action: delete policy workload-kv-read-keycape-{client_id}-{consuming_service} + - reverses: catalog-entry + action: remove catalog entry {consuming_service}-keycape-client + acceptance: + accepted_by: + - owner + - founder + risk_class: medium + # Reviewed machine descriptors (NK-WP-0048-T16 successor candidate). Digests are + # generated by sso-mfa/k8s/keycape/compose-1.2-descriptors.py --version 1.3. + execution_descriptors: + bind-env: sha256:d5fcb20cf15bf7454890d2529180c582218e3df12584a99162e4b664b899caa0 + deliver-consumer-secret: sha256:36281df8139fb1ec64b226d94f80eb592de5addab7063ff54c691c641e275fc7 + deliver-secret: sha256:75aaa7346aebdea2a0eb0ba993995515b5248ffefc34caf8e713525e9b58da85 + ensure-groups: sha256:ebc9d5b9167d18e6015f4bbeb9699e8d396d64fbfaf195142781f84f4f47a70a + hand-over: sha256:1bce07a3229b7d0c7c730f35ce5b40fe471009a82de8dd53788ed970a9be1224 + mint-secret: sha256:c92957227235db04953809d92b260d32c8b947472a8e8fd17a258ade3ae8da70 + register-client: sha256:4ecc769f75433b66c7161f13c0fe7e5599633cf466d46cb6d8031f0456f426e7 diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/type.candidate.yaml b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/type.candidate.yaml new file mode 100644 index 0000000..f46a3e8 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/type.candidate.yaml @@ -0,0 +1,514 @@ +apiVersion: netkingdom.io/security-case-type/v0.1 +kind: SecurityCaseType +spec: + id: oidc-browser-relying-party + version: '1.3' + status: proposed + title: Browser OIDC relying party for an internal service + purpose: 'Let an internal web service sign its human users in through NetKingdom + (KeyCape): an exact-redirect OIDC client, optional MFA pin, the LLDAP groups the + service maps to its own roles, and, for confidential clients, a client secret + minted straight into OpenBao custody. Replaces the per-client rollout scripts + in sso-mfa/k8s/keycape. First case: Forgejo (KEY-WP-0038).' + parameters: + - name: client_id + type: string + case_settable: true + constraints: + pattern: ^[a-z0-9][a-z0-9-]{1,62}$ + sensitivity: operational + description: OAuth2 client_id, unique in KeyCape. + - name: display_name + type: string + case_settable: true + constraints: + pattern: ^[^\n\r]{1,80}$ + sensitivity: public + description: Name shown on the sign-in page. + - name: redirect_uris + type: string + case_settable: true + constraints: + pattern: ^https://[^*?#\s,]+(,https://[^*?#\s,]+)*$ + sensitivity: security_sensitive + description: 'Comma-separated exact https redirect URIs. No wildcards, no plain + http, no query and no fragment: KeyCape refuses ''*'' and ''?'' at startup, + and OAuth 2.0 forbids fragments (key-cape type review R1).' + - name: allowed_scopes + type: string + case_settable: true + constraints: + enum: + - openid,profile,email,groups + - openid,email,groups + - openid,profile,groups + - openid,groups + sensitivity: security_sensitive + description: Scopes the client may request, openid first. Always includes groups, + because access_groups is required and KeyCape's UserInfo returns groups only + for that scope (key-cape type review R2). + - name: client_type + type: string + case_settable: true + constraints: + enum: + - confidential + - public + sensitivity: security_sensitive + description: confidential for server-side services that can hold a secret; public + for SPAs and native apps (PKCE only). + - name: mfa_required + type: string + case_settable: true + constraints: + enum: + - 'true' + - 'false' + sensitivity: security_sensitive + description: '"true" pins MFA on this client; required when the service grants + administrative or owner rights. "false" is not "no MFA": the client then carries + no mfaRequired field and KeyCape applies the per-user privacyIDEA policy. A + renderer must never emit an explicit mfaRequired false, which would skip that + policy (key-cape type review R3).' + - name: access_groups + type: string + case_settable: true + constraints: + pattern: ^[a-z][a-z0-9-]{1,62}(,[a-z][a-z0-9-]{1,62})*$ + sensitivity: operational + description: 'Comma-separated LLDAP groups that the service maps to its roles. + Created if absent. KeyCape has no per-client group gate: it issues tokens to + any authenticated user and only carries the groups claim, so the consuming service + enforces the gate itself (for Forgejo, its required-claim setting; key-cape + type review R2).' + - name: initial_members + type: string + required: false + case_settable: true + constraints: + pattern: ^[a-z][a-z0-9-]*=[a-z][a-z0-9._-]*(\+[a-z][a-z0-9._-]*)*(;[a-z][a-z0-9-]*=[a-z][a-z0-9._-]*(\+[a-z][a-z0-9._-]*)*)*$ + sensitivity: operational + description: Initial memberships as group=user+user;group=user. Usernames are + directory usernames, never secrets. + - name: consuming_service + type: string + case_settable: true + constraints: + pattern: ^[a-z0-9][a-z0-9-]{0,62}$ + sensitivity: operational + description: The service that signs users in with this client. + - name: consumer_owner_repo + type: string + case_settable: true + constraints: + pattern: ^[a-z0-9][a-z0-9-]*$ + sensitivity: operational + description: The repository that owns the consuming service's configuration. + - name: consumer_namespace + type: string + case_settable: true + constraints: + pattern: ^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$ + sensitivity: security_sensitive + description: Consumer namespace for namespaced ESO TokenRequest reader; actual + workload owner must confirm; never infer from service name. + - name: issuer + type: string + case_settable: false + default: https://kc.coulomb.social + sensitivity: security_sensitive + description: The NetKingdom issuer; fixed by the type. + composition: + authority: netkingdom + initial_trust: + - runtime_secret_trust + - runtime_identity_trust + capabilities: + - c1.lightweight-sso + providers: + c1.lightweight-sso: net-kingdom.keycape-client-registration + parameter_bindings: + net-kingdom.keycape-client-registration.client_id: client_id + net-kingdom.keycape-client-registration.display_name: display_name + net-kingdom.keycape-client-registration.redirect_uris: redirect_uris + net-kingdom.keycape-client-registration.allowed_scopes: allowed_scopes + net-kingdom.keycape-client-registration.client_type: client_type + net-kingdom.keycape-client-registration.mfa_required: mfa_required + materialization: + - id: client-secret-path + kind: kv_path + when: client_type == confidential + template: platform/workloads/keycape/{client_id} + body: field CLIENT_SECRET; minted by the credential broker, never written by hand + - id: keycape-reader-policy + kind: openbao_policy + when: client_type == confidential + template: workload-kv-read-keycape-{client_id}-eso + body: "path \"platform/data/workloads/keycape/{client_id}\" {{\n capabilities\ + \ = [\"read\"]\n}}\n" + description: KeyCape reads its client secret only through ESO. No metadata, list, + parent or sibling access. + - id: consumer-reader-policy + kind: openbao_policy + when: client_type == confidential + template: workload-kv-read-keycape-{client_id}-{consuming_service} + body: "path \"platform/data/workloads/keycape/{client_id}\" {{\n capabilities\ + \ = [\"read\"]\n}}\n" + description: The consuming service reads the same secret. No metadata, list, parent + or sibling access. + - id: eso-role + kind: openbao_role + when: client_type == confidential + template: external-secrets-keycape-{client_id} + body: 'auth: kubernetes + + bound_service_account_names: [external-secrets] + + bound_service_account_namespaces: [external-secrets] + + token_policies: [workload-kv-read-keycape-{client_id}-eso] + + token_no_default_policy: true + + token_ttl: 15m + + token_max_ttl: 15m + + ' + description: The Kubernetes-auth role deliver-secret uses; bound to the ESO service + account only. + - id: consumer-eso-role + kind: openbao_role + when: client_type == confidential + template: external-secrets-keycape-{client_id}-{consuming_service} + body: 'auth: kubernetes + + bound_service_account_names: [{consuming_service}-keycape-{client_id}-eso] + + bound_service_account_namespaces: [{consumer_namespace}] + + audience: openbao + + token_policies: [workload-kv-read-keycape-{client_id}-{consuming_service}] + + token_no_default_policy: true + + token_ttl: 15m + + token_max_ttl: 15m + + ' + description: Consumer ESO requests a token for this separate reference identity. + Common ESO controller remains trusted and can request tokens; no cryptographic + controller isolation is claimed. + - id: catalog-entry + kind: catalog_entry + template: '{consuming_service}-keycape-client' + body: 'id: {consuming_service}-keycape-client + + owner_repo: key-cape + + warden_executes: false + + risk: high + + status: active once verification passes + + workload_ref: applicable, name {consuming_service}, declaration_ref of the consumer''s + published declaration or the explicit unknown form, never inferred + + path_template: platform/workloads/keycape/{client_id} + + field: CLIENT_SECRET + + delegation: native, intended_owner key-cape + + rotation: mint-secret then verification; automatable false until a run has been + proven + + ' + description: One entry per case (ops-mason item 5, ops-warden R4'). It carries + the consumer's workload reference, which ADR-0009 needs for zone selection. + follow_through: + - id: ensure-groups + owner: net-kingdom + entry_point: reviewed adapter net-kingdom/keycape-ensure-groups, dispatched by Warden's + case kernel inside the attended session; it calls identity-provisioner + ensure_service_groups in the provisioner pod through the session's contained + cluster access (groups {access_groups}; members exactly {initial_members} as + recorded in the accepted case, nothing else) + auth: the attended human session on lane keycape-attended-case-change (actor human, + parent-session revoked per attempt); the in-pod call uses the provisioner's own + directory binding, and no provisioner or user-engine token is read or sent + lane: keycape-attended-case-change + warden_executes: false + attended: true + value_handling: none + attended_justification: The provisioner's only HTTP credential is user-engine's + runtime token, and its NetworkPolicy admits only user-engine pods. A proxy lane + would act as user-engine (ops-warden review R8; canon section 3; ops-warden + ADR-0002). Running it in the session register-client already needs adds no attended + session. It can become unattended once the provisioner has an operator credential + of its own and an admitted path. Warden's case kernel dispatches the reviewed adapter only inside the one attended human session (descriptor actor human, lane keycape-attended-case-change, parent-session revoked per attempt); the human session is the authority and the kernel is only the dispatcher. + - id: mint-secret + owner: railiance-platform + when: client_type == confidential + entry_point: 'railiance-platform credential broker: generate a client secret inside + custody at bao:platform/workloads/keycape/{client_id}, through reviewed adapter + railiance-platform/keycape-mint-secret' + auth: a human caller (net-kingdom-admins, aal2) starts it; Warden runs it unattended + through the broker lane under the caller's session, with the caller's broker grant + for the case's KV path; agents and root are refused, and the value is generated + in custody and never returned + lane: keycape-client-secret-generate + warden_executes: true + attended: false + value_handling: generated_in_custody + - id: deliver-secret + owner: railiance-platform + when: client_type == confidential + entry_point: reviewed adapter railiance-platform/keycape-deliver-secret, dispatched + by Warden's case kernel; Platform's deliver capability applies ExternalSecret + sso/keycape-{client_id}-client (key client-secret) through the ESO store for + role external-secrets-keycape-{client_id}, from bao:platform/workloads/keycape/{client_id} + auth: Platform's own delivery path on lane platform-secret-delivery, inside the human + aal2 session; ESO reads custody through role external-secrets-keycape-{client_id}; + Warden holds no Platform credential, and no value leaves custody + lane: platform-secret-delivery + warden_executes: false + attended: false + value_handling: none + ready_check: the delivery for sso/keycape-{client_id}-client reports Ready=True + (status only, no value read) + - id: bind-env + owner: net-kingdom + when: client_type == confidential + entry_point: 'reviewed adapter net-kingdom/keycape-bind-env, dispatched by Warden''s + case kernel inside the attended session: patch Deployment sso/keycape env + KEYCAPE_{client_id:env}_CLIENT_SECRET from sso/keycape-{client_id}-client, by + reference only' + auth: the attended human session on lane keycape-attended-case-change, with operator + kube credentials for namespace sso (actor human, parent-session revoked per attempt) + lane: keycape-attended-case-change + warden_executes: false + attended: true + value_handling: none + attended_justification: A change to the live identity provider's Deployment. It + runs in the same attended operator session as register-client, before it, because + KeyCape resolves a confidential client's secret at start and fails closed if + the binding is missing. Warden's case kernel dispatches the reviewed adapter only inside the one attended human session (descriptor actor human, lane keycape-attended-case-change, parent-session revoked per attempt); the human session is the authority and the kernel is only the dispatcher. + - id: register-client + owner: net-kingdom + entry_point: reviewed adapter net-kingdom/keycape-register-client, dispatched by Warden's + case kernel inside the attended session; it runs sso-mfa/k8s/keycape/keycape-client.py + apply --client-file --case-acceptance --check-config + (net-kingdom.keycape-client-registration) + auth: the attended human session on lane keycape-attended-case-change, with operator + write access to the live KeyCape client registry in namespace sso (actor human, + parent-session revoked per attempt) + lane: keycape-attended-case-change + warden_executes: false + attended: true + value_handling: none + attended_justification: 'The live KeyCape client registry is a Kubernetes Secret, + and agents do not read or write Secrets. Until KeyCape loads clients from a + non-secret registry with secrets referenced from custody, one attended operator + session applies the rendered registration. ops-warden groups all attended steps + of a run into that one session. KeyCape loads clients at startup and keeps login, + authorization and handoff state in memory, so the restart drops in-flight logins + and sessions for every client; runs go in a quiet window (key-cape type review + R4). The rendered registry must pass KeyCape''s own startup checks (keycape + check-config, the pinned checker artifact sha256:be37738fe1c08259fb4f9ca0daf0f9301bfffc237e5bb38435525dfbf11ccff5 + built from key-cape 3c71226, which keycape-client.py runs before it patches) + before the restart: a refused entry stops the single replica and every client + loses sign-in (R1). Warden''s case kernel dispatches the reviewed adapter only inside the one attended human session (descriptor actor human, lane keycape-attended-case-change, parent-session revoked per attempt); the human session is the authority and the kernel is only the dispatcher.' + - id: deliver-consumer-secret + owner: '{consumer_owner_repo}' + when: client_type == confidential + entry_point: Reviewed adapter net-kingdom/keycape-deliver-consumer-secret, dispatched + by Warden's case kernel inside the attended consumer-owner session; the owner's + apply capability creates dedicated ServiceAccount {consumer_namespace}/{consuming_service}-keycape-{client_id}-eso + (automount=false, no legacy tokens), namespace SecretStore openbao-keycape-{client_id}-consumer + with serviceAccountRef and audiences [openbao], and ExternalSecret {consuming_service}-netkingdom-oidc-client + with Owner target, Retain remote deletion and only CLIENT_SECRET from workloads/keycape/{client_id}. + Admission validates actual installed ESO TokenRequest support and common controller + RBAC before any owner-approved apply. + auth: the attended consumer-owner session on lane consumer-owner-deployment (actor + human, the consumer owner's operator deployment authority, on the founder's go + for the change); ESO authenticates to OpenBao role external-secrets-keycape-{client_id}-{consuming_service} + with a TokenRequest for the dedicated namespaced serviceAccountRef; controller + remains trusted + lane: consumer-owner-deployment + warden_executes: false + attended: true + value_handling: none + attended_justification: The consumer owner's only deployment authority in the consumer + namespace is its operator credential, and production applies need the founder's + go per change. No contained agent deployer identity exists, and the owner does + not want a standing credential an agent could trigger. It runs in one attended + consumer-owner session together with hand-over. Warden's case kernel dispatches + the reviewed adapter only inside that session (actor human, parent-session + revoked per attempt); the human session is the authority and the kernel is only + the dispatcher. + ready_check: Namespace SecretStore and ExternalSecret both report Ready=True; + dedicated SA has no legacy token refs; status and fixed metadata only, never + Secret values. + - id: hand-over + owner: '{consumer_owner_repo}' + entry_point: The consumer owner's reviewed deployment entry wraps its own Helm + configuration and the admitted hand-over in one owner configuration transaction, + for public clients as well as confidential clients even when delivery is NO_CHANGE. + Before changing {consuming_service}, durably bind exact case, acceptance, human + principal, owner configuration source, cluster/release and before revision; + authenticate the case-created post revision under the owner's release-writer + exclusion before verification. Configure issuer {issuer}, client {client_id} + and, only for confidential clients, custody bao:platform/workloads/keycape/{client_id}, + requiring one of {access_groups} before sign-in completes. Preserve identical + preexisting configuration as NO_CHANGE and refuse conflicts. Then the reviewed + adapter net-kingdom/keycape-hand-over, dispatched by Warden's case kernel inside + the attended consumer-owner session, verifies the four value-free probes + (member-login, non-member-refused, membership-add-grants, membership-remove-revokes) + and records the receipt. The owner wrapper's separate configuration inverse is + reachable on failure before a receipt exists and on later owner case unwind; + it does not depend on delivery-created objects or receipt creation. Missing or + ambiguous configuration provenance requires owner reconciliation, never replay. + auth: the attended consumer-owner session on lane apps-consumer-handover (actor human, + parent-session revoked per attempt); the consumer configuration itself is the + owner's own deployment path + lane: apps-consumer-handover + warden_executes: false + attended: true + value_handling: none + attended_justification: The verification needs a real member login and a refused + non-member login, which only an attended session can perform, and it follows + the owner's own Helm change in the same consumer-owner session as + deliver-consumer-secret. Warden's case kernel dispatches the reviewed adapter + only inside that session (actor human, parent-session revoked per attempt); the + human session is the authority and the kernel is only the dispatcher. + verification: + - id: authorize-accepts-redirects + check: GET {issuer}/authorize with client {client_id} and each of {redirect_uris} + returns the login page + expect: present + reads_secret_value: false + - id: authorize-refuses-other-redirect + check: GET {issuer}/authorize with client {client_id} and an unregistered redirect + returns an OAuth error + expect: deny + reads_secret_value: false + - id: custody-denied-to-default + when: client_type == confidential + check: a default-only OpenBao identity has no read capability on bao:platform/workloads/keycape/{client_id} + expect: deny + reads_secret_value: false + - id: custody-allowed-to-consumer + when: client_type == confidential + check: policy workload-kv-read-keycape-{client_id}-{consuming_service} grants + read on bao:platform/workloads/keycape/{client_id} (capabilities check only) + expect: allow + reads_secret_value: false + - id: groups-present + check: LLDAP groups {access_groups} exist, with exactly the memberships of the + accepted case added + expect: present + reads_secret_value: false + - id: consumer-enforces-groups + check: '{consuming_service}''s OIDC source for {client_id} (owner {consumer_owner_repo}) + requires one of {access_groups} in the groups claim (configuration read, no + secret value)' + expect: present + reads_secret_value: false + - id: secret-delivered + when: client_type == confidential + check: the delivery for sso/keycape-{client_id}-client reports Ready=True (status + only) + expect: present + reads_secret_value: false + - id: mfa-pinned + when: mfa_required == true + check: keycape-client.py inspect for {client_id} shows the MFA pin on the registered + client (non-secret client view) + expect: present + reads_secret_value: false + - id: consumer-secret-delivered + when: client_type == confidential + check: '{consumer_namespace} SecretStore openbao-keycape-{client_id}-consumer + and ExternalSecret {consuming_service}-netkingdom-oidc-client Ready=True, exact + dedicated SA/audience role binding, fixed CLIENT_SECRET mapping; no Secret value + read' + expect: present + reads_secret_value: false + rollback: + - reverses: hand-over + action: the adapter removes only its authenticated case-created hand-over receipt. + Independently, the consumer owner's configuration transaction inverse restores + only this case's configuration change through the noted before Helm revision, + after authenticating exact case/principal/source/release binding and checking + the unchanged post revision under exclusion of all release writers. It runs + on owner unwind for public and confidential cases, including skipped or NO_CHANGE + delivery and failures before receipt creation. Preserve preexisting configuration; + drift or ambiguous provenance requires owner reconciliation. This owner inverse + is outside the receipt adapter and precedes any delivery-object removal; + receipt removal alone never proves configuration rollback. + - reverses: deliver-consumer-secret + when: client_type == confidential + action: First verify the owner configuration transaction inverse and absence of + remaining consumer secret references; NO_CHANGE configuration alone is not proof of + deactivation. Do not repeat an unguarded Helm rollback inside owner_remove. + Then delete only authenticated created + ExternalSecret/owned target by UID and ownerReference, then SecretStore and + dedicated SA; preserve preexisting-matched resources and never delete a shared + target. Retain audit and ownership receipts; removing SecretStore/SA alone is + not immediate OpenBao token revocation. + - reverses: register-client + action: keycape-client.py apply --operation remove for {client_id} + - reverses: bind-env + when: client_type == confidential + action: remove env KEYCAPE_{client_id:env}_CLIENT_SECRET from Deployment sso/keycape, + after register-client is reversed + - reverses: deliver-secret + when: client_type == confidential + action: delete ExternalSecret sso/keycape-{client_id}-client and its target + - reverses: mint-secret + when: client_type == confidential + action: 'covered by client-secret-path: one metadata delete, after deliver-secret + is reversed' + - reverses: ensure-groups + action: identity-provisioner revoke_service_groups inside the provisioner pod + (attended, as ensure-groups) with the case's memberships; delete_if_empty only + if no other case uses {access_groups} + - reverses: client-secret-path + when: client_type == confidential + action: delete metadata and all versions at platform/workloads/keycape/{client_id}, + after the ExternalSecret is gone (this also reverses mint-secret) + - reverses: eso-role + when: client_type == confidential + action: delete Kubernetes-auth role external-secrets-keycape-{client_id} + - reverses: keycape-reader-policy + when: client_type == confidential + action: delete policy workload-kv-read-keycape-{client_id}-eso, after eso-role + - reverses: consumer-eso-role + when: client_type == confidential + action: Delete only this case-created role external-secrets-keycape-{client_id}-{consuming_service}, + before deleting consumer-reader-policy; revoke case-issued OpenBao tokens or + prove expiration before declaring deactivation. + - reverses: consumer-reader-policy + when: client_type == confidential + action: delete policy workload-kv-read-keycape-{client_id}-{consuming_service} + - reverses: catalog-entry + action: remove catalog entry {consuming_service}-keycape-client + acceptance: + accepted_by: + - owner + - founder + risk_class: medium + # Reviewed machine descriptors (NK-WP-0048-T16 successor candidate). Digests are + # generated by sso-mfa/k8s/keycape/compose-1.2-descriptors.py --version 1.3. + execution_descriptors: + bind-env: sha256:d5fcb20cf15bf7454890d2529180c582218e3df12584a99162e4b664b899caa0 + deliver-consumer-secret: sha256:36281df8139fb1ec64b226d94f80eb592de5addab7063ff54c691c641e275fc7 + deliver-secret: sha256:75aaa7346aebdea2a0eb0ba993995515b5248ffefc34caf8e713525e9b58da85 + ensure-groups: sha256:ebc9d5b9167d18e6015f4bbeb9699e8d396d64fbfaf195142781f84f4f47a70a + hand-over: sha256:1bce07a3229b7d0c7c730f35ce5b40fe471009a82de8dd53788ed970a9be1224 + mint-secret: sha256:c92957227235db04953809d92b260d32c8b947472a8e8fd17a258ade3ae8da70 + register-client: sha256:4ecc769f75433b66c7161f13c0fe7e5599633cf466d46cb6d8031f0456f426e7 diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/README.md b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/README.md new file mode 100644 index 0000000..90b99ef --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/README.md @@ -0,0 +1,13 @@ +# NK48-R5-F3 corrected source candidate r2 + +This is implementation continuation after independent `nk48-r5-f3-review-r1`, not another independent review or Apps owner assent. Original r1 candidate/review and historical receipts remain unchanged. No canonical edit, native command, credential access, promotion, source publication or new grant was performed. + +The corrected owner configuration inverse requires `owner.inverse_session(binding, transaction)` before acquiring the release-writer boundary. Its backend contract requires a distinct fresh authorized attended inverse operation with exact operation/principal/case/source/release/transaction scope, durable attempt/outcome evidence and per-attempt revocation on every exit. It cannot reuse the parent session that the real hand-over adapter already revokes on both success and failure. The candidate type's hand-over auth and owner-contract prose now state this explicitly. A missing current inverse session preserves pending owner reconciliation and the original failure context. + +**19 synthetic tests / 33 subtests pass** through the unchanged real frozen hand-over/delivery seams. Added positive fresh session open/revoke, revoked hand-over reuse refusal, missing current authority after failure, operation/transaction/all binding-field mismatch, and final revocation after rollback, drift and inverse-record persistence failures. A separate scratch negative control removing the mandatory inverse-session context is detected. The fixtures provide an explicit synthetic authority oracle: they authenticate no real principal and supply no native capability. They do not establish that journal, serialization or session enforcement exists in Apps. + +Existing owner type validation and both example case validators pass under explicit allow-unimplemented preparation. Type changes are confined to hand-over entry/auth and the existing two rollback action texts. Seven descriptors, maps, parameters and proposed status remain equal to the frozen source. `implementation.json` records source pins and corrected hashes; `candidate-validation.json` records the bounded structural comparison. No eighth kernel step is introduced. + +`owner-source-inventory.json` and `owner-backend-next.md` record the actual Apps executable-source inspection. No supported real configuration journal, inverse session, all-writer exclusion or T05 owner adapter backend was found. Therefore no fake-command implementation is represented as a real adapter binding. The smallest real source change remains a bounded owner wrapper/inverse around the existing attended release path, after the named owner backend choices are supplied within existing RAPPS-WP-0016-T05. + +This packet corrects RAPPS-F3-SESSION-1 in the proposed protocol; it does not close F3, T05, owner adoption of revised C3, kernel/catalog acceptance or native proof. Public and confidential NO_CHANGE-delivery inverses remain independently reachable in the candidate. Receipt rollback remains receipt-only; delivery removal remains created-only and requires independently proven reference deactivation. diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/candidate-validation.json b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/candidate-validation.json new file mode 100644 index 0000000..a6b4881 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/candidate-validation.json @@ -0,0 +1,30 @@ +{ + "status": "passed", + "checks": [ + { + "check": "existing_owner_validate_type", + "issues": [] + }, + { + "check": "existing_owner_validate_case/forgejo.yaml", + "allow_unimplemented": true, + "issues": [] + }, + { + "check": "existing_owner_validate_case/example-public-portal.yaml", + "allow_unimplemented": true, + "issues": [] + }, + { + "check": "bounded_structural_diff", + "changed": [ + "follow_through[hand-over].entry_point", + "follow_through[hand-over].auth", + "rollback[hand-over].action", + "rollback[deliver-consumer-secret].action" + ], + "seven_descriptors_parameters_proposed_status_preserved": true + } + ], + "native_or_owner_acceptance": false +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/focused-tests.json b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/focused-tests.json new file mode 100644 index 0000000..fd3bea7 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/focused-tests.json @@ -0,0 +1,19 @@ +{ + "command": [ + "/home/worsch/ops-warden/.venv/bin/python", + "-m", + "pytest", + "-q", + "-p", + "no:cacheprovider", + "/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/test_owner_configuration_contract.py" + ], + "exit_code": 0, + "environment": { + "PATH": "/usr/bin:/bin", + "PYTHONDONTWRITEBYTECODE": "1", + "NK_FROZEN_ROOT": "/tmp/nk48-r5-f1-ahudwokt/net-kingdom", + "PYTHONPATH": "/tmp/nk48-r5-f1-ahudwokt/warden-frozen/src:/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2" + }, + "scope": "synthetic corrected owner protocol through unchanged frozen adapter seams; no real backend" +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/focused-tests.txt b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/focused-tests.txt new file mode 100644 index 0000000..410a4e3 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/focused-tests.txt @@ -0,0 +1,2 @@ +................... [100%] +19 passed, 33 subtests passed in 0.19s diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/fresh-inverse-session.candidate.patch b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/fresh-inverse-session.candidate.patch new file mode 100644 index 0000000..c0772c0 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/fresh-inverse-session.candidate.patch @@ -0,0 +1,16 @@ +--- a/owner_configuration_contract.py ++++ b/owner_configuration_contract.py +@@ -11,7 +11,12 @@ + + def inverse(binding, transaction, owner): + """Owner unwind entry, reachable without any delivery/hand-over creation.""" +- with owner.exclusive(binding): ++ # This is a distinct operation: hand-over has already revoked its session. ++ # Backend must authenticate a fresh attended inverse attempt, exact operation, ++ # principal/owner/case/source/release/transaction scope and current disposition; ++ # durably retain authority + outcome and revoke this attempt on every exit. ++ # Durable configuration provenance alone is never execution authority. ++ with owner.inverse_session(binding, transaction), owner.exclusive(binding): + record = owner.authenticated_record(transaction) + if record['binding'] != binding: + raise Refused('configuration transaction binding differs') diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/fresh-session-guard-omitted.txt b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/fresh-session-guard-omitted.txt new file mode 100644 index 0000000..b863669 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/fresh-session-guard-omitted.txt @@ -0,0 +1,353 @@ +F.F.FF [100%] +=================================== FAILURES =================================== +____ OwnerFlowTests.test_fresh_inverse_session_exact_scope_open_and_revoked ____ + +self = + + def test_fresh_inverse_session_exact_scope_open_and_revoked(self): + self.setup_flow() + transaction, _ = self.flow() + self.assertEqual(self.revoked, [self.inv]) + inverse(self.binding, transaction, self.owner) +> opened, closed = self.owner.inverse_session_events + ^^^^^^^^^^^^^^ +E ValueError: not enough values to unpack (expected 2, got 0) + +nk48-apps-f3-r2-negative/test_owner_configuration_contract.py:347: ValueError +_ OwnerFlowTests.test_fresh_inverse_session_revoked_on_guard_and_persistence_failure (failure='drift') _ + +self = + + def test_fresh_inverse_session_revoked_on_guard_and_persistence_failure(self): + for failure in ('drift', 'persistence'): + with self.subTest(failure=failure): + self.setup_flow(); transaction, _ = self.flow() + if failure == 'drift': + self.owner.current['revision'] += 1 + else: + def refuse(ident, restored): + raise Refused('inverse persistence failed') + self.owner.confirm_inverse = refuse + with self.assertRaises(Refused): + inverse(self.binding, transaction, self.owner) +> opened, closed = self.owner.inverse_session_events + ^^^^^^^^^^^^^^ +E ValueError: not enough values to unpack (expected 2, got 0) + +nk48-apps-f3-r2-negative/test_owner_configuration_contract.py:415: ValueError +_ OwnerFlowTests.test_fresh_inverse_session_revoked_on_guard_and_persistence_failure (failure='persistence') _ + +self = + + def test_fresh_inverse_session_revoked_on_guard_and_persistence_failure(self): + for failure in ('drift', 'persistence'): + with self.subTest(failure=failure): + self.setup_flow(); transaction, _ = self.flow() + if failure == 'drift': + self.owner.current['revision'] += 1 + else: + def refuse(ident, restored): + raise Refused('inverse persistence failed') + self.owner.confirm_inverse = refuse + with self.assertRaises(Refused): + inverse(self.binding, transaction, self.owner) +> opened, closed = self.owner.inverse_session_events + ^^^^^^^^^^^^^^ +E ValueError: not enough values to unpack (expected 2, got 0) + +nk48-apps-f3-r2-negative/test_owner_configuration_contract.py:415: ValueError +_____ OwnerFlowTests.test_fresh_inverse_session_revoked_on_inverse_failure _____ + +self = + + def test_fresh_inverse_session_revoked_on_inverse_failure(self): + self.setup_flow(); transaction, _ = self.flow() + self.owner.fail_inverse = True + with self.assertRaisesRegex(Refused, 'owner rollback failed'): + inverse(self.binding, transaction, self.owner) +> opened, closed = self.owner.inverse_session_events + ^^^^^^^^^^^^^^ +E ValueError: not enough values to unpack (expected 2, got 0) + +nk48-apps-f3-r2-negative/test_owner_configuration_contract.py:398: ValueError +_ OwnerFlowTests.test_inverse_scope_mismatches_refused_without_mutation (changed={'operation': 'hand-over'}) _ + +self = + + def test_inverse_scope_mismatches_refused_without_mutation(self): + self.setup_flow(); transaction, _ = self.flow() + scope = {'operation': 'configuration-inverse', + 'binding': deepcopy(self.binding), 'transaction': transaction} + before = deepcopy(self.owner.current) + cases = [{'operation': 'hand-over'}, {'transaction': 'other-transaction'}] + cases += [{'binding': {**self.binding, field: 'other'}} for field in self.binding] + for changed in cases: + with self.subTest(changed=changed): + self.owner.inverse_authority_override = {**scope, **changed} +> with self.assertRaisesRegex(Refused, 'scope/authority'): +E AssertionError: Refused not raised + +nk48-apps-f3-r2-negative/test_owner_configuration_contract.py:388: AssertionError +_ OwnerFlowTests.test_inverse_scope_mismatches_refused_without_mutation (changed={'transaction': 'other-transaction'}) _ + +self = + + def test_inverse_scope_mismatches_refused_without_mutation(self): + self.setup_flow(); transaction, _ = self.flow() + scope = {'operation': 'configuration-inverse', + 'binding': deepcopy(self.binding), 'transaction': transaction} + before = deepcopy(self.owner.current) + cases = [{'operation': 'hand-over'}, {'transaction': 'other-transaction'}] + cases += [{'binding': {**self.binding, field: 'other'}} for field in self.binding] + for changed in cases: + with self.subTest(changed=changed): + self.owner.inverse_authority_override = {**scope, **changed} +> with self.assertRaisesRegex(Refused, 'scope/authority'): +E AssertionError: Refused not raised + +nk48-apps-f3-r2-negative/test_owner_configuration_contract.py:388: AssertionError +_ OwnerFlowTests.test_inverse_scope_mismatches_refused_without_mutation (changed={'binding': {'acceptance': 'sha256:3e5028cf109dc045bc677e34aa63c6e809f0ce98a7c53888d78a5c7bdb865a73', 'attempt': 'fixture-owner-attempt', 'case': 'other', 'client': 'example-portal', ...}}) _ + +self = + + def test_inverse_scope_mismatches_refused_without_mutation(self): + self.setup_flow(); transaction, _ = self.flow() + scope = {'operation': 'configuration-inverse', + 'binding': deepcopy(self.binding), 'transaction': transaction} + before = deepcopy(self.owner.current) + cases = [{'operation': 'hand-over'}, {'transaction': 'other-transaction'}] + cases += [{'binding': {**self.binding, field: 'other'}} for field in self.binding] + for changed in cases: + with self.subTest(changed=changed): + self.owner.inverse_authority_override = {**scope, **changed} +> with self.assertRaisesRegex(Refused, 'scope/authority'): +E AssertionError: Refused not raised + +nk48-apps-f3-r2-negative/test_owner_configuration_contract.py:388: AssertionError +_ OwnerFlowTests.test_inverse_scope_mismatches_refused_without_mutation (changed={'binding': {'acceptance': 'other', 'attempt': 'fixture-owner-attempt', 'case': 'sha256:2b30b9403ba124514e3ab8777b187a225099e2f7683fdd41d0ad48ef3d9649a7', 'client': 'example-portal', ...}}) _ + +self = + + def test_inverse_scope_mismatches_refused_without_mutation(self): + self.setup_flow(); transaction, _ = self.flow() + scope = {'operation': 'configuration-inverse', + 'binding': deepcopy(self.binding), 'transaction': transaction} + before = deepcopy(self.owner.current) + cases = [{'operation': 'hand-over'}, {'transaction': 'other-transaction'}] + cases += [{'binding': {**self.binding, field: 'other'}} for field in self.binding] + for changed in cases: + with self.subTest(changed=changed): + self.owner.inverse_authority_override = {**scope, **changed} +> with self.assertRaisesRegex(Refused, 'scope/authority'): +E AssertionError: Refused not raised + +nk48-apps-f3-r2-negative/test_owner_configuration_contract.py:388: AssertionError +_ OwnerFlowTests.test_inverse_scope_mismatches_refused_without_mutation (changed={'binding': {'acceptance': 'sha256:3e5028cf109dc045bc677e34aa63c6e809f0ce98a7c53888d78a5c7bdb865a73', 'attempt': 'fixt...', 'case': 'sha256:2b30b9403ba124514e3ab8777b187a225099e2f7683fdd41d0ad48ef3d9649a7', 'client': 'example-portal', ...}}) _ + +self = + + def test_inverse_scope_mismatches_refused_without_mutation(self): + self.setup_flow(); transaction, _ = self.flow() + scope = {'operation': 'configuration-inverse', + 'binding': deepcopy(self.binding), 'transaction': transaction} + before = deepcopy(self.owner.current) + cases = [{'operation': 'hand-over'}, {'transaction': 'other-transaction'}] + cases += [{'binding': {**self.binding, field: 'other'}} for field in self.binding] + for changed in cases: + with self.subTest(changed=changed): + self.owner.inverse_authority_override = {**scope, **changed} +> with self.assertRaisesRegex(Refused, 'scope/authority'): +E AssertionError: Refused not raised + +nk48-apps-f3-r2-negative/test_owner_configuration_contract.py:388: AssertionError +_ OwnerFlowTests.test_inverse_scope_mismatches_refused_without_mutation (changed={'binding': {'acceptance': 'sha256:3e5028cf109dc045bc677e34aa63c6e809f0ce98a7c53888d78a5c7bdb865a73', 'attempt': 'fixt...', 'case': 'sha256:2b30b9403ba124514e3ab8777b187a225099e2f7683fdd41d0ad48ef3d9649a7', 'client': 'example-portal', ...}}) _ + +self = + + def test_inverse_scope_mismatches_refused_without_mutation(self): + self.setup_flow(); transaction, _ = self.flow() + scope = {'operation': 'configuration-inverse', + 'binding': deepcopy(self.binding), 'transaction': transaction} + before = deepcopy(self.owner.current) + cases = [{'operation': 'hand-over'}, {'transaction': 'other-transaction'}] + cases += [{'binding': {**self.binding, field: 'other'}} for field in self.binding] + for changed in cases: + with self.subTest(changed=changed): + self.owner.inverse_authority_override = {**scope, **changed} +> with self.assertRaisesRegex(Refused, 'scope/authority'): +E AssertionError: Refused not raised + +nk48-apps-f3-r2-negative/test_owner_configuration_contract.py:388: AssertionError +_ OwnerFlowTests.test_inverse_scope_mismatches_refused_without_mutation (changed={'binding': {'acceptance': 'sha256:3e5028cf109dc045bc677e34aa63c6e809f0ce98a7c53888d78a5c7bdb865a73', 'attempt': 'fixt...', 'case': 'sha256:2b30b9403ba124514e3ab8777b187a225099e2f7683fdd41d0ad48ef3d9649a7', 'client': 'example-portal', ...}}) _ + +self = + + def test_inverse_scope_mismatches_refused_without_mutation(self): + self.setup_flow(); transaction, _ = self.flow() + scope = {'operation': 'configuration-inverse', + 'binding': deepcopy(self.binding), 'transaction': transaction} + before = deepcopy(self.owner.current) + cases = [{'operation': 'hand-over'}, {'transaction': 'other-transaction'}] + cases += [{'binding': {**self.binding, field: 'other'}} for field in self.binding] + for changed in cases: + with self.subTest(changed=changed): + self.owner.inverse_authority_override = {**scope, **changed} +> with self.assertRaisesRegex(Refused, 'scope/authority'): +E AssertionError: Refused not raised + +nk48-apps-f3-r2-negative/test_owner_configuration_contract.py:388: AssertionError +_ OwnerFlowTests.test_inverse_scope_mismatches_refused_without_mutation (changed={'binding': {'acceptance': 'sha256:3e5028cf109dc045bc677e34aa63c6e809f0ce98a7c53888d78a5c7bdb865a73', 'attempt': 'fixt...', 'case': 'sha256:2b30b9403ba124514e3ab8777b187a225099e2f7683fdd41d0ad48ef3d9649a7', 'client': 'example-portal', ...}}) _ + +self = + + def test_inverse_scope_mismatches_refused_without_mutation(self): + self.setup_flow(); transaction, _ = self.flow() + scope = {'operation': 'configuration-inverse', + 'binding': deepcopy(self.binding), 'transaction': transaction} + before = deepcopy(self.owner.current) + cases = [{'operation': 'hand-over'}, {'transaction': 'other-transaction'}] + cases += [{'binding': {**self.binding, field: 'other'}} for field in self.binding] + for changed in cases: + with self.subTest(changed=changed): + self.owner.inverse_authority_override = {**scope, **changed} +> with self.assertRaisesRegex(Refused, 'scope/authority'): +E AssertionError: Refused not raised + +nk48-apps-f3-r2-negative/test_owner_configuration_contract.py:388: AssertionError +_ OwnerFlowTests.test_inverse_scope_mismatches_refused_without_mutation (changed={'binding': {'acceptance': 'sha256:3e5028cf109dc045bc677e34aa63c6e809f0ce98a7c53888d78a5c7bdb865a73', 'attempt': 'fixt...', 'case': 'sha256:2b30b9403ba124514e3ab8777b187a225099e2f7683fdd41d0ad48ef3d9649a7', 'client': 'example-portal', ...}}) _ + +self = + + def test_inverse_scope_mismatches_refused_without_mutation(self): + self.setup_flow(); transaction, _ = self.flow() + scope = {'operation': 'configuration-inverse', + 'binding': deepcopy(self.binding), 'transaction': transaction} + before = deepcopy(self.owner.current) + cases = [{'operation': 'hand-over'}, {'transaction': 'other-transaction'}] + cases += [{'binding': {**self.binding, field: 'other'}} for field in self.binding] + for changed in cases: + with self.subTest(changed=changed): + self.owner.inverse_authority_override = {**scope, **changed} +> with self.assertRaisesRegex(Refused, 'scope/authority'): +E AssertionError: Refused not raised + +nk48-apps-f3-r2-negative/test_owner_configuration_contract.py:388: AssertionError +_ OwnerFlowTests.test_inverse_scope_mismatches_refused_without_mutation (changed={'binding': {'acceptance': 'sha256:3e5028cf109dc045bc677e34aa63c6e809f0ce98a7c53888d78a5c7bdb865a73', 'attempt': 'fixt...', 'case': 'sha256:2b30b9403ba124514e3ab8777b187a225099e2f7683fdd41d0ad48ef3d9649a7', 'client': 'example-portal', ...}}) _ + +self = + + def test_inverse_scope_mismatches_refused_without_mutation(self): + self.setup_flow(); transaction, _ = self.flow() + scope = {'operation': 'configuration-inverse', + 'binding': deepcopy(self.binding), 'transaction': transaction} + before = deepcopy(self.owner.current) + cases = [{'operation': 'hand-over'}, {'transaction': 'other-transaction'}] + cases += [{'binding': {**self.binding, field: 'other'}} for field in self.binding] + for changed in cases: + with self.subTest(changed=changed): + self.owner.inverse_authority_override = {**scope, **changed} +> with self.assertRaisesRegex(Refused, 'scope/authority'): +E AssertionError: Refused not raised + +nk48-apps-f3-r2-negative/test_owner_configuration_contract.py:388: AssertionError +_ OwnerFlowTests.test_inverse_scope_mismatches_refused_without_mutation (changed={'binding': {'acceptance': 'sha256:3e5028cf109dc045bc677e34aa63c6e809f0ce98a7c53888d78a5c7bdb865a73', 'attempt': 'fixt...r-attempt', 'case': 'sha256:2b30b9403ba124514e3ab8777b187a225099e2f7683fdd41d0ad48ef3d9649a7', 'client': 'other', ...}}) _ + +self = + + def test_inverse_scope_mismatches_refused_without_mutation(self): + self.setup_flow(); transaction, _ = self.flow() + scope = {'operation': 'configuration-inverse', + 'binding': deepcopy(self.binding), 'transaction': transaction} + before = deepcopy(self.owner.current) + cases = [{'operation': 'hand-over'}, {'transaction': 'other-transaction'}] + cases += [{'binding': {**self.binding, field: 'other'}} for field in self.binding] + for changed in cases: + with self.subTest(changed=changed): + self.owner.inverse_authority_override = {**scope, **changed} +> with self.assertRaisesRegex(Refused, 'scope/authority'): +E AssertionError: Refused not raised + +nk48-apps-f3-r2-negative/test_owner_configuration_contract.py:388: AssertionError +_ OwnerFlowTests.test_inverse_scope_mismatches_refused_without_mutation (changed={'binding': {'acceptance': 'sha256:3e5028cf109dc045bc677e34aa63c6e809f0ce98a7c53888d78a5c7bdb865a73', 'attempt': 'fixt...', 'case': 'sha256:2b30b9403ba124514e3ab8777b187a225099e2f7683fdd41d0ad48ef3d9649a7', 'client': 'example-portal', ...}}) _ + +self = + + def test_inverse_scope_mismatches_refused_without_mutation(self): + self.setup_flow(); transaction, _ = self.flow() + scope = {'operation': 'configuration-inverse', + 'binding': deepcopy(self.binding), 'transaction': transaction} + before = deepcopy(self.owner.current) + cases = [{'operation': 'hand-over'}, {'transaction': 'other-transaction'}] + cases += [{'binding': {**self.binding, field: 'other'}} for field in self.binding] + for changed in cases: + with self.subTest(changed=changed): + self.owner.inverse_authority_override = {**scope, **changed} +> with self.assertRaisesRegex(Refused, 'scope/authority'): +E AssertionError: Refused not raised + +nk48-apps-f3-r2-negative/test_owner_configuration_contract.py:388: AssertionError +_ OwnerFlowTests.test_inverse_scope_mismatches_refused_without_mutation (changed={'binding': {'acceptance': 'sha256:3e5028cf109dc045bc677e34aa63c6e809f0ce98a7c53888d78a5c7bdb865a73', 'attempt': 'other', 'case': 'sha256:2b30b9403ba124514e3ab8777b187a225099e2f7683fdd41d0ad48ef3d9649a7', 'client': 'example-portal', ...}}) _ + +self = + + def test_inverse_scope_mismatches_refused_without_mutation(self): + self.setup_flow(); transaction, _ = self.flow() + scope = {'operation': 'configuration-inverse', + 'binding': deepcopy(self.binding), 'transaction': transaction} + before = deepcopy(self.owner.current) + cases = [{'operation': 'hand-over'}, {'transaction': 'other-transaction'}] + cases += [{'binding': {**self.binding, field: 'other'}} for field in self.binding] + for changed in cases: + with self.subTest(changed=changed): + self.owner.inverse_authority_override = {**scope, **changed} +> with self.assertRaisesRegex(Refused, 'scope/authority'): +E AssertionError: Refused not raised + +nk48-apps-f3-r2-negative/test_owner_configuration_contract.py:388: AssertionError +_ OwnerFlowTests.test_missing_fresh_authority_failure_unwind_retains_original_error _ +warden.case_receipt_state.Refused: consumer verification contract differs + +During handling of the above exception, another exception occurred: + +self = + + def test_missing_fresh_authority_failure_unwind_retains_original_error(self): + self.setup_flow(); self.fail_verify = True + self.owner.inverse_authority_available = False +> with self.assertRaisesRegex(Refused, 'fresh attended') as caught: +E AssertionError: "fresh attended" does not match "consumer verification contract differs" + +nk48-apps-f3-r2-negative/test_owner_configuration_contract.py:369: AssertionError +____ OwnerFlowTests.test_revoked_handover_session_refused_for_later_inverse ____ + +self = + + def test_revoked_handover_session_refused_for_later_inverse(self): + self.setup_flow(); transaction, _ = self.flow() + self.owner.inverse_session_id_override = self.inv.attempt_id + before = deepcopy(self.owner.current) +> with self.assertRaisesRegex(Refused, 'revoked session'): +E AssertionError: Refused not raised + +nk48-apps-f3-r2-negative/test_owner_configuration_contract.py:360: AssertionError +=========================== short test summary info ============================ +FAILED nk48-apps-f3-r2-negative/test_owner_configuration_contract.py::OwnerFlowTests::test_fresh_inverse_session_exact_scope_open_and_revoked +SUBFAILED(failure='drift') nk48-apps-f3-r2-negative/test_owner_configuration_contract.py::OwnerFlowTests::test_fresh_inverse_session_revoked_on_guard_and_persistence_failure +SUBFAILED(failure='persistence') nk48-apps-f3-r2-negative/test_owner_configuration_contract.py::OwnerFlowTests::test_fresh_inverse_session_revoked_on_guard_and_persistence_failure +FAILED nk48-apps-f3-r2-negative/test_owner_configuration_contract.py::OwnerFlowTests::test_fresh_inverse_session_revoked_on_inverse_failure +SUBFAILED(changed={'operation': 'hand-over'}) nk48-apps-f3-r2-negative/test_owner_configuration_contract.py::OwnerFlowTests::test_inverse_scope_mismatches_refused_without_mutation +SUBFAILED(changed={'transaction': 'other-transaction'}) nk48-apps-f3-r2-negative/test_owner_configuration_contract.py::OwnerFlowTests::test_inverse_scope_mismatches_refused_without_mutation +SUBFAILED(changed={'binding': {'acceptance': 'sha256:3e5028cf109dc045bc677e34aa63c6e809f0ce98a7c53888d78a5c7bdb865a73', 'attempt': 'fixture-owner-attempt', 'case': 'other', 'client': 'example-portal', ...}}) nk48-apps-f3-r2-negative/test_owner_configuration_contract.py::OwnerFlowTests::test_inverse_scope_mismatches_refused_without_mutation +SUBFAILED(changed={'binding': {'acceptance': 'other', 'attempt': 'fixture-owner-attempt', 'case': 'sha256:2b30b9403ba124514e3ab8777b187a225099e2f7683fdd41d0ad48ef3d9649a7', 'client': 'example-portal', ...}}) nk48-apps-f3-r2-negative/test_owner_configuration_contract.py::OwnerFlowTests::test_inverse_scope_mismatches_refused_without_mutation +SUBFAILED(changed={'binding': {'acceptance': 'sha256:3e5028cf109dc045bc677e34aa63c6e809f0ce98a7c53888d78a5c7bdb865a73', 'attempt': 'fixt...', 'case': 'sha256:2b30b9403ba124514e3ab8777b187a225099e2f7683fdd41d0ad48ef3d9649a7', 'client': 'example-portal', ...}}) nk48-apps-f3-r2-negative/test_owner_configuration_contract.py::OwnerFlowTests::test_inverse_scope_mismatches_refused_without_mutation +SUBFAILED(changed={'binding': {'acceptance': 'sha256:3e5028cf109dc045bc677e34aa63c6e809f0ce98a7c53888d78a5c7bdb865a73', 'attempt': 'fixt...', 'case': 'sha256:2b30b9403ba124514e3ab8777b187a225099e2f7683fdd41d0ad48ef3d9649a7', 'client': 'example-portal', ...}}) nk48-apps-f3-r2-negative/test_owner_configuration_contract.py::OwnerFlowTests::test_inverse_scope_mismatches_refused_without_mutation +SUBFAILED(changed={'binding': {'acceptance': 'sha256:3e5028cf109dc045bc677e34aa63c6e809f0ce98a7c53888d78a5c7bdb865a73', 'attempt': 'fixt...', 'case': 'sha256:2b30b9403ba124514e3ab8777b187a225099e2f7683fdd41d0ad48ef3d9649a7', 'client': 'example-portal', ...}}) nk48-apps-f3-r2-negative/test_owner_configuration_contract.py::OwnerFlowTests::test_inverse_scope_mismatches_refused_without_mutation +SUBFAILED(changed={'binding': {'acceptance': 'sha256:3e5028cf109dc045bc677e34aa63c6e809f0ce98a7c53888d78a5c7bdb865a73', 'attempt': 'fixt...', 'case': 'sha256:2b30b9403ba124514e3ab8777b187a225099e2f7683fdd41d0ad48ef3d9649a7', 'client': 'example-portal', ...}}) nk48-apps-f3-r2-negative/test_owner_configuration_contract.py::OwnerFlowTests::test_inverse_scope_mismatches_refused_without_mutation +SUBFAILED(changed={'binding': {'acceptance': 'sha256:3e5028cf109dc045bc677e34aa63c6e809f0ce98a7c53888d78a5c7bdb865a73', 'attempt': 'fixt...', 'case': 'sha256:2b30b9403ba124514e3ab8777b187a225099e2f7683fdd41d0ad48ef3d9649a7', 'client': 'example-portal', ...}}) nk48-apps-f3-r2-negative/test_owner_configuration_contract.py::OwnerFlowTests::test_inverse_scope_mismatches_refused_without_mutation +SUBFAILED(changed={'binding': {'acceptance': 'sha256:3e5028cf109dc045bc677e34aa63c6e809f0ce98a7c53888d78a5c7bdb865a73', 'attempt': 'fixt...', 'case': 'sha256:2b30b9403ba124514e3ab8777b187a225099e2f7683fdd41d0ad48ef3d9649a7', 'client': 'example-portal', ...}}) nk48-apps-f3-r2-negative/test_owner_configuration_contract.py::OwnerFlowTests::test_inverse_scope_mismatches_refused_without_mutation +SUBFAILED(changed={'binding': {'acceptance': 'sha256:3e5028cf109dc045bc677e34aa63c6e809f0ce98a7c53888d78a5c7bdb865a73', 'attempt': 'fixt...r-attempt', 'case': 'sha256:2b30b9403ba124514e3ab8777b187a225099e2f7683fdd41d0ad48ef3d9649a7', 'client': 'other', ...}}) nk48-apps-f3-r2-negative/test_owner_configuration_contract.py::OwnerFlowTests::test_inverse_scope_mismatches_refused_without_mutation +SUBFAILED(changed={'binding': {'acceptance': 'sha256:3e5028cf109dc045bc677e34aa63c6e809f0ce98a7c53888d78a5c7bdb865a73', 'attempt': 'fixt...', 'case': 'sha256:2b30b9403ba124514e3ab8777b187a225099e2f7683fdd41d0ad48ef3d9649a7', 'client': 'example-portal', ...}}) nk48-apps-f3-r2-negative/test_owner_configuration_contract.py::OwnerFlowTests::test_inverse_scope_mismatches_refused_without_mutation +SUBFAILED(changed={'binding': {'acceptance': 'sha256:3e5028cf109dc045bc677e34aa63c6e809f0ce98a7c53888d78a5c7bdb865a73', 'attempt': 'other', 'case': 'sha256:2b30b9403ba124514e3ab8777b187a225099e2f7683fdd41d0ad48ef3d9649a7', 'client': 'example-portal', ...}}) nk48-apps-f3-r2-negative/test_owner_configuration_contract.py::OwnerFlowTests::test_inverse_scope_mismatches_refused_without_mutation +FAILED nk48-apps-f3-r2-negative/test_owner_configuration_contract.py::OwnerFlowTests::test_missing_fresh_authority_failure_unwind_retains_original_error +FAILED nk48-apps-f3-r2-negative/test_owner_configuration_contract.py::OwnerFlowTests::test_revoked_handover_session_refused_for_later_inverse +19 failed, 2 passed, 13 deselected in 0.50s diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/implementation.json b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/implementation.json new file mode 100644 index 0000000..7eae57b --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/implementation.json @@ -0,0 +1,146 @@ +{ + "schema": "custodian.nk48-r5-f3-candidate.v1", + "status": "corrected-source-candidate-synthetic-tested-owner-backend-absent", + "finding": "NK48-R5-F3", + "canonical_edits": false, + "native_actions": false, + "owner_assent": false, + "source_pins": [ + { + "repo": "net-kingdom", + "head": "3236d5000e52e0643f33906a26a7825aa0b67465", + "path": "AGENTS.md", + "sha256": "95158b1d5c15e6cf8be980f5c0e3b48aeef252654846273887f40516b3fa6a78", + "working_bytes_equal_commit": true + }, + { + "repo": "net-kingdom", + "head": "3236d5000e52e0643f33906a26a7825aa0b67465", + "path": "INTENT.md", + "sha256": "fd2ba8dff9dfd5583ec6d8b0348f273bfe00c59923b5d5d47cd22b1fb4658c54", + "working_bytes_equal_commit": true + }, + { + "repo": "net-kingdom", + "head": "3236d5000e52e0643f33906a26a7825aa0b67465", + "path": "SECURITY-COMPANION.md", + "sha256": "06374445ca888549f1bf79527d1345d94918d7a7ec363160d8427d0e7a31d643", + "working_bytes_equal_commit": true + }, + { + "repo": "net-kingdom", + "head": "3236d5000e52e0643f33906a26a7825aa0b67465", + "path": "sso-mfa/k8s/keycape/handover-adapter.py", + "sha256": "bad7c1132258f2140b0d83548031ac86eecea4f4019f34adedc1e9e3774a3832", + "working_bytes_equal_commit": true + }, + { + "repo": "net-kingdom", + "head": "3236d5000e52e0643f33906a26a7825aa0b67465", + "path": "sso-mfa/k8s/keycape/deliver-consumer-secret-adapter.py", + "sha256": "65db5386cdf08a1aa932886c046abed20330a36c96e63d4267adb8341b23da42", + "working_bytes_equal_commit": true + }, + { + "repo": "net-kingdom", + "head": "3236d5000e52e0643f33906a26a7825aa0b67465", + "path": "security-case-type-revisions/1.3/oidc-browser-relying-party/type.yaml", + "sha256": "e75fe94110ef9fb7fb18072fe2856941b33507c99cb406d36576046481485d0a", + "working_bytes_equal_commit": true + }, + { + "repo": "net-kingdom", + "head": "3236d5000e52e0643f33906a26a7825aa0b67465", + "path": "security-case-type-revisions/1.3/oidc-browser-relying-party/examples/example-public-portal.yaml", + "sha256": "2b30b9403ba124514e3ab8777b187a225099e2f7683fdd41d0ad48ef3d9649a7", + "working_bytes_equal_commit": true + }, + { + "repo": "net-kingdom", + "head": "3236d5000e52e0643f33906a26a7825aa0b67465", + "path": "security-case-type-revisions/1.3/oidc-browser-relying-party/examples/forgejo.yaml", + "sha256": "546aad5cf26842c484fc6ba99940d56697b85531afea11872556761008923c94", + "working_bytes_equal_commit": true + }, + { + "repo": "railiance-apps", + "head": "944b30ad63ab054cc24617e374e177d34cd572da", + "path": "AGENTS.md", + "sha256": "2a366a5fbb69ba235df97ee117153cbfa1af11ab579372751b4fb5dbb30c31a7", + "working_bytes_equal_commit": true + }, + { + "repo": "railiance-apps", + "head": "944b30ad63ab054cc24617e374e177d34cd572da", + "path": "INTENT.md", + "sha256": "31060dc1738aa4540f78735019359a276ece39dad8afbf1be740713bebbe65e5", + "working_bytes_equal_commit": true + }, + { + "repo": "railiance-apps", + "head": "944b30ad63ab054cc24617e374e177d34cd572da", + "path": "Makefile", + "sha256": "a319e264e6f04378f6b3d2c7781860fc4bac1abdb248cffa8c26065e08f2f290", + "working_bytes_equal_commit": true + }, + { + "repo": "railiance-apps", + "head": "944b30ad63ab054cc24617e374e177d34cd572da", + "path": "workplans/RAPPS-WP-0016-forgejo-netkingdom-login.md", + "sha256": "1077e9de358433bf1316e3db1459dd271f82875b9590bf3d7b5b22c0df42bbda", + "working_bytes_equal_commit": true + }, + { + "repo": "railiance-apps", + "head": "944b30ad63ab054cc24617e374e177d34cd572da", + "path": "docs/evidence/nk48-t16-owner-review/owner-review-railiance-apps.json", + "sha256": "67e9ded8d37462ba04a0a224bac03a840d0c94326235474e1d3371c8ff61865d", + "working_bytes_equal_commit": true + }, + { + "repo": "railiance-apps", + "head": "944b30ad63ab054cc24617e374e177d34cd572da", + "path": "docs/proposals/2026-10-11-forgejo-consumer-eso/owner-design.md", + "sha256": "c523949454e4c6eb58e8caee382380a561f561704136de62bf5783c592f962f2", + "working_bytes_equal_commit": true + }, + { + "repo": "the-custodian", + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-adapters-1.2-review/coordinator-review-r5.json", + "sha256": "e3016352c58178894ccbfc600b960d3f9e2b5d534a05b8df2c5b79737cb0127a", + "historical_receipt_untouched": true + }, + { + "repo": "the-custodian", + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-1.3-owner-review/owner-review-railiance-apps.json", + "sha256": "aedca85f3a742f6510c45aa17ade7375b93c46069cf4e5a7119bec9765efbd7d", + "historical_receipt_untouched": true + } + ], + "kernel": "117fec9241e7b985dfb187b992e173d0a5dc3700", + "candidate_type_before_sha256": "e75fe94110ef9fb7fb18072fe2856941b33507c99cb406d36576046481485d0a", + "candidate_type_after_sha256": "6e744be190c4f047e609bef7670ff987fe1606b4d7dac3f740f164bebc205a13", + "candidate_wrapper_sha256": "2cd04432cf3681afc126736f7daacfab46630441aeb223b3e222d6951d24ce2e", + "tests": { + "passed": 19, + "subtests_passed": 33, + "negative_controls_detected": [ + "mandatory fresh inverse-session guard omitted" + ] + }, + "blocking_owner_work": "T05 real owner backend absent: exact authenticated current inverse operation authority/session lifecycle, durable journal and recovery, all-writer release exclusion and accepted configuration projection/source binding must be selected and implemented by Apps, reusing current attended owner deployment path; no source-only operational completion.", + "proposed_type_structural_scope": [ + "follow_through[hand-over].entry_point", + "follow_through[hand-over].auth", + "rollback[hand-over].action", + "rollback[deliver-consumer-secret].action" + ], + "knowledge_orientation": { + "package": "0.2.6", + "digest": "8d1d6a9d4d07fec7b619618de7dfe2254c1790f85f4198c7fe19db9751c02", + "start_and_identity": "withheld-source-changed; owner AGENTS/INTENT and direct seam/owner sources reviewed" + }, + "predecessor": "nk48-r5-f3-implementation-r1", + "role": "implementation continuation after independent r1 review; no claim of independence for this correction", + "independent_review": "nk48-r5-f3-review-r1" +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/manifest.json b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/manifest.json new file mode 100644 index 0000000..ce7653a --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/manifest.json @@ -0,0 +1,69 @@ +{ + "schema": "custodian.source-packet-manifest.v1", + "files": [ + { + "path": "README.md", + "sha256": "168b80f6cc0d50650a484d24696022cac8a4cf8279c2fb4da0c112cfbf10fd08" + }, + { + "path": "candidate-validation.json", + "sha256": "200132ab91899f6cfb2782b19ff59c72056f05197690cecdda708ccbdd343edc" + }, + { + "path": "focused-tests.json", + "sha256": "2f48369c3d00f9a43989486882519ee5c09d4eb1f5be599a92b06ca0d7cc8581" + }, + { + "path": "focused-tests.txt", + "sha256": "436459ced69db381f44a0f14d2ebcf00a4067f596a9881cfdcca7146c237d3a1" + }, + { + "path": "fresh-inverse-session.candidate.patch", + "sha256": "0b4a466c23cdeeb34789ab79c3d7cdc4a186495a910e23bc6491a29b5ff9cff8" + }, + { + "path": "fresh-session-guard-omitted.txt", + "sha256": "f47357c3303b026dcd9edcce564725a1c7d0d77969dd2857a6f1c008f169c1e1" + }, + { + "path": "implementation.json", + "sha256": "8517713a130f23cb38c597d333f65ec27700f347c16dca617e988f1cbc948abf" + }, + { + "path": "negative-controls.json", + "sha256": "8f96396f3d342f505d1dc6ac235a7f37a7c2481d51d100125f05b26619d3d314" + }, + { + "path": "owner-backend-next.md", + "sha256": "6243d7a70868224cb89fd30818c23ac379f37b6576e4947e444ef5057d06aea8" + }, + { + "path": "owner-contract-candidate.md", + "sha256": "4f9b91a2389acbc52558347a794da02faa6de4fb189373a11feeac10ae7e7c4f" + }, + { + "path": "owner-source-inventory.json", + "sha256": "4510ce7b2f9eb9f4695ef4c57bba888c3839d3adcc397f4b4024be0fa71767bb" + }, + { + "path": "owner_configuration_contract.py", + "sha256": "2cd04432cf3681afc126736f7daacfab46630441aeb223b3e222d6951d24ce2e" + }, + { + "path": "test_owner_configuration_contract.py", + "sha256": "0f4246fea95bd3139c604da8712e6f6ac08976dd34d73e56ba83663ecba42761" + }, + { + "path": "type-owner-configuration-inverse.candidate.patch", + "sha256": "949ef6d3e0acc574e1d69c251d6d09e3da636bc73d043dec1c5677f30de29947" + }, + { + "path": "type.before.yaml", + "sha256": "e75fe94110ef9fb7fb18072fe2856941b33507c99cb406d36576046481485d0a" + }, + { + "path": "type.candidate.yaml", + "sha256": "6e744be190c4f047e609bef7670ff987fe1606b4d7dac3f740f164bebc205a13" + } + ] +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/negative-controls.json b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/negative-controls.json new file mode 100644 index 0000000..6f430bd --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/negative-controls.json @@ -0,0 +1,18 @@ +{ + "command": [ + "/home/worsch/ops-warden/.venv/bin/python", + "-m", + "pytest", + "-q", + "-p", + "no:cacheprovider", + "/tmp/nk48-apps-f3-r2-negative/test_owner_configuration_contract.py", + "-k", + "fresh_inverse_session or revoked_handover_session or missing_fresh_authority or inverse_scope_mismatches" + ], + "exit_code": 1, + "mutation": "omit mandatory inverse_session context only", + "expected": "fresh session lifecycle and scope/refusal regressions detect mutation", + "detected": true, + "native_actions": false +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/owner-backend-next.md b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/owner-backend-next.md new file mode 100644 index 0000000..4598b02 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/owner-backend-next.md @@ -0,0 +1,28 @@ +# Actual Apps source and minimum T05 continuation + +Apps HEAD remains `944b30ad63ab054cc24617e374e177d34cd572da`. Reviewed source is recorded by path/hash in `owner-source-inventory.json`; only source text was read. No referenced script was executed, no environment credential or home configuration was inspected and no native/network call was made. + +## Existing surfaces + +`Makefile:476` owns the attended Forgejo release path. It runs mailer/template prerequisite deployment targets, checks a Secret by name, then invokes normal Helm upgrade with chart/version, ordinary values, registry values and the existing value-bearing helper. It does not bind an accepted case, classify case-only configuration ownership, persist intent/post/inverse records, enforce all-release-writer exclusion or authenticate a separate inverse session. Running this entire target inside the new candidate would not prove that all its effects belong to the case. Its prerequisite applies also fall outside an OAuth-only inverse. + +`tools/forgejo-verify.sh` is an older workload/TLS/database/mail/SSH/operator smoke script for another workplan's T05. It uses ambient operator contexts and optional credential sources and is not the four NK-WP-0048 login/group observation backend. It was read as source only and was not run. `tools/forgejo-smoke.sh` checks web/registry reachability, not member/non-member/group transitions. Neither is a hand-over receipt store or configuration inverse. + +The actual owner review names the intended hand-over store as a committed `docs/evidence/-forgejo-netkingdom-handover.json` identified by path@commit, with removal as a revert confirmed on main. This is a useful existing owner design to reuse where appropriate, not executable journal/authentication code. T05 still says implement the real owner_apply/owner_remove, verifier and receipt store; it does not provide their implementation. The current repo's executable source contains no implementation of the queried owner methods, transaction/inverse session or lanes. There is no supported existing backend to bind in this packet. + +## Exact missing owner choices + +1. Adopt/publish the corrected external wrapper plus independent inverse placement, replacing the earlier C3 placement inside owner_remove. Name the accepted configuration and helper source pins and the precise non-secret projection that proves this case's OAuth/group-reference change while preserving local sign-in and unrelated release configuration. The actual any-of access_groups behavior remains the owner T02/T03 verification duty; ordinary Helm revision advancement is not proof of case-only ownership. +2. Name the existing owner-authenticated durable evidence mechanism and its write/readback semantics for pre-mutation intent, confirmed post state, inverse attempt, outcome and recovery. The existing committed-evidence design can be considered; this packet does not silently promote it into an implemented transactional configuration journal. Pending or publication ambiguity must survive process loss, and operation history must distinguish unrelated changes without storing Helm values, rendered Secrets or credentials. Loss after rollback but before inverse confirmation requires authenticated owner reconciliation, never unguarded replay. +3. Identify the actual all-release-writer exclusion mechanism, or enforce the existing attended owner release-freeze/reconciliation path across current-revision/configuration check and rollback. It must cover every manual, automation and reconciliation writer. A local wrapper lock or pre-command revision equality is insufficient because Helm supplies no CAS. Source presently establishes no such enforcement. +4. Bind `inverse_session` to the real attended owner runtime's current operation authorization and revocation evidence, using the exact original principal/owner/case/acceptance/source/cluster/namespace/release/client/descriptor/transaction scope and distinct inverse operation. Name how that exact scope/disposition is authenticated, retained and revoked on success/refusal/failure. The same operator can perform the fresh inverse; no standing agent credential is proposed. Existing valid scoped session authorization need not be requested again, but the revoked hand-over parent is unusable and a stored configuration receipt is not execution permission. + +These are missing implementations/owner bindings inside existing T05, not new workflow/permission gates or a new service, store or authority created by this packet. + +## Smallest actual source implementation next + +Keep the current Apps attended deployment and owner-reviewed evidence location. Add one owner source module in its existing tools surface and a bounded Make entry which receives the exact accepted case/source/operation binding, invokes the selected durable/session/exclusion callbacks, performs only the accepted release change through the existing Helm deployment path, and invokes the unchanged admitted hand-over seam. Publish a separate inverse entry using that same journal and exact operation scope. An implementation must factor or explicitly exclude current unrelated deployment prerequisites; it cannot claim the whole target is an OAuth-only transaction. + +Use the candidate protocol as the control-flow seam. Bind its callbacks only after the four choices above identify real owner facilities; do not ship an always-true authority oracle, local dictionary journal or advisory mutex as production enforcement. Then meaningful fake-command tests should cover argument/source pinning, metadata-only release readback, absent/matched/conflicting state, release-writer interference, pre/post/inverse persistence failures and revoked/mismatched session refusal before any command. Without those real facility choices, a command shim would falsely suggest operational completion, so none was implemented here. + +Existing owner_apply/owner_remove must use completed configuration-inverse evidence and prove no remaining reference before deleting only authenticated case-created objects. The real four-probe observer and committed hand-over receipt/revert backend still belong to T05. T01 break-glass and attended T02/T03 acceptance remain the existing first-case work, not claims made by this source packet. diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/owner-contract-candidate.md b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/owner-contract-candidate.md new file mode 100644 index 0000000..be28308 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/owner-contract-candidate.md @@ -0,0 +1,31 @@ +# Consumer configuration transaction candidate for Apps T05 + +This is a coordinator-authored proposal for independent Apps review under RAPPS-WP-0016-T05, not Apps assent or an implemented owner capability. T02 already owns the Helm change and rollback to its noted previous revision. Keep that deployment mechanism and the seven kernel descriptors. Do not move Helm side effects into hand-over's verification, receipt callbacks, or Adapter.rollback. Do not add an eighth kernel step, a new service, a standing credential, or a new catalog grant. + +The existing seven kernel adapters alone do not express this inverse: hand-over can authenticate only a newly created receipt, and confidential delivery can authenticate only its case-created objects. Public delivery is skipped; matched delivery returns NO_CHANGE. No such creation can grant authority over a Helm configuration. The reproducible direct-seam counterexample in the fixtures removes the public client's receipt and leaves the consumer configuration present. Merely adding an unused rollback helper would not fix it. + +## Proposed reachable owner entry and inverse + +The existing external owner deployment before hand-over becomes **one reviewed owner-run entry path**: begin configuration transaction, perform/confirm the T02 Helm change, then invoke the admitted hand-over step in the same attended owner session. `owner_configuration_contract.run` is an executable control-flow candidate for that path, exercised by the fixtures through the unchanged real hand-over adapter. It is not installed or callable by production. The owner must actually implement and publish its backend and bind its entry path under T05 before this text can establish readiness. + +Before any mutation, bind an owner-authenticated durable intent to the exact case bytes/digest, case acceptance, owner, human principal, cluster identity, namespace/release, client, configuration source and reviewed wrapper/helper source digests, hand-over descriptor, and distinct owner operation/attempt ID. The wrapper also compares the case/acceptance/principal/owner/client/descriptor against the exact admitted hand-over Invocation. Bind the selected configuration to the case (issuer/client/groups and the confidential reference where applicable), not just to an arbitrary revision. Never store Helm values, rendered Secrets, login cookies or credential material in this record. Full values/chart material may be sensitive: retain only the owner-reviewed non-secret projection/digests and provenance references needed for the inverse. The real owner journal must authenticate the entire record; the fixture's dictionary and literal source pin provide no durable/native proof. + +Under the owner's exclusive release-writer boundary, observe the before revision and its configuration identity, persist intent, perform the source-reviewed deployment, and persist authenticated post revision/configuration identity and a changed/no-change classification **before hand-over verification or receipt publication**. Confirm the operation provenance and that the allowed change was exactly this case's new OIDC configuration; do not infer ownership merely because a revision incremented. A deployment affecting unrelated chart/configuration changes is outside this inverse. Match an identical preexisting configuration as NO_CHANGE and give it no rollback authority. Refuse a conflicting preexisting source rather than overwrite it. Preserve local sign-in, unrelated configuration, data and preexisting resources. + +`owner_configuration_contract.inverse` is an owner-run unwind entry on the same durable transaction, independent of secret-delivery outcomes and hand-over receipt creation. It is reached (a) by the wrapper on verification, receipt-recording or publication failure after confirmed configuration and (b) by the owner case-unwind path after a successful hand-over, before removing any delivery object. It applies to public and confidential cases. A failed hand-over is not an excuse to wait for a successful kernel receipt before invoking the owner inverse. A configuration NO_CHANGE is preserved; no Helm action is authorized. An authenticated, already completed inverse is idempotent evidence, not permission to run Helm again. + +Every configuration inverse requires a distinct fresh authorized attended owner operation. `owner.inverse_session(binding, transaction)` must authenticate operation `configuration-inverse`, the exact original principal, owner, case/acceptance, configuration/helper source, cluster/namespace/release, client, descriptor and transaction scope against current disposition before entering the release-writer boundary. Its context must durably retain the inverse attempt authority/outcome and revoke its own session on every exit, including refused readback, rollback and persistence failures. The hand-over adapter revokes its own parent session on both success and failure before this inverse can run: never reuse that revoked session. Historical transaction provenance does not supply fresh execution authority. A same-principal fresh owner operation can use already established explicit scoped session authorization where valid; no new standing grant or additional workflow is proposed. If no current authorized attended inverse operation can be established, retain the confirmed/pending transaction and original hand-over error for owner reconciliation rather than mutate. The fixture's synthetic context does not authenticate any real principal or grant native authority. + +For a changed configuration, authenticate the transaction and exact scope, require the current release still equals the recorded case-created post revision/configuration, and hold the owner's release-writer exclusion continuously across that check and the normal Helm rollback to the noted before revision. Confirm a new post-rollback revision restoring the exact approved before configuration; retain inverse evidence and transaction history. Current revision or configuration drift stops the inverse without touching other work. Helm's rollback command is not a revision CAS: the owner must identify a real exclusion mechanism covering *all* writers to this release, including manual and reconciliation paths, or retain an attended per-release freeze/owner-reconciliation gate. A local mutex that other writers ignore is insufficient. No such real exclusion is supplied by this packet or the existing forgejo-deploy Make target. + +An intent/configuration/post-record persistence gap yields an ambiguous configuration outcome: keep the durable pending intent and stop for owner reconciliation using the exact operation/history/source binding. Do not replay, assert NO_CHANGE, or infer mutation ownership from an untrusted local record. Process loss does not confer an automatic inverse; T05 must supply authenticated durable readback/reconciliation before resuming. The fixture demonstrates this refusal; it does not implement the real journal/recovery backend. Failed inverse remains pending for owner reconciliation and retains the original hand-over failure as context. + +The adapter's rollback remains exactly receipt-only and created-only. The independent configuration inverse must not fabricate or extend its `allowed_created`. If publication failed after receipt creation, configuration can be restored by its separately authenticated transaction while that receipt remains uncertain for Apps reconciliation; never report whole-case rollback complete from the configuration inverse alone. + +For delivery cleanup, `owner_remove` must first verify the authenticated configuration inverse/no remaining consumer reference; it may reuse the completed inverse evidence, not run a second unguarded Helm rollback. It then removes only its authenticated case-created UIDs, in the existing reverse order, and reports `consumer_reference_reverted=true` only when actually proven. Preserve all preexisting-matched objects. If configuration itself was preexisting and still references a delivery object, NO_CHANGE is not proof of deactivation: refuse removing the referenced object until the owner resolves that dependency. Role/store deletion alone remains no token-revocation proof. + +## Exact owner choice still required + +Apps must adopt this external wrapper/transaction entry and inverse in T05, name the real authenticated journal, accepted configuration/source binding, release identity and writer-exclusion mechanism, and re-review the type's revised C3 wording. Its previous C3 expressly placed the Helm inverse inside owner_remove; this proposal changes that placement and cannot be treated as prior assent. The existing Makefile invokes normal Helm upgrade with no transaction journal or all-writer revision guard. T02's operational rollback intention is an existing legitimate mechanism, not an implemented proof boundary. + +If Apps does not adopt an owner-run external transaction around the existing pre-hand-over deployment, there is no reachable complete inverse in the current seven-step contract. Then the owners must choose a structural execution/receipt contract change before accepting this type; do not silently expand either Adapter's object scope. The seven descriptors and their pins remain untouched in this proposal. Owner backend implementation, independent review, exact source publication, kernel/catalog acceptance and separately authorized native proof all remain outstanding. diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/owner-source-inventory.json b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/owner-source-inventory.json new file mode 100644 index 0000000..cdcb8eb --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/owner-source-inventory.json @@ -0,0 +1,62 @@ +{ + "apps_head": "944b30ad63ab054cc24617e374e177d34cd572da", + "inspection": "source-only; no native/backend/credential call", + "files": [ + { + "path": "AGENTS.md", + "sha256": "2a366a5fbb69ba235df97ee117153cbfa1af11ab579372751b4fb5dbb30c31a7" + }, + { + "path": "INTENT.md", + "sha256": "31060dc1738aa4540f78735019359a276ece39dad8afbf1be740713bebbe65e5" + }, + { + "path": "Makefile", + "sha256": "a319e264e6f04378f6b3d2c7781860fc4bac1abdb248cffa8c26065e08f2f290" + }, + { + "path": "tools/forgejo-verify.sh", + "sha256": "799571da79db58be2e6eee533ec20765aa9961bb74ea3865d69f48273a86f242" + }, + { + "path": "tools/forgejo-smoke.sh", + "sha256": "f6146412cbe29f4abba9d81b3bc7a0652cf281b30a56fd7054e72f1574553869" + }, + { + "path": "workplans/RAPPS-WP-0016-forgejo-netkingdom-login.md", + "sha256": "1077e9de358433bf1316e3db1459dd271f82875b9590bf3d7b5b22c0df42bbda" + }, + { + "path": "docs/proposals/2026-10-11-forgejo-consumer-eso/owner-design.md", + "sha256": "c523949454e4c6eb58e8caee382380a561f561704136de62bf5783c592f962f2" + }, + { + "path": "docs/evidence/nk48-t16-owner-review/owner-review-railiance-apps.json", + "sha256": "67e9ded8d37462ba04a0a224bac03a840d0c94326235474e1d3371c8ff61865d" + } + ], + "executable_surface_search": { + "command": [ + "rg", + "-n", + "configuration.transaction|inverse_session|owner_configuration|consumer-secret-delivery\\.v1|def owner_apply|def owner_remove|handover-receipt|apps-consumer-handover|consumer-owner-deployment", + "--glob", + "!docs/**", + "--glob", + "!workplans/**", + "--glob", + "!wiki/**", + "--glob", + "!*.lock", + "." + ], + "cwd": "/home/worsch/railiance-apps", + "exit_code": 1, + "stdout": "", + "stderr": "" + }, + "supported_config_inverse_backend_found": false, + "real_binding_implemented": false, + "missing_choices": "owner-backend-next.md", + "existing_task": "RAPPS-WP-0016-T05" +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/owner_configuration_contract.py b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/owner_configuration_contract.py new file mode 100644 index 0000000..1947261 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/owner_configuration_contract.py @@ -0,0 +1,72 @@ +"""Unadopted Apps T05 control-flow candidate; no CLI, Helm or native capability. + +The owner backend is mandatory, including durable authenticated records and an +exclusive release-writer boundary. Tests supply a deliberately synthetic backend. +This wraps the existing owner deployment + hand-over entry path, not an eighth +kernel step or a replacement Adapter. No execution authority is provided here. +""" +from warden.case_run import Invocation +from warden.case_receipt_state import Refused + + +def inverse(binding, transaction, owner): + """Owner unwind entry, reachable without any delivery/hand-over creation.""" + # This is a distinct operation: hand-over has already revoked its session. + # Backend must authenticate a fresh attended inverse attempt, exact operation, + # principal/owner/case/source/release/transaction scope and current disposition; + # durably retain authority + outcome and revoke this attempt on every exit. + # Durable configuration provenance alone is never execution authority. + with owner.inverse_session(binding, transaction), owner.exclusive(binding): + record = owner.authenticated_record(transaction) + if record['binding'] != binding: + raise Refused('configuration transaction binding differs') + if record['state'] == 'restored': + return 'already-restored' + if record['state'] != 'confirmed': + raise Refused('configuration outcome requires owner reconciliation') + if not record['changed']: + return 'preexisting-configuration-preserved' + # The owner must serialize ALL release writers across this read + inverse. + # Checking revision before an unguarded Helm command would leave a race. + if owner.observe(binding) != record['after']: + raise Refused('current release differs from case-created post revision') + restored = owner.rollback_to(binding, record['before']['revision']) + if (restored['configuration'] != record['before']['configuration'] + or restored['revision'] <= record['after']['revision']): + raise Refused('owner Helm inverse readback differs') + owner.confirm_inverse(transaction, restored) + return 'case-configuration-restored' + + +def run(binding, owner, admitted, handover): + """One owner entry: record/configure/confirm, then invoke real hand-over. + + The owner must persist begin BEFORE configure and confirm BEFORE hand-over. + Configuration/publication ambiguity stays pending for owner reconciliation. + A failure after confirmed configuration reaches the same owner inverse even + when verification fails before a receipt exists. A successful call returns a + durable transaction identifier for a subsequent owner-run inverse request. + """ + if (type(admitted) is not Invocation or admitted.operation != 'admission' + or admitted.attempt_id or admitted.step != 'hand-over' + or admitted.actor != 'human' or admitted.lane != 'apps-consumer-handover'): + raise Refused('owner wrapper requires exact hand-over admission') + expected = {'case': admitted.pins.case, 'acceptance': admitted.pins.acceptance, + 'principal': admitted.principal, 'owner': admitted.owner, + 'client': admitted.target, 'handover_descriptor': admitted.pins.adapter} + if any(binding.get(key) != value for key, value in expected.items()): + raise Refused('owner wrapper differs from hand-over admission') + with owner.exclusive(binding): + before = owner.observe(binding) + transaction = owner.begin(binding, before) + # begin/configure/confirm exceptions deliberately do not infer rollback + # authority: a mutation may have occurred before its proof was committed. + after, changed = owner.configure(binding, before) + owner.confirm(transaction, after, changed) + try: + result = handover(admitted) + except BaseException: + # Failure to unwind propagates with the original error as its context. + inverse(binding, transaction, owner) + raise + return transaction, result diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/test_owner_configuration_contract.py b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/test_owner_configuration_contract.py new file mode 100644 index 0000000..123e654 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/test_owner_configuration_contract.py @@ -0,0 +1,421 @@ +"""Synthetic owner flow through the unchanged frozen hand-over/delivery seams. + +No owner approval, trusted journal, real Helm, kernel admission or native proof. +""" +from contextlib import contextmanager +from copy import deepcopy +from dataclasses import asdict, replace +import importlib.util +import json +import os +from pathlib import Path +import unittest + +import yaml +from warden.case_run import Invocation, digest +from warden.case_receipt_state import Outcome, Pins, Refused +from owner_configuration_contract import run, inverse + +ROOT = Path(os.environ['NK_FROZEN_ROOT']) +ADAPTERS = ROOT / 'sso-mfa/k8s/keycape' +def load(name, filename): + spec = importlib.util.spec_from_file_location(name, ADAPTERS / filename) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module +h = load('f3_frozen_handover', 'handover-adapter.py') +d = load('f3_frozen_delivery', 'deliver-consumer-secret-adapter.py') + + +class SyntheticOwner: + """In-memory stand-in for the *required*, currently absent Apps T05 backend.""" + def __init__(self, binding, desired): + self.binding, self.desired = deepcopy(binding), desired + self.current = {'revision': 7, 'configuration': {'local_signin': True, 'oidc': None}} + self.history = {7: deepcopy(self.current['configuration'])} + self.records, self.calls = {}, [] + self.exclusive_available = True + self.locked = False + self.fail_confirm = self.fail_inverse = False + # Explicitly synthetic authority oracle. No real principal or grant. + self.inverse_authority_available = True + self.inverse_authority_override = None + self.inverse_session_id_override = None + self.inverse_session_events = [] + self.revoked_sessions = set() + self.inverse_session_open = False + self.inverse_attempt_count = 0 + + @contextmanager + def inverse_session(self, binding, transaction): + requested = {'operation': 'configuration-inverse', + 'binding': deepcopy(binding), 'transaction': transaction} + authorized = (deepcopy(self.inverse_authority_override) + if self.inverse_authority_override is not None else requested) + if (not self.inverse_authority_available or authorized != requested + or binding != self.binding or transaction not in self.records): + raise Refused('fresh attended inverse scope/authority absent') + self.inverse_attempt_count += 1 + session = (self.inverse_session_id_override + or 'fixture-inverse:' + str(self.inverse_attempt_count)) + if session in self.revoked_sessions: + raise Refused('inverse cannot reuse a revoked session') + assert not self.inverse_session_open + self.inverse_session_open = True + self.inverse_session_events.append(('open', session, deepcopy(requested))) + try: + yield session + finally: + self.inverse_session_open = False + self.revoked_sessions.add(session) + self.inverse_session_events.append(('revoke', session)) + + @contextmanager + def exclusive(self, binding): + if not self.exclusive_available or binding != self.binding: + raise Refused('owner release-writer boundary or source/principal binding absent') + assert not self.locked + self.locked = True + try: + yield + finally: + self.locked = False + + def observe(self, binding): + assert self.locked and binding == self.binding + return deepcopy(self.current) + + def begin(self, binding, before): + assert self.locked + ident = 'fixture-transaction:' + str(len(self.records)) + self.records[ident] = {'binding': deepcopy(binding), 'before': deepcopy(before), 'state': 'pending'} + self.calls.append('durable-intent') + return ident + + def configure(self, binding, before): + assert self.locked and binding == self.binding and before == self.current + if self.current['configuration']['oidc'] == self.desired: + self.calls.append('matched-configuration') + return deepcopy(self.current), False + if self.current['configuration']['oidc'] is not None: + raise Refused('preexisting configuration conflicts') + self.current['revision'] += 1 + self.current['configuration']['oidc'] = self.desired + self.history[self.current['revision']] = deepcopy(self.current['configuration']) + self.calls.append('helm-configure') + return deepcopy(self.current), True + + def confirm(self, ident, after, changed): + assert self.locked + if self.fail_confirm: + raise Refused('configuration receipt persistence failed') + self.records[ident].update(after=deepcopy(after), changed=changed, state='confirmed') + self.calls.append('durable-post-revision') + + def authenticated_record(self, ident): + assert self.locked + if ident not in self.records: + raise Refused('owner transaction not authenticated') + return deepcopy(self.records[ident]) + + def rollback_to(self, binding, revision): + assert self.locked and binding == self.binding + self.calls.append(('helm-rollback', revision)) + if self.fail_inverse: + raise Refused('owner rollback failed') + self.current = {'revision': self.current['revision'] + 1, + 'configuration': deepcopy(self.history[revision])} + return deepcopy(self.current) + + def confirm_inverse(self, ident, restored): + assert self.locked + self.records[ident].update(state='restored', inverse=deepcopy(restored)) + self.calls.append('durable-inverse') + + +class OwnerFlowTests(unittest.TestCase): + def setup_flow(self, confidential=False): + name = 'forgejo.yaml' if confidential else 'example-public-portal.yaml' + source = (ROOT / 'security-case-type-revisions/1.3/oidc-browser-relying-party/examples' / name).read_bytes() + self.case = source + params = yaml.safe_load(source)['spec']['parameters'] + self.params = params + self.owner_repo, self.client = params['consumer_owner_repo'], params['client_id'] + desc = h.descriptor(self.owner_repo) + encoded = json.dumps(asdict(desc), sort_keys=True, separators=(',', ':')).encode() + pins = Pins(digest(source), digest(b'fixture-type'), digest(encoded), digest(b'fixture-catalog'), + digest(b'fixture-acceptance'), digest(b'fixture-run-sheet')) + self.admitted = Invocation('hand-over', self.owner_repo, self.client, 'human', 'fixture-founder', + 'apps-consumer-handover', pins) + self.inv = replace(self.admitted, attempt_id='fixture-run', operation='run') + self.rb = replace(self.admitted, attempt_id='fixture-rollback', operation='rollback') + self.binding = {'case': pins.case, 'acceptance': pins.acceptance, 'principal': self.admitted.principal, + 'owner': self.owner_repo, 'source': digest(b'fixture-approved-owner-config-source'), + 'cluster': 'fixture-cluster-uid', 'namespace': params['consumer_namespace'], + 'release': params['consuming_service'], 'client': self.client, + 'handover_descriptor': pins.adapter, 'attempt': 'fixture-owner-attempt'} + desired = {'issuer': params.get('issuer', 'https://fixture-issuer.invalid'), 'client': self.client, + 'groups': params['access_groups']} + self.owner = SyntheticOwner(self.binding, desired) + self.receipts, self.evidence, self.revoked = set(), {}, [] + self.fail_verify = self.fail_record = self.fail_publish = False + def verify(client, groups): + self.owner.calls.append('handover-verification') + return {'client_id': client, 'groups': list(groups), 'enforces_groups': True, + 'login_verified': not self.fail_verify, + 'observations': [{'probe': p, 'evidence': 'fixture-probe:' + p} for p in sorted(h.PROBES)]} + def record(client, observed): + self.owner.calls.append('handover-receipt') + if self.fail_record: + raise Refused('fixture receipt failure') + ref = 'fixture-handover:' + client + created = ref not in self.receipts + self.receipts.add(ref) + return ref, created + def publish(payload): + if self.fail_publish: + raise Refused('fixture publication failed') + ident = 'fixture-evidence:' + str(len(self.evidence)) + self.evidence[ident] = payload + return ident + def remove(ref): + self.receipts.remove(ref) + return True + def revoke(inv): + self.revoked.append(inv) + self.owner.revoked_sessions.add(inv.attempt_id) + return 'fixture-revoked:' + inv.attempt_id + self.adapter = h.build(admitted=self.admitted, case_source=source, case_acceptance='fixture-acceptance', + verify_consumer=verify, record_handover=record, remove_handover=remove, publish=publish, + authenticate=lambda ref, payload: self.evidence.get(ref) == payload, + revoke_session=revoke, version_root='1.3') + + def flow(self): + # Reachable candidate owner entry: configuration encloses the REAL seam. + return run(self.binding, self.owner, self.admitted, + lambda admitted: self.adapter.invoke(replace(admitted, attempt_id=self.inv.attempt_id, operation="run"))) + + def delivery_no_change(self): + encoded = json.dumps(asdict(d.descriptor(self.owner_repo)), sort_keys=True, separators=(',', ':')).encode() + admitted = replace(self.admitted, step='deliver-consumer-secret', lane='consumer-owner-deployment', + pins=replace(self.admitted.pins, adapter=digest(encoded))) + self.delivery_removals = [] + evidence = {} + def apply(inv, want): + return {'schema': 'netkingdom.consumer-secret-delivery.v1', 'operation': 'apply', + 'values_emitted': False, 'consumer_reference_reverted': False, + 'objects': {name: {'uid': 'preexisting-' + name, 'created': False, 'metadata': metadata} + for name, metadata in want.items()}} + def remove(inv, created): + self.delivery_removals.append(created) + raise AssertionError('matched delivery cannot authorize removal') + def publish(payload): + ident = 'fixture-delivery:' + str(len(evidence)); evidence[ident] = payload; return ident + adapter = d.build(admitted=admitted, case_source=self.case, owner_apply=apply, owner_remove=remove, + publish=publish, authenticate=lambda ref, payload: evidence.get(ref) == payload, + revoke_session=lambda inv: 'fixture-revoked:' + inv.attempt_id) + result = adapter.invoke(replace(admitted, attempt_id='fixture-delivery', operation='run')) + self.assertEqual((result.outcome, result.created), (Outcome.NO_CHANGE, ())) + return adapter, replace(admitted, attempt_id='fixture-delivery-inverse', operation='rollback') + + def test_existing_public_flow_counterexample_receipt_rollback_leaves_helm_change(self): + self.setup_flow() + self.assertEqual(self.params['client_type'], 'public') + # Reproduce existing text: owner's own configuration, then receipt seam. + self.owner.current['configuration']['oidc'] = self.owner.desired + result = self.adapter.invoke(self.inv) + self.adapter.rollback(self.rb, result.created) + self.assertEqual(self.receipts, set()) + self.assertEqual(self.owner.current['configuration']['oidc'], self.owner.desired) + + def test_candidate_public_flow_reaches_independent_inverse(self): + self.setup_flow() + transaction, result = self.flow() + self.assertTrue(self.adapter.verify(self.inv, result)) + self.assertLess(self.owner.calls.index('durable-post-revision'), self.owner.calls.index('handover-verification')) + self.adapter.rollback(self.rb, result.created) # unchanged receipt-only path + self.assertEqual(self.owner.current['configuration']['oidc'], self.owner.desired) + self.assertEqual(inverse(self.binding, transaction, self.owner), 'case-configuration-restored') + self.assertEqual(self.owner.current['configuration'], {'local_signin': True, 'oidc': None}) + self.assertEqual(inverse(self.binding, transaction, self.owner), 'already-restored') + + def test_confidential_no_change_delivery_cannot_block_configuration_inverse(self): + self.setup_flow(confidential=True) + adapter, rb = self.delivery_no_change() + transaction, result = self.flow() + inverse(self.binding, transaction, self.owner) + with self.assertRaises(Refused): # contained owner path sanitizes refusal text + adapter.rollback(rb, ('consumer-externalsecret',)) + self.adapter.rollback(self.rb, result.created) + self.assertEqual(self.delivery_removals, []) + self.assertIsNone(self.owner.current['configuration']['oidc']) + + def test_preexisting_configuration_and_receipt_preserved(self): + self.setup_flow() + self.owner.current['configuration']['oidc'] = self.owner.desired + before = deepcopy(self.owner.current) + self.receipts.add('fixture-handover:' + self.client) + transaction, result = self.flow() + self.assertEqual(result.outcome, Outcome.NO_CHANGE) + self.assertEqual(inverse(self.binding, transaction, self.owner), 'preexisting-configuration-preserved') + with self.assertRaises(Refused): + self.adapter.rollback(self.rb, (h.RECEIPT,)) + self.assertEqual(self.owner.current, before) + self.assertEqual(len(self.receipts), 1) + self.assertFalse(any(isinstance(c, tuple) for c in self.owner.calls)) + + def test_changed_revision_refuses_inverse_and_preserves_other_work(self): + self.setup_flow() + transaction, _ = self.flow() + self.owner.current['revision'] += 1 + self.owner.current['configuration']['other_work'] = True + before = deepcopy(self.owner.current) + with self.assertRaisesRegex(Refused, 'current release differs'): + inverse(self.binding, transaction, self.owner) + self.assertEqual(self.owner.current, before) + self.assertEqual(self.owner.records[transaction]['state'], 'confirmed') + + def test_failure_before_receipt_still_reaches_inverse(self): + for failure in ('fail_verify', 'fail_record'): + with self.subTest(failure=failure): + self.setup_flow(); setattr(self, failure, True) + with self.assertRaises(Refused): self.flow() + self.assertEqual(self.receipts, set()) + self.assertIsNone(self.owner.current['configuration']['oidc']) + self.assertEqual(next(iter(self.owner.records.values()))['state'], 'restored') + self.assertEqual(self.revoked, [self.inv]) + + def test_receipt_publication_failure_restores_config_without_claiming_receipt_removal(self): + self.setup_flow(); self.fail_publish = True + with self.assertRaises(Refused): self.flow() + self.assertIsNone(self.owner.current['configuration']['oidc']) + self.assertEqual(len(self.receipts), 1) # uncertain receipt must be reconciled + with self.assertRaises(Refused): self.adapter.rollback(self.rb, (h.RECEIPT,)) + + def test_configuration_confirmation_failure_requires_owner_reconciliation(self): + self.setup_flow(); self.owner.fail_confirm = True + with self.assertRaises(Refused): self.flow() + self.assertEqual(next(iter(self.owner.records.values()))['state'], 'pending') + self.assertEqual(self.owner.current['configuration']['oidc'], self.owner.desired) + self.assertNotIn('handover-verification', self.owner.calls) + with self.assertRaisesRegex(Refused, 'requires owner reconciliation'): + inverse(self.binding, 'fixture-transaction:0', self.owner) + + def test_exact_binding_mismatches_refused(self): + self.setup_flow(); transaction, _ = self.flow() + before = deepcopy(self.owner.current) + for field in self.binding: + with self.subTest(field=field), self.assertRaises(Refused): + inverse({**self.binding, field: 'different'}, transaction, self.owner) + self.assertEqual(self.owner.current, before) + + def test_wrapper_admission_binding_refused_before_owner_mutation(self): + self.setup_flow() + for field, value in [('principal', 'other'), ('target', 'other'), ('owner', 'other'), + ('actor', 'agent'), ('lane', 'other')]: + with self.subTest(field=field), self.assertRaises(Refused): + run(self.binding, self.owner, replace(self.admitted, **{field: value}), + lambda admitted: self.fail('must not reach hand-over')) + self.assertEqual(self.owner.records, {}) + + def test_missing_release_writer_guard_refuses_before_configuration(self): + self.setup_flow(); self.owner.exclusive_available = False + with self.assertRaises(Refused): self.flow() + self.assertEqual(self.owner.records, {}) + self.assertIsNone(self.owner.current['configuration']['oidc']) + + def test_preexisting_conflicting_configuration_not_overwritten(self): + self.setup_flow(); self.owner.current['configuration']['oidc'] = {'client': 'unrelated'} + before = deepcopy(self.owner.current) + with self.assertRaisesRegex(Refused, 'configuration conflicts'): self.flow() + self.assertEqual(self.owner.current, before) + self.assertNotIn('handover-verification', self.owner.calls) + + def test_inverse_failure_retains_original_failure_and_pending_unwind(self): + self.setup_flow(); self.fail_verify = True; self.owner.fail_inverse = True + with self.assertRaisesRegex(Refused, 'owner rollback failed') as caught: + self.flow() + self.assertIsInstance(caught.exception.__context__, Refused) + self.assertEqual(next(iter(self.owner.records.values()))['state'], 'confirmed') + self.assertEqual(self.owner.current['configuration']['oidc'], self.owner.desired) + + def test_fresh_inverse_session_exact_scope_open_and_revoked(self): + self.setup_flow() + transaction, _ = self.flow() + self.assertEqual(self.revoked, [self.inv]) + inverse(self.binding, transaction, self.owner) + opened, closed = self.owner.inverse_session_events + self.assertEqual(opened[0], 'open') + self.assertNotEqual(opened[1], self.inv.attempt_id) + self.assertEqual(opened[2], {'operation': 'configuration-inverse', + 'binding': self.binding, 'transaction': transaction}) + self.assertEqual(closed, ('revoke', opened[1])) + self.assertIn(opened[1], self.owner.revoked_sessions) + self.assertFalse(self.owner.inverse_session_open) + + def test_revoked_handover_session_refused_for_later_inverse(self): + self.setup_flow(); transaction, _ = self.flow() + self.owner.inverse_session_id_override = self.inv.attempt_id + before = deepcopy(self.owner.current) + with self.assertRaisesRegex(Refused, 'revoked session'): + inverse(self.binding, transaction, self.owner) + self.assertEqual(self.owner.current, before) + self.assertEqual(self.owner.inverse_session_events, []) + self.assertEqual(self.owner.records[transaction]['state'], 'confirmed') + + def test_missing_fresh_authority_failure_unwind_retains_original_error(self): + self.setup_flow(); self.fail_verify = True + self.owner.inverse_authority_available = False + with self.assertRaisesRegex(Refused, 'fresh attended') as caught: + self.flow() + self.assertEqual(self.revoked, [self.inv]) + self.assertIsInstance(caught.exception.__context__, Refused) + self.assertIn('consumer verification', str(caught.exception.__context__)) + self.assertEqual(self.owner.current['configuration']['oidc'], self.owner.desired) + self.assertEqual(next(iter(self.owner.records.values()))['state'], 'confirmed') + self.assertEqual(self.owner.inverse_session_events, []) + + def test_inverse_scope_mismatches_refused_without_mutation(self): + self.setup_flow(); transaction, _ = self.flow() + scope = {'operation': 'configuration-inverse', + 'binding': deepcopy(self.binding), 'transaction': transaction} + before = deepcopy(self.owner.current) + cases = [{'operation': 'hand-over'}, {'transaction': 'other-transaction'}] + cases += [{'binding': {**self.binding, field: 'other'}} for field in self.binding] + for changed in cases: + with self.subTest(changed=changed): + self.owner.inverse_authority_override = {**scope, **changed} + with self.assertRaisesRegex(Refused, 'scope/authority'): + inverse(self.binding, transaction, self.owner) + self.assertEqual(self.owner.current, before) + self.assertEqual(self.owner.inverse_session_events, []) + + def test_fresh_inverse_session_revoked_on_inverse_failure(self): + self.setup_flow(); transaction, _ = self.flow() + self.owner.fail_inverse = True + with self.assertRaisesRegex(Refused, 'owner rollback failed'): + inverse(self.binding, transaction, self.owner) + opened, closed = self.owner.inverse_session_events + self.assertEqual(closed, ('revoke', opened[1])) + self.assertFalse(self.owner.inverse_session_open) + self.assertEqual(self.owner.records[transaction]['state'], 'confirmed') + + def test_fresh_inverse_session_revoked_on_guard_and_persistence_failure(self): + for failure in ('drift', 'persistence'): + with self.subTest(failure=failure): + self.setup_flow(); transaction, _ = self.flow() + if failure == 'drift': + self.owner.current['revision'] += 1 + else: + def refuse(ident, restored): + raise Refused('inverse persistence failed') + self.owner.confirm_inverse = refuse + with self.assertRaises(Refused): + inverse(self.binding, transaction, self.owner) + opened, closed = self.owner.inverse_session_events + self.assertEqual(closed, ('revoke', opened[1])) + self.assertFalse(self.owner.inverse_session_open) + self.assertEqual(self.owner.records[transaction]['state'], 'confirmed') + +if __name__ == '__main__': + unittest.main() diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/type-owner-configuration-inverse.candidate.patch b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/type-owner-configuration-inverse.candidate.patch new file mode 100644 index 0000000..6b077d3 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/type-owner-configuration-inverse.candidate.patch @@ -0,0 +1,69 @@ +--- a/security-case-type-revisions/1.3/oidc-browser-relying-party/type.yaml ++++ b/security-case-type-revisions/1.3/oidc-browser-relying-party/type.yaml +@@ -355,16 +355,30 @@ + Secret values. + - id: hand-over + owner: '{consumer_owner_repo}' +- entry_point: The consumer owner first makes its own Helm change configuring {consuming_service} +- with issuer {issuer}, client {client_id} and custody bao:platform/workloads/keycape/{client_id}, +- requiring one of {access_groups} in the groups claim before sign-in completes. +- Then reviewed adapter net-kingdom/keycape-hand-over, dispatched by Warden's case +- kernel inside the attended consumer-owner session, verifies the consumer with +- four value-free probes (member-login, non-member-refused, membership-add-grants, +- membership-remove-revokes) and records the hand-over receipt. ++ entry_point: The consumer owner's reviewed deployment entry wraps its own Helm ++ configuration and the admitted hand-over in one owner configuration transaction, ++ for public clients as well as confidential clients even when delivery is NO_CHANGE. ++ Before changing {consuming_service}, durably bind exact case, acceptance, human ++ principal, owner configuration source, cluster/release and before revision; ++ authenticate the case-created post revision under the owner's release-writer ++ exclusion before verification. Configure issuer {issuer}, client {client_id} ++ and, only for confidential clients, custody bao:platform/workloads/keycape/{client_id}, ++ requiring one of {access_groups} before sign-in completes. Preserve identical ++ preexisting configuration as NO_CHANGE and refuse conflicts. Then the reviewed ++ adapter net-kingdom/keycape-hand-over, dispatched by Warden's case kernel inside ++ the attended consumer-owner session, verifies the four value-free probes ++ (member-login, non-member-refused, membership-add-grants, membership-remove-revokes) ++ and records the receipt. The owner wrapper's separate configuration inverse is ++ reachable on failure before a receipt exists and on later owner case unwind; ++ it does not depend on delivery-created objects or receipt creation. Missing or ++ ambiguous configuration provenance requires owner reconciliation, never replay. + auth: the attended consumer-owner session on lane apps-consumer-handover (actor human, + parent-session revoked per attempt); the consumer configuration itself is the +- owner's own deployment path ++ owner's own deployment path. Every configuration inverse is a distinct fresh ++ authorized attended owner operation, bound to operation, exact principal, case, ++ source, release and transaction, with its own per-attempt revocation on every ++ exit. Never reuse the already revoked hand-over parent session. Missing current ++ inverse authority retains pending owner reconciliation evidence + lane: apps-consumer-handover + warden_executes: false + attended: true +@@ -430,13 +444,22 @@ + reads_secret_value: false + rollback: + - reverses: hand-over +- action: the adapter removes only its hand-over receipt. Removing the OIDC source +- for {client_id} from {consuming_service} is the owner's Helm rollback (owner +- {consumer_owner_repo}), done inside deliver-consumer-secret's owner_remove +- before any consumer object is removed ++ action: the adapter removes only its authenticated case-created hand-over receipt. ++ Independently, the consumer owner's configuration transaction inverse restores ++ only this case's configuration change through the noted before Helm revision, ++ after authenticating exact case/principal/source/release binding and checking ++ the unchanged post revision under exclusion of all release writers. It runs ++ on owner unwind for public and confidential cases, including skipped or NO_CHANGE ++ delivery and failures before receipt creation. Preserve preexisting configuration; ++ drift or ambiguous provenance requires owner reconciliation. This owner inverse ++ is outside the receipt adapter and precedes any delivery-object removal; ++ receipt removal alone never proves configuration rollback. + - reverses: deliver-consumer-secret + when: client_type == confidential +- action: After reverting consumer secret reference, delete only authenticated created ++ action: First verify the owner configuration transaction inverse and absence of ++ remaining consumer secret references; NO_CHANGE configuration alone is not proof of ++ deactivation. Do not repeat an unguarded Helm rollback inside owner_remove. ++ Then delete only authenticated created + ExternalSecret/owned target by UID and ownerReference, then SecretStore and + dedicated SA; preserve preexisting-matched resources and never delete a shared + target. Retain audit and ownership receipts; removing SecretStore/SA alone is diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/type.before.yaml b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/type.before.yaml new file mode 100644 index 0000000..6ec7560 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/type.before.yaml @@ -0,0 +1,495 @@ +apiVersion: netkingdom.io/security-case-type/v0.1 +kind: SecurityCaseType +spec: + id: oidc-browser-relying-party + version: '1.3' + status: proposed + title: Browser OIDC relying party for an internal service + purpose: 'Let an internal web service sign its human users in through NetKingdom + (KeyCape): an exact-redirect OIDC client, optional MFA pin, the LLDAP groups the + service maps to its own roles, and, for confidential clients, a client secret + minted straight into OpenBao custody. Replaces the per-client rollout scripts + in sso-mfa/k8s/keycape. First case: Forgejo (KEY-WP-0038).' + parameters: + - name: client_id + type: string + case_settable: true + constraints: + pattern: ^[a-z0-9][a-z0-9-]{1,62}$ + sensitivity: operational + description: OAuth2 client_id, unique in KeyCape. + - name: display_name + type: string + case_settable: true + constraints: + pattern: ^[^\n\r]{1,80}$ + sensitivity: public + description: Name shown on the sign-in page. + - name: redirect_uris + type: string + case_settable: true + constraints: + pattern: ^https://[^*?#\s,]+(,https://[^*?#\s,]+)*$ + sensitivity: security_sensitive + description: 'Comma-separated exact https redirect URIs. No wildcards, no plain + http, no query and no fragment: KeyCape refuses ''*'' and ''?'' at startup, + and OAuth 2.0 forbids fragments (key-cape type review R1).' + - name: allowed_scopes + type: string + case_settable: true + constraints: + enum: + - openid,profile,email,groups + - openid,email,groups + - openid,profile,groups + - openid,groups + sensitivity: security_sensitive + description: Scopes the client may request, openid first. Always includes groups, + because access_groups is required and KeyCape's UserInfo returns groups only + for that scope (key-cape type review R2). + - name: client_type + type: string + case_settable: true + constraints: + enum: + - confidential + - public + sensitivity: security_sensitive + description: confidential for server-side services that can hold a secret; public + for SPAs and native apps (PKCE only). + - name: mfa_required + type: string + case_settable: true + constraints: + enum: + - 'true' + - 'false' + sensitivity: security_sensitive + description: '"true" pins MFA on this client; required when the service grants + administrative or owner rights. "false" is not "no MFA": the client then carries + no mfaRequired field and KeyCape applies the per-user privacyIDEA policy. A + renderer must never emit an explicit mfaRequired false, which would skip that + policy (key-cape type review R3).' + - name: access_groups + type: string + case_settable: true + constraints: + pattern: ^[a-z][a-z0-9-]{1,62}(,[a-z][a-z0-9-]{1,62})*$ + sensitivity: operational + description: 'Comma-separated LLDAP groups that the service maps to its roles. + Created if absent. KeyCape has no per-client group gate: it issues tokens to + any authenticated user and only carries the groups claim, so the consuming service + enforces the gate itself (for Forgejo, its required-claim setting; key-cape + type review R2).' + - name: initial_members + type: string + required: false + case_settable: true + constraints: + pattern: ^[a-z][a-z0-9-]*=[a-z][a-z0-9._-]*(\+[a-z][a-z0-9._-]*)*(;[a-z][a-z0-9-]*=[a-z][a-z0-9._-]*(\+[a-z][a-z0-9._-]*)*)*$ + sensitivity: operational + description: Initial memberships as group=user+user;group=user. Usernames are + directory usernames, never secrets. + - name: consuming_service + type: string + case_settable: true + constraints: + pattern: ^[a-z0-9][a-z0-9-]{0,62}$ + sensitivity: operational + description: The service that signs users in with this client. + - name: consumer_owner_repo + type: string + case_settable: true + constraints: + pattern: ^[a-z0-9][a-z0-9-]*$ + sensitivity: operational + description: The repository that owns the consuming service's configuration. + - name: consumer_namespace + type: string + case_settable: true + constraints: + pattern: ^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$ + sensitivity: security_sensitive + description: Consumer namespace for namespaced ESO TokenRequest reader; actual + workload owner must confirm; never infer from service name. + - name: issuer + type: string + case_settable: false + default: https://kc.coulomb.social + sensitivity: security_sensitive + description: The NetKingdom issuer; fixed by the type. + composition: + authority: netkingdom + initial_trust: + - runtime_secret_trust + - runtime_identity_trust + capabilities: + - c1.lightweight-sso + providers: + c1.lightweight-sso: net-kingdom.keycape-client-registration + parameter_bindings: + net-kingdom.keycape-client-registration.client_id: client_id + net-kingdom.keycape-client-registration.display_name: display_name + net-kingdom.keycape-client-registration.redirect_uris: redirect_uris + net-kingdom.keycape-client-registration.allowed_scopes: allowed_scopes + net-kingdom.keycape-client-registration.client_type: client_type + net-kingdom.keycape-client-registration.mfa_required: mfa_required + materialization: + - id: client-secret-path + kind: kv_path + when: client_type == confidential + template: platform/workloads/keycape/{client_id} + body: field CLIENT_SECRET; minted by the credential broker, never written by hand + - id: keycape-reader-policy + kind: openbao_policy + when: client_type == confidential + template: workload-kv-read-keycape-{client_id}-eso + body: "path \"platform/data/workloads/keycape/{client_id}\" {{\n capabilities\ + \ = [\"read\"]\n}}\n" + description: KeyCape reads its client secret only through ESO. No metadata, list, + parent or sibling access. + - id: consumer-reader-policy + kind: openbao_policy + when: client_type == confidential + template: workload-kv-read-keycape-{client_id}-{consuming_service} + body: "path \"platform/data/workloads/keycape/{client_id}\" {{\n capabilities\ + \ = [\"read\"]\n}}\n" + description: The consuming service reads the same secret. No metadata, list, parent + or sibling access. + - id: eso-role + kind: openbao_role + when: client_type == confidential + template: external-secrets-keycape-{client_id} + body: 'auth: kubernetes + + bound_service_account_names: [external-secrets] + + bound_service_account_namespaces: [external-secrets] + + token_policies: [workload-kv-read-keycape-{client_id}-eso] + + token_no_default_policy: true + + token_ttl: 15m + + token_max_ttl: 15m + + ' + description: The Kubernetes-auth role deliver-secret uses; bound to the ESO service + account only. + - id: consumer-eso-role + kind: openbao_role + when: client_type == confidential + template: external-secrets-keycape-{client_id}-{consuming_service} + body: 'auth: kubernetes + + bound_service_account_names: [{consuming_service}-keycape-{client_id}-eso] + + bound_service_account_namespaces: [{consumer_namespace}] + + audience: openbao + + token_policies: [workload-kv-read-keycape-{client_id}-{consuming_service}] + + token_no_default_policy: true + + token_ttl: 15m + + token_max_ttl: 15m + + ' + description: Consumer ESO requests a token for this separate reference identity. + Common ESO controller remains trusted and can request tokens; no cryptographic + controller isolation is claimed. + - id: catalog-entry + kind: catalog_entry + template: '{consuming_service}-keycape-client' + body: 'id: {consuming_service}-keycape-client + + owner_repo: key-cape + + warden_executes: false + + risk: high + + status: active once verification passes + + workload_ref: applicable, name {consuming_service}, declaration_ref of the consumer''s + published declaration or the explicit unknown form, never inferred + + path_template: platform/workloads/keycape/{client_id} + + field: CLIENT_SECRET + + delegation: native, intended_owner key-cape + + rotation: mint-secret then verification; automatable false until a run has been + proven + + ' + description: One entry per case (ops-mason item 5, ops-warden R4'). It carries + the consumer's workload reference, which ADR-0009 needs for zone selection. + follow_through: + - id: ensure-groups + owner: net-kingdom + entry_point: reviewed adapter net-kingdom/keycape-ensure-groups, dispatched by Warden's + case kernel inside the attended session; it calls identity-provisioner + ensure_service_groups in the provisioner pod through the session's contained + cluster access (groups {access_groups}; members exactly {initial_members} as + recorded in the accepted case, nothing else) + auth: the attended human session on lane keycape-attended-case-change (actor human, + parent-session revoked per attempt); the in-pod call uses the provisioner's own + directory binding, and no provisioner or user-engine token is read or sent + lane: keycape-attended-case-change + warden_executes: false + attended: true + value_handling: none + attended_justification: The provisioner's only HTTP credential is user-engine's + runtime token, and its NetworkPolicy admits only user-engine pods. A proxy lane + would act as user-engine (ops-warden review R8; canon section 3; ops-warden + ADR-0002). Running it in the session register-client already needs adds no attended + session. It can become unattended once the provisioner has an operator credential + of its own and an admitted path. Warden's case kernel dispatches the reviewed adapter only inside the one attended human session (descriptor actor human, lane keycape-attended-case-change, parent-session revoked per attempt); the human session is the authority and the kernel is only the dispatcher. + - id: mint-secret + owner: railiance-platform + when: client_type == confidential + entry_point: 'railiance-platform credential broker: generate a client secret inside + custody at bao:platform/workloads/keycape/{client_id}, through reviewed adapter + railiance-platform/keycape-mint-secret' + auth: a human caller (net-kingdom-admins, aal2) starts it; Warden runs it unattended + through the broker lane under the caller's session, with the caller's broker grant + for the case's KV path; agents and root are refused, and the value is generated + in custody and never returned + lane: keycape-client-secret-generate + warden_executes: true + attended: false + value_handling: generated_in_custody + - id: deliver-secret + owner: railiance-platform + when: client_type == confidential + entry_point: reviewed adapter railiance-platform/keycape-deliver-secret, dispatched + by Warden's case kernel; Platform's deliver capability applies ExternalSecret + sso/keycape-{client_id}-client (key client-secret) through the ESO store for + role external-secrets-keycape-{client_id}, from bao:platform/workloads/keycape/{client_id} + auth: Platform's own delivery path on lane platform-secret-delivery, inside the human + aal2 session; ESO reads custody through role external-secrets-keycape-{client_id}; + Warden holds no Platform credential, and no value leaves custody + lane: platform-secret-delivery + warden_executes: false + attended: false + value_handling: none + ready_check: the delivery for sso/keycape-{client_id}-client reports Ready=True + (status only, no value read) + - id: bind-env + owner: net-kingdom + when: client_type == confidential + entry_point: 'reviewed adapter net-kingdom/keycape-bind-env, dispatched by Warden''s + case kernel inside the attended session: patch Deployment sso/keycape env + KEYCAPE_{client_id:env}_CLIENT_SECRET from sso/keycape-{client_id}-client, by + reference only' + auth: the attended human session on lane keycape-attended-case-change, with operator + kube credentials for namespace sso (actor human, parent-session revoked per attempt) + lane: keycape-attended-case-change + warden_executes: false + attended: true + value_handling: none + attended_justification: A change to the live identity provider's Deployment. It + runs in the same attended operator session as register-client, before it, because + KeyCape resolves a confidential client's secret at start and fails closed if + the binding is missing. Warden's case kernel dispatches the reviewed adapter only inside the one attended human session (descriptor actor human, lane keycape-attended-case-change, parent-session revoked per attempt); the human session is the authority and the kernel is only the dispatcher. + - id: register-client + owner: net-kingdom + entry_point: reviewed adapter net-kingdom/keycape-register-client, dispatched by Warden's + case kernel inside the attended session; it runs sso-mfa/k8s/keycape/keycape-client.py + apply --client-file --case-acceptance --check-config + (net-kingdom.keycape-client-registration) + auth: the attended human session on lane keycape-attended-case-change, with operator + write access to the live KeyCape client registry in namespace sso (actor human, + parent-session revoked per attempt) + lane: keycape-attended-case-change + warden_executes: false + attended: true + value_handling: none + attended_justification: 'The live KeyCape client registry is a Kubernetes Secret, + and agents do not read or write Secrets. Until KeyCape loads clients from a + non-secret registry with secrets referenced from custody, one attended operator + session applies the rendered registration. ops-warden groups all attended steps + of a run into that one session. KeyCape loads clients at startup and keeps login, + authorization and handoff state in memory, so the restart drops in-flight logins + and sessions for every client; runs go in a quiet window (key-cape type review + R4). The rendered registry must pass KeyCape''s own startup checks (keycape + check-config, the pinned checker artifact sha256:be37738fe1c08259fb4f9ca0daf0f9301bfffc237e5bb38435525dfbf11ccff5 + built from key-cape 3c71226, which keycape-client.py runs before it patches) + before the restart: a refused entry stops the single replica and every client + loses sign-in (R1). Warden''s case kernel dispatches the reviewed adapter only inside the one attended human session (descriptor actor human, lane keycape-attended-case-change, parent-session revoked per attempt); the human session is the authority and the kernel is only the dispatcher.' + - id: deliver-consumer-secret + owner: '{consumer_owner_repo}' + when: client_type == confidential + entry_point: Reviewed adapter net-kingdom/keycape-deliver-consumer-secret, dispatched + by Warden's case kernel inside the attended consumer-owner session; the owner's + apply capability creates dedicated ServiceAccount {consumer_namespace}/{consuming_service}-keycape-{client_id}-eso + (automount=false, no legacy tokens), namespace SecretStore openbao-keycape-{client_id}-consumer + with serviceAccountRef and audiences [openbao], and ExternalSecret {consuming_service}-netkingdom-oidc-client + with Owner target, Retain remote deletion and only CLIENT_SECRET from workloads/keycape/{client_id}. + Admission validates actual installed ESO TokenRequest support and common controller + RBAC before any owner-approved apply. + auth: the attended consumer-owner session on lane consumer-owner-deployment (actor + human, the consumer owner's operator deployment authority, on the founder's go + for the change); ESO authenticates to OpenBao role external-secrets-keycape-{client_id}-{consuming_service} + with a TokenRequest for the dedicated namespaced serviceAccountRef; controller + remains trusted + lane: consumer-owner-deployment + warden_executes: false + attended: true + value_handling: none + attended_justification: The consumer owner's only deployment authority in the consumer + namespace is its operator credential, and production applies need the founder's + go per change. No contained agent deployer identity exists, and the owner does + not want a standing credential an agent could trigger. It runs in one attended + consumer-owner session together with hand-over. Warden's case kernel dispatches + the reviewed adapter only inside that session (actor human, parent-session + revoked per attempt); the human session is the authority and the kernel is only + the dispatcher. + ready_check: Namespace SecretStore and ExternalSecret both report Ready=True; + dedicated SA has no legacy token refs; status and fixed metadata only, never + Secret values. + - id: hand-over + owner: '{consumer_owner_repo}' + entry_point: The consumer owner first makes its own Helm change configuring {consuming_service} + with issuer {issuer}, client {client_id} and custody bao:platform/workloads/keycape/{client_id}, + requiring one of {access_groups} in the groups claim before sign-in completes. + Then reviewed adapter net-kingdom/keycape-hand-over, dispatched by Warden's case + kernel inside the attended consumer-owner session, verifies the consumer with + four value-free probes (member-login, non-member-refused, membership-add-grants, + membership-remove-revokes) and records the hand-over receipt. + auth: the attended consumer-owner session on lane apps-consumer-handover (actor human, + parent-session revoked per attempt); the consumer configuration itself is the + owner's own deployment path + lane: apps-consumer-handover + warden_executes: false + attended: true + value_handling: none + attended_justification: The verification needs a real member login and a refused + non-member login, which only an attended session can perform, and it follows + the owner's own Helm change in the same consumer-owner session as + deliver-consumer-secret. Warden's case kernel dispatches the reviewed adapter + only inside that session (actor human, parent-session revoked per attempt); the + human session is the authority and the kernel is only the dispatcher. + verification: + - id: authorize-accepts-redirects + check: GET {issuer}/authorize with client {client_id} and each of {redirect_uris} + returns the login page + expect: present + reads_secret_value: false + - id: authorize-refuses-other-redirect + check: GET {issuer}/authorize with client {client_id} and an unregistered redirect + returns an OAuth error + expect: deny + reads_secret_value: false + - id: custody-denied-to-default + when: client_type == confidential + check: a default-only OpenBao identity has no read capability on bao:platform/workloads/keycape/{client_id} + expect: deny + reads_secret_value: false + - id: custody-allowed-to-consumer + when: client_type == confidential + check: policy workload-kv-read-keycape-{client_id}-{consuming_service} grants + read on bao:platform/workloads/keycape/{client_id} (capabilities check only) + expect: allow + reads_secret_value: false + - id: groups-present + check: LLDAP groups {access_groups} exist, with exactly the memberships of the + accepted case added + expect: present + reads_secret_value: false + - id: consumer-enforces-groups + check: '{consuming_service}''s OIDC source for {client_id} (owner {consumer_owner_repo}) + requires one of {access_groups} in the groups claim (configuration read, no + secret value)' + expect: present + reads_secret_value: false + - id: secret-delivered + when: client_type == confidential + check: the delivery for sso/keycape-{client_id}-client reports Ready=True (status + only) + expect: present + reads_secret_value: false + - id: mfa-pinned + when: mfa_required == true + check: keycape-client.py inspect for {client_id} shows the MFA pin on the registered + client (non-secret client view) + expect: present + reads_secret_value: false + - id: consumer-secret-delivered + when: client_type == confidential + check: '{consumer_namespace} SecretStore openbao-keycape-{client_id}-consumer + and ExternalSecret {consuming_service}-netkingdom-oidc-client Ready=True, exact + dedicated SA/audience role binding, fixed CLIENT_SECRET mapping; no Secret value + read' + expect: present + reads_secret_value: false + rollback: + - reverses: hand-over + action: the adapter removes only its hand-over receipt. Removing the OIDC source + for {client_id} from {consuming_service} is the owner's Helm rollback (owner + {consumer_owner_repo}), done inside deliver-consumer-secret's owner_remove + before any consumer object is removed + - reverses: deliver-consumer-secret + when: client_type == confidential + action: After reverting consumer secret reference, delete only authenticated created + ExternalSecret/owned target by UID and ownerReference, then SecretStore and + dedicated SA; preserve preexisting-matched resources and never delete a shared + target. Retain audit and ownership receipts; removing SecretStore/SA alone is + not immediate OpenBao token revocation. + - reverses: register-client + action: keycape-client.py apply --operation remove for {client_id} + - reverses: bind-env + when: client_type == confidential + action: remove env KEYCAPE_{client_id:env}_CLIENT_SECRET from Deployment sso/keycape, + after register-client is reversed + - reverses: deliver-secret + when: client_type == confidential + action: delete ExternalSecret sso/keycape-{client_id}-client and its target + - reverses: mint-secret + when: client_type == confidential + action: 'covered by client-secret-path: one metadata delete, after deliver-secret + is reversed' + - reverses: ensure-groups + action: identity-provisioner revoke_service_groups inside the provisioner pod + (attended, as ensure-groups) with the case's memberships; delete_if_empty only + if no other case uses {access_groups} + - reverses: client-secret-path + when: client_type == confidential + action: delete metadata and all versions at platform/workloads/keycape/{client_id}, + after the ExternalSecret is gone (this also reverses mint-secret) + - reverses: eso-role + when: client_type == confidential + action: delete Kubernetes-auth role external-secrets-keycape-{client_id} + - reverses: keycape-reader-policy + when: client_type == confidential + action: delete policy workload-kv-read-keycape-{client_id}-eso, after eso-role + - reverses: consumer-eso-role + when: client_type == confidential + action: Delete only this case-created role external-secrets-keycape-{client_id}-{consuming_service}, + before deleting consumer-reader-policy; revoke case-issued OpenBao tokens or + prove expiration before declaring deactivation. + - reverses: consumer-reader-policy + when: client_type == confidential + action: delete policy workload-kv-read-keycape-{client_id}-{consuming_service} + - reverses: catalog-entry + action: remove catalog entry {consuming_service}-keycape-client + acceptance: + accepted_by: + - owner + - founder + risk_class: medium + # Reviewed machine descriptors (NK-WP-0048-T16 successor candidate). Digests are + # generated by sso-mfa/k8s/keycape/compose-1.2-descriptors.py --version 1.3. + execution_descriptors: + bind-env: sha256:d5fcb20cf15bf7454890d2529180c582218e3df12584a99162e4b664b899caa0 + deliver-consumer-secret: sha256:36281df8139fb1ec64b226d94f80eb592de5addab7063ff54c691c641e275fc7 + deliver-secret: sha256:75aaa7346aebdea2a0eb0ba993995515b5248ffefc34caf8e713525e9b58da85 + ensure-groups: sha256:ebc9d5b9167d18e6015f4bbeb9699e8d396d64fbfaf195142781f84f4f47a70a + hand-over: sha256:1bce07a3229b7d0c7c730f35ce5b40fe471009a82de8dd53788ed970a9be1224 + mint-secret: sha256:c92957227235db04953809d92b260d32c8b947472a8e8fd17a258ade3ae8da70 + register-client: sha256:4ecc769f75433b66c7161f13c0fe7e5599633cf466d46cb6d8031f0456f426e7 diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/type.candidate.yaml b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/type.candidate.yaml new file mode 100644 index 0000000..0f5e90d --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/type.candidate.yaml @@ -0,0 +1,518 @@ +apiVersion: netkingdom.io/security-case-type/v0.1 +kind: SecurityCaseType +spec: + id: oidc-browser-relying-party + version: '1.3' + status: proposed + title: Browser OIDC relying party for an internal service + purpose: 'Let an internal web service sign its human users in through NetKingdom + (KeyCape): an exact-redirect OIDC client, optional MFA pin, the LLDAP groups the + service maps to its own roles, and, for confidential clients, a client secret + minted straight into OpenBao custody. Replaces the per-client rollout scripts + in sso-mfa/k8s/keycape. First case: Forgejo (KEY-WP-0038).' + parameters: + - name: client_id + type: string + case_settable: true + constraints: + pattern: ^[a-z0-9][a-z0-9-]{1,62}$ + sensitivity: operational + description: OAuth2 client_id, unique in KeyCape. + - name: display_name + type: string + case_settable: true + constraints: + pattern: ^[^\n\r]{1,80}$ + sensitivity: public + description: Name shown on the sign-in page. + - name: redirect_uris + type: string + case_settable: true + constraints: + pattern: ^https://[^*?#\s,]+(,https://[^*?#\s,]+)*$ + sensitivity: security_sensitive + description: 'Comma-separated exact https redirect URIs. No wildcards, no plain + http, no query and no fragment: KeyCape refuses ''*'' and ''?'' at startup, + and OAuth 2.0 forbids fragments (key-cape type review R1).' + - name: allowed_scopes + type: string + case_settable: true + constraints: + enum: + - openid,profile,email,groups + - openid,email,groups + - openid,profile,groups + - openid,groups + sensitivity: security_sensitive + description: Scopes the client may request, openid first. Always includes groups, + because access_groups is required and KeyCape's UserInfo returns groups only + for that scope (key-cape type review R2). + - name: client_type + type: string + case_settable: true + constraints: + enum: + - confidential + - public + sensitivity: security_sensitive + description: confidential for server-side services that can hold a secret; public + for SPAs and native apps (PKCE only). + - name: mfa_required + type: string + case_settable: true + constraints: + enum: + - 'true' + - 'false' + sensitivity: security_sensitive + description: '"true" pins MFA on this client; required when the service grants + administrative or owner rights. "false" is not "no MFA": the client then carries + no mfaRequired field and KeyCape applies the per-user privacyIDEA policy. A + renderer must never emit an explicit mfaRequired false, which would skip that + policy (key-cape type review R3).' + - name: access_groups + type: string + case_settable: true + constraints: + pattern: ^[a-z][a-z0-9-]{1,62}(,[a-z][a-z0-9-]{1,62})*$ + sensitivity: operational + description: 'Comma-separated LLDAP groups that the service maps to its roles. + Created if absent. KeyCape has no per-client group gate: it issues tokens to + any authenticated user and only carries the groups claim, so the consuming service + enforces the gate itself (for Forgejo, its required-claim setting; key-cape + type review R2).' + - name: initial_members + type: string + required: false + case_settable: true + constraints: + pattern: ^[a-z][a-z0-9-]*=[a-z][a-z0-9._-]*(\+[a-z][a-z0-9._-]*)*(;[a-z][a-z0-9-]*=[a-z][a-z0-9._-]*(\+[a-z][a-z0-9._-]*)*)*$ + sensitivity: operational + description: Initial memberships as group=user+user;group=user. Usernames are + directory usernames, never secrets. + - name: consuming_service + type: string + case_settable: true + constraints: + pattern: ^[a-z0-9][a-z0-9-]{0,62}$ + sensitivity: operational + description: The service that signs users in with this client. + - name: consumer_owner_repo + type: string + case_settable: true + constraints: + pattern: ^[a-z0-9][a-z0-9-]*$ + sensitivity: operational + description: The repository that owns the consuming service's configuration. + - name: consumer_namespace + type: string + case_settable: true + constraints: + pattern: ^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$ + sensitivity: security_sensitive + description: Consumer namespace for namespaced ESO TokenRequest reader; actual + workload owner must confirm; never infer from service name. + - name: issuer + type: string + case_settable: false + default: https://kc.coulomb.social + sensitivity: security_sensitive + description: The NetKingdom issuer; fixed by the type. + composition: + authority: netkingdom + initial_trust: + - runtime_secret_trust + - runtime_identity_trust + capabilities: + - c1.lightweight-sso + providers: + c1.lightweight-sso: net-kingdom.keycape-client-registration + parameter_bindings: + net-kingdom.keycape-client-registration.client_id: client_id + net-kingdom.keycape-client-registration.display_name: display_name + net-kingdom.keycape-client-registration.redirect_uris: redirect_uris + net-kingdom.keycape-client-registration.allowed_scopes: allowed_scopes + net-kingdom.keycape-client-registration.client_type: client_type + net-kingdom.keycape-client-registration.mfa_required: mfa_required + materialization: + - id: client-secret-path + kind: kv_path + when: client_type == confidential + template: platform/workloads/keycape/{client_id} + body: field CLIENT_SECRET; minted by the credential broker, never written by hand + - id: keycape-reader-policy + kind: openbao_policy + when: client_type == confidential + template: workload-kv-read-keycape-{client_id}-eso + body: "path \"platform/data/workloads/keycape/{client_id}\" {{\n capabilities\ + \ = [\"read\"]\n}}\n" + description: KeyCape reads its client secret only through ESO. No metadata, list, + parent or sibling access. + - id: consumer-reader-policy + kind: openbao_policy + when: client_type == confidential + template: workload-kv-read-keycape-{client_id}-{consuming_service} + body: "path \"platform/data/workloads/keycape/{client_id}\" {{\n capabilities\ + \ = [\"read\"]\n}}\n" + description: The consuming service reads the same secret. No metadata, list, parent + or sibling access. + - id: eso-role + kind: openbao_role + when: client_type == confidential + template: external-secrets-keycape-{client_id} + body: 'auth: kubernetes + + bound_service_account_names: [external-secrets] + + bound_service_account_namespaces: [external-secrets] + + token_policies: [workload-kv-read-keycape-{client_id}-eso] + + token_no_default_policy: true + + token_ttl: 15m + + token_max_ttl: 15m + + ' + description: The Kubernetes-auth role deliver-secret uses; bound to the ESO service + account only. + - id: consumer-eso-role + kind: openbao_role + when: client_type == confidential + template: external-secrets-keycape-{client_id}-{consuming_service} + body: 'auth: kubernetes + + bound_service_account_names: [{consuming_service}-keycape-{client_id}-eso] + + bound_service_account_namespaces: [{consumer_namespace}] + + audience: openbao + + token_policies: [workload-kv-read-keycape-{client_id}-{consuming_service}] + + token_no_default_policy: true + + token_ttl: 15m + + token_max_ttl: 15m + + ' + description: Consumer ESO requests a token for this separate reference identity. + Common ESO controller remains trusted and can request tokens; no cryptographic + controller isolation is claimed. + - id: catalog-entry + kind: catalog_entry + template: '{consuming_service}-keycape-client' + body: 'id: {consuming_service}-keycape-client + + owner_repo: key-cape + + warden_executes: false + + risk: high + + status: active once verification passes + + workload_ref: applicable, name {consuming_service}, declaration_ref of the consumer''s + published declaration or the explicit unknown form, never inferred + + path_template: platform/workloads/keycape/{client_id} + + field: CLIENT_SECRET + + delegation: native, intended_owner key-cape + + rotation: mint-secret then verification; automatable false until a run has been + proven + + ' + description: One entry per case (ops-mason item 5, ops-warden R4'). It carries + the consumer's workload reference, which ADR-0009 needs for zone selection. + follow_through: + - id: ensure-groups + owner: net-kingdom + entry_point: reviewed adapter net-kingdom/keycape-ensure-groups, dispatched by Warden's + case kernel inside the attended session; it calls identity-provisioner + ensure_service_groups in the provisioner pod through the session's contained + cluster access (groups {access_groups}; members exactly {initial_members} as + recorded in the accepted case, nothing else) + auth: the attended human session on lane keycape-attended-case-change (actor human, + parent-session revoked per attempt); the in-pod call uses the provisioner's own + directory binding, and no provisioner or user-engine token is read or sent + lane: keycape-attended-case-change + warden_executes: false + attended: true + value_handling: none + attended_justification: The provisioner's only HTTP credential is user-engine's + runtime token, and its NetworkPolicy admits only user-engine pods. A proxy lane + would act as user-engine (ops-warden review R8; canon section 3; ops-warden + ADR-0002). Running it in the session register-client already needs adds no attended + session. It can become unattended once the provisioner has an operator credential + of its own and an admitted path. Warden's case kernel dispatches the reviewed adapter only inside the one attended human session (descriptor actor human, lane keycape-attended-case-change, parent-session revoked per attempt); the human session is the authority and the kernel is only the dispatcher. + - id: mint-secret + owner: railiance-platform + when: client_type == confidential + entry_point: 'railiance-platform credential broker: generate a client secret inside + custody at bao:platform/workloads/keycape/{client_id}, through reviewed adapter + railiance-platform/keycape-mint-secret' + auth: a human caller (net-kingdom-admins, aal2) starts it; Warden runs it unattended + through the broker lane under the caller's session, with the caller's broker grant + for the case's KV path; agents and root are refused, and the value is generated + in custody and never returned + lane: keycape-client-secret-generate + warden_executes: true + attended: false + value_handling: generated_in_custody + - id: deliver-secret + owner: railiance-platform + when: client_type == confidential + entry_point: reviewed adapter railiance-platform/keycape-deliver-secret, dispatched + by Warden's case kernel; Platform's deliver capability applies ExternalSecret + sso/keycape-{client_id}-client (key client-secret) through the ESO store for + role external-secrets-keycape-{client_id}, from bao:platform/workloads/keycape/{client_id} + auth: Platform's own delivery path on lane platform-secret-delivery, inside the human + aal2 session; ESO reads custody through role external-secrets-keycape-{client_id}; + Warden holds no Platform credential, and no value leaves custody + lane: platform-secret-delivery + warden_executes: false + attended: false + value_handling: none + ready_check: the delivery for sso/keycape-{client_id}-client reports Ready=True + (status only, no value read) + - id: bind-env + owner: net-kingdom + when: client_type == confidential + entry_point: 'reviewed adapter net-kingdom/keycape-bind-env, dispatched by Warden''s + case kernel inside the attended session: patch Deployment sso/keycape env + KEYCAPE_{client_id:env}_CLIENT_SECRET from sso/keycape-{client_id}-client, by + reference only' + auth: the attended human session on lane keycape-attended-case-change, with operator + kube credentials for namespace sso (actor human, parent-session revoked per attempt) + lane: keycape-attended-case-change + warden_executes: false + attended: true + value_handling: none + attended_justification: A change to the live identity provider's Deployment. It + runs in the same attended operator session as register-client, before it, because + KeyCape resolves a confidential client's secret at start and fails closed if + the binding is missing. Warden's case kernel dispatches the reviewed adapter only inside the one attended human session (descriptor actor human, lane keycape-attended-case-change, parent-session revoked per attempt); the human session is the authority and the kernel is only the dispatcher. + - id: register-client + owner: net-kingdom + entry_point: reviewed adapter net-kingdom/keycape-register-client, dispatched by Warden's + case kernel inside the attended session; it runs sso-mfa/k8s/keycape/keycape-client.py + apply --client-file --case-acceptance --check-config + (net-kingdom.keycape-client-registration) + auth: the attended human session on lane keycape-attended-case-change, with operator + write access to the live KeyCape client registry in namespace sso (actor human, + parent-session revoked per attempt) + lane: keycape-attended-case-change + warden_executes: false + attended: true + value_handling: none + attended_justification: 'The live KeyCape client registry is a Kubernetes Secret, + and agents do not read or write Secrets. Until KeyCape loads clients from a + non-secret registry with secrets referenced from custody, one attended operator + session applies the rendered registration. ops-warden groups all attended steps + of a run into that one session. KeyCape loads clients at startup and keeps login, + authorization and handoff state in memory, so the restart drops in-flight logins + and sessions for every client; runs go in a quiet window (key-cape type review + R4). The rendered registry must pass KeyCape''s own startup checks (keycape + check-config, the pinned checker artifact sha256:be37738fe1c08259fb4f9ca0daf0f9301bfffc237e5bb38435525dfbf11ccff5 + built from key-cape 3c71226, which keycape-client.py runs before it patches) + before the restart: a refused entry stops the single replica and every client + loses sign-in (R1). Warden''s case kernel dispatches the reviewed adapter only inside the one attended human session (descriptor actor human, lane keycape-attended-case-change, parent-session revoked per attempt); the human session is the authority and the kernel is only the dispatcher.' + - id: deliver-consumer-secret + owner: '{consumer_owner_repo}' + when: client_type == confidential + entry_point: Reviewed adapter net-kingdom/keycape-deliver-consumer-secret, dispatched + by Warden's case kernel inside the attended consumer-owner session; the owner's + apply capability creates dedicated ServiceAccount {consumer_namespace}/{consuming_service}-keycape-{client_id}-eso + (automount=false, no legacy tokens), namespace SecretStore openbao-keycape-{client_id}-consumer + with serviceAccountRef and audiences [openbao], and ExternalSecret {consuming_service}-netkingdom-oidc-client + with Owner target, Retain remote deletion and only CLIENT_SECRET from workloads/keycape/{client_id}. + Admission validates actual installed ESO TokenRequest support and common controller + RBAC before any owner-approved apply. + auth: the attended consumer-owner session on lane consumer-owner-deployment (actor + human, the consumer owner's operator deployment authority, on the founder's go + for the change); ESO authenticates to OpenBao role external-secrets-keycape-{client_id}-{consuming_service} + with a TokenRequest for the dedicated namespaced serviceAccountRef; controller + remains trusted + lane: consumer-owner-deployment + warden_executes: false + attended: true + value_handling: none + attended_justification: The consumer owner's only deployment authority in the consumer + namespace is its operator credential, and production applies need the founder's + go per change. No contained agent deployer identity exists, and the owner does + not want a standing credential an agent could trigger. It runs in one attended + consumer-owner session together with hand-over. Warden's case kernel dispatches + the reviewed adapter only inside that session (actor human, parent-session + revoked per attempt); the human session is the authority and the kernel is only + the dispatcher. + ready_check: Namespace SecretStore and ExternalSecret both report Ready=True; + dedicated SA has no legacy token refs; status and fixed metadata only, never + Secret values. + - id: hand-over + owner: '{consumer_owner_repo}' + entry_point: The consumer owner's reviewed deployment entry wraps its own Helm + configuration and the admitted hand-over in one owner configuration transaction, + for public clients as well as confidential clients even when delivery is NO_CHANGE. + Before changing {consuming_service}, durably bind exact case, acceptance, human + principal, owner configuration source, cluster/release and before revision; + authenticate the case-created post revision under the owner's release-writer + exclusion before verification. Configure issuer {issuer}, client {client_id} + and, only for confidential clients, custody bao:platform/workloads/keycape/{client_id}, + requiring one of {access_groups} before sign-in completes. Preserve identical + preexisting configuration as NO_CHANGE and refuse conflicts. Then the reviewed + adapter net-kingdom/keycape-hand-over, dispatched by Warden's case kernel inside + the attended consumer-owner session, verifies the four value-free probes + (member-login, non-member-refused, membership-add-grants, membership-remove-revokes) + and records the receipt. The owner wrapper's separate configuration inverse is + reachable on failure before a receipt exists and on later owner case unwind; + it does not depend on delivery-created objects or receipt creation. Missing or + ambiguous configuration provenance requires owner reconciliation, never replay. + auth: the attended consumer-owner session on lane apps-consumer-handover (actor human, + parent-session revoked per attempt); the consumer configuration itself is the + owner's own deployment path. Every configuration inverse is a distinct fresh + authorized attended owner operation, bound to operation, exact principal, case, + source, release and transaction, with its own per-attempt revocation on every + exit. Never reuse the already revoked hand-over parent session. Missing current + inverse authority retains pending owner reconciliation evidence + lane: apps-consumer-handover + warden_executes: false + attended: true + value_handling: none + attended_justification: The verification needs a real member login and a refused + non-member login, which only an attended session can perform, and it follows + the owner's own Helm change in the same consumer-owner session as + deliver-consumer-secret. Warden's case kernel dispatches the reviewed adapter + only inside that session (actor human, parent-session revoked per attempt); the + human session is the authority and the kernel is only the dispatcher. + verification: + - id: authorize-accepts-redirects + check: GET {issuer}/authorize with client {client_id} and each of {redirect_uris} + returns the login page + expect: present + reads_secret_value: false + - id: authorize-refuses-other-redirect + check: GET {issuer}/authorize with client {client_id} and an unregistered redirect + returns an OAuth error + expect: deny + reads_secret_value: false + - id: custody-denied-to-default + when: client_type == confidential + check: a default-only OpenBao identity has no read capability on bao:platform/workloads/keycape/{client_id} + expect: deny + reads_secret_value: false + - id: custody-allowed-to-consumer + when: client_type == confidential + check: policy workload-kv-read-keycape-{client_id}-{consuming_service} grants + read on bao:platform/workloads/keycape/{client_id} (capabilities check only) + expect: allow + reads_secret_value: false + - id: groups-present + check: LLDAP groups {access_groups} exist, with exactly the memberships of the + accepted case added + expect: present + reads_secret_value: false + - id: consumer-enforces-groups + check: '{consuming_service}''s OIDC source for {client_id} (owner {consumer_owner_repo}) + requires one of {access_groups} in the groups claim (configuration read, no + secret value)' + expect: present + reads_secret_value: false + - id: secret-delivered + when: client_type == confidential + check: the delivery for sso/keycape-{client_id}-client reports Ready=True (status + only) + expect: present + reads_secret_value: false + - id: mfa-pinned + when: mfa_required == true + check: keycape-client.py inspect for {client_id} shows the MFA pin on the registered + client (non-secret client view) + expect: present + reads_secret_value: false + - id: consumer-secret-delivered + when: client_type == confidential + check: '{consumer_namespace} SecretStore openbao-keycape-{client_id}-consumer + and ExternalSecret {consuming_service}-netkingdom-oidc-client Ready=True, exact + dedicated SA/audience role binding, fixed CLIENT_SECRET mapping; no Secret value + read' + expect: present + reads_secret_value: false + rollback: + - reverses: hand-over + action: the adapter removes only its authenticated case-created hand-over receipt. + Independently, the consumer owner's configuration transaction inverse restores + only this case's configuration change through the noted before Helm revision, + after authenticating exact case/principal/source/release binding and checking + the unchanged post revision under exclusion of all release writers. It runs + on owner unwind for public and confidential cases, including skipped or NO_CHANGE + delivery and failures before receipt creation. Preserve preexisting configuration; + drift or ambiguous provenance requires owner reconciliation. This owner inverse + is outside the receipt adapter and precedes any delivery-object removal; + receipt removal alone never proves configuration rollback. + - reverses: deliver-consumer-secret + when: client_type == confidential + action: First verify the owner configuration transaction inverse and absence of + remaining consumer secret references; NO_CHANGE configuration alone is not proof of + deactivation. Do not repeat an unguarded Helm rollback inside owner_remove. + Then delete only authenticated created + ExternalSecret/owned target by UID and ownerReference, then SecretStore and + dedicated SA; preserve preexisting-matched resources and never delete a shared + target. Retain audit and ownership receipts; removing SecretStore/SA alone is + not immediate OpenBao token revocation. + - reverses: register-client + action: keycape-client.py apply --operation remove for {client_id} + - reverses: bind-env + when: client_type == confidential + action: remove env KEYCAPE_{client_id:env}_CLIENT_SECRET from Deployment sso/keycape, + after register-client is reversed + - reverses: deliver-secret + when: client_type == confidential + action: delete ExternalSecret sso/keycape-{client_id}-client and its target + - reverses: mint-secret + when: client_type == confidential + action: 'covered by client-secret-path: one metadata delete, after deliver-secret + is reversed' + - reverses: ensure-groups + action: identity-provisioner revoke_service_groups inside the provisioner pod + (attended, as ensure-groups) with the case's memberships; delete_if_empty only + if no other case uses {access_groups} + - reverses: client-secret-path + when: client_type == confidential + action: delete metadata and all versions at platform/workloads/keycape/{client_id}, + after the ExternalSecret is gone (this also reverses mint-secret) + - reverses: eso-role + when: client_type == confidential + action: delete Kubernetes-auth role external-secrets-keycape-{client_id} + - reverses: keycape-reader-policy + when: client_type == confidential + action: delete policy workload-kv-read-keycape-{client_id}-eso, after eso-role + - reverses: consumer-eso-role + when: client_type == confidential + action: Delete only this case-created role external-secrets-keycape-{client_id}-{consuming_service}, + before deleting consumer-reader-policy; revoke case-issued OpenBao tokens or + prove expiration before declaring deactivation. + - reverses: consumer-reader-policy + when: client_type == confidential + action: delete policy workload-kv-read-keycape-{client_id}-{consuming_service} + - reverses: catalog-entry + action: remove catalog entry {consuming_service}-keycape-client + acceptance: + accepted_by: + - owner + - founder + risk_class: medium + # Reviewed machine descriptors (NK-WP-0048-T16 successor candidate). Digests are + # generated by sso-mfa/k8s/keycape/compose-1.2-descriptors.py --version 1.3. + execution_descriptors: + bind-env: sha256:d5fcb20cf15bf7454890d2529180c582218e3df12584a99162e4b664b899caa0 + deliver-consumer-secret: sha256:36281df8139fb1ec64b226d94f80eb592de5addab7063ff54c691c641e275fc7 + deliver-secret: sha256:75aaa7346aebdea2a0eb0ba993995515b5248ffefc34caf8e713525e9b58da85 + ensure-groups: sha256:ebc9d5b9167d18e6015f4bbeb9699e8d396d64fbfaf195142781f84f4f47a70a + hand-over: sha256:1bce07a3229b7d0c7c730f35ce5b40fe471009a82de8dd53788ed970a9be1224 + mint-secret: sha256:c92957227235db04953809d92b260d32c8b947472a8e8fd17a258ade3ae8da70 + register-client: sha256:4ecc769f75433b66c7161f13c0fe7e5599633cf466d46cb6d8031f0456f426e7 diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-review-r1/README.md b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-review-r1/README.md new file mode 100644 index 0000000..00395c9 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-review-r1/README.md @@ -0,0 +1,29 @@ +# Independent Apps-scoped F3 source review + +Verdict: **needs-change (RAPPS-F3-SESSION-1)**. The external owner configuration transaction is a coherent source design for public clients and confidential delivery NO_CHANGE. The proposed executable inverse omits a distinct fresh attended inverse-operation authorization/session boundary. This review is coordinator-delegated source review, not an actual railiance-apps owner acceptance, native grant or published implementation. + +Reviewed Apps `944b30ad63ab054cc24617e374e177d34cd572da` and NetKingdom `3236d5000e52e0643f33906a26a7825aa0b67465`. `review.json` records exact candidate/source SHA-256 values and independently checked all 17 retained input pins against their current bytes. Sources include Apps AGENTS/INTENT, owner-design, actual T16/1.3 review records, Makefile and RAPPS-WP-0016. Original candidate wrapper SHA-256: `62752f518b78fa7713bdb4d47e68decbf3aa89ecbc7950549ffdc3d08e01c554`; candidate type SHA-256: `739c927ab494f4e0f6618e6e55394dd8dc4b720db428cd4973f7a81524e1ecd9`. + +## Finding and supplied correction + +The real frozen hand-over adapter's `execute` invokes `revoke_session(inv)` in `finally` on success and failure. Candidate `run` calls `inverse` only after hand-over exits. Its `inverse` then takes only the historical binding and transaction plus `owner.exclusive`; it never asks a backend to authenticate current inverse authority. In fixtures the original session is already revoked and Helm rollback nevertheless succeeds. A durable authenticated transaction proves provenance, not current permission. Retaining the same human principal is compatible with a fresh operation session; the revoked original session cannot authorize that new operation. + +Three independent regressions fail against the original candidate and pass against the separate scratch correction: deny inverse after successful hand-over revocation; deny automatic failure unwind after revocation while retaining confirmed pending work; require fresh scoped session entry and revocation around a permitted inverse. The original 13 tests independently pass unchanged. Runs use explicit clean environments, frozen local source imports, no network and synthetic callbacks. Failed and corrected outputs are retained as `original-authority-tests.txt` and `patched-authority-tests.txt`. + +`fresh-inverse-session.candidate.patch` adds a mandatory `owner.inverse_session(binding, transaction)` context before the release lock. This is a proposed backend interface, not an implemented credential boundary. T05 must supply authenticated operation-specific evidence, exact scope and current disposition, durable attempt/outcome records and final revocation on every exit. A missing fresh authorized attended session must leave the inverse pending with original error context; it cannot be replaced with a no-op context or reuse of the hand-over session. Existing historical authorization can cover that inverse scope where explicitly established; a new user permission ceremony is not implied. The new context models the required runtime authority, not a demand for another workflow. + +Candidate type auth and owner-contract text must explicitly say: “Every configuration inverse runs as a distinct fresh authorized attended owner operation, with its own exact transaction/case/source/release binding and per-attempt revocation. The already revoked hand-over parent session is never reused. Missing current authority retains pending inverse/reconciliation evidence.” The separate patch intentionally does not edit canonical type/owner files or invent owner assent. + +## Placement, exclusion and recovery + +The proposed wrapper/inverse placement solves the reachability defect at source-design level: it covers public clients with delivery skipped and confidential delivery NO_CHANGE, and verification/receipt failures before receipt creation. Keeping receipt rollback receipt-only and delivery cleanup created-only preserves the existing authority boundaries. Preexisting configuration remains NO_CHANGE; reference deactivation is separately required before created delivery cleanup. Actual 1.3 C3 placed the inverse in owner_remove; this proposal needs explicit owner adoption of its revised placement and cannot inherit C3 assent. + +The current Forgejo Make target performs a regular Helm upgrade, has prerequisite production applies, and mixes values inputs including a secret-producing helper. It supplies no durable transaction, authenticated configuration projection, operation provenance or all-writer exclusion. T05 must publish a narrowly reviewed source/configuration projection preserving local login and unrelated changes; it cannot classify an entire ordinary release upgrade as this case's exclusive configuration mutation. No values, rendered manifests or helper outputs were executed/read during this review. + +The candidate correctly requires all release writers excluded across read-current and rollback. Helm has no CAS here. A local lock that only this wrapper honors is insufficient: manual Helm, automation and any reconciliation writer must share an enforced release boundary or an attended owner-controlled release freeze covering the critical section. Source review accepts that requirement; no real boundary is implemented or verified by this packet. A raw revision check followed by rollback remains rejected. + +The pending-intent/confirmed/restored model safely refuses unproven mutation ownership and drift. Loss after configuration but before confirmation, or rollback but before durable inverse confirmation, still requires owner reconciliation against authenticated history and operation/source binding. T05 must retain inverse-attempt intent/outcome, classify ambiguous outcomes and read back the approved before configuration; it must not replay a rollback, infer NO_CHANGE or claim whole-case success while publication/receipt uncertainty remains. Recovery is plausible within the existing T05 implementation, but its durable backend is absent. + +## Concrete existing T05 work remaining + +Implement and publish the wrapper entry plus independently reachable inverse, the mandatory fresh inverse-operation session lifecycle, authenticated durable journal/recovery, exact accepted source/configuration binding and all-writer release exclusion. Reconcile the revised C3 type prose with actual Apps owner acceptance. Complete real owner_apply/owner_remove with authenticated created UIDs and reference-deactivation proof, all four observed Forgejo probe records, and the committed evidence store/revert behavior. Preserve T01 break-glass, T02/T03 login/group verification and the existing attended production authority. Exact source publication, kernel/catalog acceptance and separately authorized native evidence remain unfinished; no eighth step, standing credential, new grant or additional workplan is proposed. diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-review-r1/fresh-inverse-session.candidate.patch b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-review-r1/fresh-inverse-session.candidate.patch new file mode 100644 index 0000000..c0772c0 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-review-r1/fresh-inverse-session.candidate.patch @@ -0,0 +1,16 @@ +--- a/owner_configuration_contract.py ++++ b/owner_configuration_contract.py +@@ -11,7 +11,12 @@ + + def inverse(binding, transaction, owner): + """Owner unwind entry, reachable without any delivery/hand-over creation.""" +- with owner.exclusive(binding): ++ # This is a distinct operation: hand-over has already revoked its session. ++ # Backend must authenticate a fresh attended inverse attempt, exact operation, ++ # principal/owner/case/source/release/transaction scope and current disposition; ++ # durably retain authority + outcome and revoke this attempt on every exit. ++ # Durable configuration provenance alone is never execution authority. ++ with owner.inverse_session(binding, transaction), owner.exclusive(binding): + record = owner.authenticated_record(transaction) + if record['binding'] != binding: + raise Refused('configuration transaction binding differs') diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-review-r1/original-authority-tests.txt b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-review-r1/original-authority-tests.txt new file mode 100644 index 0000000..b38e05a --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-review-r1/original-authority-tests.txt @@ -0,0 +1,43 @@ +test_revoked_handover_session_cannot_authorize_later_inverse (__main__.InverseAuthorityTests.test_revoked_handover_session_cannot_authorize_later_inverse) ... FAIL +test_failure_unwind_must_not_reuse_revoked_handover_session (__main__.InverseAuthorityTests.test_failure_unwind_must_not_reuse_revoked_handover_session) ... FAIL +test_fresh_scoped_inverse_session_closed_after_restore (__main__.InverseAuthorityTests.test_fresh_scoped_inverse_session_closed_after_restore) ... FAIL + +====================================================================== +FAIL: test_revoked_handover_session_cannot_authorize_later_inverse (__main__.InverseAuthorityTests.test_revoked_handover_session_cannot_authorize_later_inverse) +---------------------------------------------------------------------- +Traceback (most recent call last): + File "/tmp/nk48-apps-f3-review/test_inverse_authority.py", line 21, in test_revoked_handover_session_cannot_authorize_later_inverse + with self.assertRaisesRegex(Refused, 'fresh attended'): +AssertionError: Refused not raised + +====================================================================== +FAIL: test_failure_unwind_must_not_reuse_revoked_handover_session (__main__.InverseAuthorityTests.test_failure_unwind_must_not_reuse_revoked_handover_session) +---------------------------------------------------------------------- +warden.case_receipt_state.Refused: consumer verification contract differs + +During handling of the above exception, another exception occurred: + +Traceback (most recent call last): + File "/tmp/nk48-apps-f3-review/test_inverse_authority.py", line 34, in test_failure_unwind_must_not_reuse_revoked_handover_session + with self.assertRaisesRegex(Refused, 'fresh attended'): +AssertionError: "fresh attended" does not match "consumer verification contract differs" + +====================================================================== +FAIL: test_fresh_scoped_inverse_session_closed_after_restore (__main__.InverseAuthorityTests.test_fresh_scoped_inverse_session_closed_after_restore) +---------------------------------------------------------------------- +Traceback (most recent call last): + File "/tmp/nk48-apps-f3-review/test_inverse_authority.py", line 54, in test_fresh_scoped_inverse_session_closed_after_restore + self.assertEqual(events, ['fresh-scoped-authority', 'inverse-session-revoked']) +AssertionError: Lists differ: [] != ['fresh-scoped-authority', 'inverse-session-revoked'] + +Second list contains 2 additional elements. +First extra element 0: +'fresh-scoped-authority' + +- [] ++ ['fresh-scoped-authority', 'inverse-session-revoked'] + +---------------------------------------------------------------------- +Ran 3 tests in 0.013s + +FAILED (failures=3) diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-review-r1/patched-authority-tests.txt b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-review-r1/patched-authority-tests.txt new file mode 100644 index 0000000..b09ead6 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-review-r1/patched-authority-tests.txt @@ -0,0 +1,8 @@ +test_revoked_handover_session_cannot_authorize_later_inverse (__main__.InverseAuthorityTests.test_revoked_handover_session_cannot_authorize_later_inverse) ... ok +test_failure_unwind_must_not_reuse_revoked_handover_session (__main__.InverseAuthorityTests.test_failure_unwind_must_not_reuse_revoked_handover_session) ... ok +test_fresh_scoped_inverse_session_closed_after_restore (__main__.InverseAuthorityTests.test_fresh_scoped_inverse_session_closed_after_restore) ... ok + +---------------------------------------------------------------------- +Ran 3 tests in 0.015s + +OK diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-review-r1/review.json b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-review-r1/review.json new file mode 100644 index 0000000..9f4e287 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-review-r1/review.json @@ -0,0 +1,177 @@ +{ + "schema": "custodian.nk48-r5-f3-apps-source-review.v1", + "verdict": "needs-change", + "reviewer": "independent coordinator-delegated Apps-scoped source reviewer; not Apps owner assent", + "apps_head": "944b30ad63ab054cc24617e374e177d34cd572da", + "net_kingdom_head": "3236d5000e52e0643f33906a26a7825aa0b67465", + "finding": "RAPPS-F3-SESSION-1: inverse lacks distinct fresh attended owner operation authorization and revocation lifecycle", + "placement": "source-design accepted conditionally; not installed/owner accepted/native proven", + "native_actions": false, + "canonical_edits": false, + "new_grants": false, + "hashes": [ + { + "repo": "net-kingdom", + "path": "AGENTS.md", + "sha256": "95158b1d5c15e6cf8be980f5c0e3b48aeef252654846273887f40516b3fa6a78", + "matches": true + }, + { + "repo": "net-kingdom", + "path": "INTENT.md", + "sha256": "fd2ba8dff9dfd5583ec6d8b0348f273bfe00c59923b5d5d47cd22b1fb4658c54", + "matches": true + }, + { + "repo": "net-kingdom", + "path": "SECURITY-COMPANION.md", + "sha256": "06374445ca888549f1bf79527d1345d94918d7a7ec363160d8427d0e7a31d643", + "matches": true + }, + { + "repo": "net-kingdom", + "path": "sso-mfa/k8s/keycape/handover-adapter.py", + "sha256": "bad7c1132258f2140b0d83548031ac86eecea4f4019f34adedc1e9e3774a3832", + "matches": true + }, + { + "repo": "net-kingdom", + "path": "sso-mfa/k8s/keycape/deliver-consumer-secret-adapter.py", + "sha256": "65db5386cdf08a1aa932886c046abed20330a36c96e63d4267adb8341b23da42", + "matches": true + }, + { + "repo": "net-kingdom", + "path": "security-case-type-revisions/1.3/oidc-browser-relying-party/type.yaml", + "sha256": "e75fe94110ef9fb7fb18072fe2856941b33507c99cb406d36576046481485d0a", + "matches": true + }, + { + "repo": "net-kingdom", + "path": "security-case-type-revisions/1.3/oidc-browser-relying-party/examples/example-public-portal.yaml", + "sha256": "2b30b9403ba124514e3ab8777b187a225099e2f7683fdd41d0ad48ef3d9649a7", + "matches": true + }, + { + "repo": "net-kingdom", + "path": "security-case-type-revisions/1.3/oidc-browser-relying-party/examples/forgejo.yaml", + "sha256": "546aad5cf26842c484fc6ba99940d56697b85531afea11872556761008923c94", + "matches": true + }, + { + "repo": "railiance-apps", + "path": "AGENTS.md", + "sha256": "2a366a5fbb69ba235df97ee117153cbfa1af11ab579372751b4fb5dbb30c31a7", + "matches": true + }, + { + "repo": "railiance-apps", + "path": "INTENT.md", + "sha256": "31060dc1738aa4540f78735019359a276ece39dad8afbf1be740713bebbe65e5", + "matches": true + }, + { + "repo": "railiance-apps", + "path": "Makefile", + "sha256": "a319e264e6f04378f6b3d2c7781860fc4bac1abdb248cffa8c26065e08f2f290", + "matches": true + }, + { + "repo": "railiance-apps", + "path": "workplans/RAPPS-WP-0016-forgejo-netkingdom-login.md", + "sha256": "1077e9de358433bf1316e3db1459dd271f82875b9590bf3d7b5b22c0df42bbda", + "matches": true + }, + { + "repo": "railiance-apps", + "path": "docs/evidence/nk48-t16-owner-review/owner-review-railiance-apps.json", + "sha256": "67e9ded8d37462ba04a0a224bac03a840d0c94326235474e1d3371c8ff61865d", + "matches": true + }, + { + "repo": "railiance-apps", + "path": "docs/proposals/2026-10-11-forgejo-consumer-eso/owner-design.md", + "sha256": "c523949454e4c6eb58e8caee382380a561f561704136de62bf5783c592f962f2", + "matches": true + }, + { + "repo": "the-custodian", + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-adapters-1.2-review/coordinator-review-r5.json", + "sha256": "e3016352c58178894ccbfc600b960d3f9e2b5d534a05b8df2c5b79737cb0127a", + "matches": true + }, + { + "repo": "the-custodian", + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-1.3-owner-review/owner-review-railiance-apps.json", + "sha256": "aedca85f3a742f6510c45aa17ade7375b93c46069cf4e5a7119bec9765efbd7d", + "matches": true + }, + { + "repo": "candidate-packet", + "path": "README.md", + "sha256": "bff0eb23ce4920da6058bf71a3dace209baca69a97c94bd9db4f765f71044eb2" + }, + { + "repo": "candidate-packet", + "path": "implementation.json", + "sha256": "fce244e7a8bd1f1b9911c632de4831de291f358375183ff7959dc5615560f6d0" + }, + { + "repo": "candidate-packet", + "path": "owner-contract-candidate.md", + "sha256": "dfdc6573df76647790aabb26e4b228b04047dffd032fc33821024f8bbc9ba941" + }, + { + "repo": "candidate-packet", + "path": "owner_configuration_contract.py", + "sha256": "62752f518b78fa7713bdb4d47e68decbf3aa89ecbc7950549ffdc3d08e01c554" + }, + { + "repo": "candidate-packet", + "path": "test_owner_configuration_contract.py", + "sha256": "51482fc5bad6af5c52d736ded7e7031291b18bc4949b9ff43d7dea78f3a8ad94" + }, + { + "repo": "candidate-packet", + "path": "type.candidate.yaml", + "sha256": "739c927ab494f4e0f6618e6e55394dd8dc4b720db428cd4973f7a81524e1ecd9" + }, + { + "repo": "candidate-packet", + "path": "type-owner-configuration-inverse.candidate.patch", + "sha256": "6e23967d768759fc10203bcc25d81cb32c3d9222ed6fcb4a510a7cfe90e579bf" + } + ], + "tests": [ + { + "variant": "original", + "exit_code": 1, + "command": [ + "/home/worsch/ops-warden/.venv/bin/python", + "-c", + "import runpy; runpy.run_path('/tmp/nk48-apps-f3-review/test_inverse_authority.py', run_name='__main__')" + ], + "environment": { + "PATH": "/usr/bin:/bin", + "PYTHONDONTWRITEBYTECODE": "1", + "NK_FROZEN_ROOT": "/tmp/nk48-r5-f1-ahudwokt/net-kingdom", + "PYTHONPATH": "/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1:/tmp/nk48-r5-f1-ahudwokt/warden-frozen/src" + } + }, + { + "variant": "patched", + "exit_code": 0, + "command": [ + "/home/worsch/ops-warden/.venv/bin/python", + "-c", + "import runpy; runpy.run_path('/tmp/nk48-apps-f3-review/test_inverse_authority.py', run_name='__main__')" + ], + "environment": { + "PATH": "/usr/bin:/bin", + "PYTHONDONTWRITEBYTECODE": "1", + "NK_FROZEN_ROOT": "/tmp/nk48-r5-f1-ahudwokt/net-kingdom", + "PYTHONPATH": "/tmp/nk48-apps-f3-review:/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1:/tmp/nk48-r5-f1-ahudwokt/warden-frozen/src" + } + } + ] +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-review-r1/test_inverse_authority.py b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-review-r1/test_inverse_authority.py new file mode 100644 index 0000000..a0da13d --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-review-r1/test_inverse_authority.py @@ -0,0 +1,63 @@ +"""Independent synthetic regression: authenticated history is not fresh authority.""" +from contextlib import contextmanager +import unittest +from test_owner_configuration_contract import OwnerFlowTests +from owner_configuration_contract import inverse +from warden.case_receipt_state import Refused + + +class InverseAuthorityTests(OwnerFlowTests): + # Reuse fixtures, without rediscovering the original suite here. + def test_revoked_handover_session_cannot_authorize_later_inverse(self): + self.setup_flow() + transaction, _ = self.flow() + self.assertEqual(self.revoked, [self.inv]) + before = self.owner.current.copy() + @contextmanager + def deny(binding, ident): + raise Refused('fresh attended inverse session absent') + yield + self.owner.inverse_session = deny + with self.assertRaisesRegex(Refused, 'fresh attended'): + inverse(self.binding, transaction, self.owner) + self.assertEqual(self.owner.current, before) + + def test_failure_unwind_must_not_reuse_revoked_handover_session(self): + self.setup_flow() + self.fail_verify = True + @contextmanager + def deny(binding, ident): + self.assertEqual(self.revoked, [self.inv]) + raise Refused('fresh attended inverse session absent') + yield + self.owner.inverse_session = deny + with self.assertRaisesRegex(Refused, 'fresh attended'): + self.flow() + self.assertEqual(self.owner.current['configuration']['oidc'], self.owner.desired) + self.assertEqual(next(iter(self.owner.records.values()))['state'], 'confirmed') + + def test_fresh_scoped_inverse_session_closed_after_restore(self): + self.setup_flow() + transaction, _ = self.flow() + events = [] + @contextmanager + def fresh(binding, ident): + self.assertEqual(binding, self.binding) + self.assertEqual(ident, transaction) + events.append('fresh-scoped-authority') + try: + yield + finally: + events.append('inverse-session-revoked') + self.owner.inverse_session = fresh + inverse(self.binding, transaction, self.owner) + self.assertEqual(events, ['fresh-scoped-authority', 'inverse-session-revoked']) + + +if __name__ == '__main__': + suite = unittest.TestSuite(InverseAuthorityTests(name) for name in ( + 'test_revoked_handover_session_cannot_authorize_later_inverse', + 'test_failure_unwind_must_not_reuse_revoked_handover_session', + 'test_fresh_scoped_inverse_session_closed_after_restore')) + result = unittest.TextTestRunner(verbosity=2).run(suite) + raise SystemExit(not result.wasSuccessful()) diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-source-followthrough-r1/apply_prepare.py b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-source-followthrough-r1/apply_prepare.py new file mode 100644 index 0000000..b10253c --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-source-followthrough-r1/apply_prepare.py @@ -0,0 +1,26 @@ +from pathlib import Path +import hashlib,json,subprocess,sys +p=Path(__file__).resolve().parent;r=Path('/home/worsch/net-kingdom') +pre=json.loads((p/'preimage.json').read_text());name='workplans/NK-WP-0048-security-case-types.md' +def git(*a):return subprocess.check_output(['git','-C',str(r),*a],stderr=subprocess.PIPE,text=True).strip() +assert git('rev-parse','HEAD')==pre['base_revision'] +assert hashlib.sha256((r/name).read_bytes()).hexdigest()==pre['workplan_sha256'] +assert hashlib.sha256((r/'sso-mfa/k8s/keycape/test_admit_1_3_type.py').read_bytes()).hexdigest()==pre['test_sha256'] +sys.path.insert(0,'/home/worsch/repo-manager/src') +from repo_manager.identifiers import ensure_missing_work_record_identifiers +old=ensure_missing_work_record_identifiers(r,execute=False) +assert old['ok'] and old['assignments']==[] +candidate=(p/'workplan.candidate.md').read_bytes() +assert hashlib.sha256(candidate).hexdigest()==pre['candidate_sha256'] +(r/name).write_bytes(candidate) +preview=ensure_missing_work_record_identifiers(r,execute=False) +(p/'identifier-preview.json').write_text(json.dumps(preview,indent=2)+'\n') +assert preview['ok'] and preview['files_changed']==[name] +assert {x['record_id'] for x in preview['assignments']}=={'NK-WP-0048-T32','NK-WP-0048-T33','NK-WP-0048-T34'} +actual=ensure_missing_work_record_identifiers(r,execute=True) +assert actual['assignments']==preview['assignments'] +(p/'identifier-assignment.json').write_text(json.dumps(actual,indent=2)+'\n') +(p/'workplan.allocated.md').write_bytes((r/name).read_bytes()) +cfg={'owner':'net-kingdom','base_revision':pre['base_revision'],'message':'test: restore independent mint admission coverage and refine remaining fixes','files':[{'path':f,'sha256':hashlib.sha256((r/f).read_bytes()).hexdigest()} for f in [name,'sso-mfa/k8s/keycape/test_admit_1_3_type.py']]} +(p/'publication-inputs.json').write_text(json.dumps(cfg,indent=2)+'\n') +print(json.dumps({'allocated':len(actual['assignments']),'task_count':34,'files':2})) diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-source-followthrough-r1/consistency.json b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-source-followthrough-r1/consistency.json new file mode 100644 index 0000000..a21cd7b --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-source-followthrough-r1/consistency.json @@ -0,0 +1,376 @@ +{ + "repo_slug": "net-kingdom", + "repo_path": "/home/worsch/net-kingdom", + "source_revision": "342a6f25af5cdc602841948f42759ea52ef1c8f5", + "source_revision_reason": null, + "issues": [ + { + "severity": "WARN", + "check_id": "C-24", + "message": "Repo classification advisory: capability_tag 'security' is not a recommended family tag (allowed, check for synonym)", + "file_path": "", + "db_id": "", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-35", + "message": "Repo Manager warning: workplans/: repository uses more than one workplan prefix: ['NET-WP', 'NK-WP']", + "file_path": "workplans/", + "db_id": "", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-33", + "message": "WORK-RECORDS.md is missing or stale \u2014 regenerate via fix-consistency (CUST-WP-0061-T04 generated work-record index)", + "file_path": "WORK-RECORDS.md", + "db_id": "", + "file_value": "", + "db_value": "", + "fixable": true + }, + { + "severity": "WARN", + "check_id": "C-37", + "message": "Residual workplan 'nk-wp-0011' has no origin/origin_ref provenance (not invented)", + "file_path": "workplans/NK-WP-0011-enterprise-federation-saml.md", + "db_id": "1075448f-d533-5f9e-94b7-c3adfe151a07", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-20", + "message": "Workplan dependency target 'USER-WP-0020' is not linked to State Hub", + "file_path": "workplans/NK-WP-0022-railiance01-identity-cutover-and-coulombcore-retirement.md", + "db_id": "", + "file_value": "USER-WP-0020", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-20", + "message": "Workplan dependency target 'KEY-WP-0004' is not linked to State Hub", + "file_path": "workplans/NK-WP-0022-railiance01-identity-cutover-and-coulombcore-retirement.md", + "db_id": "", + "file_value": "KEY-WP-0004", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-20", + "message": "Workplan dependency target 'USER-WP-0020' is not linked to State Hub", + "file_path": "workplans/NK-WP-0023-user-engine-portal-platform-integration.md", + "db_id": "", + "file_value": "USER-WP-0020", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-20", + "message": "Workplan dependency target 'KEY-WP-0004' is not linked to State Hub", + "file_path": "workplans/NK-WP-0023-user-engine-portal-platform-integration.md", + "db_id": "", + "file_value": "KEY-WP-0004", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-20", + "message": "Workplan dependency target 'USER-WP-0021' is not linked to State Hub", + "file_path": "workplans/NK-WP-0024-user-engine-portal-integration-expansion.md", + "db_id": "", + "file_value": "USER-WP-0021", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-20", + "message": "Workplan dependency target 'FLEX-WP-0009' is not linked to State Hub", + "file_path": "workplans/NK-WP-0024-user-engine-portal-integration-expansion.md", + "db_id": "", + "file_value": "FLEX-WP-0009", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-20", + "message": "Workplan dependency target 'AUDIT-WP-0003' is not linked to State Hub", + "file_path": "workplans/NK-WP-0024-user-engine-portal-integration-expansion.md", + "db_id": "", + "file_value": "AUDIT-WP-0003", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-20", + "message": "Workplan dependency target 'EMAIL-WP-0004' is not linked to State Hub", + "file_path": "workplans/NK-WP-0024-user-engine-portal-integration-expansion.md", + "db_id": "", + "file_value": "EMAIL-WP-0004", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-20", + "message": "Workplan dependency target 'USER-WP-0022' is not linked to State Hub", + "file_path": "workplans/NK-WP-0025-public-self-registration-and-application-jit.md", + "db_id": "", + "file_value": "USER-WP-0022", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-20", + "message": "Workplan dependency target 'KEY-WP-0008' is not linked to State Hub", + "file_path": "workplans/NK-WP-0025-public-self-registration-and-application-jit.md", + "db_id": "", + "file_value": "KEY-WP-0008", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-20", + "message": "Workplan dependency target 'CSOC-WP-0003' is not linked to State Hub", + "file_path": "workplans/NK-WP-0025-public-self-registration-and-application-jit.md", + "db_id": "", + "file_value": "CSOC-WP-0003", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-37", + "message": "Residual workplan 'nk-wp-0027' has no origin/origin_ref provenance (not invented)", + "file_path": "workplans/NK-WP-0027-reef-placement-reconciliation.md", + "db_id": "965ad365-6b81-50a1-a2a3-2d0c1fcce0b4", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-37", + "message": "Residual workplan 'nk-wp-0031' has no origin/origin_ref provenance (not invented)", + "file_path": "workplans/NK-WP-0031-deterministic-posture-feedback.md", + "db_id": "9d7b04f9-3803-5613-b7a5-8bd606c77f5a", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-37", + "message": "Residual workplan 'nk-wp-0040' has no origin/origin_ref provenance (not invented)", + "file_path": "workplans/NK-WP-0040-execution-attribution-receipt.md", + "db_id": "e2533f3a-aa43-59b3-bff3-8e64b6149487", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-40", + "message": "blocker satisfied, task still wait: 'NK-WP-0048-T15' in workplan 'NK-WP-0048' \u2014 every depends_on target is terminal (NK-WP-0048-T31)", + "file_path": "workplans/NK-WP-0048-security-case-types.md#NK-WP-0048-T15", + "db_id": "", + "file_value": "NK-WP-0048-T31", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-22", + "message": "Task description drift 'NK-WP-0048-T16': file content differs from DB (file wins)", + "file_path": "workplans/NK-WP-0048-security-case-types.md#NK-WP-0048-T16", + "db_id": "200bf500-5084-5ae7-be26-3033fe1f296e", + "file_value": "2026-10-17 coordinator review r4 approved the 1.3 candidate at 1920b13 with no\nfindings (the-custodian `nk48-adapters-1.", + "db_value": "2026-10-17 coordinator review r4 approved the 1.3 candidate at 1920b13 with no\nfindings (the-custodian `nk48-adapters-1.", + "fixable": true + }, + { + "severity": "WARN", + "check_id": "C-11", + "message": "Derived task 'NK-WP-0048-T32' is absent from this projection", + "file_path": "workplans/NK-WP-0048-security-case-types.md#NK-WP-0048-T32", + "db_id": "c6d0a27e-f6a7-5df0-95f6-3e7473d38bb5", + "file_value": "", + "db_value": "", + "fixable": true + }, + { + "severity": "WARN", + "check_id": "C-11", + "message": "Derived task 'NK-WP-0048-T33' is absent from this projection", + "file_path": "workplans/NK-WP-0048-security-case-types.md#NK-WP-0048-T33", + "db_id": "5ace28ff-a4cb-5647-a8f6-3cd54bb9d0a5", + "file_value": "", + "db_value": "", + "fixable": true + }, + { + "severity": "WARN", + "check_id": "C-11", + "message": "Derived task 'NK-WP-0048-T34' is absent from this projection", + "file_path": "workplans/NK-WP-0048-security-case-types.md#NK-WP-0048-T34", + "db_id": "37f43c40-d014-544e-aa0b-5219dd4a6b93", + "file_value": "", + "db_value": "", + "fixable": true + }, + { + "severity": "WARN", + "check_id": "C-20", + "message": "Workplan dependency target 'RAIL-PL-WP-0002' is not linked to State Hub", + "file_path": "workplans/archived/260702-NET-WP-0017-it-security-readiness-for-user-onboarding.md", + "db_id": "", + "file_value": "RAIL-PL-WP-0002", + "db_value": "", + "fixable": false + }, + { + "severity": "INFO", + "check_id": "C-08", + "message": "Retired DB workstream 'nk-wp-0035@retired-20260908' (id=064c5e8b\u2026, status=archived) has no backing workplan file \u2014 projection tombstone, not live work", + "file_path": "", + "db_id": "064c5e8b-7852-4d7f-a5fa-e26e406ab8e0", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "INFO", + "check_id": "C-08", + "message": "Retired DB workstream 'adhoc-2026-08-14@retired-20260827' (id=a5097121\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work", + "file_path": "", + "db_id": "a5097121-1461-59f8-81fa-e6ecef527337", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "INFO", + "check_id": "C-08", + "message": "Retired DB workstream 'adhoc-2026-08-23@retired-20260827' (id=e25ba5fa\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work", + "file_path": "", + "db_id": "e25ba5fa-eaae-5039-8d93-a69c6d837fc4", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "INFO", + "check_id": "C-08", + "message": "Retired DB workstream 'net-kingdom-adhoc-2026-07-02@retired-20260827' (id=67c1c7ac\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work", + "file_path": "", + "db_id": "67c1c7ac-d8b1-41dc-a81a-4e43f1afd068", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "INFO", + "check_id": "C-08", + "message": "Retired DB workstream 'net-kingdom-adhoc-2026-06-14@retired-20260827' (id=c6f3d6bf\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work", + "file_path": "", + "db_id": "c6f3d6bf-4916-490d-96ce-092d2776d7e7", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-26", + "message": "Workplan id 'NET-WP-0020' uses prefix 'NET-WP' but repo canonical prefix is 'NK-WP' \u2014 new plans must conform", + "file_path": "workplans/NET-WP-0020-openbao-unseal-custody-and-ssh-automation.md", + "db_id": "", + "file_value": "NET-WP-0020", + "db_value": "NK-WP", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-26", + "message": "Workplan id 'NET-WP-0015' uses prefix 'NET-WP' but repo canonical prefix is 'NK-WP' \u2014 new plans must conform", + "file_path": "workplans/archived/260603-NET-WP-0015-platform-root-custody-and-openbao-identity-bootstrap.md", + "db_id": "", + "file_value": "NET-WP-0015", + "db_value": "NK-WP", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-26", + "message": "Workplan id 'NET-WP-0016' uses prefix 'NET-WP' but repo canonical prefix is 'NK-WP' \u2014 new plans must conform", + "file_path": "workplans/archived/260603-NET-WP-0016-guided-security-bootstrap-experience.md", + "db_id": "", + "file_value": "NET-WP-0016", + "db_value": "NK-WP", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-26", + "message": "Workplan id 'NET-WP-0018' uses prefix 'NET-WP' but repo canonical prefix is 'NK-WP' \u2014 new plans must conform", + "file_path": "workplans/archived/260604-NET-WP-0018-bootstrap-automation-and-rebuild-readiness.md", + "db_id": "", + "file_value": "NET-WP-0018", + "db_value": "NK-WP", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-26", + "message": "Workplan id 'NET-WP-0017' uses prefix 'NET-WP' but repo canonical prefix is 'NK-WP' \u2014 new plans must conform", + "file_path": "workplans/archived/260702-NET-WP-0017-it-security-readiness-for-user-onboarding.md", + "db_id": "", + "file_value": "NET-WP-0017", + "db_value": "NK-WP", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-26", + "message": "Workplan id 'NET-WP-0019' uses prefix 'NET-WP' but repo canonical prefix is 'NK-WP' \u2014 new plans must conform", + "file_path": "workplans/archived/260702-NET-WP-0019-t06-adjacent-user-lifecycle-dry-run-polish.md", + "db_id": "", + "file_value": "NET-WP-0019", + "db_value": "NK-WP", + "fixable": false + } + ], + "fixes_applied": [ + "bindings: synced 64/64", + "C-17 cleared: pushed 1 backlogged commit(s)", + "C-33 fixed: regenerated WORK-RECORDS.md", + "C-22 fixed: task 200bf500\u2026 description updated", + "C-11 fixed: task 'NK-WP-0048-T32' \u2192 c6d0a27e\u2026", + "C-11 fixed: task 'NK-WP-0048-T33' \u2192 5ace28ff\u2026", + "C-11 fixed: task 'NK-WP-0048-T34' \u2192 37f43c40\u2026", + "brief: .custodian-brief.md updated", + "push: pushed" + ], + "summary": { + "fail": 0, + "automation_error": 0, + "warn": 30, + "info": 5 + }, + "result": "warn" +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-source-followthrough-r1/consistency.stderr.txt b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-source-followthrough-r1/consistency.stderr.txt new file mode 100644 index 0000000..3345563 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-source-followthrough-r1/consistency.stderr.txt @@ -0,0 +1 @@ +WARN: git commit failed for writeback: Generated-only commits rejected: staged diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-source-followthrough-r1/identifier-assignment.json b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-source-followthrough-r1/identifier-assignment.json new file mode 100644 index 0000000..579356f --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-source-followthrough-r1/identifier-assignment.json @@ -0,0 +1,32 @@ +{ + "schema": "repo-manager.identifier-assignment.v1", + "ok": true, + "executed": true, + "repo": "net-kingdom", + "namespace": "helixforge", + "assignments": [ + { + "kind": "task", + "record_id": "NK-WP-0048-T32", + "uuid": "c6d0a27e-f6a7-5df0-95f6-3e7473d38bb5", + "path": "workplans/NK-WP-0048-security-case-types.md" + }, + { + "kind": "task", + "record_id": "NK-WP-0048-T33", + "uuid": "5ace28ff-a4cb-5647-a8f6-3cd54bb9d0a5", + "path": "workplans/NK-WP-0048-security-case-types.md" + }, + { + "kind": "task", + "record_id": "NK-WP-0048-T34", + "uuid": "37f43c40-d014-544e-aa0b-5219dd4a6b93", + "path": "workplans/NK-WP-0048-security-case-types.md" + } + ], + "historical_invalid_identifiers": [], + "historical_identity_collisions": [], + "files_changed": [ + "workplans/NK-WP-0048-security-case-types.md" + ] +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-source-followthrough-r1/identifier-preview.json b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-source-followthrough-r1/identifier-preview.json new file mode 100644 index 0000000..535fc3e --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-source-followthrough-r1/identifier-preview.json @@ -0,0 +1,32 @@ +{ + "schema": "repo-manager.identifier-assignment.v1", + "ok": true, + "executed": false, + "repo": "net-kingdom", + "namespace": "helixforge", + "assignments": [ + { + "kind": "task", + "record_id": "NK-WP-0048-T32", + "uuid": "c6d0a27e-f6a7-5df0-95f6-3e7473d38bb5", + "path": "workplans/NK-WP-0048-security-case-types.md" + }, + { + "kind": "task", + "record_id": "NK-WP-0048-T33", + "uuid": "5ace28ff-a4cb-5647-a8f6-3cd54bb9d0a5", + "path": "workplans/NK-WP-0048-security-case-types.md" + }, + { + "kind": "task", + "record_id": "NK-WP-0048-T34", + "uuid": "37f43c40-d014-544e-aa0b-5219dd4a6b93", + "path": "workplans/NK-WP-0048-security-case-types.md" + } + ], + "historical_invalid_identifiers": [], + "historical_identity_collisions": [], + "files_changed": [ + "workplans/NK-WP-0048-security-case-types.md" + ] +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-source-followthrough-r1/preimage.json b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-source-followthrough-r1/preimage.json new file mode 100644 index 0000000..3cb0352 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-source-followthrough-r1/preimage.json @@ -0,0 +1,18 @@ +{ + "base_revision": "3236d5000e52e0643f33906a26a7825aa0b67465", + "workplan_sha256": "1502f1f474856172d969489cab8c0642d9456d93d326f6660df3772a4716d30b", + "candidate_sha256": "f1e5245f565da2cc35b5e2d0adfcad42325c64205525d3f96171e604d8935708", + "test_sha256": "c512ba00513d066fd1ca8af08fd6bf106fbd7a132981b1dcc5af80a745ab03f0", + "files": [ + { + "path": ".custodian-brief.md", + "sha256": "3eb7f91f2443090c9b859a716d83711310364897b6144dcf10d3254342fec3d1", + "index": "100644 f60f8bad019e23e6f7e0e074745da0bb2fcbb870 0\t.custodian-brief.md" + }, + { + "path": "WORK-RECORDS.md", + "sha256": "a1fec217a6e380f528778d781dbea1fc5a836fdefa1dd4dd0308100fbe08112a", + "index": "100644 76a6b7fc304c508f1b50010cb72d892826c6a831 0\tWORK-RECORDS.md" + } + ] +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-source-followthrough-r1/projection.json b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-source-followthrough-r1/projection.json new file mode 100644 index 0000000..a4db5d6 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-source-followthrough-r1/projection.json @@ -0,0 +1,991 @@ +{ + "ok": true, + "status": "applied", + "repo_slug": "net-kingdom", + "commit": "342a6f25af5cdc602841948f42759ea52ef1c8f5", + "requests": 2, + "elapsed_ms": 6645.0, + "git": { + "upstream": "origin/main", + "behind": 0, + "ahead": 0 + }, + "identifiers": { + "schema": "repo-manager.identifier-assignment.v1", + "ok": true, + "executed": true, + "repo": "net-kingdom", + "namespace": "helixforge", + "assignments": [], + "historical_invalid_identifiers": [], + "historical_identity_collisions": [], + "files_changed": [] + }, + "receipt": { + "schema": "state-hub.repository-projection-reconcile.v1", + "instance_role": "primary", + "instance_label": "railiance01", + "expected_commit": "342a6f25af5cdc602841948f42759ea52ef1c8f5", + "derived_commit": "342a6f25af5cdc602841948f42759ea52ef1c8f5", + "outcome": { + "schema": "state-hub.projection-reset.v1", + "repo_slug": "net-kingdom", + "commit": "342a6f25af5cdc602841948f42759ea52ef1c8f5", + "status": "applied", + "counts": { + "created": 0, + "updated": 64, + "retired": 0, + "refused": 0, + "released": 0, + "created_tasks": 0, + "updated_tasks": 3, + "cancelled_tasks": 0, + "created_edges": 0, + "removed_edges": 0, + "unresolved_edges": 1, + "external_edges": 85 + }, + "created": [], + "updated": [ + "NET-WP-0015", + "NET-WP-0016", + "NET-WP-0017", + "NET-WP-0018", + "NET-WP-0019", + "NET-WP-0020", + "NK-WP-0001", + "NK-WP-0002", + "NK-WP-0003", + "NK-WP-0004", + "NK-WP-0005", + "NK-WP-0006", + "NK-WP-0007", + "NK-WP-0008", + "NK-WP-0009", + "NK-WP-0010", + "NK-WP-0011", + "NK-WP-0012", + "NK-WP-0013", + "NK-WP-0014", + "NK-WP-0015", + "NK-WP-0016", + "NK-WP-0017", + "NK-WP-0018", + "NK-WP-0019", + "NK-WP-0021", + "NK-WP-0022", + "NK-WP-0023", + "NK-WP-0024", + "NK-WP-0025", + "NK-WP-0026", + "NK-WP-0027", + "NK-WP-0028", + "NK-WP-0029", + "NK-WP-0030", + "NK-WP-0031", + "NK-WP-0032", + "NK-WP-0033", + "NK-WP-0034", + "NK-WP-0035", + "NK-WP-0036", + "NK-WP-0037", + "NK-WP-0038", + "NK-WP-0039", + "NK-WP-0040", + "NK-WP-0041", + "NK-WP-0042", + "NK-WP-0043", + "NK-WP-0044", + "NK-WP-0045", + "NK-WP-0046", + "NK-WP-0047", + "NK-WP-0048", + "NK-WP-0049", + "NK-WP-0050", + "NK-WP-0051", + "NK-WP-ADHOC-2026-06-14", + "NK-WP-ADHOC-2026-07-02", + "NK-WP-ADHOC-2026-08-14", + "NK-WP-ADHOC-2026-08-23", + "NK-WP-ADHOC-2026-10-03", + "NK-WP-ADHOC-2026-10-10", + "NK-WP-ADHOC-2026-10-11", + "NK-WP-ADHOC-2026-10-16" + ], + "retired": [], + "released": [], + "created_tasks": [], + "updated_tasks": [ + "NK-WP-0048-T32", + "NK-WP-0048-T33", + "NK-WP-0048-T34" + ], + "cancelled_tasks": [], + "created_edges": [], + "removed_edges": [], + "unresolved_edges": [ + { + "workplan": "NK-WP-0006", + "from": "NK-WP-0006", + "target": "NK-WP-0001", + "reason": "missing" + } + ], + "external_edges": [ + { + "workplan": "NK-WP-0008", + "from": "NK-WP-0008", + "target": "NK-WP-0006", + "relationship_type": "blocks", + "from_workplan_id": "053c6d96-9396-40c9-a2e5-c36531e7810d", + "from_task_id": null, + "to_workplan_id": "2eb8a5e0-4e33-4ed3-8996-a2eec3aad862", + "to_task_id": null + }, + { + "workplan": "NK-WP-0050", + "from": "NK-WP-0050-T03", + "target": "RPF-WP-0056-T03", + "relationship_type": "starts_after", + "from_workplan_id": "06ba98d5-609e-5aaa-9bf7-4c4f4460e377", + "from_task_id": "5a3aaf57-de5c-5a03-bc99-9bcf2fc5d95e", + "to_workplan_id": null, + "to_task_id": "a82762eb-bb6d-5600-89ca-a81ada2eba9f" + }, + { + "workplan": "NK-WP-0050", + "from": "NK-WP-0050-T03", + "target": "NK-WP-0050-T02", + "relationship_type": "starts_after", + "from_workplan_id": "06ba98d5-609e-5aaa-9bf7-4c4f4460e377", + "from_task_id": "5a3aaf57-de5c-5a03-bc99-9bcf2fc5d95e", + "to_workplan_id": null, + "to_task_id": "a9dadd07-f136-5dc7-9229-7a9dbb941f0d" + }, + { + "workplan": "NK-WP-0050", + "from": "NK-WP-0050-T02", + "target": "NK-WP-0050-T01", + "relationship_type": "starts_after", + "from_workplan_id": "06ba98d5-609e-5aaa-9bf7-4c4f4460e377", + "from_task_id": "a9dadd07-f136-5dc7-9229-7a9dbb941f0d", + "to_workplan_id": null, + "to_task_id": "64893d94-9717-5812-b8e3-7cabe095fcdc" + }, + { + "workplan": "NK-WP-0017", + "from": "NK-WP-0017", + "target": "NK-WP-0015", + "relationship_type": "blocks", + "from_workplan_id": "08398d26-cadf-44bc-97ee-67da790040e6", + "from_task_id": null, + "to_workplan_id": "343e035d-41e1-4fc4-b209-6872e4a5fdc8", + "to_task_id": null + }, + { + "workplan": "NK-WP-0011", + "from": "NK-WP-0011", + "target": "NK-WP-0006", + "relationship_type": "blocks", + "from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07", + "from_task_id": null, + "to_workplan_id": "2eb8a5e0-4e33-4ed3-8996-a2eec3aad862", + "to_task_id": null + }, + { + "workplan": "NK-WP-0011", + "from": "NK-WP-0011", + "target": "NK-WP-0004", + "relationship_type": "blocks", + "from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07", + "from_task_id": null, + "to_workplan_id": "d9cf7c4b-886b-4cd1-ad7b-99c4e1929c9e", + "to_task_id": null + }, + { + "workplan": "NK-WP-0011", + "from": "NK-WP-0011", + "target": "NK-WP-0003", + "relationship_type": "blocks", + "from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07", + "from_task_id": null, + "to_workplan_id": "f24cefd4-a09b-4fa1-9b25-94bf783b425e", + "to_task_id": null + }, + { + "workplan": "NK-WP-0011", + "from": "NK-WP-0011-T03", + "target": "NK-WP-0011-T02", + "relationship_type": "starts_after", + "from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07", + "from_task_id": "32d1411b-10a4-5a8b-8f43-99ac46d83908", + "to_workplan_id": null, + "to_task_id": "2fe6f100-3a5d-563e-b033-7d7b846ae487" + }, + { + "workplan": "NK-WP-0011", + "from": "NK-WP-0011-T07", + "target": "NK-WP-0011-T03", + "relationship_type": "starts_after", + "from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07", + "from_task_id": "38998c68-29bf-50d2-8c8d-0c75184d5833", + "to_workplan_id": null, + "to_task_id": "32d1411b-10a4-5a8b-8f43-99ac46d83908" + }, + { + "workplan": "NK-WP-0011", + "from": "NK-WP-0011-T07", + "target": "NK-WP-0011-T04", + "relationship_type": "starts_after", + "from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07", + "from_task_id": "38998c68-29bf-50d2-8c8d-0c75184d5833", + "to_workplan_id": null, + "to_task_id": "6697a994-7343-5ea8-8b37-bc3b921e5a9b" + }, + { + "workplan": "NK-WP-0011", + "from": "NK-WP-0011-T07", + "target": "NK-WP-0011-T05", + "relationship_type": "starts_after", + "from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07", + "from_task_id": "38998c68-29bf-50d2-8c8d-0c75184d5833", + "to_workplan_id": null, + "to_task_id": "d845380d-3dbc-5e59-8f2b-5a4d1b32d89d" + }, + { + "workplan": "NK-WP-0011", + "from": "NK-WP-0011-T04", + "target": "NK-WP-0011-T03", + "relationship_type": "starts_after", + "from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07", + "from_task_id": "6697a994-7343-5ea8-8b37-bc3b921e5a9b", + "to_workplan_id": null, + "to_task_id": "32d1411b-10a4-5a8b-8f43-99ac46d83908" + }, + { + "workplan": "NK-WP-0011", + "from": "NK-WP-0011-T06", + "target": "NK-WP-0011-T03", + "relationship_type": "starts_after", + "from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07", + "from_task_id": "9cb7fd93-c16e-5102-83ce-195b3aa87446", + "to_workplan_id": null, + "to_task_id": "32d1411b-10a4-5a8b-8f43-99ac46d83908" + }, + { + "workplan": "NK-WP-0011", + "from": "NK-WP-0011-T06", + "target": "NK-WP-0011-T04", + "relationship_type": "starts_after", + "from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07", + "from_task_id": "9cb7fd93-c16e-5102-83ce-195b3aa87446", + "to_workplan_id": null, + "to_task_id": "6697a994-7343-5ea8-8b37-bc3b921e5a9b" + }, + { + "workplan": "NK-WP-0011", + "from": "NK-WP-0011-T06", + "target": "NK-WP-0011-T05", + "relationship_type": "starts_after", + "from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07", + "from_task_id": "9cb7fd93-c16e-5102-83ce-195b3aa87446", + "to_workplan_id": null, + "to_task_id": "d845380d-3dbc-5e59-8f2b-5a4d1b32d89d" + }, + { + "workplan": "NK-WP-0011", + "from": "NK-WP-0011-T05", + "target": "NK-WP-0011-T04", + "relationship_type": "starts_after", + "from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07", + "from_task_id": "d845380d-3dbc-5e59-8f2b-5a4d1b32d89d", + "to_workplan_id": null, + "to_task_id": "6697a994-7343-5ea8-8b37-bc3b921e5a9b" + }, + { + "workplan": "NK-WP-0011", + "from": "NK-WP-0011-T09", + "target": "NK-WP-0011-T03", + "relationship_type": "starts_after", + "from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07", + "from_task_id": "d9dc4292-6b66-5797-9ff9-a1a4a556278e", + "to_workplan_id": null, + "to_task_id": "32d1411b-10a4-5a8b-8f43-99ac46d83908" + }, + { + "workplan": "NK-WP-0011", + "from": "NK-WP-0011-T09", + "target": "NK-WP-0011-T07", + "relationship_type": "starts_after", + "from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07", + "from_task_id": "d9dc4292-6b66-5797-9ff9-a1a4a556278e", + "to_workplan_id": null, + "to_task_id": "38998c68-29bf-50d2-8c8d-0c75184d5833" + }, + { + "workplan": "NK-WP-0011", + "from": "NK-WP-0011-T09", + "target": "NK-WP-0011-T08", + "relationship_type": "starts_after", + "from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07", + "from_task_id": "d9dc4292-6b66-5797-9ff9-a1a4a556278e", + "to_workplan_id": null, + "to_task_id": "e5f44ddc-9645-5f61-b84c-da5ab9cccb6d" + }, + { + "workplan": "NK-WP-0011", + "from": "NK-WP-0011-T08", + "target": "NK-WP-0011-T02", + "relationship_type": "starts_after", + "from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07", + "from_task_id": "e5f44ddc-9645-5f61-b84c-da5ab9cccb6d", + "to_workplan_id": null, + "to_task_id": "2fe6f100-3a5d-563e-b033-7d7b846ae487" + }, + { + "workplan": "NK-WP-0011", + "from": "NK-WP-0011-T08", + "target": "NK-WP-0011-T03", + "relationship_type": "starts_after", + "from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07", + "from_task_id": "e5f44ddc-9645-5f61-b84c-da5ab9cccb6d", + "to_workplan_id": null, + "to_task_id": "32d1411b-10a4-5a8b-8f43-99ac46d83908" + }, + { + "workplan": "NET-WP-0016", + "from": "NET-WP-0016", + "target": "NET-WP-0015", + "relationship_type": "blocks", + "from_workplan_id": "16069174-6698-4855-ad9e-5092c8571f38", + "from_task_id": null, + "to_workplan_id": "6b9c25e4-1008-429a-8de6-54361872c0dd", + "to_task_id": null + }, + { + "workplan": "NET-WP-0016", + "from": "NET-WP-0016", + "target": "NK-WP-0012", + "relationship_type": "blocks", + "from_workplan_id": "16069174-6698-4855-ad9e-5092c8571f38", + "from_task_id": null, + "to_workplan_id": "9b8e4afc-eb71-47d9-8750-799a082b320a", + "to_task_id": null + }, + { + "workplan": "NK-WP-0049", + "from": "NK-WP-0049-T18", + "target": "NK-WP-0049-T33", + "relationship_type": "starts_after", + "from_workplan_id": "17d315f0-829e-57c7-85cc-3a7914152804", + "from_task_id": "141dd773-c1fa-56c1-a852-bbe2372e5125", + "to_workplan_id": null, + "to_task_id": "56c8d469-7a10-5d50-a30e-95cbf3cd4756" + }, + { + "workplan": "NK-WP-0049", + "from": "NK-WP-0049-T18", + "target": "NK-WP-0049-T32", + "relationship_type": "starts_after", + "from_workplan_id": "17d315f0-829e-57c7-85cc-3a7914152804", + "from_task_id": "141dd773-c1fa-56c1-a852-bbe2372e5125", + "to_workplan_id": null, + "to_task_id": "cd78dbb7-a8f2-5754-8788-6eb1b7d97741" + }, + { + "workplan": "NK-WP-0049", + "from": "NK-WP-0049-T35", + "target": "NK-WP-0049-T34", + "relationship_type": "starts_after", + "from_workplan_id": "17d315f0-829e-57c7-85cc-3a7914152804", + "from_task_id": "1bfd1703-589f-5511-bad5-812d860e4a71", + "to_workplan_id": null, + "to_task_id": "821b3a56-6f36-5812-abc9-5a00e37cb2e7" + }, + { + "workplan": "NK-WP-0049", + "from": "NK-WP-0049-T33", + "target": "NK-WP-0049-T35", + "relationship_type": "starts_after", + "from_workplan_id": "17d315f0-829e-57c7-85cc-3a7914152804", + "from_task_id": "56c8d469-7a10-5d50-a30e-95cbf3cd4756", + "to_workplan_id": null, + "to_task_id": "1bfd1703-589f-5511-bad5-812d860e4a71" + }, + { + "workplan": "NK-WP-0049", + "from": "NK-WP-0049-T33", + "target": "NK-WP-0049-T34", + "relationship_type": "starts_after", + "from_workplan_id": "17d315f0-829e-57c7-85cc-3a7914152804", + "from_task_id": "56c8d469-7a10-5d50-a30e-95cbf3cd4756", + "to_workplan_id": null, + "to_task_id": "821b3a56-6f36-5812-abc9-5a00e37cb2e7" + }, + { + "workplan": "NK-WP-0049", + "from": "NK-WP-0049-T33", + "target": "NK-WP-0049-T36", + "relationship_type": "starts_after", + "from_workplan_id": "17d315f0-829e-57c7-85cc-3a7914152804", + "from_task_id": "56c8d469-7a10-5d50-a30e-95cbf3cd4756", + "to_workplan_id": null, + "to_task_id": "8b72334b-fc27-5707-a189-bad345eaf3bc" + }, + { + "workplan": "NK-WP-0049", + "from": "NK-WP-0049-T33", + "target": "NK-WP-0049-T32", + "relationship_type": "starts_after", + "from_workplan_id": "17d315f0-829e-57c7-85cc-3a7914152804", + "from_task_id": "56c8d469-7a10-5d50-a30e-95cbf3cd4756", + "to_workplan_id": null, + "to_task_id": "cd78dbb7-a8f2-5754-8788-6eb1b7d97741" + }, + { + "workplan": "NK-WP-0049", + "from": "NK-WP-0049-T36", + "target": "NK-WP-0049-T34", + "relationship_type": "starts_after", + "from_workplan_id": "17d315f0-829e-57c7-85cc-3a7914152804", + "from_task_id": "8b72334b-fc27-5707-a189-bad345eaf3bc", + "to_workplan_id": null, + "to_task_id": "821b3a56-6f36-5812-abc9-5a00e37cb2e7" + }, + { + "workplan": "NK-WP-0049", + "from": "NK-WP-0049-T03", + "target": "NK-WP-0049-T02", + "relationship_type": "starts_after", + "from_workplan_id": "17d315f0-829e-57c7-85cc-3a7914152804", + "from_task_id": "d68cd4d3-1ba3-530e-88f9-26ebb8dd6eac", + "to_workplan_id": null, + "to_task_id": "479d6970-6516-50f5-9436-bb5b56d58aad" + }, + { + "workplan": "NK-WP-0025", + "from": "NK-WP-0025", + "target": "NK-WP-0024", + "relationship_type": "blocks", + "from_workplan_id": "2412a251-d6b5-5f05-8082-3eb7697c51dc", + "from_task_id": null, + "to_workplan_id": "635a73d4-b3cb-5c38-af22-56a6a66beb0e", + "to_task_id": null + }, + { + "workplan": "NK-WP-0016", + "from": "NK-WP-0016", + "target": "NK-WP-0015", + "relationship_type": "blocks", + "from_workplan_id": "2d592e18-e63d-4856-97a1-f8c3e019e150", + "from_task_id": null, + "to_workplan_id": "343e035d-41e1-4fc4-b209-6872e4a5fdc8", + "to_task_id": null + }, + { + "workplan": "NK-WP-0006", + "from": "NK-WP-0006", + "target": "NK-WP-0005", + "relationship_type": "blocks", + "from_workplan_id": "2eb8a5e0-4e33-4ed3-8996-a2eec3aad862", + "from_task_id": null, + "to_workplan_id": "75bc472b-cc0a-48f2-afb6-62b896f7cc19", + "to_task_id": null + }, + { + "workplan": "NK-WP-0006", + "from": "NK-WP-0006", + "target": "NK-WP-0004", + "relationship_type": "blocks", + "from_workplan_id": "2eb8a5e0-4e33-4ed3-8996-a2eec3aad862", + "from_task_id": null, + "to_workplan_id": "d9cf7c4b-886b-4cd1-ad7b-99c4e1929c9e", + "to_task_id": null + }, + { + "workplan": "NK-WP-0013", + "from": "NK-WP-0013", + "target": "NK-WP-0006", + "relationship_type": "blocks", + "from_workplan_id": "32a54d8e-8633-42a6-8ec1-104842c581c1", + "from_task_id": null, + "to_workplan_id": "2eb8a5e0-4e33-4ed3-8996-a2eec3aad862", + "to_task_id": null + }, + { + "workplan": "NK-WP-0015", + "from": "NK-WP-0015", + "target": "NK-WP-0014", + "relationship_type": "blocks", + "from_workplan_id": "343e035d-41e1-4fc4-b209-6872e4a5fdc8", + "from_task_id": null, + "to_workplan_id": "b9bf56bc-7ef6-43eb-badc-fa0f4896c63c", + "to_task_id": null + }, + { + "workplan": "NET-WP-0017", + "from": "NET-WP-0017", + "target": "NET-WP-0016", + "relationship_type": "blocks", + "from_workplan_id": "385de708-fd59-4bab-a4f4-28c1c476b3ea", + "from_task_id": null, + "to_workplan_id": "16069174-6698-4855-ad9e-5092c8571f38", + "to_task_id": null + }, + { + "workplan": "NET-WP-0017", + "from": "NET-WP-0017", + "target": "NET-WP-0015", + "relationship_type": "blocks", + "from_workplan_id": "385de708-fd59-4bab-a4f4-28c1c476b3ea", + "from_task_id": null, + "to_workplan_id": "6b9c25e4-1008-429a-8de6-54361872c0dd", + "to_task_id": null + }, + { + "workplan": "NK-WP-0007", + "from": "NK-WP-0007", + "target": "NK-WP-0006", + "relationship_type": "blocks", + "from_workplan_id": "3cbc81ec-7ad5-46cf-a4a0-fc5fe9873695", + "from_task_id": null, + "to_workplan_id": "2eb8a5e0-4e33-4ed3-8996-a2eec3aad862", + "to_task_id": null + }, + { + "workplan": "NK-WP-0007", + "from": "NK-WP-0007", + "target": "NK-WP-0005", + "relationship_type": "blocks", + "from_workplan_id": "3cbc81ec-7ad5-46cf-a4a0-fc5fe9873695", + "from_task_id": null, + "to_workplan_id": "75bc472b-cc0a-48f2-afb6-62b896f7cc19", + "to_task_id": null + }, + { + "workplan": "NK-WP-0007", + "from": "NK-WP-0007", + "target": "NK-WP-0004", + "relationship_type": "blocks", + "from_workplan_id": "3cbc81ec-7ad5-46cf-a4a0-fc5fe9873695", + "from_task_id": null, + "to_workplan_id": "d9cf7c4b-886b-4cd1-ad7b-99c4e1929c9e", + "to_task_id": null + }, + { + "workplan": "NK-WP-0024", + "from": "NK-WP-0024", + "target": "NK-WP-0023", + "relationship_type": "blocks", + "from_workplan_id": "635a73d4-b3cb-5c38-af22-56a6a66beb0e", + "from_task_id": null, + "to_workplan_id": "fdb3deee-06c3-5d12-91ce-9c50b2ac0618", + "to_task_id": null + }, + { + "workplan": "NET-WP-0015", + "from": "NET-WP-0015", + "target": "NK-WP-0006", + "relationship_type": "blocks", + "from_workplan_id": "6b9c25e4-1008-429a-8de6-54361872c0dd", + "from_task_id": null, + "to_workplan_id": "2eb8a5e0-4e33-4ed3-8996-a2eec3aad862", + "to_task_id": null + }, + { + "workplan": "NET-WP-0015", + "from": "NET-WP-0015", + "target": "NK-WP-0012", + "relationship_type": "blocks", + "from_workplan_id": "6b9c25e4-1008-429a-8de6-54361872c0dd", + "from_task_id": null, + "to_workplan_id": "9b8e4afc-eb71-47d9-8750-799a082b320a", + "to_task_id": null + }, + { + "workplan": "NK-WP-0018", + "from": "NK-WP-0018", + "target": "NK-WP-0017", + "relationship_type": "blocks", + "from_workplan_id": "6f75035a-e056-4eab-8fdb-00a18bacdf87", + "from_task_id": null, + "to_workplan_id": "08398d26-cadf-44bc-97ee-67da790040e6", + "to_task_id": null + }, + { + "workplan": "NK-WP-0018", + "from": "NK-WP-0018", + "target": "NK-WP-0016", + "relationship_type": "blocks", + "from_workplan_id": "6f75035a-e056-4eab-8fdb-00a18bacdf87", + "from_task_id": null, + "to_workplan_id": "2d592e18-e63d-4856-97a1-f8c3e019e150", + "to_task_id": null + }, + { + "workplan": "NK-WP-0005", + "from": "NK-WP-0005", + "target": "NK-WP-0004", + "relationship_type": "blocks", + "from_workplan_id": "75bc472b-cc0a-48f2-afb6-62b896f7cc19", + "from_task_id": null, + "to_workplan_id": "d9cf7c4b-886b-4cd1-ad7b-99c4e1929c9e", + "to_task_id": null + }, + { + "workplan": "NET-WP-0019", + "from": "NET-WP-0019", + "target": "NET-WP-0017", + "relationship_type": "blocks", + "from_workplan_id": "75d388b6-7ec1-4e1b-8c87-6ff44f953210", + "from_task_id": null, + "to_workplan_id": "385de708-fd59-4bab-a4f4-28c1c476b3ea", + "to_task_id": null + }, + { + "workplan": "NET-WP-0019", + "from": "NET-WP-0019", + "target": "NET-WP-0018", + "relationship_type": "blocks", + "from_workplan_id": "75d388b6-7ec1-4e1b-8c87-6ff44f953210", + "from_task_id": null, + "to_workplan_id": "800f9f16-bc44-4bbf-a771-58a630a3b698", + "to_task_id": null + }, + { + "workplan": "NET-WP-0018", + "from": "NET-WP-0018", + "target": "NET-WP-0017", + "relationship_type": "blocks", + "from_workplan_id": "800f9f16-bc44-4bbf-a771-58a630a3b698", + "from_task_id": null, + "to_workplan_id": "385de708-fd59-4bab-a4f4-28c1c476b3ea", + "to_task_id": null + }, + { + "workplan": "NET-WP-0018", + "from": "NET-WP-0018", + "target": "NET-WP-0015", + "relationship_type": "blocks", + "from_workplan_id": "800f9f16-bc44-4bbf-a771-58a630a3b698", + "from_task_id": null, + "to_workplan_id": "6b9c25e4-1008-429a-8de6-54361872c0dd", + "to_task_id": null + }, + { + "workplan": "NK-WP-0027", + "from": "NK-WP-0027-T03", + "target": "NK-WP-0027-T02", + "relationship_type": "starts_after", + "from_workplan_id": "965ad365-6b81-50a1-a2a3-2d0c1fcce0b4", + "from_task_id": "e3f0bb0f-0c85-5cb9-a5ab-75f668cd3447", + "to_workplan_id": null, + "to_task_id": "5b35b646-a525-55da-852a-6005613d42ea" + }, + { + "workplan": "NK-WP-0012", + "from": "NK-WP-0012", + "target": "NK-WP-0006", + "relationship_type": "blocks", + "from_workplan_id": "9b8e4afc-eb71-47d9-8750-799a082b320a", + "from_task_id": null, + "to_workplan_id": "2eb8a5e0-4e33-4ed3-8996-a2eec3aad862", + "to_task_id": null + }, + { + "workplan": "NK-WP-0014", + "from": "NK-WP-0014", + "target": "NK-WP-0013", + "relationship_type": "blocks", + "from_workplan_id": "b9bf56bc-7ef6-43eb-badc-fa0f4896c63c", + "from_task_id": null, + "to_workplan_id": "32a54d8e-8633-42a6-8ec1-104842c581c1", + "to_task_id": null + }, + { + "workplan": "NK-WP-0014", + "from": "NK-WP-0014", + "target": "NK-WP-0012", + "relationship_type": "blocks", + "from_workplan_id": "b9bf56bc-7ef6-43eb-badc-fa0f4896c63c", + "from_task_id": null, + "to_workplan_id": "9b8e4afc-eb71-47d9-8750-799a082b320a", + "to_task_id": null + }, + { + "workplan": "NK-WP-0019", + "from": "NK-WP-0019", + "target": "NK-WP-0018", + "relationship_type": "blocks", + "from_workplan_id": "d2daa8b4-8ecf-4377-b382-492e653735f7", + "from_task_id": null, + "to_workplan_id": "6f75035a-e056-4eab-8fdb-00a18bacdf87", + "to_task_id": null + }, + { + "workplan": "NK-WP-0009", + "from": "NK-WP-0009", + "target": "NK-WP-0008", + "relationship_type": "blocks", + "from_workplan_id": "d4d02dbf-3974-502d-8b87-b776fc63e17e", + "from_task_id": null, + "to_workplan_id": "053c6d96-9396-40c9-a2e5-c36531e7810d", + "to_task_id": null + }, + { + "workplan": "NK-WP-0022", + "from": "NK-WP-0022", + "target": "NK-WP-0023", + "relationship_type": "blocks", + "from_workplan_id": "d76ddccc-00c8-548a-b141-2cd660fa38da", + "from_task_id": null, + "to_workplan_id": "fdb3deee-06c3-5d12-91ce-9c50b2ac0618", + "to_task_id": null + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T15", + "target": "NK-WP-0048-T31", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "1b825705-4848-5edc-832f-b1622cd9bdb7", + "to_workplan_id": null, + "to_task_id": "ed1ccd87-89a0-5e69-885c-0eaf5ab0bb5a" + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T16", + "target": "NK-WP-0048-T25", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "200bf500-5084-5ae7-be26-3033fe1f296e", + "to_workplan_id": null, + "to_task_id": "0de2a7d9-874d-578b-a076-f653fe42e30b" + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T16", + "target": "NK-WP-0048-T29", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "200bf500-5084-5ae7-be26-3033fe1f296e", + "to_workplan_id": null, + "to_task_id": "129db41d-4560-5e20-9f3b-c3c91e0a5601" + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T16", + "target": "NK-WP-0048-T24", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "200bf500-5084-5ae7-be26-3033fe1f296e", + "to_workplan_id": null, + "to_task_id": "1a3931ee-3b5f-52fc-9295-4073804f4bd2" + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T16", + "target": "NK-WP-0048-T28", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "200bf500-5084-5ae7-be26-3033fe1f296e", + "to_workplan_id": null, + "to_task_id": "6dab281c-ea9a-5a17-a83f-e8e9a4d4484f" + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T16", + "target": "NK-WP-0048-T30", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "200bf500-5084-5ae7-be26-3033fe1f296e", + "to_workplan_id": null, + "to_task_id": "6f6073ac-52dd-5b70-8874-6d76d4c072e4" + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T16", + "target": "NK-WP-0048-T26", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "200bf500-5084-5ae7-be26-3033fe1f296e", + "to_workplan_id": null, + "to_task_id": "94b3cbf0-c908-5ac6-b0db-67385c3e021c" + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T16", + "target": "NK-WP-0048-T27", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "200bf500-5084-5ae7-be26-3033fe1f296e", + "to_workplan_id": null, + "to_task_id": "e56365f8-29b5-5c89-a329-8115f9f3b2a7" + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T07", + "target": "NK-WP-0048-T06", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "c3c9c3ae-4f23-5ba7-9e6f-e63bcf29b9f7", + "to_workplan_id": null, + "to_task_id": "d9b26bcf-a7c6-59a2-92a0-f913d76a203c" + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T06", + "target": "NK-WP-0048-T09", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "d9b26bcf-a7c6-59a2-92a0-f913d76a203c", + "to_workplan_id": null, + "to_task_id": "0ee12dfc-8260-5038-bd39-cc7fb4113be0" + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T06", + "target": "NK-WP-0048-T15", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "d9b26bcf-a7c6-59a2-92a0-f913d76a203c", + "to_workplan_id": null, + "to_task_id": "1b825705-4848-5edc-832f-b1622cd9bdb7" + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T06", + "target": "NK-WP-0048-T16", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "d9b26bcf-a7c6-59a2-92a0-f913d76a203c", + "to_workplan_id": null, + "to_task_id": "200bf500-5084-5ae7-be26-3033fe1f296e" + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T06", + "target": "NK-WP-0048-T05", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "d9b26bcf-a7c6-59a2-92a0-f913d76a203c", + "to_workplan_id": null, + "to_task_id": "3d987d3d-0797-538b-86e4-51185a327ae2" + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T06", + "target": "MASON-WP-0010-T03", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "d9b26bcf-a7c6-59a2-92a0-f913d76a203c", + "to_workplan_id": null, + "to_task_id": "56cea6be-c613-586c-80cd-125340b374da" + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T06", + "target": "NK-WP-0048-T12", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "d9b26bcf-a7c6-59a2-92a0-f913d76a203c", + "to_workplan_id": null, + "to_task_id": "7ce4eee3-cd47-55a9-bfa5-4f1835d4d6fd" + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T06", + "target": "NK-WP-0048-T20", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "d9b26bcf-a7c6-59a2-92a0-f913d76a203c", + "to_workplan_id": null, + "to_task_id": "863fcbdb-e246-51ba-87ca-a2f44d01d6f8" + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T06", + "target": "NK-WP-0048-T17", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "d9b26bcf-a7c6-59a2-92a0-f913d76a203c", + "to_workplan_id": null, + "to_task_id": "8c3e4baa-a218-5996-af9e-fe1153a46dac" + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T06", + "target": "NK-WP-0048-T08", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "d9b26bcf-a7c6-59a2-92a0-f913d76a203c", + "to_workplan_id": null, + "to_task_id": "8e519082-dc8b-5e46-b660-03d03e55dcf6" + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T06", + "target": "NK-WP-0048-T18", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "d9b26bcf-a7c6-59a2-92a0-f913d76a203c", + "to_workplan_id": null, + "to_task_id": "9c45da71-fb4c-5f20-9065-90f448f37c64" + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T06", + "target": "NK-WP-0048-T13", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "d9b26bcf-a7c6-59a2-92a0-f913d76a203c", + "to_workplan_id": null, + "to_task_id": "b264c10b-565c-5d29-b788-03cf5fbd2883" + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T06", + "target": "WARDEN-WP-0044-T03", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "d9b26bcf-a7c6-59a2-92a0-f913d76a203c", + "to_workplan_id": null, + "to_task_id": "bcbf6c91-2c04-5c16-ae8c-5d6c06fd6b89" + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T06", + "target": "NK-WP-0048-T14", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "d9b26bcf-a7c6-59a2-92a0-f913d76a203c", + "to_workplan_id": null, + "to_task_id": "fce95440-a25b-5f27-8ef3-a3d182099113" + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T14", + "target": "NK-WP-0048-T20", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "fce95440-a25b-5f27-8ef3-a3d182099113", + "to_workplan_id": null, + "to_task_id": "863fcbdb-e246-51ba-87ca-a2f44d01d6f8" + }, + { + "workplan": "NK-WP-0010", + "from": "NK-WP-0010", + "target": "NK-WP-0008", + "relationship_type": "blocks", + "from_workplan_id": "f4faf8b4-ae57-40cf-a881-6fe66ca6ad74", + "from_task_id": null, + "to_workplan_id": "053c6d96-9396-40c9-a2e5-c36531e7810d", + "to_task_id": null + } + ], + "refused": [], + "notes": [] + } + } +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-source-followthrough-r1/projection.stderr.txt b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-source-followthrough-r1/projection.stderr.txt new file mode 100644 index 0000000..e69de29 diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-source-followthrough-r1/publication-commit.json b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-source-followthrough-r1/publication-commit.json new file mode 100644 index 0000000..82fc225 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-source-followthrough-r1/publication-commit.json @@ -0,0 +1,17 @@ +{ + "exit_code": 0, + "commit": "342a6f25af5cdc602841948f42759ea52ef1c8f5", + "base": "3236d5000e52e0643f33906a26a7825aa0b67465", + "files": [ + { + "path": "workplans/NK-WP-0048-security-case-types.md", + "sha256": "f92962c5aa21aade0fc2f6b4f8d9a95932fe85b107413cdd2824b6ebe72c36d9" + }, + { + "path": "sso-mfa/k8s/keycape/test_admit_1_3_type.py", + "sha256": "c512ba00513d066fd1ca8af08fd6bf106fbd7a132981b1dcc5af80a745ab03f0" + } + ], + "stdout": "[main 342a6f2] test: restore independent mint admission coverage and refine remaining fixes\n 2 files changed, 115 insertions(+), 7 deletions(-)\n", + "stderr": "{\"blocked_blob_ids\": [], \"scanned_blobs\": 2, \"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/nk48-r5-source-followthrough-r1/publication-inputs.json b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-source-followthrough-r1/publication-inputs.json new file mode 100644 index 0000000..8fc0726 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-source-followthrough-r1/publication-inputs.json @@ -0,0 +1,15 @@ +{ + "owner": "net-kingdom", + "base_revision": "3236d5000e52e0643f33906a26a7825aa0b67465", + "message": "test: restore independent mint admission coverage and refine remaining fixes", + "files": [ + { + "path": "workplans/NK-WP-0048-security-case-types.md", + "sha256": "f92962c5aa21aade0fc2f6b4f8d9a95932fe85b107413cdd2824b6ebe72c36d9" + }, + { + "path": "sso-mfa/k8s/keycape/test_admit_1_3_type.py", + "sha256": "c512ba00513d066fd1ca8af08fd6bf106fbd7a132981b1dcc5af80a745ab03f0" + } + ] +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-source-followthrough-r1/workplan.allocated.md b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-source-followthrough-r1/workplan.allocated.md new file mode 100644 index 0000000..16914fc --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-source-followthrough-r1/workplan.allocated.md @@ -0,0 +1,1609 @@ +--- +id: NK-WP-0048 +type: workplan +title: "Establish SecurityCaseTypes so that known security cases are light" +domain: infotech +repo: net-kingdom +status: active +flavor: implementation +owner: claude-code +topic_slug: netkingdom +created: "2026-10-08" +updated: "2026-10-17" +depends_on: [] +related_repos: + - ops-mason + - ops-warden + - key-cape + - railiance-platform + - informed-decision +state_hub_workstream_id: "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" +--- + +# NK-WP-0048 - Establish SecurityCaseTypes so that known security cases are light + +## Why + +The founder's direction (2026-10-08): setting up a security integration of a +kind that has been done before should be easy, meaning accept the case, then +ops-mason arranges it and ops-warden follows through. Setting up a new kind +needs ample documentation and tests and an informed-decision, because its shape +is part of the security architecture. + +Today every integration is hand-built: per-client helper scripts, per-lane +custody, founder checklists. The trigger was KEY-WP-0038 (Forgejo as an OIDC +client), whose next step was going to be yet another hand-written rollout +script. + +This plan continues the pattern work of NK-WP-0008 (pattern infospace), +NK-WP-0009 (tutorials) and NK-WP-0030 (scenario composition). It turns patterns +into **SecurityCaseTypes** that ops-mason and ops-warden can execute. Canon: +`canon/standards/security-case-types_v0.1.md` (proposed). + +Companion plans: ops-mason `MASON-WP-0010` (materialization) and ops-warden +`WARDEN-WP-0044` (follow-through). + +## Tasks + +```task +id: NK-WP-0048-T01 +status: done +priority: high +state_hub_task_id: "930ac3e9-20ec-5473-98cd-3fe8fddb730f" +``` + +### T01 — Accept the terminology and the canon + +Founder review of `canon/standards/security-case-types_v0.1.md`, including its +section 10 open questions. On acceptance the canon status goes from `proposed` +to `accepted`. + +2026-10-08: accepted by the founder (in session). Canon status `accepted`; +section 10's open questions are carried forward to the first type (T04, T05). + +```task +id: NK-WP-0048-T02 +status: done +priority: high +state_hub_task_id: "19c25d96-a55c-58f0-9168-3fdbc4955fc2" +``` + +### T02 — Inventory the de-facto types already built by hand + +Read-only. List every security integration built so far and group them by shape. +Known starting points: + +- browser OIDC relying parties on key-cape: informed-decision-approver, + coulomb-social, user-engine portal, openbao-admin, and Forgejo (pending) +- confidential service clients with `client_credentials`: secrets-engine + approval, requester and openbao clients, approval-engine operator +- workload KV read lanes on OpenBao: the railiance-platform credential lane + designs and CCRs, for example `railiance-backup-offsite-lane` +- short-lived SSH certificates through ops-warden (`warden sign`) +- tunnelled operator access (ops-bridge) + +For each: the repositories involved, the hand-built steps, attended human steps, +custody paths, verification evidence, and rollback (if any). Output +`docs/security-case-type-inventory.md`, ranking candidate types by how often the +shape recurs and how much manual work each instance costs today. + +2026-10-08: done, `docs/security-case-type-inventory.md`. 36 of 37 CCRs are +already `request_type: workload-kv-read`, a de-facto type reviewed each time as +if it were new. Browser clients: 5 hand-built plus Forgejo, each with its own +rollout script. Confidential service clients need a registration plus two or +three KV-read CCRs each. A forge read identity is needed in bulk by +ORGREF-WP-0002-T06. Recommendation: oidc-browser-relying-party first (unblocks +the forge), workload-kv-read second (largest payoff), then forge-read-identity. + +```task +id: NK-WP-0048-T03 +status: done +depends_on: [NK-WP-0048-T01] +priority: high +state_hub_task_id: "e487975b-73b1-516c-971d-da4cbbb93063" +``` + +### T03 — Schema, template and validator + +- `canon/schemas/security-case-type_v0.1.schema.json` and + `security-case_v0.1.schema.json` +- `security-case-types/_template/` with `type.yaml`, `README.md` and tests to copy +- a validator, extending `tools/security-scenario-composer/`, that checks a type, + checks a case against its type, and renders the case into the composer's + deterministic plan (still `execution.permitted: false`) + +Acceptance: the validator rejects missing sections, secret-shaped parameter +values, unpinned providers and cases against non-`implemented` types, with tests +for each. + +2026-10-08: done. `canon/schemas/security-case-type_v0.1.schema.json` and +`security-case_v0.1.schema.json`; `tools/security-case-types/security_case_validator.py` +(`validate-type`, `validate-case`, `render`, reusing the scenario composer); +`security-case-types/_template/` (a valid, runnable example type over +`net-kingdom.local-identity`, with README sections, an example case and tests that +travel with every copy). 31 tests pass, including the composer's existing suite. + +```task +id: NK-WP-0048-T04 +status: done +depends_on: [NK-WP-0048-T02, NK-WP-0048-T03] +priority: high +state_hub_task_id: "2fffe112-7d55-5680-9952-78d74f44f87f" +``` + +### T04 — Define the first type: browser OIDC relying party + +`security-case-types/oidc-browser-relying-party/`, version 1.0: + +- parameters: client id, display name, exact redirect URIs, scopes, client type + (confidential or public), MFA pin, the groups the consuming service maps, + custody path for the secret (confidential only), consuming service and its + owner +- composition: key-cape client registration (key-cape declares it as a playbook + capability), secret minting into custody (railiance-platform), LLDAP group + existence and membership (net-kingdom identity-provisioner), handover to the + consuming service +- materialization (ops-mason) and follow-through (ops-warden) sections +- verification: discovery lists the redirect URI; a test sign-in returns the + expected claims for one human; anonymous access to the custody path is denied +- rollback: remove exactly this client and its custody path +- README: threat model, MFA rationale, why redirect URIs are exact, which steps + still need an attended login and how that is minimised +- reference instances: informed-decision-approver, coulomb-social, user-engine + portal, openbao-admin + +Acceptance: complete per canon section 3, tests green, status `proposed`. + +2026-10-08: done. `security-case-types/oidc-browser-relying-party/` 1.0 (proposed): +`type.yaml` with 12 parameters (the custody reference is required only for +confidential clients), composition over key-cape's new +`key-cape.oidc-client-registration` declaration (contract PASS, `maturity: +planned`), materialization and follow-through, verification and rollback, all +conditional on client type where it matters. One attended step, +`register-client`, justified by the live registry being a Kubernetes Secret. +README with threat model, alternatives, an honest fit table (portal and vergabe +fit; Forgejo is the first confidential case; openbao-admin, coulomb.social and +informed-decision-approver do not fit 1.0), and known limits. Examples: Forgejo +(checked against the accepted request) and a public portal. The validator gained +`when` / `required_when` conditions and provider discovery. 43 tests pass. +ops-mason's and ops-warden's sections are drafted in the type. Their owners +review them in MASON-WP-0010-T01 and WARDEN-WP-0044-T01 before acknowledgment +(T05). + +```task +id: NK-WP-0048-T05 +status: done +depends_on: [NK-WP-0048-T04, MASON-WP-0010-T01, WARDEN-WP-0044-T01] +priority: high +state_hub_task_id: "3d987d3d-0797-538b-86e4-51185a327ae2" +``` + +### T05 — Acknowledge the type through informed-decision + +Prepare the Decision Memo from the type's README and `type.yaml` and submit it +through informed-decision's production intake. On acknowledgment the type +moves to `acknowledged`. + +2026-10-08: Decision Memo prepared. Hub decision `e418263e-a71f-4f01-a970-c87c9e187c09`. +`security-case-types/oidc-browser-relying-party/decision/`: `packet.md` (question, +one act, what acknowledging does not do, the shape, alternatives, attended +steps, limits, evidence), `build_intake.py` and the built `intake.json` (memo +class `decision-record`, review group `net-kingdom-admins`, resolves the hub +decision on answer). The envelope passes informed-decision's own +`validate_submission`. Queuing it is an attended operator step, a write inside +the production dispatcher pod: + + kubectl -n state-hub exec -i deploy/state-hub-memo-dispatch -- \ + python -m api.services.memo_dispatch --queue /data/private enqueue --envelope /dev/stdin \ + < security-case-types/oidc-browser-relying-party/decision/intake.json + +The answer then appears for review in informed-decision. + +2026-10-08: queued by the founder (`pending`, `queued`) after one refusal: the +first envelope's idempotency key was not State Hub's stable +`state-hub..` form, and the builder now validates against both +sides. The dispatcher delivered it to informed-decision (ledger 7 → 8 +delivered). T05 waits for the founder's answer in informed-decision, which +resolves hub decision `e418263e`. + +2026-10-08: ops-mason's review (MASON-WP-0010-T01) folded into the type, still +1.0 `proposed`: `secret_ref` is no longer a parameter, the type derives +`platform/workloads/keycape/{client_id}`; policies are +`workload-kv-read-keycape-{client_id}-eso` and +`workload-kv-read-keycape-{client_id}-{consuming_service}`, each with a templated +body (`read` on that one data path); new `openbao_role` +`external-secrets-keycape-{client_id}` (Kubernetes auth, ESO service account, +15m, no default policy); catalog entry `{consuming_service}-keycape-client`; +the two rollback steps on the KV path collapse into one metadata delete. The +schema and validator now require a `body` on policy and role items. 105 tests +pass. Two consequences: the delivered memo describes the type before these +changes (a case-set custody reference, two differently named policies, no ESO +role), so the founder answers on a superseded shape unless the memo is +re-sent; and Forgejo's custody path moves from the accepted request's proposed +`platform/workloads/forgejo/netkingdom-oidc-client` to +`platform/workloads/keycape/forgejo`, which key-cape (KEY-WP-0038) and +railiance-apps (RAPPS-WP-0016-T02) need to adopt in T06. ops-warden's review +(WARDEN-WP-0044-T01) is still in progress. + +2026-10-08: revised memo `nk-wp-0048-t05-oidc-browser-relying-party-r2` built +(`packet.md`, `intake.json`, packet `sha256:af6435e2…`); it states that it +supersedes the first memo and passes informed-decision's and the dispatcher's +validation. It needs a new memo id because the dispatcher refuses new content +under a delivered idempotency key (`idempotency_conflict`). Queuing it is the +same attended step as above. Both memos serve hub decision `e418263e`; the +first answer resolves it, so the first memo must not stay open: an accept on it +would acknowledge the superseded shape, and a decline would resolve the +decision against r2. Close it with `return` (a reviewer verb that requests a +new revision and does not resolve the hub decision; only accept and decline +do), commenting that r2 supersedes it, then answer r2. `withdraw` exists in +the vocabulary but is not offered to reviewers, and lifecycle `revise` only +applies to lifecycle-registered memos, not intake-delivered ones. + +2026-10-09: the founder reports r2 enqueued and the first memo returned. At +close, hub decision `e418263e` still showed `open` (not verified whether r2 +was answered or the answer is still in transit; the dispatcher ledger was not +read). ops-warden's review (WARDEN-WP-0044-T01, ops-warden +`history/2026-10-08-oidc-rp-follow-through-review.md`, d470f2e) arrived at +21:25, before r2 was built, and is not in r2. Required before WARDEN-WP-0044-T02: +R1 a per-step `lane` (missing: identity-provisioner caller credential, +railiance-platform broker grant), R2 canon meaning of `warden_executes: true` +(caller proxy, caller identity, no standing ops-warden token; ADR-0002), R3 a +per-step `value_handling`. Refinements R4 (one type-level catalog lane instead +of a per-case entry, which conflicts with ops-mason's item 5), R5 (ESO Ready gate +before bind-env), R6 (exact accepted memberships), R7 (mfa-pinned and +secret-delivered checks). Next: if r2 was accepted, fold R1-R3 and R5-R7 in as +a follow-up and settle R4 between ops-warden and ops-mason before T06; if not, +hold r2, fold them in and build r3. + key-cape and railiance-apps were told about Forgejo's new custody +path. + +2026-10-09: r2 was not answered (hub decision `e418263e` open; dispatcher 9 +delivered), so r2 is held and r3 replaces it. ops-warden withdrew R4 for R4′ +(message a66a9204): one catalog entry per case with its body templated in the +type. Folded in: + +- Canon section 3: a narrow meaning of `warden_executes: true` (caller proxy, + caller identity, no standing credential; ops-warden ADR-0002), plus per-step + `lane`, `value_handling` and an optional `ready_check`. A catalog entry body is + now required, like policy and role bodies. +- Schema and validator enforce all of these. +- The type: lanes `identity-provisioner-groups` and + `keycape-client-secret-generate` (both new) and `key-cape-oidc-login`; + `mint-secret` is `generated_in_custody`; a Ready gate on `deliver-secret`; + `ensure-groups` adds exactly the accepted case's memberships; new checks + `secret-delivered` and `mfa-pinned`; the catalog-entry body follows R4′. +- 110 tests pass. + +Memo r3 (`nk-wp-0048-t05-oidc-browser-relying-party-r3`, packet +`sha256:726dfc01…`) passes informed-decision's and the dispatcher's validation. +The founder queues it with the same command, returns r2 with a comment that r3 +supersedes it, then answers r3. + +Prerequisites for T06, not for T05: +- the two new lanes, each through its owner; +- a founder-approved rollout of an identity-provisioner image containing + 630ebfd (the deployed one is main-6c4fcaf from 2026-09-24, without + `/v1/groups/ensure`). + +ops-mason's request to create LLDAP group `fluid-telegram-operators` (message +259ec26d) was answered: not possible from here by that path; the founder creates +it in the LLDAP admin UI. + +2026-10-09: ops-warden finished its side of WARDEN-WP-0044-T01 (message +`58d33f40`, commit 0e7ceb8): the revised type conforms to `warden case +check-type`, with no further changes asked of the type. This acknowledges that +review. Its later addendum R8 (message `af274783`, ops-warden +`history/2026-10-08-oidc-rp-follow-through-review.md`) showed that lane +`identity-provisioner-groups` cannot exist as declared: the provisioner's only +HTTP credential is user-engine's runtime token, and its NetworkPolicy admits +only user-engine pods, so a caller-proxy lane would act as user-engine. Taken +option 1 (ops-warden's recommendation): `ensure-groups` and its rollback are +attended, in the `register-client` session on `key-cape-oidc-login`, run inside +the provisioner pod; no token is read. Option 2 (an operator credential of its +own on the provisioner) stays open for a later version. `warden case +check-type` now reports only `keycape-client-secret-generate` +(railiance-platform) as missing. 89 tests pass. + +Hub decision `e418263e` was still open, so memo r4 +(`nk-wp-0048-t05-oidc-browser-relying-party-r4`, packet `sha256:452ea421…`) +replaces r3; it passes informed-decision's and the dispatcher's validation. The +founder queues it with the same command, returns r2/r3 if still open with a +comment that r4 supersedes them, then answers r4. + +Prerequisites for T06 are now: lane `keycape-client-secret-generate` +(railiance-platform), and a founder-approved rollout of an identity-provisioner +image containing 630ebfd. + +2026-10-09: done. The founder queued r4 (`pending`, `queued`), returned r3 as +superseded and accepted r4 in informed-decision. informed-decision's hub +write-back is off in production (`INFD_HUB_WRITEBACK` unset), so net-kingdom +resolved hub decision `e418263e` from the founder's report, naming memo r4 in +the rationale. The type is `acknowledged`; it becomes `implemented` with T06. + +```task +id: NK-WP-0048-T06 +status: wait +depends_on: [NK-WP-0048-T05, NK-WP-0048-T08, NK-WP-0048-T09, NK-WP-0048-T12, NK-WP-0048-T13, NK-WP-0048-T14, NK-WP-0048-T15, NK-WP-0048-T16, NK-WP-0048-T17, NK-WP-0048-T18, NK-WP-0048-T20, MASON-WP-0010-T03, WARDEN-WP-0044-T03] +blocking_reason: "Type1.2 architecture has a confirmed source-only founder acknowledgment; canonical1.1 remains unchanged/default and the1.2 reference case stays proposed. Needs explicit version selection/reference-source integration without normal admission bypass, closed owner adapters plus global/native receipt verifier (WARDEN-WP-0044-T03), independently verified six-object structural materialization and CCR42 claims/created-only provenance, exact broker/case/source reconciliation, then separately authorized native mint/delivery and attended registry/consumer verification. Source acknowledgment, a bundle or local tests do not prove implemented/materialized/live status." +priority: high +state_hub_task_id: "d9b26bcf-a7c6-59a2-92a0-f913d76a203c" +``` + +### T06 — First KnownSecurityCase: Forgejo + +Write `security-cases/forgejo-netkingdom-oidc.yaml` from the parameters the +founder accepted in KEY-WP-0038-T01 (`key-cape/docs/forgejo-oidc-client-request.yaml`), +so that acceptance carries over as the case acceptance. ops-mason materializes it; +ops-warden follows through and verifies it. The type becomes `implemented` once +this reference case is live. + +This replaces the hand-built path proposed for KEY-WP-0038-T02. + +2026-10-09: case written, `security-cases/forgejo-netkingdom-oidc.yaml`, status +`accepted`: the example's parameters plus the founder's 2026-10-08 acceptance of +the key-cape request (owner and founder), with the custody-path derivation +covered by the type's acknowledgment (memo r4). It validates and renders (plan +`execution.permitted: false`; three attended steps on `key-cape-oidc-login`); +it needs `--allow-unimplemented` until it is live, since it is the case that +makes the type `implemented`. A test keeps it equal to the type's example. +Still open before it runs: ops-mason renders and builds it (MASON-WP-0010-T02, +T03, unblocked by T05), ops-warden plans and runs it (WARDEN-WP-0044-T02, T03), +railiance-platform's `keycape-client-secret-generate` lane (asked by ops-mason +`789feb39` and ops-warden `d9f889b1`, both unread), and a founder-approved +identity-provisioner rollout containing 630ebfd. + +2026-10-09: identity-provisioner rolled out, founder-approved in session: +`main-630ebfd` (`sha256:bafb4860…`, built by the image workflow) replaces +`main-6c4fcaf` (`sha256:ffacd5d7…`; only change: 630ebfd, additive). Applied +with `kubectl set image` on the Deployment only (generation 17 → 18, 1/1 Ready); +the file's Service, NetworkPolicies and Ingress were not reapplied. Verified in +the pod: `ensure_service_groups` and `revoke_service_groups` present, `/readyz` +200; no token used, no group changed. Rollback: `kubectl -n sso set image +deploy/identity-provisioner provisioner=forgejo.coulomb.social/coulomb/identity-provisioner@sha256:ffacd5d790a5b7800e0535f3aa95b7de52c7a68e4f575b9192c3e1622fa672c2`. +The pin in `sso-mfa/k8s/identity-provisioner/deployment.yaml` follows. + +2026-10-09: ops-warden closed WARDEN-WP-0044-T01 and shipped `warden case plan` +(T02, message `06303d1d`); the accepted Forgejo case is refused until ops-mason +materializes it, and a materialized copy plans cleanly once +`keycape-client-secret-generate` exists. Its observation that `bind-env` carries +a literal `KEYCAPE__CLIENT_SECRET`: the validator now renders a +`{param:env}` format spec (upper case, `-` as `_`, as `keycape-client.py` +derives the name) and rejects unknown specs, and follow-through and rollback +text is now checked for unknown placeholders too (patch-level, canon section 6). +The type keeps the literal until ops-warden's planner (`text.format`) renders the +spec as well; asked of ops-warden. 53 tests pass. + +2026-10-10: ops-warden rendered `{param:env}` in `warden case plan` +(23ba584, message `c1df14b7`; other specs and `!conversion` refused). The type +adopts it as a 1.0 patch (canon section 6): `bind-env` and its rollback now read +`KEYCAPE_{client_id:env}_CLIENT_SECRET`. ops-mason reports the Forgejo case +rendered and built (message `1ebddae9`; CCR drafts handed to railiance-platform), +so T06 now waits on the platform apply, the mint-secret lane and the attended +register-client session. + +2026-10-10: key-cape adopted the type's custody path (key-cape ab5cccd, message +`88a4559c`): `docs/forgejo-oidc-client-request.yaml` and KEY-WP-0038-T02 name +`platform/workloads/keycape/forgejo`, field `CLIENT_SECRET`, delivered as +`sso/keycape-forgejo-client` through `external-secrets-keycape-forgejo`, and the +`forgejo/netkingdom-oidc-client` proposal is withdrawn. This matches the type and +the Forgejo example; no change needed here. + +2026-10-10: key-cape's type-composition review (key-cape 6038483, message +`8e5ed5f6`) found the composition sound with two blocking findings, taken as a +1.0 patch (stricter validation and documentation, canon section 6): +R1, redirect URIs now refuse `?` and `#` in the type pattern and in +`keycape-client.py`'s `REDIRECT` (KeyCape refuses `?` at startup and the single +replica would stop); R2, `allowed_scopes` always includes `groups`, the renderer +refuses `access_groups` without it, and `access_groups` now says the consumer +enforces the gate because KeyCape has none; R3, `mfa_required` "false" means the +per-user privacyIDEA policy; R4, register-client's justification records the +restart for every client, the quiet window, and that the rendered registry must +pass KeyCape's client validation before it. The accepted Forgejo case and both +examples still validate; 116 tests pass. +Open, needs a decision: R2's verification step that the consumer enforces the +group gate is a new verification step, hence 1.1 (minor), and the validator pins +cases to the exact type version, so the accepted 1.0 Forgejo case would need +re-pinning. Also open with key-cape: a KeyCape entry point that validates a +rendered client registry without starting the server; until then +`keycape-client.py` mirrors the redirect rule. + +2026-10-10: founder go for 1.1, so no 1.0 run overlaps. The type adds +verification `consumer-enforces-groups`, and hand-over now requires the consumer +to enforce `access_groups` (minor, canon section 6; risk class unchanged, no new +acknowledgment; history in the type README). The accepted Forgejo case and both +examples are re-pinned to `oidc-browser-relying-party@1.1` before any apply; the +acknowledgment intake keeps `@1.0` as the record of what was acknowledged. +ops-mason's build and ops-warden's case-ref digest predate this and need a +re-render. + +2026-10-10: ops-mason re-rendered the Forgejo case from `@1.1` (message +`22c6bba6`; plan digest `af1f4f81…`, no collisions) and rebuilt the bundle. The +rendered OpenBao objects are identical to 1.0, so the already applied +CCR-2026-0041 stands; 1.1 changes only consumer-side steps. key-cape shipped +`keycape check-config` (c528daa, message `a7b0aeff`): KeyCape's startup checks +without secret values, including the new duplicate-clientId rule. +`keycape-client.py --check-config ` now runs it on the live and the +proposed registry, fed on stdin, and apply refuses a proposed registry that +fails; register-client's entry point names the option (1.1 patch). The deployed +image predates c528daa, so the binary is built from key-cape source for now. +Before the next KeyCape rollout (Forgejo register-client or the CCR-2026-0018 +window), the attended operator runs `inspect --check-config` so the live +registry is checked too. + +```task +id: NK-WP-0048-T07 +status: wait +depends_on: [NK-WP-0048-T06] +blocking_reason: "The next types follow once the first one is proven end to end." +priority: medium +state_hub_task_id: "c3c9c3ae-4f23-5ba7-9e6f-e63bcf29b9f7" +``` + +### T07 — Queue the next types + +From the T02 ranking, open the next type drafts (likely the confidential service +client and the workload KV read lane), each to be acknowledged on its own. +Record which existing hand-built instances get adopted as cases, without +rewriting them in place. + +```task +id: NK-WP-0048-T08 +status: done +priority: high +state_hub_task_id: "8e519082-dc8b-5e46-b660-03d03e55dcf6" +``` + +### T08 — identity-provisioner: ensure service-access groups + +The type's `ensure-groups` step calls `identity-provisioner groups ensure --groups + --members `. The provisioner already has idempotent +`_ensure_group` and `_add_group`, but exposes only tenant-scoped operations. Add +the general operation (create if absent, add the listed memberships, never +remove others), its rollback (remove only this case's memberships), tests, and +the ops-warden route for its service identity. + +2026-10-08: done. `identity-provisioner/provisioner.py` gained +`ensure_service_groups` (`POST /v1/groups/ensure`) and `revoke_service_groups` +(`POST /v1/groups/revoke`), behind the existing service-token check. Names +matching `tenant*`, `netkingdom-*` and `lldap_*` are reserved; members must +already exist; everything is validated before any change; memberships are read +back before success; nothing else is ever removed; groups are deleted only when +asked and empty. 7 new tests (39 in the provisioner suite). The type's +`ensure-groups` step and its rollback now name these endpoints. The ops-warden +route for the provisioner's service token belongs to WARDEN-WP-0044-T01. + +```task +id: NK-WP-0048-T09 +status: done +priority: high +state_hub_task_id: "0ee12dfc-8260-5038-bd39-cc7fb4113be0" +``` + +### T09 — The `keycape-client` entry point and its declaration + +Planned first as key-cape KEY-WP-0038-T04. Moved here because the machinery a +live registration needs already lives in net-kingdom, where KeyCape is deployed +(`sso-mfa/k8s/keycape/`): the contained kubectl, the cluster-identity pin and the +issuer pin. A copy in key-cape would drift. + +2026-10-08: done. `sso-mfa/k8s/keycape/keycape-client.py` derives the KeyCape +client record from a case (`client-from-case`). It adds or removes exactly one +client with the existing safeguards (byte-preserving merge, exact match or +refuse, shared-reference refusal, compare-and-swap, read-back, contained errors), +requires the Deployment env binding first for confidential clients, and requires +the case acceptance record for `apply` instead of a per-client founder decision +id. 15 tests, including a regression showing that the vergabe case reproduces +its hand-built registration exactly; all 71 helper tests pass. Declaration +`capabilities/playbooks/net-kingdom.keycape-client-registration.yaml` (contract +PASS, `maturity: implemented`) replaces key-cape's draft. The type now pins it, +and for confidential clients adds `deliver-secret` (delivery from custody into +the sso namespace) and `bind-env` (Deployment env), attended in the same session +as `register-client`. The per-client helpers stay until their clients are +adopted as cases. + +2026-10-10 — NetKingdom owner review of the execution seam, source only: +ops-warden/docs/security-case-execution-proposal.md SHA256 78ab1439e4c5beb205aeccf15b1b2e706511be55ab195c5a3aa682f326c7c77c +was reviewed against INTENT, SecurityCase canon, acknowledged type1.1, current +case and owner source. docs/security-case-execution-owner-review.md supports +the closed adapter/receipt direction with explicit versioned schema, structural +materialization provenance, unchanged caller/session boundaries, safe CAS +reconciliation and exact-created-object rollback requirements. This is owner +review, not approval. Supporting file digests are in the adjacent sources JSON. + +Current source exposes a circular wait: ops-mason T03 waits on mint while Warden +and the broker refuse a non-materialized case. Structural acceptance therefore +needs its own native pinned receipt before mint, not a successful mint before +materialized. The platform broker additionally pins type1.0 while Forgejo is +accepted at1.1; platform-owner alignment and tests are required, not an unchecked +version bypass. T06 remains wait. No case/type lifecycle status, grant, authority +or live runtime changed by this review. A proposed informed-decision would +adopt the source contract only; native materialization and execution acceptance +stay distinct. + +Final proposal re-review: staged Warden proposal SHA256 78ab1439e4c5beb205aeccf15b1b2e706511be55ab195c5a3aa682f326c7c77c +adds only the explicitly local reducer appendix, not an accepted wire protocol +or executor. The exact-final review and sources JSON supersede the earlier +1035f049... proposal review. Platform staged the exact type1.1 broker correction +with ten fake-backend subprocess checks (script SHA256 +9576cd211cfc06526ec4cc9bd0a5b06f0f59287a68931bb2d6e3592a951ddc7d). +The next source work is the versioned closed adapter/receipt/materialization +contract, not another relaxation of the broker's status gate. Native work still +requires installed broker provenance, structural reader receipts, exact new +materialized-case bytes, caller grant/session revocation and verified execution. +No installed/native acceptance is inferred from that staged correction. + +2026-10-11 — new source-only type1.2 architecture acknowledgment: +Confirmed native founder ACCEPT of infd-20261011-forgejo-type12-source-r1@1 +at06:47:39Z, approval8e9332c8-754a-420c-b8b7-445cb4fe6765. Exact frozen packet +0b377d647ca748cd03c92f9792396412048055d4bde9f7bb06ff6d413f7acd68 and submission +585cee731de30d2b37883a57abfd9eb32f023ee694e8d911989109d5caf80569; native receipt +retained in docs/evidence/2026-10-11-forgejo-type12-source-acknowledgment/. +The complete acknowledged1.2 source package is isolated under +security-case-type-revisions/1.2/oidc-browser-relying-party; default loader and +current accepted1.1 case remain untouched because load_type is single-version. +The new proposed1.2 case is NOT accepted/materialized/live and has no acceptance +fields. Actual reference-migration design approval is a separate Apps owner +receipt; no1.1 approval is inherited. Source-recording review/test proof and exact +next gates are in docs/forgejo-type12-source-acknowledgment-followthrough.md. +T06 remains wait and T07 remains its existing dependent task; type is NOT +implemented and no first-case/native grant or broader workplan finish is asserted. + +2026-10-11 — bounded explicit version selection source preparation for T06: +The validator now offers optional exact version_roots/--version-root VERSION=PATH +selection with duplicate/mismatch/traversal/symlink refusal and no fallback. The +default1.1 source and historicalacceptedcase hashes/ordinaryrefusal/preparation +render stayunchanged; actual1.1 is acknowledged, not implemented. Acknowledged1.2 +ordinaryadmission also refuses; optionalpreparation remains nonexecuting.43local +validatorchecks pass and original1.1 CLIrenderbytes match exactly. Makerproof: +docs/evidence/2026-10-11-explicit-type-version-selection/source-integration-validation.json. +FormalNetownerreview/reference-source integration and all existing native adapter, +provenance/broker/materialization/attendedproof gates remain; T06 stayswait, no +new residual or firstcase/typeimplementation/native grant is inferred. + + +2026-10-16 — isolated source preparation for T06 (canonical integration pending): +Added `check-evidence` to the existing validator CLI. It reuses exact version +selection and ordinary type/case/render admission, checks actual Mason structural +request source/inventory/projection pins and reads actual Warden journal rows +read-only. Successful rows require exact type-declared descriptors and attempt/ +revocation fingerprints. Existing acknowledged types without machine descriptors +remain plan-only. The compatibility result never claims authenticated native +completion: Request and journal fingerprints are not portable native evidence. +Owner authentication/status binding and actual native gates remain unresolved; +T06/T07 statuses and both type/case lifecycles remain unchanged. No dispatch, +credential access or authority expansion is introduced. See tool README for the +existing owner protocol boundaries and source integration tests. + +Concrete next Warden owner integration: current `Executor.admit` refuses every +case parameter whose sensitivity is not public/operational, so browser +`redirect_uris`, `allowed_scopes`, `client_type`, `mfa_required` (and 1.2 +`consumer_namespace`) fail after otherwise prepared admission. The sensitive +classification is a security-change review level, not secret content. The +Warden owner must validate through NetKingdom's existing exact-version case/type +validator, then project only descriptor-declared safe target metadata; do not +remove the credential/value refusal or copy complete parameters into journals. +Actual owner descriptor acknowledgment and independent evidence admission remain +prerequisites, with normal admission still refusing unimplemented types. + + +## T10 — Source compatibility checker for Mason and Warden evidence + +```task +id: NK-WP-0048-T10 +status: done +priority: high +state_hub_task_id: "4b06ca97-c1e4-5680-bf22-ab75ab1646a4" +``` + +Completed 2026-10-16 in published source `c31d21ba`. The read-only +`check-evidence` command checks the actual Mason structural request and Warden +journal source protocols against an exact version/render. Independent source +review passed 103 tests at that checkpoint. It always reports native completion +false and execution prohibited. Fixtures and local journal fingerprints do not +provide authenticated owner/native evidence. This records completed source scope +previously described under T06; it does not duplicate native acceptance credit. + +## T11 — Validate the optional machine-descriptor pin contract + +```task +id: NK-WP-0048-T11 +status: done +priority: high +state_hub_task_id: "ef2a0119-f59e-5e45-9519-115da9df7b85" +``` + +Completed 2026-10-16. Reproduced the JSON schema rejecting a valid +`execution_descriptors` map while the Python validator accepted malformed maps. +The schema now supports the existing Warden optional exact-byte pin map; semantic +validation binds keys to declared steps, bounds the map and checks digest shape. +The direct evidence API refuses malformed maps before accessing them. Twenty-one +new checks and all 127 affected source checks passed. No current type/case bytes, +status, owner descriptors or native acceptance changed. Source evidence and +publication receipt: Custodian +`docs/changes/netkingdom-target-2026-10-16/browser-acceptance-next/`. + +## T12 — Prepare NetKingdom-owned attended step adapters + +```task +id: NK-WP-0048-T12 +status: todo +priority: high +state_hub_task_id: "7ce4eee3-cd47-55a9-bfa5-4f1835d4d6fd" +``` + +Owner: NetKingdom. Retained scope: prepare bounded attended Deployment env, +config verification and restart/readiness steps around the existing helpers. +Next action: identify exact current entry points and their revision/readback +contracts, then implement fixed source-pinned callbacks or receipt-only attended +steps. Completion requires positive/refusal checks, exact target/source pins and +uncertainty behavior without acquiring user-engine runtime identity or exposing +registry/client secrets. Existing caller/session, cluster/issuer, CAS/readback +and status-only output boundaries remain mandatory. + +2026-10-16 task refinement: the original broad adapter scope is split into T13 +(completed registration source candidate), T14 (group creation provenance), T15 +(durable registration recovery) and T16 (descriptor/artifact selection and owner +review). This preserves T12's task ID and Hub UUID while giving each remaining +outcome an owner and completion gate. T06 now names every source dependency; +its native/reference lifecycle acceptance scope remains unchanged. Completion +of any source task supplies no Warden, Platform or Apps assent and no native +execution authority. + +## T13 — Prepare the fixed registration adapter source candidate + +```task +id: NK-WP-0048-T13 +status: done +priority: high +state_hub_task_id: "b264c10b-565c-5d29-b788-03cf5fbd2883" +``` + +Completed 2026-10-16, source implementation and synthetic fixture proof only. +Added an injection-only adapter around actual `keycape-client.run`: fixed +case/principal/human/lane/source/acceptance binding, existing CAS/readback, +metadata-only owner evidence and per-attempt parent-session revocation boundary. +The descriptor source composition pins the exact checker executable digest +alongside adapter/helper source. Creation-only rollback authenticates its +creation receipt and refuses changed postcreation UID/resourceVersion; +pre-existing clients confer no rollback authority. Missing readback, evidence, +revocation or process-local creation provenance cannot become retry-safe success. + +Evidence: Custodian `docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/` +R3 candidate, explicit patch and postimages, retained R1 rejected custody finding +and R2 revision, and 36 passing source checks (16 new, 20 existing). Three checks +use actual Warden kernel admission/dispatch/rollback with published Warden +`d97652a3bb5fa733b063d71a1b74068efcccf572` producer pins unchanged. Owner trust, +descriptor/type/case and Kubernetes objects remain synthetic fixtures. This +records a completed source deliverable, not independent review, publication, +type acknowledgment, exercised procedure, identity admission or native browser +acceptance. Independent root source review: Custodian browser-adapter-next/coordinator-review-r3.json. Source integration and normal publication/synchronization receipts: Custodian browser-adapter-next/canonical-integration.json. These receipts supply no other-owner assent or native authority. + +Source publication: `0775c2bfa41af236301155bb5a69bbf088bdf823`, independently +reviewed by the Custodian coordinator before integration. The required initial +projection and ordinary UUID writeback both completed with zero failures and +zero automation errors. Generated-only brief commits were refused by the normal +hook; exact pre-existing staged/working brief bytes were restored. The separate +canonical integration receipt retains those refusals and projection evidence. +Native admission and all other-owner acceptance remain open. + +## T14 — Admit governed group rollback against native owner boundaries + +```task +id: NK-WP-0048-T14 +status: progress +depends_on: [NK-WP-0048-T20, NK-WP-0048-T21] +priority: high +state_hub_task_id: "fce95440-a25b-5f27-8ef3-a3d182099113" +``` + +Owner: NetKingdom. Next action: bind the completed T21 PostgreSQL journal to +an admitted dedicated CNPG consumer through the existing `rapp-postgres` +database-per-consumer contract. Identify the actual deployment/package and +custody owner, admitted placement/capacity and role/renewable lease/TLS target; +implement the independently retained exact content checkpoint and authoritative +tenant/case/source/principal callbacks before enabling governed group operations. +The injection-only backend and fixture callbacks do not supply production scope +or authorization. Existing service tokens, caller-supplied JSON and provenance +receipts confer no new grant. The default production endpoint remains disabled +without the trusted integration. SQLite remains local/disposable only; any +production exception requires explicit scoped governance under ADR-0015. + +2026-10-17 owner-integration refinement: T23 records the completed source/package +placement review, distinct from T21's source/disposable-SQL proof. Its dedicated +`rapp-identity-provisioner` recommendation is draft and unbound, not an accepted +package or existing repository. The full six-consumer rapp-postgres renderer +accepts the inert candidate on platform-pg-2 as its third of four declared +consumers; first-cell placement is refused as a fifth. Neither the estimated +four-consumer ceiling nor source requests establish native headroom or backup. + +Next independent owner contracts: NetKingdom/package/substrate owners accept +exact deployment/rollback fate; rapp-postgres admits the dedicated consumer, +roles, five-connection budget and actual backup/restore; Platform admits exact +renewable runtime/migration delivery and TLS/CA/SAN; network owners scope SQL, +broker and checkpoint paths without assuming a narrow policy subtracts additive +same-namespace access. Protected caller owners must provide authoritative +GroupJournalBinding construction and current authorization; an independent +existing holder must authenticate and atomically advance the exact content +checkpoint, not a counter or ordinary Audit receipt. Directory/runtime owners +must select and prove either an exclusive all-writer maintenance barrier or +provider-supported CAS/generation, including established sessions, lifecycle +cascades and remove/readd ABA. No source review establishes those owner facts. + +The default PostgreSQL activation and destructive native rollback remain refused. +Storage/lease acceptance can be proposed separately from LDAP mutation only after +exact owner source/target/custody/verification/recovery and cost/risk review; +State Hub guard approval grants none of it. Source-only integration review and +native acceptance matrix: Custodian native-storage-r1/OWNER-REVIEW.md. T14's +existing status, UUID, dependencies and native completion gate are retained. + +2026-10-17 refinement: T20 retains the historical durable SQLite source and +actual disposable LLDAP proof. T21 extracts the completed PostgreSQL source and +bounded real disposable SQL compatibility proof. T14 remains `progress` for +native CNPG admission/recovery, protected owner integration and exclusion of all +external LDAP writers. Separate source publication/projection and independent +cleanup receipts belong to coordinator follow-through; no missing native assent +is represented as received. + +Completion still requires independently reviewed/admitted ADR-0015-conforming +CNPG storage (or an explicit governed exception), backup/lost-store recovery and +migration of legacy requests; a native +writer-isolation/fencing or provider CAS/version contract that closes external +writer races and remove/re-add ABA; exact case/source/principal and current +native object bindings for protected callbacks; and actual per-attempt evidence, +revocation and readback/refusal verification through the existing owner paths. +Only independently retained confirmed creations may be undone. Every +pre-existing/shared group and membership must remain preserved; unresolved +intents never expire into retry authority. The disposable fixture supplies none +of these native custody, authorization, owner-acceptance or production claims. + +## T15 — Define authenticated recovery for lost registration process state + +```task +id: NK-WP-0048-T15 +status: wait +depends_on: [NK-WP-0048-T31] +blocking_reason: "Source candidate 061461e is merged. Waiting on the owner to select the real authenticated original-attempt holder and native readback, and on independent re-review of the changed T13 adapter source." +priority: high +state_hub_task_id: "1b825705-4848-5edc-832f-b1622cd9bdb7" +``` + +2026-10-17 source part merged (061461e via 65559ef). `build()` takes optional +paired `holder`/`observe` capabilities and installs `reconcile_original`; the T31 +fixtures now run through the adapter's own path. Rollback after restart is still +refused, and there is no add replay. This changes `keycape-registration-adapter.py`, +so the revision reviewed under Custodian `browser-adapter-next/coordinator-review-r3.json` +is superseded. The new revision has no independent review yet and must not be +pinned on the strength of the old one. + +2026-10-17: T31 fixtures done (74adcd7). What remains for T15: the owner +selects the real authenticated original-attempt holder, and the T13 adapter +gains the holder capability, `reconcile_original` and reconcile-aware `verify` +described in T31. + +Owner: NetKingdom for registration state, with Warden owning kernel evidence +admission. T22 records the completed source review and exact existing-seam gaps; +T15 retains implementation and authenticated restart/lost-receipt acceptance. +Next action: select and admit the actual owner publisher/authenticator and bounded +original-attempt readback contract for the merged holder/observe adapter seam. +Warden's original-intent persistence/interface (`a45cf475`) and additive validation +(`117fec9241e7b985dfb187b992e173d0a5dc3700`) are now published and projected. +The local Journal retains admitted nonsecret original context, validates original +actor/attempt and metadata linkage to the original running receipt, and supplies +it separately from the new reconciliation attempt. This local consistency is not +independent original-holder authorization or creation provenance; neither the +journal nor a current matching registry entry reconstructs creation authority. + +The owner contract must bind the original Invocation, case/type/descriptor/catalog/ +acceptance pins, source, principal and admitted tenant/realm provenance, separately +from the new reconciliation attempt and its independently revoked parent session. +Use an existing authenticated holder/readback; Audit Core's custody API is a +candidate only until its actual registration source/class, load-bearing emission +contract and separate scoped reader are admitted. No new broker or receipt trust +root is proposed. A missing original record, ambiguous publication, NO_CHANGE, +changed native revision or lost readback stays uncertain; no add replay or undo. + +Completion requires restart/lost-receipt fixtures against the selected actual owner +contract: independently authenticated original created-only provenance, original +exact client entry and unchanged current postcreation UID/resourceVersion before +any undo, with wrong attempt/case/source/principal and unavailable-holder refusals. +The merged T13 adapter exposes reconciliation only through paired injected +holder/observe capabilities and still refuses post-restart rollback. Existing +source review, JSON receipts and synthetic trust do not satisfy independent +holder authorization/provenance or native admission, or authorize attended recovery. +Exact remaining owner facts: Custodian native-registration-r1/original-attempt-review.json. + +2026-10-17 check (net-kingdom-worker): Warden's original-intent lookup interface +is not published — ops-warden head is `d97652a`, with no WARDEN-WP-0044-T03 +commit, and the net-kingdom inbox holds no announcement. No restart/lost-receipt +fixture or readback seam was added this session; the work is deferred behind +T16's decomposition under the same token budget. Remains todo, gated on Warden +publishing the reviewed lookup/context interface and on owner selection of the +authenticated original-attempt holder. + +2026-10-17 current-source correction: the earlier same-day `d97652a` lookup +checkpoint and T31 adapter-next-action note above are historical and superseded +by the published Warden interface/validation and merged `061461e` adapter seam. +The 54 focused Warden checks establish source behavior only. Current admission +leaves tenant/realm empty; no admitted tenant/realm provenance is inferred. +T15 remains wait for actual independently authenticated owner-holder/readback +selection, independent review of the changed adapter, and native restart/lost- +receipt acceptance. Existing task metadata, owner gates and historical reviews +are unchanged. + +## T16 — Select and review the actual machine descriptor composition + +```task +id: NK-WP-0048-T16 +status: todo +depends_on: [NK-WP-0048-T24, NK-WP-0048-T25, NK-WP-0048-T26, NK-WP-0048-T27, NK-WP-0048-T28, NK-WP-0048-T29, NK-WP-0048-T30] +priority: high +state_hub_task_id: "200bf500-5084-5ae7-be26-3033fe1f296e" +``` + +2026-10-17 coordinator review r4 approved the 1.3 candidate at 1920b13 with no +findings (the-custodian `nk48-adapters-1.2-review/coordinator-review-r4.json`). The founder chose +option A of the T16 owner-review packet (`nk48-t16-owner-review/founder-option-decision.md`): +1.3 with execution descriptors for all seven steps. Owner reviews for A are open +in the-custodian `docs/changes/netkingdom-target-2026-10-17/nk48-1.3-owner-review/` (net-kingdom, +railiance-platform, railiance-apps, ops-warden, key-cape). The owner-review packet +supersedes the r1 composition pointer: T16's exact composition is now +`sso-mfa/k8s/keycape/descriptor-composition-1.3.candidate.json`. + +2026-10-17 successor candidate prepared (source only, `proposed`): +`security-case-type-revisions/1.3/oidc-browser-relying-party/` is the 1.2 package plus +an `execution_descriptors` map of the seven composed descriptors, with examples +pinned to `@1.3` and no acknowledgment. ensure-groups, hand-over, +deliver-consumer-secret and the composer accept 1.3 (835680c, merged acda26c). +`test_admit_1_3_type.py` runs Warden's real admission against the committed 1.3 type: +the proposed type is refused; an acknowledged copy is refused because ops-warden +pins parameter fingerprints only for 1.1 and 1.2; and with only a 1.3 pin (equal to +1.2's fingerprint) all seven are admitted. This is a major change under canon +section 6. Next: r4 source review of the changed adapters and the map, then owner +reviews, including the ops-warden 1.3 pin, the mint-secret warden_executes/human +question and the owner-run steps question, then the founder's Decision Memo. + +Owner: NetKingdom for descriptor/type source and exact checker artifact; Warden +independently owns global provenance/admission. T22 records the completed seven-step +source1.2 inventory and regenerated register-client descriptor using the delivered +checker and current sealed-snapshot source; T16 retains exact owner review, +complete descriptor composition and admission. Next action: bind actual native +publisher/authenticator, original/current session verification and revocation, +executable storage/custody and config pins, then obtain the six missing fixed +owner descriptors/adapters for ensure-groups, mint-secret, deliver-secret, +bind-env, deliver-consumer-secret and hand-over. The inventory identifies each +existing owner seam, exact missing contract and created-only rollback requirement; +it is not an executable bundle. T14 and T15 remain independent prerequisites. + +Review the dedicated consumer source1.2 composition only through the existing +explicit version-root selection. Keep canonical1.1/default and its accepted case +unchanged, and the source-only acknowledged1.2 reference case proposed without +acceptance fields. New descriptor bytes require a reviewed exact successor +revision/version under canon section6; they do not inherit historical source +acknowledgment. Changed authentication/materialization/trust needs the prescribed +informed-decision acknowledgment. Do not insert a partial descriptor map or use +--allow-unimplemented preparation as ordinary case/native admission. The registry +helper reports restart_required; separately review quiet-window restart/readiness +and actual consumer verification rather than infer activation from registry CAS. +Exact composition: Custodian native-registration-r1/descriptor-composition.json. +Completion requires separate owner source review of exact machine descriptors, +artifact/source/config pins, normal type/version admission and independent +evidence-verification boundaries. Warden d97652a refreshes producer validator +pins for NetKingdom 015694ee; it does not attest T13's new adapter. Pinning any +machine descriptor into an acknowledged type is a separately reviewed source +change. Platform owns structural/mint/broker/delivery evidence; Apps owns actual +consumer login/group enforcement. Neither their assent nor native acceptance is +supplied by this task. + +2026-10-16 source checkpoint: T19 below extracts the completed per-attempt +executable snapshot implementation and reviewed fixture proof. T16 retains +native executable storage/custody, exact owner evidence/session callbacks, +complete descriptor composition and ordinary type/version admission. The +successor descriptor/source pins are proposed only; the earlier descriptor +history is preserved. T14 group provenance and T15 durable restart/recovery +remain independent open requirements. No native acceptance is counted here. + +2026-10-17 budget checkpoint (net-kingdom-worker): no source change made. The +T13 registration adapter alone is 217 source and 317 test lines plus an +independent review; six further owner descriptors/adapters cannot be completed +within one per-task token budget (soft 8000 / hard 10000), so the task was +stopped below 50% and is decomposed rather than pushed through. Proposed split, +one source-candidate task each with positive/refusal fixtures, canonical1.1 +default unchanged and no --allow-unimplemented admission: (a) ensure-groups, +bound to T14's journal/provenance seam; (b) mint-secret and (c) deliver-secret, +Platform-owned evidence seams; (d) bind-env and (e) deliver-consumer-secret; +(f) hand-over, Apps-owned consumer verification; then (g) descriptor composition +over the reviewed set. Each candidate needs its owner's source review before +pinning; none is authored here as owner-accepted. Status stays todo. + +2026-10-17 task refinement after coordinator r5: completed independent mint- +guard regression coverage is extracted as T32. T33 tracks the exact Platform +issuer-store source/reference correction; T34 tracks the Apps configuration +inverse reachable without secret or hand-over resource creation. T16 retains +final exact seven-descriptor composition, independent owner reviews and admission; +its status and existing dependencies are preserved. The kernel branch also needs +its own KR1-F1/F2 admission/dispatch corrections and independent review under +WARDEN-WP-0044-T03 before any 1.3 pin. Historical r4 acceptance does not supersede +r5's findings or establish native readiness. + +## T17 — Build and exercise the exact offline config-checker artifact + +```task +id: NK-WP-0048-T17 +status: done +priority: high +state_hub_task_id: "8c3e4baa-a218-5996-af9e-fe1153a46dac" +``` + +Completed 2026-10-16, isolated artifact/source fixture proof only. Built the +existing `key-cape/src/cmd/keycape` command from published clean source revision +`3c71226a1a7b8a2bdb710e9c497e1a1f7f8a3987`, source tree +`f0010b9cab17dbc3a787628a29657d98b900320f`, with cached Go 1.23.0, +CGO disabled, Linux/amd64 v1, trimmed paths, disabled VCS stamping and offline +read-only module selection. The full retained closure pins 108 source files, +9876 toolchain files and ten used module source directories. Three isolated +build locations produced identical 13633409-byte artifacts: +`sha256:be37738fe1c08259fb4f9ca0daf0f9301bfffc237e5bb38435525dfbf11ccff5`. + +Eight disposable checks use the actual executable and maintained helper/kernel, +with complete synthetic configs and a minimal environment: add/created-only +removal, normal pinned producer admission, rollback, lost-readback uncertainty, +stale revisions, substituted checker refusal, invalid startup config and missing +secret-env refusal. No checker stub or producer-pin override remains. The +artifact-bound descriptor proposal is +`sha256:76b1153d188062ece53fcd5e8d35290fdbfd02a69dd79d4b001fc42781afcdc3`. +Evidence: Custodian +`docs/changes/netkingdom-target-2026-10-16/browser-checker-artifact-next/`. +The first two build attempts succeeded but their closure collection failed; +both failures are retained, as is refusal of the older incomplete synthetic +fixture. No production config, credentials or cluster mutation was used. + +This extracts completed build/fixture scope from T16 without claiming KeyCape +owner endorsement, native checker selection/storage custody, acknowledged type +integration, native evidence/session callbacks or attended browser acceptance. +T16 retains exact owner review and composition/admission scope. The attended +registration procedure remains unexercised. + +## T18 — Make checker input and executable selection deterministic + +```task +id: NK-WP-0048-T18 +status: done +priority: high +state_hub_task_id: "9c45da71-fb4c-5f20-9065-90f448f37c64" +``` + +Completed 2026-10-16, isolated source correction and real-artifact fixture proof +only; independent integration/publication remain separately recorded. KeyCape +source review identified non-secret `KEYCAPE_ACCOUNT_PORTAL_URL` and +`KEYCAPE_BROWSER_LOGOUT_URL` overrides in config.Load. The original helper +inherited ambient environment, allowing valid supplied configuration to be +rejected or invalid supplied URL configuration to be admitted. A bare relative +checker filename could also be replaced by PATH lookup after the adapter had +hashed the selected local file. All three counterexamples are retained against +the unchanged predecessor. + +The existing helper now resolves the selected checker file to an absolute path +and passes an explicit empty subprocess environment. Three opt-in real-checker +regressions verify both URL-verdict directions and relative-file selection under +a non-checker PATH decoy. All 39 affected checks pass with the exact supplied +artifact, as do 11 real-artifact helper/kernel scenarios. Source evidence: +Custodian `docs/changes/netkingdom-target-2026-10-16/browser-checker-environment-r2/`. +Historical descriptors remain retained; the helper correction changes successor +source-composition pins and requires their separate review/admission. An absolute +path is not immutable custody and does not eliminate file replacement races; +T16 retains native executable storage/custody and callback/type admission. +No production configuration, credentials or cluster mutation was used. + +Source integration and publication: `916ba4b33e03179274d03b78bd898a4843e8653f`, following independent +Custodian source review in browser-checker-environment-r2/coordinator-review.json. +The ordinary protected projection pass assigned T17/T18 UUIDs with zero failures +and zero automation errors; generated-only brief commit refusal is retained. +The exact prior staged/working brief bytes were restored. Final source/publication/ +projection evidence is retained in Custodian browser-checker-environment-r2/ +canonical-integration.json. These source receipts do not admit native executable +custody, owner callbacks, acknowledged type changes or browser identities. + +## T19 — Bind checker execution to verified immutable attempt bytes + +```task +id: NK-WP-0048-T19 +status: done +priority: high +state_hub_task_id: "f50fdcd1-00be-5c3c-be0c-345aa2126032" +``` + +Completed 2026-10-16, independently reviewed source implementation and synthetic +fixture proof only. The registration adapter now opens a bounded regular checker +artifact without following its terminal symlink, verifies the reviewed digest, +and snapshots the exact bytes into a write/grow/shrink/seal-removal-sealed Linux +memfd before helper access. Live and proposed config checks execute that same fd +through procfs with the existing empty environment; the fd closes on completion +or failure. Unsupported snapshot/seal/exec policy refuses through the existing +parent-session revocation and uncertainty boundaries. The earlier unbounded +pre-snapshot path read was removed during independent review; actual adapter +symlink/FIFO/oversize refusal tests verify helper access never starts. + +Evidence: Custodian `docs/changes/netkingdom-target-2026-10-16/browser-resume-r1/`, +including exact base/preimages/postimages/patch, proposed descriptor, coordinator +source review and canonical integration receipt. Independent Custodian checks +passed 49 affected source tests and 12 actual Go checker/helper/kernel scenarios; +the sets overlap and are not separate acceptance counts. Existing published +Warden d97652a producer pins remain unchanged. The exact KeyCape artifact remains +`sha256:be37738fe1c08259fb4f9ca0daf0f9301bfffc237e5bb38435525dfbf11ccff5`. +The predecessor path-replacement refusal and the first expanded reopened-fd test +failure are retained; the latter fixture was corrected to restore write mode +before independently proving seals block reopened-fd writes and shared mmap. + +This source task adds no native owner custody, callback, evidence trust, type +acknowledgment, credential access, consumer sign-in or rollback acceptance. +Executable memfds, Linux seals and usable procfs are explicit runtime dependencies +and still require native admission. This is an extracted source outcome from +T16, not completion of T16; T12/T14/T15 and all external-owner gates remain open. + + +Publication verified by the coordinator after the source worker usage limit: +`8790dd18ae83c4651ef3d1d923312a118a5b436b` is the actual remote main revision. +All five canonical source hashes match the reviewed packet. Normal synchronization +completed with zero failures/automation errors; all 19 task statuses match the +projection. T19's assigned UUID is retained. The original staged and working brief +bytes were restored and independently compared. The exact source, projection and +publication checks are in Custodian browser-resume-r1/canonical-integration.json. +Native acceptance remains open under T16 and the other named owner tasks. + + +## T20 — Prepare durable group provenance and prove disposable LLDAP compatibility + +```task +id: NK-WP-0048-T20 +status: done +priority: high +state_hub_task_id: "863fcbdb-e246-51ba-87ca-a2f44d01d6f8" +``` + +Completed 2026-10-17, source candidate preparation and bounded synthetic +compatibility proof only. This extracts evidenced implementation/fixture scope +from T14; T14 retains native storage, writer isolation, protected callback and +owner-acceptance gates. Source integration/publication and State Hub projection +are pending the coordinator's separate receipts; no revision or task UUID is +invented here. + +The existing identity-provisioner group ensure/revoke interface now has a +reviewed candidate for a bounded owner-private SQLite journal, proven only in +local/disposable use. The initial storage instructions wrongly suggested a native +persistent-volume path; source review against accepted ADR-0015 corrected them +before publication. Production SQLite-on-a-PVC is not admitted: T14 retains +CNPG/rapp-postgres storage admission or a separately governed exception. +The local journal saves +complete prestate and intent before each attempted directory mutation, inventories +only confirmed new groups/memberships, binds original ensure idempotency and +correlation to the exact request digest, and refuses unsigned caller inventories +or legacy broad revoke requests. Replays never recreate removed state. Missing, +replaced or untrusted storage and missing intent rows stop execution; definite +before-mutation refusals retain evidence without stranding unrelated valid work. +Interrupted writes/readback remain unresolved across reopening. Revoke validates +all targeted native ids and complete member postimages before destructive work, +removes only confirmed creations and never deletes a preexisting empty group. +Server/configuration wiring, image-copy closure and owner storage/request +migration instructions are included. The existing endpoint's caller +authentication remains the existing boundary, not a new grant. + +The candidate was prepared against NetKingdom +`878abc93588e12b6da1bac055cfbb3abb8d7adf1`; exact source patch digest +`sha256:1f3f3ca94c46dadac8f24896053d4d23179de795e50a24298a0cfbcc46cca30e`. +The independent Custodian review passed all 65 provisioner checks. The earlier +62-check candidate, two independent-review findings and subsequent fixes remain +retained rather than overwritten. Seven additional fixture-plan checks exercise +the preparation boundary; these counts overlap the same candidate and do not +measure task-completion percentage or independent acceptance. + +A separately reviewed bounded fixture then exercised the exact candidate against +cached pinned disposable LLDAP and Python images, through actual GraphQL. All 14 +closed compatibility/containment oracles passed: exact creation inventory, +separate-process SQLite reopening/replay/revoke, complete preexisting directory +preservation including empty/shared groups and memberships, refusal of shared +membership drift, retained uncertainty after a lost response following a real +addition, and refusal of unknown/cross-case receipt bindings. The fixture used +only newly generated synthetic users/passwords/key material in private tmpfs, +with a network-none anchor and shared loopback helper, no host ports/mounts, +no image pulls and no production or actual credential contact. The coordinator's +safe receipt reports both exact owned containers absent after cleanup; final +independent cleanup verification remains the coordinator's integration duty. + +Evidence: Custodian +`docs/changes/netkingdom-target-2026-10-16/group-provenance-r1/`, including exact +source base/pre/postimages, patch/manifest, independent review and retained +refinements. Native fixture manifest +`sha256:8a0514f5f14137355972400cd4852f48c892316d28e3abd3c86635d7cb81eef2`; +retained safe result `native-fixture/execution-result.json` (original +`/tmp/nk48-groups-native-review-5pywb19z/result.json`). The source task supplies +no native owner assent, production rollout, protected session/evidence callbacks, +ordinary type admission or consumer login proof. LLDAP has no conditional +membership mutation or generation token here: exact readback cannot exclude +ABA or a concurrent external writer, so T14 remains open. + +2026-10-17 verified source follow-through: source commit `4f65e41` passed normal +local scanning and all 65 canonical provisioner tests. The actual disposable +LLDAP receipt passed all 14 oracles; independent verification on the pinned +daemon found both exact names and the run label absent. Source integration +includes the ADR-0015 documentation correction; the journal remains local-only. + +T14 source preparation is underway. Twenty-three pinned public sources identify +NetKingdom implementation/reconciliation, Platform custody/database governance +and rapp-postgres consumer provisioning; the inspected runtime binding does not +assign identity-provisioner a package. The existing User Engine runtime token +and pod route are not a protected-case caller lane. The native proposal preserves +the same journal seam on a dedicated CNPG consumer and requires authoritative +scope binding, admitted role/lease/placement/recovery and all LDAP writers fenced. +The declared exclusive maintenance window is not mechanical exclusion of WebUI, +helper or lifecycle writers. Capacity placement and EUR cost/risk remain owner +decisions. No package, database, credential, rollout or owner assent was created. +Custodian evidence: group-provenance-r1/native-storage-proposal-r1; final source, +projection and cleanup review: resume-integration-r1/publication-verification.json. + + +## T21 — Implement the PostgreSQL journal and prove disposable SQL compatibility + +```task +id: NK-WP-0048-T21 +status: done +depends_on: [NK-WP-0048-T20] +priority: high +state_hub_task_id: "afe78e4d-408a-516a-ab92-e212e8a2d11d" +``` + +Completed 2026-10-17: bounded source preparation and actual disposable PostgreSQL +compatibility only. T14 retains native storage, custody, callbacks, authorization, +all-writer isolation and production admission. Coordinator source publication and +projection remain separate follow-through; no source revision or task UUID is +invented here. T20 remains the historical local SQLite/actual LLDAP proof. + +The eight-path source candidate adds an injection-only PostgreSQL implementation +behind the existing owner journal seam, with explicit trusted tenant/directory/ +case/source/principal/epoch binding and independent continuity callbacks. It +persists bounded intent/created-only receipts, exact-one version updates, durable +active fences, and a content-bound journal chain. SQL commits precede independent +exact-marker acknowledgment; lost replies/checkpoints poison continuation and +retain uncertainty. A counter-only checkpoint is explicitly insufficient for +divergent clones. Migration does not create database/schema/credential, admit an +epoch or clear quarantine; production HTTP activation is absent pending trusted +callbacks. Pinned psycopg package/image-copy closure is included, without claiming +an image build or native dependency admission. + +Candidate base `6cb43c0cc55212363a5406b02db046e2384a7c40`; final eight-path patch +`sha256:5549d2982b61ff70aba4b3928fecd5639db0f1a732ab2f8630f678e7acf4d34d`. +All 82 source tests pass, alongside 13 fixture plan/diagnostic checks and two +source-backed snapshot regressions. Counts overlap the same source and fixture; +they are not additive native acceptance or completion percentages. + +The independently reviewed R5 fixture ran the actual SQL migration/backend and +existing provisioner path against cached pinned disposable PostgreSQL. Directory +operations deliberately used the existing synthetic LLDAP test double. All +manifest-declared phase oracles passed: creation-only inventory and DDL refusal; +separate-process replay/revoke with complete preexisting/shared visible-state +preservation; foreign-context refusal; concurrent exactly-once reservation and +stale version refusal; actual database fork with divergent bodies at the same +counter and fresh-object rejection of the losing exact checkpoint; retained +uncertainty after checkpoint/directory/SQL response loss; and actual pg_dump/ +pg_restore rollback refusal against the independently retained fixture checkpoint. +The checkpoint is fixture-only, not an admitted native owner evidence holder. + +Only generated credentials/state were used, with two nonroot/read-only containers +on private loopback, bounded tmpfs/CPU/memory/PIDs/lifetimes, no host ports/mounts, +no pulls and no production contact. The safe receipt reports both exact owned +containers absent, with no daemon uncertainty. Independent final pinned-daemon +cleanup verification remains coordinator follow-through. + +Failed R2/R3/R4 receipts and source revisions remain retained. R2's helper failure +subphase is unknown; R3 exposed a real backend bug where replacing nested receipt +objects lost the created ID held by ensure, fixed by acknowledging only the backend +version after commit/checkpoint. R4 verified that fix through actual SQL bootstrap, +then exposed fixture snapshot aliasing and empty deleted-group backing entries; +source-backed regressions corrected the snapshot without weakening complete +visible-prestate equality, and reject nonempty orphan membership. These refinements +belong to one trial, not extra native acceptance successes. + +Custodian evidence: `docs/changes/netkingdom-target-2026-10-16/group-postgres-r1/`; +exact source/pre/postimages and retained predecessors; native R5 manifest +`sha256:d51f90816616848b93bcdc482e10a98d07061deb01eeed9e50700fc6e68b6051`; +`final-integration-r1/typed-execution-evidence.json` and byte-exact safe receipt +(original `/tmp/nk48-postgres-native-review-sguj7t8p/result.json`). No CNPG native +storage/HA/backup/lease/TLS/capacity/restore admission, authoritative protected +callbacks, real LDAP writer fencing/CAS/ABA exclusion, owner assent or production +endpoint activation is claimed. Those remain live T14 work. + + +## T22 — Establish registration recovery source gaps and complete descriptor review inventory + +```task +id: NK-WP-0048-T22 +status: done +priority: high +state_hub_task_id: "ea50d93b-0d04-55d2-98c4-9b6c3fe925b1" +``` + +Completed 2026-10-17: source-only owner-contract review and complete review +inventory. Inspected the actual registration adapter, Warden Journal/Executor, +NetKingdom evidence compatibility checker and Audit Core custody/read surfaces. +No existing admitted holder/readback supplies authenticated original-attempt +recovery: payload/creation are process-local, persisted evidence references are +hashed, and reconcile receives a new attempt without original context. The exact +four-owner contract requirements and non-replayable missing/ambiguous cases are +recorded; no recovery callback, publisher, credential grant or native trust root +was installed. T15 retains implementation and actual acceptance. + +The complete source1.2 inventory maps all seven follow-through steps to their +existing owners, missing exact contracts and created-only rollback requirements. +The actual maintained adapter regenerated the sole existing register-client +machine descriptor against the delivered 13,633,409-byte checker and current +sealed-memfd snapshot source. The checker rehash and Warden Descriptor.decode +round-trip passed; six owner descriptors remain absent. Actual source commands +render canonical1.1 and explicitly selected1.2 with execution.permitted false; +ordinary1.2 case admission still refuses its acknowledged/unimplemented type. +Canonical type/case/acknowledgment bytes were hash-checked unchanged. No tests were +added or rerun, no native configuration or effect occurred, and no owner assent +or complete machine bundle is claimed. T16 retains native executable custody, +complete exact descriptors, independent source review and normal version/type +admission. This extracted outcome supplies no native gate completion credit. + +Evidence in the-custodian: +`docs/changes/netkingdom-target-2026-10-17/native-registration-r1/`, especially +original-attempt-review.json, descriptor-composition.json, source-verification.json, +source-manifest.json and manifest.json. The prior sixteen-gate audit remains +immutable. Publication, normal task UUID allocation and projection are separate +coordinator follow-through; no Hub UUID is assigned by this source record. + + +## T23 — Review native journal package assignment and storage integration boundaries + +```task +id: NK-WP-0048-T23 +status: done +priority: high +state_hub_task_id: "6e7cd483-c7af-53a2-9d0a-3d76b6fb49ee" +``` + +Completed 2026-10-17: source/package placement and protected integration review +only, separate from T21's completed PostgreSQL source and disposable-SQL proof. +The owner review recommends a dedicated independently rolled-back +`rapp-identity-provisioner`, owned by NetKingdom on rail-kubernetes, with a +schema-valid draft declaration and no reef binding. No repository, registry +entry, native package assignment or owner assent was created. Existing +rapp-user-engine ownership/token delivery does not assign it the provisioner. + +The complete current rapp-postgres catalog has six declared consumers: four on +platform-pg and two on platform-pg-2. The inert renderer-valid provisioner +candidate fits platform-pg-2 at three of four, requesting one migration plus +four runtime connections; placing it on the first cell is refused as a fifth. +Normative package schema and full-catalog capacity/render checks passed. The +four-consumer ceiling is estimated, not measured native headroom, and no current +SQL session, backup or restore acceptance is claimed. + +The exact review identifies remaining package/substrate, SQL role/lease/TLS, +network and backup/recovery contracts; independently authenticated protected +scope callbacks; an external content-bound checkpoint holder with atomic +advance/loss handling; and all-LDAP-writer exclusion or provider CAS/generation, +including lifecycle cascades and remove/readd ABA. Generic Audit custody is not +that checkpoint service. A database-only fence, final reread or one replica +cannot exclude foreign directory writers. The native acceptance/recovery matrix +keeps storage/lease admission separate from separately approved directory +mutation; the production activation remains disabled pending trusted integration. + +Evidence in the-custodian: +`docs/changes/netkingdom-target-2026-10-17/native-storage-r1/OWNER-REVIEW.md`, +source-validation.json and hash-bound source-references.json, plus the inert +proposed declaration/consumer and renderer outputs. No native resource, lease, +credential, deployment, destructive effect, independent holder or native owner +acceptance was delivered. T14 retains these live gates with its original UUID +and progress status. This completed review adds no duplicate T21 acceptance +credit; publication, task UUID allocation and projection remain coordinator +follow-through and no UUID is hand-assigned here. + +## T24 — Ensure-groups adapter source candidate + +```task +id: NK-WP-0048-T24 +status: done +priority: high +state_hub_task_id: "1a3931ee-3b5f-52fc-9295-4073804f4bd2" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): ensure-groups-adapter.py, 8 tests (commit 5a2e7d6). Binds to the T14 PostgreSQL journal and refuses SQLite or a foreign binding. It accepts only canonical 1.1 cases, so T30 must add an explicit 1.2 path. It calls the provisioner's private `_group_binding`; a public helper is the cleaner shared edit. + +Split from T16 on 2026-10-17 so each candidate fits one per-task budget. Bind group creation to T14's journal/provenance seam; refuse without trusted journal callbacks. +Source candidate only, modelled on the T13 registration adapter +(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and +`registration-adapter.md`). Requires positive and refusal fixtures and +created-only rollback. Keeps canonical 1.1 as the default and uses no +`--allow-unimplemented` admission. Done means the candidate and its tests are +committed. NetKingdom (directory) owner source review before pinning, and any native action, +stay separate gates and are not supplied by this task. + +## T25 — Mint-secret adapter source candidate + +```task +id: NK-WP-0048-T25 +status: done +priority: high +state_hub_task_id: "0de2a7d9-874d-578b-a076-f653fe42e30b" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): mint-secret-adapter.py, 9 tests (commit 558c5bc). Value-free receipts only. The receipt schema `railiance-platform.custody-mint.v1` and the mint/destroy capability interface are proposals that Platform must confirm. + +Split from T16 on 2026-10-17 so each candidate fits one per-task budget. Platform-owned custody evidence seam: the adapter records value-free mint evidence only and never handles secret values. +Source candidate only, modelled on the T13 registration adapter +(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and +`registration-adapter.md`). Requires positive and refusal fixtures and +created-only rollback. Keeps canonical 1.1 as the default and uses no +`--allow-unimplemented` admission. Done means the candidate and its tests are +committed. Platform owner source review before pinning, and any native action, +stay separate gates and are not supplied by this task. + +## T26 — Deliver-secret adapter source candidate + +```task +id: NK-WP-0048-T26 +status: done +priority: high +state_hub_task_id: "94b3cbf0-c908-5ac6-b0db-67385c3e021c" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): deliver-secret-adapter.py, 8 tests (commit 1b60f6e). The lane `platform-secret-delivery` and the schema `netkingdom.deliver-secret.v1` are proposals that Platform must confirm. + +Split from T16 on 2026-10-17 so each candidate fits one per-task budget. Platform-owned delivery evidence seam (ESO/custody), value-free. +Source candidate only, modelled on the T13 registration adapter +(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and +`registration-adapter.md`). Requires positive and refusal fixtures and +created-only rollback. Keeps canonical 1.1 as the default and uses no +`--allow-unimplemented` admission. Done means the candidate and its tests are +committed. Platform owner source review before pinning, and any native action, +stay separate gates and are not supplied by this task. + +## T27 — Bind-env adapter source candidate + +```task +id: NK-WP-0048-T27 +status: done +priority: high +state_hub_task_id: "e56365f8-29b5-5c89-a329-8115f9f3b2a7" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): bind-env-adapter.py, 5 tests (commit 6fbce73). Binds references only. To fit the budget it does not yet wrap a full Warden `Adapter` like T13 (source-digest pin, authenticated evidence, parent-session revocation); it returns `(reference, run, rollback)`. That wrapping is a prerequisite for T30. + +Split from T16 on 2026-10-17 so each candidate fits one per-task budget. Bind the issuer Deployment env reference to the delivered secret reference, without values. +Source candidate only, modelled on the T13 registration adapter +(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and +`registration-adapter.md`). Requires positive and refusal fixtures and +created-only rollback. Keeps canonical 1.1 as the default and uses no +`--allow-unimplemented` admission. Done means the candidate and its tests are +committed. NetKingdom runtime owner source review before pinning, and any native action, +stay separate gates and are not supplied by this task. + +## T28 — Deliver-consumer-secret adapter source candidate + +```task +id: NK-WP-0048-T28 +status: done +priority: high +state_hub_task_id: "6dab281c-ea9a-5a17-a83f-e8e9a4d4484f" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): deliver-consumer-secret-adapter.py, 5 tests (commit 650c1ac). Type 1.2 only. It assumes the owner has validated ESO TokenRequest and the controller-RBAC admission; that stays an owner gate. + +Split from T16 on 2026-10-17 so each candidate fits one per-task budget. Consumer-side delivery evidence seam, value-free. +Source candidate only, modelled on the T13 registration adapter +(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and +`registration-adapter.md`). Requires positive and refusal fixtures and +created-only rollback. Keeps canonical 1.1 as the default and uses no +`--allow-unimplemented` admission. Done means the candidate and its tests are +committed. Platform and Apps owner source review before pinning, and any native action, +stay separate gates and are not supplied by this task. + +## T29 — Hand-over adapter source candidate + +```task +id: NK-WP-0048-T29 +status: done +priority: high +state_hub_task_id: "129db41d-4560-5e20-9f3b-c3c91e0a5601" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): handover-adapter.py, 8 tests (commit ec63539). The owner `apps` and lane `apps-consumer-handover` are proposals that the Apps owner must confirm. It has no reconcile path; restart handling follows T15. + +Split from T16 on 2026-10-17 so each candidate fits one per-task budget. Apps-owned consumer verification and hand-over receipt seam. +Source candidate only, modelled on the T13 registration adapter +(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and +`registration-adapter.md`). Requires positive and refusal fixtures and +created-only rollback. Keeps canonical 1.1 as the default and uses no +`--allow-unimplemented` admission. Done means the candidate and its tests are +committed. Apps owner source review before pinning, and any native action, +stay separate gates and are not supplied by this task. + +## T30 — Compose the descriptor set over the reviewed candidates + +```task +id: NK-WP-0048-T30 +status: done +depends_on: [NK-WP-0048-T24, NK-WP-0048-T25, NK-WP-0048-T26, NK-WP-0048-T27, NK-WP-0048-T28, NK-WP-0048-T29] +priority: high +state_hub_task_id: "6f6073ac-52dd-5b70-8874-6d76d4c072e4" +``` + +2026-10-17 done: coordinator review r3 (the-custodian `nk48-adapters-1.2-review/coordinator-review-r3.json`, +fd07eaa) approved all nine files at ff8f597, with R2-F1..F3 resolved, an independent +reconstruction of 117 passing and no open findings. This is a source review only: +it grants no owner admission, pin or native authority. Owner review and admission +continue under T16, which also takes the r2 observations, including the +`warden_executes: false` steps that carry execution descriptors. + +2026-10-17 coordinator review r2 (the-custodian `nk48-adapters-1.2-review/coordinator-review-r2.json`) +approved five files and rejected three, plus the artifact. The fixes are merged in +2ee4ed0: R2-F1, mint-secret revokes the session and the broker grant +independently (eeed8e1); R2-F2, deliver-secret binds to the admitted case and +derives its Secret and custody path, with no caller reference (640292c); R2-F3, +bind-env pins its five-file closure, and a new closure-completeness test checks +every adapter (d16db30). Each regression test failed on 68f4bbb. The artifact is +regenerated, and the full suite including kernel admission passed 117. Next is +the r3 review. Open for owner review under T16: the type marks four steps +`warden_executes: false`, yet each has an execution descriptor. + +2026-10-17 founder confirmed every proposed lane and schema name for T25-T29 +as-is. The artifact records them as `names_confirmed` (by founder, 2026-10-17), +and no step is still `owner_confirmation_required`. Next: independent source +review of the seven adapters. + +2026-10-17 founder decision (supersedes the `agent` decision below): the +mint-secret actor is `human`. Coordinator review r1 (the-custodian +`docs/changes/netkingdom-target-2026-10-17/nk48-adapters-1.2-review/coordinator-review-r1.json`, +finding NK48-R1-F1) showed that unchanged kernel a45cf47 refuses every +`effect: mint` admission whose actor is not `human` ("agent mint is +forbidden"), so the `agent` descriptor could never be admitted. Adapter, tests +and composer comment updated and the artifact regenerated (mint-secret +descriptor digest now `sha256:8c40c2884f8ba15e8cb6c98f33419c395e54fce8ef33d6e1a34782cf3f1fcfa9`); +98 tests pass against a45cf47. The set needs an independent r2 review. + +2026-10-17 `test_admit_1_2_adapter_descriptors.py` runs Warden's own +`Executor.admit` on all seven composed 1.2 descriptors, with the real 1.2 +type, the 1.2 forgejo example case, the reviewed `BrowserInputs` validator and +fixture-only trust (stand-in adapters, nothing dispatched). It also pins that +an agent mint descriptor is refused ("agent mint is forbidden") and that a +wrong lane is refused for every step. Against the old `agent` revision +(582ab9f) it fails as expected. Combined suite: 102 passed on ops-warden a45cf47 +and on current ops-warden. Fixture admission is not owner admission or a pin. + +2026-10-17 founder decision: the mint-secret actor is `agent`, not `warden`. +The kernel is unchanged. The adapter, its test and the composer were updated and +the artifact regenerated; all seven steps pass `Descriptor.decode` and +`complete_executable_descriptor_set` is true (98 tests passed). The artifact +stays `not_an_admission`, with no owner review, admission or dispatch permission. + +2026-10-17 composer merged (60b41a1 via 3c62bec) with follow-up 376b44b (merged; +combined suite 98 passed). `compose-1.2-descriptors.py` loads 1.2 only through an +explicit version root and refuses partial, duplicate, unknown or mismatched maps. +`descriptor-composition-1.2.candidate.json` is a reviewable candidate, marked +`not_an_admission`, with canonical 1.1 unchanged. ensure-groups and hand-over now +carry and honour `parent-session` revocation, and the hand-over owner is +parameterised (`railiance-apps` for Forgejo). Six of the seven steps pass +`Descriptor.decode`. mint-secret is still refused: its actor `warden` is outside +the kernel's human/agent set, and Platform (with ops-warden, if the kernel should +change) must decide. Five steps carry `owner_confirmation_required` names. The +set is complete only after both. Owner review, the canon section 6 successor +revision and admission remain T16's. + +2026-10-17 prerequisites merged (65559ef; combined suite 78 passed): +ensure-groups accepts 1.2 via an explicit `version_root='1.2'` (531151b), with +1.1 still the default. bind-env is now a full Warden v1 `Adapter` (7442d21); its +kernel-level fixture tests like T13's are still missing, and its `build()` +signature changed. Composition is unblocked in source. Owner confirmation of +the proposed step, lane and schema names remains before pinning. + +2026-10-17 inputs from T24–T29 that composition must resolve: T24 accepts +only 1.1 while T28 accepts only 1.2, so add an explicit 1.2 ensure-groups path; +T27 still needs full Warden `Adapter` wrapping; the lane, schema and owner names +proposed in T25, T26 and T29 need owner confirmation before pinning. + +Split from T16 on 2026-10-17. Compose the seven-step source 1.2 descriptor set +through the existing explicit version-root selection over T13 and T24–T29. No +partial descriptor map, canonical 1.1 stays the default, and new descriptor bytes +need a reviewed successor revision under canon section 6. Owner review and +admission remain T16's. + +## T31 — Restart and lost-receipt fixtures against Warden's original-intent interface + +```task +id: NK-WP-0048-T31 +status: done +priority: high +state_hub_task_id: "ed1ccd87-89a0-5e69-885c-0eaf5ab0bb5a" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): test_registration_original_intent_reconcile.py, 8 tests (commit 74adcd7), runs against the unmodified ops-warden a45cf47. The holder is a test-only in-memory store. For real T15 support the T13 adapter's `build()` needs an injected holder/readback capability, a `reconcile_original` callback, and a `verify` that accepts reconcile results. Rollback after restart stays refused until there is an owner contract. + +Split from T15 on 2026-10-17. ops-warden published the original-intent +lookup/context interface in `src/warden/case_run.py` (`OriginalIntent`, +`Journal.original_intent`, `Adapter.reconcile_original`; commit `a45cf47`). +Build T15's restart/lost-receipt fixtures for the registration adapter against +it: the original intent is kept separate from the reconcile attempt; wrong +attempt/case/source/principal and unavailable-holder cases refuse; and a missing +or ambiguous record stays uncertain, with no add replay or undo. The interface +is Warden's own dispatch record, not owner-authenticated created-only provenance. +Selecting the actual authenticated holder stays a T15 owner gate. + + +## T32 — Restore independent agent-mint admission regression coverage + +```task +id: NK-WP-0048-T32 +status: done +priority: high +state_hub_task_id: "c6d0a27e-f6a7-5df0-95f6-3e7473d38bb5" +``` + +Completed 2026-10-17, source tests only. Extracted from T16's coordinator r5 +finding NK48-R5-F1; no earlier adapter outcome is counted again. The real 1.3 +admission test now separately checks the committed descriptor map, an in-memory +fixture map acknowledging the changed agent descriptor to reach the independent +mint prohibition, and the human descriptor under an explicit fixture agent +runtime marker. Only `sso-mfa/k8s/keycape/test_admit_1_3_type.py` changes. + +Against frozen Warden `117fec9241e7b985dfb187b992e173d0a5dc3700`, the new +suite passes 10 tests / 32 subtests. Independent mutation evidence removes only +the mint admission guard in scratch: both new guard tests fail while eight +controls pass; the old suite passed that mutant. Custodian independently reran +the canonical successor: 10 tests / 32 subtests pass. Evidence: +`the-custodian/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/` +(patch, frozen/mutant results and canonical-validation.json). No descriptor, +parameter pin, catalog, acknowledgment, kernel or native execution changed. +T16 still owns actual composition and admission; F2/F3 follow below. + +## T33 — Close the issuer-store reference finding with Platform source + +```task +id: NK-WP-0048-T33 +status: todo +priority: high +state_hub_task_id: "5ace28ff-a4cb-5647-a8f6-3cd54bb9d0a5" +``` + +Owner: Platform for the fixed store/template source and its delivery boundary; +NetKingdom for the 1.3 type reference. Split from T16's current coordinator r5 +finding NK48-R5-F2. Next action: independently review the prepared Platform +candidate and publish a concrete owner-controlled issuer store kind/name/scope +and template reference, then integrate only that exact proposed source into the +1.3 deliver-secret entry point and regenerate its composition artifact. Candidate +and current review evidence are under Custodian +`docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/` +and `nk48-r5-f2-review-r1/`. + +Completion requires exact committed source, a passing independent source review +and unchanged type parameter bytes. No consumer store may be inferred as issuer +store, and no source declaration supplies owner assent, installed ESO support, +CCR/grant changes or a real delivery capability. Those remain existing +RPF-1.3-T4 and T16 admission/native requirements. + +## T34 — Close the consumer configuration inverse finding + +```task +id: NK-WP-0048-T34 +status: todo +priority: high +state_hub_task_id: "37f43c40-d014-544e-aa0b-5219dd4a6b93" +``` + +Owner: Apps for consumer configuration and inverse; NetKingdom for exact type +placement. Split from T16's coordinator r5 finding NK48-R5-F3. Next action: +independently review and implement the owner transaction/inverse through the +existing RAPPS-WP-0016-T05 path, with authenticated durable before/after release +provenance, exclusive serialization of all release writers and a fresh authorized +attended session for every inverse attempt after the hand-over session is revoked. +The inverse must remain reachable for public clients, confidential NO_CHANGE +secret delivery and verification failure before a hand-over receipt exists. + +Completion requires real owner configuration/inverse source bindings and +independent regressions, then exact reviewed 1.3 entry/rollback text and composed +artifact. A synthetic wrapper with an injected backend alone is insufficient. +Keep hand-over receipt rollback receipt-only and secret deletion created-only; +preexisting or drifted resources must refuse unsafe inverse. Candidate evidence: +Custodian `docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/` +and `nk48-r5-f3-review-r1/`. Source review, owner acknowledgment and native proof +remain distinct. T16's final composition/admission and T06's native first case +are not completed by this refinement. diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-source-followthrough-r1/workplan.candidate.md b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-source-followthrough-r1/workplan.candidate.md new file mode 100644 index 0000000..2a4d50c --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-source-followthrough-r1/workplan.candidate.md @@ -0,0 +1,1606 @@ +--- +id: NK-WP-0048 +type: workplan +title: "Establish SecurityCaseTypes so that known security cases are light" +domain: infotech +repo: net-kingdom +status: active +flavor: implementation +owner: claude-code +topic_slug: netkingdom +created: "2026-10-08" +updated: "2026-10-17" +depends_on: [] +related_repos: + - ops-mason + - ops-warden + - key-cape + - railiance-platform + - informed-decision +state_hub_workstream_id: "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" +--- + +# NK-WP-0048 - Establish SecurityCaseTypes so that known security cases are light + +## Why + +The founder's direction (2026-10-08): setting up a security integration of a +kind that has been done before should be easy, meaning accept the case, then +ops-mason arranges it and ops-warden follows through. Setting up a new kind +needs ample documentation and tests and an informed-decision, because its shape +is part of the security architecture. + +Today every integration is hand-built: per-client helper scripts, per-lane +custody, founder checklists. The trigger was KEY-WP-0038 (Forgejo as an OIDC +client), whose next step was going to be yet another hand-written rollout +script. + +This plan continues the pattern work of NK-WP-0008 (pattern infospace), +NK-WP-0009 (tutorials) and NK-WP-0030 (scenario composition). It turns patterns +into **SecurityCaseTypes** that ops-mason and ops-warden can execute. Canon: +`canon/standards/security-case-types_v0.1.md` (proposed). + +Companion plans: ops-mason `MASON-WP-0010` (materialization) and ops-warden +`WARDEN-WP-0044` (follow-through). + +## Tasks + +```task +id: NK-WP-0048-T01 +status: done +priority: high +state_hub_task_id: "930ac3e9-20ec-5473-98cd-3fe8fddb730f" +``` + +### T01 — Accept the terminology and the canon + +Founder review of `canon/standards/security-case-types_v0.1.md`, including its +section 10 open questions. On acceptance the canon status goes from `proposed` +to `accepted`. + +2026-10-08: accepted by the founder (in session). Canon status `accepted`; +section 10's open questions are carried forward to the first type (T04, T05). + +```task +id: NK-WP-0048-T02 +status: done +priority: high +state_hub_task_id: "19c25d96-a55c-58f0-9168-3fdbc4955fc2" +``` + +### T02 — Inventory the de-facto types already built by hand + +Read-only. List every security integration built so far and group them by shape. +Known starting points: + +- browser OIDC relying parties on key-cape: informed-decision-approver, + coulomb-social, user-engine portal, openbao-admin, and Forgejo (pending) +- confidential service clients with `client_credentials`: secrets-engine + approval, requester and openbao clients, approval-engine operator +- workload KV read lanes on OpenBao: the railiance-platform credential lane + designs and CCRs, for example `railiance-backup-offsite-lane` +- short-lived SSH certificates through ops-warden (`warden sign`) +- tunnelled operator access (ops-bridge) + +For each: the repositories involved, the hand-built steps, attended human steps, +custody paths, verification evidence, and rollback (if any). Output +`docs/security-case-type-inventory.md`, ranking candidate types by how often the +shape recurs and how much manual work each instance costs today. + +2026-10-08: done, `docs/security-case-type-inventory.md`. 36 of 37 CCRs are +already `request_type: workload-kv-read`, a de-facto type reviewed each time as +if it were new. Browser clients: 5 hand-built plus Forgejo, each with its own +rollout script. Confidential service clients need a registration plus two or +three KV-read CCRs each. A forge read identity is needed in bulk by +ORGREF-WP-0002-T06. Recommendation: oidc-browser-relying-party first (unblocks +the forge), workload-kv-read second (largest payoff), then forge-read-identity. + +```task +id: NK-WP-0048-T03 +status: done +depends_on: [NK-WP-0048-T01] +priority: high +state_hub_task_id: "e487975b-73b1-516c-971d-da4cbbb93063" +``` + +### T03 — Schema, template and validator + +- `canon/schemas/security-case-type_v0.1.schema.json` and + `security-case_v0.1.schema.json` +- `security-case-types/_template/` with `type.yaml`, `README.md` and tests to copy +- a validator, extending `tools/security-scenario-composer/`, that checks a type, + checks a case against its type, and renders the case into the composer's + deterministic plan (still `execution.permitted: false`) + +Acceptance: the validator rejects missing sections, secret-shaped parameter +values, unpinned providers and cases against non-`implemented` types, with tests +for each. + +2026-10-08: done. `canon/schemas/security-case-type_v0.1.schema.json` and +`security-case_v0.1.schema.json`; `tools/security-case-types/security_case_validator.py` +(`validate-type`, `validate-case`, `render`, reusing the scenario composer); +`security-case-types/_template/` (a valid, runnable example type over +`net-kingdom.local-identity`, with README sections, an example case and tests that +travel with every copy). 31 tests pass, including the composer's existing suite. + +```task +id: NK-WP-0048-T04 +status: done +depends_on: [NK-WP-0048-T02, NK-WP-0048-T03] +priority: high +state_hub_task_id: "2fffe112-7d55-5680-9952-78d74f44f87f" +``` + +### T04 — Define the first type: browser OIDC relying party + +`security-case-types/oidc-browser-relying-party/`, version 1.0: + +- parameters: client id, display name, exact redirect URIs, scopes, client type + (confidential or public), MFA pin, the groups the consuming service maps, + custody path for the secret (confidential only), consuming service and its + owner +- composition: key-cape client registration (key-cape declares it as a playbook + capability), secret minting into custody (railiance-platform), LLDAP group + existence and membership (net-kingdom identity-provisioner), handover to the + consuming service +- materialization (ops-mason) and follow-through (ops-warden) sections +- verification: discovery lists the redirect URI; a test sign-in returns the + expected claims for one human; anonymous access to the custody path is denied +- rollback: remove exactly this client and its custody path +- README: threat model, MFA rationale, why redirect URIs are exact, which steps + still need an attended login and how that is minimised +- reference instances: informed-decision-approver, coulomb-social, user-engine + portal, openbao-admin + +Acceptance: complete per canon section 3, tests green, status `proposed`. + +2026-10-08: done. `security-case-types/oidc-browser-relying-party/` 1.0 (proposed): +`type.yaml` with 12 parameters (the custody reference is required only for +confidential clients), composition over key-cape's new +`key-cape.oidc-client-registration` declaration (contract PASS, `maturity: +planned`), materialization and follow-through, verification and rollback, all +conditional on client type where it matters. One attended step, +`register-client`, justified by the live registry being a Kubernetes Secret. +README with threat model, alternatives, an honest fit table (portal and vergabe +fit; Forgejo is the first confidential case; openbao-admin, coulomb.social and +informed-decision-approver do not fit 1.0), and known limits. Examples: Forgejo +(checked against the accepted request) and a public portal. The validator gained +`when` / `required_when` conditions and provider discovery. 43 tests pass. +ops-mason's and ops-warden's sections are drafted in the type. Their owners +review them in MASON-WP-0010-T01 and WARDEN-WP-0044-T01 before acknowledgment +(T05). + +```task +id: NK-WP-0048-T05 +status: done +depends_on: [NK-WP-0048-T04, MASON-WP-0010-T01, WARDEN-WP-0044-T01] +priority: high +state_hub_task_id: "3d987d3d-0797-538b-86e4-51185a327ae2" +``` + +### T05 — Acknowledge the type through informed-decision + +Prepare the Decision Memo from the type's README and `type.yaml` and submit it +through informed-decision's production intake. On acknowledgment the type +moves to `acknowledged`. + +2026-10-08: Decision Memo prepared. Hub decision `e418263e-a71f-4f01-a970-c87c9e187c09`. +`security-case-types/oidc-browser-relying-party/decision/`: `packet.md` (question, +one act, what acknowledging does not do, the shape, alternatives, attended +steps, limits, evidence), `build_intake.py` and the built `intake.json` (memo +class `decision-record`, review group `net-kingdom-admins`, resolves the hub +decision on answer). The envelope passes informed-decision's own +`validate_submission`. Queuing it is an attended operator step, a write inside +the production dispatcher pod: + + kubectl -n state-hub exec -i deploy/state-hub-memo-dispatch -- \ + python -m api.services.memo_dispatch --queue /data/private enqueue --envelope /dev/stdin \ + < security-case-types/oidc-browser-relying-party/decision/intake.json + +The answer then appears for review in informed-decision. + +2026-10-08: queued by the founder (`pending`, `queued`) after one refusal: the +first envelope's idempotency key was not State Hub's stable +`state-hub..` form, and the builder now validates against both +sides. The dispatcher delivered it to informed-decision (ledger 7 → 8 +delivered). T05 waits for the founder's answer in informed-decision, which +resolves hub decision `e418263e`. + +2026-10-08: ops-mason's review (MASON-WP-0010-T01) folded into the type, still +1.0 `proposed`: `secret_ref` is no longer a parameter, the type derives +`platform/workloads/keycape/{client_id}`; policies are +`workload-kv-read-keycape-{client_id}-eso` and +`workload-kv-read-keycape-{client_id}-{consuming_service}`, each with a templated +body (`read` on that one data path); new `openbao_role` +`external-secrets-keycape-{client_id}` (Kubernetes auth, ESO service account, +15m, no default policy); catalog entry `{consuming_service}-keycape-client`; +the two rollback steps on the KV path collapse into one metadata delete. The +schema and validator now require a `body` on policy and role items. 105 tests +pass. Two consequences: the delivered memo describes the type before these +changes (a case-set custody reference, two differently named policies, no ESO +role), so the founder answers on a superseded shape unless the memo is +re-sent; and Forgejo's custody path moves from the accepted request's proposed +`platform/workloads/forgejo/netkingdom-oidc-client` to +`platform/workloads/keycape/forgejo`, which key-cape (KEY-WP-0038) and +railiance-apps (RAPPS-WP-0016-T02) need to adopt in T06. ops-warden's review +(WARDEN-WP-0044-T01) is still in progress. + +2026-10-08: revised memo `nk-wp-0048-t05-oidc-browser-relying-party-r2` built +(`packet.md`, `intake.json`, packet `sha256:af6435e2…`); it states that it +supersedes the first memo and passes informed-decision's and the dispatcher's +validation. It needs a new memo id because the dispatcher refuses new content +under a delivered idempotency key (`idempotency_conflict`). Queuing it is the +same attended step as above. Both memos serve hub decision `e418263e`; the +first answer resolves it, so the first memo must not stay open: an accept on it +would acknowledge the superseded shape, and a decline would resolve the +decision against r2. Close it with `return` (a reviewer verb that requests a +new revision and does not resolve the hub decision; only accept and decline +do), commenting that r2 supersedes it, then answer r2. `withdraw` exists in +the vocabulary but is not offered to reviewers, and lifecycle `revise` only +applies to lifecycle-registered memos, not intake-delivered ones. + +2026-10-09: the founder reports r2 enqueued and the first memo returned. At +close, hub decision `e418263e` still showed `open` (not verified whether r2 +was answered or the answer is still in transit; the dispatcher ledger was not +read). ops-warden's review (WARDEN-WP-0044-T01, ops-warden +`history/2026-10-08-oidc-rp-follow-through-review.md`, d470f2e) arrived at +21:25, before r2 was built, and is not in r2. Required before WARDEN-WP-0044-T02: +R1 a per-step `lane` (missing: identity-provisioner caller credential, +railiance-platform broker grant), R2 canon meaning of `warden_executes: true` +(caller proxy, caller identity, no standing ops-warden token; ADR-0002), R3 a +per-step `value_handling`. Refinements R4 (one type-level catalog lane instead +of a per-case entry, which conflicts with ops-mason's item 5), R5 (ESO Ready gate +before bind-env), R6 (exact accepted memberships), R7 (mfa-pinned and +secret-delivered checks). Next: if r2 was accepted, fold R1-R3 and R5-R7 in as +a follow-up and settle R4 between ops-warden and ops-mason before T06; if not, +hold r2, fold them in and build r3. + key-cape and railiance-apps were told about Forgejo's new custody +path. + +2026-10-09: r2 was not answered (hub decision `e418263e` open; dispatcher 9 +delivered), so r2 is held and r3 replaces it. ops-warden withdrew R4 for R4′ +(message a66a9204): one catalog entry per case with its body templated in the +type. Folded in: + +- Canon section 3: a narrow meaning of `warden_executes: true` (caller proxy, + caller identity, no standing credential; ops-warden ADR-0002), plus per-step + `lane`, `value_handling` and an optional `ready_check`. A catalog entry body is + now required, like policy and role bodies. +- Schema and validator enforce all of these. +- The type: lanes `identity-provisioner-groups` and + `keycape-client-secret-generate` (both new) and `key-cape-oidc-login`; + `mint-secret` is `generated_in_custody`; a Ready gate on `deliver-secret`; + `ensure-groups` adds exactly the accepted case's memberships; new checks + `secret-delivered` and `mfa-pinned`; the catalog-entry body follows R4′. +- 110 tests pass. + +Memo r3 (`nk-wp-0048-t05-oidc-browser-relying-party-r3`, packet +`sha256:726dfc01…`) passes informed-decision's and the dispatcher's validation. +The founder queues it with the same command, returns r2 with a comment that r3 +supersedes it, then answers r3. + +Prerequisites for T06, not for T05: +- the two new lanes, each through its owner; +- a founder-approved rollout of an identity-provisioner image containing + 630ebfd (the deployed one is main-6c4fcaf from 2026-09-24, without + `/v1/groups/ensure`). + +ops-mason's request to create LLDAP group `fluid-telegram-operators` (message +259ec26d) was answered: not possible from here by that path; the founder creates +it in the LLDAP admin UI. + +2026-10-09: ops-warden finished its side of WARDEN-WP-0044-T01 (message +`58d33f40`, commit 0e7ceb8): the revised type conforms to `warden case +check-type`, with no further changes asked of the type. This acknowledges that +review. Its later addendum R8 (message `af274783`, ops-warden +`history/2026-10-08-oidc-rp-follow-through-review.md`) showed that lane +`identity-provisioner-groups` cannot exist as declared: the provisioner's only +HTTP credential is user-engine's runtime token, and its NetworkPolicy admits +only user-engine pods, so a caller-proxy lane would act as user-engine. Taken +option 1 (ops-warden's recommendation): `ensure-groups` and its rollback are +attended, in the `register-client` session on `key-cape-oidc-login`, run inside +the provisioner pod; no token is read. Option 2 (an operator credential of its +own on the provisioner) stays open for a later version. `warden case +check-type` now reports only `keycape-client-secret-generate` +(railiance-platform) as missing. 89 tests pass. + +Hub decision `e418263e` was still open, so memo r4 +(`nk-wp-0048-t05-oidc-browser-relying-party-r4`, packet `sha256:452ea421…`) +replaces r3; it passes informed-decision's and the dispatcher's validation. The +founder queues it with the same command, returns r2/r3 if still open with a +comment that r4 supersedes them, then answers r4. + +Prerequisites for T06 are now: lane `keycape-client-secret-generate` +(railiance-platform), and a founder-approved rollout of an identity-provisioner +image containing 630ebfd. + +2026-10-09: done. The founder queued r4 (`pending`, `queued`), returned r3 as +superseded and accepted r4 in informed-decision. informed-decision's hub +write-back is off in production (`INFD_HUB_WRITEBACK` unset), so net-kingdom +resolved hub decision `e418263e` from the founder's report, naming memo r4 in +the rationale. The type is `acknowledged`; it becomes `implemented` with T06. + +```task +id: NK-WP-0048-T06 +status: wait +depends_on: [NK-WP-0048-T05, NK-WP-0048-T08, NK-WP-0048-T09, NK-WP-0048-T12, NK-WP-0048-T13, NK-WP-0048-T14, NK-WP-0048-T15, NK-WP-0048-T16, NK-WP-0048-T17, NK-WP-0048-T18, NK-WP-0048-T20, MASON-WP-0010-T03, WARDEN-WP-0044-T03] +blocking_reason: "Type1.2 architecture has a confirmed source-only founder acknowledgment; canonical1.1 remains unchanged/default and the1.2 reference case stays proposed. Needs explicit version selection/reference-source integration without normal admission bypass, closed owner adapters plus global/native receipt verifier (WARDEN-WP-0044-T03), independently verified six-object structural materialization and CCR42 claims/created-only provenance, exact broker/case/source reconciliation, then separately authorized native mint/delivery and attended registry/consumer verification. Source acknowledgment, a bundle or local tests do not prove implemented/materialized/live status." +priority: high +state_hub_task_id: "d9b26bcf-a7c6-59a2-92a0-f913d76a203c" +``` + +### T06 — First KnownSecurityCase: Forgejo + +Write `security-cases/forgejo-netkingdom-oidc.yaml` from the parameters the +founder accepted in KEY-WP-0038-T01 (`key-cape/docs/forgejo-oidc-client-request.yaml`), +so that acceptance carries over as the case acceptance. ops-mason materializes it; +ops-warden follows through and verifies it. The type becomes `implemented` once +this reference case is live. + +This replaces the hand-built path proposed for KEY-WP-0038-T02. + +2026-10-09: case written, `security-cases/forgejo-netkingdom-oidc.yaml`, status +`accepted`: the example's parameters plus the founder's 2026-10-08 acceptance of +the key-cape request (owner and founder), with the custody-path derivation +covered by the type's acknowledgment (memo r4). It validates and renders (plan +`execution.permitted: false`; three attended steps on `key-cape-oidc-login`); +it needs `--allow-unimplemented` until it is live, since it is the case that +makes the type `implemented`. A test keeps it equal to the type's example. +Still open before it runs: ops-mason renders and builds it (MASON-WP-0010-T02, +T03, unblocked by T05), ops-warden plans and runs it (WARDEN-WP-0044-T02, T03), +railiance-platform's `keycape-client-secret-generate` lane (asked by ops-mason +`789feb39` and ops-warden `d9f889b1`, both unread), and a founder-approved +identity-provisioner rollout containing 630ebfd. + +2026-10-09: identity-provisioner rolled out, founder-approved in session: +`main-630ebfd` (`sha256:bafb4860…`, built by the image workflow) replaces +`main-6c4fcaf` (`sha256:ffacd5d7…`; only change: 630ebfd, additive). Applied +with `kubectl set image` on the Deployment only (generation 17 → 18, 1/1 Ready); +the file's Service, NetworkPolicies and Ingress were not reapplied. Verified in +the pod: `ensure_service_groups` and `revoke_service_groups` present, `/readyz` +200; no token used, no group changed. Rollback: `kubectl -n sso set image +deploy/identity-provisioner provisioner=forgejo.coulomb.social/coulomb/identity-provisioner@sha256:ffacd5d790a5b7800e0535f3aa95b7de52c7a68e4f575b9192c3e1622fa672c2`. +The pin in `sso-mfa/k8s/identity-provisioner/deployment.yaml` follows. + +2026-10-09: ops-warden closed WARDEN-WP-0044-T01 and shipped `warden case plan` +(T02, message `06303d1d`); the accepted Forgejo case is refused until ops-mason +materializes it, and a materialized copy plans cleanly once +`keycape-client-secret-generate` exists. Its observation that `bind-env` carries +a literal `KEYCAPE__CLIENT_SECRET`: the validator now renders a +`{param:env}` format spec (upper case, `-` as `_`, as `keycape-client.py` +derives the name) and rejects unknown specs, and follow-through and rollback +text is now checked for unknown placeholders too (patch-level, canon section 6). +The type keeps the literal until ops-warden's planner (`text.format`) renders the +spec as well; asked of ops-warden. 53 tests pass. + +2026-10-10: ops-warden rendered `{param:env}` in `warden case plan` +(23ba584, message `c1df14b7`; other specs and `!conversion` refused). The type +adopts it as a 1.0 patch (canon section 6): `bind-env` and its rollback now read +`KEYCAPE_{client_id:env}_CLIENT_SECRET`. ops-mason reports the Forgejo case +rendered and built (message `1ebddae9`; CCR drafts handed to railiance-platform), +so T06 now waits on the platform apply, the mint-secret lane and the attended +register-client session. + +2026-10-10: key-cape adopted the type's custody path (key-cape ab5cccd, message +`88a4559c`): `docs/forgejo-oidc-client-request.yaml` and KEY-WP-0038-T02 name +`platform/workloads/keycape/forgejo`, field `CLIENT_SECRET`, delivered as +`sso/keycape-forgejo-client` through `external-secrets-keycape-forgejo`, and the +`forgejo/netkingdom-oidc-client` proposal is withdrawn. This matches the type and +the Forgejo example; no change needed here. + +2026-10-10: key-cape's type-composition review (key-cape 6038483, message +`8e5ed5f6`) found the composition sound with two blocking findings, taken as a +1.0 patch (stricter validation and documentation, canon section 6): +R1, redirect URIs now refuse `?` and `#` in the type pattern and in +`keycape-client.py`'s `REDIRECT` (KeyCape refuses `?` at startup and the single +replica would stop); R2, `allowed_scopes` always includes `groups`, the renderer +refuses `access_groups` without it, and `access_groups` now says the consumer +enforces the gate because KeyCape has none; R3, `mfa_required` "false" means the +per-user privacyIDEA policy; R4, register-client's justification records the +restart for every client, the quiet window, and that the rendered registry must +pass KeyCape's client validation before it. The accepted Forgejo case and both +examples still validate; 116 tests pass. +Open, needs a decision: R2's verification step that the consumer enforces the +group gate is a new verification step, hence 1.1 (minor), and the validator pins +cases to the exact type version, so the accepted 1.0 Forgejo case would need +re-pinning. Also open with key-cape: a KeyCape entry point that validates a +rendered client registry without starting the server; until then +`keycape-client.py` mirrors the redirect rule. + +2026-10-10: founder go for 1.1, so no 1.0 run overlaps. The type adds +verification `consumer-enforces-groups`, and hand-over now requires the consumer +to enforce `access_groups` (minor, canon section 6; risk class unchanged, no new +acknowledgment; history in the type README). The accepted Forgejo case and both +examples are re-pinned to `oidc-browser-relying-party@1.1` before any apply; the +acknowledgment intake keeps `@1.0` as the record of what was acknowledged. +ops-mason's build and ops-warden's case-ref digest predate this and need a +re-render. + +2026-10-10: ops-mason re-rendered the Forgejo case from `@1.1` (message +`22c6bba6`; plan digest `af1f4f81…`, no collisions) and rebuilt the bundle. The +rendered OpenBao objects are identical to 1.0, so the already applied +CCR-2026-0041 stands; 1.1 changes only consumer-side steps. key-cape shipped +`keycape check-config` (c528daa, message `a7b0aeff`): KeyCape's startup checks +without secret values, including the new duplicate-clientId rule. +`keycape-client.py --check-config ` now runs it on the live and the +proposed registry, fed on stdin, and apply refuses a proposed registry that +fails; register-client's entry point names the option (1.1 patch). The deployed +image predates c528daa, so the binary is built from key-cape source for now. +Before the next KeyCape rollout (Forgejo register-client or the CCR-2026-0018 +window), the attended operator runs `inspect --check-config` so the live +registry is checked too. + +```task +id: NK-WP-0048-T07 +status: wait +depends_on: [NK-WP-0048-T06] +blocking_reason: "The next types follow once the first one is proven end to end." +priority: medium +state_hub_task_id: "c3c9c3ae-4f23-5ba7-9e6f-e63bcf29b9f7" +``` + +### T07 — Queue the next types + +From the T02 ranking, open the next type drafts (likely the confidential service +client and the workload KV read lane), each to be acknowledged on its own. +Record which existing hand-built instances get adopted as cases, without +rewriting them in place. + +```task +id: NK-WP-0048-T08 +status: done +priority: high +state_hub_task_id: "8e519082-dc8b-5e46-b660-03d03e55dcf6" +``` + +### T08 — identity-provisioner: ensure service-access groups + +The type's `ensure-groups` step calls `identity-provisioner groups ensure --groups + --members `. The provisioner already has idempotent +`_ensure_group` and `_add_group`, but exposes only tenant-scoped operations. Add +the general operation (create if absent, add the listed memberships, never +remove others), its rollback (remove only this case's memberships), tests, and +the ops-warden route for its service identity. + +2026-10-08: done. `identity-provisioner/provisioner.py` gained +`ensure_service_groups` (`POST /v1/groups/ensure`) and `revoke_service_groups` +(`POST /v1/groups/revoke`), behind the existing service-token check. Names +matching `tenant*`, `netkingdom-*` and `lldap_*` are reserved; members must +already exist; everything is validated before any change; memberships are read +back before success; nothing else is ever removed; groups are deleted only when +asked and empty. 7 new tests (39 in the provisioner suite). The type's +`ensure-groups` step and its rollback now name these endpoints. The ops-warden +route for the provisioner's service token belongs to WARDEN-WP-0044-T01. + +```task +id: NK-WP-0048-T09 +status: done +priority: high +state_hub_task_id: "0ee12dfc-8260-5038-bd39-cc7fb4113be0" +``` + +### T09 — The `keycape-client` entry point and its declaration + +Planned first as key-cape KEY-WP-0038-T04. Moved here because the machinery a +live registration needs already lives in net-kingdom, where KeyCape is deployed +(`sso-mfa/k8s/keycape/`): the contained kubectl, the cluster-identity pin and the +issuer pin. A copy in key-cape would drift. + +2026-10-08: done. `sso-mfa/k8s/keycape/keycape-client.py` derives the KeyCape +client record from a case (`client-from-case`). It adds or removes exactly one +client with the existing safeguards (byte-preserving merge, exact match or +refuse, shared-reference refusal, compare-and-swap, read-back, contained errors), +requires the Deployment env binding first for confidential clients, and requires +the case acceptance record for `apply` instead of a per-client founder decision +id. 15 tests, including a regression showing that the vergabe case reproduces +its hand-built registration exactly; all 71 helper tests pass. Declaration +`capabilities/playbooks/net-kingdom.keycape-client-registration.yaml` (contract +PASS, `maturity: implemented`) replaces key-cape's draft. The type now pins it, +and for confidential clients adds `deliver-secret` (delivery from custody into +the sso namespace) and `bind-env` (Deployment env), attended in the same session +as `register-client`. The per-client helpers stay until their clients are +adopted as cases. + +2026-10-10 — NetKingdom owner review of the execution seam, source only: +ops-warden/docs/security-case-execution-proposal.md SHA256 78ab1439e4c5beb205aeccf15b1b2e706511be55ab195c5a3aa682f326c7c77c +was reviewed against INTENT, SecurityCase canon, acknowledged type1.1, current +case and owner source. docs/security-case-execution-owner-review.md supports +the closed adapter/receipt direction with explicit versioned schema, structural +materialization provenance, unchanged caller/session boundaries, safe CAS +reconciliation and exact-created-object rollback requirements. This is owner +review, not approval. Supporting file digests are in the adjacent sources JSON. + +Current source exposes a circular wait: ops-mason T03 waits on mint while Warden +and the broker refuse a non-materialized case. Structural acceptance therefore +needs its own native pinned receipt before mint, not a successful mint before +materialized. The platform broker additionally pins type1.0 while Forgejo is +accepted at1.1; platform-owner alignment and tests are required, not an unchecked +version bypass. T06 remains wait. No case/type lifecycle status, grant, authority +or live runtime changed by this review. A proposed informed-decision would +adopt the source contract only; native materialization and execution acceptance +stay distinct. + +Final proposal re-review: staged Warden proposal SHA256 78ab1439e4c5beb205aeccf15b1b2e706511be55ab195c5a3aa682f326c7c77c +adds only the explicitly local reducer appendix, not an accepted wire protocol +or executor. The exact-final review and sources JSON supersede the earlier +1035f049... proposal review. Platform staged the exact type1.1 broker correction +with ten fake-backend subprocess checks (script SHA256 +9576cd211cfc06526ec4cc9bd0a5b06f0f59287a68931bb2d6e3592a951ddc7d). +The next source work is the versioned closed adapter/receipt/materialization +contract, not another relaxation of the broker's status gate. Native work still +requires installed broker provenance, structural reader receipts, exact new +materialized-case bytes, caller grant/session revocation and verified execution. +No installed/native acceptance is inferred from that staged correction. + +2026-10-11 — new source-only type1.2 architecture acknowledgment: +Confirmed native founder ACCEPT of infd-20261011-forgejo-type12-source-r1@1 +at06:47:39Z, approval8e9332c8-754a-420c-b8b7-445cb4fe6765. Exact frozen packet +0b377d647ca748cd03c92f9792396412048055d4bde9f7bb06ff6d413f7acd68 and submission +585cee731de30d2b37883a57abfd9eb32f023ee694e8d911989109d5caf80569; native receipt +retained in docs/evidence/2026-10-11-forgejo-type12-source-acknowledgment/. +The complete acknowledged1.2 source package is isolated under +security-case-type-revisions/1.2/oidc-browser-relying-party; default loader and +current accepted1.1 case remain untouched because load_type is single-version. +The new proposed1.2 case is NOT accepted/materialized/live and has no acceptance +fields. Actual reference-migration design approval is a separate Apps owner +receipt; no1.1 approval is inherited. Source-recording review/test proof and exact +next gates are in docs/forgejo-type12-source-acknowledgment-followthrough.md. +T06 remains wait and T07 remains its existing dependent task; type is NOT +implemented and no first-case/native grant or broader workplan finish is asserted. + +2026-10-11 — bounded explicit version selection source preparation for T06: +The validator now offers optional exact version_roots/--version-root VERSION=PATH +selection with duplicate/mismatch/traversal/symlink refusal and no fallback. The +default1.1 source and historicalacceptedcase hashes/ordinaryrefusal/preparation +render stayunchanged; actual1.1 is acknowledged, not implemented. Acknowledged1.2 +ordinaryadmission also refuses; optionalpreparation remains nonexecuting.43local +validatorchecks pass and original1.1 CLIrenderbytes match exactly. Makerproof: +docs/evidence/2026-10-11-explicit-type-version-selection/source-integration-validation.json. +FormalNetownerreview/reference-source integration and all existing native adapter, +provenance/broker/materialization/attendedproof gates remain; T06 stayswait, no +new residual or firstcase/typeimplementation/native grant is inferred. + + +2026-10-16 — isolated source preparation for T06 (canonical integration pending): +Added `check-evidence` to the existing validator CLI. It reuses exact version +selection and ordinary type/case/render admission, checks actual Mason structural +request source/inventory/projection pins and reads actual Warden journal rows +read-only. Successful rows require exact type-declared descriptors and attempt/ +revocation fingerprints. Existing acknowledged types without machine descriptors +remain plan-only. The compatibility result never claims authenticated native +completion: Request and journal fingerprints are not portable native evidence. +Owner authentication/status binding and actual native gates remain unresolved; +T06/T07 statuses and both type/case lifecycles remain unchanged. No dispatch, +credential access or authority expansion is introduced. See tool README for the +existing owner protocol boundaries and source integration tests. + +Concrete next Warden owner integration: current `Executor.admit` refuses every +case parameter whose sensitivity is not public/operational, so browser +`redirect_uris`, `allowed_scopes`, `client_type`, `mfa_required` (and 1.2 +`consumer_namespace`) fail after otherwise prepared admission. The sensitive +classification is a security-change review level, not secret content. The +Warden owner must validate through NetKingdom's existing exact-version case/type +validator, then project only descriptor-declared safe target metadata; do not +remove the credential/value refusal or copy complete parameters into journals. +Actual owner descriptor acknowledgment and independent evidence admission remain +prerequisites, with normal admission still refusing unimplemented types. + + +## T10 — Source compatibility checker for Mason and Warden evidence + +```task +id: NK-WP-0048-T10 +status: done +priority: high +state_hub_task_id: "4b06ca97-c1e4-5680-bf22-ab75ab1646a4" +``` + +Completed 2026-10-16 in published source `c31d21ba`. The read-only +`check-evidence` command checks the actual Mason structural request and Warden +journal source protocols against an exact version/render. Independent source +review passed 103 tests at that checkpoint. It always reports native completion +false and execution prohibited. Fixtures and local journal fingerprints do not +provide authenticated owner/native evidence. This records completed source scope +previously described under T06; it does not duplicate native acceptance credit. + +## T11 — Validate the optional machine-descriptor pin contract + +```task +id: NK-WP-0048-T11 +status: done +priority: high +state_hub_task_id: "ef2a0119-f59e-5e45-9519-115da9df7b85" +``` + +Completed 2026-10-16. Reproduced the JSON schema rejecting a valid +`execution_descriptors` map while the Python validator accepted malformed maps. +The schema now supports the existing Warden optional exact-byte pin map; semantic +validation binds keys to declared steps, bounds the map and checks digest shape. +The direct evidence API refuses malformed maps before accessing them. Twenty-one +new checks and all 127 affected source checks passed. No current type/case bytes, +status, owner descriptors or native acceptance changed. Source evidence and +publication receipt: Custodian +`docs/changes/netkingdom-target-2026-10-16/browser-acceptance-next/`. + +## T12 — Prepare NetKingdom-owned attended step adapters + +```task +id: NK-WP-0048-T12 +status: todo +priority: high +state_hub_task_id: "7ce4eee3-cd47-55a9-bfa5-4f1835d4d6fd" +``` + +Owner: NetKingdom. Retained scope: prepare bounded attended Deployment env, +config verification and restart/readiness steps around the existing helpers. +Next action: identify exact current entry points and their revision/readback +contracts, then implement fixed source-pinned callbacks or receipt-only attended +steps. Completion requires positive/refusal checks, exact target/source pins and +uncertainty behavior without acquiring user-engine runtime identity or exposing +registry/client secrets. Existing caller/session, cluster/issuer, CAS/readback +and status-only output boundaries remain mandatory. + +2026-10-16 task refinement: the original broad adapter scope is split into T13 +(completed registration source candidate), T14 (group creation provenance), T15 +(durable registration recovery) and T16 (descriptor/artifact selection and owner +review). This preserves T12's task ID and Hub UUID while giving each remaining +outcome an owner and completion gate. T06 now names every source dependency; +its native/reference lifecycle acceptance scope remains unchanged. Completion +of any source task supplies no Warden, Platform or Apps assent and no native +execution authority. + +## T13 — Prepare the fixed registration adapter source candidate + +```task +id: NK-WP-0048-T13 +status: done +priority: high +state_hub_task_id: "b264c10b-565c-5d29-b788-03cf5fbd2883" +``` + +Completed 2026-10-16, source implementation and synthetic fixture proof only. +Added an injection-only adapter around actual `keycape-client.run`: fixed +case/principal/human/lane/source/acceptance binding, existing CAS/readback, +metadata-only owner evidence and per-attempt parent-session revocation boundary. +The descriptor source composition pins the exact checker executable digest +alongside adapter/helper source. Creation-only rollback authenticates its +creation receipt and refuses changed postcreation UID/resourceVersion; +pre-existing clients confer no rollback authority. Missing readback, evidence, +revocation or process-local creation provenance cannot become retry-safe success. + +Evidence: Custodian `docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/` +R3 candidate, explicit patch and postimages, retained R1 rejected custody finding +and R2 revision, and 36 passing source checks (16 new, 20 existing). Three checks +use actual Warden kernel admission/dispatch/rollback with published Warden +`d97652a3bb5fa733b063d71a1b74068efcccf572` producer pins unchanged. Owner trust, +descriptor/type/case and Kubernetes objects remain synthetic fixtures. This +records a completed source deliverable, not independent review, publication, +type acknowledgment, exercised procedure, identity admission or native browser +acceptance. Independent root source review: Custodian browser-adapter-next/coordinator-review-r3.json. Source integration and normal publication/synchronization receipts: Custodian browser-adapter-next/canonical-integration.json. These receipts supply no other-owner assent or native authority. + +Source publication: `0775c2bfa41af236301155bb5a69bbf088bdf823`, independently +reviewed by the Custodian coordinator before integration. The required initial +projection and ordinary UUID writeback both completed with zero failures and +zero automation errors. Generated-only brief commits were refused by the normal +hook; exact pre-existing staged/working brief bytes were restored. The separate +canonical integration receipt retains those refusals and projection evidence. +Native admission and all other-owner acceptance remain open. + +## T14 — Admit governed group rollback against native owner boundaries + +```task +id: NK-WP-0048-T14 +status: progress +depends_on: [NK-WP-0048-T20, NK-WP-0048-T21] +priority: high +state_hub_task_id: "fce95440-a25b-5f27-8ef3-a3d182099113" +``` + +Owner: NetKingdom. Next action: bind the completed T21 PostgreSQL journal to +an admitted dedicated CNPG consumer through the existing `rapp-postgres` +database-per-consumer contract. Identify the actual deployment/package and +custody owner, admitted placement/capacity and role/renewable lease/TLS target; +implement the independently retained exact content checkpoint and authoritative +tenant/case/source/principal callbacks before enabling governed group operations. +The injection-only backend and fixture callbacks do not supply production scope +or authorization. Existing service tokens, caller-supplied JSON and provenance +receipts confer no new grant. The default production endpoint remains disabled +without the trusted integration. SQLite remains local/disposable only; any +production exception requires explicit scoped governance under ADR-0015. + +2026-10-17 owner-integration refinement: T23 records the completed source/package +placement review, distinct from T21's source/disposable-SQL proof. Its dedicated +`rapp-identity-provisioner` recommendation is draft and unbound, not an accepted +package or existing repository. The full six-consumer rapp-postgres renderer +accepts the inert candidate on platform-pg-2 as its third of four declared +consumers; first-cell placement is refused as a fifth. Neither the estimated +four-consumer ceiling nor source requests establish native headroom or backup. + +Next independent owner contracts: NetKingdom/package/substrate owners accept +exact deployment/rollback fate; rapp-postgres admits the dedicated consumer, +roles, five-connection budget and actual backup/restore; Platform admits exact +renewable runtime/migration delivery and TLS/CA/SAN; network owners scope SQL, +broker and checkpoint paths without assuming a narrow policy subtracts additive +same-namespace access. Protected caller owners must provide authoritative +GroupJournalBinding construction and current authorization; an independent +existing holder must authenticate and atomically advance the exact content +checkpoint, not a counter or ordinary Audit receipt. Directory/runtime owners +must select and prove either an exclusive all-writer maintenance barrier or +provider-supported CAS/generation, including established sessions, lifecycle +cascades and remove/readd ABA. No source review establishes those owner facts. + +The default PostgreSQL activation and destructive native rollback remain refused. +Storage/lease acceptance can be proposed separately from LDAP mutation only after +exact owner source/target/custody/verification/recovery and cost/risk review; +State Hub guard approval grants none of it. Source-only integration review and +native acceptance matrix: Custodian native-storage-r1/OWNER-REVIEW.md. T14's +existing status, UUID, dependencies and native completion gate are retained. + +2026-10-17 refinement: T20 retains the historical durable SQLite source and +actual disposable LLDAP proof. T21 extracts the completed PostgreSQL source and +bounded real disposable SQL compatibility proof. T14 remains `progress` for +native CNPG admission/recovery, protected owner integration and exclusion of all +external LDAP writers. Separate source publication/projection and independent +cleanup receipts belong to coordinator follow-through; no missing native assent +is represented as received. + +Completion still requires independently reviewed/admitted ADR-0015-conforming +CNPG storage (or an explicit governed exception), backup/lost-store recovery and +migration of legacy requests; a native +writer-isolation/fencing or provider CAS/version contract that closes external +writer races and remove/re-add ABA; exact case/source/principal and current +native object bindings for protected callbacks; and actual per-attempt evidence, +revocation and readback/refusal verification through the existing owner paths. +Only independently retained confirmed creations may be undone. Every +pre-existing/shared group and membership must remain preserved; unresolved +intents never expire into retry authority. The disposable fixture supplies none +of these native custody, authorization, owner-acceptance or production claims. + +## T15 — Define authenticated recovery for lost registration process state + +```task +id: NK-WP-0048-T15 +status: wait +depends_on: [NK-WP-0048-T31] +blocking_reason: "Source candidate 061461e is merged. Waiting on the owner to select the real authenticated original-attempt holder and native readback, and on independent re-review of the changed T13 adapter source." +priority: high +state_hub_task_id: "1b825705-4848-5edc-832f-b1622cd9bdb7" +``` + +2026-10-17 source part merged (061461e via 65559ef). `build()` takes optional +paired `holder`/`observe` capabilities and installs `reconcile_original`; the T31 +fixtures now run through the adapter's own path. Rollback after restart is still +refused, and there is no add replay. This changes `keycape-registration-adapter.py`, +so the revision reviewed under Custodian `browser-adapter-next/coordinator-review-r3.json` +is superseded. The new revision has no independent review yet and must not be +pinned on the strength of the old one. + +2026-10-17: T31 fixtures done (74adcd7). What remains for T15: the owner +selects the real authenticated original-attempt holder, and the T13 adapter +gains the holder capability, `reconcile_original` and reconcile-aware `verify` +described in T31. + +Owner: NetKingdom for registration state, with Warden owning kernel evidence +admission. T22 records the completed source review and exact existing-seam gaps; +T15 retains implementation and authenticated restart/lost-receipt acceptance. +Next action: select and admit the actual owner publisher/authenticator and bounded +original-attempt readback contract for the merged holder/observe adapter seam. +Warden's original-intent persistence/interface (`a45cf475`) and additive validation +(`117fec9241e7b985dfb187b992e173d0a5dc3700`) are now published and projected. +The local Journal retains admitted nonsecret original context, validates original +actor/attempt and metadata linkage to the original running receipt, and supplies +it separately from the new reconciliation attempt. This local consistency is not +independent original-holder authorization or creation provenance; neither the +journal nor a current matching registry entry reconstructs creation authority. + +The owner contract must bind the original Invocation, case/type/descriptor/catalog/ +acceptance pins, source, principal and admitted tenant/realm provenance, separately +from the new reconciliation attempt and its independently revoked parent session. +Use an existing authenticated holder/readback; Audit Core's custody API is a +candidate only until its actual registration source/class, load-bearing emission +contract and separate scoped reader are admitted. No new broker or receipt trust +root is proposed. A missing original record, ambiguous publication, NO_CHANGE, +changed native revision or lost readback stays uncertain; no add replay or undo. + +Completion requires restart/lost-receipt fixtures against the selected actual owner +contract: independently authenticated original created-only provenance, original +exact client entry and unchanged current postcreation UID/resourceVersion before +any undo, with wrong attempt/case/source/principal and unavailable-holder refusals. +The merged T13 adapter exposes reconciliation only through paired injected +holder/observe capabilities and still refuses post-restart rollback. Existing +source review, JSON receipts and synthetic trust do not satisfy independent +holder authorization/provenance or native admission, or authorize attended recovery. +Exact remaining owner facts: Custodian native-registration-r1/original-attempt-review.json. + +2026-10-17 check (net-kingdom-worker): Warden's original-intent lookup interface +is not published — ops-warden head is `d97652a`, with no WARDEN-WP-0044-T03 +commit, and the net-kingdom inbox holds no announcement. No restart/lost-receipt +fixture or readback seam was added this session; the work is deferred behind +T16's decomposition under the same token budget. Remains todo, gated on Warden +publishing the reviewed lookup/context interface and on owner selection of the +authenticated original-attempt holder. + +2026-10-17 current-source correction: the earlier same-day `d97652a` lookup +checkpoint and T31 adapter-next-action note above are historical and superseded +by the published Warden interface/validation and merged `061461e` adapter seam. +The 54 focused Warden checks establish source behavior only. Current admission +leaves tenant/realm empty; no admitted tenant/realm provenance is inferred. +T15 remains wait for actual independently authenticated owner-holder/readback +selection, independent review of the changed adapter, and native restart/lost- +receipt acceptance. Existing task metadata, owner gates and historical reviews +are unchanged. + +## T16 — Select and review the actual machine descriptor composition + +```task +id: NK-WP-0048-T16 +status: todo +depends_on: [NK-WP-0048-T24, NK-WP-0048-T25, NK-WP-0048-T26, NK-WP-0048-T27, NK-WP-0048-T28, NK-WP-0048-T29, NK-WP-0048-T30] +priority: high +state_hub_task_id: "200bf500-5084-5ae7-be26-3033fe1f296e" +``` + +2026-10-17 coordinator review r4 approved the 1.3 candidate at 1920b13 with no +findings (the-custodian `nk48-adapters-1.2-review/coordinator-review-r4.json`). The founder chose +option A of the T16 owner-review packet (`nk48-t16-owner-review/founder-option-decision.md`): +1.3 with execution descriptors for all seven steps. Owner reviews for A are open +in the-custodian `docs/changes/netkingdom-target-2026-10-17/nk48-1.3-owner-review/` (net-kingdom, +railiance-platform, railiance-apps, ops-warden, key-cape). The owner-review packet +supersedes the r1 composition pointer: T16's exact composition is now +`sso-mfa/k8s/keycape/descriptor-composition-1.3.candidate.json`. + +2026-10-17 successor candidate prepared (source only, `proposed`): +`security-case-type-revisions/1.3/oidc-browser-relying-party/` is the 1.2 package plus +an `execution_descriptors` map of the seven composed descriptors, with examples +pinned to `@1.3` and no acknowledgment. ensure-groups, hand-over, +deliver-consumer-secret and the composer accept 1.3 (835680c, merged acda26c). +`test_admit_1_3_type.py` runs Warden's real admission against the committed 1.3 type: +the proposed type is refused; an acknowledged copy is refused because ops-warden +pins parameter fingerprints only for 1.1 and 1.2; and with only a 1.3 pin (equal to +1.2's fingerprint) all seven are admitted. This is a major change under canon +section 6. Next: r4 source review of the changed adapters and the map, then owner +reviews, including the ops-warden 1.3 pin, the mint-secret warden_executes/human +question and the owner-run steps question, then the founder's Decision Memo. + +Owner: NetKingdom for descriptor/type source and exact checker artifact; Warden +independently owns global provenance/admission. T22 records the completed seven-step +source1.2 inventory and regenerated register-client descriptor using the delivered +checker and current sealed-snapshot source; T16 retains exact owner review, +complete descriptor composition and admission. Next action: bind actual native +publisher/authenticator, original/current session verification and revocation, +executable storage/custody and config pins, then obtain the six missing fixed +owner descriptors/adapters for ensure-groups, mint-secret, deliver-secret, +bind-env, deliver-consumer-secret and hand-over. The inventory identifies each +existing owner seam, exact missing contract and created-only rollback requirement; +it is not an executable bundle. T14 and T15 remain independent prerequisites. + +Review the dedicated consumer source1.2 composition only through the existing +explicit version-root selection. Keep canonical1.1/default and its accepted case +unchanged, and the source-only acknowledged1.2 reference case proposed without +acceptance fields. New descriptor bytes require a reviewed exact successor +revision/version under canon section6; they do not inherit historical source +acknowledgment. Changed authentication/materialization/trust needs the prescribed +informed-decision acknowledgment. Do not insert a partial descriptor map or use +--allow-unimplemented preparation as ordinary case/native admission. The registry +helper reports restart_required; separately review quiet-window restart/readiness +and actual consumer verification rather than infer activation from registry CAS. +Exact composition: Custodian native-registration-r1/descriptor-composition.json. +Completion requires separate owner source review of exact machine descriptors, +artifact/source/config pins, normal type/version admission and independent +evidence-verification boundaries. Warden d97652a refreshes producer validator +pins for NetKingdom 015694ee; it does not attest T13's new adapter. Pinning any +machine descriptor into an acknowledged type is a separately reviewed source +change. Platform owns structural/mint/broker/delivery evidence; Apps owns actual +consumer login/group enforcement. Neither their assent nor native acceptance is +supplied by this task. + +2026-10-16 source checkpoint: T19 below extracts the completed per-attempt +executable snapshot implementation and reviewed fixture proof. T16 retains +native executable storage/custody, exact owner evidence/session callbacks, +complete descriptor composition and ordinary type/version admission. The +successor descriptor/source pins are proposed only; the earlier descriptor +history is preserved. T14 group provenance and T15 durable restart/recovery +remain independent open requirements. No native acceptance is counted here. + +2026-10-17 budget checkpoint (net-kingdom-worker): no source change made. The +T13 registration adapter alone is 217 source and 317 test lines plus an +independent review; six further owner descriptors/adapters cannot be completed +within one per-task token budget (soft 8000 / hard 10000), so the task was +stopped below 50% and is decomposed rather than pushed through. Proposed split, +one source-candidate task each with positive/refusal fixtures, canonical1.1 +default unchanged and no --allow-unimplemented admission: (a) ensure-groups, +bound to T14's journal/provenance seam; (b) mint-secret and (c) deliver-secret, +Platform-owned evidence seams; (d) bind-env and (e) deliver-consumer-secret; +(f) hand-over, Apps-owned consumer verification; then (g) descriptor composition +over the reviewed set. Each candidate needs its owner's source review before +pinning; none is authored here as owner-accepted. Status stays todo. + +2026-10-17 task refinement after coordinator r5: completed independent mint- +guard regression coverage is extracted as T32. T33 tracks the exact Platform +issuer-store source/reference correction; T34 tracks the Apps configuration +inverse reachable without secret or hand-over resource creation. T16 retains +final exact seven-descriptor composition, independent owner reviews and admission; +its status and existing dependencies are preserved. The kernel branch also needs +its own KR1-F1/F2 admission/dispatch corrections and independent review under +WARDEN-WP-0044-T03 before any 1.3 pin. Historical r4 acceptance does not supersede +r5's findings or establish native readiness. + +## T17 — Build and exercise the exact offline config-checker artifact + +```task +id: NK-WP-0048-T17 +status: done +priority: high +state_hub_task_id: "8c3e4baa-a218-5996-af9e-fe1153a46dac" +``` + +Completed 2026-10-16, isolated artifact/source fixture proof only. Built the +existing `key-cape/src/cmd/keycape` command from published clean source revision +`3c71226a1a7b8a2bdb710e9c497e1a1f7f8a3987`, source tree +`f0010b9cab17dbc3a787628a29657d98b900320f`, with cached Go 1.23.0, +CGO disabled, Linux/amd64 v1, trimmed paths, disabled VCS stamping and offline +read-only module selection. The full retained closure pins 108 source files, +9876 toolchain files and ten used module source directories. Three isolated +build locations produced identical 13633409-byte artifacts: +`sha256:be37738fe1c08259fb4f9ca0daf0f9301bfffc237e5bb38435525dfbf11ccff5`. + +Eight disposable checks use the actual executable and maintained helper/kernel, +with complete synthetic configs and a minimal environment: add/created-only +removal, normal pinned producer admission, rollback, lost-readback uncertainty, +stale revisions, substituted checker refusal, invalid startup config and missing +secret-env refusal. No checker stub or producer-pin override remains. The +artifact-bound descriptor proposal is +`sha256:76b1153d188062ece53fcd5e8d35290fdbfd02a69dd79d4b001fc42781afcdc3`. +Evidence: Custodian +`docs/changes/netkingdom-target-2026-10-16/browser-checker-artifact-next/`. +The first two build attempts succeeded but their closure collection failed; +both failures are retained, as is refusal of the older incomplete synthetic +fixture. No production config, credentials or cluster mutation was used. + +This extracts completed build/fixture scope from T16 without claiming KeyCape +owner endorsement, native checker selection/storage custody, acknowledged type +integration, native evidence/session callbacks or attended browser acceptance. +T16 retains exact owner review and composition/admission scope. The attended +registration procedure remains unexercised. + +## T18 — Make checker input and executable selection deterministic + +```task +id: NK-WP-0048-T18 +status: done +priority: high +state_hub_task_id: "9c45da71-fb4c-5f20-9065-90f448f37c64" +``` + +Completed 2026-10-16, isolated source correction and real-artifact fixture proof +only; independent integration/publication remain separately recorded. KeyCape +source review identified non-secret `KEYCAPE_ACCOUNT_PORTAL_URL` and +`KEYCAPE_BROWSER_LOGOUT_URL` overrides in config.Load. The original helper +inherited ambient environment, allowing valid supplied configuration to be +rejected or invalid supplied URL configuration to be admitted. A bare relative +checker filename could also be replaced by PATH lookup after the adapter had +hashed the selected local file. All three counterexamples are retained against +the unchanged predecessor. + +The existing helper now resolves the selected checker file to an absolute path +and passes an explicit empty subprocess environment. Three opt-in real-checker +regressions verify both URL-verdict directions and relative-file selection under +a non-checker PATH decoy. All 39 affected checks pass with the exact supplied +artifact, as do 11 real-artifact helper/kernel scenarios. Source evidence: +Custodian `docs/changes/netkingdom-target-2026-10-16/browser-checker-environment-r2/`. +Historical descriptors remain retained; the helper correction changes successor +source-composition pins and requires their separate review/admission. An absolute +path is not immutable custody and does not eliminate file replacement races; +T16 retains native executable storage/custody and callback/type admission. +No production configuration, credentials or cluster mutation was used. + +Source integration and publication: `916ba4b33e03179274d03b78bd898a4843e8653f`, following independent +Custodian source review in browser-checker-environment-r2/coordinator-review.json. +The ordinary protected projection pass assigned T17/T18 UUIDs with zero failures +and zero automation errors; generated-only brief commit refusal is retained. +The exact prior staged/working brief bytes were restored. Final source/publication/ +projection evidence is retained in Custodian browser-checker-environment-r2/ +canonical-integration.json. These source receipts do not admit native executable +custody, owner callbacks, acknowledged type changes or browser identities. + +## T19 — Bind checker execution to verified immutable attempt bytes + +```task +id: NK-WP-0048-T19 +status: done +priority: high +state_hub_task_id: "f50fdcd1-00be-5c3c-be0c-345aa2126032" +``` + +Completed 2026-10-16, independently reviewed source implementation and synthetic +fixture proof only. The registration adapter now opens a bounded regular checker +artifact without following its terminal symlink, verifies the reviewed digest, +and snapshots the exact bytes into a write/grow/shrink/seal-removal-sealed Linux +memfd before helper access. Live and proposed config checks execute that same fd +through procfs with the existing empty environment; the fd closes on completion +or failure. Unsupported snapshot/seal/exec policy refuses through the existing +parent-session revocation and uncertainty boundaries. The earlier unbounded +pre-snapshot path read was removed during independent review; actual adapter +symlink/FIFO/oversize refusal tests verify helper access never starts. + +Evidence: Custodian `docs/changes/netkingdom-target-2026-10-16/browser-resume-r1/`, +including exact base/preimages/postimages/patch, proposed descriptor, coordinator +source review and canonical integration receipt. Independent Custodian checks +passed 49 affected source tests and 12 actual Go checker/helper/kernel scenarios; +the sets overlap and are not separate acceptance counts. Existing published +Warden d97652a producer pins remain unchanged. The exact KeyCape artifact remains +`sha256:be37738fe1c08259fb4f9ca0daf0f9301bfffc237e5bb38435525dfbf11ccff5`. +The predecessor path-replacement refusal and the first expanded reopened-fd test +failure are retained; the latter fixture was corrected to restore write mode +before independently proving seals block reopened-fd writes and shared mmap. + +This source task adds no native owner custody, callback, evidence trust, type +acknowledgment, credential access, consumer sign-in or rollback acceptance. +Executable memfds, Linux seals and usable procfs are explicit runtime dependencies +and still require native admission. This is an extracted source outcome from +T16, not completion of T16; T12/T14/T15 and all external-owner gates remain open. + + +Publication verified by the coordinator after the source worker usage limit: +`8790dd18ae83c4651ef3d1d923312a118a5b436b` is the actual remote main revision. +All five canonical source hashes match the reviewed packet. Normal synchronization +completed with zero failures/automation errors; all 19 task statuses match the +projection. T19's assigned UUID is retained. The original staged and working brief +bytes were restored and independently compared. The exact source, projection and +publication checks are in Custodian browser-resume-r1/canonical-integration.json. +Native acceptance remains open under T16 and the other named owner tasks. + + +## T20 — Prepare durable group provenance and prove disposable LLDAP compatibility + +```task +id: NK-WP-0048-T20 +status: done +priority: high +state_hub_task_id: "863fcbdb-e246-51ba-87ca-a2f44d01d6f8" +``` + +Completed 2026-10-17, source candidate preparation and bounded synthetic +compatibility proof only. This extracts evidenced implementation/fixture scope +from T14; T14 retains native storage, writer isolation, protected callback and +owner-acceptance gates. Source integration/publication and State Hub projection +are pending the coordinator's separate receipts; no revision or task UUID is +invented here. + +The existing identity-provisioner group ensure/revoke interface now has a +reviewed candidate for a bounded owner-private SQLite journal, proven only in +local/disposable use. The initial storage instructions wrongly suggested a native +persistent-volume path; source review against accepted ADR-0015 corrected them +before publication. Production SQLite-on-a-PVC is not admitted: T14 retains +CNPG/rapp-postgres storage admission or a separately governed exception. +The local journal saves +complete prestate and intent before each attempted directory mutation, inventories +only confirmed new groups/memberships, binds original ensure idempotency and +correlation to the exact request digest, and refuses unsigned caller inventories +or legacy broad revoke requests. Replays never recreate removed state. Missing, +replaced or untrusted storage and missing intent rows stop execution; definite +before-mutation refusals retain evidence without stranding unrelated valid work. +Interrupted writes/readback remain unresolved across reopening. Revoke validates +all targeted native ids and complete member postimages before destructive work, +removes only confirmed creations and never deletes a preexisting empty group. +Server/configuration wiring, image-copy closure and owner storage/request +migration instructions are included. The existing endpoint's caller +authentication remains the existing boundary, not a new grant. + +The candidate was prepared against NetKingdom +`878abc93588e12b6da1bac055cfbb3abb8d7adf1`; exact source patch digest +`sha256:1f3f3ca94c46dadac8f24896053d4d23179de795e50a24298a0cfbcc46cca30e`. +The independent Custodian review passed all 65 provisioner checks. The earlier +62-check candidate, two independent-review findings and subsequent fixes remain +retained rather than overwritten. Seven additional fixture-plan checks exercise +the preparation boundary; these counts overlap the same candidate and do not +measure task-completion percentage or independent acceptance. + +A separately reviewed bounded fixture then exercised the exact candidate against +cached pinned disposable LLDAP and Python images, through actual GraphQL. All 14 +closed compatibility/containment oracles passed: exact creation inventory, +separate-process SQLite reopening/replay/revoke, complete preexisting directory +preservation including empty/shared groups and memberships, refusal of shared +membership drift, retained uncertainty after a lost response following a real +addition, and refusal of unknown/cross-case receipt bindings. The fixture used +only newly generated synthetic users/passwords/key material in private tmpfs, +with a network-none anchor and shared loopback helper, no host ports/mounts, +no image pulls and no production or actual credential contact. The coordinator's +safe receipt reports both exact owned containers absent after cleanup; final +independent cleanup verification remains the coordinator's integration duty. + +Evidence: Custodian +`docs/changes/netkingdom-target-2026-10-16/group-provenance-r1/`, including exact +source base/pre/postimages, patch/manifest, independent review and retained +refinements. Native fixture manifest +`sha256:8a0514f5f14137355972400cd4852f48c892316d28e3abd3c86635d7cb81eef2`; +retained safe result `native-fixture/execution-result.json` (original +`/tmp/nk48-groups-native-review-5pywb19z/result.json`). The source task supplies +no native owner assent, production rollout, protected session/evidence callbacks, +ordinary type admission or consumer login proof. LLDAP has no conditional +membership mutation or generation token here: exact readback cannot exclude +ABA or a concurrent external writer, so T14 remains open. + +2026-10-17 verified source follow-through: source commit `4f65e41` passed normal +local scanning and all 65 canonical provisioner tests. The actual disposable +LLDAP receipt passed all 14 oracles; independent verification on the pinned +daemon found both exact names and the run label absent. Source integration +includes the ADR-0015 documentation correction; the journal remains local-only. + +T14 source preparation is underway. Twenty-three pinned public sources identify +NetKingdom implementation/reconciliation, Platform custody/database governance +and rapp-postgres consumer provisioning; the inspected runtime binding does not +assign identity-provisioner a package. The existing User Engine runtime token +and pod route are not a protected-case caller lane. The native proposal preserves +the same journal seam on a dedicated CNPG consumer and requires authoritative +scope binding, admitted role/lease/placement/recovery and all LDAP writers fenced. +The declared exclusive maintenance window is not mechanical exclusion of WebUI, +helper or lifecycle writers. Capacity placement and EUR cost/risk remain owner +decisions. No package, database, credential, rollout or owner assent was created. +Custodian evidence: group-provenance-r1/native-storage-proposal-r1; final source, +projection and cleanup review: resume-integration-r1/publication-verification.json. + + +## T21 — Implement the PostgreSQL journal and prove disposable SQL compatibility + +```task +id: NK-WP-0048-T21 +status: done +depends_on: [NK-WP-0048-T20] +priority: high +state_hub_task_id: "afe78e4d-408a-516a-ab92-e212e8a2d11d" +``` + +Completed 2026-10-17: bounded source preparation and actual disposable PostgreSQL +compatibility only. T14 retains native storage, custody, callbacks, authorization, +all-writer isolation and production admission. Coordinator source publication and +projection remain separate follow-through; no source revision or task UUID is +invented here. T20 remains the historical local SQLite/actual LLDAP proof. + +The eight-path source candidate adds an injection-only PostgreSQL implementation +behind the existing owner journal seam, with explicit trusted tenant/directory/ +case/source/principal/epoch binding and independent continuity callbacks. It +persists bounded intent/created-only receipts, exact-one version updates, durable +active fences, and a content-bound journal chain. SQL commits precede independent +exact-marker acknowledgment; lost replies/checkpoints poison continuation and +retain uncertainty. A counter-only checkpoint is explicitly insufficient for +divergent clones. Migration does not create database/schema/credential, admit an +epoch or clear quarantine; production HTTP activation is absent pending trusted +callbacks. Pinned psycopg package/image-copy closure is included, without claiming +an image build or native dependency admission. + +Candidate base `6cb43c0cc55212363a5406b02db046e2384a7c40`; final eight-path patch +`sha256:5549d2982b61ff70aba4b3928fecd5639db0f1a732ab2f8630f678e7acf4d34d`. +All 82 source tests pass, alongside 13 fixture plan/diagnostic checks and two +source-backed snapshot regressions. Counts overlap the same source and fixture; +they are not additive native acceptance or completion percentages. + +The independently reviewed R5 fixture ran the actual SQL migration/backend and +existing provisioner path against cached pinned disposable PostgreSQL. Directory +operations deliberately used the existing synthetic LLDAP test double. All +manifest-declared phase oracles passed: creation-only inventory and DDL refusal; +separate-process replay/revoke with complete preexisting/shared visible-state +preservation; foreign-context refusal; concurrent exactly-once reservation and +stale version refusal; actual database fork with divergent bodies at the same +counter and fresh-object rejection of the losing exact checkpoint; retained +uncertainty after checkpoint/directory/SQL response loss; and actual pg_dump/ +pg_restore rollback refusal against the independently retained fixture checkpoint. +The checkpoint is fixture-only, not an admitted native owner evidence holder. + +Only generated credentials/state were used, with two nonroot/read-only containers +on private loopback, bounded tmpfs/CPU/memory/PIDs/lifetimes, no host ports/mounts, +no pulls and no production contact. The safe receipt reports both exact owned +containers absent, with no daemon uncertainty. Independent final pinned-daemon +cleanup verification remains coordinator follow-through. + +Failed R2/R3/R4 receipts and source revisions remain retained. R2's helper failure +subphase is unknown; R3 exposed a real backend bug where replacing nested receipt +objects lost the created ID held by ensure, fixed by acknowledging only the backend +version after commit/checkpoint. R4 verified that fix through actual SQL bootstrap, +then exposed fixture snapshot aliasing and empty deleted-group backing entries; +source-backed regressions corrected the snapshot without weakening complete +visible-prestate equality, and reject nonempty orphan membership. These refinements +belong to one trial, not extra native acceptance successes. + +Custodian evidence: `docs/changes/netkingdom-target-2026-10-16/group-postgres-r1/`; +exact source/pre/postimages and retained predecessors; native R5 manifest +`sha256:d51f90816616848b93bcdc482e10a98d07061deb01eeed9e50700fc6e68b6051`; +`final-integration-r1/typed-execution-evidence.json` and byte-exact safe receipt +(original `/tmp/nk48-postgres-native-review-sguj7t8p/result.json`). No CNPG native +storage/HA/backup/lease/TLS/capacity/restore admission, authoritative protected +callbacks, real LDAP writer fencing/CAS/ABA exclusion, owner assent or production +endpoint activation is claimed. Those remain live T14 work. + + +## T22 — Establish registration recovery source gaps and complete descriptor review inventory + +```task +id: NK-WP-0048-T22 +status: done +priority: high +state_hub_task_id: "ea50d93b-0d04-55d2-98c4-9b6c3fe925b1" +``` + +Completed 2026-10-17: source-only owner-contract review and complete review +inventory. Inspected the actual registration adapter, Warden Journal/Executor, +NetKingdom evidence compatibility checker and Audit Core custody/read surfaces. +No existing admitted holder/readback supplies authenticated original-attempt +recovery: payload/creation are process-local, persisted evidence references are +hashed, and reconcile receives a new attempt without original context. The exact +four-owner contract requirements and non-replayable missing/ambiguous cases are +recorded; no recovery callback, publisher, credential grant or native trust root +was installed. T15 retains implementation and actual acceptance. + +The complete source1.2 inventory maps all seven follow-through steps to their +existing owners, missing exact contracts and created-only rollback requirements. +The actual maintained adapter regenerated the sole existing register-client +machine descriptor against the delivered 13,633,409-byte checker and current +sealed-memfd snapshot source. The checker rehash and Warden Descriptor.decode +round-trip passed; six owner descriptors remain absent. Actual source commands +render canonical1.1 and explicitly selected1.2 with execution.permitted false; +ordinary1.2 case admission still refuses its acknowledged/unimplemented type. +Canonical type/case/acknowledgment bytes were hash-checked unchanged. No tests were +added or rerun, no native configuration or effect occurred, and no owner assent +or complete machine bundle is claimed. T16 retains native executable custody, +complete exact descriptors, independent source review and normal version/type +admission. This extracted outcome supplies no native gate completion credit. + +Evidence in the-custodian: +`docs/changes/netkingdom-target-2026-10-17/native-registration-r1/`, especially +original-attempt-review.json, descriptor-composition.json, source-verification.json, +source-manifest.json and manifest.json. The prior sixteen-gate audit remains +immutable. Publication, normal task UUID allocation and projection are separate +coordinator follow-through; no Hub UUID is assigned by this source record. + + +## T23 — Review native journal package assignment and storage integration boundaries + +```task +id: NK-WP-0048-T23 +status: done +priority: high +state_hub_task_id: "6e7cd483-c7af-53a2-9d0a-3d76b6fb49ee" +``` + +Completed 2026-10-17: source/package placement and protected integration review +only, separate from T21's completed PostgreSQL source and disposable-SQL proof. +The owner review recommends a dedicated independently rolled-back +`rapp-identity-provisioner`, owned by NetKingdom on rail-kubernetes, with a +schema-valid draft declaration and no reef binding. No repository, registry +entry, native package assignment or owner assent was created. Existing +rapp-user-engine ownership/token delivery does not assign it the provisioner. + +The complete current rapp-postgres catalog has six declared consumers: four on +platform-pg and two on platform-pg-2. The inert renderer-valid provisioner +candidate fits platform-pg-2 at three of four, requesting one migration plus +four runtime connections; placing it on the first cell is refused as a fifth. +Normative package schema and full-catalog capacity/render checks passed. The +four-consumer ceiling is estimated, not measured native headroom, and no current +SQL session, backup or restore acceptance is claimed. + +The exact review identifies remaining package/substrate, SQL role/lease/TLS, +network and backup/recovery contracts; independently authenticated protected +scope callbacks; an external content-bound checkpoint holder with atomic +advance/loss handling; and all-LDAP-writer exclusion or provider CAS/generation, +including lifecycle cascades and remove/readd ABA. Generic Audit custody is not +that checkpoint service. A database-only fence, final reread or one replica +cannot exclude foreign directory writers. The native acceptance/recovery matrix +keeps storage/lease admission separate from separately approved directory +mutation; the production activation remains disabled pending trusted integration. + +Evidence in the-custodian: +`docs/changes/netkingdom-target-2026-10-17/native-storage-r1/OWNER-REVIEW.md`, +source-validation.json and hash-bound source-references.json, plus the inert +proposed declaration/consumer and renderer outputs. No native resource, lease, +credential, deployment, destructive effect, independent holder or native owner +acceptance was delivered. T14 retains these live gates with its original UUID +and progress status. This completed review adds no duplicate T21 acceptance +credit; publication, task UUID allocation and projection remain coordinator +follow-through and no UUID is hand-assigned here. + +## T24 — Ensure-groups adapter source candidate + +```task +id: NK-WP-0048-T24 +status: done +priority: high +state_hub_task_id: "1a3931ee-3b5f-52fc-9295-4073804f4bd2" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): ensure-groups-adapter.py, 8 tests (commit 5a2e7d6). Binds to the T14 PostgreSQL journal and refuses SQLite or a foreign binding. It accepts only canonical 1.1 cases, so T30 must add an explicit 1.2 path. It calls the provisioner's private `_group_binding`; a public helper is the cleaner shared edit. + +Split from T16 on 2026-10-17 so each candidate fits one per-task budget. Bind group creation to T14's journal/provenance seam; refuse without trusted journal callbacks. +Source candidate only, modelled on the T13 registration adapter +(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and +`registration-adapter.md`). Requires positive and refusal fixtures and +created-only rollback. Keeps canonical 1.1 as the default and uses no +`--allow-unimplemented` admission. Done means the candidate and its tests are +committed. NetKingdom (directory) owner source review before pinning, and any native action, +stay separate gates and are not supplied by this task. + +## T25 — Mint-secret adapter source candidate + +```task +id: NK-WP-0048-T25 +status: done +priority: high +state_hub_task_id: "0de2a7d9-874d-578b-a076-f653fe42e30b" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): mint-secret-adapter.py, 9 tests (commit 558c5bc). Value-free receipts only. The receipt schema `railiance-platform.custody-mint.v1` and the mint/destroy capability interface are proposals that Platform must confirm. + +Split from T16 on 2026-10-17 so each candidate fits one per-task budget. Platform-owned custody evidence seam: the adapter records value-free mint evidence only and never handles secret values. +Source candidate only, modelled on the T13 registration adapter +(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and +`registration-adapter.md`). Requires positive and refusal fixtures and +created-only rollback. Keeps canonical 1.1 as the default and uses no +`--allow-unimplemented` admission. Done means the candidate and its tests are +committed. Platform owner source review before pinning, and any native action, +stay separate gates and are not supplied by this task. + +## T26 — Deliver-secret adapter source candidate + +```task +id: NK-WP-0048-T26 +status: done +priority: high +state_hub_task_id: "94b3cbf0-c908-5ac6-b0db-67385c3e021c" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): deliver-secret-adapter.py, 8 tests (commit 1b60f6e). The lane `platform-secret-delivery` and the schema `netkingdom.deliver-secret.v1` are proposals that Platform must confirm. + +Split from T16 on 2026-10-17 so each candidate fits one per-task budget. Platform-owned delivery evidence seam (ESO/custody), value-free. +Source candidate only, modelled on the T13 registration adapter +(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and +`registration-adapter.md`). Requires positive and refusal fixtures and +created-only rollback. Keeps canonical 1.1 as the default and uses no +`--allow-unimplemented` admission. Done means the candidate and its tests are +committed. Platform owner source review before pinning, and any native action, +stay separate gates and are not supplied by this task. + +## T27 — Bind-env adapter source candidate + +```task +id: NK-WP-0048-T27 +status: done +priority: high +state_hub_task_id: "e56365f8-29b5-5c89-a329-8115f9f3b2a7" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): bind-env-adapter.py, 5 tests (commit 6fbce73). Binds references only. To fit the budget it does not yet wrap a full Warden `Adapter` like T13 (source-digest pin, authenticated evidence, parent-session revocation); it returns `(reference, run, rollback)`. That wrapping is a prerequisite for T30. + +Split from T16 on 2026-10-17 so each candidate fits one per-task budget. Bind the issuer Deployment env reference to the delivered secret reference, without values. +Source candidate only, modelled on the T13 registration adapter +(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and +`registration-adapter.md`). Requires positive and refusal fixtures and +created-only rollback. Keeps canonical 1.1 as the default and uses no +`--allow-unimplemented` admission. Done means the candidate and its tests are +committed. NetKingdom runtime owner source review before pinning, and any native action, +stay separate gates and are not supplied by this task. + +## T28 — Deliver-consumer-secret adapter source candidate + +```task +id: NK-WP-0048-T28 +status: done +priority: high +state_hub_task_id: "6dab281c-ea9a-5a17-a83f-e8e9a4d4484f" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): deliver-consumer-secret-adapter.py, 5 tests (commit 650c1ac). Type 1.2 only. It assumes the owner has validated ESO TokenRequest and the controller-RBAC admission; that stays an owner gate. + +Split from T16 on 2026-10-17 so each candidate fits one per-task budget. Consumer-side delivery evidence seam, value-free. +Source candidate only, modelled on the T13 registration adapter +(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and +`registration-adapter.md`). Requires positive and refusal fixtures and +created-only rollback. Keeps canonical 1.1 as the default and uses no +`--allow-unimplemented` admission. Done means the candidate and its tests are +committed. Platform and Apps owner source review before pinning, and any native action, +stay separate gates and are not supplied by this task. + +## T29 — Hand-over adapter source candidate + +```task +id: NK-WP-0048-T29 +status: done +priority: high +state_hub_task_id: "129db41d-4560-5e20-9f3b-c3c91e0a5601" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): handover-adapter.py, 8 tests (commit ec63539). The owner `apps` and lane `apps-consumer-handover` are proposals that the Apps owner must confirm. It has no reconcile path; restart handling follows T15. + +Split from T16 on 2026-10-17 so each candidate fits one per-task budget. Apps-owned consumer verification and hand-over receipt seam. +Source candidate only, modelled on the T13 registration adapter +(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and +`registration-adapter.md`). Requires positive and refusal fixtures and +created-only rollback. Keeps canonical 1.1 as the default and uses no +`--allow-unimplemented` admission. Done means the candidate and its tests are +committed. Apps owner source review before pinning, and any native action, +stay separate gates and are not supplied by this task. + +## T30 — Compose the descriptor set over the reviewed candidates + +```task +id: NK-WP-0048-T30 +status: done +depends_on: [NK-WP-0048-T24, NK-WP-0048-T25, NK-WP-0048-T26, NK-WP-0048-T27, NK-WP-0048-T28, NK-WP-0048-T29] +priority: high +state_hub_task_id: "6f6073ac-52dd-5b70-8874-6d76d4c072e4" +``` + +2026-10-17 done: coordinator review r3 (the-custodian `nk48-adapters-1.2-review/coordinator-review-r3.json`, +fd07eaa) approved all nine files at ff8f597, with R2-F1..F3 resolved, an independent +reconstruction of 117 passing and no open findings. This is a source review only: +it grants no owner admission, pin or native authority. Owner review and admission +continue under T16, which also takes the r2 observations, including the +`warden_executes: false` steps that carry execution descriptors. + +2026-10-17 coordinator review r2 (the-custodian `nk48-adapters-1.2-review/coordinator-review-r2.json`) +approved five files and rejected three, plus the artifact. The fixes are merged in +2ee4ed0: R2-F1, mint-secret revokes the session and the broker grant +independently (eeed8e1); R2-F2, deliver-secret binds to the admitted case and +derives its Secret and custody path, with no caller reference (640292c); R2-F3, +bind-env pins its five-file closure, and a new closure-completeness test checks +every adapter (d16db30). Each regression test failed on 68f4bbb. The artifact is +regenerated, and the full suite including kernel admission passed 117. Next is +the r3 review. Open for owner review under T16: the type marks four steps +`warden_executes: false`, yet each has an execution descriptor. + +2026-10-17 founder confirmed every proposed lane and schema name for T25-T29 +as-is. The artifact records them as `names_confirmed` (by founder, 2026-10-17), +and no step is still `owner_confirmation_required`. Next: independent source +review of the seven adapters. + +2026-10-17 founder decision (supersedes the `agent` decision below): the +mint-secret actor is `human`. Coordinator review r1 (the-custodian +`docs/changes/netkingdom-target-2026-10-17/nk48-adapters-1.2-review/coordinator-review-r1.json`, +finding NK48-R1-F1) showed that unchanged kernel a45cf47 refuses every +`effect: mint` admission whose actor is not `human` ("agent mint is +forbidden"), so the `agent` descriptor could never be admitted. Adapter, tests +and composer comment updated and the artifact regenerated (mint-secret +descriptor digest now `sha256:8c40c2884f8ba15e8cb6c98f33419c395e54fce8ef33d6e1a34782cf3f1fcfa9`); +98 tests pass against a45cf47. The set needs an independent r2 review. + +2026-10-17 `test_admit_1_2_adapter_descriptors.py` runs Warden's own +`Executor.admit` on all seven composed 1.2 descriptors, with the real 1.2 +type, the 1.2 forgejo example case, the reviewed `BrowserInputs` validator and +fixture-only trust (stand-in adapters, nothing dispatched). It also pins that +an agent mint descriptor is refused ("agent mint is forbidden") and that a +wrong lane is refused for every step. Against the old `agent` revision +(582ab9f) it fails as expected. Combined suite: 102 passed on ops-warden a45cf47 +and on current ops-warden. Fixture admission is not owner admission or a pin. + +2026-10-17 founder decision: the mint-secret actor is `agent`, not `warden`. +The kernel is unchanged. The adapter, its test and the composer were updated and +the artifact regenerated; all seven steps pass `Descriptor.decode` and +`complete_executable_descriptor_set` is true (98 tests passed). The artifact +stays `not_an_admission`, with no owner review, admission or dispatch permission. + +2026-10-17 composer merged (60b41a1 via 3c62bec) with follow-up 376b44b (merged; +combined suite 98 passed). `compose-1.2-descriptors.py` loads 1.2 only through an +explicit version root and refuses partial, duplicate, unknown or mismatched maps. +`descriptor-composition-1.2.candidate.json` is a reviewable candidate, marked +`not_an_admission`, with canonical 1.1 unchanged. ensure-groups and hand-over now +carry and honour `parent-session` revocation, and the hand-over owner is +parameterised (`railiance-apps` for Forgejo). Six of the seven steps pass +`Descriptor.decode`. mint-secret is still refused: its actor `warden` is outside +the kernel's human/agent set, and Platform (with ops-warden, if the kernel should +change) must decide. Five steps carry `owner_confirmation_required` names. The +set is complete only after both. Owner review, the canon section 6 successor +revision and admission remain T16's. + +2026-10-17 prerequisites merged (65559ef; combined suite 78 passed): +ensure-groups accepts 1.2 via an explicit `version_root='1.2'` (531151b), with +1.1 still the default. bind-env is now a full Warden v1 `Adapter` (7442d21); its +kernel-level fixture tests like T13's are still missing, and its `build()` +signature changed. Composition is unblocked in source. Owner confirmation of +the proposed step, lane and schema names remains before pinning. + +2026-10-17 inputs from T24–T29 that composition must resolve: T24 accepts +only 1.1 while T28 accepts only 1.2, so add an explicit 1.2 ensure-groups path; +T27 still needs full Warden `Adapter` wrapping; the lane, schema and owner names +proposed in T25, T26 and T29 need owner confirmation before pinning. + +Split from T16 on 2026-10-17. Compose the seven-step source 1.2 descriptor set +through the existing explicit version-root selection over T13 and T24–T29. No +partial descriptor map, canonical 1.1 stays the default, and new descriptor bytes +need a reviewed successor revision under canon section 6. Owner review and +admission remain T16's. + +## T31 — Restart and lost-receipt fixtures against Warden's original-intent interface + +```task +id: NK-WP-0048-T31 +status: done +priority: high +state_hub_task_id: "ed1ccd87-89a0-5e69-885c-0eaf5ab0bb5a" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): test_registration_original_intent_reconcile.py, 8 tests (commit 74adcd7), runs against the unmodified ops-warden a45cf47. The holder is a test-only in-memory store. For real T15 support the T13 adapter's `build()` needs an injected holder/readback capability, a `reconcile_original` callback, and a `verify` that accepts reconcile results. Rollback after restart stays refused until there is an owner contract. + +Split from T15 on 2026-10-17. ops-warden published the original-intent +lookup/context interface in `src/warden/case_run.py` (`OriginalIntent`, +`Journal.original_intent`, `Adapter.reconcile_original`; commit `a45cf47`). +Build T15's restart/lost-receipt fixtures for the registration adapter against +it: the original intent is kept separate from the reconcile attempt; wrong +attempt/case/source/principal and unavailable-holder cases refuse; and a missing +or ambiguous record stays uncertain, with no add replay or undo. The interface +is Warden's own dispatch record, not owner-authenticated created-only provenance. +Selecting the actual authenticated holder stays a T15 owner gate. + + +## T32 — Restore independent agent-mint admission regression coverage + +```task +id: NK-WP-0048-T32 +status: done +priority: high +``` + +Completed 2026-10-17, source tests only. Extracted from T16's coordinator r5 +finding NK48-R5-F1; no earlier adapter outcome is counted again. The real 1.3 +admission test now separately checks the committed descriptor map, an in-memory +fixture map acknowledging the changed agent descriptor to reach the independent +mint prohibition, and the human descriptor under an explicit fixture agent +runtime marker. Only `sso-mfa/k8s/keycape/test_admit_1_3_type.py` changes. + +Against frozen Warden `117fec9241e7b985dfb187b992e173d0a5dc3700`, the new +suite passes 10 tests / 32 subtests. Independent mutation evidence removes only +the mint admission guard in scratch: both new guard tests fail while eight +controls pass; the old suite passed that mutant. Custodian independently reran +the canonical successor: 10 tests / 32 subtests pass. Evidence: +`the-custodian/docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/` +(patch, frozen/mutant results and canonical-validation.json). No descriptor, +parameter pin, catalog, acknowledgment, kernel or native execution changed. +T16 still owns actual composition and admission; F2/F3 follow below. + +## T33 — Close the issuer-store reference finding with Platform source + +```task +id: NK-WP-0048-T33 +status: todo +priority: high +``` + +Owner: Platform for the fixed store/template source and its delivery boundary; +NetKingdom for the 1.3 type reference. Split from T16's current coordinator r5 +finding NK48-R5-F2. Next action: independently review the prepared Platform +candidate and publish a concrete owner-controlled issuer store kind/name/scope +and template reference, then integrate only that exact proposed source into the +1.3 deliver-secret entry point and regenerate its composition artifact. Candidate +and current review evidence are under Custodian +`docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/` +and `nk48-r5-f2-review-r1/`. + +Completion requires exact committed source, a passing independent source review +and unchanged type parameter bytes. No consumer store may be inferred as issuer +store, and no source declaration supplies owner assent, installed ESO support, +CCR/grant changes or a real delivery capability. Those remain existing +RPF-1.3-T4 and T16 admission/native requirements. + +## T34 — Close the consumer configuration inverse finding + +```task +id: NK-WP-0048-T34 +status: todo +priority: high +``` + +Owner: Apps for consumer configuration and inverse; NetKingdom for exact type +placement. Split from T16's coordinator r5 finding NK48-R5-F3. Next action: +independently review and implement the owner transaction/inverse through the +existing RAPPS-WP-0016-T05 path, with authenticated durable before/after release +provenance, exclusive serialization of all release writers and a fresh authorized +attended session for every inverse attempt after the hand-over session is revoked. +The inverse must remain reachable for public clients, confidential NO_CHANGE +secret delivery and verification failure before a hand-over receipt exists. + +Completion requires real owner configuration/inverse source bindings and +independent regressions, then exact reviewed 1.3 entry/rollback text and composed +artifact. A synthetic wrapper with an injected backend alone is insufficient. +Keep hand-over receipt rollback receipt-only and secret deletion created-only; +preexisting or drifted resources must refuse unsafe inverse. Candidate evidence: +Custodian `docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/` +and `nk48-r5-f3-review-r1/`. Source review, owner acknowledgment and native proof +remain distinct. T16's final composition/admission and T06's native first case +are not completed by this refinement. diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-r5-source-followthrough-r1/workplan.preimage.md b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-source-followthrough-r1/workplan.preimage.md new file mode 100644 index 0000000..8c3529a --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-r5-source-followthrough-r1/workplan.preimage.md @@ -0,0 +1,1519 @@ +--- +id: NK-WP-0048 +type: workplan +title: "Establish SecurityCaseTypes so that known security cases are light" +domain: infotech +repo: net-kingdom +status: active +flavor: implementation +owner: claude-code +topic_slug: netkingdom +created: "2026-10-08" +updated: "2026-10-17" +depends_on: [] +related_repos: + - ops-mason + - ops-warden + - key-cape + - railiance-platform + - informed-decision +state_hub_workstream_id: "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" +--- + +# NK-WP-0048 - Establish SecurityCaseTypes so that known security cases are light + +## Why + +The founder's direction (2026-10-08): setting up a security integration of a +kind that has been done before should be easy, meaning accept the case, then +ops-mason arranges it and ops-warden follows through. Setting up a new kind +needs ample documentation and tests and an informed-decision, because its shape +is part of the security architecture. + +Today every integration is hand-built: per-client helper scripts, per-lane +custody, founder checklists. The trigger was KEY-WP-0038 (Forgejo as an OIDC +client), whose next step was going to be yet another hand-written rollout +script. + +This plan continues the pattern work of NK-WP-0008 (pattern infospace), +NK-WP-0009 (tutorials) and NK-WP-0030 (scenario composition). It turns patterns +into **SecurityCaseTypes** that ops-mason and ops-warden can execute. Canon: +`canon/standards/security-case-types_v0.1.md` (proposed). + +Companion plans: ops-mason `MASON-WP-0010` (materialization) and ops-warden +`WARDEN-WP-0044` (follow-through). + +## Tasks + +```task +id: NK-WP-0048-T01 +status: done +priority: high +state_hub_task_id: "930ac3e9-20ec-5473-98cd-3fe8fddb730f" +``` + +### T01 — Accept the terminology and the canon + +Founder review of `canon/standards/security-case-types_v0.1.md`, including its +section 10 open questions. On acceptance the canon status goes from `proposed` +to `accepted`. + +2026-10-08: accepted by the founder (in session). Canon status `accepted`; +section 10's open questions are carried forward to the first type (T04, T05). + +```task +id: NK-WP-0048-T02 +status: done +priority: high +state_hub_task_id: "19c25d96-a55c-58f0-9168-3fdbc4955fc2" +``` + +### T02 — Inventory the de-facto types already built by hand + +Read-only. List every security integration built so far and group them by shape. +Known starting points: + +- browser OIDC relying parties on key-cape: informed-decision-approver, + coulomb-social, user-engine portal, openbao-admin, and Forgejo (pending) +- confidential service clients with `client_credentials`: secrets-engine + approval, requester and openbao clients, approval-engine operator +- workload KV read lanes on OpenBao: the railiance-platform credential lane + designs and CCRs, for example `railiance-backup-offsite-lane` +- short-lived SSH certificates through ops-warden (`warden sign`) +- tunnelled operator access (ops-bridge) + +For each: the repositories involved, the hand-built steps, attended human steps, +custody paths, verification evidence, and rollback (if any). Output +`docs/security-case-type-inventory.md`, ranking candidate types by how often the +shape recurs and how much manual work each instance costs today. + +2026-10-08: done, `docs/security-case-type-inventory.md`. 36 of 37 CCRs are +already `request_type: workload-kv-read`, a de-facto type reviewed each time as +if it were new. Browser clients: 5 hand-built plus Forgejo, each with its own +rollout script. Confidential service clients need a registration plus two or +three KV-read CCRs each. A forge read identity is needed in bulk by +ORGREF-WP-0002-T06. Recommendation: oidc-browser-relying-party first (unblocks +the forge), workload-kv-read second (largest payoff), then forge-read-identity. + +```task +id: NK-WP-0048-T03 +status: done +depends_on: [NK-WP-0048-T01] +priority: high +state_hub_task_id: "e487975b-73b1-516c-971d-da4cbbb93063" +``` + +### T03 — Schema, template and validator + +- `canon/schemas/security-case-type_v0.1.schema.json` and + `security-case_v0.1.schema.json` +- `security-case-types/_template/` with `type.yaml`, `README.md` and tests to copy +- a validator, extending `tools/security-scenario-composer/`, that checks a type, + checks a case against its type, and renders the case into the composer's + deterministic plan (still `execution.permitted: false`) + +Acceptance: the validator rejects missing sections, secret-shaped parameter +values, unpinned providers and cases against non-`implemented` types, with tests +for each. + +2026-10-08: done. `canon/schemas/security-case-type_v0.1.schema.json` and +`security-case_v0.1.schema.json`; `tools/security-case-types/security_case_validator.py` +(`validate-type`, `validate-case`, `render`, reusing the scenario composer); +`security-case-types/_template/` (a valid, runnable example type over +`net-kingdom.local-identity`, with README sections, an example case and tests that +travel with every copy). 31 tests pass, including the composer's existing suite. + +```task +id: NK-WP-0048-T04 +status: done +depends_on: [NK-WP-0048-T02, NK-WP-0048-T03] +priority: high +state_hub_task_id: "2fffe112-7d55-5680-9952-78d74f44f87f" +``` + +### T04 — Define the first type: browser OIDC relying party + +`security-case-types/oidc-browser-relying-party/`, version 1.0: + +- parameters: client id, display name, exact redirect URIs, scopes, client type + (confidential or public), MFA pin, the groups the consuming service maps, + custody path for the secret (confidential only), consuming service and its + owner +- composition: key-cape client registration (key-cape declares it as a playbook + capability), secret minting into custody (railiance-platform), LLDAP group + existence and membership (net-kingdom identity-provisioner), handover to the + consuming service +- materialization (ops-mason) and follow-through (ops-warden) sections +- verification: discovery lists the redirect URI; a test sign-in returns the + expected claims for one human; anonymous access to the custody path is denied +- rollback: remove exactly this client and its custody path +- README: threat model, MFA rationale, why redirect URIs are exact, which steps + still need an attended login and how that is minimised +- reference instances: informed-decision-approver, coulomb-social, user-engine + portal, openbao-admin + +Acceptance: complete per canon section 3, tests green, status `proposed`. + +2026-10-08: done. `security-case-types/oidc-browser-relying-party/` 1.0 (proposed): +`type.yaml` with 12 parameters (the custody reference is required only for +confidential clients), composition over key-cape's new +`key-cape.oidc-client-registration` declaration (contract PASS, `maturity: +planned`), materialization and follow-through, verification and rollback, all +conditional on client type where it matters. One attended step, +`register-client`, justified by the live registry being a Kubernetes Secret. +README with threat model, alternatives, an honest fit table (portal and vergabe +fit; Forgejo is the first confidential case; openbao-admin, coulomb.social and +informed-decision-approver do not fit 1.0), and known limits. Examples: Forgejo +(checked against the accepted request) and a public portal. The validator gained +`when` / `required_when` conditions and provider discovery. 43 tests pass. +ops-mason's and ops-warden's sections are drafted in the type. Their owners +review them in MASON-WP-0010-T01 and WARDEN-WP-0044-T01 before acknowledgment +(T05). + +```task +id: NK-WP-0048-T05 +status: done +depends_on: [NK-WP-0048-T04, MASON-WP-0010-T01, WARDEN-WP-0044-T01] +priority: high +state_hub_task_id: "3d987d3d-0797-538b-86e4-51185a327ae2" +``` + +### T05 — Acknowledge the type through informed-decision + +Prepare the Decision Memo from the type's README and `type.yaml` and submit it +through informed-decision's production intake. On acknowledgment the type +moves to `acknowledged`. + +2026-10-08: Decision Memo prepared. Hub decision `e418263e-a71f-4f01-a970-c87c9e187c09`. +`security-case-types/oidc-browser-relying-party/decision/`: `packet.md` (question, +one act, what acknowledging does not do, the shape, alternatives, attended +steps, limits, evidence), `build_intake.py` and the built `intake.json` (memo +class `decision-record`, review group `net-kingdom-admins`, resolves the hub +decision on answer). The envelope passes informed-decision's own +`validate_submission`. Queuing it is an attended operator step, a write inside +the production dispatcher pod: + + kubectl -n state-hub exec -i deploy/state-hub-memo-dispatch -- \ + python -m api.services.memo_dispatch --queue /data/private enqueue --envelope /dev/stdin \ + < security-case-types/oidc-browser-relying-party/decision/intake.json + +The answer then appears for review in informed-decision. + +2026-10-08: queued by the founder (`pending`, `queued`) after one refusal: the +first envelope's idempotency key was not State Hub's stable +`state-hub..` form, and the builder now validates against both +sides. The dispatcher delivered it to informed-decision (ledger 7 → 8 +delivered). T05 waits for the founder's answer in informed-decision, which +resolves hub decision `e418263e`. + +2026-10-08: ops-mason's review (MASON-WP-0010-T01) folded into the type, still +1.0 `proposed`: `secret_ref` is no longer a parameter, the type derives +`platform/workloads/keycape/{client_id}`; policies are +`workload-kv-read-keycape-{client_id}-eso` and +`workload-kv-read-keycape-{client_id}-{consuming_service}`, each with a templated +body (`read` on that one data path); new `openbao_role` +`external-secrets-keycape-{client_id}` (Kubernetes auth, ESO service account, +15m, no default policy); catalog entry `{consuming_service}-keycape-client`; +the two rollback steps on the KV path collapse into one metadata delete. The +schema and validator now require a `body` on policy and role items. 105 tests +pass. Two consequences: the delivered memo describes the type before these +changes (a case-set custody reference, two differently named policies, no ESO +role), so the founder answers on a superseded shape unless the memo is +re-sent; and Forgejo's custody path moves from the accepted request's proposed +`platform/workloads/forgejo/netkingdom-oidc-client` to +`platform/workloads/keycape/forgejo`, which key-cape (KEY-WP-0038) and +railiance-apps (RAPPS-WP-0016-T02) need to adopt in T06. ops-warden's review +(WARDEN-WP-0044-T01) is still in progress. + +2026-10-08: revised memo `nk-wp-0048-t05-oidc-browser-relying-party-r2` built +(`packet.md`, `intake.json`, packet `sha256:af6435e2…`); it states that it +supersedes the first memo and passes informed-decision's and the dispatcher's +validation. It needs a new memo id because the dispatcher refuses new content +under a delivered idempotency key (`idempotency_conflict`). Queuing it is the +same attended step as above. Both memos serve hub decision `e418263e`; the +first answer resolves it, so the first memo must not stay open: an accept on it +would acknowledge the superseded shape, and a decline would resolve the +decision against r2. Close it with `return` (a reviewer verb that requests a +new revision and does not resolve the hub decision; only accept and decline +do), commenting that r2 supersedes it, then answer r2. `withdraw` exists in +the vocabulary but is not offered to reviewers, and lifecycle `revise` only +applies to lifecycle-registered memos, not intake-delivered ones. + +2026-10-09: the founder reports r2 enqueued and the first memo returned. At +close, hub decision `e418263e` still showed `open` (not verified whether r2 +was answered or the answer is still in transit; the dispatcher ledger was not +read). ops-warden's review (WARDEN-WP-0044-T01, ops-warden +`history/2026-10-08-oidc-rp-follow-through-review.md`, d470f2e) arrived at +21:25, before r2 was built, and is not in r2. Required before WARDEN-WP-0044-T02: +R1 a per-step `lane` (missing: identity-provisioner caller credential, +railiance-platform broker grant), R2 canon meaning of `warden_executes: true` +(caller proxy, caller identity, no standing ops-warden token; ADR-0002), R3 a +per-step `value_handling`. Refinements R4 (one type-level catalog lane instead +of a per-case entry, which conflicts with ops-mason's item 5), R5 (ESO Ready gate +before bind-env), R6 (exact accepted memberships), R7 (mfa-pinned and +secret-delivered checks). Next: if r2 was accepted, fold R1-R3 and R5-R7 in as +a follow-up and settle R4 between ops-warden and ops-mason before T06; if not, +hold r2, fold them in and build r3. + key-cape and railiance-apps were told about Forgejo's new custody +path. + +2026-10-09: r2 was not answered (hub decision `e418263e` open; dispatcher 9 +delivered), so r2 is held and r3 replaces it. ops-warden withdrew R4 for R4′ +(message a66a9204): one catalog entry per case with its body templated in the +type. Folded in: + +- Canon section 3: a narrow meaning of `warden_executes: true` (caller proxy, + caller identity, no standing credential; ops-warden ADR-0002), plus per-step + `lane`, `value_handling` and an optional `ready_check`. A catalog entry body is + now required, like policy and role bodies. +- Schema and validator enforce all of these. +- The type: lanes `identity-provisioner-groups` and + `keycape-client-secret-generate` (both new) and `key-cape-oidc-login`; + `mint-secret` is `generated_in_custody`; a Ready gate on `deliver-secret`; + `ensure-groups` adds exactly the accepted case's memberships; new checks + `secret-delivered` and `mfa-pinned`; the catalog-entry body follows R4′. +- 110 tests pass. + +Memo r3 (`nk-wp-0048-t05-oidc-browser-relying-party-r3`, packet +`sha256:726dfc01…`) passes informed-decision's and the dispatcher's validation. +The founder queues it with the same command, returns r2 with a comment that r3 +supersedes it, then answers r3. + +Prerequisites for T06, not for T05: +- the two new lanes, each through its owner; +- a founder-approved rollout of an identity-provisioner image containing + 630ebfd (the deployed one is main-6c4fcaf from 2026-09-24, without + `/v1/groups/ensure`). + +ops-mason's request to create LLDAP group `fluid-telegram-operators` (message +259ec26d) was answered: not possible from here by that path; the founder creates +it in the LLDAP admin UI. + +2026-10-09: ops-warden finished its side of WARDEN-WP-0044-T01 (message +`58d33f40`, commit 0e7ceb8): the revised type conforms to `warden case +check-type`, with no further changes asked of the type. This acknowledges that +review. Its later addendum R8 (message `af274783`, ops-warden +`history/2026-10-08-oidc-rp-follow-through-review.md`) showed that lane +`identity-provisioner-groups` cannot exist as declared: the provisioner's only +HTTP credential is user-engine's runtime token, and its NetworkPolicy admits +only user-engine pods, so a caller-proxy lane would act as user-engine. Taken +option 1 (ops-warden's recommendation): `ensure-groups` and its rollback are +attended, in the `register-client` session on `key-cape-oidc-login`, run inside +the provisioner pod; no token is read. Option 2 (an operator credential of its +own on the provisioner) stays open for a later version. `warden case +check-type` now reports only `keycape-client-secret-generate` +(railiance-platform) as missing. 89 tests pass. + +Hub decision `e418263e` was still open, so memo r4 +(`nk-wp-0048-t05-oidc-browser-relying-party-r4`, packet `sha256:452ea421…`) +replaces r3; it passes informed-decision's and the dispatcher's validation. The +founder queues it with the same command, returns r2/r3 if still open with a +comment that r4 supersedes them, then answers r4. + +Prerequisites for T06 are now: lane `keycape-client-secret-generate` +(railiance-platform), and a founder-approved rollout of an identity-provisioner +image containing 630ebfd. + +2026-10-09: done. The founder queued r4 (`pending`, `queued`), returned r3 as +superseded and accepted r4 in informed-decision. informed-decision's hub +write-back is off in production (`INFD_HUB_WRITEBACK` unset), so net-kingdom +resolved hub decision `e418263e` from the founder's report, naming memo r4 in +the rationale. The type is `acknowledged`; it becomes `implemented` with T06. + +```task +id: NK-WP-0048-T06 +status: wait +depends_on: [NK-WP-0048-T05, NK-WP-0048-T08, NK-WP-0048-T09, NK-WP-0048-T12, NK-WP-0048-T13, NK-WP-0048-T14, NK-WP-0048-T15, NK-WP-0048-T16, NK-WP-0048-T17, NK-WP-0048-T18, NK-WP-0048-T20, MASON-WP-0010-T03, WARDEN-WP-0044-T03] +blocking_reason: "Type1.2 architecture has a confirmed source-only founder acknowledgment; canonical1.1 remains unchanged/default and the1.2 reference case stays proposed. Needs explicit version selection/reference-source integration without normal admission bypass, closed owner adapters plus global/native receipt verifier (WARDEN-WP-0044-T03), independently verified six-object structural materialization and CCR42 claims/created-only provenance, exact broker/case/source reconciliation, then separately authorized native mint/delivery and attended registry/consumer verification. Source acknowledgment, a bundle or local tests do not prove implemented/materialized/live status." +priority: high +state_hub_task_id: "d9b26bcf-a7c6-59a2-92a0-f913d76a203c" +``` + +### T06 — First KnownSecurityCase: Forgejo + +Write `security-cases/forgejo-netkingdom-oidc.yaml` from the parameters the +founder accepted in KEY-WP-0038-T01 (`key-cape/docs/forgejo-oidc-client-request.yaml`), +so that acceptance carries over as the case acceptance. ops-mason materializes it; +ops-warden follows through and verifies it. The type becomes `implemented` once +this reference case is live. + +This replaces the hand-built path proposed for KEY-WP-0038-T02. + +2026-10-09: case written, `security-cases/forgejo-netkingdom-oidc.yaml`, status +`accepted`: the example's parameters plus the founder's 2026-10-08 acceptance of +the key-cape request (owner and founder), with the custody-path derivation +covered by the type's acknowledgment (memo r4). It validates and renders (plan +`execution.permitted: false`; three attended steps on `key-cape-oidc-login`); +it needs `--allow-unimplemented` until it is live, since it is the case that +makes the type `implemented`. A test keeps it equal to the type's example. +Still open before it runs: ops-mason renders and builds it (MASON-WP-0010-T02, +T03, unblocked by T05), ops-warden plans and runs it (WARDEN-WP-0044-T02, T03), +railiance-platform's `keycape-client-secret-generate` lane (asked by ops-mason +`789feb39` and ops-warden `d9f889b1`, both unread), and a founder-approved +identity-provisioner rollout containing 630ebfd. + +2026-10-09: identity-provisioner rolled out, founder-approved in session: +`main-630ebfd` (`sha256:bafb4860…`, built by the image workflow) replaces +`main-6c4fcaf` (`sha256:ffacd5d7…`; only change: 630ebfd, additive). Applied +with `kubectl set image` on the Deployment only (generation 17 → 18, 1/1 Ready); +the file's Service, NetworkPolicies and Ingress were not reapplied. Verified in +the pod: `ensure_service_groups` and `revoke_service_groups` present, `/readyz` +200; no token used, no group changed. Rollback: `kubectl -n sso set image +deploy/identity-provisioner provisioner=forgejo.coulomb.social/coulomb/identity-provisioner@sha256:ffacd5d790a5b7800e0535f3aa95b7de52c7a68e4f575b9192c3e1622fa672c2`. +The pin in `sso-mfa/k8s/identity-provisioner/deployment.yaml` follows. + +2026-10-09: ops-warden closed WARDEN-WP-0044-T01 and shipped `warden case plan` +(T02, message `06303d1d`); the accepted Forgejo case is refused until ops-mason +materializes it, and a materialized copy plans cleanly once +`keycape-client-secret-generate` exists. Its observation that `bind-env` carries +a literal `KEYCAPE__CLIENT_SECRET`: the validator now renders a +`{param:env}` format spec (upper case, `-` as `_`, as `keycape-client.py` +derives the name) and rejects unknown specs, and follow-through and rollback +text is now checked for unknown placeholders too (patch-level, canon section 6). +The type keeps the literal until ops-warden's planner (`text.format`) renders the +spec as well; asked of ops-warden. 53 tests pass. + +2026-10-10: ops-warden rendered `{param:env}` in `warden case plan` +(23ba584, message `c1df14b7`; other specs and `!conversion` refused). The type +adopts it as a 1.0 patch (canon section 6): `bind-env` and its rollback now read +`KEYCAPE_{client_id:env}_CLIENT_SECRET`. ops-mason reports the Forgejo case +rendered and built (message `1ebddae9`; CCR drafts handed to railiance-platform), +so T06 now waits on the platform apply, the mint-secret lane and the attended +register-client session. + +2026-10-10: key-cape adopted the type's custody path (key-cape ab5cccd, message +`88a4559c`): `docs/forgejo-oidc-client-request.yaml` and KEY-WP-0038-T02 name +`platform/workloads/keycape/forgejo`, field `CLIENT_SECRET`, delivered as +`sso/keycape-forgejo-client` through `external-secrets-keycape-forgejo`, and the +`forgejo/netkingdom-oidc-client` proposal is withdrawn. This matches the type and +the Forgejo example; no change needed here. + +2026-10-10: key-cape's type-composition review (key-cape 6038483, message +`8e5ed5f6`) found the composition sound with two blocking findings, taken as a +1.0 patch (stricter validation and documentation, canon section 6): +R1, redirect URIs now refuse `?` and `#` in the type pattern and in +`keycape-client.py`'s `REDIRECT` (KeyCape refuses `?` at startup and the single +replica would stop); R2, `allowed_scopes` always includes `groups`, the renderer +refuses `access_groups` without it, and `access_groups` now says the consumer +enforces the gate because KeyCape has none; R3, `mfa_required` "false" means the +per-user privacyIDEA policy; R4, register-client's justification records the +restart for every client, the quiet window, and that the rendered registry must +pass KeyCape's client validation before it. The accepted Forgejo case and both +examples still validate; 116 tests pass. +Open, needs a decision: R2's verification step that the consumer enforces the +group gate is a new verification step, hence 1.1 (minor), and the validator pins +cases to the exact type version, so the accepted 1.0 Forgejo case would need +re-pinning. Also open with key-cape: a KeyCape entry point that validates a +rendered client registry without starting the server; until then +`keycape-client.py` mirrors the redirect rule. + +2026-10-10: founder go for 1.1, so no 1.0 run overlaps. The type adds +verification `consumer-enforces-groups`, and hand-over now requires the consumer +to enforce `access_groups` (minor, canon section 6; risk class unchanged, no new +acknowledgment; history in the type README). The accepted Forgejo case and both +examples are re-pinned to `oidc-browser-relying-party@1.1` before any apply; the +acknowledgment intake keeps `@1.0` as the record of what was acknowledged. +ops-mason's build and ops-warden's case-ref digest predate this and need a +re-render. + +2026-10-10: ops-mason re-rendered the Forgejo case from `@1.1` (message +`22c6bba6`; plan digest `af1f4f81…`, no collisions) and rebuilt the bundle. The +rendered OpenBao objects are identical to 1.0, so the already applied +CCR-2026-0041 stands; 1.1 changes only consumer-side steps. key-cape shipped +`keycape check-config` (c528daa, message `a7b0aeff`): KeyCape's startup checks +without secret values, including the new duplicate-clientId rule. +`keycape-client.py --check-config ` now runs it on the live and the +proposed registry, fed on stdin, and apply refuses a proposed registry that +fails; register-client's entry point names the option (1.1 patch). The deployed +image predates c528daa, so the binary is built from key-cape source for now. +Before the next KeyCape rollout (Forgejo register-client or the CCR-2026-0018 +window), the attended operator runs `inspect --check-config` so the live +registry is checked too. + +```task +id: NK-WP-0048-T07 +status: wait +depends_on: [NK-WP-0048-T06] +blocking_reason: "The next types follow once the first one is proven end to end." +priority: medium +state_hub_task_id: "c3c9c3ae-4f23-5ba7-9e6f-e63bcf29b9f7" +``` + +### T07 — Queue the next types + +From the T02 ranking, open the next type drafts (likely the confidential service +client and the workload KV read lane), each to be acknowledged on its own. +Record which existing hand-built instances get adopted as cases, without +rewriting them in place. + +```task +id: NK-WP-0048-T08 +status: done +priority: high +state_hub_task_id: "8e519082-dc8b-5e46-b660-03d03e55dcf6" +``` + +### T08 — identity-provisioner: ensure service-access groups + +The type's `ensure-groups` step calls `identity-provisioner groups ensure --groups + --members `. The provisioner already has idempotent +`_ensure_group` and `_add_group`, but exposes only tenant-scoped operations. Add +the general operation (create if absent, add the listed memberships, never +remove others), its rollback (remove only this case's memberships), tests, and +the ops-warden route for its service identity. + +2026-10-08: done. `identity-provisioner/provisioner.py` gained +`ensure_service_groups` (`POST /v1/groups/ensure`) and `revoke_service_groups` +(`POST /v1/groups/revoke`), behind the existing service-token check. Names +matching `tenant*`, `netkingdom-*` and `lldap_*` are reserved; members must +already exist; everything is validated before any change; memberships are read +back before success; nothing else is ever removed; groups are deleted only when +asked and empty. 7 new tests (39 in the provisioner suite). The type's +`ensure-groups` step and its rollback now name these endpoints. The ops-warden +route for the provisioner's service token belongs to WARDEN-WP-0044-T01. + +```task +id: NK-WP-0048-T09 +status: done +priority: high +state_hub_task_id: "0ee12dfc-8260-5038-bd39-cc7fb4113be0" +``` + +### T09 — The `keycape-client` entry point and its declaration + +Planned first as key-cape KEY-WP-0038-T04. Moved here because the machinery a +live registration needs already lives in net-kingdom, where KeyCape is deployed +(`sso-mfa/k8s/keycape/`): the contained kubectl, the cluster-identity pin and the +issuer pin. A copy in key-cape would drift. + +2026-10-08: done. `sso-mfa/k8s/keycape/keycape-client.py` derives the KeyCape +client record from a case (`client-from-case`). It adds or removes exactly one +client with the existing safeguards (byte-preserving merge, exact match or +refuse, shared-reference refusal, compare-and-swap, read-back, contained errors), +requires the Deployment env binding first for confidential clients, and requires +the case acceptance record for `apply` instead of a per-client founder decision +id. 15 tests, including a regression showing that the vergabe case reproduces +its hand-built registration exactly; all 71 helper tests pass. Declaration +`capabilities/playbooks/net-kingdom.keycape-client-registration.yaml` (contract +PASS, `maturity: implemented`) replaces key-cape's draft. The type now pins it, +and for confidential clients adds `deliver-secret` (delivery from custody into +the sso namespace) and `bind-env` (Deployment env), attended in the same session +as `register-client`. The per-client helpers stay until their clients are +adopted as cases. + +2026-10-10 — NetKingdom owner review of the execution seam, source only: +ops-warden/docs/security-case-execution-proposal.md SHA256 78ab1439e4c5beb205aeccf15b1b2e706511be55ab195c5a3aa682f326c7c77c +was reviewed against INTENT, SecurityCase canon, acknowledged type1.1, current +case and owner source. docs/security-case-execution-owner-review.md supports +the closed adapter/receipt direction with explicit versioned schema, structural +materialization provenance, unchanged caller/session boundaries, safe CAS +reconciliation and exact-created-object rollback requirements. This is owner +review, not approval. Supporting file digests are in the adjacent sources JSON. + +Current source exposes a circular wait: ops-mason T03 waits on mint while Warden +and the broker refuse a non-materialized case. Structural acceptance therefore +needs its own native pinned receipt before mint, not a successful mint before +materialized. The platform broker additionally pins type1.0 while Forgejo is +accepted at1.1; platform-owner alignment and tests are required, not an unchecked +version bypass. T06 remains wait. No case/type lifecycle status, grant, authority +or live runtime changed by this review. A proposed informed-decision would +adopt the source contract only; native materialization and execution acceptance +stay distinct. + +Final proposal re-review: staged Warden proposal SHA256 78ab1439e4c5beb205aeccf15b1b2e706511be55ab195c5a3aa682f326c7c77c +adds only the explicitly local reducer appendix, not an accepted wire protocol +or executor. The exact-final review and sources JSON supersede the earlier +1035f049... proposal review. Platform staged the exact type1.1 broker correction +with ten fake-backend subprocess checks (script SHA256 +9576cd211cfc06526ec4cc9bd0a5b06f0f59287a68931bb2d6e3592a951ddc7d). +The next source work is the versioned closed adapter/receipt/materialization +contract, not another relaxation of the broker's status gate. Native work still +requires installed broker provenance, structural reader receipts, exact new +materialized-case bytes, caller grant/session revocation and verified execution. +No installed/native acceptance is inferred from that staged correction. + +2026-10-11 — new source-only type1.2 architecture acknowledgment: +Confirmed native founder ACCEPT of infd-20261011-forgejo-type12-source-r1@1 +at06:47:39Z, approval8e9332c8-754a-420c-b8b7-445cb4fe6765. Exact frozen packet +0b377d647ca748cd03c92f9792396412048055d4bde9f7bb06ff6d413f7acd68 and submission +585cee731de30d2b37883a57abfd9eb32f023ee694e8d911989109d5caf80569; native receipt +retained in docs/evidence/2026-10-11-forgejo-type12-source-acknowledgment/. +The complete acknowledged1.2 source package is isolated under +security-case-type-revisions/1.2/oidc-browser-relying-party; default loader and +current accepted1.1 case remain untouched because load_type is single-version. +The new proposed1.2 case is NOT accepted/materialized/live and has no acceptance +fields. Actual reference-migration design approval is a separate Apps owner +receipt; no1.1 approval is inherited. Source-recording review/test proof and exact +next gates are in docs/forgejo-type12-source-acknowledgment-followthrough.md. +T06 remains wait and T07 remains its existing dependent task; type is NOT +implemented and no first-case/native grant or broader workplan finish is asserted. + +2026-10-11 — bounded explicit version selection source preparation for T06: +The validator now offers optional exact version_roots/--version-root VERSION=PATH +selection with duplicate/mismatch/traversal/symlink refusal and no fallback. The +default1.1 source and historicalacceptedcase hashes/ordinaryrefusal/preparation +render stayunchanged; actual1.1 is acknowledged, not implemented. Acknowledged1.2 +ordinaryadmission also refuses; optionalpreparation remains nonexecuting.43local +validatorchecks pass and original1.1 CLIrenderbytes match exactly. Makerproof: +docs/evidence/2026-10-11-explicit-type-version-selection/source-integration-validation.json. +FormalNetownerreview/reference-source integration and all existing native adapter, +provenance/broker/materialization/attendedproof gates remain; T06 stayswait, no +new residual or firstcase/typeimplementation/native grant is inferred. + + +2026-10-16 — isolated source preparation for T06 (canonical integration pending): +Added `check-evidence` to the existing validator CLI. It reuses exact version +selection and ordinary type/case/render admission, checks actual Mason structural +request source/inventory/projection pins and reads actual Warden journal rows +read-only. Successful rows require exact type-declared descriptors and attempt/ +revocation fingerprints. Existing acknowledged types without machine descriptors +remain plan-only. The compatibility result never claims authenticated native +completion: Request and journal fingerprints are not portable native evidence. +Owner authentication/status binding and actual native gates remain unresolved; +T06/T07 statuses and both type/case lifecycles remain unchanged. No dispatch, +credential access or authority expansion is introduced. See tool README for the +existing owner protocol boundaries and source integration tests. + +Concrete next Warden owner integration: current `Executor.admit` refuses every +case parameter whose sensitivity is not public/operational, so browser +`redirect_uris`, `allowed_scopes`, `client_type`, `mfa_required` (and 1.2 +`consumer_namespace`) fail after otherwise prepared admission. The sensitive +classification is a security-change review level, not secret content. The +Warden owner must validate through NetKingdom's existing exact-version case/type +validator, then project only descriptor-declared safe target metadata; do not +remove the credential/value refusal or copy complete parameters into journals. +Actual owner descriptor acknowledgment and independent evidence admission remain +prerequisites, with normal admission still refusing unimplemented types. + + +## T10 — Source compatibility checker for Mason and Warden evidence + +```task +id: NK-WP-0048-T10 +status: done +priority: high +state_hub_task_id: "4b06ca97-c1e4-5680-bf22-ab75ab1646a4" +``` + +Completed 2026-10-16 in published source `c31d21ba`. The read-only +`check-evidence` command checks the actual Mason structural request and Warden +journal source protocols against an exact version/render. Independent source +review passed 103 tests at that checkpoint. It always reports native completion +false and execution prohibited. Fixtures and local journal fingerprints do not +provide authenticated owner/native evidence. This records completed source scope +previously described under T06; it does not duplicate native acceptance credit. + +## T11 — Validate the optional machine-descriptor pin contract + +```task +id: NK-WP-0048-T11 +status: done +priority: high +state_hub_task_id: "ef2a0119-f59e-5e45-9519-115da9df7b85" +``` + +Completed 2026-10-16. Reproduced the JSON schema rejecting a valid +`execution_descriptors` map while the Python validator accepted malformed maps. +The schema now supports the existing Warden optional exact-byte pin map; semantic +validation binds keys to declared steps, bounds the map and checks digest shape. +The direct evidence API refuses malformed maps before accessing them. Twenty-one +new checks and all 127 affected source checks passed. No current type/case bytes, +status, owner descriptors or native acceptance changed. Source evidence and +publication receipt: Custodian +`docs/changes/netkingdom-target-2026-10-16/browser-acceptance-next/`. + +## T12 — Prepare NetKingdom-owned attended step adapters + +```task +id: NK-WP-0048-T12 +status: todo +priority: high +state_hub_task_id: "7ce4eee3-cd47-55a9-bfa5-4f1835d4d6fd" +``` + +Owner: NetKingdom. Retained scope: prepare bounded attended Deployment env, +config verification and restart/readiness steps around the existing helpers. +Next action: identify exact current entry points and their revision/readback +contracts, then implement fixed source-pinned callbacks or receipt-only attended +steps. Completion requires positive/refusal checks, exact target/source pins and +uncertainty behavior without acquiring user-engine runtime identity or exposing +registry/client secrets. Existing caller/session, cluster/issuer, CAS/readback +and status-only output boundaries remain mandatory. + +2026-10-16 task refinement: the original broad adapter scope is split into T13 +(completed registration source candidate), T14 (group creation provenance), T15 +(durable registration recovery) and T16 (descriptor/artifact selection and owner +review). This preserves T12's task ID and Hub UUID while giving each remaining +outcome an owner and completion gate. T06 now names every source dependency; +its native/reference lifecycle acceptance scope remains unchanged. Completion +of any source task supplies no Warden, Platform or Apps assent and no native +execution authority. + +## T13 — Prepare the fixed registration adapter source candidate + +```task +id: NK-WP-0048-T13 +status: done +priority: high +state_hub_task_id: "b264c10b-565c-5d29-b788-03cf5fbd2883" +``` + +Completed 2026-10-16, source implementation and synthetic fixture proof only. +Added an injection-only adapter around actual `keycape-client.run`: fixed +case/principal/human/lane/source/acceptance binding, existing CAS/readback, +metadata-only owner evidence and per-attempt parent-session revocation boundary. +The descriptor source composition pins the exact checker executable digest +alongside adapter/helper source. Creation-only rollback authenticates its +creation receipt and refuses changed postcreation UID/resourceVersion; +pre-existing clients confer no rollback authority. Missing readback, evidence, +revocation or process-local creation provenance cannot become retry-safe success. + +Evidence: Custodian `docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/` +R3 candidate, explicit patch and postimages, retained R1 rejected custody finding +and R2 revision, and 36 passing source checks (16 new, 20 existing). Three checks +use actual Warden kernel admission/dispatch/rollback with published Warden +`d97652a3bb5fa733b063d71a1b74068efcccf572` producer pins unchanged. Owner trust, +descriptor/type/case and Kubernetes objects remain synthetic fixtures. This +records a completed source deliverable, not independent review, publication, +type acknowledgment, exercised procedure, identity admission or native browser +acceptance. Independent root source review: Custodian browser-adapter-next/coordinator-review-r3.json. Source integration and normal publication/synchronization receipts: Custodian browser-adapter-next/canonical-integration.json. These receipts supply no other-owner assent or native authority. + +Source publication: `0775c2bfa41af236301155bb5a69bbf088bdf823`, independently +reviewed by the Custodian coordinator before integration. The required initial +projection and ordinary UUID writeback both completed with zero failures and +zero automation errors. Generated-only brief commits were refused by the normal +hook; exact pre-existing staged/working brief bytes were restored. The separate +canonical integration receipt retains those refusals and projection evidence. +Native admission and all other-owner acceptance remain open. + +## T14 — Admit governed group rollback against native owner boundaries + +```task +id: NK-WP-0048-T14 +status: progress +depends_on: [NK-WP-0048-T20, NK-WP-0048-T21] +priority: high +state_hub_task_id: "fce95440-a25b-5f27-8ef3-a3d182099113" +``` + +Owner: NetKingdom. Next action: bind the completed T21 PostgreSQL journal to +an admitted dedicated CNPG consumer through the existing `rapp-postgres` +database-per-consumer contract. Identify the actual deployment/package and +custody owner, admitted placement/capacity and role/renewable lease/TLS target; +implement the independently retained exact content checkpoint and authoritative +tenant/case/source/principal callbacks before enabling governed group operations. +The injection-only backend and fixture callbacks do not supply production scope +or authorization. Existing service tokens, caller-supplied JSON and provenance +receipts confer no new grant. The default production endpoint remains disabled +without the trusted integration. SQLite remains local/disposable only; any +production exception requires explicit scoped governance under ADR-0015. + +2026-10-17 owner-integration refinement: T23 records the completed source/package +placement review, distinct from T21's source/disposable-SQL proof. Its dedicated +`rapp-identity-provisioner` recommendation is draft and unbound, not an accepted +package or existing repository. The full six-consumer rapp-postgres renderer +accepts the inert candidate on platform-pg-2 as its third of four declared +consumers; first-cell placement is refused as a fifth. Neither the estimated +four-consumer ceiling nor source requests establish native headroom or backup. + +Next independent owner contracts: NetKingdom/package/substrate owners accept +exact deployment/rollback fate; rapp-postgres admits the dedicated consumer, +roles, five-connection budget and actual backup/restore; Platform admits exact +renewable runtime/migration delivery and TLS/CA/SAN; network owners scope SQL, +broker and checkpoint paths without assuming a narrow policy subtracts additive +same-namespace access. Protected caller owners must provide authoritative +GroupJournalBinding construction and current authorization; an independent +existing holder must authenticate and atomically advance the exact content +checkpoint, not a counter or ordinary Audit receipt. Directory/runtime owners +must select and prove either an exclusive all-writer maintenance barrier or +provider-supported CAS/generation, including established sessions, lifecycle +cascades and remove/readd ABA. No source review establishes those owner facts. + +The default PostgreSQL activation and destructive native rollback remain refused. +Storage/lease acceptance can be proposed separately from LDAP mutation only after +exact owner source/target/custody/verification/recovery and cost/risk review; +State Hub guard approval grants none of it. Source-only integration review and +native acceptance matrix: Custodian native-storage-r1/OWNER-REVIEW.md. T14's +existing status, UUID, dependencies and native completion gate are retained. + +2026-10-17 refinement: T20 retains the historical durable SQLite source and +actual disposable LLDAP proof. T21 extracts the completed PostgreSQL source and +bounded real disposable SQL compatibility proof. T14 remains `progress` for +native CNPG admission/recovery, protected owner integration and exclusion of all +external LDAP writers. Separate source publication/projection and independent +cleanup receipts belong to coordinator follow-through; no missing native assent +is represented as received. + +Completion still requires independently reviewed/admitted ADR-0015-conforming +CNPG storage (or an explicit governed exception), backup/lost-store recovery and +migration of legacy requests; a native +writer-isolation/fencing or provider CAS/version contract that closes external +writer races and remove/re-add ABA; exact case/source/principal and current +native object bindings for protected callbacks; and actual per-attempt evidence, +revocation and readback/refusal verification through the existing owner paths. +Only independently retained confirmed creations may be undone. Every +pre-existing/shared group and membership must remain preserved; unresolved +intents never expire into retry authority. The disposable fixture supplies none +of these native custody, authorization, owner-acceptance or production claims. + +## T15 — Define authenticated recovery for lost registration process state + +```task +id: NK-WP-0048-T15 +status: wait +depends_on: [NK-WP-0048-T31] +blocking_reason: "Source candidate 061461e is merged. Waiting on the owner to select the real authenticated original-attempt holder and native readback, and on independent re-review of the changed T13 adapter source." +priority: high +state_hub_task_id: "1b825705-4848-5edc-832f-b1622cd9bdb7" +``` + +2026-10-17 source part merged (061461e via 65559ef). `build()` takes optional +paired `holder`/`observe` capabilities and installs `reconcile_original`; the T31 +fixtures now run through the adapter's own path. Rollback after restart is still +refused, and there is no add replay. This changes `keycape-registration-adapter.py`, +so the revision reviewed under Custodian `browser-adapter-next/coordinator-review-r3.json` +is superseded. The new revision has no independent review yet and must not be +pinned on the strength of the old one. + +2026-10-17: T31 fixtures done (74adcd7). What remains for T15: the owner +selects the real authenticated original-attempt holder, and the T13 adapter +gains the holder capability, `reconcile_original` and reconcile-aware `verify` +described in T31. + +Owner: NetKingdom for registration state, with Warden owning kernel evidence +admission. T22 records the completed source review and exact existing-seam gaps; +T15 retains implementation and authenticated restart/lost-receipt acceptance. +Next action: select and admit the actual owner publisher/authenticator and bounded +original-attempt readback contract for the merged holder/observe adapter seam. +Warden's original-intent persistence/interface (`a45cf475`) and additive validation +(`117fec9241e7b985dfb187b992e173d0a5dc3700`) are now published and projected. +The local Journal retains admitted nonsecret original context, validates original +actor/attempt and metadata linkage to the original running receipt, and supplies +it separately from the new reconciliation attempt. This local consistency is not +independent original-holder authorization or creation provenance; neither the +journal nor a current matching registry entry reconstructs creation authority. + +The owner contract must bind the original Invocation, case/type/descriptor/catalog/ +acceptance pins, source, principal and admitted tenant/realm provenance, separately +from the new reconciliation attempt and its independently revoked parent session. +Use an existing authenticated holder/readback; Audit Core's custody API is a +candidate only until its actual registration source/class, load-bearing emission +contract and separate scoped reader are admitted. No new broker or receipt trust +root is proposed. A missing original record, ambiguous publication, NO_CHANGE, +changed native revision or lost readback stays uncertain; no add replay or undo. + +Completion requires restart/lost-receipt fixtures against the selected actual owner +contract: independently authenticated original created-only provenance, original +exact client entry and unchanged current postcreation UID/resourceVersion before +any undo, with wrong attempt/case/source/principal and unavailable-holder refusals. +The merged T13 adapter exposes reconciliation only through paired injected +holder/observe capabilities and still refuses post-restart rollback. Existing +source review, JSON receipts and synthetic trust do not satisfy independent +holder authorization/provenance or native admission, or authorize attended recovery. +Exact remaining owner facts: Custodian native-registration-r1/original-attempt-review.json. + +2026-10-17 check (net-kingdom-worker): Warden's original-intent lookup interface +is not published — ops-warden head is `d97652a`, with no WARDEN-WP-0044-T03 +commit, and the net-kingdom inbox holds no announcement. No restart/lost-receipt +fixture or readback seam was added this session; the work is deferred behind +T16's decomposition under the same token budget. Remains todo, gated on Warden +publishing the reviewed lookup/context interface and on owner selection of the +authenticated original-attempt holder. + +2026-10-17 current-source correction: the earlier same-day `d97652a` lookup +checkpoint and T31 adapter-next-action note above are historical and superseded +by the published Warden interface/validation and merged `061461e` adapter seam. +The 54 focused Warden checks establish source behavior only. Current admission +leaves tenant/realm empty; no admitted tenant/realm provenance is inferred. +T15 remains wait for actual independently authenticated owner-holder/readback +selection, independent review of the changed adapter, and native restart/lost- +receipt acceptance. Existing task metadata, owner gates and historical reviews +are unchanged. + +## T16 — Select and review the actual machine descriptor composition + +```task +id: NK-WP-0048-T16 +status: todo +depends_on: [NK-WP-0048-T24, NK-WP-0048-T25, NK-WP-0048-T26, NK-WP-0048-T27, NK-WP-0048-T28, NK-WP-0048-T29, NK-WP-0048-T30] +priority: high +state_hub_task_id: "200bf500-5084-5ae7-be26-3033fe1f296e" +``` + +2026-10-17 coordinator review r4 approved the 1.3 candidate at 1920b13 with no +findings (the-custodian `nk48-adapters-1.2-review/coordinator-review-r4.json`). The founder chose +option A of the T16 owner-review packet (`nk48-t16-owner-review/founder-option-decision.md`): +1.3 with execution descriptors for all seven steps. Owner reviews for A are open +in the-custodian `docs/changes/netkingdom-target-2026-10-17/nk48-1.3-owner-review/` (net-kingdom, +railiance-platform, railiance-apps, ops-warden, key-cape). The owner-review packet +supersedes the r1 composition pointer: T16's exact composition is now +`sso-mfa/k8s/keycape/descriptor-composition-1.3.candidate.json`. + +2026-10-17 successor candidate prepared (source only, `proposed`): +`security-case-type-revisions/1.3/oidc-browser-relying-party/` is the 1.2 package plus +an `execution_descriptors` map of the seven composed descriptors, with examples +pinned to `@1.3` and no acknowledgment. ensure-groups, hand-over, +deliver-consumer-secret and the composer accept 1.3 (835680c, merged acda26c). +`test_admit_1_3_type.py` runs Warden's real admission against the committed 1.3 type: +the proposed type is refused; an acknowledged copy is refused because ops-warden +pins parameter fingerprints only for 1.1 and 1.2; and with only a 1.3 pin (equal to +1.2's fingerprint) all seven are admitted. This is a major change under canon +section 6. Next: r4 source review of the changed adapters and the map, then owner +reviews, including the ops-warden 1.3 pin, the mint-secret warden_executes/human +question and the owner-run steps question, then the founder's Decision Memo. + +Owner: NetKingdom for descriptor/type source and exact checker artifact; Warden +independently owns global provenance/admission. T22 records the completed seven-step +source1.2 inventory and regenerated register-client descriptor using the delivered +checker and current sealed-snapshot source; T16 retains exact owner review, +complete descriptor composition and admission. Next action: bind actual native +publisher/authenticator, original/current session verification and revocation, +executable storage/custody and config pins, then obtain the six missing fixed +owner descriptors/adapters for ensure-groups, mint-secret, deliver-secret, +bind-env, deliver-consumer-secret and hand-over. The inventory identifies each +existing owner seam, exact missing contract and created-only rollback requirement; +it is not an executable bundle. T14 and T15 remain independent prerequisites. + +Review the dedicated consumer source1.2 composition only through the existing +explicit version-root selection. Keep canonical1.1/default and its accepted case +unchanged, and the source-only acknowledged1.2 reference case proposed without +acceptance fields. New descriptor bytes require a reviewed exact successor +revision/version under canon section6; they do not inherit historical source +acknowledgment. Changed authentication/materialization/trust needs the prescribed +informed-decision acknowledgment. Do not insert a partial descriptor map or use +--allow-unimplemented preparation as ordinary case/native admission. The registry +helper reports restart_required; separately review quiet-window restart/readiness +and actual consumer verification rather than infer activation from registry CAS. +Exact composition: Custodian native-registration-r1/descriptor-composition.json. +Completion requires separate owner source review of exact machine descriptors, +artifact/source/config pins, normal type/version admission and independent +evidence-verification boundaries. Warden d97652a refreshes producer validator +pins for NetKingdom 015694ee; it does not attest T13's new adapter. Pinning any +machine descriptor into an acknowledged type is a separately reviewed source +change. Platform owns structural/mint/broker/delivery evidence; Apps owns actual +consumer login/group enforcement. Neither their assent nor native acceptance is +supplied by this task. + +2026-10-16 source checkpoint: T19 below extracts the completed per-attempt +executable snapshot implementation and reviewed fixture proof. T16 retains +native executable storage/custody, exact owner evidence/session callbacks, +complete descriptor composition and ordinary type/version admission. The +successor descriptor/source pins are proposed only; the earlier descriptor +history is preserved. T14 group provenance and T15 durable restart/recovery +remain independent open requirements. No native acceptance is counted here. + +2026-10-17 budget checkpoint (net-kingdom-worker): no source change made. The +T13 registration adapter alone is 217 source and 317 test lines plus an +independent review; six further owner descriptors/adapters cannot be completed +within one per-task token budget (soft 8000 / hard 10000), so the task was +stopped below 50% and is decomposed rather than pushed through. Proposed split, +one source-candidate task each with positive/refusal fixtures, canonical1.1 +default unchanged and no --allow-unimplemented admission: (a) ensure-groups, +bound to T14's journal/provenance seam; (b) mint-secret and (c) deliver-secret, +Platform-owned evidence seams; (d) bind-env and (e) deliver-consumer-secret; +(f) hand-over, Apps-owned consumer verification; then (g) descriptor composition +over the reviewed set. Each candidate needs its owner's source review before +pinning; none is authored here as owner-accepted. Status stays todo. + +## T17 — Build and exercise the exact offline config-checker artifact + +```task +id: NK-WP-0048-T17 +status: done +priority: high +state_hub_task_id: "8c3e4baa-a218-5996-af9e-fe1153a46dac" +``` + +Completed 2026-10-16, isolated artifact/source fixture proof only. Built the +existing `key-cape/src/cmd/keycape` command from published clean source revision +`3c71226a1a7b8a2bdb710e9c497e1a1f7f8a3987`, source tree +`f0010b9cab17dbc3a787628a29657d98b900320f`, with cached Go 1.23.0, +CGO disabled, Linux/amd64 v1, trimmed paths, disabled VCS stamping and offline +read-only module selection. The full retained closure pins 108 source files, +9876 toolchain files and ten used module source directories. Three isolated +build locations produced identical 13633409-byte artifacts: +`sha256:be37738fe1c08259fb4f9ca0daf0f9301bfffc237e5bb38435525dfbf11ccff5`. + +Eight disposable checks use the actual executable and maintained helper/kernel, +with complete synthetic configs and a minimal environment: add/created-only +removal, normal pinned producer admission, rollback, lost-readback uncertainty, +stale revisions, substituted checker refusal, invalid startup config and missing +secret-env refusal. No checker stub or producer-pin override remains. The +artifact-bound descriptor proposal is +`sha256:76b1153d188062ece53fcd5e8d35290fdbfd02a69dd79d4b001fc42781afcdc3`. +Evidence: Custodian +`docs/changes/netkingdom-target-2026-10-16/browser-checker-artifact-next/`. +The first two build attempts succeeded but their closure collection failed; +both failures are retained, as is refusal of the older incomplete synthetic +fixture. No production config, credentials or cluster mutation was used. + +This extracts completed build/fixture scope from T16 without claiming KeyCape +owner endorsement, native checker selection/storage custody, acknowledged type +integration, native evidence/session callbacks or attended browser acceptance. +T16 retains exact owner review and composition/admission scope. The attended +registration procedure remains unexercised. + +## T18 — Make checker input and executable selection deterministic + +```task +id: NK-WP-0048-T18 +status: done +priority: high +state_hub_task_id: "9c45da71-fb4c-5f20-9065-90f448f37c64" +``` + +Completed 2026-10-16, isolated source correction and real-artifact fixture proof +only; independent integration/publication remain separately recorded. KeyCape +source review identified non-secret `KEYCAPE_ACCOUNT_PORTAL_URL` and +`KEYCAPE_BROWSER_LOGOUT_URL` overrides in config.Load. The original helper +inherited ambient environment, allowing valid supplied configuration to be +rejected or invalid supplied URL configuration to be admitted. A bare relative +checker filename could also be replaced by PATH lookup after the adapter had +hashed the selected local file. All three counterexamples are retained against +the unchanged predecessor. + +The existing helper now resolves the selected checker file to an absolute path +and passes an explicit empty subprocess environment. Three opt-in real-checker +regressions verify both URL-verdict directions and relative-file selection under +a non-checker PATH decoy. All 39 affected checks pass with the exact supplied +artifact, as do 11 real-artifact helper/kernel scenarios. Source evidence: +Custodian `docs/changes/netkingdom-target-2026-10-16/browser-checker-environment-r2/`. +Historical descriptors remain retained; the helper correction changes successor +source-composition pins and requires their separate review/admission. An absolute +path is not immutable custody and does not eliminate file replacement races; +T16 retains native executable storage/custody and callback/type admission. +No production configuration, credentials or cluster mutation was used. + +Source integration and publication: `916ba4b33e03179274d03b78bd898a4843e8653f`, following independent +Custodian source review in browser-checker-environment-r2/coordinator-review.json. +The ordinary protected projection pass assigned T17/T18 UUIDs with zero failures +and zero automation errors; generated-only brief commit refusal is retained. +The exact prior staged/working brief bytes were restored. Final source/publication/ +projection evidence is retained in Custodian browser-checker-environment-r2/ +canonical-integration.json. These source receipts do not admit native executable +custody, owner callbacks, acknowledged type changes or browser identities. + +## T19 — Bind checker execution to verified immutable attempt bytes + +```task +id: NK-WP-0048-T19 +status: done +priority: high +state_hub_task_id: "f50fdcd1-00be-5c3c-be0c-345aa2126032" +``` + +Completed 2026-10-16, independently reviewed source implementation and synthetic +fixture proof only. The registration adapter now opens a bounded regular checker +artifact without following its terminal symlink, verifies the reviewed digest, +and snapshots the exact bytes into a write/grow/shrink/seal-removal-sealed Linux +memfd before helper access. Live and proposed config checks execute that same fd +through procfs with the existing empty environment; the fd closes on completion +or failure. Unsupported snapshot/seal/exec policy refuses through the existing +parent-session revocation and uncertainty boundaries. The earlier unbounded +pre-snapshot path read was removed during independent review; actual adapter +symlink/FIFO/oversize refusal tests verify helper access never starts. + +Evidence: Custodian `docs/changes/netkingdom-target-2026-10-16/browser-resume-r1/`, +including exact base/preimages/postimages/patch, proposed descriptor, coordinator +source review and canonical integration receipt. Independent Custodian checks +passed 49 affected source tests and 12 actual Go checker/helper/kernel scenarios; +the sets overlap and are not separate acceptance counts. Existing published +Warden d97652a producer pins remain unchanged. The exact KeyCape artifact remains +`sha256:be37738fe1c08259fb4f9ca0daf0f9301bfffc237e5bb38435525dfbf11ccff5`. +The predecessor path-replacement refusal and the first expanded reopened-fd test +failure are retained; the latter fixture was corrected to restore write mode +before independently proving seals block reopened-fd writes and shared mmap. + +This source task adds no native owner custody, callback, evidence trust, type +acknowledgment, credential access, consumer sign-in or rollback acceptance. +Executable memfds, Linux seals and usable procfs are explicit runtime dependencies +and still require native admission. This is an extracted source outcome from +T16, not completion of T16; T12/T14/T15 and all external-owner gates remain open. + + +Publication verified by the coordinator after the source worker usage limit: +`8790dd18ae83c4651ef3d1d923312a118a5b436b` is the actual remote main revision. +All five canonical source hashes match the reviewed packet. Normal synchronization +completed with zero failures/automation errors; all 19 task statuses match the +projection. T19's assigned UUID is retained. The original staged and working brief +bytes were restored and independently compared. The exact source, projection and +publication checks are in Custodian browser-resume-r1/canonical-integration.json. +Native acceptance remains open under T16 and the other named owner tasks. + + +## T20 — Prepare durable group provenance and prove disposable LLDAP compatibility + +```task +id: NK-WP-0048-T20 +status: done +priority: high +state_hub_task_id: "863fcbdb-e246-51ba-87ca-a2f44d01d6f8" +``` + +Completed 2026-10-17, source candidate preparation and bounded synthetic +compatibility proof only. This extracts evidenced implementation/fixture scope +from T14; T14 retains native storage, writer isolation, protected callback and +owner-acceptance gates. Source integration/publication and State Hub projection +are pending the coordinator's separate receipts; no revision or task UUID is +invented here. + +The existing identity-provisioner group ensure/revoke interface now has a +reviewed candidate for a bounded owner-private SQLite journal, proven only in +local/disposable use. The initial storage instructions wrongly suggested a native +persistent-volume path; source review against accepted ADR-0015 corrected them +before publication. Production SQLite-on-a-PVC is not admitted: T14 retains +CNPG/rapp-postgres storage admission or a separately governed exception. +The local journal saves +complete prestate and intent before each attempted directory mutation, inventories +only confirmed new groups/memberships, binds original ensure idempotency and +correlation to the exact request digest, and refuses unsigned caller inventories +or legacy broad revoke requests. Replays never recreate removed state. Missing, +replaced or untrusted storage and missing intent rows stop execution; definite +before-mutation refusals retain evidence without stranding unrelated valid work. +Interrupted writes/readback remain unresolved across reopening. Revoke validates +all targeted native ids and complete member postimages before destructive work, +removes only confirmed creations and never deletes a preexisting empty group. +Server/configuration wiring, image-copy closure and owner storage/request +migration instructions are included. The existing endpoint's caller +authentication remains the existing boundary, not a new grant. + +The candidate was prepared against NetKingdom +`878abc93588e12b6da1bac055cfbb3abb8d7adf1`; exact source patch digest +`sha256:1f3f3ca94c46dadac8f24896053d4d23179de795e50a24298a0cfbcc46cca30e`. +The independent Custodian review passed all 65 provisioner checks. The earlier +62-check candidate, two independent-review findings and subsequent fixes remain +retained rather than overwritten. Seven additional fixture-plan checks exercise +the preparation boundary; these counts overlap the same candidate and do not +measure task-completion percentage or independent acceptance. + +A separately reviewed bounded fixture then exercised the exact candidate against +cached pinned disposable LLDAP and Python images, through actual GraphQL. All 14 +closed compatibility/containment oracles passed: exact creation inventory, +separate-process SQLite reopening/replay/revoke, complete preexisting directory +preservation including empty/shared groups and memberships, refusal of shared +membership drift, retained uncertainty after a lost response following a real +addition, and refusal of unknown/cross-case receipt bindings. The fixture used +only newly generated synthetic users/passwords/key material in private tmpfs, +with a network-none anchor and shared loopback helper, no host ports/mounts, +no image pulls and no production or actual credential contact. The coordinator's +safe receipt reports both exact owned containers absent after cleanup; final +independent cleanup verification remains the coordinator's integration duty. + +Evidence: Custodian +`docs/changes/netkingdom-target-2026-10-16/group-provenance-r1/`, including exact +source base/pre/postimages, patch/manifest, independent review and retained +refinements. Native fixture manifest +`sha256:8a0514f5f14137355972400cd4852f48c892316d28e3abd3c86635d7cb81eef2`; +retained safe result `native-fixture/execution-result.json` (original +`/tmp/nk48-groups-native-review-5pywb19z/result.json`). The source task supplies +no native owner assent, production rollout, protected session/evidence callbacks, +ordinary type admission or consumer login proof. LLDAP has no conditional +membership mutation or generation token here: exact readback cannot exclude +ABA or a concurrent external writer, so T14 remains open. + +2026-10-17 verified source follow-through: source commit `4f65e41` passed normal +local scanning and all 65 canonical provisioner tests. The actual disposable +LLDAP receipt passed all 14 oracles; independent verification on the pinned +daemon found both exact names and the run label absent. Source integration +includes the ADR-0015 documentation correction; the journal remains local-only. + +T14 source preparation is underway. Twenty-three pinned public sources identify +NetKingdom implementation/reconciliation, Platform custody/database governance +and rapp-postgres consumer provisioning; the inspected runtime binding does not +assign identity-provisioner a package. The existing User Engine runtime token +and pod route are not a protected-case caller lane. The native proposal preserves +the same journal seam on a dedicated CNPG consumer and requires authoritative +scope binding, admitted role/lease/placement/recovery and all LDAP writers fenced. +The declared exclusive maintenance window is not mechanical exclusion of WebUI, +helper or lifecycle writers. Capacity placement and EUR cost/risk remain owner +decisions. No package, database, credential, rollout or owner assent was created. +Custodian evidence: group-provenance-r1/native-storage-proposal-r1; final source, +projection and cleanup review: resume-integration-r1/publication-verification.json. + + +## T21 — Implement the PostgreSQL journal and prove disposable SQL compatibility + +```task +id: NK-WP-0048-T21 +status: done +depends_on: [NK-WP-0048-T20] +priority: high +state_hub_task_id: "afe78e4d-408a-516a-ab92-e212e8a2d11d" +``` + +Completed 2026-10-17: bounded source preparation and actual disposable PostgreSQL +compatibility only. T14 retains native storage, custody, callbacks, authorization, +all-writer isolation and production admission. Coordinator source publication and +projection remain separate follow-through; no source revision or task UUID is +invented here. T20 remains the historical local SQLite/actual LLDAP proof. + +The eight-path source candidate adds an injection-only PostgreSQL implementation +behind the existing owner journal seam, with explicit trusted tenant/directory/ +case/source/principal/epoch binding and independent continuity callbacks. It +persists bounded intent/created-only receipts, exact-one version updates, durable +active fences, and a content-bound journal chain. SQL commits precede independent +exact-marker acknowledgment; lost replies/checkpoints poison continuation and +retain uncertainty. A counter-only checkpoint is explicitly insufficient for +divergent clones. Migration does not create database/schema/credential, admit an +epoch or clear quarantine; production HTTP activation is absent pending trusted +callbacks. Pinned psycopg package/image-copy closure is included, without claiming +an image build or native dependency admission. + +Candidate base `6cb43c0cc55212363a5406b02db046e2384a7c40`; final eight-path patch +`sha256:5549d2982b61ff70aba4b3928fecd5639db0f1a732ab2f8630f678e7acf4d34d`. +All 82 source tests pass, alongside 13 fixture plan/diagnostic checks and two +source-backed snapshot regressions. Counts overlap the same source and fixture; +they are not additive native acceptance or completion percentages. + +The independently reviewed R5 fixture ran the actual SQL migration/backend and +existing provisioner path against cached pinned disposable PostgreSQL. Directory +operations deliberately used the existing synthetic LLDAP test double. All +manifest-declared phase oracles passed: creation-only inventory and DDL refusal; +separate-process replay/revoke with complete preexisting/shared visible-state +preservation; foreign-context refusal; concurrent exactly-once reservation and +stale version refusal; actual database fork with divergent bodies at the same +counter and fresh-object rejection of the losing exact checkpoint; retained +uncertainty after checkpoint/directory/SQL response loss; and actual pg_dump/ +pg_restore rollback refusal against the independently retained fixture checkpoint. +The checkpoint is fixture-only, not an admitted native owner evidence holder. + +Only generated credentials/state were used, with two nonroot/read-only containers +on private loopback, bounded tmpfs/CPU/memory/PIDs/lifetimes, no host ports/mounts, +no pulls and no production contact. The safe receipt reports both exact owned +containers absent, with no daemon uncertainty. Independent final pinned-daemon +cleanup verification remains coordinator follow-through. + +Failed R2/R3/R4 receipts and source revisions remain retained. R2's helper failure +subphase is unknown; R3 exposed a real backend bug where replacing nested receipt +objects lost the created ID held by ensure, fixed by acknowledging only the backend +version after commit/checkpoint. R4 verified that fix through actual SQL bootstrap, +then exposed fixture snapshot aliasing and empty deleted-group backing entries; +source-backed regressions corrected the snapshot without weakening complete +visible-prestate equality, and reject nonempty orphan membership. These refinements +belong to one trial, not extra native acceptance successes. + +Custodian evidence: `docs/changes/netkingdom-target-2026-10-16/group-postgres-r1/`; +exact source/pre/postimages and retained predecessors; native R5 manifest +`sha256:d51f90816616848b93bcdc482e10a98d07061deb01eeed9e50700fc6e68b6051`; +`final-integration-r1/typed-execution-evidence.json` and byte-exact safe receipt +(original `/tmp/nk48-postgres-native-review-sguj7t8p/result.json`). No CNPG native +storage/HA/backup/lease/TLS/capacity/restore admission, authoritative protected +callbacks, real LDAP writer fencing/CAS/ABA exclusion, owner assent or production +endpoint activation is claimed. Those remain live T14 work. + + +## T22 — Establish registration recovery source gaps and complete descriptor review inventory + +```task +id: NK-WP-0048-T22 +status: done +priority: high +state_hub_task_id: "ea50d93b-0d04-55d2-98c4-9b6c3fe925b1" +``` + +Completed 2026-10-17: source-only owner-contract review and complete review +inventory. Inspected the actual registration adapter, Warden Journal/Executor, +NetKingdom evidence compatibility checker and Audit Core custody/read surfaces. +No existing admitted holder/readback supplies authenticated original-attempt +recovery: payload/creation are process-local, persisted evidence references are +hashed, and reconcile receives a new attempt without original context. The exact +four-owner contract requirements and non-replayable missing/ambiguous cases are +recorded; no recovery callback, publisher, credential grant or native trust root +was installed. T15 retains implementation and actual acceptance. + +The complete source1.2 inventory maps all seven follow-through steps to their +existing owners, missing exact contracts and created-only rollback requirements. +The actual maintained adapter regenerated the sole existing register-client +machine descriptor against the delivered 13,633,409-byte checker and current +sealed-memfd snapshot source. The checker rehash and Warden Descriptor.decode +round-trip passed; six owner descriptors remain absent. Actual source commands +render canonical1.1 and explicitly selected1.2 with execution.permitted false; +ordinary1.2 case admission still refuses its acknowledged/unimplemented type. +Canonical type/case/acknowledgment bytes were hash-checked unchanged. No tests were +added or rerun, no native configuration or effect occurred, and no owner assent +or complete machine bundle is claimed. T16 retains native executable custody, +complete exact descriptors, independent source review and normal version/type +admission. This extracted outcome supplies no native gate completion credit. + +Evidence in the-custodian: +`docs/changes/netkingdom-target-2026-10-17/native-registration-r1/`, especially +original-attempt-review.json, descriptor-composition.json, source-verification.json, +source-manifest.json and manifest.json. The prior sixteen-gate audit remains +immutable. Publication, normal task UUID allocation and projection are separate +coordinator follow-through; no Hub UUID is assigned by this source record. + + +## T23 — Review native journal package assignment and storage integration boundaries + +```task +id: NK-WP-0048-T23 +status: done +priority: high +state_hub_task_id: "6e7cd483-c7af-53a2-9d0a-3d76b6fb49ee" +``` + +Completed 2026-10-17: source/package placement and protected integration review +only, separate from T21's completed PostgreSQL source and disposable-SQL proof. +The owner review recommends a dedicated independently rolled-back +`rapp-identity-provisioner`, owned by NetKingdom on rail-kubernetes, with a +schema-valid draft declaration and no reef binding. No repository, registry +entry, native package assignment or owner assent was created. Existing +rapp-user-engine ownership/token delivery does not assign it the provisioner. + +The complete current rapp-postgres catalog has six declared consumers: four on +platform-pg and two on platform-pg-2. The inert renderer-valid provisioner +candidate fits platform-pg-2 at three of four, requesting one migration plus +four runtime connections; placing it on the first cell is refused as a fifth. +Normative package schema and full-catalog capacity/render checks passed. The +four-consumer ceiling is estimated, not measured native headroom, and no current +SQL session, backup or restore acceptance is claimed. + +The exact review identifies remaining package/substrate, SQL role/lease/TLS, +network and backup/recovery contracts; independently authenticated protected +scope callbacks; an external content-bound checkpoint holder with atomic +advance/loss handling; and all-LDAP-writer exclusion or provider CAS/generation, +including lifecycle cascades and remove/readd ABA. Generic Audit custody is not +that checkpoint service. A database-only fence, final reread or one replica +cannot exclude foreign directory writers. The native acceptance/recovery matrix +keeps storage/lease admission separate from separately approved directory +mutation; the production activation remains disabled pending trusted integration. + +Evidence in the-custodian: +`docs/changes/netkingdom-target-2026-10-17/native-storage-r1/OWNER-REVIEW.md`, +source-validation.json and hash-bound source-references.json, plus the inert +proposed declaration/consumer and renderer outputs. No native resource, lease, +credential, deployment, destructive effect, independent holder or native owner +acceptance was delivered. T14 retains these live gates with its original UUID +and progress status. This completed review adds no duplicate T21 acceptance +credit; publication, task UUID allocation and projection remain coordinator +follow-through and no UUID is hand-assigned here. + +## T24 — Ensure-groups adapter source candidate + +```task +id: NK-WP-0048-T24 +status: done +priority: high +state_hub_task_id: "1a3931ee-3b5f-52fc-9295-4073804f4bd2" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): ensure-groups-adapter.py, 8 tests (commit 5a2e7d6). Binds to the T14 PostgreSQL journal and refuses SQLite or a foreign binding. It accepts only canonical 1.1 cases, so T30 must add an explicit 1.2 path. It calls the provisioner's private `_group_binding`; a public helper is the cleaner shared edit. + +Split from T16 on 2026-10-17 so each candidate fits one per-task budget. Bind group creation to T14's journal/provenance seam; refuse without trusted journal callbacks. +Source candidate only, modelled on the T13 registration adapter +(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and +`registration-adapter.md`). Requires positive and refusal fixtures and +created-only rollback. Keeps canonical 1.1 as the default and uses no +`--allow-unimplemented` admission. Done means the candidate and its tests are +committed. NetKingdom (directory) owner source review before pinning, and any native action, +stay separate gates and are not supplied by this task. + +## T25 — Mint-secret adapter source candidate + +```task +id: NK-WP-0048-T25 +status: done +priority: high +state_hub_task_id: "0de2a7d9-874d-578b-a076-f653fe42e30b" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): mint-secret-adapter.py, 9 tests (commit 558c5bc). Value-free receipts only. The receipt schema `railiance-platform.custody-mint.v1` and the mint/destroy capability interface are proposals that Platform must confirm. + +Split from T16 on 2026-10-17 so each candidate fits one per-task budget. Platform-owned custody evidence seam: the adapter records value-free mint evidence only and never handles secret values. +Source candidate only, modelled on the T13 registration adapter +(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and +`registration-adapter.md`). Requires positive and refusal fixtures and +created-only rollback. Keeps canonical 1.1 as the default and uses no +`--allow-unimplemented` admission. Done means the candidate and its tests are +committed. Platform owner source review before pinning, and any native action, +stay separate gates and are not supplied by this task. + +## T26 — Deliver-secret adapter source candidate + +```task +id: NK-WP-0048-T26 +status: done +priority: high +state_hub_task_id: "94b3cbf0-c908-5ac6-b0db-67385c3e021c" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): deliver-secret-adapter.py, 8 tests (commit 1b60f6e). The lane `platform-secret-delivery` and the schema `netkingdom.deliver-secret.v1` are proposals that Platform must confirm. + +Split from T16 on 2026-10-17 so each candidate fits one per-task budget. Platform-owned delivery evidence seam (ESO/custody), value-free. +Source candidate only, modelled on the T13 registration adapter +(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and +`registration-adapter.md`). Requires positive and refusal fixtures and +created-only rollback. Keeps canonical 1.1 as the default and uses no +`--allow-unimplemented` admission. Done means the candidate and its tests are +committed. Platform owner source review before pinning, and any native action, +stay separate gates and are not supplied by this task. + +## T27 — Bind-env adapter source candidate + +```task +id: NK-WP-0048-T27 +status: done +priority: high +state_hub_task_id: "e56365f8-29b5-5c89-a329-8115f9f3b2a7" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): bind-env-adapter.py, 5 tests (commit 6fbce73). Binds references only. To fit the budget it does not yet wrap a full Warden `Adapter` like T13 (source-digest pin, authenticated evidence, parent-session revocation); it returns `(reference, run, rollback)`. That wrapping is a prerequisite for T30. + +Split from T16 on 2026-10-17 so each candidate fits one per-task budget. Bind the issuer Deployment env reference to the delivered secret reference, without values. +Source candidate only, modelled on the T13 registration adapter +(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and +`registration-adapter.md`). Requires positive and refusal fixtures and +created-only rollback. Keeps canonical 1.1 as the default and uses no +`--allow-unimplemented` admission. Done means the candidate and its tests are +committed. NetKingdom runtime owner source review before pinning, and any native action, +stay separate gates and are not supplied by this task. + +## T28 — Deliver-consumer-secret adapter source candidate + +```task +id: NK-WP-0048-T28 +status: done +priority: high +state_hub_task_id: "6dab281c-ea9a-5a17-a83f-e8e9a4d4484f" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): deliver-consumer-secret-adapter.py, 5 tests (commit 650c1ac). Type 1.2 only. It assumes the owner has validated ESO TokenRequest and the controller-RBAC admission; that stays an owner gate. + +Split from T16 on 2026-10-17 so each candidate fits one per-task budget. Consumer-side delivery evidence seam, value-free. +Source candidate only, modelled on the T13 registration adapter +(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and +`registration-adapter.md`). Requires positive and refusal fixtures and +created-only rollback. Keeps canonical 1.1 as the default and uses no +`--allow-unimplemented` admission. Done means the candidate and its tests are +committed. Platform and Apps owner source review before pinning, and any native action, +stay separate gates and are not supplied by this task. + +## T29 — Hand-over adapter source candidate + +```task +id: NK-WP-0048-T29 +status: done +priority: high +state_hub_task_id: "129db41d-4560-5e20-9f3b-c3c91e0a5601" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): handover-adapter.py, 8 tests (commit ec63539). The owner `apps` and lane `apps-consumer-handover` are proposals that the Apps owner must confirm. It has no reconcile path; restart handling follows T15. + +Split from T16 on 2026-10-17 so each candidate fits one per-task budget. Apps-owned consumer verification and hand-over receipt seam. +Source candidate only, modelled on the T13 registration adapter +(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and +`registration-adapter.md`). Requires positive and refusal fixtures and +created-only rollback. Keeps canonical 1.1 as the default and uses no +`--allow-unimplemented` admission. Done means the candidate and its tests are +committed. Apps owner source review before pinning, and any native action, +stay separate gates and are not supplied by this task. + +## T30 — Compose the descriptor set over the reviewed candidates + +```task +id: NK-WP-0048-T30 +status: done +depends_on: [NK-WP-0048-T24, NK-WP-0048-T25, NK-WP-0048-T26, NK-WP-0048-T27, NK-WP-0048-T28, NK-WP-0048-T29] +priority: high +state_hub_task_id: "6f6073ac-52dd-5b70-8874-6d76d4c072e4" +``` + +2026-10-17 done: coordinator review r3 (the-custodian `nk48-adapters-1.2-review/coordinator-review-r3.json`, +fd07eaa) approved all nine files at ff8f597, with R2-F1..F3 resolved, an independent +reconstruction of 117 passing and no open findings. This is a source review only: +it grants no owner admission, pin or native authority. Owner review and admission +continue under T16, which also takes the r2 observations, including the +`warden_executes: false` steps that carry execution descriptors. + +2026-10-17 coordinator review r2 (the-custodian `nk48-adapters-1.2-review/coordinator-review-r2.json`) +approved five files and rejected three, plus the artifact. The fixes are merged in +2ee4ed0: R2-F1, mint-secret revokes the session and the broker grant +independently (eeed8e1); R2-F2, deliver-secret binds to the admitted case and +derives its Secret and custody path, with no caller reference (640292c); R2-F3, +bind-env pins its five-file closure, and a new closure-completeness test checks +every adapter (d16db30). Each regression test failed on 68f4bbb. The artifact is +regenerated, and the full suite including kernel admission passed 117. Next is +the r3 review. Open for owner review under T16: the type marks four steps +`warden_executes: false`, yet each has an execution descriptor. + +2026-10-17 founder confirmed every proposed lane and schema name for T25-T29 +as-is. The artifact records them as `names_confirmed` (by founder, 2026-10-17), +and no step is still `owner_confirmation_required`. Next: independent source +review of the seven adapters. + +2026-10-17 founder decision (supersedes the `agent` decision below): the +mint-secret actor is `human`. Coordinator review r1 (the-custodian +`docs/changes/netkingdom-target-2026-10-17/nk48-adapters-1.2-review/coordinator-review-r1.json`, +finding NK48-R1-F1) showed that unchanged kernel a45cf47 refuses every +`effect: mint` admission whose actor is not `human` ("agent mint is +forbidden"), so the `agent` descriptor could never be admitted. Adapter, tests +and composer comment updated and the artifact regenerated (mint-secret +descriptor digest now `sha256:8c40c2884f8ba15e8cb6c98f33419c395e54fce8ef33d6e1a34782cf3f1fcfa9`); +98 tests pass against a45cf47. The set needs an independent r2 review. + +2026-10-17 `test_admit_1_2_adapter_descriptors.py` runs Warden's own +`Executor.admit` on all seven composed 1.2 descriptors, with the real 1.2 +type, the 1.2 forgejo example case, the reviewed `BrowserInputs` validator and +fixture-only trust (stand-in adapters, nothing dispatched). It also pins that +an agent mint descriptor is refused ("agent mint is forbidden") and that a +wrong lane is refused for every step. Against the old `agent` revision +(582ab9f) it fails as expected. Combined suite: 102 passed on ops-warden a45cf47 +and on current ops-warden. Fixture admission is not owner admission or a pin. + +2026-10-17 founder decision: the mint-secret actor is `agent`, not `warden`. +The kernel is unchanged. The adapter, its test and the composer were updated and +the artifact regenerated; all seven steps pass `Descriptor.decode` and +`complete_executable_descriptor_set` is true (98 tests passed). The artifact +stays `not_an_admission`, with no owner review, admission or dispatch permission. + +2026-10-17 composer merged (60b41a1 via 3c62bec) with follow-up 376b44b (merged; +combined suite 98 passed). `compose-1.2-descriptors.py` loads 1.2 only through an +explicit version root and refuses partial, duplicate, unknown or mismatched maps. +`descriptor-composition-1.2.candidate.json` is a reviewable candidate, marked +`not_an_admission`, with canonical 1.1 unchanged. ensure-groups and hand-over now +carry and honour `parent-session` revocation, and the hand-over owner is +parameterised (`railiance-apps` for Forgejo). Six of the seven steps pass +`Descriptor.decode`. mint-secret is still refused: its actor `warden` is outside +the kernel's human/agent set, and Platform (with ops-warden, if the kernel should +change) must decide. Five steps carry `owner_confirmation_required` names. The +set is complete only after both. Owner review, the canon section 6 successor +revision and admission remain T16's. + +2026-10-17 prerequisites merged (65559ef; combined suite 78 passed): +ensure-groups accepts 1.2 via an explicit `version_root='1.2'` (531151b), with +1.1 still the default. bind-env is now a full Warden v1 `Adapter` (7442d21); its +kernel-level fixture tests like T13's are still missing, and its `build()` +signature changed. Composition is unblocked in source. Owner confirmation of +the proposed step, lane and schema names remains before pinning. + +2026-10-17 inputs from T24–T29 that composition must resolve: T24 accepts +only 1.1 while T28 accepts only 1.2, so add an explicit 1.2 ensure-groups path; +T27 still needs full Warden `Adapter` wrapping; the lane, schema and owner names +proposed in T25, T26 and T29 need owner confirmation before pinning. + +Split from T16 on 2026-10-17. Compose the seven-step source 1.2 descriptor set +through the existing explicit version-root selection over T13 and T24–T29. No +partial descriptor map, canonical 1.1 stays the default, and new descriptor bytes +need a reviewed successor revision under canon section 6. Owner review and +admission remain T16's. + +## T31 — Restart and lost-receipt fixtures against Warden's original-intent interface + +```task +id: NK-WP-0048-T31 +status: done +priority: high +state_hub_task_id: "ed1ccd87-89a0-5e69-885c-0eaf5ab0bb5a" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): test_registration_original_intent_reconcile.py, 8 tests (commit 74adcd7), runs against the unmodified ops-warden a45cf47. The holder is a test-only in-memory store. For real T15 support the T13 adapter's `build()` needs an injected holder/readback capability, a `reconcile_original` callback, and a `verify` that accepts reconcile results. Rollback after restart stays refused until there is an owner contract. + +Split from T15 on 2026-10-17. ops-warden published the original-intent +lookup/context interface in `src/warden/case_run.py` (`OriginalIntent`, +`Journal.original_intent`, `Adapter.reconcile_original`; commit `a45cf47`). +Build T15's restart/lost-receipt fixtures for the registration adapter against +it: the original intent is kept separate from the reconcile attempt; wrong +attempt/case/source/principal and unavailable-holder cases refuse; and a missing +or ambiguous record stays uncertain, with no add replay or undo. The interface +is Warden's own dispatch record, not owner-authenticated created-only provenance. +Selecting the actual authenticated holder stays a T15 owner gate. diff --git a/docs/changes/netkingdom-target-2026-10-17/source-corrections-publication-r1/netkingdom-progress.json b/docs/changes/netkingdom-target-2026-10-17/source-corrections-publication-r1/netkingdom-progress.json new file mode 100644 index 0000000..65b0ba1 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/source-corrections-publication-r1/netkingdom-progress.json @@ -0,0 +1,14 @@ +{ + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "id": "be95b020-5ce9-423b-a859-47b36c6d6247", + "topic_id": null, + "task_id": "200bf500-5084-5ae7-be26-3033fe1f296e", + "decision_id": null, + "event_type": "note", + "summary": "Published net-kingdom342a6f25 and befb0bc68cd25b625804fce559476eefd702521d; normal allocator source/primary readback verifies35NK48 tasks, all31 original UUID/status bindings unchanged. Added T32done independent agent-mint guard regression, T33done exact proposed issuer-template reference only, T34progress real Apps inverse backend missing, T35done byte-bound valid synthetic catalog fixtures. Canonical guarded current-kernel tests31passed/53subtests; unresolved Apps sessions correctly refuse. Parameters, seven descriptors, canonical1.1/default and1.2 unchanged;1.3 remains proposed, regenerated artifact byte-identical. Corrected inverse prototype requires fresh authenticated inverse session, but actual durable journal/all-release-writer exclusion/session backend absent under existing AppsT05; no owner assent or native acceptance. Existing T16/T06 and all16target native gates remain open. Evidence: dated nk48-current-source-publication-r3/verified-task-bindings.json and nk48-r5-f3-implementation-r2.", + "detail": null, + "author": "codex", + "session_id": null, + "created_at": "2026-10-17T20:02:37.226055Z", + "workstream_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" +} diff --git a/docs/changes/netkingdom-target-2026-10-17/source-corrections-publication-r1/owner-source-state.json b/docs/changes/netkingdom-target-2026-10-17/source-corrections-publication-r1/owner-source-state.json new file mode 100644 index 0000000..143e0dd --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/source-corrections-publication-r1/owner-source-state.json @@ -0,0 +1,29 @@ +{ + "owners": [ + { + "owner": "ops-warden", + "published_commit": "b593c879831378db7dc62adfabdfedf2d44ddd38" + }, + { + "owner": "net-kingdom", + "published_commit": "befb0bc68cd25b625804fce559476eefd702521d" + }, + { + "owner": "railiance-platform", + "published_commit": "104ed90f42a9a86bf9fda343b49b65a85c9ee4b9" + } + ], + "native_target_gates_closed": 0, + "all16_native_gates_remain_open": true, + "NK48_task_count": 35, + "NK48_new_tasks": { + "T32": "done: independent mint guard regression", + "T33": "done: exact proposed issuer-source reference", + "T34": "progress: real Apps inverse backend absent", + "T35": "done: authenticated catalog fixtures" + }, + "safe_ordinary_unit_result": "785 passed,4 deselected", + "native_or_credential_actions": false, + "PARAMETER_PINS_or_draft_promotions": false, + "failed_and_unknown_outcomes_retained": true +} diff --git a/docs/changes/netkingdom-target-2026-10-17/source-corrections-publication-r1/platform-progress.json b/docs/changes/netkingdom-target-2026-10-17/source-corrections-publication-r1/platform-progress.json new file mode 100644 index 0000000..79b0ddc --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/source-corrections-publication-r1/platform-progress.json @@ -0,0 +1,14 @@ +{ + "workplan_id": null, + "id": "baafd842-a280-4bc1-8857-a1da0f4e9b62", + "topic_id": null, + "task_id": null, + "decision_id": null, + "event_type": "note", + "summary": "Platform source-only coordinator completion: published104ed90f42a9a86bf9fda343b49b65a85c9ee4b9 with proposed fixed issuer ClusterSecretStore openbao-keycape-{client_id}-issuer restricted to sso, exact ESO template, pure metadata helper and8passing tests. Independent source review accepts exact source fit, not formal owner assent, installed ESO compatibility, grant/custody or native authority. NK proposed1.3 now pins exact template digest; no parameters or descriptors changed. Actual RPF-1.3-T4 deliver/remove/authenticated provenance/session capabilities remain unimplemented; that label exists only in owner-review JSON, so no fabricated Platform task/workplan is used for this progress event. No live or credential action; source and unrelated staged brief/WORK edits preserved. Evidence: dated nk48-r5-f2-publication-r1 and nk48-issuer-reference-integration-r1.", + "detail": null, + "author": "codex", + "session_id": null, + "created_at": "2026-10-17T20:02:37.369532Z", + "workstream_id": null +} diff --git a/docs/changes/netkingdom-target-2026-10-17/source-corrections-publication-r1/prepare.py b/docs/changes/netkingdom-target-2026-10-17/source-corrections-publication-r1/prepare.py new file mode 100644 index 0000000..1390411 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/source-corrections-publication-r1/prepare.py @@ -0,0 +1,36 @@ +from pathlib import Path +import hashlib,json,subprocess +r=Path('/home/worsch/the-custodian');p=Path(__file__).resolve().parent;base=p.parent +def git(root,*a):return subprocess.check_output(['git','-C',str(root),*a],text=True).strip() +expected={'ops-warden':'b593c879831378db7dc62adfabdfedf2d44ddd38','net-kingdom':'befb0bc68cd25b625804fce559476eefd702521d','railiance-platform':'104ed90f42a9a86bf9fda343b49b65a85c9ee4b9'} +owner_state=[] +for owner,want in expected.items(): + owner_root=r.parent/owner;assert git(owner_root,'rev-parse','HEAD')==want + assert git(owner_root,'ls-remote','origin','refs/heads/main').split()[0]==want + owner_state.append({'owner':owner,'published_commit':want}) +receipt=json.loads((base/'warden-safe-unit-integration-r1/projection.json').read_text()) +assert receipt['ok'] and receipt['status']=='applied' and receipt['commit']==expected['ops-warden'] +assert receipt['receipt']['instance_role']=='primary' and receipt['receipt']['instance_label']=='railiance01' +assert receipt['receipt']['outcome']['counts']['retired']==0 +names=['nk48-r5-f1-implementation-r1','nk48-r5-f2-implementation-r1','nk48-r5-f2-review-r1','nk48-r5-f2-publication-r1','nk48-r5-f3-implementation-r1','nk48-r5-f3-review-r1','nk48-r5-f3-implementation-r2','warden-1.3-kernel-fixes-r1','warden-1.3-kernel-review-r2','warden-kernel-source-integration-r1','nk48-r5-source-followthrough-r1','nk48-catalog-fixture-update-r1','nk48-catalog-fixture-publication-r1','nk48-catalog-fixture-publication-r2','nk48-issuer-reference-integration-r1','nk48-current-source-publication-r3','key39-infra-finalization-r1','warden-safe-unit-runner-r1','warden-safe-unit-runner-review-r1','warden-safe-unit-integration-r1'] +owned_docs=['docs/netkingdom-target-security-architecture.md','docs/netkingdom-target-implementation.md','workplans/CUST-WP-0080-netkingdom-security-blueprint-and-gap-closure.md'] +state={'owners':owner_state,'native_target_gates_closed':0,'all16_native_gates_remain_open':True,'NK48_task_count':35,'NK48_new_tasks':{'T32':'done: independent mint guard regression','T33':'done: exact proposed issuer-source reference','T34':'progress: real Apps inverse backend absent','T35':'done: authenticated catalog fixtures'},'safe_ordinary_unit_result':'785 passed,4 deselected','native_or_credential_actions':False,'PARAMETER_PINS_or_draft_promotions':False,'failed_and_unknown_outcomes_retained':True} +(p/'owner-source-state.json').write_text(json.dumps(state,indent=2)+'\n') +paths=[str((base/n).relative_to(r)) for n in names]+owned_docs+[str((p/'owner-source-state.json').relative_to(r)),str((p/'prepare.py').relative_to(r))] +new=git(r,'ls-files','--others','--exclude-standard','--',*paths).splitlines() +changed=git(r,'diff','--name-only','HEAD','--',*paths).splitlines() +files=[] +for name in sorted(set(new+changed)): + f=r/name + if any(x in {'publication-backup','consistency-backup','backup','test-home','synthetic-parent-home'} for x in f.parts) or name.endswith('.backup') or f.name.startswith('WORK-RECORDS.md'):continue + assert not f.is_symlink() + files.append({'path':name,'sha256':hashlib.sha256(f.read_bytes()).hexdigest()}) +assert len(files)>200 +protected_names=git(r,'diff','--name-only','HEAD').splitlines();protected_names=[n for n in protected_names if n not in owned_docs] +assert len(protected_names)==8,'Unexpected unrelated source changes; inspect before proceeding' +protected=[{'path':n,'sha256':hashlib.sha256((r/n).read_bytes()).hexdigest(),'index':git(r,'ls-files','--stage','--',n)} for n in protected_names] +(p/'protected-state.json').write_text(json.dumps(protected,indent=2)+'\n') +head=git(r,'rev-parse','HEAD');assert git(r,'ls-remote','origin','refs/heads/main').split()[0]==head +cfg={'owner':'the-custodian','base_revision':head,'message':'Record reviewed case admission fixes, exact issuer source and isolated unit checks','files':files} +(p/'publication-inputs.json').write_text(json.dumps(cfg,indent=2)+'\n') +print(json.dumps({'base':head,'files':len(files),'bytes':sum((r/x['path']).stat().st_size for x in files),'protected_unrelated_paths':len(protected),'source_only':True})) diff --git a/docs/changes/netkingdom-target-2026-10-17/source-corrections-publication-r1/publication-commit.json b/docs/changes/netkingdom-target-2026-10-17/source-corrections-publication-r1/publication-commit.json new file mode 100644 index 0000000..832b54d --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/source-corrections-publication-r1/publication-commit.json @@ -0,0 +1,937 @@ +{ + "exit_code": 1, + "commit": "d68d131e32c523fcf989fa0e7ad44b60b33c6067", + "base": "d68d131e32c523fcf989fa0e7ad44b60b33c6067", + "files": [ + { + "path": "docs/changes/netkingdom-target-2026-10-17/key39-infra-finalization-r1/finalization-recheck-progress.json", + "sha256": "a8a2142632f0bf5adc0148542c71b68a9a95e6b98730f5f3b825d6ca945f2d69" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/key39-infra-finalization-r1/finalization-recheck.json", + "sha256": "de18dc982213742c86d9b1e21af778a587275d84da128f76d8768204d3f6fee4" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r1/apply_prepare.py", + "sha256": "b294e6339e8c9af9ba911667646dd0ab52e75cf798de93238d84d1057c8a9e70" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r1/candidate.patch", + "sha256": "df69fb2ec9f1e1cf0dd94194e3c046c995ed8a7deabf65a8e9bec34662adb501" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r1/preparation.json", + "sha256": "c6fc0d7393fc40607ee8157ad2c1f92c73549fb8b52abedc517aac27c9137370" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r1/publication-inputs.json", + "sha256": "d9bf5555013aa9e399037672c80a72af23c6815db03088bee3db4c8583197146" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r1/run_validation.py", + "sha256": "16236aa55a774f8ea6d66da4ee17f7737c6d1b013d844ca9b9759e02b615af51" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r1/task-T35.md", + "sha256": "7d14d261ffbab2a930c4a3ff962cec786060cad6177280773cf1c0b0f006127f" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r1/validation-child.py", + "sha256": "6bd4e3cda22e65f90bbb4006b403270405947293be2ae8bf8fdade0abed61f45" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r1/validation-guard.py", + "sha256": "13611f4de25dc7d01348905c4b365a0ffbb95fd93a61c75c8faf38d705c54f50" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r1/workplan.candidate.md", + "sha256": "3bce122c5b83d9044b142fc2cabc48de5558ab0d65dc0223a8ed0325f219e981" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r1/workplan.preimage.md", + "sha256": "f92962c5aa21aade0fc2f6b4f8d9a95932fe85b107413cdd2824b6ebe72c36d9" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/apply_prepare.py", + "sha256": "b294e6339e8c9af9ba911667646dd0ab52e75cf798de93238d84d1057c8a9e70" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/candidate.patch", + "sha256": "df69fb2ec9f1e1cf0dd94194e3c046c995ed8a7deabf65a8e9bec34662adb501" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/canonical-validation.json", + "sha256": "12748c25e4177a7e8e38edce9e53ae683c6f4b4369314a5db0d7e2cc1659fc29" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/identifier-assignment.json", + "sha256": "1f78bf76592aea84c50dbb3b7181d703b41abdb60937833db30632395cf06673" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/identifier-preview.json", + "sha256": "5f28c6fa85de2e9bafbbd5f567c8c755aabfb3fdf4224cac05c1762910ccd512" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/preparation.json", + "sha256": "c6fc0d7393fc40607ee8157ad2c1f92c73549fb8b52abedc517aac27c9137370" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/publication-inputs.allocated.json", + "sha256": "33dac5218d7b54756b05fea6e1ea885dfc45634896fa3a45b543f9a46dc7afe9" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/publication-inputs.json", + "sha256": "0820bbf9c40eda750e2fc374b43745a0ca82c076c9467f1f3e5c26350dd8db9f" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/revision-refresh.json", + "sha256": "a5b341694aaeb84ef8cc7b76eeff3ba7a97f7cc24eb1e2158d3e85ed901c5667" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/run_validation.py", + "sha256": "16236aa55a774f8ea6d66da4ee17f7737c6d1b013d844ca9b9759e02b615af51" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/task-T35.md", + "sha256": "7d14d261ffbab2a930c4a3ff962cec786060cad6177280773cf1c0b0f006127f" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/validation-child.py", + "sha256": "6bd4e3cda22e65f90bbb4006b403270405947293be2ae8bf8fdade0abed61f45" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/validation-guard.py", + "sha256": "13611f4de25dc7d01348905c4b365a0ffbb95fd93a61c75c8faf38d705c54f50" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/workplan.allocated.md", + "sha256": "53f9e5826bd7e34287d59f46089ee50c31598ba1a56bcf6d3249525f7e23bb9b" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/workplan.candidate.md", + "sha256": "3bce122c5b83d9044b142fc2cabc48de5558ab0d65dc0223a8ed0325f219e981" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-publication-r2/workplan.preimage.md", + "sha256": "f92962c5aa21aade0fc2f6b4f8d9a95932fe85b107413cdd2824b6ebe72c36d9" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-update-r1/candidate.patch", + "sha256": "df69fb2ec9f1e1cf0dd94194e3c046c995ed8a7deabf65a8e9bec34662adb501" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-update-r1/edit.py", + "sha256": "da1fef62073b92b59307e9f52115fa4fa784b260b5202db9d28fc62938982040" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-update-r1/implementation-r1.json", + "sha256": "be1e23dc29be795fb9cdd96172d288a83338c2c0f0cef45d6f29f974d83857df" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-update-r1/run.py", + "sha256": "8ad707bd9bb509e4b27503c284cee8aff3a7205882ca87febdace0a050c5f749" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-catalog-fixture-update-r1/test_runner.py", + "sha256": "d6f4e8f0f5c34557bf678543ae2c69202dc321f1226c2e1a2d462b2d81efc9c5" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-current-source-publication-r3/canonical-validation.json", + "sha256": "0e3cf1ad47f8aa486e8a6704563dfb548a59b7c19f1c7c639e0599a1cc026cb9" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-current-source-publication-r3/consistency.json", + "sha256": "b76e4abfa87880dedb40f3bc87caed622237678c8745a26d34f4a70cb06ed242" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-current-source-publication-r3/consistency.stderr.txt", + "sha256": "f4a6aa1609a13600a0b97cbb08bbc03b3e247b619ff37912d37f7e109ce798ba" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-current-source-publication-r3/projection.json", + "sha256": "454680e758b154d8439cd9d458e9fb8b29dbff4fc501eaf2ea9b6b3c5147b057" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-current-source-publication-r3/projection.stderr.txt", + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-current-source-publication-r3/publication-commit.json", + "sha256": "0e51461bb6934d3bf68d9c49023b0f2d7f10f451137cfe6e36a9c1000aa0972a" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-current-source-publication-r3/publication-inputs.final.json", + "sha256": "8885f9a3561d5a48dc1cde11a86b374d65d5d81f32b8ef684e73a22bd0200c1c" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-current-source-publication-r3/publication-inputs.json", + "sha256": "a0dcaecd1af428c4bfe6176d77d4595f260f351b4ddb61766141f6e8ba5db62c" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-current-source-publication-r3/run_validation.py", + "sha256": "16236aa55a774f8ea6d66da4ee17f7737c6d1b013d844ca9b9759e02b615af51" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-current-source-publication-r3/validation-child.py", + "sha256": "6bd4e3cda22e65f90bbb4006b403270405947293be2ae8bf8fdade0abed61f45" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-current-source-publication-r3/validation-guard.py", + "sha256": "13611f4de25dc7d01348905c4b365a0ffbb95fd93a61c75c8faf38d705c54f50" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-current-source-publication-r3/validation-inputs-refresh.json", + "sha256": "7c359c9c05b1d5737166b59686eb765e4ba2e34497290a921704e0becdceb4a3" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-current-source-publication-r3/verified-task-bindings.json", + "sha256": "610ef9e256a8be8ada2af16d71675af53aecd9340436140936f93b1432fd1c43" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-issuer-reference-integration-r1/apply.py", + "sha256": "98285ff57389e37fa1f5d8d0f1b3fcbea9c7fad7927e6ee29fb890c4696e3877" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-issuer-reference-integration-r1/composition-regenerated.json", + "sha256": "773daa1df72cc7cf6a6d8aa082208181ca2438cdecc9179b1bc5b1c3a09099c2" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-issuer-reference-integration-r1/initial-syntax-failure.py.txt", + "sha256": "2f9d6e97df945195225573e976b4d809c784d51a59bc1611db8ad0b9fff92fd3" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-issuer-reference-integration-r1/type.preimage.yaml", + "sha256": "e75fe94110ef9fb7fb18072fe2856941b33507c99cb406d36576046481485d0a" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-issuer-reference-integration-r1/verification.json", + "sha256": "9092dc8d772155b1a68fb23095b45233727d718eb8e255dd6894a518bd50f01c" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-issuer-reference-integration-r1/workplan.preimage.md", + "sha256": "53f9e5826bd7e34287d59f46089ee50c31598ba1a56bcf6d3249525f7e23bb9b" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/README.md", + "sha256": "78a4dbef0088f18db2b9c1dbe701b3e8a2c20516ae6d2039420a82b8d6b1fe66" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/canonical-application.json", + "sha256": "f3e08764dac6d407beb33b22afe9c154efd761c4f12f6b3d4941fc6b9076a233" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/canonical-net-kingdom-status-after.txt", + "sha256": "354b3dd65c711df0855c22ba053a4ca295d5491941c5a64c1157a0b7d43cf4cb" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/canonical-validation.json", + "sha256": "232416ea6fc095e868ec0f2af34a6b9c44885561b98cba2576ff72a49fc71bb4" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/implementation.json", + "sha256": "33d51784fe3ab7d9307292a265b0e684a8bc2fc77564ba58099833e41c849306" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/manifest.json", + "sha256": "9e137321abde7c2997f30a4ae7978883879c194a4c70c80ebf64d1972ecd66d5" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/preimage-frozen.txt", + "sha256": "63dd3d754896c0e8578d82f98ae6f8e460a6ac43f3937a95fa61f65f8aa4accf" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/preimage-mutant.txt", + "sha256": "6c823ed279c355a34169d7753817256890d56fe4a1996ea59aa01fb19e5a5e1f" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/reproduce-scratch-checks.py", + "sha256": "8f0b01e606c03b3b27b178df60eab664a758df6fa3f9b1bc62ef8b58122c2cd8" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/scratch-kernel-mutation.patch", + "sha256": "dd12c63bd331b8f127149c402077e49fc66d62a452f48044486f58e79e0e29f7" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/scratch.json", + "sha256": "7e329154c5c60528d9f4387f0bbc9cfb4167c79decd49b14497e67d59e21e469" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/successor-frozen.txt", + "sha256": "275324dcf33eb852eb467832ff18fe9dd09f09d64d6ac37b43bf01766ea745d3" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/successor-mutant.txt", + "sha256": "9f03083de5863b3855e533b94671cfbab6fd0dd565e0a0b8c385d51d9510d97c" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/test-admit-1-3-agent-mint-coverage.patch", + "sha256": "5bc1da176836eea65195cfe5fae965284c1f38809a502a0be58c231b60111a75" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/test-results.json", + "sha256": "2aab986ed96fb9fa683992051ffe6ec7b2a9327d816542b908dcc66f9dd8df23" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/test_admit_1_3_type.after.py", + "sha256": "c512ba00513d066fd1ca8af08fd6bf106fbd7a132981b1dcc5af80a745ab03f0" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f1-implementation-r1/test_admit_1_3_type.before.py", + "sha256": "f4f6a35cfbada462c939fe357e51fa5d03efc9d20c17a511f224c1359ca0faa5" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/OWNER-REVIEW.md", + "sha256": "7983fd841f09bc7584047426912c33d568c97b4c1126776f99aac42b34d2b742" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/canonical-source-readonly-state.json", + "sha256": "129d6926bb996e510ab4b41d30e738096b53b71de1fb2dd470fe0ac94848cbec" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/focused-tests.json", + "sha256": "8de5862a8d752dce0b3b96a9275e42952a99415a18d3f67c06bf9f5b6008d0e3" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/focused-tests.txt", + "sha256": "aab46c51d87198ad4be6dd3078f61286e2faf1ef4d76fa14298698388f78ab24" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/manifest.json", + "sha256": "754531fbef468bc0e432bd26d4dbed992294c34d57e23fe7c3b7f829f7a24ba4" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/netkingdom-entry-point.conditional.patch", + "sha256": "6784a21fbd10bd62712be2e0f4929034601c94629aa74ccaa2e93fc815eecd61" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/platform-candidate/docs/keycape-issuer-eso-contract.md", + "sha256": "7f59e59b17c58252982931ac61fe50dd7a07340add6c437fe48af36971eafe68" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/platform-candidate/manifests/templates/keycape-issuer-eso.yaml", + "sha256": "e17498bd8a4c820041a4105e4efe9c60c6a50953db8f621ff54887080065c743" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/platform-candidate/scripts/keycape_issuer_eso.py", + "sha256": "21c5797280c2a7825dbda8ac970d34a0cdef4c6458f68bce0b1c12407f122a87" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/platform-candidate/tests/test_keycape_issuer_eso.py", + "sha256": "223d4c5b349062b9f93e6af79e8d5feda44377ba41a11726f6f2b762691e8308" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/platform.candidate.patch", + "sha256": "a490df7dee41e15351da8d2906e1342edebb533984befe3d9ae74d952dbb091b" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/review.json", + "sha256": "d99db9ab4f326308cde464129bbbb7573434e500fd03c130a37a590bac3643a4" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/source-pins.json", + "sha256": "6e896b6196a0aca01ddaf433b4d14cc041fc0296c5dd6404c3a698b941b61bd8" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/structural-and-patch-checks.json", + "sha256": "a3dc4cd405c412d4369986cc6736cd2d9eed02d4b86d95d62d5a9914106fdb14" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/type.before.yaml", + "sha256": "e75fe94110ef9fb7fb18072fe2856941b33507c99cb406d36576046481485d0a" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/type.candidate.yaml", + "sha256": "ef02ddae3167bdd1558193a5f6db0379fb8cc84207531706d5ef35caabc3684f" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/README.md", + "sha256": "522392bd35d2e0b22fd097509361d1476200a361c2ced22603d43e5758aa691d" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/canonical-validation.json", + "sha256": "2f81d70761b154b042d09321cb3b04e0d30d5f9ce79be05503870d245423bbf5" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/consistency.json", + "sha256": "1543d1dcd86706889463e3d0915d1a9aa02b72c3861400a010df44ed4c9b392a" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/consistency.stderr.txt", + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/integrate_platform.py", + "sha256": "ef8a1647e54013c0c6f2c11c8f74ef43fc97ceaca99e8f8caa89ee16ff80efba" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/integration-checks.json", + "sha256": "0ab8b058414dde1be9c7fd6ee2cb3f246e4deef2036d17472e846fae23bba4b9" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/manifest.json", + "sha256": "c52692fb154cd46f1bfa62e7a0cda73241716b1024d992449e24e87e18750bbb" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/netkingdom-entry-point.conditional.patch", + "sha256": "6784a21fbd10bd62712be2e0f4929034601c94629aa74ccaa2e93fc815eecd61" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/packet-hashes.json", + "sha256": "4b5643c09d5e76bf1b034b16e3ddc204a5a83319d5317391b26d836d44529dc7" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/platform-source/docs/keycape-issuer-eso-contract.md", + "sha256": "7f59e59b17c58252982931ac61fe50dd7a07340add6c437fe48af36971eafe68" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/platform-source/manifests/templates/keycape-issuer-eso.yaml", + "sha256": "e17498bd8a4c820041a4105e4efe9c60c6a50953db8f621ff54887080065c743" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/platform-source/scripts/keycape_issuer_eso.py", + "sha256": "21c5797280c2a7825dbda8ac970d34a0cdef4c6458f68bce0b1c12407f122a87" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/platform-source/tests/test_keycape_issuer_eso.py", + "sha256": "223d4c5b349062b9f93e6af79e8d5feda44377ba41a11726f6f2b762691e8308" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/progress-paragraph.md", + "sha256": "151199597ba37225e5d46922ae90453f7338c5602ff81b3d870b579278bef2d4" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/projection.json", + "sha256": "b52dee68c6dd1d6c6fe6cb412a372f919af53fdab39c116d070ded6f2067af0b" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/projection.stderr.txt", + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/publication-commit.json", + "sha256": "4d7bfca9479f4fbf57a9ce3b9f737749f47d9e1ecae3afb07897cd16b43e4645" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/publication-inputs.json", + "sha256": "fb2893c6a95566849b30aa8ab61bf5f2d24a6ccf61e2242b6bff8a2e05b0004e" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-review-r1/REVIEW.md", + "sha256": "508afd5695eda998cf651874c00bcaa14725afd7a52b54a5c27854b0bfc44978" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-review-r1/evidence.json", + "sha256": "7fa36069248ba9c3bb6a57da0a940a35ac6d6a293a4edd653fa54e19a3f71d9e" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-review-r1/focused-tests.txt", + "sha256": "fd09c39d8434d0a9ed746dcddd0acd11657d67541c59ab632ef36be1f9cc7ec2" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-review-r1/reproduce.py", + "sha256": "61633872de8244db3bc94e32d406479605eceef1f2c6f415164e1b6c0680068b" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-review-r1/review.json", + "sha256": "99b78098a6eb257b2e58c22b076d43c687cc2708560bba46b82f50129d72aecb" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/README.md", + "sha256": "bff0eb23ce4920da6058bf71a3dace209baca69a97c94bd9db4f765f71044eb2" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/candidate-validation.json", + "sha256": "17ed30550f7330beb49b5f866c634e78fbcbf98088fcc3e0ce92faa7b9042524" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/failure_inverse_omitted.txt", + "sha256": "543906e345b1b5daa28f0f3cfc620b3e06253cfb65c3a861586630dc63b57608" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/focused-tests-before-admission-binding.json", + "sha256": "c11b15e97b3da1c9b4819fec539b17823f98f4e6f39ece9bc5c8422325ddbc8e" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/focused-tests-before-admission-binding.txt", + "sha256": "9fb7e13b0e58942d2dc1921fd303ad372b1385841e3df88f5a23365a4a4b95bf" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/focused-tests.json", + "sha256": "8b1f7ecf75fedee9c03d1421efc6a6e9dd89eb99da70df5e8e4d47027b5fe331" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/focused-tests.txt", + "sha256": "99ddd55245e57dd9047793bd206158c66b723a7fe79cc8e7b785f8183f1e4654" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/implementation.json", + "sha256": "fce244e7a8bd1f1b9911c632de4831de291f358375183ff7959dc5615560f6d0" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/manifest.json", + "sha256": "5a6c08e13422cd2d21d5df5d9dce41685a6cf1cf1cdcd3a64f807806602f1dfd" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/negative-controls.json", + "sha256": "d13a0cf8b22abef1cba502b3fd3fd10c9f40955a3ffa8fee473d6189bfb25783" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/net-kingdom-unchanged-owner-status.txt", + "sha256": "354b3dd65c711df0855c22ba053a4ca295d5491941c5a64c1157a0b7d43cf4cb" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/owner-contract-candidate.md", + "sha256": "dfdc6573df76647790aabb26e4b228b04047dffd032fc33821024f8bbc9ba941" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/owner_configuration_contract.py", + "sha256": "62752f518b78fa7713bdb4d47e68decbf3aa89ecbc7950549ffdc3d08e01c554" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/post_revision_guard_removed.txt", + "sha256": "e9e4a134ece9095af5dbf790858b6e3ed44ae9388dde98cbb9a25b4183a16f50" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/railiance-apps-unchanged-owner-status.txt", + "sha256": "251b0eb219198e4ad334a76e0b39f23848242354105e97f3f61701168817cdd3" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/test_owner_configuration_contract.py", + "sha256": "51482fc5bad6af5c52d736ded7e7031291b18bc4949b9ff43d7dea78f3a8ad94" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/type-owner-configuration-inverse.candidate.patch", + "sha256": "6e23967d768759fc10203bcc25d81cb32c3d9222ed6fcb4a510a7cfe90e579bf" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/type.before.yaml", + "sha256": "e75fe94110ef9fb7fb18072fe2856941b33507c99cb406d36576046481485d0a" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r1/type.candidate.yaml", + "sha256": "739c927ab494f4e0f6618e6e55394dd8dc4b720db428cd4973f7a81524e1ecd9" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/README.md", + "sha256": "168b80f6cc0d50650a484d24696022cac8a4cf8279c2fb4da0c112cfbf10fd08" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/candidate-validation.json", + "sha256": "200132ab91899f6cfb2782b19ff59c72056f05197690cecdda708ccbdd343edc" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/focused-tests.json", + "sha256": "2f48369c3d00f9a43989486882519ee5c09d4eb1f5be599a92b06ca0d7cc8581" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/focused-tests.txt", + "sha256": "436459ced69db381f44a0f14d2ebcf00a4067f596a9881cfdcca7146c237d3a1" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/fresh-inverse-session.candidate.patch", + "sha256": "0b4a466c23cdeeb34789ab79c3d7cdc4a186495a910e23bc6491a29b5ff9cff8" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/fresh-session-guard-omitted.txt", + "sha256": "f47357c3303b026dcd9edcce564725a1c7d0d77969dd2857a6f1c008f169c1e1" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/implementation.json", + "sha256": "8517713a130f23cb38c597d333f65ec27700f347c16dca617e988f1cbc948abf" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/manifest.json", + "sha256": "14058734b40a0394a5d26b5063ba60ea343b1ad3ad47b789b9e640637c4e8c4b" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/negative-controls.json", + "sha256": "8f96396f3d342f505d1dc6ac235a7f37a7c2481d51d100125f05b26619d3d314" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/owner-backend-next.md", + "sha256": "6243d7a70868224cb89fd30818c23ac379f37b6576e4947e444ef5057d06aea8" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/owner-contract-candidate.md", + "sha256": "4f9b91a2389acbc52558347a794da02faa6de4fb189373a11feeac10ae7e7c4f" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/owner-source-inventory.json", + "sha256": "4510ce7b2f9eb9f4695ef4c57bba888c3839d3adcc397f4b4024be0fa71767bb" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/owner_configuration_contract.py", + "sha256": "2cd04432cf3681afc126736f7daacfab46630441aeb223b3e222d6951d24ce2e" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/test_owner_configuration_contract.py", + "sha256": "0f4246fea95bd3139c604da8712e6f6ac08976dd34d73e56ba83663ecba42761" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/type-owner-configuration-inverse.candidate.patch", + "sha256": "949ef6d3e0acc574e1d69c251d6d09e3da636bc73d043dec1c5677f30de29947" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/type.before.yaml", + "sha256": "e75fe94110ef9fb7fb18072fe2856941b33507c99cb406d36576046481485d0a" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-implementation-r2/type.candidate.yaml", + "sha256": "6e744be190c4f047e609bef7670ff987fe1606b4d7dac3f740f164bebc205a13" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-review-r1/README.md", + "sha256": "659ce8da07ef4f16077b9b2fcf5e89c5c1ed177603f589acf344e57b12ace18f" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-review-r1/fresh-inverse-session.candidate.patch", + "sha256": "0b4a466c23cdeeb34789ab79c3d7cdc4a186495a910e23bc6491a29b5ff9cff8" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-review-r1/original-authority-tests.txt", + "sha256": "6c5326d4a189509e823110e748a2e6baed9a8b809b15bc0aa61cc247f2a8df35" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-review-r1/patched-authority-tests.txt", + "sha256": "2d5029651d5d31355683da0f774a17eb418355714ae42af60b6a7b901b65676e" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-review-r1/review.json", + "sha256": "8199a9937c1091de5101c9a3c6bb0bcc3742677e0b5830e1b6a0b00c06fe85e1" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f3-review-r1/test_inverse_authority.py", + "sha256": "4c83407535dd37e571f293a9cc6b47d787b02b925402741d52c70a5532d76e1c" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-source-followthrough-r1/apply_prepare.py", + "sha256": "e759865139078a6ea7509fa80c8521d81446a3f6fc236550628df3d1b14a9e50" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-source-followthrough-r1/consistency.json", + "sha256": "6eae09358f008c873345b417f33e606a70a862786a29cca1e870ed7f48a232b3" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-source-followthrough-r1/consistency.stderr.txt", + "sha256": "f4a6aa1609a13600a0b97cbb08bbc03b3e247b619ff37912d37f7e109ce798ba" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-source-followthrough-r1/identifier-assignment.json", + "sha256": "50c3ef9b95557fd01af751a84ee99e17b868fc128f4101c268cee8157f8b72b2" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-source-followthrough-r1/identifier-preview.json", + "sha256": "5f3bda9eeccf3dd639c2d0bd2315ff1c71e79de021200c53e136559ad658d67e" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-source-followthrough-r1/preimage.json", + "sha256": "93a300b5e744764221671b4a3534aad0bdf511b7b4b45643dd8c3b1e357b541f" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-source-followthrough-r1/projection.json", + "sha256": "11cf3e0838f914a2aab970950dbe0d7f32846c11fc7b593791957d3f8381b497" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-source-followthrough-r1/projection.stderr.txt", + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-source-followthrough-r1/publication-commit.json", + "sha256": "b692f5d2487bcf5f9a8d6db09bc6b0d4fe4a69ae39a6bf79184a26e32ef5f44a" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-source-followthrough-r1/publication-inputs.json", + "sha256": "ebb1f954932140654291c0d9ddd6fcd8448e0edabb1dd194e82bd7633d2a71ea" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-source-followthrough-r1/workplan.allocated.md", + "sha256": "f92962c5aa21aade0fc2f6b4f8d9a95932fe85b107413cdd2824b6ebe72c36d9" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-source-followthrough-r1/workplan.candidate.md", + "sha256": "f1e5245f565da2cc35b5e2d0adfcad42325c64205525d3f96171e604d8935708" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-source-followthrough-r1/workplan.preimage.md", + "sha256": "1502f1f474856172d969489cab8c0642d9456d93d326f6660df3772a4716d30b" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/source-corrections-publication-r1/owner-source-state.json", + "sha256": "c9d3d3ae1372007b35526d592d3a7ea2d2bc6374d34840ef02fdda9535973785" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/source-corrections-publication-r1/prepare.py", + "sha256": "749703026767ccb196c01ac8b4c9fafbcdbd564d20ae73b468e297a114f073d6" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-fixes-r1/candidate.patch", + "sha256": "1a3530a1e469fc03c2c29f1b66fa3ba1904e56bf2abcfb242c7a2ed015fb3e74" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-fixes-r1/candidate/src/warden/case_run.py", + "sha256": "1bc73e30ac718fd75bbaff2a739495fc051aeaec73802ac71f0583cb2d53ab0a" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-fixes-r1/candidate/src/warden/case_type.py", + "sha256": "7b659b8d682863cd28df24025be4b10dd7ca3df724350a22d360c6af9615bca7" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-fixes-r1/candidate/src/warden/routing/models.py", + "sha256": "b85372b2572a49af62e9534f26a33963b894633c7cd43dd2a28416b8959cba3e" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-fixes-r1/candidate/tests/test_case_1_3_integration.py", + "sha256": "4862c5a004075eb18733d354bee7dda3f55e04a6ee8ecb44b76166347c121158" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-fixes-r1/candidate/tests/test_case_admission_modes.py", + "sha256": "9110d6b6286cf0f85268d4970d1fd403250da3f04351d70b1fd2d9d7a93e2d89" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-fixes-r1/candidate/tests/test_case_attended_boundary.py", + "sha256": "4615e653f06144305ea1a84e98bc6260e16ed41ef9e5db2d0f162ef9f723ef2d" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-fixes-r1/evidence/run.py", + "sha256": "19746062528f63909a268e8d97e801c9d116d57e5a1c0e148b2a22ec0ecd0f8f" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-fixes-r1/evidence/sitecustomize.py", + "sha256": "ffaa62732340efd8d40bb54a3c4499cb62a89896e5ee825c8fe7b5d2bf93db22" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-fixes-r1/implementation-r1.json", + "sha256": "b9ee843bba417c82ad77ac8a221cf64dd5088aa640528524b2e0ef0c129d3184" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-fixes-r1/test-isolation-proposal.patch", + "sha256": "0f6c46303ec24f337e4190ec84773ccefb4833241233b4b7d56bd1927eba838c" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-review-r2/integration_runner.py", + "sha256": "d77da107d67ec827f19e0c46045a2802081a62283c3bdb5bfec35feabb454234" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-review-r2/kernel-review-r2.json", + "sha256": "9002efdac67f451ef4fc59c02056c8e3ed23de57677ee9974d7477614960e515" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-review-r2/reviewer-probes.py", + "sha256": "e52025893ca0755b3a62e2585cf2f16002a5b705f7cfb0582b05435d3b9e336a" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-review-r2/run-baseline.py", + "sha256": "02e2a409cd23fb52f518c202c923877fdcc381f708b4e8d77bad04e034b46e73" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-review-r2/run.py", + "sha256": "c6f931ba4ff9b0ef42627030a016dd7ebe3f353b3024f1c40b80df890aefe59f" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-review-r2/setup.py", + "sha256": "fcdeb50a196ce5afda6243b9fb51584af58ba5d55e75a4caed017f742d560500" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-review-r2/sitecustomize.py", + "sha256": "6b7cec873fa1fca6767b77f514bec499adb5e1e7745dbc8eddf36771917ad4f4" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/warden-kernel-source-integration-r1/commit.json", + "sha256": "16883619d6ae1433b30c61ea2fbb8ada44f4423d1102a9390867efa580d15de2" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/warden-kernel-source-integration-r1/consistency-preimage.json", + "sha256": "9c8d006e95c32bbd689c16a5ee575e3d54c46b327d5f2f47a697570480d8e9fb" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/warden-kernel-source-integration-r1/consistency.json", + "sha256": "8032a9d073c6334bc7136ca88a2878b2525f552c4e25266884d45456f4152fb6" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/warden-kernel-source-integration-r1/consistency.stderr.txt", + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/warden-kernel-source-integration-r1/focused-tests.txt", + "sha256": "a25e2b6355475168ff5a9dba6deec605584711d5c63de8ddc3da4644c93f6a5e" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/warden-kernel-source-integration-r1/integrate.py", + "sha256": "8eab2d72e67a9af5230d7978d9533d964b8162a85a7fc053a210da05b3a5219d" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/warden-kernel-source-integration-r1/integration.json", + "sha256": "0781a745e9bcb4e052c80818a71695a1c91672345c7cfd7628fb0a56a7075d82" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/warden-kernel-source-integration-r1/normal-sync.json", + "sha256": "00a81cede4636a8adcbb2315a3a79c69d472accbab83f5d99cbb51a5ffa48653" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/warden-kernel-source-integration-r1/normal-sync.stderr.txt", + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/warden-kernel-source-integration-r1/publish.py", + "sha256": "c632d670d3b53efbf7a1b5c2c29402e3e5d57cce3a40f119e672153e3fe21189" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/warden-kernel-source-integration-r1/verification.json", + "sha256": "f6d9efb1f3118a80945465b08ba17e26b76ab14d017e7fba56ab3e911767b2ac" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/warden-kernel-source-integration-r1/workplan.preimage.md", + "sha256": "721a90b2ed8c896257c34a1451eb642cb8824c8263e37c483fb61b36adc54088" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-integration-r1/commit.json", + "sha256": "7bf38dbd9c4b6c59f6b1b45e86abeba8bff2a7a0e847da85f7e7e6f422a99186" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-integration-r1/consistency.json", + "sha256": "b70a0b6356d42b173b9d11af7c0f4df6850e4c3bc186dee69a7348f2d66e62bc" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-integration-r1/consistency.stderr.txt", + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-integration-r1/full-unit-result.json", + "sha256": "9f9460f8b0f06e5f5668e554a059300ed5b40fbb1ea0ffc3e1092fe0ac11a4f0" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-integration-r1/integrate.py", + "sha256": "333d24d6f01d6d23b571e02846d71938ae8b78fee63aefbe90d7a7c1888cf7e1" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-integration-r1/integration.json", + "sha256": "663628195799e298e47e01cf9067fddc0f21a1c06967d11266c2fdc8a9e68bf0" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-integration-r1/prepare_publication.py", + "sha256": "5a22d66b1986c5b17414ea5d70dbd42bda1f69c24941bc80a6aeca86c79d4795" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-integration-r1/projection.json", + "sha256": "1d63a42ddbe4055fb70035948f3e2dea2dd167498bad87282f095ecfcdb32a89" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-integration-r1/projection.stderr.txt", + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-integration-r1/publication-commit.json", + "sha256": "ced6545c22193dd4d8dc272bf16603a9ed9cf0973e21c94a97ede68f289dc423" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-integration-r1/publication-inputs.json", + "sha256": "bc2f8fd65e7517849f52c888baf89f7f160b20027a946d3aacbf1e5c5aa5102f" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-integration-r1/root-disposition.json", + "sha256": "190780394f6b062be7483c40da28ab1d28a6a29794fb88dc2e77264faa60b9f7" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/README.md", + "sha256": "7322316db3fdd7107efe82d42166a25dd494161f8062aaa833b4497392fd04bb" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate.patch", + "sha256": "1fe65076ce7e633be304fc779bf2c87b7ee1e4e460de55ca74355b5842d036f5" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/Makefile", + "sha256": "8cf08ec680ed6bd323a1ce8475ade731dbeebcd64ffd43ecd169308f8a727940" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/docs/development/safe-unit-tests.md", + "sha256": "b157c3e6956a41dcd9fc3912d0c7d09a664f11f08f0f8a8e4cae57fac9747cf7" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/scripts/run_unit_tests.py", + "sha256": "8157e44df298fa4c47c6c08b2dc68e3ac945c4beb3f47a251a7afbbc55eb1bd2" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/tests/fixtures/workload-coverage/provenance.json", + "sha256": "a4a203c1bd7bc5b8039d1b2525bea65dbae288a3f552969e4218cd5abda96990" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/tests/fixtures/workload-coverage/rapp-core-hub/declarations/rapp.yaml", + "sha256": "9ce902da0c77809f1094a634244652835b162f59682e9fa6923d8c8aa4ed4a3c" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/tests/fixtures/workload-coverage/rapp-issue-core/declarations/rapp.yaml", + "sha256": "a1956deecf2f0fbca44549e5ea5e8f1c4b74ebf91a9482c64c982491af0d923b" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/tests/fixtures/workload-coverage/rapp-qonto/declarations/rapp.yaml", + "sha256": "15aa9809e8d6a120c516be5ce9adfb72c00382140c171e791083ad117f503b3f" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/tests/test_case_plan.py", + "sha256": "944bc2a725ca1b7a10f4516c3ceed65f99627057dd15a859da4f8ffb3d97e71f" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/tests/test_case_type.py", + "sha256": "5a217a172e7e2ffbea66f1beee9a1c18bf5e97f9fb25b83cb96b6ba976cbb2a5" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/tests/test_layer_conformance.py", + "sha256": "3edf0a0d49c3560278a564c477834335cfaf5243341cb9345f0d4bf8994db56c" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/tests/test_safe_unit_runner.py", + "sha256": "6b623d18879e7564dd11447c16d3ec9f80321a25a525fcd0306b6a903a68031a" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/tests/test_workload_join.py", + "sha256": "1828908a5cd1f0bc13ca367dbd1245221c52d1ac60789c9e3bd51dd36cb8beb9" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/tests/unit_isolation_probe.py", + "sha256": "726d3e01ae21c1379588e1f08459449908cc8a75ffea1b337c33a1a888c66f59" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/workplans/WARDEN-WP-0044-security-case-follow-through.md", + "sha256": "074bfcf2f7841e78bcd404bf573982d244b00f2f500e198b7fe80fea660de991" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/implementation.json", + "sha256": "8033b323178e1d3b031a45466687fe05479627e99559d9de7b3baef52ab8671f" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/packet-hashes.json", + "sha256": "1567ff1ad558fdb464818bb555dab79152477c85113f89874daa3049029733a8" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-review-r1/review-r1.json", + "sha256": "6cb7e3d1af81ffbf9bd4908841ae5c4772a96c909965f077e5ffd8dc57f0b8b4" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-review-r1/reviewer-correction.patch", + "sha256": "9407289f8da41ed6fff367baf566ad30f5510a2627c14e0ac0cf971bf9b465c9" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-review-r1/reviewer-probes.py", + "sha256": "0b49fc2ad7aeeca0cabb0961b06d8cdc85ae5109f36bf81e4245e55127357574" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-review-r1/run.py", + "sha256": "f7fc81b8acbcd0712ee5f79a16b27aa1cdd0af11db761f9e94fba8b91e51ec8a" + }, + { + "path": "docs/netkingdom-target-implementation.md", + "sha256": "8452750de483b79a2cbb09b192d18cf6dc421ad736dd06c738af0727c3d74854" + }, + { + "path": "docs/netkingdom-target-security-architecture.md", + "sha256": "ec2079be18c7cdb8a1e4fdbd22131c08c83721d7a1bf18b4802bc5e229520f35" + }, + { + "path": "workplans/CUST-WP-0080-netkingdom-security-blueprint-and-gap-closure.md", + "sha256": "2f4adf4348346e31c4581754352f0d3329231af75333b988d2dc336d4f4b8a35" + } + ], + "stdout": "", + "stderr": "{\"blocked_blob_ids\": [\"187aa2fedbd42b96194059f30aedc99353ebf64b\", \"3e6e3ae1861a9dcce8916a85dbd30ec99d097a58\", \"b23adafc5a1f891f55dfcfd4aa8314074e10f392\"], \"scanned_blobs\": 199, \"scanner_sha256\": \"88f91962aa2f93ac6ab281d553b9e125f5197bbbce38f9f2437f7299c32e5509\", \"scanner_version\": \"8.30.1\", \"scope\": \"changed-staged-blobs\", \"status\": \"blocked\"}\n" +} diff --git a/docs/changes/netkingdom-target-2026-10-17/source-corrections-publication-r1/scanner-blocked-paths.json b/docs/changes/netkingdom-target-2026-10-17/source-corrections-publication-r1/scanner-blocked-paths.json new file mode 100644 index 0000000..3b8a197 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/source-corrections-publication-r1/scanner-blocked-paths.json @@ -0,0 +1,17 @@ +[ + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/manifest.json", + "oid": "187aa2fedbd42b96194059f30aedc99353ebf64b", + "stage": "0" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/packet-hashes.json", + "oid": "3e6e3ae1861a9dcce8916a85dbd30ec99d097a58", + "stage": "0" + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-review-r1/evidence.json", + "oid": "b23adafc5a1f891f55dfcfd4aa8314074e10f392", + "stage": "0" + } +] diff --git a/docs/changes/netkingdom-target-2026-10-17/source-corrections-publication-r1/warden-progress.json b/docs/changes/netkingdom-target-2026-10-17/source-corrections-publication-r1/warden-progress.json new file mode 100644 index 0000000..cb4c47f --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/source-corrections-publication-r1/warden-progress.json @@ -0,0 +1,14 @@ +{ + "workplan_id": "f7e81013-6868-5d99-8528-ba75a442eca9", + "id": "df65784e-68c7-424a-a134-9acceb2fa084", + "topic_id": null, + "task_id": "bcbf6c91-2c04-5c16-ae8c-5d6c06fd6b89", + "decision_id": null, + "event_type": "note", + "summary": "Published reviewed mode-aware case kernel and corrected ordinary safe unit-test entry in ops-warden b593c879831378db7dc62adfabdfedf2d44ddd38 (kernel implementation ae7bc73, normal allocator-only historical UUID commit 922c26d retained). Cached attended run/rollback/reconcile recheck caller/session and the authenticated catalog snapshot before writes/callbacks. Original kernel review and unknown ambient lookup remain preserved. Original safe-runner candidate rejected for O_TRUNC and unchecked projection hashes; minimal reviewed corrections accepted by root and full ordinary make test passes785 with4 deselected under synthetic parent token/HOME/Python/pytest controls. Trusted-test accident guard only; directpytest/native/non-open/preopened-descriptor limits explicit. No production src/catalog/parameter pin changes from test runner, no draft promotion, credential or native action. All4WP44 task bindings/statuses unchanged; T03 progress, native first case remains open. Evidence: dated warden-kernel-source-integration-r1 and warden-safe-unit-integration-r1.", + "detail": null, + "author": "codex", + "session_id": null, + "created_at": "2026-10-17T20:02:37.087423Z", + "workstream_id": "f7e81013-6868-5d99-8528-ba75a442eca9" +} diff --git a/docs/changes/netkingdom-target-2026-10-17/source-corrections-publication-r2/public-digest-derivation.json b/docs/changes/netkingdom-target-2026-10-17/source-corrections-publication-r2/public-digest-derivation.json new file mode 100644 index 0000000..1e36f1d --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/source-corrections-publication-r2/public-digest-derivation.json @@ -0,0 +1,32 @@ +{ + "schema": "custodian.public-digest-publication-derivation.v1", + "scanner_rejection_retained": "../source-corrections-publication-r1/publication-commit.json", + "diagnosis": "All values in the three scanner-blocked metadata documents are public file/archive/commit hashes or non-secret metadata. Every SHA256 verified against its exact public source artifact before conversion.", + "verified_public_artifacts": 43, + "representation": "Bare 64-hex public digest values become explicit sha256:; no credential encoded, no scanner policy/config/ignore/allowlist change.", + "exact_original_reconstruction": "Load JSON, remove sha256: from each value matching that exact 64-hex scheme, serialize with json.dumps(indent=2)+newline. Verified byte-identical for all three originals. Historical input pins still refer to those exact reconstructable original bytes.", + "retained_local_original_directory": "/tmp/custodian-original-public-digest-metadata-awy5nzfr", + "rows": [ + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-implementation-r1/manifest.json", + "original_sha256": "sha256:754531fbef468bc0e432bd26d4dbed992294c34d57e23fe7c3b7f829f7a24ba4", + "published_sha256": "sha256:cfbc40890f94302764b7b6302a90ea07e975d1eadad91b9dd9642c45a5b13717", + "exact_original_reconstruction_verified": true, + "executable_and_template_input_bytes_changed": false + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-publication-r1/packet-hashes.json", + "original_sha256": "sha256:4b5643c09d5e76bf1b034b16e3ddc204a5a83319d5317391b26d836d44529dc7", + "published_sha256": "sha256:057f7cf2a6ea98ddd5963cf2608cd8d7a43847048e77aa2c2a571e7f3a3c53d2", + "exact_original_reconstruction_verified": true, + "executable_and_template_input_bytes_changed": false + }, + { + "path": "docs/changes/netkingdom-target-2026-10-17/nk48-r5-f2-review-r1/evidence.json", + "original_sha256": "sha256:7fa36069248ba9c3bb6a57da0a940a35ac6d6a293a4edd653fa54e19a3f71d9e", + "published_sha256": "sha256:cc8cf37c1d4f56dfb16233b31e293c158d68cd4c49a1ab1b730205cb325531b6", + "exact_original_reconstruction_verified": true, + "executable_and_template_input_bytes_changed": false + } + ] +} diff --git a/docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-fixes-r1/candidate.patch b/docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-fixes-r1/candidate.patch new file mode 100644 index 0000000..ecc627f --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-fixes-r1/candidate.patch @@ -0,0 +1,314 @@ +diff --git a/src/warden/case_run.py b/src/warden/case_run.py +index 2f66583..60692b8 100644 +--- a/src/warden/case_run.py ++++ b/src/warden/case_run.py +@@ -587,7 +587,18 @@ def _catalog_lane(catalog_source: bytes, descriptor: Descriptor): + raise Refused("descriptor owner does not fit catalog lane owner_repo") + if descriptor.effect in ("mint", "change") and entry.risk != "high": + raise Refused("catalog lane risk does not cover the descriptor effect") +- return entry ++ return entry, entries ++ ++ ++def _attended_admission(descriptor, actor, entry, entries): ++ from .routing.models import attended_lane_issue ++ ++ if (actor != "human" or "WARDEN_AGENT_ID" in os.environ ++ or "parent-session" not in descriptor.revocation_scopes): ++ raise Refused("attended step requires a human parent session") ++ issue = attended_lane_issue(entry, entries.get) ++ if issue: ++ raise Refused(issue[1]) + + + def _step_mode(case_type: dict, case: dict, descriptor: Descriptor) -> str: +@@ -711,14 +722,10 @@ class Executor: + actor != "human" or os.getenv("WARDEN_AGENT_ID") + ): + raise Refused("agent mint is forbidden") +- entry = _catalog_lane(catalog_source, descriptor) ++ entry, entries = _catalog_lane(catalog_source, descriptor) + mode = _step_mode(case_type, case, descriptor) +- if mode == "attended" and ( +- actor != "human" +- or os.getenv("WARDEN_AGENT_ID") +- or "parent-session" not in descriptor.revocation_scopes +- ): +- raise Refused("attended step requires a human parent session") ++ if mode == "attended": ++ _attended_admission(descriptor, actor, entry, entries) + if mode == "owner" and ( + entry.effective_delegation.mode != "native" + or entry.owner_repo != descriptor.owner +@@ -796,7 +803,11 @@ class Executor: + descriptor.allowed_created, + descriptor.retry_safe, + ) +- self._admitted[invocation] = (descriptor, initial, session_evidence) ++ # Keep the mode and exact authenticated catalog bytes with this admission; ++ # dispatch must not substitute a later ambient catalog or lose its mode. ++ self._admitted[invocation] = ( ++ descriptor, initial, session_evidence, mode, catalog_source ++ ) + return descriptor, invocation, initial + + def _result( +@@ -861,7 +872,7 @@ class Executor: + descriptor.adapter_source, + ): + raise Refused("adapter changed after admission") +- session = self._admitted[invocation][2] ++ _, _, session, mode, catalog_source = self._admitted[invocation] + if ( + self._verify_evidence( + f"caller-session:{invocation.actor}:{invocation.principal}:{invocation.lane}", +@@ -873,6 +884,9 @@ class Executor: + raise Refused("caller session no longer verified") + if descriptor.effect == "mint" and os.getenv("WARDEN_AGENT_ID"): + raise Refused("agent mint is forbidden") ++ if mode == "attended": ++ entry, entries = _catalog_lane(catalog_source, descriptor) ++ _attended_admission(descriptor, invocation.actor, entry, entries) + return adapter + + def _attempt(self, invocation: Invocation, state: StepState, operation: str): +diff --git a/src/warden/case_type.py b/src/warden/case_type.py +index 986d74f..1454cd3 100644 +--- a/src/warden/case_type.py ++++ b/src/warden/case_type.py +@@ -24,6 +24,7 @@ from typing import Iterable, List, Optional + import yaml + + from warden.routing.catalog import Catalog ++from warden.routing.models import attended_lane_issue + + VALUE_HANDLING = ("none", "generated_in_custody") + REQUIRED_STEP_FIELDS = ("id", "owner", "entry_point", "auth", "warden_executes", "attended", "value_handling") +@@ -138,13 +139,10 @@ def check_follow_through(doc: dict, catalog: Optional[Catalog] = None) -> Follow + findings.append(Finding("catalog", "LANE-MISSING", where, f"lane '{lane}' is not in the catalog")) + elif not entry.is_active or not entry.reviewed: + findings.append(Finding("catalog", "LANE-UNREVIEWED", where, f"lane '{lane}' is not active and reviewed")) +- elif attended and entry.lane == "attended-change": +- session = catalog.get(entry.session_lane or "") +- if session is None or not session.is_active or not session.reviewed or session.lane != "login": +- findings.append(Finding("catalog", "LANE-KIND", where, +- f"attended-change lane '{lane}' session_lane '{entry.session_lane}' does not resolve to an active, reviewed login lane")) +- elif attended and entry.lane != "login": +- findings.append(Finding("catalog", "LANE-KIND", where, f"attended step's lane '{lane}' is not a login or attended-change lane")) ++ elif attended: ++ issue = attended_lane_issue(entry, catalog.get) ++ if issue: ++ findings.append(Finding("catalog", issue[0], where, issue[1])) + + if executes: + warden_steps.append(sid) +diff --git a/src/warden/routing/models.py b/src/warden/routing/models.py +index 21abe64..c691b33 100644 +--- a/src/warden/routing/models.py ++++ b/src/warden/routing/models.py +@@ -8,7 +8,7 @@ pointers only (the no-double-source rule, enforced in `catalog.py`). + from __future__ import annotations + + from dataclasses import dataclass, field +-from typing import List, Optional ++from typing import Callable, List, Optional + + + # Risk grade vocabulary (ADR-0007). Grades outside LOW_RISK_GRADES — including +@@ -324,3 +324,20 @@ class RouteEntry: + elif any(t in h or h in t for h in haystack): + score += 1 + return score ++ ++ ++def attended_lane_issue( ++ entry: RouteEntry, get_entry: Callable[[str], Optional[RouteEntry]] ++) -> Optional[tuple[str, str]]: ++ """Shared planner/executor boundary, using one parsed catalog snapshot.""" ++ if not entry.is_active or not entry.reviewed: ++ return ("LANE-UNREVIEWED", f"lane '{entry.id}' is not active and reviewed") ++ if entry.lane == "attended-change": ++ session = get_entry(entry.session_lane or "") ++ if (session is None or not session.is_active or not session.reviewed ++ or session.lane != "login"): ++ return ("LANE-KIND", f"attended-change lane '{entry.id}' session_lane " ++ f"'{entry.session_lane}' does not resolve to an active, reviewed login lane") ++ elif entry.lane != "login": ++ return ("LANE-KIND", f"attended step's lane '{entry.id}' is not a login or attended-change lane") ++ return None +diff --git a/tests/test_case_1_3_integration.py b/tests/test_case_1_3_integration.py +index 342bdda..bb13c09 100644 +--- a/tests/test_case_1_3_integration.py ++++ b/tests/test_case_1_3_integration.py +@@ -1,10 +1,15 @@ + """Integration: the real net-kingdom 1.3 candidate against this kernel and catalog. + + Reads the actual 1.3 type, its forgejo example and the committed composition +-artifact from net-kingdom, plus this branch's real routing catalog. Only two things +-are simulated, both in memory: draft lanes flipped to active (promotion needs owner +-confirmation) and a 1.3 PARAMETER_PINS entry (PIN-1 waits for the founder's +-acknowledgment). Trust is fixture-only, and nothing is dispatched. ++artifact from net-kingdom, plus this branch's real routing catalog. The descriptor ++map and metadata come from those committed files. All authority is fixture-only: ++we change the proposed type to acknowledged, change the example case to ++materialized and replace its acceptance record, promote draft lanes in memory, ++and temporarily install the 1.3 parameter pin. Stub adapters replace native ++adapters; an unconditional evidence verifier simulates every acceptance and caller ++session. WARDEN_AGENT_ID is removed to simulate a human runtime. No adapter is ++dispatched. This proves conditional source admission, not authenticated owner ++acceptance, native execution or readiness; unresolved Apps sessions stay refused. + """ + from dataclasses import asdict + import hashlib +@@ -99,12 +104,15 @@ def test_artifact_descriptors_are_the_type_map(): + assert {s: digest(encode(d)) for s, d in descs.items()} == type_doc()["spec"]["execution_descriptors"] + + +-def test_all_seven_admitted_once_promoted_and_pinned(tmp_path): ++def test_conditional_admission_keeps_unresolved_apps_sessions_refused(tmp_path): + for step, desc in descriptors().items(): +- admitted, invocation, _ = admit(tmp_path, desc) +- assert admitted == desc +- assert (invocation.step, invocation.actor, invocation.target) == (step, "human", "forgejo") +- ++ if step in ("deliver-consumer-secret", "hand-over"): ++ with pytest.raises(Refused, match="does not resolve to an active, reviewed login lane"): ++ admit(tmp_path, desc) ++ else: ++ admitted, invocation, _ = admit(tmp_path, desc) ++ assert admitted == desc ++ assert (invocation.step, invocation.actor, invocation.target) == (step, "human", "forgejo") + + def test_draft_lane_steps_refused_until_promoted(tmp_path): + for step, desc in descriptors().items(): +diff --git a/tests/test_case_admission_modes.py b/tests/test_case_admission_modes.py +index e425020..5069491 100644 +--- a/tests/test_case_admission_modes.py ++++ b/tests/test_case_admission_modes.py +@@ -53,6 +53,7 @@ def test_attended_mode_admits_human_parent_session(rig, monkeypatch): + admitted = admit( + rig, + step=mode_step(False, True), ++ entries=[dict(lane_entry(), lane="login")], + desc=dict(effect="change", revocation_scopes=("parent-session",)), + ) + assert admitted[1].actor == "human" +diff --git a/tests/test_case_attended_boundary.py b/tests/test_case_attended_boundary.py +new file mode 100644 +index 0000000..ba07b33 +--- /dev/null ++++ b/tests/test_case_attended_boundary.py +@@ -0,0 +1,113 @@ ++"""Synthetic attended admission/dispatch regressions; no owner/native actions.""" ++from dataclasses import replace ++ ++import pytest ++ ++from tests.test_case_admission_modes import admit, mode_step ++from tests.test_case_run import fixture_catalog, lane_entry, make_rig ++from warden.case_receipt_state import Outcome, Refused ++from warden.case_type import check_follow_through ++from warden.routing.catalog import CatalogError, _parse_entry, load_catalog ++ ++ ++def attended(rig, entries): ++ return admit(rig, step=mode_step(False, True), entries=entries, ++ desc=dict(effect="change", revocation_scopes=("parent-session",))) ++ ++ ++@pytest.mark.parametrize("operation", ["run", "rollback", "reconcile"]) ++@pytest.mark.parametrize("marker", ["agent-1", "", None]) ++def test_identity_change_refuses_before_any_write_or_callback(tmp_path, monkeypatch, operation, marker): ++ monkeypatch.delenv("WARDEN_AGENT_ID", raising=False) ++ rig = make_rig(tmp_path) ++ executor = rig[0] ++ admitted = attended(rig, [dict(lane_entry(), lane="login")]) ++ executor.evidence_verifier = lambda *_: True ++ if operation == "rollback": ++ executor.run(*admitted) ++ elif operation == "reconcile": ++ old = executor.adapters[admitted[0].adapter] ++ def lost(_): ++ raise RuntimeError("synthetic lost reply") ++ executor.adapters[old.reference] = replace(old, invoke=lost) ++ executor.run(*admitted) ++ calls = [] ++ old = executor.adapters[admitted[0].adapter] ++ callback = getattr(old, "invoke" if operation == "run" else operation) ++ def observed(*args): ++ calls.append(args) ++ return callback(*args) ++ executor.adapters[old.reference] = replace(old, **{ ++ "invoke" if operation == "run" else operation: observed, ++ }) ++ def rows(): ++ return tuple(executor.journal.db.execute(f"SELECT COUNT(*) FROM {table}").fetchone()[0] ++ for table in ("intents", "receipts")) ++ before = rows() ++ if marker is None: ++ result = getattr(executor, operation)(*admitted) ++ assert result.last.outcome is {"run": Outcome.SUCCESS, ++ "rollback": Outcome.ROLLED_BACK, ++ "reconcile": Outcome.NO_CHANGE}[operation] ++ assert len(calls) == 1 ++ assert rows() != before ++ return ++ monkeypatch.setenv("WARDEN_AGENT_ID", marker) ++ with pytest.raises(Refused, match="attended step requires a human parent session"): ++ getattr(executor, operation)(*admitted) ++ assert rows() == before ++ assert not calls ++ ++ ++def entries_for(case): ++ change = dict(lane_entry(), lane="attended-change", session_lane="login-lane") ++ login = dict(lane_entry("login-lane"), lane="login") ++ if case == "missing": ++ return [change] ++ if case == "draft-login": ++ login["status"] = "draft" ++ elif case == "unreviewed-login": ++ login["reviewed"] = "" ++ elif case == "wrong-kind-login": ++ login["lane"] = "secret" ++ elif case == "draft-change": ++ change["status"] = "draft" ++ elif case == "unreviewed-change": ++ change["reviewed"] = "" ++ elif case == "missing-session-field": ++ del change["session_lane"] ++ elif case in ("secret", "ceremony"): ++ change["lane"] = case ++ del change["session_lane"] ++ return [change, login] ++ ++ ++@pytest.mark.parametrize("case, code", [ ++ ("valid", None), ("missing", "LANE-KIND"), ("draft-login", "LANE-KIND"), ++ ("unreviewed-login", "LANE-KIND"), ("wrong-kind-login", "LANE-KIND"), ++ ("draft-change", "LANE-UNREVIEWED"), ("unreviewed-change", "LANE-UNREVIEWED"), ++ ("secret", "LANE-KIND"), ("ceremony", "LANE-KIND"), ++]) ++def test_attended_change_planner_and_executor_agree(tmp_path, monkeypatch, case, code): ++ monkeypatch.delenv("WARDEN_AGENT_ID", raising=False) ++ entries = entries_for(case) ++ path = tmp_path / "catalog.yaml" ++ path.write_bytes(fixture_catalog(*entries)) ++ catalog = load_catalog(path) ++ step = dict(mode_step(False, True)[0], attended_justification="synthetic test", ++ entry_point="owner/test", auth="synthetic session", value_handling="none") ++ doc = {"spec": {"follow_through": [step], "rollback": [{"reverses": step["id"]}]}} ++ report = check_follow_through(doc, catalog) ++ assert [f.code for f in report.findings] == ([] if code is None else [code]) ++ rig = make_rig(tmp_path) ++ if code: ++ with pytest.raises(Refused): ++ attended(rig, entries) ++ assert rig[0].journal.db.execute("SELECT COUNT(*) FROM receipts").fetchone()[0] == 0 ++ else: ++ assert attended(rig, entries)[1].lane == "issuer-lane" ++ ++ ++def test_attended_change_missing_session_is_parser_rejection(): ++ with pytest.raises(CatalogError, match="must name session_lane"): ++ _parse_entry(entries_for("missing-session-field")[0], 0) diff --git a/docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-fixes-r1/candidate/src/warden/case_run.py b/docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-fixes-r1/candidate/src/warden/case_run.py new file mode 100644 index 0000000..60692b8 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-fixes-r1/candidate/src/warden/case_run.py @@ -0,0 +1,1072 @@ +"""Closed local execution kernel v1; no installed adapters or live CLI dispatch. + +An owner must acknowledge each descriptor and provide an independent provenance +verifier. Descriptive case types remain plan-only. Nothing executes shell prose. +""" + +from __future__ import annotations + +from contextlib import contextmanager, redirect_stderr, redirect_stdout +from dataclasses import asdict, dataclass, replace +import fcntl +import hashlib +import json +import os +from pathlib import Path +import re +import sqlite3 +import threading +import uuid +from typing import Callable + +import yaml +from .case_inputs import BrowserInputs +from .case_receipt_state import ( + Outcome, + Pins, + Receipt, + Refused, + StepState, + begin, + finish, + recover, + begin_rollback, + finish_rollback, +) + +IDENTIFIER = re.compile(r"[a-zA-Z0-9][a-zA-Z0-9_.:/@-]{0,191}\Z") +MAX_SOURCE_BYTES = 262144 +MAX_HISTORY = 128 +OUTPUT_LOCK = threading.RLock() + + +def bounded_source(value: bytes): + if type(value) is not bytes or len(value) > MAX_SOURCE_BYTES: + raise Refused("source exceeds bounded input contract") + + +@contextmanager +def contained_output(): + """Discard Python and inherited OS stdout/stderr; serialize process fd changes.""" + with OUTPUT_LOCK: + saved = [] + null = os.open(os.devnull, os.O_WRONLY) + try: + for target in (1, 2): + saved.append((target, os.dup(target))) + os.dup2(null, target) + with redirect_stdout(_Discard()), redirect_stderr(_Discard()): + try: + yield + except Exception: + raise Refused("owner callback failed") from None + except BaseException: + raise OwnerInterrupted() from None + finally: + for target, original in saved: + os.dup2(original, target) + os.close(original) + os.close(null) + + +DIGEST = re.compile(r"sha256:[0-9a-f]{64}\Z") + + +def digest(value: bytes) -> str: + return "sha256:" + hashlib.sha256(value).hexdigest() + + +@dataclass(frozen=True, slots=True) +class Descriptor: + version: str + step: str + owner: str + adapter: str + adapter_source: str + actor: str + lane: str + target_parameter: str + effect: str + allowed_created: tuple[str, ...] + retry_safe: bool + revocation_scopes: tuple[str, ...] + + @classmethod + def decode(cls, source: bytes) -> Descriptor: + bounded_source(source) + try: + obj = json.loads(source) + if set(obj) != set(cls.__dataclass_fields__): + raise ValueError + if not isinstance(obj["allowed_created"], list): + raise ValueError + obj["allowed_created"] = tuple(obj["allowed_created"]) + if not isinstance(obj["revocation_scopes"], list): + raise ValueError + obj["revocation_scopes"] = tuple(obj["revocation_scopes"]) + result = cls(**obj) + identifiers = ( + result.step, + result.owner, + result.adapter, + result.lane, + result.target_parameter, + *result.allowed_created, + ) + if result.version != "warden.case.execution.v1" or not all( + isinstance(v, str) and IDENTIFIER.fullmatch(v) for v in identifiers + ): + raise ValueError + if not DIGEST.fullmatch(result.adapter_source) or result.actor not in ( + "human", + "agent", + ): + raise ValueError + if ( + result.effect not in ("read", "change", "mint") + or type(result.retry_safe) is not bool + ): + raise ValueError + scopes = set(result.revocation_scopes) + if ( + len(scopes) != len(result.revocation_scopes) + or not scopes <= {"parent-session", "broker-grant"} + or "parent-session" not in scopes + ): + raise ValueError + if result.effect == "mint" and "broker-grant" not in scopes: + raise ValueError + if len(result.allowed_created) > 32: + raise ValueError + if len(set(result.allowed_created)) != len(result.allowed_created): + raise ValueError + return result + except (ValueError, TypeError, KeyError): + raise Refused("invalid closed execution descriptor") from None + + +@dataclass(frozen=True, slots=True) +class OwnerResult: + outcome: Outcome + evidence: str + created: tuple[str, ...] = () + revoked: bool = False + session_revocation_evidence: str | None = None + grant_revocation_evidence: str | None = None + attempt_id: str = "" + operation: str = "" + + +@dataclass(frozen=True, slots=True) +class Invocation: + step: str + owner: str + target: str + actor: str + principal: str + lane: str + pins: Pins + attempt_id: str = "" + operation: str = "admission" + tenant: str = "" + realm: str = "" + + +INTENT_SCHEMA = "warden.case.original-intent.v1" + + +@dataclass(frozen=True, slots=True) +class OriginalIntent: + """Durable record of the original mutating dispatch, written before dispatch. + + Binds the original Invocation (attempt, step, owner, target, actor, principal, + lane), all pins and the admitted adapter source. Current admit leaves tenant + and realm empty; their reserved fields are not admitted provenance. This is + local dispatch correspondence, not owner proof of creation. Owners still + authenticate original created-only provenance independently. + """ + + binding: str + invocation: Invocation + source: str + + def validate(self) -> None: + """Fixed bounded metadata only; tenant/realm are not authority claims. + + Current admit leaves tenant/realm empty. Nonempty future coordinates need + separate owner provenance; serializability alone must never supply it. + """ + if ( + type(self.invocation) is not Invocation + or type(self.invocation.pins) is not Pins + ): + raise Refused("exact original invocation required") + i = self.invocation + fields = (i.step, i.owner, i.target, i.principal, i.lane, i.attempt_id) + if ( + not all( + type(value) is str and IDENTIFIER.fullmatch(value) for value in fields + ) + or type(i.actor) is not str + or i.actor not in ("human", "agent") + or type(i.operation) is not str + or i.operation not in ("run", "rollback") + or not all( + type(value) is str and (not value or IDENTIFIER.fullmatch(value)) + for value in (i.tenant, i.realm) + ) + or type(self.source) is not str + or not DIGEST.fullmatch(self.source) + or type(self.binding) is not str + or self.binding != f"{i.step}:{i.target}" + ): + raise Refused("invalid original intent metadata") + if set(Invocation.__dataclass_fields__) != { + "step", + "owner", + "target", + "actor", + "principal", + "lane", + "pins", + "attempt_id", + "operation", + "tenant", + "realm", + }: + raise Refused("original invocation field contract changed") + if not all( + type(v) is str and DIGEST.fullmatch(v) + for v in ( + i.pins.case, + i.pins.case_type, + i.pins.adapter, + i.pins.catalog, + i.pins.acceptance, + i.pins.run_sheet, + ) + ): + raise Refused("invalid original intent pins") + + def payload(self) -> str: + self.validate() + return json.dumps( + {"schema": INTENT_SCHEMA, "binding": self.binding, "source": self.source} + | asdict(self.invocation), + separators=(",", ":"), + sort_keys=True, + ) + + @classmethod + def decode(cls, raw: str) -> OriginalIntent: + def unique(pairs): + result = {} + for key, value in pairs: + if key in result: + raise Refused("duplicate original intent field") + result[key] = value + return result + + try: + if type(raw) is not str or len(raw.encode()) > MAX_SOURCE_BYTES: + raise Refused("original intent input exceeds bound") + obj = json.loads(raw, object_pairs_hook=unique) + fields = { + "step", + "owner", + "target", + "actor", + "principal", + "lane", + "pins", + "attempt_id", + "operation", + "tenant", + "realm", + } + if ( + type(obj) is not dict + or set(obj) != fields | {"schema", "binding", "source"} + or obj["schema"] != INTENT_SCHEMA + ): + raise Refused("unknown original intent schema") + inv = {k: obj[k] for k in fields} + inv["pins"] = Pins(**inv["pins"]) + result = cls(obj["binding"], Invocation(**inv), obj["source"]) + result.validate() + return result + except (TypeError, ValueError, KeyError): + raise Refused("invalid original intent record") from None + + +@dataclass(frozen=True, slots=True) +class ReconcileContext: + """Original intent and the new reconciliation attempt, kept separate.""" + + original: OriginalIntent + attempt: Invocation + + +@dataclass(frozen=True, slots=True) +class Adapter: + reference: str + source: str + invoke: Callable[[Invocation], OwnerResult] + verify: Callable[[Invocation, OwnerResult], bool] + rollback: Callable[[Invocation, tuple[str, ...]], OwnerResult] | None = None + reconcile: Callable[[Invocation], OwnerResult] | None = None + reconcile_original: Callable[[ReconcileContext], OwnerResult] | None = None + + +class Journal: + """Private SQLite journal plus process lock; transaction commits before dispatch.""" + + def __init__(self, directory: Path): + directory.mkdir(mode=0o700, parents=True, exist_ok=True) + if ( + directory.is_symlink() + or directory.stat().st_uid != os.getuid() + or directory.stat().st_mode & 0o077 + ): + raise Refused("journal directory is not private") + self.directory = directory + for name in ("journal.sqlite", "execution.lock"): + path = directory / name + fd = os.open(path, os.O_CREAT | os.O_RDWR | os.O_NOFOLLOW, 0o600) + try: + if os.fstat(fd).st_uid != os.getuid() or os.fstat(fd).st_mode & 0o077: + raise Refused("journal file is not private") + finally: + os.close(fd) + self.db = sqlite3.connect(directory / "journal.sqlite", isolation_level=None) + self.db.execute("PRAGMA synchronous=FULL") + self.db.execute( + "CREATE TABLE IF NOT EXISTS receipts (seq INTEGER PRIMARY KEY, binding TEXT NOT NULL, payload TEXT NOT NULL)" + ) + self.db.execute( + "CREATE TABLE IF NOT EXISTS intents (seq INTEGER PRIMARY KEY, attempt_id TEXT NOT NULL UNIQUE, binding TEXT NOT NULL, payload TEXT NOT NULL)" + ) + + def original_intent( + self, binding: str, current: Invocation, source: str + ) -> OriginalIntent: + """Latest original run intent for binding, refused unless it matches exactly. + + Missing or mismatched records refuse; the step stays uncertain. + """ + row = self.db.execute( + "SELECT attempt_id,payload FROM intents WHERE binding=? ORDER BY seq DESC LIMIT 1", + (binding,), + ).fetchone() + if row is None: + raise Refused("original intent record missing; step stays uncertain") + intent = OriginalIntent.decode(row[1]) + o = intent.invocation + if row[0] != o.attempt_id: + raise Refused("original intent attempt identity differs") + if intent.binding != binding or o.operation != "run": + raise Refused("original intent binding mismatch") + if o.pins != current.pins: + raise Refused("original intent pins mismatch") + if intent.source != source: + raise Refused("original intent source mismatch") + for name in ( + "step", + "owner", + "target", + "actor", + "principal", + "lane", + "tenant", + "realm", + ): + if getattr(o, name) != getattr(current, name): + raise Refused(f"original intent {name} mismatch") + # Match the persisted original running receipt. An orphan context or a + # payload from another attempt cannot become the current lookup key. + receipts = self.db.execute( + "SELECT payload FROM receipts WHERE binding=? ORDER BY seq DESC LIMIT ?", + (binding, MAX_HISTORY + 1), + ).fetchall() + if len(receipts) > MAX_HISTORY: + raise Refused("original intent receipt history exceeds bound") + matched = False + try: + for (raw,) in receipts: + if type(raw) is not str or len(raw.encode()) > MAX_SOURCE_BYTES: + raise Refused("original receipt input exceeds bound") + payload = json.loads(raw) + if ( + payload.get("attempt_id") != o.attempt_id + or payload.get("operation") != "run" + ): + continue + last = payload.get("receipts", [])[-1] + if last.get("outcome") != "running": + continue + if ( + payload.get("journal_schema") != "warden.case.step-journal.v1" + or payload.get("step_id") != o.step + or payload.get("owner") != o.owner + or payload.get("pins") != asdict(o.pins) + ): + raise Refused("original running receipt binding differs") + matched = True + break + except (TypeError, ValueError, KeyError, IndexError, AttributeError): + raise Refused("invalid original running receipt") from None + if not matched: + raise Refused("original running receipt missing; no backfill") + return intent + + @contextmanager + def locked(self): + with (self.directory / "execution.lock").open("rb") as fh: + try: + fcntl.flock(fh, fcntl.LOCK_EX | fcntl.LOCK_NB) + except BlockingIOError: + raise Refused("case execution already active") from None + try: + yield + finally: + fcntl.flock(fh, fcntl.LOCK_UN) + + def load(self, binding: str, initial: StepState) -> StepState: + row = self.db.execute( + "SELECT payload FROM receipts WHERE binding=? ORDER BY seq DESC LIMIT 1", + (binding,), + ).fetchone() + if row is None: + return initial + obj = json.loads(row[0]) + expected = { + "step_id", + "owner", + "pins", + "allowed_created", + "retry_safe", + "receipts", + "journal_schema", + "evidence_digest", + "session_revoked", + "session_revocation_digest", + "grant_revocation_digest", + "attempt_id", + "operation", + } + if ( + set(obj) != expected + or obj["journal_schema"] != "warden.case.step-journal.v1" + ): + raise Refused("unknown journal receipt schema") + state = StepState( + obj["step_id"], + obj["owner"], + Pins(**obj["pins"]), + tuple(obj["allowed_created"]), + obj["retry_safe"], + tuple( + Receipt(r["attempt"], Outcome(r["outcome"]), tuple(r["created"])) + for r in obj["receipts"] + ), + ) + if ( + state.step_id, + state.owner, + state.pins, + state.allowed_created, + state.retry_safe, + ) != ( + initial.step_id, + initial.owner, + initial.pins, + initial.allowed_created, + initial.retry_safe, + ): + raise Refused("journal pins changed") + return state + + def save( + self, + binding: str, + state: StepState, + evidence: OwnerResult | None = None, + invocation: Invocation | None = None, + intent: OriginalIntent | None = None, + ): + if intent is not None: + if type(intent) is not OriginalIntent: + raise Refused("exact original intent required") + intent.validate() + expected = ( + Outcome.RUNNING + if intent.invocation.operation == "run" + else Outcome.ROLLBACK_RUNNING + ) + if ( + intent.binding != binding + or intent.invocation != invocation + or intent.invocation.pins != state.pins + or not state.last + or state.last.outcome is not expected + ): + raise Refused("original intent and running receipt differ") + self.db.execute("BEGIN IMMEDIATE") + try: + if intent is not None: + self.db.execute( + "INSERT INTO intents(attempt_id,binding,payload) VALUES (?,?,?)", + (intent.invocation.attempt_id, binding, intent.payload()), + ) + self.db.execute( + "INSERT INTO receipts(binding,payload) VALUES (?,?)", + ( + binding, + json.dumps( + asdict(state) + | { + "journal_schema": "warden.case.step-journal.v1", + "attempt_id": invocation.attempt_id if invocation else None, + "operation": invocation.operation if invocation else None, + "evidence_digest": digest(evidence.evidence.encode()) + if evidence + else None, + "session_revoked": evidence.revoked if evidence else None, + "session_revocation_digest": digest( + evidence.session_revocation_evidence.encode() + ) + if evidence and evidence.session_revocation_evidence + else None, + "grant_revocation_digest": digest( + evidence.grant_revocation_evidence.encode() + ) + if evidence and evidence.grant_revocation_evidence + else None, + }, + separators=(",", ":"), + ), + ), + ) + self.db.execute("COMMIT") + except BaseException: + self.db.execute("ROLLBACK") + raise + + +def _catalog_lane(catalog_source: bytes, descriptor: Descriptor): + """KERNEL-2: the descriptor's lane must be an active, fitting catalog entry. + + catalog-routing-acceptance still covers the whole catalog digest; this + ties the one lane the descriptor names to an entry inside that digest. + """ + from .routing.catalog import CatalogError, _parse_entry + + try: + raw = yaml.safe_load(catalog_source) + items = raw["entries"] + if not isinstance(items, list): + raise TypeError + entries = {} + for index, item in enumerate(items): + parsed = _parse_entry(item, index) + if parsed.id in entries: + raise ValueError + entries[parsed.id] = parsed + except (yaml.YAMLError, CatalogError, KeyError, TypeError, ValueError): + raise Refused("invalid catalog source shape") from None + entry = entries.get(descriptor.lane) + if entry is None: + raise Refused("descriptor lane is not in the catalog") + if not entry.is_active: + raise Refused("descriptor lane is not active in the catalog") + if entry.delegation is None: + raise Refused("descriptor lane has no catalog delegation record") + if not entry.is_graded: + raise Refused("descriptor lane carries no risk grade") + if descriptor.owner not in entry.owner_repo.split("/"): + raise Refused("descriptor owner does not fit catalog lane owner_repo") + if descriptor.effect in ("mint", "change") and entry.risk != "high": + raise Refused("catalog lane risk does not cover the descriptor effect") + return entry, entries + + +def _attended_admission(descriptor, actor, entry, entries): + from .routing.models import attended_lane_issue + + if (actor != "human" or "WARDEN_AGENT_ID" in os.environ + or "parent-session" not in descriptor.revocation_scopes): + raise Refused("attended step requires a human parent session") + issue = attended_lane_issue(entry, entries.get) + if issue: + raise Refused(issue[1]) + + +def _step_mode(case_type: dict, case: dict, descriptor: Descriptor) -> str: + """KERNEL-3: warden, attended or owner, from the type step's own text.""" + steps = [ + step + for step in case_type.get("follow_through") or [] + if isinstance(step, dict) and step.get("id") == descriptor.step + ] + step = steps[0] if len(steps) == 1 else {} + executes, attended = step.get("warden_executes"), step.get("attended") + if ( + type(executes) is not bool + or type(attended) is not bool + or (executes and attended) + or not isinstance(step.get("lane"), str) + ): + raise Refused("type step does not declare how the kernel dispatches it") + owner = step.get("owner") + if isinstance(owner, str) and re.fullmatch(r"\{[a-z_]+\}", owner): + owner = (case.get("parameters") or {}).get(owner[1:-1]) + if step["lane"] != descriptor.lane or owner != descriptor.owner: + raise Refused("type step lane or owner disagrees with descriptor") + return "warden" if executes else "attended" if attended else "owner" + + +class Executor: + """Injected fixed owner adapters and provenance verifier; none ship by default. + + evidence verifier must authenticate owner custody/provenance or explicitly + attest attended manual evidence; a bare JSON reference is never sufficient. + It receives purpose, exact source digest, and named evidence reference. + """ + + def __init__( + self, + journal: Journal, + adapters: dict[str, Adapter], + evidence_verifier: Callable[[str, str, str], bool], + *, + browser_inputs: BrowserInputs | None = None, + ): + self.journal, self.adapters, self.evidence_verifier = ( + journal, + dict(adapters), + evidence_verifier, + ) + self._admitted = {} + self.browser_inputs = browser_inputs + + def admit( + self, + case_source: bytes, + type_source: bytes, + descriptor_source: bytes, + catalog_source: bytes, + *, + principal: str, + actor: str, + lane: str, + acceptance_evidence: str, + materialization_evidence: str, + descriptor_evidence: str, + session_evidence: str, + ) -> tuple[Descriptor, Invocation, StepState]: + for source in (case_source, type_source, descriptor_source, catalog_source): + bounded_source(source) + descriptor = Descriptor.decode(descriptor_source) + try: + case = yaml.safe_load(case_source)["spec"] + case_type = yaml.safe_load(type_source)["spec"] + if not isinstance(case, dict) or not isinstance(case_type, dict): + raise ValueError + except (yaml.YAMLError, KeyError, TypeError, ValueError): + raise Refused("invalid case or type source shape") from None + if case.get("status") != "materialized" or not case.get("acceptance"): + raise Refused("case is not accepted and structurally materialized") + if case_type.get("status") not in ("acknowledged", "implemented"): + raise Refused("type is not acknowledged") + if case.get("type") != f"{case_type['id']}@{case_type['version']}": + raise Refused("case type pin mismatch") + if case_type.get("execution_descriptors", {}).get(descriptor.step) != digest( + descriptor_source + ): + raise Refused( + "type has no exact acknowledged machine descriptor; plan only" + ) + for purpose, source, evidence in ( + ("case-acceptance", digest(case_source), acceptance_evidence), + ( + "ops-mason-materialization", + digest(case_source), + materialization_evidence, + ), + ( + "owner-descriptor-acceptance:" + descriptor.owner, + digest(descriptor_source), + descriptor_evidence, + ), + ("type-machine-seam-acceptance", digest(type_source), descriptor_evidence), + ("catalog-routing-acceptance", digest(catalog_source), descriptor_evidence), + ( + f"caller-session:{actor}:{principal}:{lane}", + digest(case_source), + session_evidence, + ), + ): + if ( + not isinstance(evidence, str) + or not IDENTIFIER.fullmatch(evidence) + or self._verify_evidence(purpose, source, evidence) is not True + ): + raise Refused("independent owner evidence not verified") + if ( + not IDENTIFIER.fullmatch(principal) + or actor != descriptor.actor + or lane != descriptor.lane + ): + raise Refused("principal actor or lane mismatch") + if descriptor.effect == "mint" and ( + actor != "human" or os.getenv("WARDEN_AGENT_ID") + ): + raise Refused("agent mint is forbidden") + entry, entries = _catalog_lane(catalog_source, descriptor) + mode = _step_mode(case_type, case, descriptor) + if mode == "attended": + _attended_admission(descriptor, actor, entry, entries) + if mode == "owner" and ( + entry.effective_delegation.mode != "native" + or entry.owner_repo != descriptor.owner + or entry.warden_executes + or descriptor.effect == "mint" + ): + raise Refused("owner step lane is not the owner's native path") + parameters = case.get("parameters", {}) + declared = { + item["name"]: item + for item in case_type.get("parameters", []) + if isinstance(item, dict) and isinstance(item.get("name"), str) + } + if ( + not isinstance(parameters, dict) + or descriptor.target_parameter not in declared + ): + raise Refused("target parameter is not declared by acknowledged type") + if case_type.get("id") == "oidc-browser-relying-party": + if type(self.browser_inputs) is not BrowserInputs: + raise Refused("browser metadata validator is not configured") + target = self.browser_inputs.target( + yaml.safe_load(case_source), + yaml.safe_load(type_source), + descriptor.target_parameter, + ) + else: + for name in parameters: + definition = declared.get(name) + if ( + definition is None + or definition.get("sensitivity") not in ("public", "operational") + or definition.get("case_settable") is False + ): + raise Refused("unknown, fixed or secret-sensitivity case parameter") + target = parameters.get(descriptor.target_parameter) + if not isinstance(target, str) or not IDENTIFIER.fullmatch(target): + raise Refused("target must be exact case metadata") + adapter = self.adapters.get(descriptor.adapter) + if adapter is None or (adapter.reference, adapter.source) != ( + descriptor.adapter, + descriptor.adapter_source, + ): + raise Refused("unknown adapter or owner source pin") + sheet = json.dumps( + [ + descriptor.step, + descriptor.owner, + target, + actor, + principal, + lane, + acceptance_evidence, + materialization_evidence, + descriptor_evidence, + session_evidence, + ], + separators=(",", ":"), + ).encode() + pins = Pins( + digest(case_source), + digest(type_source), + digest(descriptor_source), + digest(catalog_source), + digest(acceptance_evidence.encode()), + digest(sheet), + ) + invocation = Invocation( + descriptor.step, descriptor.owner, target, actor, principal, lane, pins + ) + initial = StepState( + descriptor.step, + descriptor.owner, + pins, + descriptor.allowed_created, + descriptor.retry_safe, + ) + # Keep the mode and exact authenticated catalog bytes with this admission; + # dispatch must not substitute a later ambient catalog or lose its mode. + self._admitted[invocation] = ( + descriptor, initial, session_evidence, mode, catalog_source + ) + return descriptor, invocation, initial + + def _result( + self, adapter: Adapter, invocation: Invocation, result: OwnerResult + ) -> OwnerResult: + if type(result) is not OwnerResult or not isinstance(result.outcome, Outcome): + raise Refused("unknown owner result shape") + if type(result.created) is not tuple or not all( + isinstance(v, str) and IDENTIFIER.fullmatch(v) for v in result.created + ): + raise Refused("owner created-object result invalid") + if not isinstance(result.evidence, str) or not IDENTIFIER.fullmatch( + result.evidence + ): + raise Refused("owner evidence identifier invalid") + if adapter.verify(invocation, result) is not True or result.revoked is not True: + raise Refused("owner result or session revocation unverified") + if (result.attempt_id, result.operation) != ( + invocation.attempt_id, + invocation.operation, + ): + raise Refused("owner receipt belongs to another attempt") + descriptor = self._admitted[ + replace(invocation, attempt_id="", operation="admission") + ][0] + proof_fields = { + "parent-session": ( + f"session-revocation:{invocation.lane}:{invocation.operation}:{invocation.attempt_id}", + result.session_revocation_evidence, + ), + "broker-grant": ( + f"grant-revocation:{invocation.lane}:{invocation.operation}:{invocation.attempt_id}", + result.grant_revocation_evidence, + ), + } + proofs = [proof_fields[scope] for scope in descriptor.revocation_scopes] + for purpose, evidence in proofs: + if ( + not isinstance(evidence, str) + or not IDENTIFIER.fullmatch(evidence) + or self._verify_evidence(purpose, invocation.pins.case, evidence) + is not True + ): + raise Refused("independent grant or parent-session revocation absent") + return result + + def _verify_evidence(self, purpose, source, evidence): + try: + with contained_output(): + return self.evidence_verifier(purpose, source, evidence) is True + except Exception: + raise Refused("independent evidence verification failed") from None + + def _admission( + self, descriptor: Descriptor, invocation: Invocation, initial: StepState + ): + if self._admitted.get(invocation, ())[:2] != (descriptor, initial): + raise Refused("execution requires verified admission on this executor") + adapter = self.adapters.get(descriptor.adapter) + if adapter is None or (adapter.reference, adapter.source) != ( + descriptor.adapter, + descriptor.adapter_source, + ): + raise Refused("adapter changed after admission") + _, _, session, mode, catalog_source = self._admitted[invocation] + if ( + self._verify_evidence( + f"caller-session:{invocation.actor}:{invocation.principal}:{invocation.lane}", + invocation.pins.case, + session, + ) + is not True + ): + raise Refused("caller session no longer verified") + if descriptor.effect == "mint" and os.getenv("WARDEN_AGENT_ID"): + raise Refused("agent mint is forbidden") + if mode == "attended": + entry, entries = _catalog_lane(catalog_source, descriptor) + _attended_admission(descriptor, invocation.actor, entry, entries) + return adapter + + def _attempt(self, invocation: Invocation, state: StepState, operation: str): + identity = json.dumps( + [asdict(invocation.pins), operation, state.last.attempt, uuid.uuid4().hex], + separators=(",", ":"), + ).encode() + return replace( + invocation, + attempt_id="attempt-" + hashlib.sha256(identity).hexdigest(), + operation=operation, + ) + + def run( + self, + descriptor: Descriptor, + invocation: Invocation, + initial: StepState, + *, + retry=False, + ) -> StepState: + adapter = self._admission(descriptor, invocation, initial) + with self.journal.locked(): + binding = f"{invocation.step}:{invocation.target}" + state = self.journal.load(binding, initial) + recovered = recover(state) + if recovered != state: + self.journal.save(binding, recovered) + state = recovered + if len(state.receipts) > MAX_HISTORY - 4: + raise Refused("bounded journal history requires owner archival") + running = begin(state, invocation.pins, retry=retry) + dispatched = self._attempt(invocation, running, "run") + self.journal.save( + binding, + running, + invocation=dispatched, + intent=OriginalIntent(binding, dispatched, adapter.source), + ) + evidence = None + try: + with contained_output(): + result = self._result( + adapter, dispatched, adapter.invoke(dispatched) + ) + completed = finish( + running, invocation.pins, result.outcome, created=result.created + ) + evidence = result + except Exception: + completed = finish(running, invocation.pins, Outcome.UNCERTAIN) + self.journal.save(binding, completed, evidence, dispatched) + return completed + + def rollback( + self, descriptor: Descriptor, invocation: Invocation, initial: StepState + ) -> StepState: + adapter = self._admission(descriptor, invocation, initial) + with self.journal.locked(): + binding = f"{invocation.step}:{invocation.target}" + state = self.journal.load(binding, initial) + if recover(state) != state: + self.journal.save(binding, recover(state)) + raise Refused("lost rollback result requires owner reconciliation") + if adapter.rollback is None: + raise Refused("owner has no reviewed undo adapter") + if len(state.receipts) > MAX_HISTORY - 4: + raise Refused("bounded journal history requires owner archival") + running = begin_rollback(state, invocation.pins) + dispatched = self._attempt(invocation, running, "rollback") + self.journal.save( + binding, + running, + invocation=dispatched, + intent=OriginalIntent(binding, dispatched, adapter.source), + ) + evidence = None + try: + with contained_output(): + result = self._result( + adapter, + dispatched, + adapter.rollback(dispatched, running.last.created), + ) + verified = result.outcome is Outcome.ROLLED_BACK and not result.created + evidence = result if verified else None + except Exception: + verified = False + completed = finish_rollback(running, invocation.pins, verified=verified) + self.journal.save(binding, completed, evidence, dispatched) + return completed + + def reconcile( + self, descriptor: Descriptor, invocation: Invocation, initial: StepState + ) -> StepState: + """Explicit owner-native state inspection; never redispatches the mutation.""" + adapter = self._admission(descriptor, invocation, initial) + if adapter.reconcile is None and adapter.reconcile_original is None: + raise Refused("owner reconciliation adapter absent") + with self.journal.locked(): + binding = f"{invocation.step}:{invocation.target}" + state = recover(self.journal.load(binding, initial)) + if not state.last or state.last.outcome not in ( + Outcome.UNCERTAIN, + Outcome.FAILED_AFTER, + ): + raise Refused("no uncertain mutation to reconcile") + if len(state.receipts) > MAX_HISTORY - 4: + raise Refused("bounded journal history requires owner archival") + state = replace( + state, + receipts=( + *state.receipts, + Receipt(len(state.receipts) + 1, Outcome.UNCERTAIN), + ), + ) + original = ( + self.journal.original_intent(binding, invocation, adapter.source) + if adapter.reconcile_original is not None + else None + ) + dispatched = self._attempt(invocation, state, "reconcile") + self.journal.save(binding, state, invocation=dispatched) + with contained_output(): + result = self._result( + adapter, + dispatched, + adapter.reconcile_original(ReconcileContext(original, dispatched)) + if original is not None + else adapter.reconcile(dispatched), + ) + if result.outcome not in ( + Outcome.NO_CHANGE, + Outcome.SUCCESS, + Outcome.FAILED_BEFORE, + ): + raise Refused("owner did not reconcile target state") + # Validate created-object scope using the same closed result reducer. + finish( + begin( + StepState( + state.step_id, + state.owner, + state.pins, + state.allowed_created, + state.retry_safe, + ), + state.pins, + ), + state.pins, + result.outcome, + created=result.created, + ) + reconciled = StepState( + state.step_id, + state.owner, + state.pins, + state.allowed_created, + state.retry_safe, + ( + *state.receipts, + Receipt(len(state.receipts) + 1, result.outcome, result.created), + ), + ) + self.journal.save(binding, reconciled, result, dispatched) + return reconciled + + +class OwnerInterrupted(KeyboardInterrupt): + """Sanitized interruption preserves persisted running/crash recovery semantics.""" + + def __init__(self): + super().__init__("owner execution interrupted") + + +class _Discard: + """Never retain or forward adapter output, including unbounded malicious text.""" + + def write(self, text): + return len(text) + + def flush(self): + pass diff --git a/docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-fixes-r1/candidate/src/warden/case_type.py b/docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-fixes-r1/candidate/src/warden/case_type.py new file mode 100644 index 0000000..1454cd3 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-fixes-r1/candidate/src/warden/case_type.py @@ -0,0 +1,173 @@ +"""SecurityCaseType follow-through check (WARDEN-WP-0044-T01). + +Checks the ``follow_through`` section of a NetKingdom SecurityCaseType against +the rules ops-warden needs before it can plan a run (canon +``security-case-types_v0.1`` section on follow_through; ops-warden review +``history/2026-10-08-oidc-rp-follow-through-review.md``, R1-R3, R5-R7). + +Two kinds of finding: + +* ``schema`` — the type itself is wrong; net-kingdom fixes the type. +* ``catalog`` — the type is right but a lane it names is not yet a reviewed, + active catalog entry; the lane's owner supplies it. ``warden case plan`` + (T02) refuses to plan while any catalog finding remains. + +Read-only: it parses YAML and the catalog, executes nothing, and never handles a +secret value. +""" +from __future__ import annotations + +from dataclasses import dataclass +from pathlib import Path +from typing import Iterable, List, Optional + +import yaml + +from warden.routing.catalog import Catalog +from warden.routing.models import attended_lane_issue + +VALUE_HANDLING = ("none", "generated_in_custody") +REQUIRED_STEP_FIELDS = ("id", "owner", "entry_point", "auth", "warden_executes", "attended", "value_handling") + + +@dataclass(frozen=True) +class Finding: + kind: str # "schema" | "catalog" + code: str + where: str + message: str + + def as_dict(self) -> dict: + return {"kind": self.kind, "code": self.code, "where": self.where, "message": self.message} + + +@dataclass(frozen=True) +class FollowThroughReport: + type_id: str + version: str + findings: List[Finding] + attended_lanes: List[str] # one attended session per lane, in step order + warden_steps: List[str] + + @property + def schema_ok(self) -> bool: + return not any(f.kind == "schema" for f in self.findings) + + @property + def plannable(self) -> bool: + return not self.findings + + def as_dict(self) -> dict: + return { + "type": self.type_id, + "version": self.version, + "schema_ok": self.schema_ok, + "plannable": self.plannable, + "warden_steps": self.warden_steps, + "attended_sessions": self.attended_lanes, + "findings": [f.as_dict() for f in self.findings], + } + + +def load_type(path: Path) -> dict: + with open(path) as fh: + doc = yaml.safe_load(fh) + if not isinstance(doc, dict) or doc.get("kind") != "SecurityCaseType": + raise ValueError(f"{path}: not a SecurityCaseType document") + return doc + + +def _bool(step: dict, key: str) -> Optional[bool]: + value = step.get(key) + return value if isinstance(value, bool) else None + + +def check_follow_through(doc: dict, catalog: Optional[Catalog] = None) -> FollowThroughReport: + spec = doc.get("spec") or {} + steps = spec.get("follow_through") or [] + findings: List[Finding] = [] + attended_lanes: List[str] = [] + warden_steps: List[str] = [] + + def schema(code: str, where: str, message: str) -> None: + findings.append(Finding("schema", code, where, message)) + + if not steps: + schema("FT-EMPTY", "follow_through", "type declares no follow-through steps") + + seen: set = set() + for index, step in enumerate(steps): + sid = step.get("id") if isinstance(step, dict) else None + where = f"follow_through[{sid or index}]" + if not isinstance(step, dict): + schema("FT-SHAPE", where, "step is not a mapping") + continue + for key in REQUIRED_STEP_FIELDS: + if key not in step or step[key] in (None, ""): + schema("FT-FIELD", where, f"missing required field '{key}'") + if sid in seen: + schema("FT-DUP", where, "duplicate step id") + seen.add(sid) + + executes = _bool(step, "warden_executes") + attended = _bool(step, "attended") + for key, value in (("warden_executes", executes), ("attended", attended)): + if key in step and value is None: + schema("FT-BOOL", where, f"'{key}' must be a boolean") + + # R3: values never pass through the run. + handling = step.get("value_handling") + if handling is not None and handling not in VALUE_HANDLING: + schema("FT-VALUE", where, f"value_handling '{handling}' not in {list(VALUE_HANDLING)}") + if handling == "generated_in_custody" and attended: + schema("FT-VALUE", where, "a generated-in-custody step may not run in an attended session") + + # Attended steps: justified, and never executed by warden itself. + if attended: + if not step.get("attended_justification"): + schema("FT-ATTEND", where, "attended step carries no attended_justification") + if executes: + schema("FT-ATTEND", where, "a step is either warden_executes or attended, not both") + + # R1: a lane for every step warden executes or groups into a session. + lane = step.get("lane") + if (executes or attended) and not lane: + schema("FT-LANE", where, "warden_executes or attended step names no catalog lane") + if lane and catalog is not None: + entry = catalog.get(lane) + if entry is None: + findings.append(Finding("catalog", "LANE-MISSING", where, f"lane '{lane}' is not in the catalog")) + elif not entry.is_active or not entry.reviewed: + findings.append(Finding("catalog", "LANE-UNREVIEWED", where, f"lane '{lane}' is not active and reviewed")) + elif attended: + issue = attended_lane_issue(entry, catalog.get) + if issue: + findings.append(Finding("catalog", issue[0], where, issue[1])) + + if executes: + warden_steps.append(sid) + if attended and lane and lane not in attended_lanes: + attended_lanes.append(lane) + + # R7: verification never reads a value. + for index, item in enumerate(spec.get("verification") or []): + if not isinstance(item, dict) or item.get("reads_secret_value") is not False: + vid = item.get("id", index) if isinstance(item, dict) else index + schema("VER-VALUE", f"verification[{vid}]", "reads_secret_value must be explicitly false") + + # Every step must be reversible (or explicitly the consumer's to undo). + reversed_ids = {r.get("reverses") for r in spec.get("rollback") or [] if isinstance(r, dict)} + for sid in sorted(s for s in seen if s and s not in reversed_ids): + schema("RB-MISSING", f"follow_through[{sid}]", "no rollback entry reverses this step") + + return FollowThroughReport( + type_id=str(spec.get("id", "")), + version=str(spec.get("version", "")), + findings=findings, + attended_lanes=attended_lanes, + warden_steps=warden_steps, + ) + + +def summarize(findings: Iterable[Finding]) -> str: + return "\n".join(f"[{f.kind}] {f.code} {f.where}: {f.message}" for f in findings) diff --git a/docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-fixes-r1/candidate/src/warden/routing/models.py b/docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-fixes-r1/candidate/src/warden/routing/models.py new file mode 100644 index 0000000..c691b33 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-fixes-r1/candidate/src/warden/routing/models.py @@ -0,0 +1,343 @@ +"""Data model for routing catalog entries. + +A `RouteEntry` is a pointer: it names the owner and the authoritative doc for a +credential need. Only the SSH lane (`warden_executes: true`) may carry an authored +`steps` block and a `cert_command` pattern — every other entry is identifiers and +pointers only (the no-double-source rule, enforced in `catalog.py`). +""" +from __future__ import annotations + +from dataclasses import dataclass, field +from typing import Callable, List, Optional + + +# Risk grade vocabulary (ADR-0007). Grades outside LOW_RISK_GRADES — including +# the "ungraded" default and any value from a newer catalog — are treated as +# high by is_high_risk, so the read-boundary fails safe in both directions. +LOW_RISK_GRADES = frozenset({"standard", "low", "accepted"}) +GRADED_RISK = frozenset({"standard", "low", "accepted", "high", "critical"}) + + +@dataclass +class RotationGuide: + """Structured-but-advisory renewal guidance for a lane (WARDEN-WP-0026 T06). + + Held in the ops-warden registry, never in OpenBao. ``steps`` are authored + advisory prose (screened for secret material like every catalog string) — they + tell an operator *how* to renew, they are not executed here. ``method`` is + ``rotate`` (provider re-mints the same kind of credential) or ``re-establish`` + (regenerate from source, e.g. a new age keypair + re-encrypt). ``automatable`` + is a hint for a future Strand-B executable driver (WARDEN-WP-0027). + """ + method: str # "rotate" | "re-establish" + steps: List[str] + owner: str + automatable: bool = False + + +# Risk classes for agent read-boundary (WARDEN-WP-0026 T04). +# high — recovery escrow, upload tokens, admin PATs, high-spend provider keys. +# Agent identities must not hold raw data-read (metadata/capabilities only). +# standard — ordinary workload secrets (ESO-fed, non-escrow); normal least-privilege. +VALID_RISK = ("standard", "high") + +# Delegation modes (WARDEN-WP-0030). Absence of a block is implicit interim. +# native — intended owner already fronts the lane (route-primary / pointer) +# interim — ops-warden covers a gap; intended_owner + blocked_on required +# permanent — ops-warden is the designed owner of this front door (SSH only today) +VALID_DELEGATION_MODES = ("native", "interim", "permanent") +VALID_WORKLOAD_APPLICABILITY = ("applicable", "not-applicable") + +IMPLICIT_DELEGATION_BLOCKED_ON = ( + "unclassified — no delegation block; treat as a question, not a settlement" +) + + +@dataclass +class Delegation: + """Who should own this front door, and what is missing (WARDEN-WP-0030). + + Pointer-layer only: names the intended owner and the blocker. Does not + restate how that owner will implement their front door. + """ + + mode: str # native | interim | permanent + intended_owner: Optional[str] = None + blocked_on: Optional[str] = None + reviewed: Optional[str] = None + verified: Optional[str] = None + implicit: bool = False + + #: How `reviewed` was established. The distinction exists because a date + #: bumped by editing the entry looks identical to one bumped by re-checking + #: the blocker, and on 2026-08-21 six lanes read as freshly reviewed when + #: only some had actually been re-verified (WARDEN-WP-0033-T05). + #: + #: `asked-and-waiting` deliberately does NOT count as verification: it is the + #: state the secrets-engine lanes sat in for ten days while looking fresh. + VERIFICATION_METHODS = ( + "owner-confirmed", # the intended owner stated the blocker's status + "source-read", # re-derived from the owner's code, canon, or CCR + "asked-and-waiting", # a question is outstanding — NOT verification + "unverified", # carried forward without a check + ) + #: Methods that mean the claim was actually re-established. + VERIFYING_METHODS = ("owner-confirmed", "source-read") + + @property + def is_verified(self) -> bool: + """True only when the blocker was re-established, not merely re-edited.""" + return self.verified in self.VERIFYING_METHODS + + def to_dict(self) -> dict: + return { + "mode": self.mode, + "intended_owner": self.intended_owner, + "blocked_on": self.blocked_on, + "reviewed": self.reviewed, + "verified": self.verified, + "is_verified": self.is_verified, + "implicit": self.implicit, + } + + +@dataclass(frozen=True) +class WorkloadReference: + """Authoritative workload join for a catalog lane (WARDEN-WP-0032). + + Managed deployables use the Repo Manager v1 ``rapp_id``/``name`` tuple. + Independently governed operational workloads use ``name`` plus an owner + declaration reference. An applicable lane whose owner has not published an + identity remains explicitly ``unknown``; it is never inferred from the + credential path or repository name. + """ + + applicability: str # applicable | not-applicable + rapp_id: Optional[str] = None + name: Optional[str] = None + deployable: Optional[str] = None + declaration_ref: Optional[str] = None + reason: Optional[str] = None + unknown_reason: Optional[str] = None + + @property + def resolution(self) -> str: + if self.applicability == "not-applicable": + return "not-applicable" + if self.unknown_reason: + return "unknown" + return "resolved" + + def to_dict(self) -> dict: + return { + "applicability": self.applicability, + "rapp_id": self.rapp_id, + "name": self.name, + "deployable": self.deployable, + "declaration_ref": self.declaration_ref, + "reason": self.reason, + "unknown_reason": self.unknown_reason, + "resolution": self.resolution, + } + + +@dataclass +class RouteEntry: + id: str + title: str + need_keywords: List[str] + owner_repo: str + subsystem: str + warden_executes: bool + wiki_ref: str + canon_ref: str + reviewed: str + status: str # "active" | "draft" + # Explicit workload applicability and authoritative join. Never inferred. + workload_ref: Optional[WorkloadReference] = None + # SSH lane only — None/empty for routed (non-executed) needs. + steps: List[str] = field(default_factory=list) + cert_command: Optional[str] = None + # Structured handoff (WP-0014) — optional, allowed on any lane. These are + # *templates and pointers* the `warden access` assist layer renders (and, for + # exec_capable lanes, proxies). They are NOT authored procedure prose and they + # never carry a secret value — only placeholders (`<...>`) and field names. + # Validation in catalog.py enforces the no-secret-material rule on every one. + auth_method: Optional[str] = None # how the caller authenticates to the owner + path_template: Optional[str] = None # owner-side path with `<...>` placeholders + fetch_command: Optional[str] = None # command skeleton run *as the caller* + exec_capable: bool = False # may `warden access --fetch/--exec` proxy it + policy_ref: Optional[str] = None # flex-auth check the fetch path runs first + # Proxy lane semantics (WP-0014 T4): + # "secret" — read a value (gated by flex-auth secret-read; caller must already + # be authenticated; value transits via inherit-stdout or child env). + # "login" — interactive auth bootstrap (OIDC/MFA). No secret-read gate (you have + # no identity yet), no caller-auth precheck (the point is to get one), + # run interactively as the caller; warden never captures the token. + # "ceremony" — attended owner operation such as Shamir seal/unseal. It is a + # pointer plus approval boundary, never an executable access lane. + lane: str = "secret" + session_lane: Optional[str] = None # attended-change: login lane whose session establishes it + # Owner-native exec front door (WP-0019). When `exec_owner` is set, that subsystem + # (e.g. secrets-engine) provides the PRIMARY way to run a secret-backed command; the + # catalog routes to it and keeps ops-warden's own --fetch/--exec proxy as a transparent + # fallback (route-primary, proxy-fallback). Pointers/templates only — never a value. + exec_owner: Optional[str] = None # subsystem owning the native exec (e.g. secrets-engine) + exec_command: Optional[str] = None # e.g. "secrets-engine exec --catalog -- " + pointer_command: Optional[str] = None # e.g. "secrets-engine route --json" + # Rotation / re-establishment guidance (WP-0026 T06) — advisory, no secret values. + rotation: Optional[RotationGuide] = None + # Agent read-boundary risk class (WP-0026 T04). high → agents use wrap/out/exec only. + # Default is "ungraded", which FAILS SAFE: it is treated as high. Before + # ADR-0007 this defaulted to "standard", so a lane that simply omitted the + # field was silently placed outside the read-boundary (RISK-F-0003) — the + # control was never relaxed by decision, it was never reached. + risk: str = "ungraded" # "standard" | "high" | "ungraded" + # Delegation register (WP-0030). None → implicit interim with unknown owner. + delegation: Optional[Delegation] = None + + @property + def is_active(self) -> bool: + return self.status == "active" + + @property + def is_graded(self) -> bool: + """False when this lane carries no explicit risk grade (ADR-0007).""" + return self.risk in GRADED_RISK + + @property + def is_high_risk(self) -> bool: + """True when this lane is on the agent raw-read deny list (WP-0026 T04). + + Anything not explicitly graded low is high. An ungraded lane, or one + carrying a grade this version does not recognise, is treated as high + rather than waved through — ADR-0007: absence is not a grade. + """ + return self.risk not in LOW_RISK_GRADES + + def risk_for_zone( + self, + *, + effective_zone: str = "unknown", + admission: str = "unknown", + synthetic_only: bool = False, + ) -> str: + """Resolve an absent grade using security-zones_v0.1 section 5.1. + + Explicit grades always win. The sole lower default is a satisfied + ``z0-experimental`` workload proven synthetic-only. Every other zone, + failed/unknown admission, and missing context fails safe to at least + ``high``; z3 reports ``critical`` (which the read boundary treats as + high). Catalog CI still requires explicit grades, so this is the safe + runtime behavior for malformed or newer external catalogs. + """ + if self.is_graded: + return self.risk + if ( + effective_zone == "z0-experimental" + and admission == "satisfied" + and synthetic_only + ): + return "standard" + if effective_zone == "z3-critical" and admission == "satisfied": + return "critical" + return "high" + + @property + def has_rotation(self) -> bool: + """True when this lane carries renewal guidance (WP-0026 T06).""" + return self.rotation is not None + + @property + def vends_secret(self) -> bool: + """True when this lane hands back a rotatable static secret value. + + Rotation guidance (WP-0026 T06) applies to these. It excludes the SSH lane + (short-lived certs — renewal is re-issuance), ``login`` lanes (re-auth, no + stored value), and pure routing pointers with no secret path (tunnel, + principals, emission sinks, policy checks). + """ + if self.warden_executes or self.lane != "secret": + return False + return bool(self.path_template or self.fetch_command or self.exec_owner) + + @property + def has_native_exec(self) -> bool: + """True when an owner-native exec front door is the primary path for this lane.""" + return bool(self.exec_owner and self.exec_command) + + @property + def effective_delegation(self) -> Delegation: + """Declared delegation, or implicit interim with an unknown owner. + + Absence of a ``delegation:`` block is a question (WP-0030), not a + settlement that ops-warden owns the front door. + """ + if self.delegation is not None: + return self.delegation + return Delegation( + mode="interim", + intended_owner=None, + blocked_on=IMPLICIT_DELEGATION_BLOCKED_ON, + reviewed=None, + implicit=True, + ) + + @property + def is_interim(self) -> bool: + """True when this lane is a tracked gap (explicit or implicit).""" + return self.effective_delegation.mode == "interim" + + @property + def has_handoff(self) -> bool: + """True when structured assist fields are present (advisory richness).""" + return any((self.auth_method, self.path_template, self.fetch_command)) + + @property + def resolvable(self) -> bool: + """True when `warden access --fetch` can run this lane with no further input. + + A resolvable lane is active, exec_capable, and its fetch command (with the path + inlined) carries no unresolved ``<...>`` placeholder. Template lanes — like the + generic ``openbao-api-key`` or the ````-parameterized login — are *not* + resolvable until an owner ships concrete names. Lets an automated caller know + whether ``--fetch`` will work *before* attempting it (whynot-design request). + """ + if not (self.is_active and self.exec_capable and self.fetch_command): + return False + blob = f"{self.fetch_command} {self.path_template or ''}" + return "<" not in blob and ">" not in blob + + def match_score(self, tokens: List[str]) -> int: + """Keyword-overlap score against need_keywords, title, and id. + + Pure ranking helper — no I/O, no external calls. + """ + haystack = set(k.lower() for k in self.need_keywords) + haystack.update(self.id.lower().replace("-", " ").split()) + haystack.update(self.title.lower().replace("-", " ").split()) + score = 0 + for tok in tokens: + t = tok.lower() + if t in haystack: + score += 2 + elif any(t in h or h in t for h in haystack): + score += 1 + return score + + +def attended_lane_issue( + entry: RouteEntry, get_entry: Callable[[str], Optional[RouteEntry]] +) -> Optional[tuple[str, str]]: + """Shared planner/executor boundary, using one parsed catalog snapshot.""" + if not entry.is_active or not entry.reviewed: + return ("LANE-UNREVIEWED", f"lane '{entry.id}' is not active and reviewed") + if entry.lane == "attended-change": + session = get_entry(entry.session_lane or "") + if (session is None or not session.is_active or not session.reviewed + or session.lane != "login"): + return ("LANE-KIND", f"attended-change lane '{entry.id}' session_lane " + f"'{entry.session_lane}' does not resolve to an active, reviewed login lane") + elif entry.lane != "login": + return ("LANE-KIND", f"attended step's lane '{entry.id}' is not a login or attended-change lane") + return None diff --git a/docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-fixes-r1/candidate/tests/test_case_1_3_integration.py b/docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-fixes-r1/candidate/tests/test_case_1_3_integration.py new file mode 100644 index 0000000..bb13c09 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-fixes-r1/candidate/tests/test_case_1_3_integration.py @@ -0,0 +1,151 @@ +"""Integration: the real net-kingdom 1.3 candidate against this kernel and catalog. + +Reads the actual 1.3 type, its forgejo example and the committed composition +artifact from net-kingdom, plus this branch's real routing catalog. The descriptor +map and metadata come from those committed files. All authority is fixture-only: +we change the proposed type to acknowledged, change the example case to +materialized and replace its acceptance record, promote draft lanes in memory, +and temporarily install the 1.3 parameter pin. Stub adapters replace native +adapters; an unconditional evidence verifier simulates every acceptance and caller +session. WARDEN_AGENT_ID is removed to simulate a human runtime. No adapter is +dispatched. This proves conditional source admission, not authenticated owner +acceptance, native execution or readiness; unresolved Apps sessions stay refused. +""" +from dataclasses import asdict +import hashlib +import json +import os +from pathlib import Path +from unittest import mock + +import pytest +import yaml + +from warden import case_inputs as inputs +from warden.case_receipt_state import Refused +from warden.case_run import Adapter, Descriptor, Executor, Journal, digest +from warden.case_type import check_follow_through +from warden.routing.catalog import load_catalog + +NK = Path(os.environ.get("WARDEN_TEST_NETKINGDOM_ROOT", + Path(__file__).resolve().parents[2] / "net-kingdom")) +TYPE_DIR = NK / "security-case-type-revisions/1.3/oidc-browser-relying-party" +ARTIFACT = NK / "sso-mfa/k8s/keycape/descriptor-composition-1.3.candidate.json" +CATALOG = Path(__file__).resolve().parents[1] / "registry/routing/catalog.yaml" + +pytestmark = pytest.mark.skipif(not (TYPE_DIR / "type.yaml").is_file() or not ARTIFACT.is_file(), + reason="net-kingdom 1.3 candidate not available") + +DRAFT_LANES = {"keycape-attended-case-change", "platform-secret-delivery", + "consumer-owner-deployment", "apps-consumer-handover"} + + +def encode(desc): # net-kingdom compose-1.2-descriptors.py encode() + return json.dumps(asdict(desc), sort_keys=True, separators=(",", ":")).encode() + + +def type_doc(status="acknowledged"): + doc = yaml.safe_load((TYPE_DIR / "type.yaml").read_bytes()) + doc["spec"]["status"] = status + return doc + + +def catalog_bytes(promote): + raw = yaml.safe_load(CATALOG.read_bytes()) + if promote: + for entry in raw["entries"]: + if entry["id"] in DRAFT_LANES and entry.get("status") == "draft": + entry["status"] = "active" + return yaml.safe_dump(raw).encode() + + +def descriptors(): + art = json.loads(ARTIFACT.read_text()) + out = {} + for row in art["steps"]: + d = dict(row["descriptor"]) + for key in ("allowed_created", "revocation_scopes"): + d[key] = tuple(d[key]) + out[row["step"]] = Descriptor(**d) + return out + + +def pin_13(): + params = type_doc()["spec"]["parameters"] + fp = hashlib.sha256(json.dumps(params, sort_keys=True, separators=(",", ":")).encode()).hexdigest() + return mock.patch.dict(inputs.PARAMETER_PINS, {"1.3": fp}) + + +def unused(*_): + raise AssertionError("admission must not dispatch") + + +def admit(tmp_path, desc, *, promote=True, pinned=True, status="acknowledged"): + case = yaml.safe_load((TYPE_DIR / "examples/forgejo.yaml").read_bytes()) + case["spec"].update(status="materialized", acceptance={ + "at": "2026-10-17", "record": "fixture-only acceptance, not an owner record", + "by": [{"name": "fixture", "role": "owner"}, {"name": "fixture", "role": "founder"}]}) + adapter = Adapter(desc.adapter, desc.adapter_source, unused, unused) + executor = Executor(Journal(tmp_path / desc.step), {adapter.reference: adapter}, + lambda purpose, source, evidence: True, + browser_inputs=inputs.BrowserInputs(NK)) + with mock.patch.dict(os.environ), (pin_13() if pinned else mock.patch.dict({})): + os.environ.pop("WARDEN_AGENT_ID", None) + return executor.admit(yaml.safe_dump(case).encode(), yaml.safe_dump(type_doc(status)).encode(), + encode(desc), catalog_bytes(promote), principal="founder", + actor=desc.actor, lane=desc.lane, + acceptance_evidence="e-acceptance", materialization_evidence="e-mason", + descriptor_evidence="e-descriptor", session_evidence="e-session") + + +def test_artifact_descriptors_are_the_type_map(): + descs = descriptors() + assert len(descs) == 7 + assert {s: digest(encode(d)) for s, d in descs.items()} == type_doc()["spec"]["execution_descriptors"] + + +def test_conditional_admission_keeps_unresolved_apps_sessions_refused(tmp_path): + for step, desc in descriptors().items(): + if step in ("deliver-consumer-secret", "hand-over"): + with pytest.raises(Refused, match="does not resolve to an active, reviewed login lane"): + admit(tmp_path, desc) + else: + admitted, invocation, _ = admit(tmp_path, desc) + assert admitted == desc + assert (invocation.step, invocation.actor, invocation.target) == (step, "human", "forgejo") + +def test_draft_lane_steps_refused_until_promoted(tmp_path): + for step, desc in descriptors().items(): + if desc.lane in DRAFT_LANES: + with pytest.raises(Refused, match="descriptor lane is not active in the catalog"): + admit(tmp_path, desc, promote=False) + else: + admit(tmp_path, desc, promote=False) # mint-secret's lane is already active + + +def test_unpinned_1_3_refused_by_browser_validation(tmp_path): + with pytest.raises(Refused, match="failed exact-version validation"): + admit(tmp_path, descriptors()["mint-secret"], pinned=False) + + +def test_proposed_type_refused(tmp_path): + with pytest.raises(Refused, match="type is not acknowledged"): + admit(tmp_path, descriptors()["mint-secret"], status="proposed") + + +def test_type_check_findings_against_the_real_catalog(tmp_path): + def findings(promote): + path = tmp_path / f"catalog-{promote}.yaml" + path.write_bytes(catalog_bytes(promote)) + report = check_follow_through(type_doc("proposed"), load_catalog(path)) + return {(f.code, f.where) for f in report.findings} + + draft = findings(False) + assert {code for code, _ in draft} == {"LANE-UNREVIEWED"} + assert len(draft) == 6 # six steps use the four draft lanes; mint-secret's lane is active + + promoted = findings(True) + # Only the two railiance-apps lanes remain: their session_lane awaits owner confirmation. + assert {code for code, _ in promoted} == {"LANE-KIND"} + assert len(promoted) == 2 + assert all("deliver-consumer-secret" in where or "hand-over" in where for _, where in promoted) diff --git a/docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-fixes-r1/candidate/tests/test_case_admission_modes.py b/docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-fixes-r1/candidate/tests/test_case_admission_modes.py new file mode 100644 index 0000000..5069491 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-fixes-r1/candidate/tests/test_case_admission_modes.py @@ -0,0 +1,136 @@ +"""KERNEL-2 lane resolution and KERNEL-3 mode-aware admission (fixture only).""" +from dataclasses import asdict, replace +import json + +import pytest +import yaml + +from warden.case_receipt_state import Refused +from warden.case_run import Descriptor, digest +from tests.test_case_run import MINT_STEP, fixture_catalog, lane_entry, make_rig + + +@pytest.fixture +def rig(tmp_path): + return make_rig(tmp_path) + +EVIDENCE = {"acceptance", "materialization", "descriptor", "session"} + + +def admit(rig, *, step=None, entries=None, catalog=None, desc=None, **over): + executor, _, _, sources, kwargs = rig + case, typ, descriptor, _ = sources + if desc is not None: + base = Descriptor.decode(descriptor) + descriptor = json.dumps(asdict(replace(base, **desc))).encode() + doc = yaml.safe_load(typ) + doc["spec"]["execution_descriptors"] = {"mint-secret": digest(descriptor)} + if step is not None: + doc["spec"]["follow_through"] = step + typ = yaml.safe_dump(doc).encode() + if catalog is None: + catalog = fixture_catalog(*(entries or [lane_entry()])) + executor.evidence_verifier = lambda purpose, source, evidence: evidence in EVIDENCE + return executor.admit(case, typ, descriptor, catalog, **{**kwargs, **over}) + + +def mode_step(executes, attended): + return [dict(MINT_STEP, warden_executes=executes, attended=attended)] + + +def refused(message, rig, **kw): + with pytest.raises(Refused, match=message): + admit(rig, **kw) + + +# Positive modes +def test_warden_mode_admits(rig): + assert admit(rig)[1].lane == "issuer-lane" + + +def test_attended_mode_admits_human_parent_session(rig, monkeypatch): + monkeypatch.delenv("WARDEN_AGENT_ID", raising=False) + admitted = admit( + rig, + step=mode_step(False, True), + entries=[dict(lane_entry(), lane="login")], + desc=dict(effect="change", revocation_scopes=("parent-session",)), + ) + assert admitted[1].actor == "human" + + +def test_owner_mode_admits_native_owner_lane(rig): + admitted = admit(rig, step=mode_step(False, False), desc=dict(effect="change")) + assert admitted[1].owner == "owner" + + +# KERNEL-2 +@pytest.mark.parametrize( + "entries, catalog, message", + [ + (None, b"not: [a catalog", "invalid catalog source shape"), + ([lane_entry("other-lane")], None, "descriptor lane is not in the catalog"), + ([lane_entry(status="draft")], None, "descriptor lane is not active in the catalog"), + ([{k: v for k, v in lane_entry().items() if k != "delegation"}], None, + "descriptor lane has no catalog delegation record"), + ([{k: v for k, v in lane_entry().items() if k != "risk"}], None, + "descriptor lane carries no risk grade"), + ([lane_entry(owner="someone-else")], None, + "descriptor owner does not fit catalog lane owner_repo"), + ([lane_entry(risk="standard")], None, + "catalog lane risk does not cover the descriptor effect"), + ], +) +def test_catalog_lane_refusals(rig, entries, catalog, message): + refused(message, rig, entries=entries, catalog=catalog) + + +# KERNEL-3: the step must declare dispatch and agree with the descriptor +@pytest.mark.parametrize( + "step, message", + [ + ([], "type step does not declare how the kernel dispatches it"), + (mode_step(True, True), "type step does not declare how the kernel dispatches it"), + ([{k: v for k, v in MINT_STEP.items() if k != "lane"}], + "type step does not declare how the kernel dispatches it"), + ([dict(MINT_STEP, warden_executes="yes")], + "type step does not declare how the kernel dispatches it"), + ([dict(MINT_STEP, lane="other-lane")], "type step lane or owner disagrees with descriptor"), + ([dict(MINT_STEP, owner="other")], "type step lane or owner disagrees with descriptor"), + ], +) +def test_step_declaration_refusals(rig, step, message): + refused(message, rig, step=step) + + +def test_attended_refuses_agent_session(rig, monkeypatch): + monkeypatch.setenv("WARDEN_AGENT_ID", "agent-1") + refused("attended step requires a human parent session", rig, + step=mode_step(False, True), desc=dict(effect="change")) + + +def test_attended_refuses_agent_actor(rig): + refused("attended step requires a human parent session", rig, + step=mode_step(False, True), desc=dict(effect="change", actor="agent"), + actor="agent") + + +def test_attended_refuses_caller_lane_other_than_step_lane(rig): + refused("principal actor or lane mismatch", rig, + step=mode_step(False, True), desc=dict(effect="change"), lane="login-lane") + + +@pytest.mark.parametrize( + "entry, desc", + [ + (lane_entry(delegation={"mode": "interim", "intended_owner": "owner", + "blocked_on": "fixture", "reviewed": "2026-10-17"}), + dict(effect="change")), + (lane_entry(owner="owner/other"), dict(effect="change")), + (lane_entry(warden_executes=True), dict(effect="change")), + (lane_entry(), {}), # owner step may not be a Warden-brokered mint + ], +) +def test_owner_mode_refusals(rig, entry, desc): + refused("owner step lane is not the owner's native path", rig, + step=mode_step(False, False), entries=[entry], desc=desc) diff --git a/docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-fixes-r1/candidate/tests/test_case_attended_boundary.py b/docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-fixes-r1/candidate/tests/test_case_attended_boundary.py new file mode 100644 index 0000000..ba07b33 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-fixes-r1/candidate/tests/test_case_attended_boundary.py @@ -0,0 +1,113 @@ +"""Synthetic attended admission/dispatch regressions; no owner/native actions.""" +from dataclasses import replace + +import pytest + +from tests.test_case_admission_modes import admit, mode_step +from tests.test_case_run import fixture_catalog, lane_entry, make_rig +from warden.case_receipt_state import Outcome, Refused +from warden.case_type import check_follow_through +from warden.routing.catalog import CatalogError, _parse_entry, load_catalog + + +def attended(rig, entries): + return admit(rig, step=mode_step(False, True), entries=entries, + desc=dict(effect="change", revocation_scopes=("parent-session",))) + + +@pytest.mark.parametrize("operation", ["run", "rollback", "reconcile"]) +@pytest.mark.parametrize("marker", ["agent-1", "", None]) +def test_identity_change_refuses_before_any_write_or_callback(tmp_path, monkeypatch, operation, marker): + monkeypatch.delenv("WARDEN_AGENT_ID", raising=False) + rig = make_rig(tmp_path) + executor = rig[0] + admitted = attended(rig, [dict(lane_entry(), lane="login")]) + executor.evidence_verifier = lambda *_: True + if operation == "rollback": + executor.run(*admitted) + elif operation == "reconcile": + old = executor.adapters[admitted[0].adapter] + def lost(_): + raise RuntimeError("synthetic lost reply") + executor.adapters[old.reference] = replace(old, invoke=lost) + executor.run(*admitted) + calls = [] + old = executor.adapters[admitted[0].adapter] + callback = getattr(old, "invoke" if operation == "run" else operation) + def observed(*args): + calls.append(args) + return callback(*args) + executor.adapters[old.reference] = replace(old, **{ + "invoke" if operation == "run" else operation: observed, + }) + def rows(): + return tuple(executor.journal.db.execute(f"SELECT COUNT(*) FROM {table}").fetchone()[0] + for table in ("intents", "receipts")) + before = rows() + if marker is None: + result = getattr(executor, operation)(*admitted) + assert result.last.outcome is {"run": Outcome.SUCCESS, + "rollback": Outcome.ROLLED_BACK, + "reconcile": Outcome.NO_CHANGE}[operation] + assert len(calls) == 1 + assert rows() != before + return + monkeypatch.setenv("WARDEN_AGENT_ID", marker) + with pytest.raises(Refused, match="attended step requires a human parent session"): + getattr(executor, operation)(*admitted) + assert rows() == before + assert not calls + + +def entries_for(case): + change = dict(lane_entry(), lane="attended-change", session_lane="login-lane") + login = dict(lane_entry("login-lane"), lane="login") + if case == "missing": + return [change] + if case == "draft-login": + login["status"] = "draft" + elif case == "unreviewed-login": + login["reviewed"] = "" + elif case == "wrong-kind-login": + login["lane"] = "secret" + elif case == "draft-change": + change["status"] = "draft" + elif case == "unreviewed-change": + change["reviewed"] = "" + elif case == "missing-session-field": + del change["session_lane"] + elif case in ("secret", "ceremony"): + change["lane"] = case + del change["session_lane"] + return [change, login] + + +@pytest.mark.parametrize("case, code", [ + ("valid", None), ("missing", "LANE-KIND"), ("draft-login", "LANE-KIND"), + ("unreviewed-login", "LANE-KIND"), ("wrong-kind-login", "LANE-KIND"), + ("draft-change", "LANE-UNREVIEWED"), ("unreviewed-change", "LANE-UNREVIEWED"), + ("secret", "LANE-KIND"), ("ceremony", "LANE-KIND"), +]) +def test_attended_change_planner_and_executor_agree(tmp_path, monkeypatch, case, code): + monkeypatch.delenv("WARDEN_AGENT_ID", raising=False) + entries = entries_for(case) + path = tmp_path / "catalog.yaml" + path.write_bytes(fixture_catalog(*entries)) + catalog = load_catalog(path) + step = dict(mode_step(False, True)[0], attended_justification="synthetic test", + entry_point="owner/test", auth="synthetic session", value_handling="none") + doc = {"spec": {"follow_through": [step], "rollback": [{"reverses": step["id"]}]}} + report = check_follow_through(doc, catalog) + assert [f.code for f in report.findings] == ([] if code is None else [code]) + rig = make_rig(tmp_path) + if code: + with pytest.raises(Refused): + attended(rig, entries) + assert rig[0].journal.db.execute("SELECT COUNT(*) FROM receipts").fetchone()[0] == 0 + else: + assert attended(rig, entries)[1].lane == "issuer-lane" + + +def test_attended_change_missing_session_is_parser_rejection(): + with pytest.raises(CatalogError, match="must name session_lane"): + _parse_entry(entries_for("missing-session-field")[0], 0) diff --git a/docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-fixes-r1/evidence/run.py b/docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-fixes-r1/evidence/run.py new file mode 100644 index 0000000..869be8d --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-fixes-r1/evidence/run.py @@ -0,0 +1,12 @@ +import os +from pathlib import Path +import subprocess +import sys +root=Path('/tmp/warden-kernel-fix-r1') +tree=root/os.environ.get('FIX_TEST_TREE', 'ops-warden') +env={'HOME':str(root/'home'),'PATH':'/usr/bin:/bin','PYTHONPATH':str(root/'guard')+':'+str(tree/'src')+':/home/worsch/phase-memory/src','PYTHONDONTWRITEBYTECODE':'1','WARDEN_TEST_NETKINGDOM_ROOT':str(root/'net-kingdom'),'LANG':'C.UTF-8'} +python='/tmp/warden-kernel-review-r1/final/.venv/bin/python' +with (root/sys.argv[1]).open('w') as out: + r=subprocess.run([python,*sys.argv[2:]],cwd=tree,env=env,stdout=out,stderr=subprocess.STDOUT) +print(r.returncode) +sys.exit(r.returncode) diff --git a/docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-fixes-r1/evidence/sitecustomize.py b/docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-fixes-r1/evidence/sitecustomize.py new file mode 100644 index 0000000..fd8113a --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-fixes-r1/evidence/sitecustomize.py @@ -0,0 +1,21 @@ +import sys + +bound_sockets = [] + +def deny_network(event, args): + if event == 'socket.bind': + bound_sockets.append(args[0]) + if event == 'socket.getaddrinfo' and args[0] not in {'localhost', '127.0.0.1', '::1'}: + raise OSError('independent review: network disabled') + if event == 'socket.connect': + address = args[1] + for server in bound_sockets: + try: + bound = server.getsockname() + if address == bound and bound[0] in {'127.0.0.1', '::1'}: + return + except OSError: + pass + raise OSError('independent review: network disabled') + +sys.addaudithook(deny_network) diff --git a/docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-fixes-r1/implementation-r1.json b/docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-fixes-r1/implementation-r1.json new file mode 100644 index 0000000..36eef52 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-fixes-r1/implementation-r1.json @@ -0,0 +1,93 @@ +{ + "schema": "custodian.warden-1.3-kernel-fixes.v1", + "date": "2026-10-17", + "role": "implementation successor; not independent review or owner/native acceptance", + "status": "candidate prepared; no canonical apply/commit/push", + "base_ops_warden": "5abde817775d6304e3cf5a46948717a1606981bf", + "net_kingdom": "001a38ca2aa85155ce4840c4da22aca6e2346171", + "accepted_kernel_1": "117fec9241e7b985dfb187b992e173d0a5dc3700", + "existing_review": { + "path": "/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-review/kernel-review-r1.json", + "sha256": "sha256:43c96da94605082b4924ccd8ef41a1f8737d7e9ada95047ff16415b4614905dc", + "disposition": "retained unchanged; reuse existing KERNEL-1 validation; its initial ambient-lookup boundary deviation remains adverse/unknown and is not counted as compliant success" + }, + "patch": { + "file": "candidate.patch", + "sha256": "sha256:1a3530a1e469fc03c2c29f1b66fa3ba1904e56bf2abcfb242c7a2ed015fb3e74", + "applies_to_base": "git apply --check succeeded against frozen base archive" + }, + "files": { + "src/warden/case_run.py": { + "base": "sha256:e071b485162b2db5c4a9da5821917b7ff81a07980270530ed91e987796591ddd", + "candidate": "sha256:1bc73e30ac718fd75bbaff2a739495fc051aeaec73802ac71f0583cb2d53ab0a" + }, + "src/warden/case_type.py": { + "base": "sha256:3ee1004b7c0ac3975be157408376cf14acc0c1e1e1623d79d3179540040f40ce", + "candidate": "sha256:7b659b8d682863cd28df24025be4b10dd7ca3df724350a22d360c6af9615bca7" + }, + "src/warden/routing/models.py": { + "base": "sha256:2da3bc90c451ed9b66c2d43035951c2a777c2b896201e9a87966a7a8f7ead426", + "candidate": "sha256:b85372b2572a49af62e9534f26a33963b894633c7cd43dd2a28416b8959cba3e" + }, + "tests/test_case_1_3_integration.py": { + "base": "sha256:591cc4e5859e1cebce29912b19b7ac1479bf49373fe04425542d1ecd32c74192", + "candidate": "sha256:4862c5a004075eb18733d354bee7dda3f55e04a6ee8ecb44b76166347c121158" + }, + "tests/test_case_admission_modes.py": { + "base": "sha256:3f5c648cec9e075818a616207aff508bdbc9ae95b100f5b8400a86fcae826182", + "candidate": "sha256:9110d6b6286cf0f85268d4970d1fd403250da3f04351d70b1fd2d9d7a93e2d89" + }, + "tests/test_case_attended_boundary.py": { + "base": null, + "candidate": "sha256:4615e653f06144305ea1a84e98bc6260e16ed41ef9e5db2d0f162ef9f723ef2d" + } + }, + "unchanged_pins": { + "src/warden/case_inputs.py": "sha256:1334b0cd9897e763cfea2d306e997c659f3f284ecc3f9612140c2104835da7c7", + "registry/routing/catalog.yaml": "sha256:ba9634a92e584dac8c42e8624ab52adce06e79a285209cb64c0daa4dac9cd7d8", + "registry/generated/high-risk-data-paths.yaml": "sha256:5cd9c03b86751f7197cb5661a53a86d59765137958b215ec14bb343156f216d0" + }, + "changes": { + "KR1-F1": "Retain mode with admission. Recheck human actor, absence of WARDEN_AGENT_ID (including empty marker), and parent-session scope in common run/rollback/reconcile guard, after caller-session revalidation and before journal writes/owner callbacks.", + "KR1-F2": "Retain exact authenticated whole-catalog bytes. Parse and validate that snapshot at admission and attended dispatch; share attended lane active/reviewed/login-or-attended-change/session-login rule with planner. Pending Apps sessions stay refused, even under hypothetical in-memory draft promotion.", + "KR1-F3": "Integration docstring enumerates type acknowledgment, case materialization/replacement acceptance, promotion, pin, stub adapters, unconditional evidence verifier and human environment simulation. Positive expectation covers five resolvable steps; two Apps steps refused.", + "KR1-F4": "Sibling-independent 19-test matrix: six marked runtime negatives, three unmarked run/rollback/reconcile positive controls, nine planner/executor lane cases, one missing-session parser rejection." + }, + "validation": { + "baseline_regressions": "Final 19 new tests on unmodified frozen production source:13 failed,6 passed. Six runtime identity and seven invalid lane/session/review negatives fail because original code does not refuse. Six controls pass.", + "initial_test_authoring_errors": "First run:16 failures, due fixture lane_entry argument misuse; second:15 failures/1 pass, including planner schema omissions. These are invalid setup attempts, retained in logs. Corrected meaningful pre-expansion baseline:13 failed/3 passed; final with positive controls:13 failed/6 passed.", + "candidate_new_regressions": "19 passed in 1.57s", + "focused": "99 passed in 13.95s; expanded:129 passed,2 skipped in 16.40s (home-relative canonical lookups; later supplied frozen NetKingdom link)", + "full_suite": "765 passed,4 integration-marked deselected in 68.11s; uninterrupted final suite includes resolved home-relative lookups", + "command": "python3 /tmp/warden-kernel-fix-r1/run.py candidate-full.log -m pytest -q", + "lint": "python -m ruff check .: All checks passed; git diff --check passed", + "isolation": "Allowlisted environment; HOME=/tmp/warden-kernel-fix-r1/home with only frozen NetKingdom symlink and three public workload declarations. No ambient credentials copied or inventoried. Python socket audit guard denies all remote connections and allows only loopback endpoints bound by same test process. PATH=/usr/bin:/bin. Reused existing scratch venv interpreter/dependencies read-only; no live or native owner callback used.", + "limits": "Four repository integration-marked tests remain deselected. No new native/provenance claim; independent successor review still required.", + "patch_check_attempts": "First check incorrectly used the regression base tree, where the new test already existed; refused with already exists. Fresh untouched archive check then passed; no source conflict." + }, + "separate_test_isolation_proposal": { + "file": "test-isolation-proposal.patch", + "sha256": "sha256:0f6c46303ec24f337e4190ec84773ccefb4833241233b4b7d56bd1927eba838c", + "included_in_candidate": false, + "description": "Autouse fixture only in tests/test_access.py replaces ambient identity helper with deterministic no-token metadata. Existing explicit identity-display test overrides it. Production advisory helper unchanged; separately subprocess-mocked helper tests retained.", + "regression": "Synthetic BAO_TOKEN sentinel plus forbidden subprocess callback:1 failed against base, with no subprocess/native lookup permitted. Proposed fixture:26 access/helper tests passed in1.87s.", + "limit": "This narrow fixture is not a whole-suite sandbox; allowlisted HOME/environment and network guard remain required. Root to create live owner handoff for broader isolation work." + }, + "boundaries": { + "canonical_owner_edits": false, + "credentials_or_secret_reads": false, + "native_actions": false, + "grant": "none", + "catalog_promotion": false, + "owner_login_selection": false, + "PARAMETER_PINS_changed": false, + "draft_lanes_changed": false + }, + "follow_up": [ + "Independent review of candidate patch and exact file pins before owner application.", + "Owner confirmation/login choices and catalog promotion remain separate gates.", + "Founder exact-type acknowledgment and PIN-1 remain separate; no pin added.", + "Native WARDEN-WP-0044-T03 evidence and downstream acceptance remain outside source tests.", + "Root handles progress logging, publication and live test-isolation handoff." + ] +} diff --git a/docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-fixes-r1/test-isolation-proposal.patch b/docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-fixes-r1/test-isolation-proposal.patch new file mode 100644 index 0000000..4b447e0 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-fixes-r1/test-isolation-proposal.patch @@ -0,0 +1,48 @@ +diff --git a/tests/test_access.py b/tests/test_access.py +index c4286aa..1736a45 100644 +--- a/tests/test_access.py ++++ b/tests/test_access.py +@@ -4,6 +4,8 @@ from __future__ import annotations + import json + from pathlib import Path + ++import pytest ++ + from typer.testing import CliRunner + + from warden.access import agent_read_boundary_identity, expand_handoff, policy_gate_status +@@ -13,6 +15,34 @@ from warden.routing.models import RouteEntry + runner = CliRunner() + + ++@pytest.fixture(autouse=True) ++def synthetic_openbao_advisory_identity(monkeypatch): ++ """Advisory CLI tests must not inspect or use workstation credentials. ++ ++ Identity-display tests override this stub explicitly. The real helper has ++ separate subprocess-mocked unit coverage in test_openbao_caller.py. ++ """ ++ monkeypatch.setattr( ++ "warden.openbao_caller.describe_ambient_openbao_caller", ++ lambda *args, **kwargs: { ++ "source": "none", "entity_id": None, "display_name": None, ++ "auth_path": None, "policies": [], "oidc_role": None, "ttl": None, ++ "error": "synthetic test: no ambient OpenBao token", ++ }, ++ ) ++ ++ ++def test_advisory_does_not_invoke_ambient_lookup(monkeypatch): ++ monkeypatch.setenv("BAO_TOKEN", "fixture-only-never-a-real-token") ++ def forbidden(*args, **kwargs): ++ raise AssertionError("advisory unit tests must not launch a token lookup") ++ monkeypatch.setattr("warden.openbao_caller.subprocess.run", forbidden) ++ result = runner.invoke(app, ["access", "npm token", "--json"]) ++ assert result.exit_code == 0 ++ assert json.loads(result.stdout)["openbao_caller"]["source"] == "none" ++ ++ ++ + def _repo_catalog() -> Path: + return Path(__file__).resolve().parents[1] / "registry" / "routing" / "catalog.yaml" + diff --git a/docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-review-r2/integration_runner.py b/docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-review-r2/integration_runner.py new file mode 100644 index 0000000..7b6001e --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-review-r2/integration_runner.py @@ -0,0 +1,18 @@ +"""Invoke only pinned, offline validator child; keep socket/native denial in child.""" +from pathlib import Path +import sys +import subprocess +import warden.case_inputs as inputs +import pytest + +PREAMBLE = '''import sys as _review_sys\ndef _review_guard(event,args):\n if event.startswith('socket.') or event in {'subprocess.Popen','os.system','os.exec','os.posix_spawn'}:\n raise RuntimeError('review validator: network/native disabled')\n if event=='open' and isinstance(args[0],str) and (args[0].startswith('/home/') or args[0].startswith('/root/')):\n raise RuntimeError('review validator: ambient home denied')\n_review_sys.addaudithook(_review_guard)\n''' +old_run = subprocess.run + +def run_pinned(args,*pos,**kw): + if (type(args) is not list or len(args)!=5 or args[:3]!=[sys.executable,'-I','-c'] + or args[3]!=inputs._VALIDATE or not str(args[4]).startswith('/tmp/warden-case-validator-') + or kw.get('env')!={}): + raise RuntimeError('review validator: unexpected process refused') + return old_run([*args[:3],PREAMBLE+args[3],args[4]],*pos,**kw) +inputs.subprocess.run=run_pinned +raise SystemExit(pytest.main(['-q','tests/test_case_1_3_integration.py'])) diff --git a/docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-review-r2/kernel-review-r2.json b/docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-review-r2/kernel-review-r2.json new file mode 100644 index 0000000..883b31e --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-review-r2/kernel-review-r2.json @@ -0,0 +1,128 @@ +{ + "schema": "custodian.warden-1.3-kernel-review-r2.v1", + "date": "2026-10-17", + "role": "independent successor source reviewer; not candidate author", + "disposition": "accept candidate source delta for KR1-F1, KR1-F2, KR1-F3 and KR1-F4", + "scope": "Exact candidate.patch on frozen 5abde817775d6304e3cf5a46948717a1606981bf; separate test-isolation-proposal.patch reviewed and accepted as narrow test hygiene. No native acceptance, external owner assent, catalog promotion, founder acknowledgment or PARAMETER_PINS authorization.", + "inputs": { + "accepted_kernel_1": "117fec9241e7b985dfb187b992e173d0a5dc3700", + "frozen_branch": "5abde817775d6304e3cf5a46948717a1606981bf", + "net_kingdom": "001a38ca2aa85155ce4840c4da22aca6e2346171", + "candidate.patch": "sha256:1a3530a1e469fc03c2c29f1b66fa3ba1904e56bf2abcfb242c7a2ed015fb3e74", + "implementation-r1.json": "sha256:b9ee843bba417c82ad77ac8a221cf64dd5088aa640528524b2e0ef0c129d3184", + "test-isolation-proposal.patch": "sha256:0f6c46303ec24f337e4190ec84773ccefb4833241233b4b7d56bd1927eba838c", + "retained_kernel-review-r1.json": "sha256:43c96da94605082b4924ccd8ef41a1f8737d7e9ada95047ff16415b4614905dc", + "candidate_files_verified": { + "src/warden/case_run.py": "sha256:1bc73e30ac718fd75bbaff2a739495fc051aeaec73802ac71f0583cb2d53ab0a", + "src/warden/case_type.py": "sha256:7b659b8d682863cd28df24025be4b10dd7ca3df724350a22d360c6af9615bca7", + "src/warden/routing/models.py": "sha256:b85372b2572a49af62e9534f26a33963b894633c7cd43dd2a28416b8959cba3e", + "tests/test_case_1_3_integration.py": "sha256:4862c5a004075eb18733d354bee7dda3f55e04a6ee8ecb44b76166347c121158", + "tests/test_case_admission_modes.py": "sha256:9110d6b6286cf0f85268d4970d1fd403250da3f04351d70b1fd2d9d7a93e2d89", + "tests/test_case_attended_boundary.py": "sha256:4615e653f06144305ea1a84e98bc6260e16ed41ef9e5db2d0f162ef9f723ef2d" + }, + "unchanged_pins_verified": { + "src/warden/case_inputs.py": "sha256:1334b0cd9897e763cfea2d306e997c659f3f284ecc3f9612140c2104835da7c7", + "registry/routing/catalog.yaml": "sha256:ba9634a92e584dac8c42e8624ab52adce06e79a285209cb64c0daa4dac9cd7d8", + "registry/generated/high-risk-data-paths.yaml": "sha256:5cd9c03b86751f7197cb5661a53a86d59765137958b215ec14bb343156f216d0" + } + }, + "findings": [ + { + "id": "KR1-F1", + "status": "resolved in source", + "evidence": "The cached tuple now retains attended mode. All run/rollback/reconcile paths enter _admission before journal lock/load/save or owner callbacks. Caller-session evidence is reverified, then attended human actor, presence of any WARDEN_AGENT_ID key (including empty), and parent-session scope are checked. Independent changed actor/principal/descriptor-scope/session probes reject with no journal change/callback; both marker variants reject on all three cached operations." + }, + { + "id": "KR1-F2", + "status": "resolved in source", + "evidence": "Admission authenticates the complete catalog digest and parses its exact bytes. The same bytes are retained and reparsed at attended dispatch. Shared attended_lane_issue checks active/reviewed login or attended-change whose session_lane resolves to active/reviewed login in that same parsed dictionary. The 9-case author matrix checks planner/executor parity and the parser case checks missing session_lane. Independent dispatch interception verifies byte-identical catalog input on run/rollback/reconcile despite a later invalid ambient catalog file. The two unresolved Apps steps refuse in actual frozen composition integration." + }, + { + "id": "KR1-F3", + "status": "resolved in source", + "evidence": "Docstring explicitly declares acknowledged-type substitution, materialized example plus replaced acceptance, in-memory promotion and parameter pin, stub adapters, unconditional evidence verifier and removed agent marker. Test expects five conditional admissions and two Apps refusals, with no adapter dispatch. These are fixture admissions, not authenticated events." + }, + { + "id": "KR1-F4", + "status": "resolved in source", + "evidence": "New 19-test attended matrix has no sibling source dependency; 13 tests fail against unchanged frozen production source and 6 controls pass. Reviewer-owned 22 tests add independent changed authority and catalog snapshot checks. Combined frozen baseline: 22 failed,19 passed; candidate tests:41 passed within focused set. No control is weakened." + } + ], + "additional_blocking_findings": [], + "candidate_correction_required": false, + "test_isolation_proposal": { + "disposition": "accept narrow test-only patch", + "production_changes": false, + "baseline_without_proposal": "25 original access/helper tests pass in empty home; they do not alone prove absence of ambient lookup. Appended sentinel regression fails (1 failed in0.51s), with forbidden mocked subprocess callback raising before any process starts.", + "with_proposal": "26 access/helper tests pass in2.16s. Autouse fixture stubs the lazy advisory identity function; existing explicit display test overrides it. Real helper coverage remains separately subprocess-mocked.", + "limits": "Module-local fixture is not an execution sandbox, does not fix production behavior and does not protect unrelated tests. Parent must retain broader isolation handoff." + }, + "validation": [ + { + "command": "python3 /tmp/warden-kernel-review-r2/run.py focused-corrected.log -m pytest -q tests/test_review_r2_independent.py tests/test_case_attended_boundary.py tests/test_case_admission_modes.py", + "result": "64 passed in2.55s" + }, + { + "command": "python3 /tmp/warden-kernel-review-r2/run-baseline.py baseline-corrected.log -m pytest -q tests/test_case_attended_boundary.py tests/test_review_r2_independent.py", + "result": "22 failed,19 passed in2.11s; expected differential:13 original defect probes,6 independent identity marker probes,3 reparse assertions fail on predecessor" + }, + { + "command": "python3 /tmp/warden-kernel-review-r2/run.py integration-safe.log /tmp/warden-kernel-review-r2/integration_runner.py", + "result": "6 passed in10.11s; actual pinned metadata validator executes as isolated Python child with guard preamble; native owner callbacks remain forbidden" + }, + { + "command": "python3 /tmp/warden-kernel-review-r2/run.py final-focused.log -m pytest -q tests/test_review_r2_independent.py tests/test_case_attended_boundary.py tests/test_case_admission_modes.py tests/test_access.py tests/test_openbao_caller.py", + "result": "90 passed in4.22s, including separate isolation proposal" + } + ], + "invalid_initial_attempts": { + "independent.log": "23 failed,47 passed.21 reviewer tests used lane_entry(lane=login), which changes lane id rather than kind; corrected to dict(lane_entry(),lane=login).2 integration tests were refused by strict subprocess guard at pinned validator launch. No owner/native callback occurred. These are setup/isolation-control refusals, not candidate defects or passing evidence.", + "baseline.log": "Same initial reviewer fixture mistake; superseded by preserved corrected baseline. Earlier author tests still reproduced13 defects." + }, + "isolation": { + "home": "/tmp/warden-kernel-review-r2/home (empty, independently asserted)", + "environment": "Allowlisted HOME,PATH=/usr/bin:/bin,PYTHONPATH pointing only to scratch guard and source,PYTHONDONTWRITEBYTECODE,WARDEN_TEST_NETKINGDOM_ROOT,LANG. No ambient home/environment/token copied, inspected or inventoried. Synthetic BAO_TOKEN sentinel exists only under monkeypatch.", + "interpreter": "Read-only existing scratch venv /tmp/warden-kernel-review-r1/final/.venv/bin/python; no dependency install/network", + "controls": "Socket audit events denied. Process creation denied except the exact reviewed offline BrowserInputs validator command (empty child environment, pinned source modules copied by production code). Reviewer wrapper prepends child audit denial for sockets, further subprocess, and ambient /home or /root file reads. Final focused run also denies ambient home reads in parent. Guard probes independently demonstrate socket/native lookup refusal.", + "native_actions": false, + "credential_access": false, + "canonical_edits": false, + "new_commits": false + }, + "limitations": [ + "Focused independent review, not a new full-suite certification; author full-suite claims are not independently adopted.", + "Existing KERNEL-1 review remains retained, including its prior ambient-lookup deviation and unknown outcome; this successor does not erase or reclassify it.", + "WARDEN_AGENT_ID is a source marker boundary, not proof of isolated human authentication; the injected evidence verifier and owner adapter remain required trust roots.", + "No real native execution, owner evidence provenance, login choice, original-created-object readback or revocation event was exercised.", + "No external owner assent, founder exact-type acknowledgment, 1.3 pin, native T03 or downstream acceptance is supplied.", + "Exact-type known fixed pins and all draft lanes remain unchanged; no catalog was promoted." + ], + "knowledge_orientation": { + "package_version": "0.2.6", + "digest": "8d1d6a9d4d07fec7b619618de7dfe2be2254c1790f85f4198c7fe19db9751c02", + "validated_start_status": "withheld/orientation-only (exit2; changed source repositories)", + "source_review": "Read start/local-work cards as orientation only, actual ops-warden AGENTS.md/INTENT.md, frozen NetKingdom SECURITY-COMPANION.md and scoped production/test source directly. No inferred authority from withheld card." + }, + "follow_up": [ + "Root may integrate exact reviewed source and narrow separate test patch within its existing authorization.", + "Keep original adverse review intact and hand off broader test isolation as live owner work.", + "Owner login/catalog promotion, founder exact type/PIN-1, authenticated native T03 and downstream acceptance remain separate gates." + ], + "grants": "none", + "packet_hashes": { + "final-focused.log": "sha256:53adf310d91cbaaa401731f7680654bf8a0bf0f5aa955b22bbc9b6adafacb793", + "focused-corrected.log": "sha256:e942df3c21caf8eef7e94cfc70ba98b8005da909079fb9adb8502b4c7ede60ec", + "reviewer-probes.py": "sha256:e52025893ca0755b3a62e2585cf2f16002a5b705f7cfb0582b05435d3b9e336a", + "integration_runner.py": "sha256:d77da107d67ec827f19e0c46045a2802081a62283c3bdb5bfec35feabb454234", + "isolation-proposal.log": "sha256:a2a8b7c722b4b682291e588ccc374559218555adbf75b3102784d7dba2223202", + "run.py": "sha256:c6f931ba4ff9b0ef42627030a016dd7ebe3f353b3024f1c40b80df890aefe59f", + "baseline-corrected.log": "sha256:1becfd551cea8b80bc0821f266396c69ea435d644d734a605379da342902ad37", + "isolation-sentinel-baseline.log": "sha256:69907c6283e73d78a50c871ff3f021da07c00b8902330a4f7b6fce72fc5b0071", + "isolation-baseline.log": "sha256:caa59ec08f580e3130ece8f55340e0ffbf170b205cd6e8c7263d5cb0a8948cfa", + "integration-safe.log": "sha256:e4bfb3379648c76417228ff5049df1fdbfc09f81a979500e549e1eed41a61c4e", + "run-baseline.py": "sha256:02e2a409cd23fb52f518c202c923877fdcc381f708b4e8d77bad04e034b46e73", + "independent.log": "sha256:ffb5623ba301aebcf597a5009d7cfe3e04356fd59f1e4bc858084f3e8d0e4c5a", + "setup.py": "sha256:fcdeb50a196ce5afda6243b9fb51584af58ba5d55e75a4caed017f742d560500", + "sitecustomize.py": "sha256:6b7cec873fa1fca6767b77f514bec499adb5e1e7745dbc8eddf36771917ad4f4" + } +} diff --git a/docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-review-r2/reviewer-probes.py b/docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-review-r2/reviewer-probes.py new file mode 100644 index 0000000..db57d01 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-review-r2/reviewer-probes.py @@ -0,0 +1,94 @@ +"""Reviewer-owned synthetic probes; no native owner acceptance.""" +from dataclasses import asdict, replace +import json +import os +import socket +import subprocess + +import pytest +import yaml +from tests.test_case_run import make_rig, fixture_catalog, lane_entry +from warden.case_run import digest +from warden.case_receipt_state import Refused, Outcome + + +def setup(tmp_path, monkeypatch, *, entries=None): + monkeypatch.delenv('WARDEN_AGENT_ID', raising=False) + rig = make_rig(tmp_path) + ex, original, calls, sources, kwargs = rig + case, typ, _, _ = sources + d = replace(original[0], effect='change', revocation_scopes=('parent-session',)) + ds = json.dumps(asdict(d)).encode() + t = yaml.safe_load(typ) + t['spec']['execution_descriptors'] = {d.step: digest(ds)} + t['spec']['follow_through'] = [dict(id=d.step,owner=d.owner,lane=d.lane,warden_executes=False,attended=True)] + catalog = fixture_catalog(*(entries or [dict(lane_entry(),lane='login')])) + ex.evidence_verifier = lambda *_: True + admitted = ex.admit(case,yaml.safe_dump(t).encode(),ds,catalog,**kwargs) + return ex, admitted, calls, catalog + + +def count(ex): + return tuple(ex.journal.db.execute('SELECT COUNT(*) FROM '+name).fetchone()[0] for name in ['intents','receipts']) + + +def prepare(ex, admitted, operation): + if operation == 'rollback': + assert ex.run(*admitted).last.outcome is Outcome.SUCCESS + if operation == 'reconcile': + old = ex.adapters[admitted[0].adapter] + def lost(_): + raise RuntimeError('lost synthetic response') + ex.adapters[old.reference] = replace(old,invoke=lost) + assert ex.run(*admitted).last.outcome is Outcome.UNCERTAIN + + +@pytest.mark.parametrize('operation',['run','rollback','reconcile']) +@pytest.mark.parametrize('tamper',['marker','empty-marker','actor','principal','scopes','expired-session']) +def test_each_cached_operation_rejects_changed_authority(tmp_path,monkeypatch,operation,tamper): + ex,args,calls,_ = setup(tmp_path,monkeypatch) + prepare(ex,args,operation) + callbacks=[] + old=ex.adapters[args[0].adapter] + def forbidden(*_): + callbacks.append('called') + raise AssertionError('must not dispatch') + ex.adapters[old.reference]=replace(old,invoke=forbidden,rollback=forbidden,reconcile=forbidden) + before=count(ex) + if tamper in ('marker','empty-marker'): + monkeypatch.setenv('WARDEN_AGENT_ID', '' if tamper=='empty-marker' else 'review-agent') + elif tamper=='actor':args=(args[0],replace(args[1],actor='agent'),args[2]) + elif tamper=='principal':args=(args[0],replace(args[1],principal='other'),args[2]) + elif tamper=='scopes':args=(replace(args[0],revocation_scopes=()),args[1],args[2]) + else:ex.evidence_verifier=lambda *_: False + with pytest.raises(Refused):getattr(ex,operation)(*args) + assert count(ex)==before + assert callbacks==[] + + +@pytest.mark.parametrize('operation',['run','rollback','reconcile']) +def test_dispatch_reparses_exact_admitted_catalog(tmp_path,monkeypatch,operation): + ex,args,_,catalog = setup(tmp_path,monkeypatch) + prepare(ex,args,operation) + import warden.case_run as source + parse=source._catalog_lane + seen=[] + def checked(blob,descriptor): + seen.append(blob) + return parse(blob,descriptor) + monkeypatch.setattr(source,'_catalog_lane',checked) + # A later ambient file must not replace the authenticated admission snapshot. + monkeypatch.setenv('WARDEN_ROUTING_CATALOG',str(tmp_path/'untrusted.yaml')) + (tmp_path/'untrusted.yaml').write_bytes(fixture_catalog(dict(lane_entry(),lane='secret'))) + result=getattr(ex,operation)(*args) + assert result.last.outcome in (Outcome.SUCCESS,Outcome.ROLLED_BACK,Outcome.NO_CHANGE) + assert seen==[catalog] + assert isinstance(catalog,bytes) + + +def test_isolation_denies_network_and_native_process(): + assert os.environ.get('BAO_TOKEN') is None + assert os.environ.get('VAULT_TOKEN') is None + assert list(__import__('pathlib').Path.home().glob('*'))==[] + with pytest.raises(RuntimeError,match='sockets disabled'):socket.socket() + with pytest.raises(RuntimeError,match='subprocess disabled'):subprocess.run(['bao','token','lookup']) diff --git a/docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-review-r2/run-baseline.py b/docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-review-r2/run-baseline.py new file mode 100644 index 0000000..3875d7e --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-review-r2/run-baseline.py @@ -0,0 +1,9 @@ +from pathlib import Path +import subprocess,sys +r=Path('/tmp/warden-kernel-review-r2') +env={'HOME':str(r/'home'),'PATH':'/usr/bin:/bin','PYTHONPATH':str(r/'guard')+':'+str(r/'baseline/src'),'PYTHONDONTWRITEBYTECODE':'1','WARDEN_TEST_NETKINGDOM_ROOT':str(r/'net-kingdom'),'LANG':'C.UTF-8'} +p='/tmp/warden-kernel-review-r1/final/.venv/bin/python' +with (r/sys.argv[1]).open('w') as log: + result=subprocess.run([p,*sys.argv[2:]],cwd=r/'baseline',env=env,stdout=log,stderr=subprocess.STDOUT) +print(result.returncode) +sys.exit(result.returncode) diff --git a/docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-review-r2/run.py b/docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-review-r2/run.py new file mode 100644 index 0000000..7bfae1a --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-review-r2/run.py @@ -0,0 +1,9 @@ +from pathlib import Path +import subprocess,sys +r=Path('/tmp/warden-kernel-review-r2') +env={'HOME':str(r/'home'),'PATH':'/usr/bin:/bin','PYTHONPATH':str(r/'guard')+':'+str(r/'candidate/src'),'PYTHONDONTWRITEBYTECODE':'1','WARDEN_TEST_NETKINGDOM_ROOT':str(r/'net-kingdom'),'LANG':'C.UTF-8'} +p='/tmp/warden-kernel-review-r1/final/.venv/bin/python' +with (r/sys.argv[1]).open('w') as log: + result=subprocess.run([p,*sys.argv[2:]],cwd=r/'candidate',env=env,stdout=log,stderr=subprocess.STDOUT) +print(result.returncode) +sys.exit(result.returncode) diff --git a/docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-review-r2/setup.py b/docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-review-r2/setup.py new file mode 100644 index 0000000..e06203b --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-review-r2/setup.py @@ -0,0 +1,13 @@ +from pathlib import Path +import subprocess,tarfile,io,hashlib,json +root=Path('/tmp/warden-kernel-review-r2') +packet=Path('/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-fixes-r1') +for name,repo,rev in [('candidate','ops-warden','5abde817775d6304e3cf5a46948717a1606981bf'),('net-kingdom','net-kingdom','001a38ca2aa85155ce4840c4da22aca6e2346171')]: + tree=root/name; tree.mkdir() + archive=subprocess.check_output(['git','-C','/home/worsch/'+repo,'archive',rev]) + with tarfile.open(fileobj=io.BytesIO(archive)) as t:t.extractall(tree,filter='data') +subprocess.run(['git','apply',str(packet/'candidate.patch')],cwd=root/'candidate',check=True) +(root/'home').mkdir(); (root/'guard').mkdir() +(root/'guard/sitecustomize.py').write_text('''import sys\ndef guard(event,args):\n if event.startswith('socket.'):\n raise RuntimeError('review isolation: sockets disabled')\n if event in {'subprocess.Popen','os.system','os.exec','os.posix_spawn'}:\n raise RuntimeError('review isolation: subprocess disabled')\nsys.addaudithook(guard)\n''') +(root/'run.py').write_text('''from pathlib import Path\nimport subprocess,sys\nr=Path('/tmp/warden-kernel-review-r2')\nenv={'HOME':str(r/'home'),'PATH':'/usr/bin:/bin','PYTHONPATH':str(r/'guard')+':'+str(r/'candidate/src'),'PYTHONDONTWRITEBYTECODE':'1','WARDEN_TEST_NETKINGDOM_ROOT':str(r/'net-kingdom'),'LANG':'C.UTF-8'}\np='/tmp/warden-kernel-review-r1/final/.venv/bin/python'\nwith (r/sys.argv[1]).open('w') as log:\n result=subprocess.run([p,*sys.argv[2:]],cwd=r/'candidate',env=env,stdout=log,stderr=subprocess.STDOUT)\nprint(result.returncode)\nsys.exit(result.returncode)\n''') +print('Archived public source and applied exact patch; isolated runner created.') diff --git a/docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-review-r2/sitecustomize.py b/docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-review-r2/sitecustomize.py new file mode 100644 index 0000000..f99fd0c --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/warden-1.3-kernel-review-r2/sitecustomize.py @@ -0,0 +1,17 @@ +import sys + +def guard(event,args): + if event.startswith('socket.'): + raise RuntimeError('review isolation: sockets disabled') + if event == 'subprocess.Popen': + cmd=args[1] + if (type(cmd) is list and len(cmd)==5 and cmd[:3]==[sys.executable,'-I','-c'] + and cmd[3].startswith('import sys as _review_sys\ndef _review_guard(') + and str(cmd[4]).startswith('/tmp/warden-case-validator-') and args[3]=={}): + return + raise RuntimeError('review isolation: subprocess disabled') + if event in {'os.system','os.exec','os.posix_spawn'}: + raise RuntimeError('review isolation: subprocess disabled') + if event=='open' and isinstance(args[0],str) and (args[0].startswith('/home/') or args[0].startswith('/root/')): + raise RuntimeError('review isolation: ambient home denied') +sys.addaudithook(guard) diff --git a/docs/changes/netkingdom-target-2026-10-17/warden-kernel-source-integration-r1/commit.json b/docs/changes/netkingdom-target-2026-10-17/warden-kernel-source-integration-r1/commit.json new file mode 100644 index 0000000..b71e001 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/warden-kernel-source-integration-r1/commit.json @@ -0,0 +1,5 @@ +{ + "exit_code": 0, + "stdout": "[detached HEAD ae7bc73] fix(case): preserve attended authority at dispatch and validate session lanes\n 8 files changed, 252 insertions(+), 27 deletions(-)\n create mode 100644 tests/test_case_attended_boundary.py\n", + "stderr": "" +} diff --git a/docs/changes/netkingdom-target-2026-10-17/warden-kernel-source-integration-r1/consistency-preimage.json b/docs/changes/netkingdom-target-2026-10-17/warden-kernel-source-integration-r1/consistency-preimage.json new file mode 100644 index 0000000..437a4f9 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/warden-kernel-source-integration-r1/consistency-preimage.json @@ -0,0 +1,267 @@ +[ + { + "path": ".custodian-brief.md", + "sha256": "4ff4bb94315149e07d36b8f4ecbbfc63b61d72e9d81f0f43a9c2f02bc13ada85", + "index": "100644 34573d262327057c11628ce8a7ca78292968545b 0\t.custodian-brief.md" + }, + { + "path": "WORK-RECORDS.md", + "sha256": "b6af9b1cb21e3a64b2ef8a5e732bc35c39cdeaef6af7a18f24824e8ec9fb5a76", + "index": "100644 34cf268862326a5832a7d0be9e84eb296e5601bb 0\tWORK-RECORDS.md" + }, + { + "path": "workplans/ADHOC-2026-06-27.md", + "sha256": "f0a89c1809e23b436767a73b70636c283e79216fb19678f08bef2b9e62e42ddb", + "index": "100644 d75908fb225c1f964b5b697032a6f98c4a13c1db 0\tworkplans/ADHOC-2026-06-27.md" + }, + { + "path": "workplans/ADHOC-2026-06-29.md", + "sha256": "9869f07cdfe79d37314d36db74a434a55b50c0b965ae53f29d76ca9a9a73151f", + "index": "100644 b46be0fd6ee2eeea9ffeff695b887b61aab8a7a2 0\tworkplans/ADHOC-2026-06-29.md" + }, + { + "path": "workplans/ADHOC-2026-08-11.md", + "sha256": "6bba4f5dab9fc64f7b65df7c2ab7e4b9d4dbb69ba1c4a41c8440dbd23555aea4", + "index": "100644 3d858fe808bd7a9d8318cdcd38ee012a8d907ca4 0\tworkplans/ADHOC-2026-08-11.md" + }, + { + "path": "workplans/ADHOC-2026-08-17.md", + "sha256": "39ec882551bf5643fabb3ebc9f47300b4e6f6caf6d67a0a26bc100b68d4e313e", + "index": "100644 65d75959a0fd23dd861e24321b1b46057d6fea5f 0\tworkplans/ADHOC-2026-08-17.md" + }, + { + "path": "workplans/ADHOC-2026-09-08.md", + "sha256": "27c64084fdd16b926b138584d96688061a18a58d4d8152d9688b4486b9687c05", + "index": "100644 c1ae7c3ea0d8e722d7073ae920780a5e56611294 0\tworkplans/ADHOC-2026-09-08.md" + }, + { + "path": "workplans/ADHOC-2026-10-03.md", + "sha256": "8de5e5ba28ce2cd94c5864e316d54fe32e6cdfdb0e75551fe92ed25b81eeafa6", + "index": "100644 e76cd016eab0dfd9eceac7d8033b40f92c9ada32 0\tworkplans/ADHOC-2026-10-03.md" + }, + { + "path": "workplans/WARDEN-WP-0016-ops-bridge-tunnel-cert-pilot.md", + "sha256": "ad904837aa2d83d888d46c482fe381eba328c96a4617209ea8a9ae2d7d64c467", + "index": "100644 a787f1da8347fdd2219ef67a569d21a192563343 0\tworkplans/WARDEN-WP-0016-ops-bridge-tunnel-cert-pilot.md" + }, + { + "path": "workplans/WARDEN-WP-0017-access-front-door-discoverability.md", + "sha256": "a35f65a40c98d35e86f352c1430837b8bc9f269bddaddfe4a71c6414f678d85c", + "index": "100644 fe9c5b15a464aa3ae104576bfc0a04510d944f89 0\tworkplans/WARDEN-WP-0017-access-front-door-discoverability.md" + }, + { + "path": "workplans/WARDEN-WP-0018-whynot-design-npm-lane-activation.md", + "sha256": "6d9145534f7fa80cec9f185751d3796fdaab4a2d88b289a5451ba04e24822855", + "index": "100644 f923be5351582a2bab38afc1aca3b4e5da0fe008 0\tworkplans/WARDEN-WP-0018-whynot-design-npm-lane-activation.md" + }, + { + "path": "workplans/WARDEN-WP-0019-route-to-secrets-engine.md", + "sha256": "8fa2d79c85a821f7bc51044f37001bad613afb75d143181e2e0a01b213e06df8", + "index": "100644 602fad1b4e56fc69de85aa2e0843676f50186340 0\tworkplans/WARDEN-WP-0019-route-to-secrets-engine.md" + }, + { + "path": "workplans/WARDEN-WP-0020-ops-warden-worker.md", + "sha256": "d565f65413a2698160caf33ab30d7513a03d06ddcee7d624b198bb8841abdcc3", + "index": "100644 1230ed3175d3f5f175fc0146e8006ebd8be5a369 0\tworkplans/WARDEN-WP-0020-ops-warden-worker.md" + }, + { + "path": "workplans/WARDEN-WP-0021-enable-scheduled-worker-tick.md", + "sha256": "4f7afe5bdd147705e68d31d02f39251543c6c120ce22d893b98af063e303c361", + "index": "100644 2500d4b346dffbe6d5f9848c5440bfa8d85d5d14 0\tworkplans/WARDEN-WP-0021-enable-scheduled-worker-tick.md" + }, + { + "path": "workplans/WARDEN-WP-0022-audit-trail-and-activity.md", + "sha256": "f733fb0d7cfcc7b44aba49a730e7f51381d6478a87c394161e77c90577c26505", + "index": "100644 63171f58096efb8d4bc07c69db250fff7038f4e1 0\tworkplans/WARDEN-WP-0022-audit-trail-and-activity.md" + }, + { + "path": "workplans/WARDEN-WP-0023-intent-scope-alignment-closeout.md", + "sha256": "97d25825ba626a3979e8d6217aebd27f58d27fe9f92e8b94eb2f63e2bb843cb9", + "index": "100644 99278d609aa5324c625364b2e0ac636ad9219ddf 0\tworkplans/WARDEN-WP-0023-intent-scope-alignment-closeout.md" + }, + { + "path": "workplans/WARDEN-WP-0024-experiential-memory-and-agent-sessions.md", + "sha256": "22bde2d1398a4260e337768c6cf71cf80cd61d466371b6cfd0c0f0c18fc72378", + "index": "100644 3ebac662f6c96f69a5fa6cbb5917999a4e24455a 0\tworkplans/WARDEN-WP-0024-experiential-memory-and-agent-sessions.md" + }, + { + "path": "workplans/WARDEN-WP-0025-forgejo-admin-api-token-lane.md", + "sha256": "ded5bdfdbe897f3376c71c7a169caca51a9d75453ba66cf216ec8dfcab91c708", + "index": "100644 5c4e77249b01fd8cb9300a237c917d95363cf2d7 0\tworkplans/WARDEN-WP-0025-forgejo-admin-api-token-lane.md" + }, + { + "path": "workplans/WARDEN-WP-0026-credential-disclosure-hygiene.md", + "sha256": "f65ccdf9c920e9138353018e2ed5a7fbbd590246eb730fc6f57c75b6d2e09eea", + "index": "100644 309f77187b31c1287b085affefc9d271ea40858c 0\tworkplans/WARDEN-WP-0026-credential-disclosure-hygiene.md" + }, + { + "path": "workplans/WARDEN-WP-0027-credential-governance-lockdown.md", + "sha256": "0d0c08458718ec88883c4b257ea80011123a6d5aa2eea36fe28db331ff6d438f", + "index": "100644 c14e9c303a1f1a096fdf710ee48112f68205435e 0\tworkplans/WARDEN-WP-0027-credential-governance-lockdown.md" + }, + { + "path": "workplans/WARDEN-WP-0028-tenant-secret-custody.md", + "sha256": "92cc0ad8d42209d5d7e45122e8ddd67e1970de1d200df373885ba5fb75e0d22a", + "index": "100644 fe81983444e5127bb8a0fdf94f42c3817758c67e 0\tworkplans/WARDEN-WP-0028-tenant-secret-custody.md" + }, + { + "path": "workplans/WARDEN-WP-0029-policy-front-door-and-founder-surface.md", + "sha256": "2b21f06c062edd96943a1d26fd86c6a27b341b1db4532e528edbaebd4ced7e66", + "index": "100644 4008d825b4c39d3f0e4a80f6d290af9b551fd812 0\tworkplans/WARDEN-WP-0029-policy-front-door-and-founder-surface.md" + }, + { + "path": "workplans/WARDEN-WP-0030-delegation-register.md", + "sha256": "59bf962b355cd9734315108f9868508739ddcb4855591bff53aea3634aed47f7", + "index": "100644 49612d072663af831f7b7314c4b77e4b9596097b 0\tworkplans/WARDEN-WP-0030-delegation-register.md" + }, + { + "path": "workplans/WARDEN-WP-0031-policy-caller-identity.md", + "sha256": "0fa890ba81833180feb727ba35dcf01ead7d7dedf55a285a49a2585049b4bffb", + "index": "100644 c4c600c47a6e4a65951b1adf324cc33f13cb1be6 0\tworkplans/WARDEN-WP-0031-policy-caller-identity.md" + }, + { + "path": "workplans/WARDEN-WP-0032-security-zones.md", + "sha256": "4497947e57fb86e883090d07c8bef031fd5fe4a7f3eb065d530ebe327d97b1cc", + "index": "100644 6c8000ade056b82e019826d01a7e9e8cf0f4de98 0\tworkplans/WARDEN-WP-0032-security-zones.md" + }, + { + "path": "workplans/WARDEN-WP-0033-native-lane-handoff.md", + "sha256": "74747ebf7c628b1a5336c63aa3924212068a6015f370e647cf9127949c181998", + "index": "100644 55cd015061bb610fe43c8f2ee821bb71b092d77c 0\tworkplans/WARDEN-WP-0033-native-lane-handoff.md" + }, + { + "path": "workplans/WARDEN-WP-0034-layer-model-v07-conformance.md", + "sha256": "57e10b9efff02d7437231424ebc4d48864a2019939c2cda2fa10629a4ede2a3b", + "index": "100644 d6ebfdce0e2901b42c98e38e9eaaac0f1d817e79 0\tworkplans/WARDEN-WP-0034-layer-model-v07-conformance.md" + }, + { + "path": "workplans/WARDEN-WP-0035-policy-nexus-forgejo-source-read-route.md", + "sha256": "d40987b48a8416de386972d214ce01dba138a9f294cc948de36085c794c76418", + "index": "100644 1210b158a3836d2b4df934f50fb538c5241b09d5 0\tworkplans/WARDEN-WP-0035-policy-nexus-forgejo-source-read-route.md" + }, + { + "path": "workplans/WARDEN-WP-0036-attended-login-openbao-output.md", + "sha256": "e0584491b5a6a7d4db0cf61a689f733b4a6597778ca79a3f8f2207fe7ecfb8cc", + "index": "100644 452eb41267ea99cc83104fcb2a782c0b244b87d1 0\tworkplans/WARDEN-WP-0036-attended-login-openbao-output.md" + }, + { + "path": "workplans/WARDEN-WP-0037-whynot-design-forgejo-npm-lane.md", + "sha256": "7c64a631eb70d9ff42826ea5966cac571786ff86bac5393f88c328aeb14aa9ba", + "index": "100644 a3eba2dff6517a64b2c5d3d9b9a9c14ca4570ec6 0\tworkplans/WARDEN-WP-0037-whynot-design-forgejo-npm-lane.md" + }, + { + "path": "workplans/WARDEN-WP-0038-plan-mutation-intent.md", + "sha256": "09b0ed8c6315255796a2da0d64dfd001b1f6de2e903ac2ea79bc5667b499fb83", + "index": "100644 8c9c5b6df7d1f48b48f8c2056c2a8186935184ee 0\tworkplans/WARDEN-WP-0038-plan-mutation-intent.md" + }, + { + "path": "workplans/WARDEN-WP-0039-explicit-policy-refusal.md", + "sha256": "e8773a7b4196cf77a48ab4ed0804a5f826a0a0b5e7972f968681746ebbc6e3e9", + "index": "100644 ca0828d2679eda031fb86d66eb886ccb38ee1843 0\tworkplans/WARDEN-WP-0039-explicit-policy-refusal.md" + }, + { + "path": "workplans/WARDEN-WP-0040-unknown-zone-fail-closed-adoption.md", + "sha256": "a4b8e768eaacd5ab5a90f69a1abcb3bd48ce821fcdea208b0ee75d9326f0d512", + "index": "100644 f07fb535d5ad00f5330f16a5a6e927da6960fe52 0\tworkplans/WARDEN-WP-0040-unknown-zone-fail-closed-adoption.md" + }, + { + "path": "workplans/WARDEN-WP-0041-catalog-lane-scaffolding.md", + "sha256": "c16993301f29e7f5ea005cccdbe360ac9c4c3949e6988e2b7a1f1e5df6235bc1", + "index": "100644 8a1ca341e9fcc041dcf99be8706f0db2a39408a6 0\tworkplans/WARDEN-WP-0041-catalog-lane-scaffolding.md" + }, + { + "path": "workplans/WARDEN-WP-0042-openbao-structure-survey-route.md", + "sha256": "1e48399d6ef8a509898e8ea0b334a378c51ec467aaa704b6dbf8485aed29fa8b", + "index": "100644 097d64791e79e782640514c7324e2ff8a5dc435c 0\tworkplans/WARDEN-WP-0042-openbao-structure-survey-route.md" + }, + { + "path": "workplans/WARDEN-WP-0043-platform-supplier-credentials.md", + "sha256": "c059b7f80ad4c0e5faa8ad15532ab6c2577d0c383ba91cde7293534b6e4683e2", + "index": "100644 d359328239b0b897ccffbf037a12b06d1f0d126a 0\tworkplans/WARDEN-WP-0043-platform-supplier-credentials.md" + }, + { + "path": "workplans/WARDEN-WP-0044-security-case-follow-through.md", + "sha256": "10c6f02d578837dee12acd8b638d18c5b9f93b6a9c4aff3083ce60c3752d1664", + "index": "100644 aa9f798b39bcfe1f42b9a6777b1f83e7973e3c42 0\tworkplans/WARDEN-WP-0044-security-case-follow-through.md" + }, + { + "path": "workplans/archived/260515-WARDEN-WP-0001-initial-implementation.md", + "sha256": "359456ecd25525d95748ca611e7c6c118eee2b914dae447c84c59442a6520458", + "index": "100644 5425cbd886ff77afc9be7c8b62b97d03278e0a8e 0\tworkplans/archived/260515-WARDEN-WP-0001-initial-implementation.md" + }, + { + "path": "workplans/archived/260515-WARDEN-WP-0002-correctness-and-completeness.md", + "sha256": "43ba78e9c6e9914360edc88a6f8f5af0f981b3078ef41808a784ce7388559fb7", + "index": "100644 0b6044f99391e81a439f2782ee697434319df05b 0\tworkplans/archived/260515-WARDEN-WP-0002-correctness-and-completeness.md" + }, + { + "path": "workplans/archived/260515-WARDEN-WP-0003-test-coverage-and-quality.md", + "sha256": "4fb4ac0cb72cf14dd72c6e5f313a068d90ead203460ad3ac68f0d5dd12b6a755", + "index": "100644 d544f950037cb705d5f7195ee559f91ca9f6a4f4 0\tworkplans/archived/260515-WARDEN-WP-0003-test-coverage-and-quality.md" + }, + { + "path": "workplans/archived/260617-WARDEN-WP-0004-repo-hygiene-and-hub-sync.md", + "sha256": "fbb36a3d9c4ab0ac829bba713160d919b3c12c19f5f5826fb4e8483edeec3ac1", + "index": "100644 31330938708c5ae9b6412c5715549f2d69192589 0\tworkplans/archived/260617-WARDEN-WP-0004-repo-hygiene-and-hub-sync.md" + }, + { + "path": "workplans/archived/260617-WARDEN-WP-0005-openbao-doc-alignment.md", + "sha256": "3c930a395d518f9b287855632c21fe99a3d5c78a758ece06498fb9e35f5ea8b9", + "index": "100644 9969260deee2960143102edecadb9fd7c9736832 0\tworkplans/archived/260617-WARDEN-WP-0005-openbao-doc-alignment.md" + }, + { + "path": "workplans/archived/260617-WARDEN-WP-0006-netkingdom-alignment-and-access-stewardship.md", + "sha256": "fbc90ca4c18f8180a7fbcd2b43f21d7f99a44c914eb17c78f823228f8e3cc64f", + "index": "100644 8a7cc2f69821ac8cbf0de744073b7a938fa6b51d 0\tworkplans/archived/260617-WARDEN-WP-0006-netkingdom-alignment-and-access-stewardship.md" + }, + { + "path": "workplans/archived/260617-WARDEN-WP-0007-policy-gate-and-production-verify.md", + "sha256": "613b73c79bad0929f621feb136b3141f9c60e23bf6e23650881a47c762101214", + "index": "100644 f49eea27ea7c15f4f3d608669ddd60e3a13803b5 0\tworkplans/archived/260617-WARDEN-WP-0007-policy-gate-and-production-verify.md" + }, + { + "path": "workplans/archived/260618-WARDEN-WP-0008-production-ssh-path-and-stewardship-closeout.md", + "sha256": "8362d4cdb933b52c9be968d5410c8548c7554cc4cd44ab0389b913674795a10a", + "index": "100644 f3827285c4e279f36ac63c14ad166ff90544659a 0\tworkplans/archived/260618-WARDEN-WP-0008-production-ssh-path-and-stewardship-closeout.md" + }, + { + "path": "workplans/archived/260623-WARDEN-WP-0009-flex-auth-policy-gate-production.md", + "sha256": "fa611c1a2e5c5a214e38f0673ebcda17e3f30e10556b268fb126ded9af2bdbbb", + "index": "100644 7f87d0ed26b7f19438f164a10a10a46808824776 0\tworkplans/archived/260623-WARDEN-WP-0009-flex-auth-policy-gate-production.md" + }, + { + "path": "workplans/archived/260624-WARDEN-WP-0010-access-routing-charter.md", + "sha256": "62bc994e3efbb124004f98992ded6842ff2748568b2e405c61b335385a35b9d7", + "index": "100644 12357da7a3d1d5f994e70d0f06dcc80ea1faf848 0\tworkplans/archived/260624-WARDEN-WP-0010-access-routing-charter.md" + }, + { + "path": "workplans/archived/260624-WARDEN-WP-0011-routing-guide-cli.md", + "sha256": "767cd3c53cfe64beab93f19475be823d0637a40140877db6dfcc60e2693652ce", + "index": "100644 3fe363e93d7e7d38db58deeec0ef3023bb35f8fa 0\tworkplans/archived/260624-WARDEN-WP-0011-routing-guide-cli.md" + }, + { + "path": "workplans/archived/260624-WARDEN-WP-0013-production-integration-and-stewardship-closeout.md", + "sha256": "ecc38b4998796e20d734105df81ef6e2eb05c8e830b6d0dbe344d6336b9f668b", + "index": "100644 9812e11e5e3499a22d0d4dde01fccd26b7d0ee03 0\tworkplans/archived/260624-WARDEN-WP-0013-production-integration-and-stewardship-closeout.md" + }, + { + "path": "workplans/archived/260627-WARDEN-WP-0012-routing-scenario-playbooks.md", + "sha256": "7a7a1d25cd1d015c0a28f0d443de0512fc48cb1fda4317760051251988aa96f0", + "index": "100644 acf9cce9f55395827479d4963e2d257be182f507 0\tworkplans/archived/260627-WARDEN-WP-0012-routing-scenario-playbooks.md" + }, + { + "path": "workplans/archived/260627-WARDEN-WP-0014-operator-access-assist.md", + "sha256": "c1b84e750b2580fa1deae7dd56fd29abe5d9924a3ccd47ce9fc5f99c26aa4924", + "index": "100644 01240484d955b0af77443d6dd49948c96f9fb032 0\tworkplans/archived/260627-WARDEN-WP-0014-operator-access-assist.md" + }, + { + "path": "workplans/archived/260627-WARDEN-WP-0015-secret-lifecycle-tiering.md", + "sha256": "300b8b6cfdd7306fbb81ab30d4f59551976eed1de0012fd655e4b6fc096ccf10", + "index": "100644 c59207892dcf723ce7b4ca0908635fb32407176e 0\tworkplans/archived/260627-WARDEN-WP-0015-secret-lifecycle-tiering.md" + }, + { + "path": "workplans/archived/260707-ADHOC-2026-07-07.md", + "sha256": "e169418f6d83f954de1907ae8fa26a5f5cf4fe011be01dba9363e69befa05cfa", + "index": "100644 31e9d27f2aa37f8eac17cb5500873a60b9ee8833 0\tworkplans/archived/260707-ADHOC-2026-07-07.md" + } +] diff --git a/docs/changes/netkingdom-target-2026-10-17/warden-kernel-source-integration-r1/consistency.json b/docs/changes/netkingdom-target-2026-10-17/warden-kernel-source-integration-r1/consistency.json new file mode 100644 index 0000000..f990cc5 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/warden-kernel-source-integration-r1/consistency.json @@ -0,0 +1,151 @@ +{ + "repo_slug": "ops-warden", + "repo_path": "/home/worsch/ops-warden", + "source_revision": "ae7bc73b4f4bac22454144959b870d161498e0af", + "source_revision_reason": null, + "issues": [ + { + "severity": "WARN", + "check_id": "C-24", + "message": "Repo classification advisory: capability_tag 'security' is not a recommended family tag (allowed, check for synonym)", + "file_path": "", + "db_id": "", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-42", + "message": "Blocked workplan 'warden-wp-0027' has no task in status wait \u2014 nothing states what it waits for; qualify a wait task or re-status the workplan", + "file_path": "workplans/WARDEN-WP-0027-credential-governance-lockdown.md", + "db_id": "21528e8d-a049-523d-9ae1-da7a27cb8bbf", + "file_value": "blocked", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-37", + "message": "Residual workplan 'warden-wp-0034' has no origin/origin_ref provenance (not invented)", + "file_path": "workplans/WARDEN-WP-0034-layer-model-v07-conformance.md", + "db_id": "ae3ff76f-883d-5e2f-b6aa-144d61e8fdef", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-40", + "message": "blocker satisfied, task still wait: 'WARDEN-WP-0037-T03' in workplan 'WARDEN-WP-0037' \u2014 decision 1591daa1-6e85-4dfd-aadd-5a9fe1ef47fa is resolved", + "file_path": "workplans/WARDEN-WP-0037-whynot-design-forgejo-npm-lane.md#WARDEN-WP-0037-T03", + "db_id": "", + "file_value": "1591daa1-6e85-4dfd-aadd-5a9fe1ef47fa", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-11", + "message": "Task 'WARDEN-WP-0038-T01' has no state_hub_task_id", + "file_path": "workplans/WARDEN-WP-0038-plan-mutation-intent.md#WARDEN-WP-0038-T01", + "db_id": "", + "file_value": "", + "db_value": "", + "fixable": true + }, + { + "severity": "WARN", + "check_id": "C-37", + "message": "Residual workplan 'warden-wp-0040' has no origin/origin_ref provenance (not invented)", + "file_path": "workplans/WARDEN-WP-0040-unknown-zone-fail-closed-adoption.md", + "db_id": "c8ee441e-1be1-5219-910c-e79ff23cc9ec", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "INFO", + "check_id": "C-08", + "message": "Retired DB workstream 'adhoc-2026-06-29@retired-20260828' (id=1c0460b7\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work", + "file_path": "", + "db_id": "1c0460b7-bc8a-48db-96d4-681bce18ac91", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "INFO", + "check_id": "C-08", + "message": "Retired DB workstream 'adhoc-2026-08-17@retired-20260827' (id=8376eb89\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work", + "file_path": "", + "db_id": "8376eb89-36f2-52b0-a59a-2954b2e49349", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "INFO", + "check_id": "C-08", + "message": "Retired DB workstream 'adhoc-2026-08-11@retired-20260827' (id=bc6dd3e3\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work", + "file_path": "", + "db_id": "bc6dd3e3-ab2b-567a-9574-30d2650f20a2", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "INFO", + "check_id": "C-08", + "message": "Retired DB workstream 'ops-warden-adhoc-2026-06-27@retired-20260827' (id=142b171b\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work", + "file_path": "", + "db_id": "142b171b-c34b-4a45-91a5-c77e6d07ec6f", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "INFO", + "check_id": "C-08", + "message": "Retired DB workstream 'adhoc-2026-07-07@retired-20260827' (id=2e8bc783\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work", + "file_path": "", + "db_id": "2e8bc783-2e37-4b86-a691-177b18329783", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-28", + "message": "2 unread inbox message(s) older than 3 day(s): the-custodian:23172cb2, the-custodian:51e9eace", + "file_path": "", + "db_id": "", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-28", + "message": "3 unread message(s) lack thread_id for supersession tracking: the-custodian:d422aecb, the-custodian:23172cb2, the-custodian:51e9eace", + "file_path": "", + "db_id": "", + "file_value": "", + "db_value": "", + "fixable": false + } + ], + "fixes_applied": [ + "bindings: synced 51/51", + "C-17 cleared: pushed 6 backlogged commit(s)", + "C-11 skipped: task 'WARDEN-WP-0038-T01' in finished workstream \u2014 not created", + "push: pushed" + ], + "summary": { + "fail": 0, + "automation_error": 0, + "warn": 8, + "info": 5 + }, + "result": "warn" +} diff --git a/docs/changes/netkingdom-target-2026-10-17/warden-kernel-source-integration-r1/consistency.stderr.txt b/docs/changes/netkingdom-target-2026-10-17/warden-kernel-source-integration-r1/consistency.stderr.txt new file mode 100644 index 0000000..e69de29 diff --git a/docs/changes/netkingdom-target-2026-10-17/warden-kernel-source-integration-r1/focused-tests.txt b/docs/changes/netkingdom-target-2026-10-17/warden-kernel-source-integration-r1/focused-tests.txt new file mode 100644 index 0000000..df15d6c --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/warden-kernel-source-integration-r1/focused-tests.txt @@ -0,0 +1,3 @@ +........................................................................ [ 80%] +.................. [100%] +90 passed in 4.74s diff --git a/docs/changes/netkingdom-target-2026-10-17/warden-kernel-source-integration-r1/integrate.py b/docs/changes/netkingdom-target-2026-10-17/warden-kernel-source-integration-r1/integrate.py new file mode 100644 index 0000000..8c0d8f2 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/warden-kernel-source-integration-r1/integrate.py @@ -0,0 +1,93 @@ +from pathlib import Path +import hashlib,json,re,subprocess,sys +p=Path(__file__).resolve().parent;r=Path('/home/worsch/ops-warden') +f=p.parent/'warden-1.3-kernel-fixes-r1'; review=json.loads((p.parent/'warden-1.3-kernel-review-r2/kernel-review-r2.json').read_text()) +w=Path('/tmp/warden-kernel-source-integration-r1') +base='117fec9241e7b985dfb187b992e173d0a5dc3700'; branch='5abde817775d6304e3cf5a46948717a1606981bf' +def git(root,*a):return subprocess.check_output(['git','-C',str(root),*a],stderr=subprocess.PIPE,text=True,timeout=60).strip() +assert git(r,'rev-parse','HEAD')==base +assert git(r,'rev-parse','warden-1.3-kernel')==branch +assert git(r,'ls-remote','origin','refs/heads/main').split()[0]==base +assert not w.exists() +assert hashlib.sha256((f/'candidate.patch').read_bytes()).hexdigest()=='1a3530a1e469fc03c2c29f1b66fa3ba1904e56bf2abcfb242c7a2ed015fb3e74' +assert hashlib.sha256((f/'test-isolation-proposal.patch').read_bytes()).hexdigest()=='0f6c46303ec24f337e4190ec84773ccefb4833241233b4b7d56bd1927eba838c' +original_work=(r/'WORK-RECORDS.md').read_bytes(); original_index=git(r,'ls-files','--stage','--','WORK-RECORDS.md') +(p/'WORK-RECORDS.md.backup').write_bytes(original_work) +git(r,'worktree','add','--detach',str(w),branch) +for file in ['candidate.patch','test-isolation-proposal.patch']: + git(w,'apply','--check',str(f/file));git(w,'apply',str(f/file)) +for file,want in review['inputs']['candidate_files_verified'].items(): + assert hashlib.sha256((w/file).read_bytes()).hexdigest()==want.split(':')[-1] +for file,want in review['inputs']['unchanged_pins_verified'].items(): + assert hashlib.sha256((w/file).read_bytes()).hexdigest()==want.split(':')[-1] +name='workplans/WARDEN-WP-0044-security-case-follow-through.md' +old=(w/name).read_text();(p/'workplan.preimage.md').write_text(old) +new=old+""" + +## T03 source correction and safe-test follow-through — 2026-10-17 + +The reviewed KERNEL-2/3 source now authenticates and parses the actual catalog +snapshot, checks lane owner/risk/delegation, and resolves each type step's Warden, +attended or owner dispatch mode. Four proposed attended-change lanes remain +DRAFT; no lane is promoted and PARAMETER_PINS still contains only 1.1/1.2. + +Independent review KR1-F1/F2 found cached attended run/rollback/reconcile could +lose the human/runtime boundary, and actual admission lacked the planner's +establishing-login checks. The exact successor retains the mode and authenticated +catalog bytes and repeats caller/session, agent marker and attended-lane checks +before any journal write or callback on all three operations. Planner and executor +share active/reviewed login/session-lane validation. Two unresolved Apps session +lanes correctly remain refused. Integration tests now explicitly disclose all +fixture authority substitutions; they establish no owner or native authority. + +The new 19-test regression matrix reproduces 13 failures on its predecessor and +passes on the candidate. A fresh independent reviewer adds changed-authority and +catalog-snapshot probes: 90 focused checks and six guarded integration checks +pass; the combined predecessor comparison has 22 expected failures. Author's +isolated full suite passes 765 with four integration-marked tests deselected. +Evidence: Custodian warden-1.3-kernel-fixes-r1 and warden-1.3-kernel-review-r2 +under docs/changes/netkingdom-target-2026-10-17. Original unsuccessful review, +invalid setup attempts and unknown ambient-lookup outcome are retained. + +A separate reviewed test-only fix stubs ambient OpenBao identity in access CLI +unit tests and adds a synthetic sentinel regression which fails before any +subprocess can launch on the predecessor; 26 access/helper checks pass. The real +production helper is unchanged. The first old review's unisolated launches may +have invoked ambient `bao token lookup`; remote reach/success remain unknown, +with no secret displayed. This patch does not erase that adverse outcome. + +Explicit remaining safe-test work is retained in this live T03 scope, owned by +ops-warden: replace or wrap the ordinary unit-test entry path with allowlisted +environment/empty synthetic HOME and guarded subprocess/network execution, +including nested validator children. Completion requires representative access +and case tests to refuse token-file/environment and live provider access under +synthetic negative controls while permitted local fixtures remain usable. Until +then use the preserved isolated review runner, not the ambient `make test` path. +No credential lookup, live investigation or rotation is authorized by this note. +T03 remains progress; contained sessions, authenticated owner provenance, +materialization, exact type acknowledgment/pin and first native case remain open. +""" +assert re.findall(r'```task\n.*?```',old,re.S)==re.findall(r'```task\n.*?```',new,re.S) +(w/name).write_text(new) +# Repeat the independently reviewed focused cases with an empty allowlisted HOME. +probe=p.parent/'warden-1.3-kernel-review-r2/reviewer-probes.py' +(w/'tests/test_review_r2_independent.py').write_bytes(probe.read_bytes()) +home=p/'test-home';home.mkdir() +env={'HOME':str(home),'PATH':'/usr/bin:/bin','PYTHONDONTWRITEBYTECODE':'1','PYTHONPATH':'/tmp/warden-kernel-review-r2/guard:'+str(w/'src'),'LANG':'C.UTF-8'} +with (p/'focused-tests.txt').open('w') as log: + result=subprocess.run(['/tmp/warden-kernel-review-r1/final/.venv/bin/python','-B','-m','pytest','-q','-p','no:cacheprovider','tests/test_review_r2_independent.py','tests/test_case_attended_boundary.py','tests/test_case_admission_modes.py','tests/test_access.py','tests/test_openbao_caller.py'],cwd=w,env=env,stdout=log,stderr=subprocess.STDOUT,timeout=120) +assert result.returncode==0,'guarded focused tests failed; preserve worktree' +paths=[*review['inputs']['candidate_files_verified'],'tests/test_access.py',name] +git(w,'diff','--check') +run=subprocess.run(['git','-C',str(w),'add','--',*paths],capture_output=True,text=True);assert run.returncode==0 +run=subprocess.run(['git','-C',str(w),'commit','--only','-m','fix(case): preserve attended authority at dispatch and validate session lanes','--',*paths],capture_output=True,text=True,timeout=240) +(p/'commit.json').write_text(json.dumps({'exit_code':run.returncode,'stdout':run.stdout,'stderr':run.stderr},indent=2)+'\n') +assert run.returncode==0,'normal commit refused' +head=git(w,'rev-parse','HEAD') +assert set(git(w,'diff-tree','--no-commit-id','--name-only','-r',head).splitlines())==set(paths) +assert git(r,'rev-parse','HEAD')==base and git(r,'ls-remote','origin','refs/heads/main').split()[0]==base +assert (r/'WORK-RECORDS.md').read_bytes()==original_work and git(r,'ls-files','--stage','--','WORK-RECORDS.md')==original_index +git(r,'merge','--ff-only',head) +assert (r/'WORK-RECORDS.md').read_bytes()==original_work and git(r,'ls-files','--stage','--','WORK-RECORDS.md')==original_index +(p/'integration.json').write_text(json.dumps({'status':'integrated-source-not-published','base':base,'draft_branch':branch,'head':head,'paths':paths,'unchanged_original_workplan_tasks':4,'ambient_owner_dirt_preserved':True,'catalog_promotion':False,'PARAMETER_PINS_changed':False,'native_action':False,'test_result':(p/'focused-tests.txt').read_text()},indent=2)+'\n') +print(json.dumps({'status':'integrated','head':head,'paths':len(paths),'focused_tests':'passed','native_action':False})) diff --git a/docs/changes/netkingdom-target-2026-10-17/warden-kernel-source-integration-r1/integration.json b/docs/changes/netkingdom-target-2026-10-17/warden-kernel-source-integration-r1/integration.json new file mode 100644 index 0000000..b8a57ba --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/warden-kernel-source-integration-r1/integration.json @@ -0,0 +1,22 @@ +{ + "status": "integrated-source-not-published", + "base": "117fec9241e7b985dfb187b992e173d0a5dc3700", + "draft_branch": "5abde817775d6304e3cf5a46948717a1606981bf", + "head": "ae7bc73b4f4bac22454144959b870d161498e0af", + "paths": [ + "src/warden/case_run.py", + "src/warden/case_type.py", + "src/warden/routing/models.py", + "tests/test_case_1_3_integration.py", + "tests/test_case_admission_modes.py", + "tests/test_case_attended_boundary.py", + "tests/test_access.py", + "workplans/WARDEN-WP-0044-security-case-follow-through.md" + ], + "unchanged_original_workplan_tasks": 4, + "ambient_owner_dirt_preserved": true, + "catalog_promotion": false, + "PARAMETER_PINS_changed": false, + "native_action": false, + "test_result": "........................................................................ [ 80%]\n.................. [100%]\n90 passed in 4.74s\n" +} diff --git a/docs/changes/netkingdom-target-2026-10-17/warden-kernel-source-integration-r1/normal-sync.json b/docs/changes/netkingdom-target-2026-10-17/warden-kernel-source-integration-r1/normal-sync.json new file mode 100644 index 0000000..33a183b --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/warden-kernel-source-integration-r1/normal-sync.json @@ -0,0 +1,296 @@ +{ + "ok": true, + "status": "applied", + "repo_slug": "ops-warden", + "commit": "922c26da0f3fcb0182352ea83e6ae06f29878580", + "requests": 2, + "elapsed_ms": 12875.5, + "git": { + "upstream": "origin/main", + "behind": 0, + "ahead": 0 + }, + "identifiers": { + "schema": "repo-manager.identifier-assignment.v1", + "ok": true, + "executed": true, + "repo": "ops-warden", + "namespace": "helixforge", + "assignments": [ + { + "kind": "task", + "record_id": "WARDEN-WP-0038-T01", + "uuid": "ef28df7c-2c72-56be-ad90-4e079f37639f", + "path": "workplans/WARDEN-WP-0038-plan-mutation-intent.md" + } + ], + "historical_invalid_identifiers": [], + "historical_identity_collisions": [], + "files_changed": [ + "workplans/WARDEN-WP-0038-plan-mutation-intent.md" + ] + }, + "receipt": { + "schema": "state-hub.repository-projection-reconcile.v1", + "instance_role": "primary", + "instance_label": "railiance01", + "expected_commit": "922c26da0f3fcb0182352ea83e6ae06f29878580", + "derived_commit": "922c26da0f3fcb0182352ea83e6ae06f29878580", + "outcome": { + "schema": "state-hub.projection-reset.v1", + "repo_slug": "ops-warden", + "commit": "922c26da0f3fcb0182352ea83e6ae06f29878580", + "status": "applied", + "counts": { + "created": 0, + "updated": 51, + "retired": 0, + "refused": 0, + "released": 0, + "created_tasks": 0, + "updated_tasks": 0, + "cancelled_tasks": 0, + "created_edges": 0, + "removed_edges": 0, + "unresolved_edges": 0, + "external_edges": 17 + }, + "created": [], + "updated": [ + "WARDEN-WP-0001", + "WARDEN-WP-0002", + "WARDEN-WP-0003", + "WARDEN-WP-0004", + "WARDEN-WP-0005", + "WARDEN-WP-0006", + "WARDEN-WP-0007", + "WARDEN-WP-0008", + "WARDEN-WP-0009", + "WARDEN-WP-0010", + "WARDEN-WP-0011", + "WARDEN-WP-0012", + "WARDEN-WP-0013", + "WARDEN-WP-0014", + "WARDEN-WP-0015", + "WARDEN-WP-0016", + "WARDEN-WP-0017", + "WARDEN-WP-0018", + "WARDEN-WP-0019", + "WARDEN-WP-0020", + "WARDEN-WP-0021", + "WARDEN-WP-0022", + "WARDEN-WP-0023", + "WARDEN-WP-0024", + "WARDEN-WP-0025", + "WARDEN-WP-0026", + "WARDEN-WP-0027", + "WARDEN-WP-0028", + "WARDEN-WP-0029", + "WARDEN-WP-0030", + "WARDEN-WP-0031", + "WARDEN-WP-0032", + "WARDEN-WP-0033", + "WARDEN-WP-0034", + "WARDEN-WP-0035", + "WARDEN-WP-0036", + "WARDEN-WP-0037", + "WARDEN-WP-0038", + "WARDEN-WP-0039", + "WARDEN-WP-0040", + "WARDEN-WP-0041", + "WARDEN-WP-0042", + "WARDEN-WP-0043", + "WARDEN-WP-0044", + "WARDEN-WP-ADHOC-2026-06-27", + "WARDEN-WP-ADHOC-2026-06-29", + "WARDEN-WP-ADHOC-2026-07-07", + "WARDEN-WP-ADHOC-2026-08-11", + "WARDEN-WP-ADHOC-2026-08-17", + "WARDEN-WP-ADHOC-2026-09-08", + "WARDEN-WP-ADHOC-2026-10-03" + ], + "retired": [], + "released": [], + "created_tasks": [], + "updated_tasks": [], + "cancelled_tasks": [], + "created_edges": [], + "removed_edges": [], + "unresolved_edges": [], + "external_edges": [ + { + "workplan": "WARDEN-WP-0032", + "from": "WARDEN-WP-0032", + "target": "WARDEN-WP-0031", + "relationship_type": "blocks", + "from_workplan_id": "38c6a5f3-fb0d-5230-be85-f9e3ffc850f6", + "from_task_id": null, + "to_workplan_id": "739bad25-2345-5f4f-aaa3-cc4cd8c71f6c", + "to_task_id": null + }, + { + "workplan": "WARDEN-WP-0037", + "from": "WARDEN-WP-0037-T03", + "target": "SECRETS-WP-0006-T06", + "relationship_type": "starts_after", + "from_workplan_id": "42a097db-1c24-558e-a724-030bb2b4443e", + "from_task_id": "a8b1b855-ab34-5835-b9fd-5f48bc0b6817", + "to_workplan_id": null, + "to_task_id": "1431edae-5791-5892-8c4b-829419b537d2" + }, + { + "workplan": "WARDEN-WP-0033", + "from": "WARDEN-WP-0033", + "target": "WARDEN-WP-0030", + "relationship_type": "blocks", + "from_workplan_id": "4627d89b-4b00-562a-81e9-76e96f90fa7e", + "from_task_id": null, + "to_workplan_id": "da3367d5-890c-52c6-aa54-1bdd0f277342", + "to_task_id": null + }, + { + "workplan": "WARDEN-WP-0013", + "from": "WARDEN-WP-0013", + "target": "WARDEN-WP-0011", + "relationship_type": "blocks", + "from_workplan_id": "4678c41a-c1d0-48cd-9988-4ea0380e8258", + "from_task_id": null, + "to_workplan_id": "0a520f8e-01b4-48f1-9af3-2f3f69fd0672", + "to_task_id": null + }, + { + "workplan": "WARDEN-WP-0013", + "from": "WARDEN-WP-0013", + "target": "WARDEN-WP-0009", + "relationship_type": "blocks", + "from_workplan_id": "4678c41a-c1d0-48cd-9988-4ea0380e8258", + "from_task_id": null, + "to_workplan_id": "9213b262-e2f5-480e-a5bc-56635d5eb4c9", + "to_task_id": null + }, + { + "workplan": "WARDEN-WP-0013", + "from": "WARDEN-WP-0013", + "target": "WARDEN-WP-0008", + "relationship_type": "blocks", + "from_workplan_id": "4678c41a-c1d0-48cd-9988-4ea0380e8258", + "from_task_id": null, + "to_workplan_id": "a174963a-4ff1-4565-b19f-896cd4ff14a0", + "to_task_id": null + }, + { + "workplan": "WARDEN-WP-0013", + "from": "WARDEN-WP-0013", + "target": "WARDEN-WP-0010", + "relationship_type": "blocks", + "from_workplan_id": "4678c41a-c1d0-48cd-9988-4ea0380e8258", + "from_task_id": null, + "to_workplan_id": "e93de9fd-0192-4d02-bb7c-5e859fb76b9b", + "to_task_id": null + }, + { + "workplan": "WARDEN-WP-0031", + "from": "WARDEN-WP-0031", + "target": "WARDEN-WP-0007", + "relationship_type": "blocks", + "from_workplan_id": "739bad25-2345-5f4f-aaa3-cc4cd8c71f6c", + "from_task_id": null, + "to_workplan_id": "3718ac07-2fa2-47d0-a02a-c9a7b83a5ba9", + "to_task_id": null + }, + { + "workplan": "WARDEN-WP-0038", + "from": "WARDEN-WP-0038", + "target": "WARDEN-WP-0029", + "relationship_type": "blocks", + "from_workplan_id": "751ad530-e44b-52c4-b70e-cb47568a0179", + "from_task_id": null, + "to_workplan_id": "bbb3d9ec-d88d-5088-b4c0-55bfba0a10cf", + "to_task_id": null + }, + { + "workplan": "WARDEN-WP-0023", + "from": "WARDEN-WP-0023", + "target": "WARDEN-WP-0022", + "relationship_type": "blocks", + "from_workplan_id": "7bad1ec4-a7c2-4980-b8f9-49a7f5408574", + "from_task_id": null, + "to_workplan_id": "fc8afa28-68a7-4250-a19e-9754829f0cd5", + "to_task_id": null + }, + { + "workplan": "WARDEN-WP-0034", + "from": "WARDEN-WP-0034", + "target": "WARDEN-WP-0030", + "relationship_type": "blocks", + "from_workplan_id": "ae3ff76f-883d-5e2f-b6aa-144d61e8fdef", + "from_task_id": null, + "to_workplan_id": "da3367d5-890c-52c6-aa54-1bdd0f277342", + "to_task_id": null + }, + { + "workplan": "WARDEN-WP-0040", + "from": "WARDEN-WP-0040", + "target": "WARDEN-WP-0032", + "relationship_type": "blocks", + "from_workplan_id": "c8ee441e-1be1-5219-910c-e79ff23cc9ec", + "from_task_id": null, + "to_workplan_id": "38c6a5f3-fb0d-5230-be85-f9e3ffc850f6", + "to_task_id": null + }, + { + "workplan": "WARDEN-WP-0040", + "from": "WARDEN-WP-0040", + "target": "WARDEN-WP-0034", + "relationship_type": "blocks", + "from_workplan_id": "c8ee441e-1be1-5219-910c-e79ff23cc9ec", + "from_task_id": null, + "to_workplan_id": "ae3ff76f-883d-5e2f-b6aa-144d61e8fdef", + "to_task_id": null + }, + { + "workplan": "WARDEN-WP-0040", + "from": "WARDEN-WP-0040-T03", + "target": "WARDEN-WP-0040-T01", + "relationship_type": "starts_after", + "from_workplan_id": "c8ee441e-1be1-5219-910c-e79ff23cc9ec", + "from_task_id": "93eaf1f2-daaa-5e22-8804-85ca9433571c", + "to_workplan_id": null, + "to_task_id": "611f0901-9fb5-5954-8dd0-a860c5f0cbec" + }, + { + "workplan": "WARDEN-WP-0044", + "from": "WARDEN-WP-0044-T04", + "target": "WARDEN-WP-0044-T03", + "relationship_type": "starts_after", + "from_workplan_id": "f7e81013-6868-5d99-8528-ba75a442eca9", + "from_task_id": "4a0fa4a7-5ca9-518a-ab92-a79a5e2cf93e", + "to_workplan_id": null, + "to_task_id": "bcbf6c91-2c04-5c16-ae8c-5d6c06fd6b89" + }, + { + "workplan": "WARDEN-WP-0044", + "from": "WARDEN-WP-0044-T03", + "target": "MASON-WP-0010-T03", + "relationship_type": "starts_after", + "from_workplan_id": "f7e81013-6868-5d99-8528-ba75a442eca9", + "from_task_id": "bcbf6c91-2c04-5c16-ae8c-5d6c06fd6b89", + "to_workplan_id": null, + "to_task_id": "56cea6be-c613-586c-80cd-125340b374da" + }, + { + "workplan": "WARDEN-WP-0044", + "from": "WARDEN-WP-0044-T03", + "target": "WARDEN-WP-0044-T02", + "relationship_type": "starts_after", + "from_workplan_id": "f7e81013-6868-5d99-8528-ba75a442eca9", + "from_task_id": "bcbf6c91-2c04-5c16-ae8c-5d6c06fd6b89", + "to_workplan_id": null, + "to_task_id": "5f68c991-f490-58f8-a121-92f74073d272" + } + ], + "refused": [], + "notes": [] + } + } +} diff --git a/docs/changes/netkingdom-target-2026-10-17/warden-kernel-source-integration-r1/normal-sync.stderr.txt b/docs/changes/netkingdom-target-2026-10-17/warden-kernel-source-integration-r1/normal-sync.stderr.txt new file mode 100644 index 0000000..e69de29 diff --git a/docs/changes/netkingdom-target-2026-10-17/warden-kernel-source-integration-r1/publish.py b/docs/changes/netkingdom-target-2026-10-17/warden-kernel-source-integration-r1/publish.py new file mode 100644 index 0000000..b1fb0c2 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/warden-kernel-source-integration-r1/publish.py @@ -0,0 +1,64 @@ +from pathlib import Path +import hashlib,json,subprocess,urllib.request,re +p=Path(__file__).resolve().parent;r=Path('/home/worsch/ops-warden') +cfg=json.loads((p/'integration.json').read_text());head=cfg['head'] +def git(*a):return subprocess.check_output(['git','-C',str(r),*a],stderr=subprocess.PIPE,text=True,timeout=90).strip() +assert git('rev-parse','HEAD')==head +assert git('ls-remote','origin','refs/heads/main').split()[0] in {cfg['base'],head} +backup=p/'consistency-backup';assert not backup.exists();backup.mkdir() +files=[r/'.custodian-brief.md',r/'WORK-RECORDS.md',*sorted((r/'workplans').rglob('*.md'))] +rows=[] +for f in files: + if not f.exists():continue + n=str(f.relative_to(r));dst=backup/n;dst.parent.mkdir(parents=True,exist_ok=True);dst.write_bytes(f.read_bytes()) + rows.append({'path':n,'sha256':hashlib.sha256(f.read_bytes()).hexdigest(),'index':git('ls-files','--stage','--',n)}) +(p/'consistency-preimage.json').write_text(json.dumps(rows,indent=2)+'\n') +try: + result=subprocess.run(['statehub','fix-consistency','--repo','ops-warden','--no-writeback','--json'],cwd=r,capture_output=True,text=True,timeout=300) + (p/'consistency.json').write_text(result.stdout);(p/'consistency.stderr.txt').write_text(result.stderr) + assert result.returncode==0,'required consistency failed' + data=json.loads(result.stdout);assert data.get('fail',data.get('summary',{}).get('fail',0))==0 +finally: + for row in rows: + n=row['path'];f=r/n;old=(backup/n).read_bytes() + if f.read_bytes()!=old: + (p/('changed-'+n.replace('/','-'))).write_bytes(f.read_bytes());f.write_bytes(old) + if git('ls-files','--stage','--',n)!=row['index']: + mode,oid,stage,name=row['index'].split();assert stage=='0';git('update-index','--cacheinfo',mode,oid,name) + assert hashlib.sha256(f.read_bytes()).hexdigest()==row['sha256'] +assert git('rev-parse','HEAD')==head +result=subprocess.run(['/home/worsch/repo-manager/.venv/bin/rmgr','sync','--path',str(r),'--push'],capture_output=True,text=True,timeout=180) +(p/'normal-sync.json').write_text(result.stdout);(p/'normal-sync.stderr.txt').write_text(result.stderr) +if result.returncode: + text=result.stdout+result.stderr + assert 'WARDEN-WP-0038-T01' in text and ('UUID' in text or 'uuid' in text),'unexpected sync refusal; stop' + assert git('ls-remote','origin','refs/heads/main').split()[0] in {cfg['base'],head} + push=subprocess.run(['git','-C',str(r),'push','origin','main'],capture_output=True,text=True,timeout=120) + (p/'source-push.json').write_text(json.dumps({'exit_code':push.returncode,'stdout':push.stdout,'stderr':push.stderr},indent=2)+'\n') + assert push.returncode==0,'normal source push refused' +else: + data=json.loads(result.stdout);assert data.get('status') in {'applied','noop'} +assert git('ls-remote','origin','refs/heads/main').split()[0]==head +body={'expected_commit':head,'acknowledge_retirements':False} +request=urllib.request.Request('http://127.0.0.1:8000/repos/ops-warden/work-record-projection/reconcile',data=json.dumps(body).encode(),headers={'Content-Type':'application/json','X-StateHub-Component':'ops-warden'},method='POST') +with urllib.request.urlopen(request,timeout=120) as response: receipt=json.load(response) +(p/'receiver-receipt.json').write_text(json.dumps(receipt,indent=2)+'\n') +assert receipt['ok'] and receipt['status'] in {'applied','noop'} +rp=receipt['receipt'];assert rp['expected_commit']==rp['derived_commit']==head +assert (rp['instance_role'],rp['instance_label'])==('primary','railiance01') +assert rp['outcome']['counts']['retired']==0 +name='workplans/WARDEN-WP-0044-security-case-follow-through.md';text=(r/name).read_text() +blocks=re.findall(r'```task\n.*?```',text,re.S) +assert len(blocks)==4 +bindings=[] +for b in blocks: + tid=re.search(r'state_hub_task_id: "(.*?)"',b).group(1);status=re.search(r'^status: (.+)$',b,re.M).group(1) + with urllib.request.urlopen('http://127.0.0.1:8000/tasks/'+tid,timeout=15) as response:task=json.load(response) + assert task['status']==status + bindings.append({'record_id':task.get('record_id'),'id':tid,'status':status}) +for row in rows: + assert hashlib.sha256((r/row['path']).read_bytes()).hexdigest()==row['sha256'] + assert git('ls-files','--stage','--',row['path'])==row['index'] +verification={'status':'published-and-projected','head':head,'primary_task_bindings':bindings,'preserved_files_and_index':len(rows),'normal_allocator_refusal_preserved':result.returncode!=0,'catalog_promotion':False,'PARAMETER_PINS_changed':False,'native_actions':False} +(p/'verification.json').write_text(json.dumps(verification,indent=2)+'\n') +print(json.dumps(verification)) diff --git a/docs/changes/netkingdom-target-2026-10-17/warden-kernel-source-integration-r1/verification.json b/docs/changes/netkingdom-target-2026-10-17/warden-kernel-source-integration-r1/verification.json new file mode 100644 index 0000000..d3674f8 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/warden-kernel-source-integration-r1/verification.json @@ -0,0 +1,53 @@ +{ + "status": "published-and-projected", + "implementation_commit": "ae7bc73b4f4bac22454144959b870d161498e0af", + "final_published_commit": "922c26da0f3fcb0182352ea83e6ae06f29878580", + "normal_allocator_assignment": [ + { + "kind": "task", + "record_id": "WARDEN-WP-0038-T01", + "uuid": "ef28df7c-2c72-56be-ad90-4e079f37639f", + "path": "workplans/WARDEN-WP-0038-plan-mutation-intent.md" + } + ], + "allocator_commit_only_adds_missing_uuid": true, + "preserved_other_files_and_index": 52, + "primary_task_bindings": [ + { + "id": "ef0920fc-418e-52cb-acff-9a5c0c728bf5", + "record_id": "WARDEN-WP-0044-T01", + "status": "done", + "needs_human": false + }, + { + "id": "5f68c991-f490-58f8-a121-92f74073d272", + "record_id": "WARDEN-WP-0044-T02", + "status": "done", + "needs_human": false + }, + { + "id": "bcbf6c91-2c04-5c16-ae8c-5d6c06fd6b89", + "record_id": "WARDEN-WP-0044-T03", + "status": "progress", + "needs_human": false + }, + { + "id": "4a0fa4a7-5ca9-518a-ab92-a79a5e2cf93e", + "record_id": "WARDEN-WP-0044-T04", + "status": "wait", + "needs_human": false + }, + { + "id": "ef28df7c-2c72-56be-ad90-4e079f37639f", + "record_id": "WARDEN-WP-0038-T01", + "status": "done", + "needs_human": false + } + ], + "retired": 0, + "cancelled": 0, + "native_actions": false, + "catalog_promotion": false, + "PARAMETER_PINS_changed": false, + "prior_attempt": "publish.py stopped at stale expected-head assertion after normal rmgr sync succeeded and assigned missing historical UUID; no retry, rewrite or duplicate sync needed." +} diff --git a/docs/changes/netkingdom-target-2026-10-17/warden-kernel-source-integration-r1/workplan.preimage.md b/docs/changes/netkingdom-target-2026-10-17/warden-kernel-source-integration-r1/workplan.preimage.md new file mode 100644 index 0000000..f641324 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/warden-kernel-source-integration-r1/workplan.preimage.md @@ -0,0 +1,312 @@ +--- +id: WARDEN-WP-0044 +type: workplan +title: "Follow through on KnownSecurityCases along their type's route" +domain: infotech +repo: ops-warden +status: active +flavor: implementation +owner: claude-code +topic_slug: netkingdom +created: "2026-10-08" +updated: "2026-10-11" +depends_on: [] +related_repos: + - net-kingdom + - ops-mason + - key-cape + - railiance-platform +state_hub_workstream_id: "f7e81013-6868-5d99-8528-ba75a442eca9" +--- + +# WARDEN-WP-0044 — Follow through on KnownSecurityCases along their type's route + +## Why + +NetKingdom is introducing SecurityCaseTypes (net-kingdom `NK-WP-0048`, canon +`security-case-types_v0.1`, proposed). Once a case of an implemented type is +accepted and ops-mason has materialized its structure (`MASON-WP-0010`), +ops-warden carries out the remaining steps. These are the owners' declared entry +points, run on the authentication path the type defines, followed by the type's +verification. + +Today ops-warden routes needs and, for `exec_capable` lanes, proxies fetches as +the caller. It does not yet know about cases, and each new integration ends with +a founder running a hand-written script (for example the proposed KEY-WP-0038-T02 +rollout). This plan gives ops-warden the follow-through role, without widening +what it may do: each step still runs under its owner's entry point and the +caller's identity. + +## Tasks + +```task +id: WARDEN-WP-0044-T01 +status: done +depends_on: [NK-WP-0048-T03] +priority: high +state_hub_task_id: "ef0920fc-418e-52cb-acff-9a5c0c728bf5" +``` + +### T01 — Define the type's follow-through section + +Propose the `follow_through` part of the SecurityCaseType schema together with +net-kingdom. For each step it names the owner entry point, `warden_executes`, +the auth method and lane, whether an attended human login is unavoidable (with +the type's justification), and the verification command. Write it for the first +type, `oidc-browser-relying-party`, covering key-cape client registration, +custody minting and LLDAP group membership. + +2026-10-08: NK-WP-0048-T03/T04 landed with a drafted section. Review is in +`history/2026-10-08-oidc-rp-follow-through-review.md`. It requires three +changes before T02: a per-step `lane`, a narrow `warden_executes`, and +`value_handling`. It also lists four refinements. This task is done when +net-kingdom acknowledges the review (NK-WP-0048-T05). + +2026-10-09: R4 was withdrawn in favour of ops-mason's per-case +`{consuming_service}-keycape-client`, with the entry body templated in the type +(R4′, see the addendum in the review). + +2026-10-09: the revised 1.0 type and canon now carry R1–R3, R4′, R5–R7. +`warden case check-type ` (`src/warden/case_type.py`, tests in +`tests/test_case_type.py`) checks a type's follow-through mechanically: the +live type is schema-conformant, with one attended session +(`key-cape-oidc-login`). It is not yet plannable, because two lanes are not +in the catalog: `identity-provisioner-groups` (net-kingdom) and +`keycape-client-secret-generate` (railiance-platform). These gate T02, not +T01. T01 still closes on net-kingdom's acknowledgement (NK-WP-0048-T05). + +2026-10-09: ops-warden cannot add `identity-provisioner-groups` as the type +declares it. The provisioner accepts only user-engine's runtime token and +admits only user-engine pods, so no caller-identity path exists. R8 (review +addendum) asks net-kingdom either to move `ensure-groups` into the existing +attended session (recommended) or to give the provisioner an operator +credential. `keycape-client-secret-generate` needs a generate-in-custody entry +point from railiance-platform, and the broker does not have one yet. Both were +sent to their owners. + +2026-10-09: done. net-kingdom took R8 option 1, so `ensure-groups` runs attended +in the `register-client` session and `identity-provisioner-groups` is dropped +(net-kingdom `0e86f95`). The founder acknowledged the type with memo r4 (hub +decision `e418263e`, NK-WP-0048-T05 done). + +```task +id: WARDEN-WP-0044-T02 +status: done +depends_on: [WARDEN-WP-0044-T01] +priority: high +state_hub_task_id: "5f68c991-f490-58f8-a121-92f74073d272" +``` + +### T02 — Plan a case run + +`warden case plan ` checks that the case is accepted and materialized, +resolves each step to its catalog entry and entry point, and prints the run: +which steps warden executes, which run as the attended caller, what the +verification is. No secret values; no execution. + +Tests: refuse an unaccepted or unmaterialized case; refuse a step whose catalog +entry is missing or not reviewed; show attended steps explicitly. + +2026-10-09: done. `warden case plan [--type] [--json]` +(`src/warden/case_plan.py`, 14 tests in `tests/test_case_plan.py`). It refuses: +- a case that is not `materialized`, or has no acceptance record +- a type that is mismatched or not acknowledged +- any `case check-type` finding, including a missing or unreviewed lane +- undeclared, type-fixed or secret-sensitivity parameters +- a placeholder the case does not set, or an unsupported `when` + +It fills type defaults (`issuer`), evaluates ` == ` conditions, +renders owner, entry point, auth and ready_check, groups attended steps per +lane, and lists the applicable verification. + +The live Forgejo case is correctly refused: it is `accepted`, not +materialized. A materialized copy, with a stand-in lane for +`keycape-client-secret-generate`, renders six steps: two attended steps plus +`register-client` in one `key-cape-oidc-login` session, one warden step +(`mint-secret`), and two owner steps. Seven checks follow. + +Observation for net-kingdom (not blocking): `bind-env` runs before +`register-client`, in type order. Its entry carries a literal +`KEYCAPE__CLIENT_SECRET`, which is not a `{param}` placeholder, so it +reaches the plan unrendered. + +2026-10-09 follow-up: +- **Format specs.** `_render` supports net-kingdom's `{param:env}` spec (upper + case, `-` as `_`, the same as `keycape-client.py env_name`) and refuses any + other spec or `!conversion`. +- **Case reference.** The plan prints `case-ref: @sha256:` over the + case file's exact bytes. This is the reference proposed to railiance-platform + for `credential.py generate --case-ref` (RPF-WP-0054-T01); the broker + re-derives it from the file. +- 18 tests now. + +```task +id: WARDEN-WP-0044-T03 +status: progress +depends_on: [WARDEN-WP-0044-T02, MASON-WP-0010-T03] +blocking_reason: "Local durable execution kernel and injected failure harness implemented. Native acceptance still requires authenticated owner machine descriptors/adapters, actual Forgejo materialization receipt, contained session integration, ordered verification and first native run." +priority: high +state_hub_task_id: "bcbf6c91-2c04-5c16-ae8c-5d6c06fd6b89" +``` + +### T03 — Run and verify a case + +`warden case run ` executes the plan. Owner entry points run through +the existing proxy as the caller; steps that need an attended login use the +existing contained login envelope (silent command, session revoked afterwards), +grouped into as few attended sessions as possible. Then it runs the type's +verification and writes a receipt without values. `warden case rollback` +reverses exactly this case's steps. + +Acceptance: the Forgejo reference case (NK-WP-0048-T06) reaches `live` through +this path, with no case-specific script. + +2026-10-17: original-intent lookup/context interface for NK-WP-0048-T15, source +and synthetic tests only. `src/warden/case_run.py`: `Executor.run` writes an +`OriginalIntent` (schema `warden.case.original-intent.v1`: the dispatched run +Invocation — attempt, step, owner, target, actor, principal, lane, all six pins, +tenant, realm — plus admitted adapter source) in the same SQLite transaction as +the pre-dispatch journal row. `Journal.original_intent(binding, current, source)` +returns it after restart and refuses on a missing record or any +pins/source/step/owner/target/actor/principal/lane/tenant/realm mismatch, before any +journal write, so the step stays uncertain. An adapter's +`reconcile_original(ReconcileContext(original, attempt))` receives the original +intent separately from the new reconcile attempt. This is Warden's record of what +it dispatched, not owner created-only provenance. Current Executor.admit leaves +tenant/realm empty; the reserved coordinates establish no admitted provenance. +Owner descriptors/adapters, contained session integration, Forgejo +materialization and the native run remain open. + +2026-10-17 source consistency follow-through: retained the additive intents table +and reconcile_original callback and fixed four reproduced local defects: original +actor mismatch, payload/SQL attempt-ID mismatch, orphan intent without its +matching original running receipt, and arbitrary nonmetadata values accepted by +the original-intent serializer. Exact bounded field validation, duplicate-free +bounded decode and save-time intent/invocation/running-state equality now preserve +the original receipt linkage before owner lookup. No new context store, native +verifier, trust root, adapter activation or authority migration was introduced. + +The actual canonical focused kernel and new consistency suite passes 54 checks; +owner-installed Ruff check/format passes. The existing generated-data/synthetic +owner fixture and a distinct interpreter prove source correspondence/refusal, +not independent native custody or creation. Earlier comparison probes and failed +preparation are retained. Current caller and fresh session/grant revocation checks +remain required; original owner payload/revocation, exact native readback and +created-only provenance still need independent authenticated owner admission. +No JSON/SQL context, unknown tenant/realm, changed run-sheet or legacy missing +intent supplies recovery, replay or undo authority. T03 remains progress and all +native acceptance gates/statuses/dependencies/UUIDs remain unchanged. Evidence: +Custodian original-context-compare-r2 and original-context-owner-followthrough-r3 +under docs/changes/netkingdom-target-2026-10-17/; publication/projection is separate. + +Agreed with railiance-platform (RPF-WP-0054-T01, 2026-10-09): +- `mint-secret` runs `credential.py generate --grant keycape/client-secret-generate + --client-id --case-ref --case-file ` as the caller's + child process. The broker re-derives the case-ref and checks status and + client_id. +- warden refuses that step when `WARDEN_AGENT_ID` is set. + +2026-10-10: railiance-platform implemented the command (RPF-WP-0054-T03). It is +catalogued as draft `keycape-client-secret-generate`, with `exec_owner` +railiance-platform and `exec_capable: false`. `warden case check-type` now +reports the lane as LANE-UNREVIEWED rather than LANE-MISSING. Before it can be +activated: +- the founder binds the issuer policy to the operator OIDC role +- key-cape confirms the secret format +- the RPF-WP-0054-T05 probe passes + +2026-10-10 (later): +- **Broker lane.** The issuer role is bound (CCR-2026-0043) and the T05 probe + passed (railiance-platform `21b8af3`). Only key-cape's format confirmation is + left. The playbook now includes the issuer-role login, a 15m token with + unlimited uses that `warden case run` must group with the attended session. +- **Env spec.** net-kingdom adopted `{client_id:env}` as a 1.0 patch + (`9a9b8e3`). The plan renders `KEYCAPE_FORGEJO_CLIENT_SECRET`. +- **Forgejo case.** Rendered and built by ops-mason. It is not yet + `materialized`: MASON-WP-0010-T03 waits on the railiance-platform CCRs and + their founder apply. + +2026-10-10: `keycape-client-secret-generate` activated on railiance-platform's +go-ahead (message 20973a9c) after key-cape confirmed the format (4e0213b3). +`warden case check-type` on the live type now reports `plannable=True`. The +only thing T03 still waits on is the Forgejo case reaching `materialized` +(MASON-WP-0010-T03). + +2026-10-10: net-kingdom moved the type to 1.1 (`e2174ce`) and re-pinned the +Forgejo case. 1.1 adds the verification `consumer-enforces-groups` and requires +the hand-over to enforce `access_groups`. `check-type` on 1.1 reports +`plannable=True`, and a materialized copy renders six steps and the new check. +The case-ref digest covers the whole case file, including `status`, so it +changes when the case becomes `materialized`. `warden case run` therefore takes +it from the file at run time, and the broker re-derives it from the same file. + +```task +id: WARDEN-WP-0044-T04 +status: wait +depends_on: [WARDEN-WP-0044-T03] +blocking_reason: "Catalog integration follows a proven run." +priority: medium +state_hub_task_id: "4a0fa4a7-5ca9-518a-ab92-a79a5e2cf93e" +``` + +### T04 — Catalog entries from types + +Let a type contribute reviewed catalog entries once, so that every case of that +type routes without a new catalog edit. Cases add parameter values, not routes. +`warden route find` should be able to answer "which SecurityCaseType covers this +need?" before anyone builds by hand. + + +2026-10-10 consolidation source prototype (not an accepted owner adapter API): +`src/warden/case_receipt_state.py` provides a pure immutable receipt reducer, +with exact case/type/adapter/catalog/acceptance/run-sheet pins, running intent +before dispatch, uncertain recovery after a lost result, no success replay, +explicit retry-safe failed-before-change retry and created-only reverse undo. +It executes nothing and accepts no command output. The caller must durably +persist running before any future side effect; storage/authenticated owner +receipts/live adapters are not implemented. Focused offline tests cover changed +pins, lost receipts, retry/refusal, uncertain rollback and shared-object +preservation. Source proposal: `docs/security-case-execution-proposal.md`. + +T03 remains open: typed owner execution/receipt contracts require review, the +Forgejo structural materialization needs owner receipts, and CLI dispatch, +contained sessions, native verification and provenance remain unimplemented. +There is a circularity to resolve with net-kingdom/ops-mason: materialization +cannot require mint-secret if mint-secret is itself a follow-through step whose +planning requires materialized. No case status is changed by this prototype. + + +2026-10-11: the exact execution-design memo version1 has confirmed native human +acceptance (approval e46b149b-ec3c-4b3b-98a7-dcb4934448ca). The approved proposal +bytes remain unchanged. `src/warden/case_run.py` now implements a closed v1 local +execution seam, independent admission/evidence callbacks, private transactional +SQLite receipt storage, process locking, running-before-dispatch, crash recovery +to uncertainty, explicit safe retry, separately required broker-grant and parent-session revocation +proofs, native owner reconciliation and created-only +reverse rollback. Thirty-seven kernel tests and fourteen reducer tests pass; +ruff checks pass. See `docs/security-case-execution-kernel-v1.md` for exact scope +and receipt provenance. No installed native adapter or live CLI execution is +claimed. Current prose types stay plan-only; acknowledged descriptor/type machine +seams and authenticated owner/caller receipts are still required. T03 progresses +for implementation but cannot finish before ordered native Forgejo acceptance. + +2026-10-16 source preparation (isolated checkout; no native acceptance): +T03's browser parameter mismatch now has a producer-validator-backed projection +in `case_inputs.py`, explicitly configured on the real Executor. Exact 1.1/1.2 +parameter definitions and all four reviewed producer modules are pinned; full case +validation admits reviewed security-sensitive metadata while unknown/fixed/secret +inputs and type/producer drift refuse. Only the reviewed descriptor target +reaches the owner callback; no parameter journal payload is added. Actual +Executor/producer tests cover positive versions and refusal/containment paths. +Acknowledged machine descriptors, authenticated owner evidence, real Forgejo +materialization and contained native sessions remain mandatory and unimplemented; +T03 stays progress, T04 stays wait. No type or case status changed. + +2026-10-16 producer source refresh: independently reviewed NetKingdom +`015694ee414c802dc2ef1fa0120dfed610086e69` optional descriptor-map repair. +Only the validator/evidence source pins change; exact parameter and transitive +source identity remain enforced. Actual canonical producer, Executor and reducer +suite passes 87 checks, including 14 new malformed-map integration cases; all +four missing/changed modules still refuse. Review: +`docs/reviews/2026-10-16-browser-producer-pin-refresh.md`. No adapter attestation, +native acceptance, credential use or privileged dispatch. T03 remains progress. diff --git a/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-integration-r1/commit.json b/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-integration-r1/commit.json new file mode 100644 index 0000000..8bc656c --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-integration-r1/commit.json @@ -0,0 +1,5 @@ +{ + "exit_code": 0, + "stdout": "[detached HEAD b593c87] test: isolate ordinary unit runs from ambient credentials and provider I/O\n 14 files changed, 592 insertions(+), 6 deletions(-)\n create mode 100644 docs/development/safe-unit-tests.md\n create mode 100644 scripts/run_unit_tests.py\n create mode 100644 tests/fixtures/workload-coverage/provenance.json\n create mode 100644 tests/fixtures/workload-coverage/rapp-core-hub/declarations/rapp.yaml\n create mode 100644 tests/fixtures/workload-coverage/rapp-issue-core/declarations/rapp.yaml\n create mode 100644 tests/fixtures/workload-coverage/rapp-qonto/declarations/rapp.yaml\n create mode 100644 tests/test_safe_unit_runner.py\n create mode 100644 tests/unit_isolation_probe.py\n", + "stderr": "" +} diff --git a/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-integration-r1/consistency.json b/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-integration-r1/consistency.json new file mode 100644 index 0000000..3675144 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-integration-r1/consistency.json @@ -0,0 +1,140 @@ +{ + "repo_slug": "ops-warden", + "repo_path": "/home/worsch/ops-warden", + "source_revision": "b593c879831378db7dc62adfabdfedf2d44ddd38", + "source_revision_reason": null, + "issues": [ + { + "severity": "WARN", + "check_id": "C-24", + "message": "Repo classification advisory: capability_tag 'security' is not a recommended family tag (allowed, check for synonym)", + "file_path": "", + "db_id": "", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-42", + "message": "Blocked workplan 'warden-wp-0027' has no task in status wait \u2014 nothing states what it waits for; qualify a wait task or re-status the workplan", + "file_path": "workplans/WARDEN-WP-0027-credential-governance-lockdown.md", + "db_id": "21528e8d-a049-523d-9ae1-da7a27cb8bbf", + "file_value": "blocked", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-37", + "message": "Residual workplan 'warden-wp-0034' has no origin/origin_ref provenance (not invented)", + "file_path": "workplans/WARDEN-WP-0034-layer-model-v07-conformance.md", + "db_id": "ae3ff76f-883d-5e2f-b6aa-144d61e8fdef", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-40", + "message": "blocker satisfied, task still wait: 'WARDEN-WP-0037-T03' in workplan 'WARDEN-WP-0037' \u2014 decision 1591daa1-6e85-4dfd-aadd-5a9fe1ef47fa is resolved", + "file_path": "workplans/WARDEN-WP-0037-whynot-design-forgejo-npm-lane.md#WARDEN-WP-0037-T03", + "db_id": "", + "file_value": "1591daa1-6e85-4dfd-aadd-5a9fe1ef47fa", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-37", + "message": "Residual workplan 'warden-wp-0040' has no origin/origin_ref provenance (not invented)", + "file_path": "workplans/WARDEN-WP-0040-unknown-zone-fail-closed-adoption.md", + "db_id": "c8ee441e-1be1-5219-910c-e79ff23cc9ec", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "INFO", + "check_id": "C-08", + "message": "Retired DB workstream 'adhoc-2026-06-29@retired-20260828' (id=1c0460b7\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work", + "file_path": "", + "db_id": "1c0460b7-bc8a-48db-96d4-681bce18ac91", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "INFO", + "check_id": "C-08", + "message": "Retired DB workstream 'adhoc-2026-08-17@retired-20260827' (id=8376eb89\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work", + "file_path": "", + "db_id": "8376eb89-36f2-52b0-a59a-2954b2e49349", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "INFO", + "check_id": "C-08", + "message": "Retired DB workstream 'adhoc-2026-08-11@retired-20260827' (id=bc6dd3e3\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work", + "file_path": "", + "db_id": "bc6dd3e3-ab2b-567a-9574-30d2650f20a2", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "INFO", + "check_id": "C-08", + "message": "Retired DB workstream 'ops-warden-adhoc-2026-06-27@retired-20260827' (id=142b171b\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work", + "file_path": "", + "db_id": "142b171b-c34b-4a45-91a5-c77e6d07ec6f", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "INFO", + "check_id": "C-08", + "message": "Retired DB workstream 'adhoc-2026-07-07@retired-20260827' (id=2e8bc783\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work", + "file_path": "", + "db_id": "2e8bc783-2e37-4b86-a691-177b18329783", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-28", + "message": "2 unread inbox message(s) older than 3 day(s): the-custodian:23172cb2, the-custodian:51e9eace", + "file_path": "", + "db_id": "", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-28", + "message": "3 unread message(s) lack thread_id for supersession tracking: the-custodian:d422aecb, the-custodian:23172cb2, the-custodian:51e9eace", + "file_path": "", + "db_id": "", + "file_value": "", + "db_value": "", + "fixable": false + } + ], + "fixes_applied": [ + "bindings: synced 51/51", + "C-17 cleared: pushed 1 backlogged commit(s)", + "push: pushed" + ], + "summary": { + "fail": 0, + "automation_error": 0, + "warn": 7, + "info": 5 + }, + "result": "warn" +} diff --git a/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-integration-r1/consistency.stderr.txt b/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-integration-r1/consistency.stderr.txt new file mode 100644 index 0000000..e69de29 diff --git a/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-integration-r1/full-unit-result.json b/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-integration-r1/full-unit-result.json new file mode 100644 index 0000000..32b4c76 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-integration-r1/full-unit-result.json @@ -0,0 +1,14 @@ +{ + "exit_code": 0, + "command": [ + "make", + "test", + "UNIT_PYTHON=/home/worsch/ops-warden/.venv/bin/python", + "PHASE_MEMORY_REPO=/home/worsch/phase-memory", + "TEST_ARGS=--netkingdom-root /home/worsch/net-kingdom -q" + ], + "expected": "785 passed,4 deselected", + "log_sha256": "75563bad2656a7566a7913cbacab6178c3943b9ed8f28ff6796dffe5e53da4a7", + "synthetic_parent_environment": true, + "real_credentials_or_provider_calls": false +} diff --git a/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-integration-r1/integrate.py b/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-integration-r1/integrate.py new file mode 100644 index 0000000..ba1e647 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-integration-r1/integrate.py @@ -0,0 +1,65 @@ +from pathlib import Path +import hashlib,json,re,subprocess +p=Path(__file__).resolve().parent;r=Path('/home/worsch/ops-warden');w=Path('/tmp/warden-safe-unit-integration-r1') +a=p.parent/'warden-safe-unit-runner-r1';v=p.parent/'warden-safe-unit-runner-review-r1' +author=json.loads((a/'implementation.json').read_text());review=json.loads((v/'review-r1.json').read_text());base=author['base'] +def git(root,*args):return subprocess.check_output(['git','-C',str(root),*args],stderr=subprocess.PIPE,text=True,timeout=60).strip() +def sha(f):return hashlib.sha256(f.read_bytes()).hexdigest() +assert git(r,'rev-parse','HEAD')==base and git(r,'ls-remote','origin','refs/heads/main').split()[0]==base +assert sha(a/'candidate.patch')=='1fe65076ce7e633be304fc779bf2c87b7ee1e4e460de55ca74355b5842d036f5' +assert sha(v/'reviewer-correction.patch')=='9407289f8da41ed6fff367baf566ad30f5510a2627c14e0ac0cf971bf9b465c9' +assert not w.exists() +protected={} +for name in ['WORK-RECORDS.md','.custodian-brief.md']: + protected[name]={'sha256':sha(r/name),'index':git(r,'ls-files','--stage','--',name)} + (p/(name+'.backup')).write_bytes((r/name).read_bytes()) +git(r,'worktree','add','--detach',str(w),base) +for packet,file in [(a,'candidate.patch'),(v,'reviewer-correction.patch')]: + git(w,'apply','--check',str(packet/file));git(w,'apply',str(packet/file)) +expected={row['path']:row['after_sha256'] for row in author['files']} +for name,row in review['correction']['files'].items():expected[name]=row['after_sha256'].split(':')[-1] +for name,want in expected.items():assert sha(w/name)==want,name +for name,want in review['protected_source_hashes'].items():assert sha(w/name)==want.split(':')[-1],name +# Root source disposition: accept the minimal reviewed corrections; original +# rejection and both negative controls stay preserved. This is test hygiene only. +(p/'root-disposition.json').write_text(json.dumps({'original_candidate':'rejected as reviewed','corrected_candidate':'accepted for source revalidation','fixes':['Classify O_TRUNC and O_APPEND opens as writes before the OS open.','Check closed fixture projection set and every recorded byte digest in default unit selection.'],'candidate_sha256':sha(a/'candidate.patch'),'correction_sha256':sha(v/'reviewer-correction.patch'),'remaining_limits':review['limits'],'native_acceptance':False},indent=2)+'\n') +sentinel=p/'synthetic-parent-home';sentinel.mkdir();(sentinel/'.vault-token').write_text('synthetic-never-a-real-token') +env={'HOME':str(sentinel),'PATH':'/usr/bin:/bin','BAO_TOKEN':'synthetic-never-a-real-token','VAULT_TOKEN':'synthetic-never-a-real-token','PYTHONPATH':'/synthetic-untrusted-pythonpath','PYTEST_ADDOPTS':'--synthetic-invalid-if-inherited','LANG':'C.UTF-8'} +cmd=['make','test','UNIT_PYTHON=/home/worsch/ops-warden/.venv/bin/python','PHASE_MEMORY_REPO=/home/worsch/phase-memory','TEST_ARGS=--netkingdom-root /home/worsch/net-kingdom -q'] +with (p/'full-unit-tests.log').open('w') as log: + result=subprocess.run(cmd,cwd=w,env=env,stdout=log,stderr=subprocess.STDOUT,timeout=180) +text=(p/'full-unit-tests.log').read_text() +(p/'full-unit-result.json').write_text(json.dumps({'exit_code':result.returncode,'command':cmd,'expected':'785 passed,4 deselected','log_sha256':sha(p/'full-unit-tests.log'),'synthetic_parent_environment':True,'real_credentials_or_provider_calls':False},indent=2)+'\n') +assert result.returncode==0 and re.search(r'785 passed, 4 deselected',text),'corrected ordinary suite failed; preserve scratch and logs' +name='workplans/WARDEN-WP-0044-security-case-follow-through.md';old=(w/name).read_text() +new=old+""" + +2026-10-17 root follow-through: the original safe-runner source candidate was +independently rejected for O_TRUNC open-write misclassification and unchecked +public fixture projection hashes. Both counterexamples are retained. The minimal +correction treats truncating/appending opens as writes and adds the closed +projection-byte provenance regression. Root inspected the two exact corrections +and ran ordinary make test on stable corrected source with only synthetic parent +HOME/token/Python/pytest sentinels: 785 passed, four deselected. No real credential +or native provider action occurred. Initial author failures and rejected review +are preserved in the Custodian warden-safe-unit-runner-r1 and +warden-safe-unit-runner-review-r1 packets, with root disposition/full result in +warden-safe-unit-integration-r1. This completes the known trusted-unit-entry +accident-prevention follow-through; direct pytest bypass, native/non-open/preopened +file-descriptor limits remain explicit. No OS sandbox, isolated agent assignment, +production grant or native T03 acceptance follows. T03 stays progress. +""" +assert re.findall(r'```task\n.*?```',old,re.S)==re.findall(r'```task\n.*?```',new,re.S) +(w/name).write_text(new) +paths=list(expected) +git(w,'diff','--check');git(w,'add','--',*paths) +result=subprocess.run(['git','-C',str(w),'commit','--only','-m','test: isolate ordinary unit runs from ambient credentials and provider I/O','--',*paths],capture_output=True,text=True,timeout=240) +(p/'commit.json').write_text(json.dumps({'exit_code':result.returncode,'stdout':result.stdout,'stderr':result.stderr},indent=2)+'\n');assert result.returncode==0,'normal commit refused' +head=git(w,'rev-parse','HEAD');assert set(git(w,'diff-tree','--no-commit-id','--name-only','-r',head).splitlines())==set(paths) +assert git(r,'rev-parse','HEAD')==base and git(r,'ls-remote','origin','refs/heads/main').split()[0]==base +for name,row in protected.items():assert sha(r/name)==row['sha256'] and git(r,'ls-files','--stage','--',name)==row['index'] +git(r,'merge','--ff-only',head) +for name,row in protected.items():assert sha(r/name)==row['sha256'] and git(r,'ls-files','--stage','--',name)==row['index'] +for name,want in review['protected_source_hashes'].items():assert sha(r/name)==want.split(':')[-1] +(p/'integration.json').write_text(json.dumps({'status':'integrated-corrected-source-not-published','base':base,'head':head,'paths':paths,'source_and_catalog_pins_unchanged':True,'owner_dirt_preserved':True,'original_four_task_blocks_preserved':True,'full_unit_tests':'785 passed,4 deselected','native_actions':False},indent=2)+'\n') +print(json.dumps({'status':'integrated','head':head,'files':len(paths),'tests':'785 passed,4 deselected','production_src_catalog_pin_changes':False})) diff --git a/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-integration-r1/integration.json b/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-integration-r1/integration.json new file mode 100644 index 0000000..4922fde --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-integration-r1/integration.json @@ -0,0 +1,26 @@ +{ + "status": "integrated-corrected-source-not-published", + "base": "922c26da0f3fcb0182352ea83e6ae06f29878580", + "head": "b593c879831378db7dc62adfabdfedf2d44ddd38", + "paths": [ + "Makefile", + "scripts/run_unit_tests.py", + "tests/test_safe_unit_runner.py", + "tests/unit_isolation_probe.py", + "tests/test_layer_conformance.py", + "tests/test_workload_join.py", + "tests/test_case_plan.py", + "tests/test_case_type.py", + "docs/development/safe-unit-tests.md", + "workplans/WARDEN-WP-0044-security-case-follow-through.md", + "tests/fixtures/workload-coverage/provenance.json", + "tests/fixtures/workload-coverage/rapp-core-hub/declarations/rapp.yaml", + "tests/fixtures/workload-coverage/rapp-issue-core/declarations/rapp.yaml", + "tests/fixtures/workload-coverage/rapp-qonto/declarations/rapp.yaml" + ], + "source_and_catalog_pins_unchanged": true, + "owner_dirt_preserved": true, + "original_four_task_blocks_preserved": true, + "full_unit_tests": "785 passed,4 deselected", + "native_actions": false +} diff --git a/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-integration-r1/prepare_publication.py b/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-integration-r1/prepare_publication.py new file mode 100644 index 0000000..0c38d28 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-integration-r1/prepare_publication.py @@ -0,0 +1,19 @@ +from pathlib import Path +import hashlib,json,subprocess +p=Path(__file__).resolve().parent;r=Path('/home/worsch/ops-warden') +i=json.loads((p/'integration.json').read_text());head=i['head'] +def git(*a):return subprocess.check_output(['git','-C',str(r),*a],text=True).strip() +assert git('rev-parse','HEAD')==head +assert git('ls-remote','origin','refs/heads/main').split()[0]==i['base'] +assert json.loads((p/'full-unit-result.json').read_text())['exit_code']==0 +cfg={'owner':'ops-warden','base_revision':i['base'],'message':'test: isolate ordinary unit runs from ambient credentials and provider I/O','files':[{'path':name,'sha256':hashlib.sha256((r/name).read_bytes()).hexdigest()} for name in i['paths']]} +(p/'publication-inputs.json').write_text(json.dumps(cfg,indent=2)+'\n') +commit=json.loads((p/'commit.json').read_text());assert commit['exit_code']==0 +commit.update(commit=head,base=i['base'],files=cfg['files']) +(p/'publication-commit.json').write_text(json.dumps(commit,indent=2)+'\n') +b=p/'publication-backup';assert not b.exists();b.mkdir() +rows=[] +for name in ['.custodian-brief.md','WORK-RECORDS.md']: + (b/(name+'.worktree')).write_bytes((r/name).read_bytes());rows.append({'path':name,'index':git('ls-files','--stage','--',name)}) +(b/'originals.json').write_text(json.dumps(rows,indent=2)+'\n') +print(json.dumps({'prepared_normal_publication_for':head,'files':len(i['paths'])})) diff --git a/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-integration-r1/projection.json b/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-integration-r1/projection.json new file mode 100644 index 0000000..6804807 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-integration-r1/projection.json @@ -0,0 +1,287 @@ +{ + "ok": true, + "status": "applied", + "repo_slug": "ops-warden", + "commit": "b593c879831378db7dc62adfabdfedf2d44ddd38", + "requests": 2, + "elapsed_ms": 4502.2, + "git": { + "upstream": "origin/main", + "behind": 0, + "ahead": 0 + }, + "identifiers": { + "schema": "repo-manager.identifier-assignment.v1", + "ok": true, + "executed": true, + "repo": "ops-warden", + "namespace": "helixforge", + "assignments": [], + "historical_invalid_identifiers": [], + "historical_identity_collisions": [], + "files_changed": [] + }, + "receipt": { + "schema": "state-hub.repository-projection-reconcile.v1", + "instance_role": "primary", + "instance_label": "railiance01", + "expected_commit": "b593c879831378db7dc62adfabdfedf2d44ddd38", + "derived_commit": "b593c879831378db7dc62adfabdfedf2d44ddd38", + "outcome": { + "schema": "state-hub.projection-reset.v1", + "repo_slug": "ops-warden", + "commit": "b593c879831378db7dc62adfabdfedf2d44ddd38", + "status": "applied", + "counts": { + "created": 0, + "updated": 51, + "retired": 0, + "refused": 0, + "released": 0, + "created_tasks": 0, + "updated_tasks": 0, + "cancelled_tasks": 0, + "created_edges": 0, + "removed_edges": 0, + "unresolved_edges": 0, + "external_edges": 17 + }, + "created": [], + "updated": [ + "WARDEN-WP-0001", + "WARDEN-WP-0002", + "WARDEN-WP-0003", + "WARDEN-WP-0004", + "WARDEN-WP-0005", + "WARDEN-WP-0006", + "WARDEN-WP-0007", + "WARDEN-WP-0008", + "WARDEN-WP-0009", + "WARDEN-WP-0010", + "WARDEN-WP-0011", + "WARDEN-WP-0012", + "WARDEN-WP-0013", + "WARDEN-WP-0014", + "WARDEN-WP-0015", + "WARDEN-WP-0016", + "WARDEN-WP-0017", + "WARDEN-WP-0018", + "WARDEN-WP-0019", + "WARDEN-WP-0020", + "WARDEN-WP-0021", + "WARDEN-WP-0022", + "WARDEN-WP-0023", + "WARDEN-WP-0024", + "WARDEN-WP-0025", + "WARDEN-WP-0026", + "WARDEN-WP-0027", + "WARDEN-WP-0028", + "WARDEN-WP-0029", + "WARDEN-WP-0030", + "WARDEN-WP-0031", + "WARDEN-WP-0032", + "WARDEN-WP-0033", + "WARDEN-WP-0034", + "WARDEN-WP-0035", + "WARDEN-WP-0036", + "WARDEN-WP-0037", + "WARDEN-WP-0038", + "WARDEN-WP-0039", + "WARDEN-WP-0040", + "WARDEN-WP-0041", + "WARDEN-WP-0042", + "WARDEN-WP-0043", + "WARDEN-WP-0044", + "WARDEN-WP-ADHOC-2026-06-27", + "WARDEN-WP-ADHOC-2026-06-29", + "WARDEN-WP-ADHOC-2026-07-07", + "WARDEN-WP-ADHOC-2026-08-11", + "WARDEN-WP-ADHOC-2026-08-17", + "WARDEN-WP-ADHOC-2026-09-08", + "WARDEN-WP-ADHOC-2026-10-03" + ], + "retired": [], + "released": [], + "created_tasks": [], + "updated_tasks": [], + "cancelled_tasks": [], + "created_edges": [], + "removed_edges": [], + "unresolved_edges": [], + "external_edges": [ + { + "workplan": "WARDEN-WP-0032", + "from": "WARDEN-WP-0032", + "target": "WARDEN-WP-0031", + "relationship_type": "blocks", + "from_workplan_id": "38c6a5f3-fb0d-5230-be85-f9e3ffc850f6", + "from_task_id": null, + "to_workplan_id": "739bad25-2345-5f4f-aaa3-cc4cd8c71f6c", + "to_task_id": null + }, + { + "workplan": "WARDEN-WP-0037", + "from": "WARDEN-WP-0037-T03", + "target": "SECRETS-WP-0006-T06", + "relationship_type": "starts_after", + "from_workplan_id": "42a097db-1c24-558e-a724-030bb2b4443e", + "from_task_id": "a8b1b855-ab34-5835-b9fd-5f48bc0b6817", + "to_workplan_id": null, + "to_task_id": "1431edae-5791-5892-8c4b-829419b537d2" + }, + { + "workplan": "WARDEN-WP-0033", + "from": "WARDEN-WP-0033", + "target": "WARDEN-WP-0030", + "relationship_type": "blocks", + "from_workplan_id": "4627d89b-4b00-562a-81e9-76e96f90fa7e", + "from_task_id": null, + "to_workplan_id": "da3367d5-890c-52c6-aa54-1bdd0f277342", + "to_task_id": null + }, + { + "workplan": "WARDEN-WP-0013", + "from": "WARDEN-WP-0013", + "target": "WARDEN-WP-0011", + "relationship_type": "blocks", + "from_workplan_id": "4678c41a-c1d0-48cd-9988-4ea0380e8258", + "from_task_id": null, + "to_workplan_id": "0a520f8e-01b4-48f1-9af3-2f3f69fd0672", + "to_task_id": null + }, + { + "workplan": "WARDEN-WP-0013", + "from": "WARDEN-WP-0013", + "target": "WARDEN-WP-0009", + "relationship_type": "blocks", + "from_workplan_id": "4678c41a-c1d0-48cd-9988-4ea0380e8258", + "from_task_id": null, + "to_workplan_id": "9213b262-e2f5-480e-a5bc-56635d5eb4c9", + "to_task_id": null + }, + { + "workplan": "WARDEN-WP-0013", + "from": "WARDEN-WP-0013", + "target": "WARDEN-WP-0008", + "relationship_type": "blocks", + "from_workplan_id": "4678c41a-c1d0-48cd-9988-4ea0380e8258", + "from_task_id": null, + "to_workplan_id": "a174963a-4ff1-4565-b19f-896cd4ff14a0", + "to_task_id": null + }, + { + "workplan": "WARDEN-WP-0013", + "from": "WARDEN-WP-0013", + "target": "WARDEN-WP-0010", + "relationship_type": "blocks", + "from_workplan_id": "4678c41a-c1d0-48cd-9988-4ea0380e8258", + "from_task_id": null, + "to_workplan_id": "e93de9fd-0192-4d02-bb7c-5e859fb76b9b", + "to_task_id": null + }, + { + "workplan": "WARDEN-WP-0031", + "from": "WARDEN-WP-0031", + "target": "WARDEN-WP-0007", + "relationship_type": "blocks", + "from_workplan_id": "739bad25-2345-5f4f-aaa3-cc4cd8c71f6c", + "from_task_id": null, + "to_workplan_id": "3718ac07-2fa2-47d0-a02a-c9a7b83a5ba9", + "to_task_id": null + }, + { + "workplan": "WARDEN-WP-0038", + "from": "WARDEN-WP-0038", + "target": "WARDEN-WP-0029", + "relationship_type": "blocks", + "from_workplan_id": "751ad530-e44b-52c4-b70e-cb47568a0179", + "from_task_id": null, + "to_workplan_id": "bbb3d9ec-d88d-5088-b4c0-55bfba0a10cf", + "to_task_id": null + }, + { + "workplan": "WARDEN-WP-0023", + "from": "WARDEN-WP-0023", + "target": "WARDEN-WP-0022", + "relationship_type": "blocks", + "from_workplan_id": "7bad1ec4-a7c2-4980-b8f9-49a7f5408574", + "from_task_id": null, + "to_workplan_id": "fc8afa28-68a7-4250-a19e-9754829f0cd5", + "to_task_id": null + }, + { + "workplan": "WARDEN-WP-0034", + "from": "WARDEN-WP-0034", + "target": "WARDEN-WP-0030", + "relationship_type": "blocks", + "from_workplan_id": "ae3ff76f-883d-5e2f-b6aa-144d61e8fdef", + "from_task_id": null, + "to_workplan_id": "da3367d5-890c-52c6-aa54-1bdd0f277342", + "to_task_id": null + }, + { + "workplan": "WARDEN-WP-0040", + "from": "WARDEN-WP-0040", + "target": "WARDEN-WP-0032", + "relationship_type": "blocks", + "from_workplan_id": "c8ee441e-1be1-5219-910c-e79ff23cc9ec", + "from_task_id": null, + "to_workplan_id": "38c6a5f3-fb0d-5230-be85-f9e3ffc850f6", + "to_task_id": null + }, + { + "workplan": "WARDEN-WP-0040", + "from": "WARDEN-WP-0040", + "target": "WARDEN-WP-0034", + "relationship_type": "blocks", + "from_workplan_id": "c8ee441e-1be1-5219-910c-e79ff23cc9ec", + "from_task_id": null, + "to_workplan_id": "ae3ff76f-883d-5e2f-b6aa-144d61e8fdef", + "to_task_id": null + }, + { + "workplan": "WARDEN-WP-0040", + "from": "WARDEN-WP-0040-T03", + "target": "WARDEN-WP-0040-T01", + "relationship_type": "starts_after", + "from_workplan_id": "c8ee441e-1be1-5219-910c-e79ff23cc9ec", + "from_task_id": "93eaf1f2-daaa-5e22-8804-85ca9433571c", + "to_workplan_id": null, + "to_task_id": "611f0901-9fb5-5954-8dd0-a860c5f0cbec" + }, + { + "workplan": "WARDEN-WP-0044", + "from": "WARDEN-WP-0044-T04", + "target": "WARDEN-WP-0044-T03", + "relationship_type": "starts_after", + "from_workplan_id": "f7e81013-6868-5d99-8528-ba75a442eca9", + "from_task_id": "4a0fa4a7-5ca9-518a-ab92-a79a5e2cf93e", + "to_workplan_id": null, + "to_task_id": "bcbf6c91-2c04-5c16-ae8c-5d6c06fd6b89" + }, + { + "workplan": "WARDEN-WP-0044", + "from": "WARDEN-WP-0044-T03", + "target": "MASON-WP-0010-T03", + "relationship_type": "starts_after", + "from_workplan_id": "f7e81013-6868-5d99-8528-ba75a442eca9", + "from_task_id": "bcbf6c91-2c04-5c16-ae8c-5d6c06fd6b89", + "to_workplan_id": null, + "to_task_id": "56cea6be-c613-586c-80cd-125340b374da" + }, + { + "workplan": "WARDEN-WP-0044", + "from": "WARDEN-WP-0044-T03", + "target": "WARDEN-WP-0044-T02", + "relationship_type": "starts_after", + "from_workplan_id": "f7e81013-6868-5d99-8528-ba75a442eca9", + "from_task_id": "bcbf6c91-2c04-5c16-ae8c-5d6c06fd6b89", + "to_workplan_id": null, + "to_task_id": "5f68c991-f490-58f8-a121-92f74073d272" + } + ], + "refused": [], + "notes": [] + } + } +} diff --git a/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-integration-r1/projection.stderr.txt b/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-integration-r1/projection.stderr.txt new file mode 100644 index 0000000..e69de29 diff --git a/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-integration-r1/publication-commit.json b/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-integration-r1/publication-commit.json new file mode 100644 index 0000000..d436812 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-integration-r1/publication-commit.json @@ -0,0 +1,65 @@ +{ + "exit_code": 0, + "stdout": "[detached HEAD b593c87] test: isolate ordinary unit runs from ambient credentials and provider I/O\n 14 files changed, 592 insertions(+), 6 deletions(-)\n create mode 100644 docs/development/safe-unit-tests.md\n create mode 100644 scripts/run_unit_tests.py\n create mode 100644 tests/fixtures/workload-coverage/provenance.json\n create mode 100644 tests/fixtures/workload-coverage/rapp-core-hub/declarations/rapp.yaml\n create mode 100644 tests/fixtures/workload-coverage/rapp-issue-core/declarations/rapp.yaml\n create mode 100644 tests/fixtures/workload-coverage/rapp-qonto/declarations/rapp.yaml\n create mode 100644 tests/test_safe_unit_runner.py\n create mode 100644 tests/unit_isolation_probe.py\n", + "stderr": "", + "commit": "b593c879831378db7dc62adfabdfedf2d44ddd38", + "base": "922c26da0f3fcb0182352ea83e6ae06f29878580", + "files": [ + { + "path": "Makefile", + "sha256": "8cf08ec680ed6bd323a1ce8475ade731dbeebcd64ffd43ecd169308f8a727940" + }, + { + "path": "scripts/run_unit_tests.py", + "sha256": "86880094b33114fdefb26691140b5c9a516cdba5fe6ddabd70fd5b7f18e9d2aa" + }, + { + "path": "tests/test_safe_unit_runner.py", + "sha256": "5408e7face98b01b92da077b0b2d115fcb370ec46e572ad5a69e3d0c10df8433" + }, + { + "path": "tests/unit_isolation_probe.py", + "sha256": "726d3e01ae21c1379588e1f08459449908cc8a75ffea1b337c33a1a888c66f59" + }, + { + "path": "tests/test_layer_conformance.py", + "sha256": "3edf0a0d49c3560278a564c477834335cfaf5243341cb9345f0d4bf8994db56c" + }, + { + "path": "tests/test_workload_join.py", + "sha256": "8ef0891daf4b7ac520430a514690364917110628edc760911a850195cffbdd4a" + }, + { + "path": "tests/test_case_plan.py", + "sha256": "944bc2a725ca1b7a10f4516c3ceed65f99627057dd15a859da4f8ffb3d97e71f" + }, + { + "path": "tests/test_case_type.py", + "sha256": "5a217a172e7e2ffbea66f1beee9a1c18bf5e97f9fb25b83cb96b6ba976cbb2a5" + }, + { + "path": "docs/development/safe-unit-tests.md", + "sha256": "b157c3e6956a41dcd9fc3912d0c7d09a664f11f08f0f8a8e4cae57fac9747cf7" + }, + { + "path": "workplans/WARDEN-WP-0044-security-case-follow-through.md", + "sha256": "44f88a5a84894d951add888206fe823d3d13019aca39a572ad8dfa15d510e05d" + }, + { + "path": "tests/fixtures/workload-coverage/provenance.json", + "sha256": "a4a203c1bd7bc5b8039d1b2525bea65dbae288a3f552969e4218cd5abda96990" + }, + { + "path": "tests/fixtures/workload-coverage/rapp-core-hub/declarations/rapp.yaml", + "sha256": "9ce902da0c77809f1094a634244652835b162f59682e9fa6923d8c8aa4ed4a3c" + }, + { + "path": "tests/fixtures/workload-coverage/rapp-issue-core/declarations/rapp.yaml", + "sha256": "a1956deecf2f0fbca44549e5ea5e8f1c4b74ebf91a9482c64c982491af0d923b" + }, + { + "path": "tests/fixtures/workload-coverage/rapp-qonto/declarations/rapp.yaml", + "sha256": "15aa9809e8d6a120c516be5ce9adfb72c00382140c171e791083ad117f503b3f" + } + ] +} diff --git a/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-integration-r1/publication-inputs.json b/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-integration-r1/publication-inputs.json new file mode 100644 index 0000000..753a508 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-integration-r1/publication-inputs.json @@ -0,0 +1,63 @@ +{ + "owner": "ops-warden", + "base_revision": "922c26da0f3fcb0182352ea83e6ae06f29878580", + "message": "test: isolate ordinary unit runs from ambient credentials and provider I/O", + "files": [ + { + "path": "Makefile", + "sha256": "8cf08ec680ed6bd323a1ce8475ade731dbeebcd64ffd43ecd169308f8a727940" + }, + { + "path": "scripts/run_unit_tests.py", + "sha256": "86880094b33114fdefb26691140b5c9a516cdba5fe6ddabd70fd5b7f18e9d2aa" + }, + { + "path": "tests/test_safe_unit_runner.py", + "sha256": "5408e7face98b01b92da077b0b2d115fcb370ec46e572ad5a69e3d0c10df8433" + }, + { + "path": "tests/unit_isolation_probe.py", + "sha256": "726d3e01ae21c1379588e1f08459449908cc8a75ffea1b337c33a1a888c66f59" + }, + { + "path": "tests/test_layer_conformance.py", + "sha256": "3edf0a0d49c3560278a564c477834335cfaf5243341cb9345f0d4bf8994db56c" + }, + { + "path": "tests/test_workload_join.py", + "sha256": "8ef0891daf4b7ac520430a514690364917110628edc760911a850195cffbdd4a" + }, + { + "path": "tests/test_case_plan.py", + "sha256": "944bc2a725ca1b7a10f4516c3ceed65f99627057dd15a859da4f8ffb3d97e71f" + }, + { + "path": "tests/test_case_type.py", + "sha256": "5a217a172e7e2ffbea66f1beee9a1c18bf5e97f9fb25b83cb96b6ba976cbb2a5" + }, + { + "path": "docs/development/safe-unit-tests.md", + "sha256": "b157c3e6956a41dcd9fc3912d0c7d09a664f11f08f0f8a8e4cae57fac9747cf7" + }, + { + "path": "workplans/WARDEN-WP-0044-security-case-follow-through.md", + "sha256": "44f88a5a84894d951add888206fe823d3d13019aca39a572ad8dfa15d510e05d" + }, + { + "path": "tests/fixtures/workload-coverage/provenance.json", + "sha256": "a4a203c1bd7bc5b8039d1b2525bea65dbae288a3f552969e4218cd5abda96990" + }, + { + "path": "tests/fixtures/workload-coverage/rapp-core-hub/declarations/rapp.yaml", + "sha256": "9ce902da0c77809f1094a634244652835b162f59682e9fa6923d8c8aa4ed4a3c" + }, + { + "path": "tests/fixtures/workload-coverage/rapp-issue-core/declarations/rapp.yaml", + "sha256": "a1956deecf2f0fbca44549e5ea5e8f1c4b74ebf91a9482c64c982491af0d923b" + }, + { + "path": "tests/fixtures/workload-coverage/rapp-qonto/declarations/rapp.yaml", + "sha256": "15aa9809e8d6a120c516be5ce9adfb72c00382140c171e791083ad117f503b3f" + } + ] +} diff --git a/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-integration-r1/root-disposition.json b/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-integration-r1/root-disposition.json new file mode 100644 index 0000000..89adf8e --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-integration-r1/root-disposition.json @@ -0,0 +1,18 @@ +{ + "original_candidate": "rejected as reviewed", + "corrected_candidate": "accepted for source revalidation", + "fixes": [ + "Classify O_TRUNC and O_APPEND opens as writes before the OS open.", + "Check closed fixture projection set and every recorded byte digest in default unit selection." + ], + "candidate_sha256": "1fe65076ce7e633be304fc779bf2c87b7ee1e4e460de55ca74355b5842d036f5", + "correction_sha256": "9407289f8da41ed6fff367baf566ad30f5510a2627c14e0ac0cf971bf9b465c9", + "remaining_limits": [ + "No full-suite recertification; author783-pass final run is attributed to author, not adopted as independent full-suite evidence.", + "Source roots/interpreters/libraries remain trusted and directory-readable by design. This is no OS isolation or grant.", + "Projection digest check proves shipped fixture consistency, not live estate freshness or source-owner authenticity.", + "No native acceptance,1.3 pin,draft promotion,credential investigation or external owner assent.", + "Original author initial failure logs remain intact." + ], + "native_acceptance": false +} diff --git a/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/README.md b/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/README.md new file mode 100644 index 0000000..1aaf67a --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/README.md @@ -0,0 +1,60 @@ +# Warden safe unit runner — implementation candidate + +Prepared against actual owner source `922c26da0f3fcb0182352ea83e6ae06f29878580`, +preserving the normal allocator commit after `ae7bc73`. This is an implementation +packet, not an independent review or canonical integration. `candidate.patch` +applies to that exact archive and all 14 resulting file hashes match +`implementation.json`. No canonical checkout, credential, catalog or pin changed. + +The substantive change is `scripts/run_unit_tests.py` plus the ordinary +`make test` entry. It selects an already installed interpreter directly, constructs +the environment/temporary HOME and installs the guard before pytest collection. +No uv/dependency resolution occurs. The supplied documentation lists the closed +Python, Bash/Git fixture and one synthetic-loopback exceptions and their limits. +Unexpected processes, provider commands, sockets and ambient file opens refuse. +Allowed Python children install guards, including the exact reverified BrowserInputs +validator; nested execution refuses. Child HOME/environment are reconstructed +even when a test monkeypatches synthetic token and HOME variables. + +Existing tests depended on workstation state. The candidate makes their public +workload declaration snapshots explicit with provenance; default-type/case tests +use the already established explicit NetKingdom source option. It preserves every +test assertion about counts, masking, file modes, original intent, validator +identity and parent/child containment. Existing fixed `printf` transport fixtures +and exact-byte contract doubles remain real local fixture processes rather than +mocked success. Native SSH-keygen integration tests remain deselected and denied +by this unit entry if selected without separate support. + +Validation: + +- Final ordinary `make test`: **783 passed, four deselected**, 62.70 seconds, + with synthetic input BAO_TOKEN/VAULT_TOKEN/HOME and hostile synthetic pytest/Python + environment options. No real token or home file was read or copied. +- Corrected affected focus: **117 passed**. New runner and regression code pass Ruff. +- Initial focused **136 passed/two failed** and initial full **13 failed/768 + passed/two skipped** are retained. They exposed explicit fixture dependencies; + failures were corrected rather than excluded. The first full was an intermediate + diagnostic state, with one configuration edit made while it ran; it is not + evidence for the stable final candidate. See `implementation.json`. +- Exact patch application/hashes verified in a second disposable archive. + +One local replay, using existing installed dependencies and explicit source roots: + +```bash +git clone --shared --no-hardlinks /home/worsch/ops-warden /tmp/warden-unit-review +git -C /tmp/warden-unit-review checkout --detach 922c26da0f3fcb0182352ea83e6ae06f29878580 +git -C /tmp/warden-unit-review apply /home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate.patch +make -C /tmp/warden-unit-review test UNIT_PYTHON=/home/worsch/ops-warden/.venv/bin/python PHASE_MEMORY_REPO=/home/worsch/phase-memory TEST_ARGS='--netkingdom-root /home/worsch/net-kingdom -q' +``` + +Use a fresh scratch path; existing paths should be inspected, not reset. Tests need +local Git history for the existing generated-catalog provenance assertion. +The existing `WARDEN-WP-0044-T03` paragraph is appended without altering its IDs, +status or dependencies. T03 remains progress and native acceptance is incomplete. + +This is an accident guard for trusted unit tests. It is not a hostile-code OS +sandbox, a production isolation grant or proof of native owner provenance. +Installed libraries/source roots remain trusted. Native Bash/Git fixture binaries +do not carry Python audit hooks. Already-open descriptors, native extensions and +all non-open filesystem syscalls are not fully contained. Direct pytest bypasses +the launcher. These limits are also recorded in the owner documentation. diff --git a/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate.patch b/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate.patch new file mode 100644 index 0000000..092539f --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate.patch @@ -0,0 +1,660 @@ +--- a/Makefile ++++ b/Makefile +@@ -1,5 +1,6 @@ + # ops-warden development and install targets + ROOT := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) ++UNIT_PYTHON ?= $(ROOT)/.venv/bin/python + PHASE_MEMORY_REPO ?= $(abspath $(ROOT)/../phase-memory) + + .DEFAULT_GOAL := help +@@ -43,7 +44,7 @@ + @warden memory status + + test: +- PYTHONPATH=src:$(PHASE_MEMORY_REPO)/src uv run pytest ++ "$(UNIT_PYTHON)" -I scripts/run_unit_tests.py --phase-memory-root "$(PHASE_MEMORY_REPO)" $(TEST_ARGS) + + lint: + uv run ruff check . +--- /dev/null ++++ b/scripts/run_unit_tests.py +@@ -0,0 +1,283 @@ ++"""Run trusted unit tests with no inherited credentials or external I/O. ++ ++This is an accident guard for this suite, not an OS sandbox for hostile Python. ++Direct pytest bypasses it. Use make test (already installed dependencies only). ++""" ++from __future__ import annotations ++ ++import hashlib ++import json ++import os ++from pathlib import Path ++import runpy ++import socket ++import subprocess ++import sys ++import tempfile ++ ++ROOT = Path(__file__).resolve().parents[1] ++SELF = Path(__file__).resolve() ++SHELL_FIXTURES = { ++ "printf 'the-secret-value'", "printf 'sekret'", "printf 'new'", ++ "printf '%s' '{\"wrap_info\":{\"token\":\"hvs.WRAP\"}}'", ++ "printf 'not-json'", ++} ++JOURNAL_CODE_SHA256 = '69aa18f302fd698104cb2253b4af0c3a077e72a0a427183e3dce1444c4926df9' ++ ++ ++class IsolationDenied(PermissionError, subprocess.SubprocessError): ++ """A unit test attempted an unreviewed external operation.""" ++ ++ ++def clean_environment(home: Path, temporary: Path) -> dict[str, str]: ++ # Construct, never copy/inventory the caller's environment. ++ return {'HOME': str(home), 'TMPDIR': str(temporary), 'PATH': '/usr/bin:/bin', ++ 'LANG': 'C.UTF-8', 'PYTHONDONTWRITEBYTECODE': '1', ++ 'PYTEST_DISABLE_PLUGIN_AUTOLOAD': '1'} ++ ++ ++def inside(path: Path, roots: tuple[Path, ...]) -> bool: ++ return any(path == root or root in path.parents for root in roots) ++ ++ ++class Guard: ++ def __init__(self, temporary: Path, read_roots: list[Path], child: bool = False, git_metadata: bool = False): ++ self.temporary = temporary.resolve() ++ self.read_roots = tuple(p.resolve() for p in read_roots) ++ self.child = child ++ self.git_metadata = git_metadata ++ self.loopback_test = False ++ self.endpoints: set[tuple[str, int]] = set() ++ self.launch = None ++ self.original_popen = subprocess.Popen ++ ++ def fixture_path(self, value: str) -> Path: ++ path = Path(value).resolve() ++ if not inside(path, (self.temporary,)): ++ raise IsolationDenied('unit isolation: non-fixture path') ++ return path ++ ++ def audit(self, event, args): ++ if event == 'open' and not isinstance(args[0], int): ++ path = Path(os.fsdecode(args[0])).resolve() ++ mode, flags = args[1:3] ++ write = ((isinstance(mode, str) and any(c in mode for c in 'wax+')) ++ or (isinstance(flags, int) and flags & (os.O_WRONLY | os.O_RDWR | os.O_CREAT))) ++ roots = (self.temporary,) if write else self.read_roots ++ if path != Path('/dev/null') and not inside(path, roots): ++ raise IsolationDenied('unit isolation: file outside reviewed roots') ++ if event == 'subprocess.Popen': ++ if self.launch is None or tuple(args[1]) != self.launch: ++ raise IsolationDenied('unit isolation: unexpected process') ++ if event in {'os.system', 'os.exec', 'os.posix_spawn', 'os.fork', 'os.forkpty'}: ++ raise IsolationDenied('unit isolation: native execution disabled') ++ if event.startswith('socket.'): ++ if not self.loopback_test or self.child: ++ raise IsolationDenied('unit isolation: sockets disabled') ++ if event == 'socket.__new__': ++ if args[1] != socket.AF_INET or args[2] & 15 != socket.SOCK_STREAM: ++ raise IsolationDenied('unit isolation: socket family/type denied') ++ elif event == 'socket.bind': ++ if args[1] != ('127.0.0.1', 0): ++ raise IsolationDenied('unit isolation: only ephemeral fixture bind') ++ elif event == 'socket.connect': ++ if args[1] not in self.endpoints: ++ raise IsolationDenied('unit isolation: only this fixture endpoint') ++ elif event not in {'socket.gethostname'}: ++ raise IsolationDenied('unit isolation: DNS/socket operation denied') ++ ++ def python_payload(self, argv): ++ """Closed subprocess inventory; every allowed Python child installs guard.""" ++ from warden import case_inputs ++ if (len(argv) == 5 and argv[:3] == [sys.executable, '-I', '-c'] ++ and argv[3] == case_inputs._VALIDATE): ++ directory = self.fixture_path(argv[4]) ++ if not directory.name.startswith('warden-case-validator-'): ++ raise IsolationDenied('unit isolation: validator fixture directory differs') ++ # Reverify copied modules immediately before launch, independently of caller. ++ for relative, digest in case_inputs.SOURCE_PINS.items(): ++ if hashlib.sha256((directory / relative).read_bytes()).hexdigest() != digest: ++ raise IsolationDenied('unit isolation: validator source pin differs') ++ return {'code': argv[3], 'args': [str(directory)]} ++ if len(argv) == 3 and argv[:2] == [sys.executable, '-c'] and argv[2] == 'print("SYNTHETIC-SECRET")': ++ return {'code': argv[2], 'args': []} ++ if (len(argv) == 6 and argv[:3] == [sys.executable, '-B', '-c'] ++ and hashlib.sha256(argv[3].encode()).hexdigest() == JOURNAL_CODE_SHA256): ++ self.fixture_path(argv[4]) ++ if len(argv[5]) > 192: ++ raise IsolationDenied('unit isolation: journal source bound') ++ return {'code': argv[3], 'args': argv[4:]} ++ if len(argv) >= 2 and argv[0] in (sys.executable, 'python3'): ++ script = Path(argv[1]).resolve() ++ args = argv[2:] ++ if script == ROOT / 'tests/unit_isolation_probe.py' and len(args) == 1 and args[0] in {'state', 'socket', 'provider', 'home', 'nested'}: ++ return {'script': str(script), 'args': args} ++ if script == ROOT / 'scripts/check_layer_conformance.py' and not args: ++ return {'script': str(script), 'args': args} ++ if script == ROOT / 'scripts/emit_high_risk_paths.py' and args == ['--check']: ++ return {'script': str(script), 'args': args, 'git_metadata': True} ++ if script == ROOT / 'scripts/check_principals_drift.py' and len(args) == 4 and args[::2] == ['--inventory', '--infra']: ++ for value in args[1::2]: ++ self.fixture_path(value) ++ return {'script': str(script), 'args': args} ++ if script == ROOT / 'scripts/build_flex_auth_registry.py': ++ if (len(args) in (3, 5) and Path(args[0]).resolve() == ROOT / 'examples/inventory.seed.yaml' ++ and args[-2] == '-o' and (len(args) == 3 or args[1] == '--zone-resolutions')): ++ self.fixture_path(args[-1]) ++ if len(args) == 5: ++ self.fixture_path(args[2]) ++ return {'script': str(script), 'args': args} ++ raise IsolationDenied('unit isolation: command not in reviewed subprocess inventory') ++ ++ def popen(self, argv, *pos, **kw): ++ if pos or kw.get('executable') or kw.get('preexec_fn') or kw.get('pass_fds') or kw.get('close_fds') is False: ++ raise IsolationDenied('unit isolation: unexpected process options') ++ if kw.pop('shell', False): ++ if self.child or not isinstance(argv, str) or argv not in SHELL_FIXTURES: ++ raise IsolationDenied('unit isolation: unreviewed shell command') ++ return self.spawn(['/bin/bash', '--noprofile', '--norc', '-c', argv], kw) ++ if not isinstance(argv, (list, tuple)): ++ raise IsolationDenied('unit isolation: argv list required') ++ argv = [os.fspath(x) for x in argv] ++ # Never run a real provider: the only shell fixture is an exact-byte double. ++ from warden.doubles import _DOUBLES ++ metadata_commands = (['git', 'log', '-1', '--format=%H %cI', '--', str(ROOT / 'registry/routing/catalog.yaml')], ++ ['git', 'status', '--porcelain', '--', str(ROOT / 'registry/routing/catalog.yaml')]) ++ if self.child and not (self.git_metadata and argv in metadata_commands): ++ raise IsolationDenied('unit isolation: nested process disabled') ++ candidate = Path(argv[0]) ++ if not candidate.is_absolute(): ++ import shutil ++ resolved = shutil.which(argv[0], path=kw.get('env', os.environ).get('PATH', '')) ++ candidate = Path(resolved) if resolved else candidate ++ if self.child and self.git_metadata: ++ if Path(kw.get('cwd', '.')).resolve() != ROOT: ++ raise IsolationDenied('unit isolation: metadata cwd differs') ++ command = ['/usr/bin/git', '-c', 'core.fsmonitor=false', '-c', 'core.hooksPath=/dev/null', '--no-pager', *argv[1:]] ++ elif candidate.is_absolute() and inside(candidate.resolve(), (self.temporary,)) and candidate.name in _DOUBLES: ++ if candidate.read_text() != _DOUBLES[candidate.name].script: ++ raise IsolationDenied('unit isolation: double bytes differ') ++ tail = argv[1:] ++ if (candidate.name == 'bao' and tail not in ( ++ ['kv', 'get', '-field=NPM_AUTH_TOKEN', 'platform/x/y'], ++ ['kv', 'get', '-field=API_KEY', 'platform/x/y/z'], ++ ['login', '-method=oidc'], ['write', 'secret/x'])) or (candidate.name == 'key-cape' and tail != ['login']): ++ raise IsolationDenied('unit isolation: double arguments differ') ++ command = ['/bin/bash', '--noprofile', '--norc', str(candidate), *tail] ++ else: ++ payload = self.python_payload(argv) ++ payload.update(temporary=str(self.temporary), read_roots=[str(x) for x in self.read_roots]) ++ command = [sys.executable, '-I', str(SELF), '--child', json.dumps(payload)] ++ return self.spawn(command, kw) ++ ++ def spawn(self, command, kw): ++ cwd = Path(kw.get('cwd') or ROOT).resolve() ++ if not inside(cwd, (ROOT, self.temporary)): ++ raise IsolationDenied('unit isolation: non-fixture working directory') ++ kw['cwd'] = str(cwd) ++ # env supplied by tests is never propagated: even synthetic token mutations ++ # remain in the parent. All approved children have only this fresh environment. ++ kw['env'] = clean_environment(self.temporary / 'home', self.temporary) ++ kw['env'].update(GIT_CONFIG_NOSYSTEM='1', GIT_CONFIG_GLOBAL='/dev/null', GIT_OPTIONAL_LOCKS='0') ++ self.launch = tuple(command) ++ try: ++ return self.original_popen(command, **kw) ++ finally: ++ self.launch = None ++ ++ def install(self): ++ sys.addaudithook(self.audit) ++ subprocess.Popen = self.popen ++ guard = self ++ original_socket = socket.socket ++ class FixtureSocket(original_socket): ++ def bind(self, address): ++ super().bind(address) ++ guard.endpoints.add(self.getsockname()) ++ socket.socket = FixtureSocket ++ def numeric_addresses(host, port, family=0, type=0, proto=0, flags=0): ++ if not guard.loopback_test or (host, port) not in guard.endpoints: ++ raise IsolationDenied('unit isolation: DNS/external endpoint denied') ++ return [(socket.AF_INET, socket.SOCK_STREAM, 6, '', (host, port))] ++ socket.getaddrinfo = numeric_addresses ++ def local_name(host=''): ++ if not guard.loopback_test or host != '127.0.0.1': ++ raise IsolationDenied('unit isolation: hostname lookup denied') ++ return 'localhost' ++ socket.getfqdn = local_name ++ ++ def pytest_configure(self, config): ++ config._unit_guard = self ++ ++ # Exact single synthetic HTTP test only; no other test gets a loopback lease. ++ def pytest_runtest_setup(self, item): ++ self.loopback_test = item.nodeid == 'tests/test_desk.py::test_approve_flow_http_dry' ++ self.endpoints.clear() ++ ++ def pytest_runtest_teardown(self, item): ++ self.loopback_test = False ++ self.endpoints.clear() ++ ++ ++def worker(temporary: Path, arguments: list[str], netkingdom: Path | None, phase: Path | None): ++ sys.dont_write_bytecode = True ++ tempfile.tempdir = str(temporary) ++ paths = [ROOT / 'src'] ++ if phase: ++ paths.append(phase / 'src') ++ # Explicit trusted source roots only, never ambient PYTHONPATH/plugins. ++ sys.path[:0] = [str(p) for p in paths] ++ roots = [ROOT, *paths, Path(sys.prefix), Path(sys.base_prefix), temporary] ++ if phase: ++ roots.append(phase / 'tests/fixtures/ops-warden-coordination-profile.json') ++ if netkingdom: ++ roots.append(netkingdom) ++ os.environ['WARDEN_TEST_NETKINGDOM_ROOT'] = str(netkingdom) ++ guard = Guard(temporary, roots) ++ guard.install() ++ # Pytest itself and collection run only after installation. ++ import pytest ++ raise SystemExit(pytest.main(['-p', 'no:cacheprovider', '--basetemp', str(temporary / 'pytest'), *arguments], plugins=[guard])) ++ ++ ++def main(): ++ if sys.argv[1:2] == ['--child']: ++ payload = json.loads(sys.argv[2]) ++ temporary = Path(payload['temporary']) ++ sys.dont_write_bytecode = True ++ tempfile.tempdir = str(temporary) ++ sys.path.insert(0, str(ROOT / 'src')) ++ guard = Guard(temporary, [Path(p) for p in payload['read_roots']], child=True, ++ git_metadata=payload.get('git_metadata', False)) ++ guard.install() ++ sys.argv = [payload.get('script', '-c'), *payload['args']] ++ if 'script' in payload: ++ runpy.run_path(payload['script'], run_name='__main__') ++ else: ++ exec(compile(payload['code'], '', 'exec'), {'__name__': '__main__'}) ++ return ++ if sys.argv[1:2] == ['--worker']: ++ temporary = Path(sys.argv[2]) ++ netkingdom = Path(sys.argv[3]) if sys.argv[3] else None ++ phase = Path(sys.argv[4]) if sys.argv[4] else None ++ worker(temporary, sys.argv[5:], netkingdom, phase) ++ return ++ import argparse ++ parser = argparse.ArgumentParser(description=__doc__) ++ parser.add_argument('--netkingdom-root', type=Path, default=ROOT.parent / 'net-kingdom') ++ parser.add_argument('--phase-memory-root', type=Path, default=ROOT.parent / 'phase-memory') ++ options, arguments = parser.parse_known_args() ++ netkingdom = options.netkingdom_root.resolve() if options.netkingdom_root.is_dir() else None ++ phase = options.phase_memory_root.resolve() if (options.phase_memory_root / 'src').is_dir() else None ++ with tempfile.TemporaryDirectory(prefix='warden-unit-') as directory: ++ temporary = Path(directory) ++ home = temporary / 'home' ++ home.mkdir() ++ result = subprocess.run([sys.executable, '-I', str(SELF), '--worker', str(temporary), ++ str(netkingdom) if netkingdom else '', str(phase) if phase else '', *arguments], ++ cwd=ROOT, env=clean_environment(home, temporary)) ++ raise SystemExit(result.returncode) ++ ++ ++if __name__ == '__main__': ++ main() +--- /dev/null ++++ b/tests/test_safe_unit_runner.py +@@ -0,0 +1,81 @@ ++"""Exercise installed parent and nested child controls, never real credentials.""" ++import json ++import os ++from pathlib import Path ++import socket ++import subprocess ++import sys ++ ++import pytest ++ ++ROOT = Path(__file__).resolve().parents[1] ++ ++ ++@pytest.fixture ++def guarded(request): ++ guard = getattr(request.config, '_unit_guard', None) ++ if guard is None: ++ pytest.skip('requires make test / scripts/run_unit_tests.py') ++ return guard ++ ++ ++def test_parent_environment_is_allowlisted_and_home_is_temporary(guarded): ++ # The external regression invocation sets synthetic BAO_TOKEN/VAULT_TOKEN/HOME. ++ assert 'BAO_TOKEN' not in os.environ ++ assert 'VAULT_TOKEN' not in os.environ ++ assert Path(os.environ['TMPDIR']) in Path.home().parents ++ assert 'PYTHONPATH' not in os.environ ++ ++ ++@pytest.mark.parametrize('command', [ ++ ['bao', 'token', 'lookup'], ['ssh', 'railiance01'], ['curl', 'http://127.0.0.1:8000'], ++ [sys.executable, '-c', 'import os; print(os.environ)'], ++]) ++def test_unexpected_process_refused_before_launch(guarded, command): ++ with pytest.raises(PermissionError, match='unit isolation'): ++ subprocess.run(command, check=True) ++ ++ ++def test_parent_socket_denies_even_unregistered_loopback(guarded): ++ with pytest.raises(PermissionError, match='unit isolation'): ++ socket.socket().connect(('127.0.0.1', 8000)) ++ ++ ++@pytest.mark.parametrize('path', ['/home/synthetic-ambient-unit-fixture/.vault-token', ++ '/root/.kube/config', '/proc/self/environ', ++ '/tmp/warden-synthetic-ambient-home/.vault-token']) ++def test_ambient_paths_refused_without_opening_them(guarded, path): ++ with pytest.raises(PermissionError, match='unit isolation'): ++ Path(path).read_bytes() ++ ++ ++def test_symlink_cannot_escape_fixture_root(guarded, tmp_path): ++ link = tmp_path / 'ambient' ++ link.symlink_to('/home/synthetic-ambient-unit-fixture/.vault-token') ++ with pytest.raises(PermissionError, match='unit isolation'): ++ link.read_bytes() ++ ++ ++def test_child_drops_even_synthetic_token_environment(guarded, monkeypatch): ++ monkeypatch.setenv('BAO_TOKEN', 'synthetic-only-never-a-real-token') ++ monkeypatch.setenv('VAULT_TOKEN', 'synthetic-only-never-a-real-token') ++ monkeypatch.setenv('HOME', '/home/synthetic-ambient-unit-fixture') ++ result = subprocess.run([sys.executable, str(ROOT / 'tests/unit_isolation_probe.py'), 'state'], ++ capture_output=True, text=True, check=True, env=dict(os.environ)) ++ assert json.loads(result.stdout) == {'bao_token_present': False, ++ 'vault_token_present': False, 'home_in_temporary': True} ++ ++ ++@pytest.mark.parametrize('mode', ['socket', 'provider', 'home', 'nested']) ++def test_guard_exists_in_each_allowed_python_child(guarded, mode): ++ result = subprocess.run([sys.executable, str(ROOT / 'tests/unit_isolation_probe.py'), mode], ++ capture_output=True, text=True, check=True) ++ assert result.stdout == 'denied\n' ++ ++ ++def test_forged_provider_double_refused(guarded, tmp_path): ++ provider = tmp_path / 'bao' ++ provider.write_text('#!/bin/bash\necho not-the-reviewed-fixture\n') ++ provider.chmod(0o700) ++ with pytest.raises(PermissionError, match='double bytes differ'): ++ subprocess.run([str(provider), 'token', 'lookup'], check=True) +--- /dev/null ++++ b/tests/unit_isolation_probe.py +@@ -0,0 +1,29 @@ ++"""Closed child regression probes; executed only under the unit runner guard.""" ++import json ++import os ++from pathlib import Path ++import socket ++import subprocess ++import sys ++ ++mode = sys.argv[1] ++if mode == 'state': ++ print(json.dumps({'bao_token_present': 'BAO_TOKEN' in os.environ, ++ 'vault_token_present': 'VAULT_TOKEN' in os.environ, ++ 'home_in_temporary': Path(os.environ['TMPDIR']) in Path.home().parents})) ++else: ++ try: ++ if mode == 'socket': ++ socket.socket().connect(('127.0.0.1', 8000)) ++ elif mode == 'provider': ++ subprocess.run(['bao', 'token', 'lookup'], check=True) ++ elif mode == 'home': ++ Path('/home/synthetic-ambient-unit-fixture/.vault-token').read_text() ++ elif mode == 'nested': ++ subprocess.run([sys.executable, '-c', 'print("SYNTHETIC-SECRET")'], check=True) ++ else: ++ raise AssertionError('unknown probe') ++ except PermissionError: ++ print('denied') ++ else: ++ raise AssertionError('external operation was not refused') +--- a/tests/test_layer_conformance.py ++++ b/tests/test_layer_conformance.py +@@ -273,7 +273,11 @@ + # --- classification coverage (v0.8 §6.4 obligation 3) ------------------------- + + def test_published_coverage_equals_measured_coverage(): +- """The published figure must equal what the repo actually measures. ++ """The published figure must equal its explicit public declaration snapshot. ++ ++ This is a deterministic source check, not a claim about current estate state. ++ Live measurement remains scripts/report_coverage.py with an explicit estate ++ root; tests never treat the developer HOME as an authority or fixture. + + ops-warden asked gate-house for §13.1's Coverage column and its figures are + that column's first entries, so their accuracy is ours to hold. The register +@@ -295,7 +299,7 @@ + published = yaml.safe_load((repo / "pep-stance.yaml").read_text())[ + "classification_coverage" + ] +- measured = module.measure() ++ measured = module.measure(repo / "tests/fixtures/workload-coverage") + + for population in ("signing_targets", "routing_lanes"): + assert published[population] == measured[population], population +--- a/tests/test_workload_join.py ++++ b/tests/test_workload_join.py +@@ -10,7 +10,8 @@ + + + def test_repo_catalog_uses_only_explicit_workload_references(): +- report = build(ROOT / "registry/routing/catalog.yaml", Path.home()) ++ report = build(ROOT / "registry/routing/catalog.yaml", ++ ROOT / "tests/fixtures/workload-coverage") + assert report["ok"] is True + assert len(report["resolved"]) == 4 + assert len(report["unknown"]) == 22 +--- a/tests/test_case_plan.py ++++ b/tests/test_case_plan.py +@@ -2,6 +2,7 @@ + from __future__ import annotations + + import copy ++import os + from pathlib import Path + + import pytest +@@ -12,7 +13,8 @@ + from warden.cli import app + from warden.routing.catalog import load_catalog + +-LIVE_CASE = Path.home() / "net-kingdom/security-cases/forgejo-netkingdom-oidc.yaml" ++LIVE_CASE = Path(os.environ.get("WARDEN_TEST_NETKINGDOM_ROOT", ++ Path(__file__).resolve().parents[2] / "net-kingdom")) / "security-cases/forgejo-netkingdom-oidc.yaml" + + TYPE = { + "apiVersion": "netkingdom.io/security-case-type/v0.1", +--- a/tests/test_case_type.py ++++ b/tests/test_case_type.py +@@ -2,6 +2,7 @@ + from __future__ import annotations + + import copy ++import os + from pathlib import Path + + import pytest +@@ -12,7 +13,8 @@ + from warden.cli import app + from warden.routing.catalog import load_catalog + +-LIVE_TYPE = Path.home() / "net-kingdom/security-case-types/oidc-browser-relying-party/type.yaml" ++LIVE_TYPE = Path(os.environ.get("WARDEN_TEST_NETKINGDOM_ROOT", ++ Path(__file__).resolve().parents[2] / "net-kingdom")) / "security-case-types/oidc-browser-relying-party/type.yaml" + + BASE = { + "apiVersion": "netkingdom.io/security-case-type/v0.1", +--- /dev/null ++++ b/docs/development/safe-unit-tests.md +@@ -0,0 +1,64 @@ ++# Safe unit-test entry point ++ ++Use `make test`, or `.venv/bin/python -I scripts/run_unit_tests.py`. Dependencies ++must already be installed; this path does not run uv, synchronize or download. ++`UNIT_PYTHON` may select another existing interpreter. `PHASE_MEMORY_REPO` remains ++an explicit local source option. Pass pytest options with `TEST_ARGS`, for example ++`make test TEST_ARGS='-q tests/test_safe_unit_runner.py'`. ++ ++The launcher creates an empty temporary HOME and temporary fixture root and starts ++an isolated (`-I`) Python interpreter with a constructed environment. Caller token, ++proxy, Python, pytest, XDG and Warden configuration variables are never copied or ++inventoried. Third-party pytest plugin autoload is disabled. Tests can explicitly ++introduce synthetic variables with monkeypatch, but allowed children always get ++the original temporary HOME and a fresh environment, never those variables. ++ ++Before pytest import/collection, a Python audit hook and subprocess wrapper: ++ ++- deny file opens outside the selected source roots, installed interpreter/library ++ roots and fixture root; writes through open are limited to the fixture root; ++- deny sockets, DNS and process creation except the closed fixture inventory; ++- refuse shell, executable, preexec and inherited-FD overrides outside that inventory; ++- install the same file/socket/process denial before executing every allowed ++ Python child, including BrowserInputs' exact validator code and reverified copied ++ module hashes. Validator children cannot start further processes. ++ ++The process inventory in `scripts/run_unit_tests.py` is intentionally small: ++BrowserInputs validation, four metadata-checker scripts with fixed argument shapes, ++the exact original-intent restart probe, the synthetic containment print, and ++closed guard-regression probes. Metadata checkers receive only fixture output ++paths. Only the generated-artifact checker child can run the exact read-only Git ++log/status queries for this repository's catalog, with global/system config and ++optional locks disabled, hooks disabled and fsmonitor disabled. Those local Git ++commands are not provider operations. ++ ++Two reviewed non-Python cases remain: exact-byte, temporary contract-double scripts ++with a closed argv list, and five exact fixed `printf` commands used by existing ++transport tests. They run Bash without profile/rc startup and without inherited ++environment; no arbitrary shell text, provider binary, SSH, kubectl or OpenBao ++command is permitted. Default unit selection excludes four integration-marked ++SSH-keygen tests. Selecting them explicitly does not grant their native commands. ++ ++Only `test_desk.py::test_approve_flow_http_dry` receives a network lease. It may ++bind `127.0.0.1:0` and connect to the endpoint actually allocated to that test's ++socket. DNS is replaced by numeric fixture resolution. Another loopback service ++(e.g. the real Hub on port 8000), Unix sockets and remote endpoints stay denied. ++The lease and registered endpoints are cleared after the test. ++ ++Coverage tests now use the explicit public declaration projection snapshot in ++`tests/fixtures/workload-coverage`, with provenance hashes. They compare the ++published source snapshot rather than silently consulting the workstation HOME. ++Run the existing coverage report separately with an explicit estate root when ++measuring current estate state. Tests of the acknowledged NetKingdom source use ++`--netkingdom-root` (default: sibling source checkout), never a HOME symlink. The ++optional Phase Memory library's one existing public coordination-profile fixture ++is explicitly readable alongside its source; no runtime memory store is inherited. ++ ++This is an accident guard for trusted unit tests, not a security sandbox for ++hostile Python, native extensions or hostile installed dependencies. It does not ++revoke already-open descriptors, prevent every filesystem mutation syscall, or ++provide OS process/network isolation. Fixed Bash/Git fixture processes rely on ++the reviewed executable and source/argument shapes; Python audit hooks do not run ++inside those binaries. Do not run untrusted test code under a privileged runtime. ++Direct `pytest` bypasses this launcher and is not equivalent validation. No ++production Warden boundary, grant, catalog, source pin or owner credential changes. +--- a/workplans/WARDEN-WP-0044-security-case-follow-through.md ++++ b/workplans/WARDEN-WP-0044-security-case-follow-through.md +@@ -354,3 +354,25 @@ + No credential lookup, live investigation or rotation is authorized by this note. + T03 remains progress; contained sessions, authenticated owner provenance, + materialization, exact type acknowledgment/pin and first native case remain open. ++ ++ ++2026-10-17 safe unit-runner source candidate: `make test` now selects an existing ++interpreter and `scripts/run_unit_tests.py`, which constructs the environment and ++empty temporary HOME before pytest collection, denies unexpected process/socket ++use and ambient file opens, and guards the pinned BrowserInputs child too. ++Closed fixture exceptions cover the synthetic desk endpoint, metadata checkers, ++exact-byte contract doubles and fixed transport prints; they confer no provider ++authority. Explicit public declaration fixtures replace ambient HOME source ++lookups. See `docs/development/safe-unit-tests.md` and Custodian packet ++`docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1`. ++Synthetic token, ambient-path, forged-provider and nested-child regressions are ++retained alongside the initial failed isolation runs and corrected results. This ++is a source candidate for the existing safe-test handoff, not an OS sandbox or ++native case acceptance. T03 remains progress; contained sessions, authenticated ++owner provenance, materialization, exact type acknowledgment/pin and first native ++case remain open. No credential lookup, live investigation or rotation occurred. ++ ++Candidate validation: ordinary `make test` under synthetic ambient token/HOME ++controls passes 783 tests with four integration-marked tests deselected. Affected ++focused checks pass 117 tests; new runner code/regressions pass Ruff. Retained ++initial failures exposed fixture dependencies and were corrected, not suppressed. +--- /dev/null ++++ b/tests/fixtures/workload-coverage/provenance.json +@@ -0,0 +1,20 @@ ++[ ++ { ++ "repo": "rapp-core-hub", ++ "source": "retained public snapshot from warden-1.3-kernel-fixes-r1 test preparation", ++ "source_sha256": "98e81fb1116aa0ac9dc1a3e7aef9dd87c6d2110d98d46fbfca2f76eec8f3a77b", ++ "projection_sha256": "9ce902da0c77809f1094a634244652835b162f59682e9fa6923d8c8aa4ed4a3c" ++ }, ++ { ++ "repo": "rapp-issue-core", ++ "source": "retained public snapshot from warden-1.3-kernel-fixes-r1 test preparation", ++ "source_sha256": "0425c9fe84e26b359b2a35fc37ef8fcb781de1aaa6229c59fcd246ee523514a5", ++ "projection_sha256": "a1956deecf2f0fbca44549e5ea5e8f1c4b74ebf91a9482c64c982491af0d923b" ++ }, ++ { ++ "repo": "rapp-qonto", ++ "source": "retained public snapshot from warden-1.3-kernel-fixes-r1 test preparation", ++ "source_sha256": "6279dc2f3ab09519dffa1ceeedb2095f1182960e0106109a87aa5c0052446883", ++ "projection_sha256": "15aa9809e8d6a120c516be5ce9adfb72c00382140c171e791083ad117f503b3f" ++ } ++] +--- /dev/null ++++ b/tests/fixtures/workload-coverage/rapp-core-hub/declarations/rapp.yaml +@@ -0,0 +1,15 @@ ++# Public declaration projection for the published coverage snapshot; not live evidence. ++rapp_id: rapp-core-hub ++workload_identity: ++ name: core-hub ++composition: ++ member_repos: ++ - deployables: ++ - core-hub-api ++ - core-hub-migrate ++ - hub-core-candidate ++ - hub-core-candidate-migrate ++ - repository-classification-publisher ++data_classification: confidential ++criticality: high ++readiness_state: verified +--- /dev/null ++++ b/tests/fixtures/workload-coverage/rapp-issue-core/declarations/rapp.yaml +@@ -0,0 +1,11 @@ ++# Public declaration projection for the published coverage snapshot; not live evidence. ++rapp_id: rapp-issue-core ++workload_identity: ++ name: issue-core ++composition: ++ member_repos: ++ - deployables: ++ - issue-core ++data_classification: confidential ++criticality: high ++readiness_state: verified +--- /dev/null ++++ b/tests/fixtures/workload-coverage/rapp-qonto/declarations/rapp.yaml +@@ -0,0 +1,11 @@ ++# Public declaration projection for the published coverage snapshot; not live evidence. ++rapp_id: rapp-qonto ++workload_identity: ++ name: qonto ++composition: ++ member_repos: ++ - deployables: ++ - rapp-qonto ++data_classification: restricted ++criticality: critical ++readiness_state: verified diff --git a/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/Makefile b/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/Makefile new file mode 100644 index 0000000..60a726a --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/Makefile @@ -0,0 +1,50 @@ +# ops-warden development and install targets +ROOT := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) +UNIT_PYTHON ?= $(ROOT)/.venv/bin/python +PHASE_MEMORY_REPO ?= $(abspath $(ROOT)/../phase-memory) + +.DEFAULT_GOAL := help + +.PHONY: help sync install install-warden install-memory install-all check-memory verify-memory test lint + +help: + @echo "ops-warden make targets" + @echo "" + @echo " make sync # uv sync (dev dependencies)" + @echo " make install # install warden CLI (SSH only, no phase-memory)" + @echo " make install-memory # reinstall warden with editable phase-memory" + @echo " make install-all # sync + warden + phase-memory (recommended)" + @echo " make check-memory # fail unless phase-memory is importable by warden" + @echo " make verify-memory # check-memory + warden memory status" + @echo " make test # unit tests" + @echo " make lint # ruff check" + @echo "" + @echo "Override phase-memory path: make install-all PHASE_MEMORY_REPO=/path/to/phase-memory" + +sync: + uv sync + +install-warden: + uv tool install . --force --reinstall-package ops-warden + +install-memory: + @test -d "$(PHASE_MEMORY_REPO)" || (echo "ERROR: phase-memory not found at $(PHASE_MEMORY_REPO). Clone it or set PHASE_MEMORY_REPO."; exit 1) + @test -f "$(PHASE_MEMORY_REPO)/pyproject.toml" || (echo "ERROR: $(PHASE_MEMORY_REPO) is not a phase-memory checkout."; exit 1) + uv tool install . --with-editable "$(PHASE_MEMORY_REPO)" --force --reinstall-package ops-warden + +install: install-warden + +install-all: sync install-memory + +check-memory: + @warden memory status --json >/dev/null 2>&1 || (echo "ERROR: phase-memory is not available to warden. Run: make install-memory"; exit 1) + @echo "phase-memory: ok (warden memory status succeeded)" + +verify-memory: check-memory + @warden memory status + +test: + "$(UNIT_PYTHON)" -I scripts/run_unit_tests.py --phase-memory-root "$(PHASE_MEMORY_REPO)" $(TEST_ARGS) + +lint: + uv run ruff check . diff --git a/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/docs/development/safe-unit-tests.md b/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/docs/development/safe-unit-tests.md new file mode 100644 index 0000000..6600c6a --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/docs/development/safe-unit-tests.md @@ -0,0 +1,64 @@ +# Safe unit-test entry point + +Use `make test`, or `.venv/bin/python -I scripts/run_unit_tests.py`. Dependencies +must already be installed; this path does not run uv, synchronize or download. +`UNIT_PYTHON` may select another existing interpreter. `PHASE_MEMORY_REPO` remains +an explicit local source option. Pass pytest options with `TEST_ARGS`, for example +`make test TEST_ARGS='-q tests/test_safe_unit_runner.py'`. + +The launcher creates an empty temporary HOME and temporary fixture root and starts +an isolated (`-I`) Python interpreter with a constructed environment. Caller token, +proxy, Python, pytest, XDG and Warden configuration variables are never copied or +inventoried. Third-party pytest plugin autoload is disabled. Tests can explicitly +introduce synthetic variables with monkeypatch, but allowed children always get +the original temporary HOME and a fresh environment, never those variables. + +Before pytest import/collection, a Python audit hook and subprocess wrapper: + +- deny file opens outside the selected source roots, installed interpreter/library + roots and fixture root; writes through open are limited to the fixture root; +- deny sockets, DNS and process creation except the closed fixture inventory; +- refuse shell, executable, preexec and inherited-FD overrides outside that inventory; +- install the same file/socket/process denial before executing every allowed + Python child, including BrowserInputs' exact validator code and reverified copied + module hashes. Validator children cannot start further processes. + +The process inventory in `scripts/run_unit_tests.py` is intentionally small: +BrowserInputs validation, four metadata-checker scripts with fixed argument shapes, +the exact original-intent restart probe, the synthetic containment print, and +closed guard-regression probes. Metadata checkers receive only fixture output +paths. Only the generated-artifact checker child can run the exact read-only Git +log/status queries for this repository's catalog, with global/system config and +optional locks disabled, hooks disabled and fsmonitor disabled. Those local Git +commands are not provider operations. + +Two reviewed non-Python cases remain: exact-byte, temporary contract-double scripts +with a closed argv list, and five exact fixed `printf` commands used by existing +transport tests. They run Bash without profile/rc startup and without inherited +environment; no arbitrary shell text, provider binary, SSH, kubectl or OpenBao +command is permitted. Default unit selection excludes four integration-marked +SSH-keygen tests. Selecting them explicitly does not grant their native commands. + +Only `test_desk.py::test_approve_flow_http_dry` receives a network lease. It may +bind `127.0.0.1:0` and connect to the endpoint actually allocated to that test's +socket. DNS is replaced by numeric fixture resolution. Another loopback service +(e.g. the real Hub on port 8000), Unix sockets and remote endpoints stay denied. +The lease and registered endpoints are cleared after the test. + +Coverage tests now use the explicit public declaration projection snapshot in +`tests/fixtures/workload-coverage`, with provenance hashes. They compare the +published source snapshot rather than silently consulting the workstation HOME. +Run the existing coverage report separately with an explicit estate root when +measuring current estate state. Tests of the acknowledged NetKingdom source use +`--netkingdom-root` (default: sibling source checkout), never a HOME symlink. The +optional Phase Memory library's one existing public coordination-profile fixture +is explicitly readable alongside its source; no runtime memory store is inherited. + +This is an accident guard for trusted unit tests, not a security sandbox for +hostile Python, native extensions or hostile installed dependencies. It does not +revoke already-open descriptors, prevent every filesystem mutation syscall, or +provide OS process/network isolation. Fixed Bash/Git fixture processes rely on +the reviewed executable and source/argument shapes; Python audit hooks do not run +inside those binaries. Do not run untrusted test code under a privileged runtime. +Direct `pytest` bypasses this launcher and is not equivalent validation. No +production Warden boundary, grant, catalog, source pin or owner credential changes. diff --git a/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/scripts/run_unit_tests.py b/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/scripts/run_unit_tests.py new file mode 100644 index 0000000..3616a92 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/scripts/run_unit_tests.py @@ -0,0 +1,283 @@ +"""Run trusted unit tests with no inherited credentials or external I/O. + +This is an accident guard for this suite, not an OS sandbox for hostile Python. +Direct pytest bypasses it. Use make test (already installed dependencies only). +""" +from __future__ import annotations + +import hashlib +import json +import os +from pathlib import Path +import runpy +import socket +import subprocess +import sys +import tempfile + +ROOT = Path(__file__).resolve().parents[1] +SELF = Path(__file__).resolve() +SHELL_FIXTURES = { + "printf 'the-secret-value'", "printf 'sekret'", "printf 'new'", + "printf '%s' '{\"wrap_info\":{\"token\":\"hvs.WRAP\"}}'", + "printf 'not-json'", +} +JOURNAL_CODE_SHA256 = '69aa18f302fd698104cb2253b4af0c3a077e72a0a427183e3dce1444c4926df9' + + +class IsolationDenied(PermissionError, subprocess.SubprocessError): + """A unit test attempted an unreviewed external operation.""" + + +def clean_environment(home: Path, temporary: Path) -> dict[str, str]: + # Construct, never copy/inventory the caller's environment. + return {'HOME': str(home), 'TMPDIR': str(temporary), 'PATH': '/usr/bin:/bin', + 'LANG': 'C.UTF-8', 'PYTHONDONTWRITEBYTECODE': '1', + 'PYTEST_DISABLE_PLUGIN_AUTOLOAD': '1'} + + +def inside(path: Path, roots: tuple[Path, ...]) -> bool: + return any(path == root or root in path.parents for root in roots) + + +class Guard: + def __init__(self, temporary: Path, read_roots: list[Path], child: bool = False, git_metadata: bool = False): + self.temporary = temporary.resolve() + self.read_roots = tuple(p.resolve() for p in read_roots) + self.child = child + self.git_metadata = git_metadata + self.loopback_test = False + self.endpoints: set[tuple[str, int]] = set() + self.launch = None + self.original_popen = subprocess.Popen + + def fixture_path(self, value: str) -> Path: + path = Path(value).resolve() + if not inside(path, (self.temporary,)): + raise IsolationDenied('unit isolation: non-fixture path') + return path + + def audit(self, event, args): + if event == 'open' and not isinstance(args[0], int): + path = Path(os.fsdecode(args[0])).resolve() + mode, flags = args[1:3] + write = ((isinstance(mode, str) and any(c in mode for c in 'wax+')) + or (isinstance(flags, int) and flags & (os.O_WRONLY | os.O_RDWR | os.O_CREAT))) + roots = (self.temporary,) if write else self.read_roots + if path != Path('/dev/null') and not inside(path, roots): + raise IsolationDenied('unit isolation: file outside reviewed roots') + if event == 'subprocess.Popen': + if self.launch is None or tuple(args[1]) != self.launch: + raise IsolationDenied('unit isolation: unexpected process') + if event in {'os.system', 'os.exec', 'os.posix_spawn', 'os.fork', 'os.forkpty'}: + raise IsolationDenied('unit isolation: native execution disabled') + if event.startswith('socket.'): + if not self.loopback_test or self.child: + raise IsolationDenied('unit isolation: sockets disabled') + if event == 'socket.__new__': + if args[1] != socket.AF_INET or args[2] & 15 != socket.SOCK_STREAM: + raise IsolationDenied('unit isolation: socket family/type denied') + elif event == 'socket.bind': + if args[1] != ('127.0.0.1', 0): + raise IsolationDenied('unit isolation: only ephemeral fixture bind') + elif event == 'socket.connect': + if args[1] not in self.endpoints: + raise IsolationDenied('unit isolation: only this fixture endpoint') + elif event not in {'socket.gethostname'}: + raise IsolationDenied('unit isolation: DNS/socket operation denied') + + def python_payload(self, argv): + """Closed subprocess inventory; every allowed Python child installs guard.""" + from warden import case_inputs + if (len(argv) == 5 and argv[:3] == [sys.executable, '-I', '-c'] + and argv[3] == case_inputs._VALIDATE): + directory = self.fixture_path(argv[4]) + if not directory.name.startswith('warden-case-validator-'): + raise IsolationDenied('unit isolation: validator fixture directory differs') + # Reverify copied modules immediately before launch, independently of caller. + for relative, digest in case_inputs.SOURCE_PINS.items(): + if hashlib.sha256((directory / relative).read_bytes()).hexdigest() != digest: + raise IsolationDenied('unit isolation: validator source pin differs') + return {'code': argv[3], 'args': [str(directory)]} + if len(argv) == 3 and argv[:2] == [sys.executable, '-c'] and argv[2] == 'print("SYNTHETIC-SECRET")': + return {'code': argv[2], 'args': []} + if (len(argv) == 6 and argv[:3] == [sys.executable, '-B', '-c'] + and hashlib.sha256(argv[3].encode()).hexdigest() == JOURNAL_CODE_SHA256): + self.fixture_path(argv[4]) + if len(argv[5]) > 192: + raise IsolationDenied('unit isolation: journal source bound') + return {'code': argv[3], 'args': argv[4:]} + if len(argv) >= 2 and argv[0] in (sys.executable, 'python3'): + script = Path(argv[1]).resolve() + args = argv[2:] + if script == ROOT / 'tests/unit_isolation_probe.py' and len(args) == 1 and args[0] in {'state', 'socket', 'provider', 'home', 'nested'}: + return {'script': str(script), 'args': args} + if script == ROOT / 'scripts/check_layer_conformance.py' and not args: + return {'script': str(script), 'args': args} + if script == ROOT / 'scripts/emit_high_risk_paths.py' and args == ['--check']: + return {'script': str(script), 'args': args, 'git_metadata': True} + if script == ROOT / 'scripts/check_principals_drift.py' and len(args) == 4 and args[::2] == ['--inventory', '--infra']: + for value in args[1::2]: + self.fixture_path(value) + return {'script': str(script), 'args': args} + if script == ROOT / 'scripts/build_flex_auth_registry.py': + if (len(args) in (3, 5) and Path(args[0]).resolve() == ROOT / 'examples/inventory.seed.yaml' + and args[-2] == '-o' and (len(args) == 3 or args[1] == '--zone-resolutions')): + self.fixture_path(args[-1]) + if len(args) == 5: + self.fixture_path(args[2]) + return {'script': str(script), 'args': args} + raise IsolationDenied('unit isolation: command not in reviewed subprocess inventory') + + def popen(self, argv, *pos, **kw): + if pos or kw.get('executable') or kw.get('preexec_fn') or kw.get('pass_fds') or kw.get('close_fds') is False: + raise IsolationDenied('unit isolation: unexpected process options') + if kw.pop('shell', False): + if self.child or not isinstance(argv, str) or argv not in SHELL_FIXTURES: + raise IsolationDenied('unit isolation: unreviewed shell command') + return self.spawn(['/bin/bash', '--noprofile', '--norc', '-c', argv], kw) + if not isinstance(argv, (list, tuple)): + raise IsolationDenied('unit isolation: argv list required') + argv = [os.fspath(x) for x in argv] + # Never run a real provider: the only shell fixture is an exact-byte double. + from warden.doubles import _DOUBLES + metadata_commands = (['git', 'log', '-1', '--format=%H %cI', '--', str(ROOT / 'registry/routing/catalog.yaml')], + ['git', 'status', '--porcelain', '--', str(ROOT / 'registry/routing/catalog.yaml')]) + if self.child and not (self.git_metadata and argv in metadata_commands): + raise IsolationDenied('unit isolation: nested process disabled') + candidate = Path(argv[0]) + if not candidate.is_absolute(): + import shutil + resolved = shutil.which(argv[0], path=kw.get('env', os.environ).get('PATH', '')) + candidate = Path(resolved) if resolved else candidate + if self.child and self.git_metadata: + if Path(kw.get('cwd', '.')).resolve() != ROOT: + raise IsolationDenied('unit isolation: metadata cwd differs') + command = ['/usr/bin/git', '-c', 'core.fsmonitor=false', '-c', 'core.hooksPath=/dev/null', '--no-pager', *argv[1:]] + elif candidate.is_absolute() and inside(candidate.resolve(), (self.temporary,)) and candidate.name in _DOUBLES: + if candidate.read_text() != _DOUBLES[candidate.name].script: + raise IsolationDenied('unit isolation: double bytes differ') + tail = argv[1:] + if (candidate.name == 'bao' and tail not in ( + ['kv', 'get', '-field=NPM_AUTH_TOKEN', 'platform/x/y'], + ['kv', 'get', '-field=API_KEY', 'platform/x/y/z'], + ['login', '-method=oidc'], ['write', 'secret/x'])) or (candidate.name == 'key-cape' and tail != ['login']): + raise IsolationDenied('unit isolation: double arguments differ') + command = ['/bin/bash', '--noprofile', '--norc', str(candidate), *tail] + else: + payload = self.python_payload(argv) + payload.update(temporary=str(self.temporary), read_roots=[str(x) for x in self.read_roots]) + command = [sys.executable, '-I', str(SELF), '--child', json.dumps(payload)] + return self.spawn(command, kw) + + def spawn(self, command, kw): + cwd = Path(kw.get('cwd') or ROOT).resolve() + if not inside(cwd, (ROOT, self.temporary)): + raise IsolationDenied('unit isolation: non-fixture working directory') + kw['cwd'] = str(cwd) + # env supplied by tests is never propagated: even synthetic token mutations + # remain in the parent. All approved children have only this fresh environment. + kw['env'] = clean_environment(self.temporary / 'home', self.temporary) + kw['env'].update(GIT_CONFIG_NOSYSTEM='1', GIT_CONFIG_GLOBAL='/dev/null', GIT_OPTIONAL_LOCKS='0') + self.launch = tuple(command) + try: + return self.original_popen(command, **kw) + finally: + self.launch = None + + def install(self): + sys.addaudithook(self.audit) + subprocess.Popen = self.popen + guard = self + original_socket = socket.socket + class FixtureSocket(original_socket): + def bind(self, address): + super().bind(address) + guard.endpoints.add(self.getsockname()) + socket.socket = FixtureSocket + def numeric_addresses(host, port, family=0, type=0, proto=0, flags=0): + if not guard.loopback_test or (host, port) not in guard.endpoints: + raise IsolationDenied('unit isolation: DNS/external endpoint denied') + return [(socket.AF_INET, socket.SOCK_STREAM, 6, '', (host, port))] + socket.getaddrinfo = numeric_addresses + def local_name(host=''): + if not guard.loopback_test or host != '127.0.0.1': + raise IsolationDenied('unit isolation: hostname lookup denied') + return 'localhost' + socket.getfqdn = local_name + + def pytest_configure(self, config): + config._unit_guard = self + + # Exact single synthetic HTTP test only; no other test gets a loopback lease. + def pytest_runtest_setup(self, item): + self.loopback_test = item.nodeid == 'tests/test_desk.py::test_approve_flow_http_dry' + self.endpoints.clear() + + def pytest_runtest_teardown(self, item): + self.loopback_test = False + self.endpoints.clear() + + +def worker(temporary: Path, arguments: list[str], netkingdom: Path | None, phase: Path | None): + sys.dont_write_bytecode = True + tempfile.tempdir = str(temporary) + paths = [ROOT / 'src'] + if phase: + paths.append(phase / 'src') + # Explicit trusted source roots only, never ambient PYTHONPATH/plugins. + sys.path[:0] = [str(p) for p in paths] + roots = [ROOT, *paths, Path(sys.prefix), Path(sys.base_prefix), temporary] + if phase: + roots.append(phase / 'tests/fixtures/ops-warden-coordination-profile.json') + if netkingdom: + roots.append(netkingdom) + os.environ['WARDEN_TEST_NETKINGDOM_ROOT'] = str(netkingdom) + guard = Guard(temporary, roots) + guard.install() + # Pytest itself and collection run only after installation. + import pytest + raise SystemExit(pytest.main(['-p', 'no:cacheprovider', '--basetemp', str(temporary / 'pytest'), *arguments], plugins=[guard])) + + +def main(): + if sys.argv[1:2] == ['--child']: + payload = json.loads(sys.argv[2]) + temporary = Path(payload['temporary']) + sys.dont_write_bytecode = True + tempfile.tempdir = str(temporary) + sys.path.insert(0, str(ROOT / 'src')) + guard = Guard(temporary, [Path(p) for p in payload['read_roots']], child=True, + git_metadata=payload.get('git_metadata', False)) + guard.install() + sys.argv = [payload.get('script', '-c'), *payload['args']] + if 'script' in payload: + runpy.run_path(payload['script'], run_name='__main__') + else: + exec(compile(payload['code'], '', 'exec'), {'__name__': '__main__'}) + return + if sys.argv[1:2] == ['--worker']: + temporary = Path(sys.argv[2]) + netkingdom = Path(sys.argv[3]) if sys.argv[3] else None + phase = Path(sys.argv[4]) if sys.argv[4] else None + worker(temporary, sys.argv[5:], netkingdom, phase) + return + import argparse + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--netkingdom-root', type=Path, default=ROOT.parent / 'net-kingdom') + parser.add_argument('--phase-memory-root', type=Path, default=ROOT.parent / 'phase-memory') + options, arguments = parser.parse_known_args() + netkingdom = options.netkingdom_root.resolve() if options.netkingdom_root.is_dir() else None + phase = options.phase_memory_root.resolve() if (options.phase_memory_root / 'src').is_dir() else None + with tempfile.TemporaryDirectory(prefix='warden-unit-') as directory: + temporary = Path(directory) + home = temporary / 'home' + home.mkdir() + result = subprocess.run([sys.executable, '-I', str(SELF), '--worker', str(temporary), + str(netkingdom) if netkingdom else '', str(phase) if phase else '', *arguments], + cwd=ROOT, env=clean_environment(home, temporary)) + raise SystemExit(result.returncode) + + +if __name__ == '__main__': + main() diff --git a/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/tests/fixtures/workload-coverage/provenance.json b/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/tests/fixtures/workload-coverage/provenance.json new file mode 100644 index 0000000..6340fb1 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/tests/fixtures/workload-coverage/provenance.json @@ -0,0 +1,20 @@ +[ + { + "repo": "rapp-core-hub", + "source": "retained public snapshot from warden-1.3-kernel-fixes-r1 test preparation", + "source_sha256": "98e81fb1116aa0ac9dc1a3e7aef9dd87c6d2110d98d46fbfca2f76eec8f3a77b", + "projection_sha256": "9ce902da0c77809f1094a634244652835b162f59682e9fa6923d8c8aa4ed4a3c" + }, + { + "repo": "rapp-issue-core", + "source": "retained public snapshot from warden-1.3-kernel-fixes-r1 test preparation", + "source_sha256": "0425c9fe84e26b359b2a35fc37ef8fcb781de1aaa6229c59fcd246ee523514a5", + "projection_sha256": "a1956deecf2f0fbca44549e5ea5e8f1c4b74ebf91a9482c64c982491af0d923b" + }, + { + "repo": "rapp-qonto", + "source": "retained public snapshot from warden-1.3-kernel-fixes-r1 test preparation", + "source_sha256": "6279dc2f3ab09519dffa1ceeedb2095f1182960e0106109a87aa5c0052446883", + "projection_sha256": "15aa9809e8d6a120c516be5ce9adfb72c00382140c171e791083ad117f503b3f" + } +] diff --git a/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/tests/fixtures/workload-coverage/rapp-core-hub/declarations/rapp.yaml b/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/tests/fixtures/workload-coverage/rapp-core-hub/declarations/rapp.yaml new file mode 100644 index 0000000..4f7bf9c --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/tests/fixtures/workload-coverage/rapp-core-hub/declarations/rapp.yaml @@ -0,0 +1,15 @@ +# Public declaration projection for the published coverage snapshot; not live evidence. +rapp_id: rapp-core-hub +workload_identity: + name: core-hub +composition: + member_repos: + - deployables: + - core-hub-api + - core-hub-migrate + - hub-core-candidate + - hub-core-candidate-migrate + - repository-classification-publisher +data_classification: confidential +criticality: high +readiness_state: verified diff --git a/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/tests/fixtures/workload-coverage/rapp-issue-core/declarations/rapp.yaml b/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/tests/fixtures/workload-coverage/rapp-issue-core/declarations/rapp.yaml new file mode 100644 index 0000000..0fda745 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/tests/fixtures/workload-coverage/rapp-issue-core/declarations/rapp.yaml @@ -0,0 +1,11 @@ +# Public declaration projection for the published coverage snapshot; not live evidence. +rapp_id: rapp-issue-core +workload_identity: + name: issue-core +composition: + member_repos: + - deployables: + - issue-core +data_classification: confidential +criticality: high +readiness_state: verified diff --git a/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/tests/fixtures/workload-coverage/rapp-qonto/declarations/rapp.yaml b/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/tests/fixtures/workload-coverage/rapp-qonto/declarations/rapp.yaml new file mode 100644 index 0000000..7392f75 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/tests/fixtures/workload-coverage/rapp-qonto/declarations/rapp.yaml @@ -0,0 +1,11 @@ +# Public declaration projection for the published coverage snapshot; not live evidence. +rapp_id: rapp-qonto +workload_identity: + name: qonto +composition: + member_repos: + - deployables: + - rapp-qonto +data_classification: restricted +criticality: critical +readiness_state: verified diff --git a/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/tests/test_case_plan.py b/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/tests/test_case_plan.py new file mode 100644 index 0000000..7de67e4 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/tests/test_case_plan.py @@ -0,0 +1,205 @@ +"""WARDEN-WP-0044-T02: plan a KnownSecurityCase run.""" +from __future__ import annotations + +import copy +import os +from pathlib import Path + +import pytest +import yaml +from typer.testing import CliRunner + +from warden.case_plan import CaseError, plan_case +from warden.cli import app +from warden.routing.catalog import load_catalog + +LIVE_CASE = Path(os.environ.get("WARDEN_TEST_NETKINGDOM_ROOT", + Path(__file__).resolve().parents[2] / "net-kingdom")) / "security-cases/forgejo-netkingdom-oidc.yaml" + +TYPE = { + "apiVersion": "netkingdom.io/security-case-type/v0.1", + "kind": "SecurityCaseType", + "spec": { + "id": "t", + "version": "1.0", + "status": "acknowledged", + "parameters": [ + {"name": "client_id", "case_settable": True, "sensitivity": "operational"}, + {"name": "client_type", "case_settable": True, "sensitivity": "operational"}, + {"name": "consumer", "case_settable": True, "sensitivity": "operational"}, + {"name": "issuer", "case_settable": False, "default": "https://kc.example"}, + ], + "follow_through": [ + {"id": "proxy", "owner": "o", "entry_point": "ensure {client_id}", "auth": "caller", + "lane": "key-cape-oidc-login", "warden_executes": True, "attended": False, + "value_handling": "none"}, + {"id": "mint", "owner": "o", "entry_point": "mint {client_id}", "auth": "caller", + "lane": "key-cape-oidc-login", "warden_executes": True, "attended": False, + "value_handling": "generated_in_custody", "when": "client_type == confidential"}, + {"id": "login", "owner": "o", "entry_point": "apply {client_id} env KEYCAPE_{client_id:env}_X", + "auth": "operator", + "lane": "key-cape-oidc-login", "warden_executes": False, "attended": True, + "value_handling": "none", "attended_justification": "live registry is a Secret"}, + {"id": "handover", "owner": "{consumer}", "entry_point": "use {issuer}", "auth": "a", + "warden_executes": False, "attended": False, "value_handling": "none", + "ready_check": "{consumer} ready"}, + ], + "verification": [ + {"id": "v", "check": "GET {issuer} for {client_id}", "expect": "present", + "reads_secret_value": False}, + {"id": "vc", "check": "custody {client_id}", "expect": "deny", + "reads_secret_value": False, "when": "client_type == confidential"}, + ], + "rollback": [{"reverses": s} for s in ("proxy", "mint", "login", "handover")], + }, +} + +CASE = { + "apiVersion": "netkingdom.io/security-case/v0.1", + "kind": "SecurityCase", + "spec": { + "id": "c", + "type": "t@1.0", + "status": "materialized", + "acceptance": {"at": "2026-10-09", "record": "r"}, + "parameters": {"client_id": "forgejo", "client_type": "public", "consumer": "railiance-apps"}, + }, +} + + +@pytest.fixture(scope="module") +def catalog(): + return load_catalog() + + +def _case(**spec): + doc = copy.deepcopy(CASE) + doc["spec"].update(spec) + return doc + + +def _type(): + return copy.deepcopy(TYPE) + + +def test_plan_resolves_modes_params_and_defaults(catalog): + plan = plan_case(_case(), _type(), catalog) + modes = {s.id: s.mode for s in plan.steps} + assert modes == {"proxy": "warden", "login": "attended", "handover": "owner"} + assert plan.skipped == ["mint"] + assert plan.attended_sessions == ["key-cape-oidc-login"] + handover = next(s for s in plan.steps if s.id == "handover") + assert handover.owner == "railiance-apps" + assert handover.entry_point == "use https://kc.example" + assert handover.ready_check == "railiance-apps ready" + assert [v["id"] for v in plan.verification] == ["v"] + assert plan.verification[0]["check"] == "GET https://kc.example for forgejo" + + +def test_condition_true_includes_step_and_check(catalog): + params = dict(CASE["spec"]["parameters"], client_type="confidential") + plan = plan_case(_case(parameters=params), _type(), catalog) + assert "mint" in [s.id for s in plan.steps] and not plan.skipped + assert [v["id"] for v in plan.verification] == ["v", "vc"] + + +@pytest.mark.parametrize("status,needle", [ + ("proposed", "not accepted"), + ("accepted", "not materialized"), + ("live", "already live"), +]) +def test_refuse_case_not_materialized(catalog, status, needle): + with pytest.raises(CaseError, match=needle): + plan_case(_case(status=status), _type(), catalog) + + +def test_refuse_case_without_acceptance(catalog): + with pytest.raises(CaseError, match="acceptance"): + plan_case(_case(acceptance=None), _type(), catalog) + + +def test_refuse_type_mismatch_and_unacknowledged_type(catalog): + with pytest.raises(CaseError, match="type file is"): + plan_case(_case(type="t@2.0"), _type(), catalog) + doc = _type() + doc["spec"]["status"] = "proposed" + with pytest.raises(CaseError, match="not acknowledged"): + plan_case(_case(), doc, catalog) + + +def test_refuse_missing_catalog_lane(catalog): + doc = _type() + doc["spec"]["follow_through"][0]["lane"] = "no-such-lane" + with pytest.raises(CaseError, match="LANE-MISSING"): + plan_case(_case(), doc, catalog) + + +def test_refuse_unreviewed_catalog_lane(catalog): + draft = next((e.id for e in catalog.entries if not (e.is_active and e.reviewed)), None) + if draft is None: + pytest.skip("catalog has no inactive or unreviewed lane") + doc = _type() + doc["spec"]["follow_through"][0]["lane"] = draft + with pytest.raises(CaseError, match="LANE-UNREVIEWED"): + plan_case(_case(), doc, catalog) + + +def test_refuse_bad_parameters(catalog): + with pytest.raises(CaseError, match="does not declare"): + plan_case(_case(parameters=dict(CASE["spec"]["parameters"], extra="x")), _type(), catalog) + with pytest.raises(CaseError, match="fixes"): + plan_case(_case(parameters=dict(CASE["spec"]["parameters"], issuer="https://evil")), _type(), catalog) + params = {k: v for k, v in CASE["spec"]["parameters"].items() if k != "consumer"} + with pytest.raises(CaseError, match="not set by the case"): + plan_case(_case(parameters=params), _type(), catalog) + + +def test_refuse_secret_parameter(catalog): + doc = _type() + doc["spec"]["parameters"].append({"name": "pw", "case_settable": True, "sensitivity": "secret"}) + with pytest.raises(CaseError, match="secret-sensitivity"): + plan_case(_case(parameters=dict(CASE["spec"]["parameters"], pw="hunter2")), doc, catalog) + + +def test_env_format_spec_renders_like_keycape_client(catalog): + params = dict(CASE["spec"]["parameters"], client_id="my-app") + plan = plan_case(_case(parameters=params), _type(), catalog) + login = next(s for s in plan.steps if s.id == "login") + assert login.entry_point == "apply my-app env KEYCAPE_MY_APP_X" + + +@pytest.mark.parametrize("template", ["{client_id:upper}", "{client_id:>9}", "{client_id!r}"]) +def test_refuse_unknown_format_spec_or_conversion(catalog, template): + doc = _type() + doc["spec"]["follow_through"][0]["entry_point"] = template + with pytest.raises(CaseError, match="format spec|conversions"): + plan_case(_case(), doc, catalog) + + +def test_refuse_unsupported_condition(catalog): + doc = _type() + doc["spec"]["follow_through"][0]["when"] = "client_type in (a, b)" + with pytest.raises(CaseError, match="unsupported condition"): + plan_case(_case(), doc, catalog) + + +def test_cli_plan_shows_attended_and_exit_codes(tmp_path): + case_file, type_file = tmp_path / "case.yaml", tmp_path / "type.yaml" + case_file.write_text(yaml.safe_dump(CASE)) + type_file.write_text(yaml.safe_dump(TYPE)) + runner = CliRunner() + ok = runner.invoke(app, ["case", "plan", str(case_file), "--type", str(type_file)]) + assert ok.exit_code == 0, ok.output + assert "ATTENDED" in ok.output and "nothing executed" in ok.output + import hashlib + digest = hashlib.sha256(case_file.read_bytes()).hexdigest() + assert f"case-ref: c@sha256:{digest}" in ok.output + case_file.write_text(yaml.safe_dump(_case(status="accepted"))) + refused = runner.invoke(app, ["case", "plan", str(case_file), "--type", str(type_file)]) + assert refused.exit_code == 1 + + +@pytest.mark.skipif(not LIVE_CASE.exists(), reason="net-kingdom checkout absent") +def test_live_forgejo_case_is_refused_until_materialized(): + result = CliRunner().invoke(app, ["case", "plan", str(LIVE_CASE)]) + assert result.exit_code == 1 diff --git a/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/tests/test_case_type.py b/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/tests/test_case_type.py new file mode 100644 index 0000000..2ca08b8 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/tests/test_case_type.py @@ -0,0 +1,130 @@ +"""WARDEN-WP-0044-T01: SecurityCaseType follow-through check.""" +from __future__ import annotations + +import copy +import os +from pathlib import Path + +import pytest +import yaml +from typer.testing import CliRunner + +from warden.case_type import check_follow_through, load_type +from warden.cli import app +from warden.routing.catalog import load_catalog + +LIVE_TYPE = Path(os.environ.get("WARDEN_TEST_NETKINGDOM_ROOT", + Path(__file__).resolve().parents[2] / "net-kingdom")) / "security-case-types/oidc-browser-relying-party/type.yaml" + +BASE = { + "apiVersion": "netkingdom.io/security-case-type/v0.1", + "kind": "SecurityCaseType", + "spec": { + "id": "t", + "version": "1.0", + "follow_through": [ + {"id": "run", "owner": "o", "entry_point": "e", "auth": "a", "lane": "key-cape-oidc-login", + "warden_executes": True, "attended": False, "value_handling": "none"}, + {"id": "login", "owner": "o", "entry_point": "e", "auth": "a", "lane": "key-cape-oidc-login", + "warden_executes": False, "attended": True, "value_handling": "none", + "attended_justification": "live registry is a Secret"}, + {"id": "handover", "owner": "o", "entry_point": "e", "auth": "a", + "warden_executes": False, "attended": False, "value_handling": "none"}, + ], + "verification": [{"id": "v", "check": "c", "expect": "present", "reads_secret_value": False}], + "rollback": [{"reverses": "run"}, {"reverses": "login"}, {"reverses": "handover"}], + }, +} + + +@pytest.fixture(scope="module") +def catalog(): + return load_catalog() + + +def _doc(**step_overrides): + doc = copy.deepcopy(BASE) + doc["spec"]["follow_through"][0].update(step_overrides) + return doc + + +def _codes(report): + return {f.code for f in report.findings} + + +def test_conforming_type_is_plannable(catalog): + report = check_follow_through(_doc(), catalog) + assert report.plannable, report.findings + assert report.warden_steps == ["run"] + assert report.attended_lanes == ["key-cape-oidc-login"] + + +def test_executed_step_without_lane_refused(catalog): + doc = _doc() + del doc["spec"]["follow_through"][0]["lane"] + assert "FT-LANE" in _codes(check_follow_through(doc, catalog)) + + +def test_unknown_lane_is_catalog_finding_not_schema(catalog): + report = check_follow_through(_doc(lane="no-such-lane"), catalog) + assert report.schema_ok and not report.plannable + assert _codes(report) == {"LANE-MISSING"} + + +def test_value_handling_required_and_enumerated(catalog): + assert "FT-VALUE" in _codes(check_follow_through(_doc(value_handling="returned"), catalog)) + doc = _doc() + del doc["spec"]["follow_through"][0]["value_handling"] + assert "FT-FIELD" in _codes(check_follow_through(doc, catalog)) + + +def test_attended_needs_justification_and_excludes_execution(catalog): + doc = copy.deepcopy(BASE) + del doc["spec"]["follow_through"][1]["attended_justification"] + doc["spec"]["follow_through"][1]["warden_executes"] = True + msgs = [f.message for f in check_follow_through(doc, catalog).findings if f.code == "FT-ATTEND"] + assert len(msgs) == 2 + + +def test_attended_step_needs_login_lane(catalog): + doc = copy.deepcopy(BASE) + secret_lane = next(e.id for e in catalog.entries if e.is_active and e.reviewed and e.lane == "secret") + doc["spec"]["follow_through"][1]["lane"] = secret_lane + assert "LANE-KIND" in _codes(check_follow_through(doc, catalog)) + + +def test_generated_value_never_in_attended_session(catalog): + doc = copy.deepcopy(BASE) + doc["spec"]["follow_through"][1]["value_handling"] = "generated_in_custody" + assert "FT-VALUE" in _codes(check_follow_through(doc, catalog)) + + +def test_verification_must_not_read_values(catalog): + doc = _doc() + doc["spec"]["verification"][0]["reads_secret_value"] = True + assert "VER-VALUE" in _codes(check_follow_through(doc, catalog)) + + +def test_every_step_reversible(catalog): + doc = _doc() + doc["spec"]["rollback"] = doc["spec"]["rollback"][1:] + assert "RB-MISSING" in _codes(check_follow_through(doc, catalog)) + + +def test_cli_exit_codes(tmp_path): + good, bad = tmp_path / "good.yaml", tmp_path / "bad.yaml" + good.write_text(yaml.safe_dump(_doc())) + bad.write_text(yaml.safe_dump(_doc(lane="no-such-lane"))) + runner = CliRunner() + assert runner.invoke(app, ["case", "check-type", str(good), "--json"]).exit_code == 0 + assert runner.invoke(app, ["case", "check-type", str(bad), "--json"]).exit_code == 1 + + +@pytest.mark.skipif(not LIVE_TYPE.exists(), reason="net-kingdom checkout not present") +def test_live_oidc_rp_type_schema_conforms(catalog): + """The acknowledged 1.0 type is plannable: every lane it names is active (2026-10-10).""" + report = check_follow_through(load_type(LIVE_TYPE), catalog) + assert report.schema_ok, report.findings + assert report.plannable, report.findings + assert report.attended_lanes == ["key-cape-oidc-login"] + assert report.warden_steps == ["mint-secret"] diff --git a/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/tests/test_layer_conformance.py b/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/tests/test_layer_conformance.py new file mode 100644 index 0000000..64ea533 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/tests/test_layer_conformance.py @@ -0,0 +1,355 @@ +"""Layer-model conformance (security-layer-model_v0.8 §5, §11). + +Two things are checked here. §11 makes one of them mechanical: every direct +Tooling client maps to a declared shape. §5.2 asks for the other: the conduit's +supplied-authority property covered by a test. + +Deliberately absent: any assertion on a §5.3 review date. A date-triggered +failure breaks the build on a calendar day with no code change, punishing +whoever commits next rather than whoever owns the gap — the same reasoning +recorded in WARDEN-WP-0033-T05 for blocker staleness. +""" +from __future__ import annotations + +import os +import subprocess +import sys +from pathlib import Path + +import yaml + +ROOT = Path(__file__).resolve().parents[1] + + +def _decl() -> dict: + return yaml.safe_load((ROOT / "layer.yaml").read_text()) + + +def _intent_frontmatter() -> dict: + lines = (ROOT / "INTENT.md").read_text().splitlines() + assert lines[0].strip() == "---", "INTENT.md must carry frontmatter — it is the declaration" + end = next(i for i, ln in enumerate(lines[1:], 1) if ln.strip() == "---") + return yaml.safe_load("\n".join(lines[1:end])) + + +def _checker(): + import importlib.util + + spec = importlib.util.spec_from_file_location( + "check_layer_conformance", ROOT / "scripts" / "check_layer_conformance.py" + ) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + + +def _fold(value: str) -> str: + return str(value).strip().encode("ascii", "ignore").decode().lower() + + +class TestDeclaration: + def test_declares_staff_layer_in_its_own_voice(self): + d = _decl() + assert d["repository"] == "ops-warden" + assert _fold(d["layer"]) == "staff" + # §11: "only the repository's own file, in its own voice, conforms." + assert d["declared_by"] == "docs/adr/ADR-0010" + + def test_intent_md_carries_the_governing_declaration(self): + """GH-DEC-2026-017 §1 / A11: INTENT.md's frontmatter is the declaration.""" + assert _fold(_intent_frontmatter()["layer"]) == "staff" + + def test_sidecar_is_marked_derived_and_names_its_source(self): + """§11's derived-artifact rule, applied to the layer sidecar.""" + d = _decl() + assert d["derived"] is True + assert d["derived_from"] == "INTENT.md" + + def test_the_two_forms_agree_once_case_is_folded(self): + """A11: the derived form must agree; A9: comparison folds case. + + `Staff` in INTENT.md and `staff` in layer.yaml are the SAME value. This + test is deliberately a fold rather than an equality: the ruling asked + nobody to re-spell anything, and an equality assertion here would be this + repository quietly doing the re-spelling the ruling declined to order. + The next *real* divergence — a different layer — still fails. + """ + assert _fold(_decl()["layer"]) == _fold(_intent_frontmatter()["layer"]) + + def test_layer_is_in_section_3_closed_vocabulary(self): + """A9: {Taxonomy, Tooling, Engine, Staff}, closed, case-insensitive.""" + vocabulary = {"taxonomy", "tooling", "engine", "staff"} + assert _fold(_intent_frontmatter()["layer"]) in vocabulary + assert _fold(_decl()["layer"]) in vocabulary + + def test_no_declaration_carries_a_standard_version(self): + """GH-DEC-2026-017 §5 / A12 — and the regression guard on its return. + + The field was removed from the estate's reference form, not just from + this file. A field that is present will be branched on, so absence is + asserted rather than trusted. + """ + assert "standard_version" not in _decl() + assert "standard_version" not in _intent_frontmatter() + + def test_no_version_anywhere_in_either_declaration(self): + """A12 r2 / GH-DEC-2026-020 §1-§2: content, not a key name. + + A versioned `standard:` path or a `companion_version` is the same pin as + `standard_version`, so the guard walks every key and value of both forms. + """ + checker = _checker() + assert checker.find_version_pins(_intent_frontmatter()) == [] + assert checker.find_version_pins(_decl()) == [] + assert not str(_intent_frontmatter()["standard"]).endswith(".md") + + def test_checker_catches_a_versioned_standard_path(self): + checker = _checker() + pins = checker.find_version_pins( + {"layer": "Staff", "standard": "net-kingdom/canon/standards/security-layer-model_v0.7.md"} + ) + assert pins and pins[0].startswith("standard") + + def test_checker_catches_a_companion_version(self): + checker = _checker() + assert checker.find_version_pins({"layer": "Staff", "companion_version": "0.2"}) + assert checker.find_version_pins({"nested": {"standard_version": "0.7"}}) + + def test_version_token_in_identity_value_is_a_pin(self): + """GH-DEC-2026-021 §3: any `v?N.N` in a standard:/companion: value is a pin.""" + checker = _checker() + pins = checker.find_version_pins({"standard": "security-layer-model v0.7"}) + assert pins and pins[0].startswith("standard") + assert checker.find_version_pins({"companion": "SECURITY-COMPANION 0.2"}) + assert checker.find_version_pins({"nested": {"standard": ["security-layer-model v0.8"]}}) + + def test_prose_citation_and_intent_version_are_not_reached(self): + """GH-DEC-2026-021 §1 (A12 r3): prose provenance and `intent_version` pass.""" + checker = _checker() + assert checker.find_version_pins( + {"layer": "Staff", "note": "Outside §5 by the v0.5 scope rule", "intent_version": "0.1.0"} + ) == [] + + def test_schema_version_is_not_reached(self): + assert _checker().find_version_pins({"schema_version": "0.2", "layer": "Staff"}) == [] + + def test_stance_map_is_outside_the_run(self): + """GH-DEC-2026-020 §3: a stance map keeps its version; the run must not read it.""" + stance = yaml.safe_load((ROOT / "pep-stance.yaml").read_text()) + assert "standard_version" in stance, "pep-stance.yaml keeps its clause-scoped version" + assert "pep-stance" not in _checker().SCOPE + + def test_every_run_states_version_and_scope(self): + """GH-DEC-2026-020 §4: the version belongs to the run, printed every time.""" + checker = _checker() + out = subprocess.run( + [sys.executable, str(ROOT / "scripts" / "check_layer_conformance.py")], + capture_output=True, + text=True, + ).stdout + assert f"validated against: {checker.VALIDATED_AGAINST}" in out + assert f"scope: {checker.SCOPE}" in out + pass_line = next(ln for ln in out.splitlines() if ln.startswith("PASS")) + assert checker.VALIDATED_AGAINST in pass_line + + def test_every_tooling_contact_maps_to_a_declared_shape(self): + """§11 mechanical check — the guard against a new undeclared client.""" + result = subprocess.run( + [sys.executable, str(ROOT / "scripts" / "check_layer_conformance.py")], + capture_output=True, + text=True, + ) + assert result.returncode == 0, ( + f"undeclared Tooling contact — a finding under §11, not a tracked gap:\n" + f"{result.stdout}{result.stderr}" + ) + + def test_declared_gaps_carry_all_four_fields(self): + """§5.3 is machine-readable or it is prose wearing a schema.""" + for c in _decl()["tooling_contacts"]: + if c["shape"] == "5.3": + for field in ("capability", "intended_owner", "blocked_on", "review"): + assert c.get(field), f"{c['id']} missing {field}" + + def test_gaps_are_not_counted_as_conformance(self): + """§11: a declared gap is tracked non-conformance. Keep that visible.""" + text = (ROOT / "layer.yaml").read_text() + assert "TRACKED NON-CONFORMANCE" in text.upper() + + +class TestConduitSuppliesNoAuthority: + """§5.2: 'MUST NOT present its own credential, MUST NOT widen what the + caller could already do.' The standard says this SHOULD be covered by a + test; this is that test.""" + + def test_conduit_supplies_no_authority_of_its_own(self, monkeypatch): + from warden import proxy + + monkeypatch.setenv("VAULT_TOKEN", "caller-own-token") + monkeypatch.setenv("HOME", "/home/nobody") + before = dict(os.environ) + + env = proxy._caller_env() + + # The child environment IS the caller's environment — nothing added, + # nothing removed, no ops-warden credential injected. + assert env == before, ( + "conduit altered the caller's environment; §5.2 requires it to " + "supply no authority of its own" + ) + assert env["VAULT_TOKEN"] == "caller-own-token" + + def test_conduit_declares_supplied_authority_none(self): + conduits = [c for c in _decl()["tooling_contacts"] if c["shape"] == "5.2"] + assert conduits, "no §5.2 conduit declared — proxy.py is one" + for c in conduits: + assert c["supplied_authority"] == "none" + + def test_proxy_holds_no_credential_constant(self): + """A conduit that presents its own token is not a conduit (§5.2).""" + src = (ROOT / "src" / "warden" / "proxy.py").read_text() + # It may name token ENV VARS to detect caller auth; it must not carry a + # token value or mint one. + for forbidden in ("X-Vault-Token", "auth/approle/login", "token create"): + assert forbidden not in src, ( + f"proxy.py references {forbidden!r} — that is presenting or " + f"minting authority, not conducting the caller's" + ) + + +class TestPepStanceMap: + """§6.4: every PEP-shaped consumer MUST publish its unreachable-engine + stance map, total and per zone, 'published rather than held in code'. + ADR-0009 is named as the reference shape, so it should actually hold.""" + + def _stance(self) -> dict: + return yaml.safe_load((ROOT / "pep-stance.yaml").read_text()) + + def test_published_map_equals_shipped_behaviour(self): + """The whole point. A published map that may drift from the code is + worse than none, because it invites reliance it cannot support.""" + from warden.config import PolicyConfig + + assert self._stance()["stance"] == PolicyConfig().failure_modes + + def test_stance_is_total_over_the_zone_model(self): + """§6.4 obligation 3: total, no implicit default.""" + stance = self._stance()["stance"] + required = { + "z0-experimental", "z1-operational", "z2-protected", + "z2-continuity", "z3-critical", "unknown", "not-applicable", + } + assert required <= set(stance), f"stance not total; missing {required - set(stance)}" + assert set(stance.values()) <= {"fail_open", "fail_closed"} + + def test_critical_zone_fails_closed(self): + """ADR-0009's one non-negotiable row.""" + assert self._stance()["stance"]["z3-critical"] == "fail_closed" + + def test_verdict_is_never_cached(self): + """§6.4 obligation 2: caching an input claim is permitted; caching the + answer is a second decision point deciding early (§6.1).""" + assert self._stance()["verdict_caching"] == "none" + + def test_revocation_visibility_deadline_equals_enforced_ttl_policy(self): + """§9.7.2: a published replay window must not drift from issuance.""" + from warden.models import ActorType, MAX_TTL_HOURS + + published = self._stance()["revocation_visibility"] + expected = {actor.value: MAX_TTL_HOURS[actor] for actor in ActorType} + assert published["deadline_hours"] == expected + assert published["mechanism"] == "ttl_expiry" + assert published["revocation_channel"] == "none" + + def test_attributive_emission_cadence_deferral_carries_measurement(self): + cadence = self._stance()["emission_cadence"] + assert cadence["classification"] == "attributive" + assert cadence["status"] == "deferred" + assert cadence["observed_window"]["signature_records"] == 3 + assert cadence["observed_window"]["active_signature_days"] == 2 + assert cadence["reason"] + + +# --- classification coverage (v0.8 §6.4 obligation 3) ------------------------- + +def test_published_coverage_equals_measured_coverage(): + """The published figure must equal its explicit public declaration snapshot. + + This is a deterministic source check, not a claim about current estate state. + Live measurement remains scripts/report_coverage.py with an explicit estate + root; tests never treat the developer HOME as an authority or fixture. + + ops-warden asked gate-house for §13.1's Coverage column and its figures are + that column's first entries, so their accuracy is ours to hold. The register + explicitly does not compute anyone's coverage, and a stale number beside a + marked cell is worse than a blank -- a blank at least reads as "not reported". + + This is the same property that makes the stance map worth publishing (the map + equals PolicyConfig.failure_modes by test), applied one level up. + """ + import importlib.util + + repo = Path(__file__).resolve().parents[1] + spec = importlib.util.spec_from_file_location( + "report_coverage", repo / "scripts" / "report_coverage.py" + ) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + + published = yaml.safe_load((repo / "pep-stance.yaml").read_text())[ + "classification_coverage" + ] + measured = module.measure(repo / "tests/fixtures/workload-coverage") + + for population in ("signing_targets", "routing_lanes"): + assert published[population] == measured[population], population + + +def test_the_unknown_cell_is_marked_as_a_declared_gap(): + """A non-conformant cell must say so where it is declared, not only in a review. + + §11's marking obligation, which ops-warden argued for in the v0.6 round and + then acquired a marked cell under. If the cell is ever flipped to fail_closed + this test fails, which is the correct time to remove the marking. + """ + repo = Path(__file__).resolve().parents[1] + text = (repo / "pep-stance.yaml").read_text() + stance = yaml.safe_load(text)["stance"] + + if stance["unknown"] == "fail_open": + assert "DECLARED GAP" in text + assert "WARDEN-WP-0040" in text + else: + assert stance["unknown"] == "fail_closed" + + +def test_accepted_v08_run_provenance_and_catalog_scope(): + checker = _checker() + assert "security-layer-model_v0.8.md" in checker.VALIDATED_AGAINST + assert "sha256:3d8298560069ef471fba28d2b1245fb2309328f231dad1a667cdf0ce5a5a7c8b" in checker.VALIDATED_AGAINST + assert "single repository ops-warden (catalogued in §4)" in checker.SCOPE + + +def test_reference_semantics_propagate_identity_through_nested_sequences(): + checker = _checker() + for data in ( + {"nested": {"standard": ["Security Layer Model v0.8"]}}, + {"companion": [["Security Companion 0.3"]]}, + {"standard": {"alternate": ["Security Layer Model v0.7"]}}, + ): + # A dictionary begins a fresh identity key; only sequence descendants + # inherit the immediate standard:/companion: identity in the reference. + if isinstance(data.get("standard"), dict): + assert checker.find_version_pins(data) == [] + else: + assert checker.find_version_pins(data), data + assert checker.find_version_pins({"rationale": "Assented at v0.7; migrated at v0.8."}) == [] + assert checker.find_version_pins({"intent_version": "0.2.0", "schema_version": "0.1"}) == [] + + +def test_ascii_folding_never_discards_non_ascii_letters(): + checker = _checker() + assert checker._fold("Staff") == "staff" + assert checker._fold("Engine") == "engine" + # Deleting non-ASCII bytes would turn this fifth token into Staff. + assert checker._fold("Stαaff") not in checker.LAYER_VOCABULARY diff --git a/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/tests/test_safe_unit_runner.py b/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/tests/test_safe_unit_runner.py new file mode 100644 index 0000000..1cf404d --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/tests/test_safe_unit_runner.py @@ -0,0 +1,81 @@ +"""Exercise installed parent and nested child controls, never real credentials.""" +import json +import os +from pathlib import Path +import socket +import subprocess +import sys + +import pytest + +ROOT = Path(__file__).resolve().parents[1] + + +@pytest.fixture +def guarded(request): + guard = getattr(request.config, '_unit_guard', None) + if guard is None: + pytest.skip('requires make test / scripts/run_unit_tests.py') + return guard + + +def test_parent_environment_is_allowlisted_and_home_is_temporary(guarded): + # The external regression invocation sets synthetic BAO_TOKEN/VAULT_TOKEN/HOME. + assert 'BAO_TOKEN' not in os.environ + assert 'VAULT_TOKEN' not in os.environ + assert Path(os.environ['TMPDIR']) in Path.home().parents + assert 'PYTHONPATH' not in os.environ + + +@pytest.mark.parametrize('command', [ + ['bao', 'token', 'lookup'], ['ssh', 'railiance01'], ['curl', 'http://127.0.0.1:8000'], + [sys.executable, '-c', 'import os; print(os.environ)'], +]) +def test_unexpected_process_refused_before_launch(guarded, command): + with pytest.raises(PermissionError, match='unit isolation'): + subprocess.run(command, check=True) + + +def test_parent_socket_denies_even_unregistered_loopback(guarded): + with pytest.raises(PermissionError, match='unit isolation'): + socket.socket().connect(('127.0.0.1', 8000)) + + +@pytest.mark.parametrize('path', ['/home/synthetic-ambient-unit-fixture/.vault-token', + '/root/.kube/config', '/proc/self/environ', + '/tmp/warden-synthetic-ambient-home/.vault-token']) +def test_ambient_paths_refused_without_opening_them(guarded, path): + with pytest.raises(PermissionError, match='unit isolation'): + Path(path).read_bytes() + + +def test_symlink_cannot_escape_fixture_root(guarded, tmp_path): + link = tmp_path / 'ambient' + link.symlink_to('/home/synthetic-ambient-unit-fixture/.vault-token') + with pytest.raises(PermissionError, match='unit isolation'): + link.read_bytes() + + +def test_child_drops_even_synthetic_token_environment(guarded, monkeypatch): + monkeypatch.setenv('BAO_TOKEN', 'synthetic-only-never-a-real-token') + monkeypatch.setenv('VAULT_TOKEN', 'synthetic-only-never-a-real-token') + monkeypatch.setenv('HOME', '/home/synthetic-ambient-unit-fixture') + result = subprocess.run([sys.executable, str(ROOT / 'tests/unit_isolation_probe.py'), 'state'], + capture_output=True, text=True, check=True, env=dict(os.environ)) + assert json.loads(result.stdout) == {'bao_token_present': False, + 'vault_token_present': False, 'home_in_temporary': True} + + +@pytest.mark.parametrize('mode', ['socket', 'provider', 'home', 'nested']) +def test_guard_exists_in_each_allowed_python_child(guarded, mode): + result = subprocess.run([sys.executable, str(ROOT / 'tests/unit_isolation_probe.py'), mode], + capture_output=True, text=True, check=True) + assert result.stdout == 'denied\n' + + +def test_forged_provider_double_refused(guarded, tmp_path): + provider = tmp_path / 'bao' + provider.write_text('#!/bin/bash\necho not-the-reviewed-fixture\n') + provider.chmod(0o700) + with pytest.raises(PermissionError, match='double bytes differ'): + subprocess.run([str(provider), 'token', 'lookup'], check=True) diff --git a/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/tests/test_workload_join.py b/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/tests/test_workload_join.py new file mode 100644 index 0000000..db6a06d --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/tests/test_workload_join.py @@ -0,0 +1,70 @@ +"""Explicit lane-to-workload join tests (WARDEN-WP-0032 / RMGR-WP-0010-T06).""" +from pathlib import Path + +import yaml + +from scripts.report_workload_join import build + + +ROOT = Path(__file__).resolve().parents[1] + + +def test_repo_catalog_uses_only_explicit_workload_references(): + report = build(ROOT / "registry/routing/catalog.yaml", + ROOT / "tests/fixtures/workload-coverage") + assert report["ok"] is True + assert len(report["resolved"]) == 4 + assert len(report["unknown"]) == 22 + # 15 since 2026-09-21: the layer-declaration route is repository conformance + # guidance, not a workload delivery lane, and the two NetKingdom SSO lanes + # (c374d41) remain provider/control-plane credentials. Lanes added after + # 2026-09-09 moved the denominator without moving `resolved`; these counts + # and pep-stance.yaml's classification_coverage measure the same populations + # and must be updated together. 16 since 2026-10-05: the + # openbao-structure-survey pointer (WARDEN-WP-0042) is a builder survey + # under a founder-minted grant, not a workload credential. 17 since + # 2026-10-08: platform-supplier-credentials (WARDEN-WP-0043) is a generic + # supplier-account path pattern; concrete consumers keep their own lanes. + # unknown 22 since 2026-10-09: fluid-telegram-operator (MASON-WP-0005-T06) + # names fluid-telegram, which has published no workload declaration. + # not_applicable 18 since 2026-10-10: keycape-client-secret-generate is a + # per-case generation lane; each case's consumer gets its own entry. + # not_applicable 22 since 2026-10-17: the four CATALOG-1 draft lanes + # (nk48 1.3 owner review) are per-case lanes like the one above. + assert len(report["not_applicable"]) == 22 + assert {row["lane"] for row in report["resolved"]} == { + "ops-warden-warden-sign-token", + "issue-core-ingestion-api-key", + "rapp-qonto-keycape-client", + "core-hub-forgejo-source-read", # CCR-2026-0031, 2026-10-03 + } + + +def test_invalid_exact_deployable_resolves_unknown(tmp_path): + rapp = tmp_path / "rapp-x" / "declarations" + rapp.mkdir(parents=True) + (rapp / "rapp.yaml").write_text(yaml.safe_dump({ + "rapp_id": "rapp-x", + "workload_identity": {"name": "x"}, + "composition": {"member_repos": [{"deployables": ["api"]}]}, + })) + catalog_dir = tmp_path / "ops-warden" / "registry" / "routing" + catalog_dir.mkdir(parents=True) + catalog = catalog_dir / "catalog.yaml" + catalog.write_text(yaml.safe_dump({"entries": [{ + "id": "x", + "workload_ref": { + "applicability": "applicable", + "rapp_id": "rapp-x", + "name": "x", + "deployable": "missing", + }, + }]})) + posture = catalog_dir.parent / "policy" + posture.mkdir() + (posture / "security-posture.yaml").write_text("dataclass_floor: {}\n") + + report = build(catalog, tmp_path) + assert not report["resolved"] + assert report["unknown"][0]["lane"] == "x" + assert "deployable" in report["unknown"][0]["reason"] diff --git a/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/tests/unit_isolation_probe.py b/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/tests/unit_isolation_probe.py new file mode 100644 index 0000000..d4c6596 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/tests/unit_isolation_probe.py @@ -0,0 +1,29 @@ +"""Closed child regression probes; executed only under the unit runner guard.""" +import json +import os +from pathlib import Path +import socket +import subprocess +import sys + +mode = sys.argv[1] +if mode == 'state': + print(json.dumps({'bao_token_present': 'BAO_TOKEN' in os.environ, + 'vault_token_present': 'VAULT_TOKEN' in os.environ, + 'home_in_temporary': Path(os.environ['TMPDIR']) in Path.home().parents})) +else: + try: + if mode == 'socket': + socket.socket().connect(('127.0.0.1', 8000)) + elif mode == 'provider': + subprocess.run(['bao', 'token', 'lookup'], check=True) + elif mode == 'home': + Path('/home/synthetic-ambient-unit-fixture/.vault-token').read_text() + elif mode == 'nested': + subprocess.run([sys.executable, '-c', 'print("SYNTHETIC-SECRET")'], check=True) + else: + raise AssertionError('unknown probe') + except PermissionError: + print('denied') + else: + raise AssertionError('external operation was not refused') diff --git a/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/workplans/WARDEN-WP-0044-security-case-follow-through.md b/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/workplans/WARDEN-WP-0044-security-case-follow-through.md new file mode 100644 index 0000000..b46c0d6 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/candidate/workplans/WARDEN-WP-0044-security-case-follow-through.md @@ -0,0 +1,378 @@ +--- +id: WARDEN-WP-0044 +type: workplan +title: "Follow through on KnownSecurityCases along their type's route" +domain: infotech +repo: ops-warden +status: active +flavor: implementation +owner: claude-code +topic_slug: netkingdom +created: "2026-10-08" +updated: "2026-10-11" +depends_on: [] +related_repos: + - net-kingdom + - ops-mason + - key-cape + - railiance-platform +state_hub_workstream_id: "f7e81013-6868-5d99-8528-ba75a442eca9" +--- + +# WARDEN-WP-0044 — Follow through on KnownSecurityCases along their type's route + +## Why + +NetKingdom is introducing SecurityCaseTypes (net-kingdom `NK-WP-0048`, canon +`security-case-types_v0.1`, proposed). Once a case of an implemented type is +accepted and ops-mason has materialized its structure (`MASON-WP-0010`), +ops-warden carries out the remaining steps. These are the owners' declared entry +points, run on the authentication path the type defines, followed by the type's +verification. + +Today ops-warden routes needs and, for `exec_capable` lanes, proxies fetches as +the caller. It does not yet know about cases, and each new integration ends with +a founder running a hand-written script (for example the proposed KEY-WP-0038-T02 +rollout). This plan gives ops-warden the follow-through role, without widening +what it may do: each step still runs under its owner's entry point and the +caller's identity. + +## Tasks + +```task +id: WARDEN-WP-0044-T01 +status: done +depends_on: [NK-WP-0048-T03] +priority: high +state_hub_task_id: "ef0920fc-418e-52cb-acff-9a5c0c728bf5" +``` + +### T01 — Define the type's follow-through section + +Propose the `follow_through` part of the SecurityCaseType schema together with +net-kingdom. For each step it names the owner entry point, `warden_executes`, +the auth method and lane, whether an attended human login is unavoidable (with +the type's justification), and the verification command. Write it for the first +type, `oidc-browser-relying-party`, covering key-cape client registration, +custody minting and LLDAP group membership. + +2026-10-08: NK-WP-0048-T03/T04 landed with a drafted section. Review is in +`history/2026-10-08-oidc-rp-follow-through-review.md`. It requires three +changes before T02: a per-step `lane`, a narrow `warden_executes`, and +`value_handling`. It also lists four refinements. This task is done when +net-kingdom acknowledges the review (NK-WP-0048-T05). + +2026-10-09: R4 was withdrawn in favour of ops-mason's per-case +`{consuming_service}-keycape-client`, with the entry body templated in the type +(R4′, see the addendum in the review). + +2026-10-09: the revised 1.0 type and canon now carry R1–R3, R4′, R5–R7. +`warden case check-type ` (`src/warden/case_type.py`, tests in +`tests/test_case_type.py`) checks a type's follow-through mechanically: the +live type is schema-conformant, with one attended session +(`key-cape-oidc-login`). It is not yet plannable, because two lanes are not +in the catalog: `identity-provisioner-groups` (net-kingdom) and +`keycape-client-secret-generate` (railiance-platform). These gate T02, not +T01. T01 still closes on net-kingdom's acknowledgement (NK-WP-0048-T05). + +2026-10-09: ops-warden cannot add `identity-provisioner-groups` as the type +declares it. The provisioner accepts only user-engine's runtime token and +admits only user-engine pods, so no caller-identity path exists. R8 (review +addendum) asks net-kingdom either to move `ensure-groups` into the existing +attended session (recommended) or to give the provisioner an operator +credential. `keycape-client-secret-generate` needs a generate-in-custody entry +point from railiance-platform, and the broker does not have one yet. Both were +sent to their owners. + +2026-10-09: done. net-kingdom took R8 option 1, so `ensure-groups` runs attended +in the `register-client` session and `identity-provisioner-groups` is dropped +(net-kingdom `0e86f95`). The founder acknowledged the type with memo r4 (hub +decision `e418263e`, NK-WP-0048-T05 done). + +```task +id: WARDEN-WP-0044-T02 +status: done +depends_on: [WARDEN-WP-0044-T01] +priority: high +state_hub_task_id: "5f68c991-f490-58f8-a121-92f74073d272" +``` + +### T02 — Plan a case run + +`warden case plan ` checks that the case is accepted and materialized, +resolves each step to its catalog entry and entry point, and prints the run: +which steps warden executes, which run as the attended caller, what the +verification is. No secret values; no execution. + +Tests: refuse an unaccepted or unmaterialized case; refuse a step whose catalog +entry is missing or not reviewed; show attended steps explicitly. + +2026-10-09: done. `warden case plan [--type] [--json]` +(`src/warden/case_plan.py`, 14 tests in `tests/test_case_plan.py`). It refuses: +- a case that is not `materialized`, or has no acceptance record +- a type that is mismatched or not acknowledged +- any `case check-type` finding, including a missing or unreviewed lane +- undeclared, type-fixed or secret-sensitivity parameters +- a placeholder the case does not set, or an unsupported `when` + +It fills type defaults (`issuer`), evaluates ` == ` conditions, +renders owner, entry point, auth and ready_check, groups attended steps per +lane, and lists the applicable verification. + +The live Forgejo case is correctly refused: it is `accepted`, not +materialized. A materialized copy, with a stand-in lane for +`keycape-client-secret-generate`, renders six steps: two attended steps plus +`register-client` in one `key-cape-oidc-login` session, one warden step +(`mint-secret`), and two owner steps. Seven checks follow. + +Observation for net-kingdom (not blocking): `bind-env` runs before +`register-client`, in type order. Its entry carries a literal +`KEYCAPE__CLIENT_SECRET`, which is not a `{param}` placeholder, so it +reaches the plan unrendered. + +2026-10-09 follow-up: +- **Format specs.** `_render` supports net-kingdom's `{param:env}` spec (upper + case, `-` as `_`, the same as `keycape-client.py env_name`) and refuses any + other spec or `!conversion`. +- **Case reference.** The plan prints `case-ref: @sha256:` over the + case file's exact bytes. This is the reference proposed to railiance-platform + for `credential.py generate --case-ref` (RPF-WP-0054-T01); the broker + re-derives it from the file. +- 18 tests now. + +```task +id: WARDEN-WP-0044-T03 +status: progress +depends_on: [WARDEN-WP-0044-T02, MASON-WP-0010-T03] +blocking_reason: "Local durable execution kernel and injected failure harness implemented. Native acceptance still requires authenticated owner machine descriptors/adapters, actual Forgejo materialization receipt, contained session integration, ordered verification and first native run." +priority: high +state_hub_task_id: "bcbf6c91-2c04-5c16-ae8c-5d6c06fd6b89" +``` + +### T03 — Run and verify a case + +`warden case run ` executes the plan. Owner entry points run through +the existing proxy as the caller; steps that need an attended login use the +existing contained login envelope (silent command, session revoked afterwards), +grouped into as few attended sessions as possible. Then it runs the type's +verification and writes a receipt without values. `warden case rollback` +reverses exactly this case's steps. + +Acceptance: the Forgejo reference case (NK-WP-0048-T06) reaches `live` through +this path, with no case-specific script. + +2026-10-17: original-intent lookup/context interface for NK-WP-0048-T15, source +and synthetic tests only. `src/warden/case_run.py`: `Executor.run` writes an +`OriginalIntent` (schema `warden.case.original-intent.v1`: the dispatched run +Invocation — attempt, step, owner, target, actor, principal, lane, all six pins, +tenant, realm — plus admitted adapter source) in the same SQLite transaction as +the pre-dispatch journal row. `Journal.original_intent(binding, current, source)` +returns it after restart and refuses on a missing record or any +pins/source/step/owner/target/actor/principal/lane/tenant/realm mismatch, before any +journal write, so the step stays uncertain. An adapter's +`reconcile_original(ReconcileContext(original, attempt))` receives the original +intent separately from the new reconcile attempt. This is Warden's record of what +it dispatched, not owner created-only provenance. Current Executor.admit leaves +tenant/realm empty; the reserved coordinates establish no admitted provenance. +Owner descriptors/adapters, contained session integration, Forgejo +materialization and the native run remain open. + +2026-10-17 source consistency follow-through: retained the additive intents table +and reconcile_original callback and fixed four reproduced local defects: original +actor mismatch, payload/SQL attempt-ID mismatch, orphan intent without its +matching original running receipt, and arbitrary nonmetadata values accepted by +the original-intent serializer. Exact bounded field validation, duplicate-free +bounded decode and save-time intent/invocation/running-state equality now preserve +the original receipt linkage before owner lookup. No new context store, native +verifier, trust root, adapter activation or authority migration was introduced. + +The actual canonical focused kernel and new consistency suite passes 54 checks; +owner-installed Ruff check/format passes. The existing generated-data/synthetic +owner fixture and a distinct interpreter prove source correspondence/refusal, +not independent native custody or creation. Earlier comparison probes and failed +preparation are retained. Current caller and fresh session/grant revocation checks +remain required; original owner payload/revocation, exact native readback and +created-only provenance still need independent authenticated owner admission. +No JSON/SQL context, unknown tenant/realm, changed run-sheet or legacy missing +intent supplies recovery, replay or undo authority. T03 remains progress and all +native acceptance gates/statuses/dependencies/UUIDs remain unchanged. Evidence: +Custodian original-context-compare-r2 and original-context-owner-followthrough-r3 +under docs/changes/netkingdom-target-2026-10-17/; publication/projection is separate. + +Agreed with railiance-platform (RPF-WP-0054-T01, 2026-10-09): +- `mint-secret` runs `credential.py generate --grant keycape/client-secret-generate + --client-id --case-ref --case-file ` as the caller's + child process. The broker re-derives the case-ref and checks status and + client_id. +- warden refuses that step when `WARDEN_AGENT_ID` is set. + +2026-10-10: railiance-platform implemented the command (RPF-WP-0054-T03). It is +catalogued as draft `keycape-client-secret-generate`, with `exec_owner` +railiance-platform and `exec_capable: false`. `warden case check-type` now +reports the lane as LANE-UNREVIEWED rather than LANE-MISSING. Before it can be +activated: +- the founder binds the issuer policy to the operator OIDC role +- key-cape confirms the secret format +- the RPF-WP-0054-T05 probe passes + +2026-10-10 (later): +- **Broker lane.** The issuer role is bound (CCR-2026-0043) and the T05 probe + passed (railiance-platform `21b8af3`). Only key-cape's format confirmation is + left. The playbook now includes the issuer-role login, a 15m token with + unlimited uses that `warden case run` must group with the attended session. +- **Env spec.** net-kingdom adopted `{client_id:env}` as a 1.0 patch + (`9a9b8e3`). The plan renders `KEYCAPE_FORGEJO_CLIENT_SECRET`. +- **Forgejo case.** Rendered and built by ops-mason. It is not yet + `materialized`: MASON-WP-0010-T03 waits on the railiance-platform CCRs and + their founder apply. + +2026-10-10: `keycape-client-secret-generate` activated on railiance-platform's +go-ahead (message 20973a9c) after key-cape confirmed the format (4e0213b3). +`warden case check-type` on the live type now reports `plannable=True`. The +only thing T03 still waits on is the Forgejo case reaching `materialized` +(MASON-WP-0010-T03). + +2026-10-10: net-kingdom moved the type to 1.1 (`e2174ce`) and re-pinned the +Forgejo case. 1.1 adds the verification `consumer-enforces-groups` and requires +the hand-over to enforce `access_groups`. `check-type` on 1.1 reports +`plannable=True`, and a materialized copy renders six steps and the new check. +The case-ref digest covers the whole case file, including `status`, so it +changes when the case becomes `materialized`. `warden case run` therefore takes +it from the file at run time, and the broker re-derives it from the same file. + +```task +id: WARDEN-WP-0044-T04 +status: wait +depends_on: [WARDEN-WP-0044-T03] +blocking_reason: "Catalog integration follows a proven run." +priority: medium +state_hub_task_id: "4a0fa4a7-5ca9-518a-ab92-a79a5e2cf93e" +``` + +### T04 — Catalog entries from types + +Let a type contribute reviewed catalog entries once, so that every case of that +type routes without a new catalog edit. Cases add parameter values, not routes. +`warden route find` should be able to answer "which SecurityCaseType covers this +need?" before anyone builds by hand. + + +2026-10-10 consolidation source prototype (not an accepted owner adapter API): +`src/warden/case_receipt_state.py` provides a pure immutable receipt reducer, +with exact case/type/adapter/catalog/acceptance/run-sheet pins, running intent +before dispatch, uncertain recovery after a lost result, no success replay, +explicit retry-safe failed-before-change retry and created-only reverse undo. +It executes nothing and accepts no command output. The caller must durably +persist running before any future side effect; storage/authenticated owner +receipts/live adapters are not implemented. Focused offline tests cover changed +pins, lost receipts, retry/refusal, uncertain rollback and shared-object +preservation. Source proposal: `docs/security-case-execution-proposal.md`. + +T03 remains open: typed owner execution/receipt contracts require review, the +Forgejo structural materialization needs owner receipts, and CLI dispatch, +contained sessions, native verification and provenance remain unimplemented. +There is a circularity to resolve with net-kingdom/ops-mason: materialization +cannot require mint-secret if mint-secret is itself a follow-through step whose +planning requires materialized. No case status is changed by this prototype. + + +2026-10-11: the exact execution-design memo version1 has confirmed native human +acceptance (approval e46b149b-ec3c-4b3b-98a7-dcb4934448ca). The approved proposal +bytes remain unchanged. `src/warden/case_run.py` now implements a closed v1 local +execution seam, independent admission/evidence callbacks, private transactional +SQLite receipt storage, process locking, running-before-dispatch, crash recovery +to uncertainty, explicit safe retry, separately required broker-grant and parent-session revocation +proofs, native owner reconciliation and created-only +reverse rollback. Thirty-seven kernel tests and fourteen reducer tests pass; +ruff checks pass. See `docs/security-case-execution-kernel-v1.md` for exact scope +and receipt provenance. No installed native adapter or live CLI execution is +claimed. Current prose types stay plan-only; acknowledged descriptor/type machine +seams and authenticated owner/caller receipts are still required. T03 progresses +for implementation but cannot finish before ordered native Forgejo acceptance. + +2026-10-16 source preparation (isolated checkout; no native acceptance): +T03's browser parameter mismatch now has a producer-validator-backed projection +in `case_inputs.py`, explicitly configured on the real Executor. Exact 1.1/1.2 +parameter definitions and all four reviewed producer modules are pinned; full case +validation admits reviewed security-sensitive metadata while unknown/fixed/secret +inputs and type/producer drift refuse. Only the reviewed descriptor target +reaches the owner callback; no parameter journal payload is added. Actual +Executor/producer tests cover positive versions and refusal/containment paths. +Acknowledged machine descriptors, authenticated owner evidence, real Forgejo +materialization and contained native sessions remain mandatory and unimplemented; +T03 stays progress, T04 stays wait. No type or case status changed. + +2026-10-16 producer source refresh: independently reviewed NetKingdom +`015694ee414c802dc2ef1fa0120dfed610086e69` optional descriptor-map repair. +Only the validator/evidence source pins change; exact parameter and transitive +source identity remain enforced. Actual canonical producer, Executor and reducer +suite passes 87 checks, including 14 new malformed-map integration cases; all +four missing/changed modules still refuse. Review: +`docs/reviews/2026-10-16-browser-producer-pin-refresh.md`. No adapter attestation, +native acceptance, credential use or privileged dispatch. T03 remains progress. + + +## T03 source correction and safe-test follow-through — 2026-10-17 + +The reviewed KERNEL-2/3 source now authenticates and parses the actual catalog +snapshot, checks lane owner/risk/delegation, and resolves each type step's Warden, +attended or owner dispatch mode. Four proposed attended-change lanes remain +DRAFT; no lane is promoted and PARAMETER_PINS still contains only 1.1/1.2. + +Independent review KR1-F1/F2 found cached attended run/rollback/reconcile could +lose the human/runtime boundary, and actual admission lacked the planner's +establishing-login checks. The exact successor retains the mode and authenticated +catalog bytes and repeats caller/session, agent marker and attended-lane checks +before any journal write or callback on all three operations. Planner and executor +share active/reviewed login/session-lane validation. Two unresolved Apps session +lanes correctly remain refused. Integration tests now explicitly disclose all +fixture authority substitutions; they establish no owner or native authority. + +The new 19-test regression matrix reproduces 13 failures on its predecessor and +passes on the candidate. A fresh independent reviewer adds changed-authority and +catalog-snapshot probes: 90 focused checks and six guarded integration checks +pass; the combined predecessor comparison has 22 expected failures. Author's +isolated full suite passes 765 with four integration-marked tests deselected. +Evidence: Custodian warden-1.3-kernel-fixes-r1 and warden-1.3-kernel-review-r2 +under docs/changes/netkingdom-target-2026-10-17. Original unsuccessful review, +invalid setup attempts and unknown ambient-lookup outcome are retained. + +A separate reviewed test-only fix stubs ambient OpenBao identity in access CLI +unit tests and adds a synthetic sentinel regression which fails before any +subprocess can launch on the predecessor; 26 access/helper checks pass. The real +production helper is unchanged. The first old review's unisolated launches may +have invoked ambient `bao token lookup`; remote reach/success remain unknown, +with no secret displayed. This patch does not erase that adverse outcome. + +Explicit remaining safe-test work is retained in this live T03 scope, owned by +ops-warden: replace or wrap the ordinary unit-test entry path with allowlisted +environment/empty synthetic HOME and guarded subprocess/network execution, +including nested validator children. Completion requires representative access +and case tests to refuse token-file/environment and live provider access under +synthetic negative controls while permitted local fixtures remain usable. Until +then use the preserved isolated review runner, not the ambient `make test` path. +No credential lookup, live investigation or rotation is authorized by this note. +T03 remains progress; contained sessions, authenticated owner provenance, +materialization, exact type acknowledgment/pin and first native case remain open. + + +2026-10-17 safe unit-runner source candidate: `make test` now selects an existing +interpreter and `scripts/run_unit_tests.py`, which constructs the environment and +empty temporary HOME before pytest collection, denies unexpected process/socket +use and ambient file opens, and guards the pinned BrowserInputs child too. +Closed fixture exceptions cover the synthetic desk endpoint, metadata checkers, +exact-byte contract doubles and fixed transport prints; they confer no provider +authority. Explicit public declaration fixtures replace ambient HOME source +lookups. See `docs/development/safe-unit-tests.md` and Custodian packet +`docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1`. +Synthetic token, ambient-path, forged-provider and nested-child regressions are +retained alongside the initial failed isolation runs and corrected results. This +is a source candidate for the existing safe-test handoff, not an OS sandbox or +native case acceptance. T03 remains progress; contained sessions, authenticated +owner provenance, materialization, exact type acknowledgment/pin and first native +case remain open. No credential lookup, live investigation or rotation occurred. + +Candidate validation: ordinary `make test` under synthetic ambient token/HOME +controls passes 783 tests with four integration-marked tests deselected. Affected +focused checks pass 117 tests; new runner code/regressions pass Ruff. Retained +initial failures exposed fixture dependencies and were corrected, not suppressed. diff --git a/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/implementation.json b/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/implementation.json new file mode 100644 index 0000000..e5d43ab --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/implementation.json @@ -0,0 +1,120 @@ +{ + "schema": "custodian.warden-safe-unit-runner-implementation.v1", + "date": "2026-10-17", + "role": "implementation author; not independent review", + "status": "source-candidate-not-canonically-applied", + "base": "922c26da0f3fcb0182352ea83e6ae06f29878580", + "preserved_allocator_commit": "922c26da0f3fcb0182352ea83e6ae06f29878580", + "previous_base": "ae7bc73b4f4bac22454144959b870d161498e0af", + "netkingdom_test_source_head": "befb0bc68cd25b625804fce559476eefd702521d", + "phase_memory_test_source_head": "755fd2c3e2a5414cd89ee59df1814196d872b768", + "files": [ + { + "path": "Makefile", + "before_sha256": "9186d6166f3192b814f901350724b9c8a14d7db5b6c8664e9b0f2b5d5565e9b8", + "after_sha256": "8cf08ec680ed6bd323a1ce8475ade731dbeebcd64ffd43ecd169308f8a727940" + }, + { + "path": "scripts/run_unit_tests.py", + "before_sha256": null, + "after_sha256": "8157e44df298fa4c47c6c08b2dc68e3ac945c4beb3f47a251a7afbbc55eb1bd2" + }, + { + "path": "tests/test_safe_unit_runner.py", + "before_sha256": null, + "after_sha256": "6b623d18879e7564dd11447c16d3ec9f80321a25a525fcd0306b6a903a68031a" + }, + { + "path": "tests/unit_isolation_probe.py", + "before_sha256": null, + "after_sha256": "726d3e01ae21c1379588e1f08459449908cc8a75ffea1b337c33a1a888c66f59" + }, + { + "path": "tests/test_layer_conformance.py", + "before_sha256": "93e3a8805460c4a6a4fd78734cf4dd85947a46e23d16a0d6898a43e8ec39275d", + "after_sha256": "3edf0a0d49c3560278a564c477834335cfaf5243341cb9345f0d4bf8994db56c" + }, + { + "path": "tests/test_workload_join.py", + "before_sha256": "b5f5be43192d78105dbe86ada736d8a10705cadd4ee64682d1fb78768cbcd191", + "after_sha256": "1828908a5cd1f0bc13ca367dbd1245221c52d1ac60789c9e3bd51dd36cb8beb9" + }, + { + "path": "tests/test_case_plan.py", + "before_sha256": "df8ccc89ab840efd1d5242c1b4e68a1c589168d89768ae44bdbb2b5f6d0e6b55", + "after_sha256": "944bc2a725ca1b7a10f4516c3ceed65f99627057dd15a859da4f8ffb3d97e71f" + }, + { + "path": "tests/test_case_type.py", + "before_sha256": "4e2ec3faa030d3036534b7f9564a41abf567d4a951c8458a589b68076b7debf9", + "after_sha256": "5a217a172e7e2ffbea66f1beee9a1c18bf5e97f9fb25b83cb96b6ba976cbb2a5" + }, + { + "path": "docs/development/safe-unit-tests.md", + "before_sha256": null, + "after_sha256": "b157c3e6956a41dcd9fc3912d0c7d09a664f11f08f0f8a8e4cae57fac9747cf7" + }, + { + "path": "workplans/WARDEN-WP-0044-security-case-follow-through.md", + "before_sha256": "10c6f02d578837dee12acd8b638d18c5b9f93b6a9c4aff3083ce60c3752d1664", + "after_sha256": "074bfcf2f7841e78bcd404bf573982d244b00f2f500e198b7fe80fea660de991" + }, + { + "path": "tests/fixtures/workload-coverage/provenance.json", + "before_sha256": null, + "after_sha256": "a4a203c1bd7bc5b8039d1b2525bea65dbae288a3f552969e4218cd5abda96990" + }, + { + "path": "tests/fixtures/workload-coverage/rapp-core-hub/declarations/rapp.yaml", + "before_sha256": null, + "after_sha256": "9ce902da0c77809f1094a634244652835b162f59682e9fa6923d8c8aa4ed4a3c" + }, + { + "path": "tests/fixtures/workload-coverage/rapp-issue-core/declarations/rapp.yaml", + "before_sha256": null, + "after_sha256": "a1956deecf2f0fbca44549e5ea5e8f1c4b74ebf91a9482c64c982491af0d923b" + }, + { + "path": "tests/fixtures/workload-coverage/rapp-qonto/declarations/rapp.yaml", + "before_sha256": null, + "after_sha256": "15aa9809e8d6a120c516be5ce9adfb72c00382140c171e791083ad117f503b3f" + } + ], + "patch_sha256": "1fe65076ce7e633be304fc779bf2c87b7ee1e4e460de55ca74355b5842d036f5", + "patch_applies_to_exact_archive": true, + "validation": { + "focused_initial": "136 passed, 2 failed (ambient source/metadata assumptions)", + "focused_metadata_corrected": "42 passed", + "guard_initial": "28 passed", + "full_initial": "13 failed, 768 passed, 2 skipped, 4 deselected; diagnostic intermediate state, retained", + "focused_corrected": "117 passed", + "full_final": "783 passed, 4 deselected in 62.70s", + "lint": "new runner and regression files: all checks passed" + }, + "full_initial_note": "Intermediate diagnostic run: a source correction removing WARDEN_MEMORY=0 was made while its parent had already loaded the earlier runner. No final-candidate claim is made from that mixed-version diagnostic run. Its actual refusal traces and failures are retained. Final full run used stable corrected code.", + "ambient_controls": { + "input_tokens": "synthetic BAO_TOKEN and VAULT_TOKEN only", + "input_home": "temporary synthetic HOME with synthetic .vault-token created by harness; never a real user home", + "input_other": "synthetic PYTHONPATH and invalid synthetic PYTEST_ADDOPTS prove they are not propagated", + "child_probe": "synthetic token/HOME monkeypatches discarded; child reports only presence booleans; socket/provider/home/nested operations refused", + "actual_credential_values_read": false + }, + "native_owner_actions": false, + "local_process_exceptions": "guarded Python fixtures; bounded local git log/status; exact-byte synthetic Bash doubles and five fixed printf fixtures", + "canonical_edits": false, + "commits": false, + "dependencies_downloaded": false, + "workplan": { + "path": "workplans/WARDEN-WP-0044-security-case-follow-through.md", + "task": "WARDEN-WP-0044-T03", + "status": "progress preserved", + "ids_statuses_dependencies_unchanged": true, + "new_workplan_or_task": false + }, + "limits": [ + "Trusted Python unit-test accident guard, not an OS isolation or hostile-code security boundary.", + "Direct pytest bypasses launcher. Native extensions, preopened descriptors and non-open filesystem syscalls are not fully controlled.", + "Reviewed system/interpreter/source roots remain readable. Exact Bash/Git fixtures have no Python audit hook inside their native binaries.", + "No production Warden code, authority, catalog/source/parameter pin or native acceptance changed." + ] +} diff --git a/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/packet-hashes.json b/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/packet-hashes.json new file mode 100644 index 0000000..9212360 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-r1/packet-hashes.json @@ -0,0 +1,26 @@ +{ + "README.md": "7322316db3fdd7107efe82d42166a25dd494161f8062aaa833b4497392fd04bb", + "candidate/Makefile": "8cf08ec680ed6bd323a1ce8475ade731dbeebcd64ffd43ecd169308f8a727940", + "candidate/docs/development/safe-unit-tests.md": "b157c3e6956a41dcd9fc3912d0c7d09a664f11f08f0f8a8e4cae57fac9747cf7", + "candidate/scripts/run_unit_tests.py": "8157e44df298fa4c47c6c08b2dc68e3ac945c4beb3f47a251a7afbbc55eb1bd2", + "candidate/tests/fixtures/workload-coverage/provenance.json": "a4a203c1bd7bc5b8039d1b2525bea65dbae288a3f552969e4218cd5abda96990", + "candidate/tests/fixtures/workload-coverage/rapp-core-hub/declarations/rapp.yaml": "9ce902da0c77809f1094a634244652835b162f59682e9fa6923d8c8aa4ed4a3c", + "candidate/tests/fixtures/workload-coverage/rapp-issue-core/declarations/rapp.yaml": "a1956deecf2f0fbca44549e5ea5e8f1c4b74ebf91a9482c64c982491af0d923b", + "candidate/tests/fixtures/workload-coverage/rapp-qonto/declarations/rapp.yaml": "15aa9809e8d6a120c516be5ce9adfb72c00382140c171e791083ad117f503b3f", + "candidate/tests/test_case_plan.py": "944bc2a725ca1b7a10f4516c3ceed65f99627057dd15a859da4f8ffb3d97e71f", + "candidate/tests/test_case_type.py": "5a217a172e7e2ffbea66f1beee9a1c18bf5e97f9fb25b83cb96b6ba976cbb2a5", + "candidate/tests/test_layer_conformance.py": "3edf0a0d49c3560278a564c477834335cfaf5243341cb9345f0d4bf8994db56c", + "candidate/tests/test_safe_unit_runner.py": "6b623d18879e7564dd11447c16d3ec9f80321a25a525fcd0306b6a903a68031a", + "candidate/tests/test_workload_join.py": "1828908a5cd1f0bc13ca367dbd1245221c52d1ac60789c9e3bd51dd36cb8beb9", + "candidate/tests/unit_isolation_probe.py": "726d3e01ae21c1379588e1f08459449908cc8a75ffea1b337c33a1a888c66f59", + "candidate/workplans/WARDEN-WP-0044-security-case-follow-through.md": "074bfcf2f7841e78bcd404bf573982d244b00f2f500e198b7fe80fea660de991", + "candidate.patch": "1fe65076ce7e633be304fc779bf2c87b7ee1e4e460de55ca74355b5842d036f5", + "focused-corrected.log": "b882102d13be229872d931d97187b8893ad8edfd889b2571b3cfb558b73c6811", + "focused-initial.log": "c115cbd9ea50e9a55cd35f3b78b42cc10204d5780beba8c40501d55ee04760e3", + "focused-metadata-corrected.log": "e027958397e90c267e7d07e43ce4c78bbe8cfb980bdad575eff0bcadce023b3f", + "full-final.log": "544a4be60b5409ad7af5d1b399691c962ed0fffefd2732376b1a2a4ea2c992be", + "full-initial-failed.log": "be4aedc321eb7ae6e956f670b4d6ee89d12ef7057fec7dcbe35e9c98829b3d04", + "guard-regressions-initial.log": "399377e7ef48191e50c473a085fa44f25132a838a3597010bd416ea91dce5373", + "implementation.json": "8033b323178e1d3b031a45466687fe05479627e99559d9de7b3baef52ab8671f", + "lint.log": "82b3e6a6c090a57601d22943bd23fca9218d1031dbe5a7b754092f9a156b4f18" +} diff --git a/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-review-r1/review-r1.json b/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-review-r1/review-r1.json new file mode 100644 index 0000000..fb646d5 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-review-r1/review-r1.json @@ -0,0 +1,127 @@ +{ + "schema": "custodian.warden-safe-unit-runner-review.v1", + "date": "2026-10-17", + "role": "independent reviewer of platform_f2_review author candidate; reviewer-proposed correction kept separate", + "disposition": "reject original exact14-file candidate; minimal correction proposed and focused-tested separately", + "inputs": { + "base": "922c26da0f3fcb0182352ea83e6ae06f29878580", + "candidate.patch": "sha256:1fe65076ce7e633be304fc779bf2c87b7ee1e4e460de55ca74355b5842d036f5", + "implementation.json": "sha256:8033b323178e1d3b031a45466687fe05479627e99559d9de7b3baef52ab8671f", + "README.md": "sha256:7322316db3fdd7107efe82d42166a25dd494161f8062aaa833b4497392fd04bb", + "packet-hashes.json": "sha256:1567ff1ad558fdb464818bb555dab79152477c85113f89874daa3049029733a8", + "all14_candidate_after_hashes": "independently verified against applied fresh archive before reviewer mutations", + "net_kingdom": "befb0bc68cd25b625804fce559476eefd702521d", + "phase_memory": "755fd2c3e2a5414cd89ee59df1814196d872b768" + }, + "findings": [ + { + "id": "SUR-R1-F1", + "severity": "medium", + "blocking": true, + "location": "scripts/run_unit_tests.py Guard.audit open write-mask", + "title": "O_TRUNC alone is classified as read access and truncates readable source", + "counterexample": "os.open(synthetic_readable_source, os.O_RDONLY | os.O_TRUNC) triggers the audited open event with no O_WRONLY/O_RDWR/O_CREAT bits. Candidate classifies it as a read and permits it under ROOT. On Linux it successfully truncates the source file. Independent probe used only a21-byte synthetic scratch fixture and observed truncation; no canonical file or ambient value was touched.", + "expected": "Refuse before opening/truncation whenever open flags request truncation, including read-only flag combinations.", + "scope": "An open-audit loophole within the advertised contract, not the documented limitation covering non-open mutation syscalls.", + "minimal_fix": "Include os.O_TRUNC and conservative os.O_APPEND in write mask. New regression uses only synthetic data in the temporary fixture tree with a separate guarded writable subtree." + }, + { + "id": "SUR-R1-F2", + "severity": "medium", + "blocking": true, + "location": "tests/fixtures/workload-coverage/provenance.json and snapshot consumers", + "title": "Public fixture projection hashes are recorded but never checked", + "counterexample": "Append one synthetic comment to rapp-core-hub projection. Existing workload-join and published-coverage tests still both pass, despite projection_sha256 being stale. Reviewer hash check fails with expected9ce902... versus observed2e9381....", + "expected": "Default unit tests fail closed when shipped projection bytes disagree with their recorded provenance, while preserving documented snapshot-only semantics.", + "minimal_fix": "Add one ordinary test validating the closed3-entry projection set and every projection_sha256. It does not query live repositories or claim live/source-owner freshness." + } + ], + "positive_review": { + "ordinary_make_entry": "Makefile now launches an already installed interpreter with-I and the committed runner, without uv/resolution. Worker creates guard before pytest import/collection. Parent environment is constructed and guarded children receive reconstructed HOME/env even after synthetic parent mutation.", + "closed_process_inventory": "No generic Python -c, generic Git status, provider token lookup, extra shell text or inherited-FD widening was permitted by independent probes. Native Bash exceptions are exact fixed printf commands/exact-byte doubles; child Git is restricted to2 metadata queries. Production adapter behavior and catalog/parameter pins are untouched.", + "python_children": "Existing child probes independently passed under ordinary make test. A corrupted pinned validator module was refused before even the synthetic Popen observer ran. Six real pinned BrowserInputs integration tests passed in first focused run.", + "loopback": "Single actual synthetic dry desk HTTP test passed. Unleased TCP sockets were refused. Code binds127.0.0.1:0 only, registers its resulting endpoint and restricts connect to it; lease clears per test. No real loopback service was contacted.", + "docs_and_maintainability": "14-file scope is justified by launcher/regressions,4 explicit source-fixture adaptations, documentation/workplan note and3 projections/provenance. Docs candidly call this a trusted-test accident guard, not an OS sandbox; direct pytest and native/non-open/preopened-descriptor limits remain. Frozen artifact tests now explicitly measure a snapshot, not the current estate." + }, + "validation": [ + { + "command": "python3 /tmp/warden-safe-unit-runner-review-r1/run.py focused.log <18 author guard tests,18 independent probes,6 BrowserInputs integration tests,desk HTTP,2 coverage tests>", + "result": "43 passed,1 failed in12.64s; only original O_TRUNC guard failure" + }, + { + "command": "python3 .../run.py provenance-drift-existing.log <2 existing coverage tests>", + "result": "2 passed despite deliberately stale fixture projection hash" + }, + { + "command": "python3 .../run.py provenance-drift-negative.log tests/test_review_safe_runner.py::test_public_projection_hashes_are_current", + "result": "1 failed; independently detects the deliberately stale projection hash" + }, + { + "command": "python3 .../run.py correction-regressions-before.log <2 newly proposed correction tests>", + "result": "2 failed before code fix and fixture restoration; exact counterexamples retained" + }, + { + "command": "python3 .../run.py corrected-focus.log tests/test_safe_unit_runner.py tests/test_review_safe_runner.py tests/test_workload_join.py tests/test_layer_conformance.py::test_published_coverage_equals_measured_coverage", + "result": "40 passed in0.97s after minimal write-mask fix and restoration of original public fixture bytes" + }, + { + "command": "python3 .../run.py collect-default.log --collect-only -q", + "result": "Collect-only receipt lists785 selected tests with correction2 tests; source has4 integration-marked tests excluded by unchanged default not-integration expression. Double-quiet collection omits the deselection summary; no native integration execution." + } + ], + "correction": { + "file": "reviewer-correction.patch", + "sha256": "sha256:9407289f8da41ed6fff367baf566ad30f5510a2627c14e0ac0cf971bf9b465c9", + "applies": "After exact original candidate on fresh922c26d archive; application and all3 afterhashes independently checked in second scratch tree", + "files": { + "scripts/run_unit_tests.py": { + "before_sha256": "sha256:8157e44df298fa4c47c6c08b2dc68e3ac945c4beb3f47a251a7afbbc55eb1bd2", + "after_sha256": "sha256:86880094b33114fdefb26691140b5c9a516cdba5fe6ddabd70fd5b7f18e9d2aa" + }, + "tests/test_safe_unit_runner.py": { + "before_sha256": "sha256:6b623d18879e7564dd11447c16d3ec9f80321a25a525fcd0306b6a903a68031a", + "after_sha256": "sha256:5408e7face98b01b92da077b0b2d115fcb370ec46e572ad5a69e3d0c10df8433" + }, + "tests/test_workload_join.py": { + "before_sha256": "sha256:1828908a5cd1f0bc13ca367dbd1245221c52d1ac60789c9e3bd51dd36cb8beb9", + "after_sha256": "sha256:8ef0891daf4b7ac520430a514690364917110628edc760911a850195cffbdd4a" + } + }, + "status": "Reviewer-proposed minimal correction, focused-tested; separate from original author patch and original rejection. Root may carry as corrected candidate pending its source disposition/revalidation.", + "original_public_fixture_bytes": "Restored unchanged; no projection/provenance hash was updated to hide the negative test." + }, + "protected_source_hashes": { + "src/warden/case_run.py": "sha256:1bc73e30ac718fd75bbaff2a739495fc051aeaec73802ac71f0583cb2d53ab0a", + "src/warden/case_inputs.py": "sha256:1334b0cd9897e763cfea2d306e997c659f3f284ecc3f9612140c2104835da7c7", + "src/warden/routing/models.py": "sha256:b85372b2572a49af62e9534f26a33963b894633c7cd43dd2a28416b8959cba3e", + "registry/routing/catalog.yaml": "sha256:ba9634a92e584dac8c42e8624ab52adce06e79a285209cb64c0daa4dac9cd7d8", + "registry/generated/high-risk-data-paths.yaml": "sha256:5cd9c03b86751f7197cb5661a53a86d59765137958b215ec14bb343156f216d0", + "pyproject.toml": "sha256:c8f7c53f0bd0f8ae4e7242342fe47b1f36d47fd221964782a3a6953e0f804e01" + }, + "boundaries": { + "canonical_edits": false, + "commits": false, + "ambient_credential_reads": false, + "native_owner_actions": false, + "dependencies_downloaded": false, + "synthetic_controls": "Allowlisted outer process environment with only synthetic BAO_TOKEN,VAULT_TOKEN,HOME/.vault-token,PYTHONPATH,PYTEST_ADDOPTS,BASH_ENV/ENV sentinels. Source archives are public frozen commits. Provider commands were refused before launch; only intended local fixture Python/Bash and synthetic HTTP were exercised." + }, + "limits": [ + "No full-suite recertification; author783-pass final run is attributed to author, not adopted as independent full-suite evidence.", + "Source roots/interpreters/libraries remain trusted and directory-readable by design. This is no OS isolation or grant.", + "Projection digest check proves shipped fixture consistency, not live estate freshness or source-owner authenticity.", + "No native acceptance,1.3 pin,draft promotion,credential investigation or external owner assent.", + "Original author initial failure logs remain intact." + ], + "packet_hashes": { + "provenance-drift-negative.log": "sha256:357556c55ac2a8befe88b688ced1808a50132f4fcb42b670ba3468085ea63098", + "reviewer-probes.py": "sha256:0b49fc2ad7aeeca0cabb0961b06d8cdc85ae5109f36bf81e4245e55127357574", + "provenance-drift-existing.log": "sha256:d633d56ea09739398410f73e61cc2f24fb25aa180d885a5a8ceea55f3d4e3fb4", + "run.py": "sha256:f7fc81b8acbcd0712ee5f79a16b27aa1cdd0af11db761f9e94fba8b91e51ec8a", + "reviewer-correction.patch": "sha256:9407289f8da41ed6fff367baf566ad30f5510a2627c14e0ac0cf971bf9b465c9", + "corrected-focus.log": "sha256:2c55ba79aefa81de60a93ef7aa385f95c91925cad6889aa963c381bbaa0b2589", + "focused.log": "sha256:592a914c2c0855d6177bc84d72e433bfa2683865336ff1dde891bb099bba22f0", + "collect-default.log": "sha256:d41e8c609c11b28dd5baf0fc3763f0c882284a20da6018e00110a4a15f607228", + "correction-regressions-before.log": "sha256:7927b0e20deca4da6450534bd1d0566733908ffcf5b944ede95b02c9f2492af6" + } +} diff --git a/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-review-r1/reviewer-correction.patch b/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-review-r1/reviewer-correction.patch new file mode 100644 index 0000000..c791c11 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-review-r1/reviewer-correction.patch @@ -0,0 +1,56 @@ +diff --git a/scripts/run_unit_tests.py b/scripts/run_unit_tests.py +--- a/scripts/run_unit_tests.py ++++ b/scripts/run_unit_tests.py +@@ -62,7 +62,7 @@ + path = Path(os.fsdecode(args[0])).resolve() + mode, flags = args[1:3] + write = ((isinstance(mode, str) and any(c in mode for c in 'wax+')) +- or (isinstance(flags, int) and flags & (os.O_WRONLY | os.O_RDWR | os.O_CREAT))) ++ or (isinstance(flags, int) and flags & (os.O_WRONLY | os.O_RDWR | os.O_CREAT | os.O_TRUNC | os.O_APPEND))) + roots = (self.temporary,) if write else self.read_roots + if path != Path('/dev/null') and not inside(path, roots): + raise IsolationDenied('unit isolation: file outside reviewed roots') +diff --git a/tests/test_safe_unit_runner.py b/tests/test_safe_unit_runner.py +--- a/tests/test_safe_unit_runner.py ++++ b/tests/test_safe_unit_runner.py +@@ -79,3 +79,14 @@ + provider.chmod(0o700) + with pytest.raises(PermissionError, match='double bytes differ'): + subprocess.run([str(provider), 'token', 'lookup'], check=True) ++ ++ ++def test_os_open_truncation_respects_read_only_source_root(guarded, tmp_path, monkeypatch): ++ # Synthetic readable source outside the guard's writable subtree. ++ source = tmp_path / 'synthetic-source' ++ source.write_bytes(b'synthetic public source') ++ monkeypatch.setattr(guarded, 'temporary', tmp_path / 'writable') ++ with pytest.raises(PermissionError, match='unit isolation'): ++ descriptor = os.open(source, os.O_RDONLY | os.O_TRUNC) ++ os.close(descriptor) ++ assert source.read_bytes() == b'synthetic public source' +diff --git a/tests/test_workload_join.py b/tests/test_workload_join.py +--- a/tests/test_workload_join.py ++++ b/tests/test_workload_join.py +@@ -1,5 +1,7 @@ + """Explicit lane-to-workload join tests (WARDEN-WP-0032 / RMGR-WP-0010-T06).""" + from pathlib import Path ++import hashlib ++import json + + import yaml + +@@ -68,3 +70,14 @@ + assert not report["resolved"] + assert report["unknown"][0]["lane"] == "x" + assert "deployable" in report["unknown"][0]["reason"] ++ ++ ++def test_public_workload_projection_matches_provenance(): ++ base = ROOT / 'tests/fixtures/workload-coverage' ++ provenance = json.loads((base / 'provenance.json').read_text()) ++ expected = {'rapp-core-hub', 'rapp-issue-core', 'rapp-qonto'} ++ assert len(provenance) == len(expected) ++ assert {row['repo'] for row in provenance} == expected ++ for row in provenance: ++ projection = base / row['repo'] / 'declarations/rapp.yaml' ++ assert hashlib.sha256(projection.read_bytes()).hexdigest() == row['projection_sha256'] diff --git a/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-review-r1/reviewer-probes.py b/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-review-r1/reviewer-probes.py new file mode 100644 index 0000000..6f42b2f --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-review-r1/reviewer-probes.py @@ -0,0 +1,82 @@ +"""Reviewer-owned synthetic accident-control probes, no credential/native actions.""" +import hashlib +import json +import os +from pathlib import Path +import socket +import subprocess +import sys +import pytest + +ROOT=Path(__file__).resolve().parents[1] + +@pytest.fixture +def guard(request): + value=getattr(request.config,'_unit_guard',None) + assert value is not None + return value + +@pytest.mark.parametrize('name',['BAO_TOKEN','VAULT_TOKEN','PYTHONPATH','PYTEST_ADDOPTS','BASH_ENV','ENV']) +def test_synthetic_ambient_environment_not_propagated(guard,name): + assert name not in os.environ + +@pytest.mark.parametrize('path',[ + '/tmp/warden-safe-unit-runner-review-r1/outer-home/.vault-token', + '/tmp/warden-review-nonexistent-ambient-home/.kube/config', +]) +def test_synthetic_outer_home_open_refused(guard,path): + with pytest.raises(PermissionError,match='unit isolation'): + Path(path).read_bytes() + +@pytest.mark.parametrize('command,options',[ + (['bao','token','lookup'],{}), + (['/usr/bin/git','status'],{}), + ([sys.executable,'-c','print(42)'],{}), + ("printf 'sekret'; printf extra",{'shell':True}), + (['printf','sekret'],{}), + ([sys.executable,'-c','print("SYNTHETIC-SECRET")'],{'close_fds':False}), +]) +def test_no_generic_or_widened_process_execution(guard,command,options): + with pytest.raises(PermissionError,match='unit isolation'): + subprocess.run(command,**options) + + +def test_changed_validator_module_refuses_before_launch(guard,tmp_path,monkeypatch): + from warden import case_inputs + directory=tmp_path/'warden-case-validator-review' + for relative in case_inputs.SOURCE_PINS: + target=directory/relative;target.parent.mkdir(parents=True,exist_ok=True) + target.write_bytes(b'not the pinned producer') + calls=[] + def forbidden(*args,**kwargs): + calls.append(args) + raise AssertionError('process must not launch') + monkeypatch.setattr(guard,'original_popen',forbidden) + with pytest.raises(PermissionError,match='validator source pin differs'): + subprocess.run([sys.executable,'-I','-c',case_inputs._VALIDATE,str(directory)]) + assert calls==[] + + +def test_unexpected_loopback_socket_refused(guard): + with pytest.raises(PermissionError,match='sockets disabled'): + socket.socket(socket.AF_INET,socket.SOCK_STREAM) + + +def test_source_truncate_via_os_open_is_refused(guard): + path=ROOT/'tests/reviewer-truncate.fixture' + before=path.stat().st_size + try: + descriptor=os.open(path,os.O_RDONLY|os.O_TRUNC) + except PermissionError: + pass + else: + os.close(descriptor) + pytest.fail('source O_TRUNC open escaped the write guard') + assert path.stat().st_size==before + + +def test_public_projection_hashes_are_current(): + base=ROOT/'tests/fixtures/workload-coverage' + for row in json.loads((base/'provenance.json').read_text()): + data=(base/row['repo']/'declarations/rapp.yaml').read_bytes() + assert hashlib.sha256(data).hexdigest()==row['projection_sha256'] diff --git a/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-review-r1/run.py b/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-review-r1/run.py new file mode 100644 index 0000000..10bcf17 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/warden-safe-unit-runner-review-r1/run.py @@ -0,0 +1,8 @@ +from pathlib import Path +import subprocess,sys +r=Path('/tmp/warden-safe-unit-runner-review-r1') +env={'HOME':str(r/'outer-home'),'TMPDIR':str(r/'outer-tmp'),'PATH':'/usr/bin:/bin','LANG':'C.UTF-8','BAO_TOKEN':'synthetic-review-token-only','VAULT_TOKEN':'synthetic-review-token-only','PYTEST_ADDOPTS':'invalid-synthetic-pytest-options','PYTHONPATH':'/tmp/never-import-synthetic','BASH_ENV':'/tmp/never-read-synthetic-bash-env','ENV':'/tmp/never-read-synthetic-env'} +command=['make','test','UNIT_PYTHON=/tmp/warden-kernel-review-r1/final/.venv/bin/python','PHASE_MEMORY_REPO='+str(r/'phase-memory'),'TEST_ARGS=--netkingdom-root '+str(r/'net-kingdom')+' -q '+ ' '.join(sys.argv[2:])] +with (r/sys.argv[1]).open('w') as output: + result=subprocess.run(command,cwd=r/'ops-warden',env=env,stdout=output,stderr=subprocess.STDOUT) +print(result.returncode);sys.exit(result.returncode) diff --git a/docs/netkingdom-target-implementation.md b/docs/netkingdom-target-implementation.md index 2de08a5..79dcde6 100644 --- a/docs/netkingdom-target-implementation.md +++ b/docs/netkingdom-target-implementation.md @@ -1061,3 +1061,88 @@ then the attended negative/revoke/cleanup proof. KEY39 stays active with T02 waiting. Infrastructure source-review approval supplies no missing native proof. Evidence: `key39-infra-finalization-r1/assessment.json` and its linked owner reviews in the dated 17 October packet. + + +## Case admission and issuer-reference source corrections, 2026-10-17 + +The source path behind TSA-03/TSA-04 and KEY39's Forgejo prerequisite now carries +reviewed Warden kernel corrections. Implementation commit `ae7bc73` is published +and projected at `922c26d`; the latter is the normal allocator's separate addition +of one previously missing UUID on an already projected completed task, with no +code, task status or authority change. The kernel retains the authenticated +catalog snapshot and dispatch mode and rechecks attended human/session/lane +conditions before run, rollback or reconcile can write the journal or call an +owner. Planner and executor share login/session-lane checks. Ninety focused +integration-worktree checks pass; independent review also passed six guarded +integration checks. The two unresolved Apps sessions remain refused. Four new +catalog entries stay draft and the production parameter pins still cover only +1.1 and 1.2. No catalog promotion or native acceptance follows. + +NetKingdom source commit `342a6f25` restores independent mint-guard regressions: +the old suite missed a guard-removed mutant, while the two new tests detect it. +The next commit `befb0bc6` uses valid byte-bound synthetic catalogs in downstream +admission tests. Canonical guarded validation passes 31 tests / 53 subtests on +the reviewed current Warden API, preserving the two unresolved Apps refusals. +T32 and T35 record distinct source-test outcomes; existing first-case/native +gates remain open. + +Platform commit `104ed90` publishes a proposed fixed issuer ESO template and +pure public-metadata checks. Independent source review and eight focused tests +pass. The proposed 1.3 deliver-secret entry now names the exact cluster-scoped +`openbao-keycape-{client_id}-issuer` store restricted to `sso`, and pins that +Platform template's digest. This resolves the vague source reference; all type +parameter bytes and seven descriptors remain unchanged, and composition +regeneration is byte-identical. T33 records this narrow source completion, +not owner assent, installed ESO support or actual delivery/removal/session +implementation. Those remain existing Platform and T16 admission obligations. + +The consumer inverse is not closed. A fresh Apps-scoped review reproduced an +inverse using configuration provenance after the hand-over session had been +revoked without obtaining fresh inverse authority. The corrected r2 prototype +requires a separately authenticated inverse session before the release-writer +lock, with revocation on every exit; 19 tests / 33 subtests pass and guard-removal +controls fail. Actual Apps source has no supported durable configuration journal, +all-release-writer exclusion or inverse-session backend to bind. Four concrete +owner facilities and the minimum existing RAPPS-WP-0016-T05 implementation are +recorded in the r2 packet; T34 progresses and T16 retains exact composition and +admission. The candidate is not installed and its type prose is not integrated +as an operational capability. Public clients, confidential NO_CHANGE delivery, +and failure before hand-over receipt creation must all have a reachable inverse. + +The old kernel review's possible ambient `bao token lookup` remains an adverse +unknown, not a compliant test success. A reviewed access-unit-test fixture and +synthetic forbidden-subprocess regression prevent that known lookup path; real +production helper behavior is unchanged. Broader isolated test-entry work remains +explicitly in live WARDEN-WP-0044-T03. No credential investigation or rotation +was run. All sixteen native target gates remain open. + +Evidence in the dated 17 October packet: `warden-1.3-kernel-review-r2/`, +`warden-kernel-source-integration-r1/verification.json`, +`nk48-r5-source-followthrough-r1/`, `nk48-current-source-publication-r3/`, +`nk48-r5-f2-publication-r1/`, `nk48-issuer-reference-integration-r1/`, and +`nk48-r5-f3-{review-r1,implementation-r2}/`. + + +### Corrected ordinary unit-test entry + +Warden source commit `b593c879` integrates the corrected safe unit runner after +root review of the two independently reproduced defects. Ordinary `make test` +now constructs its temporary HOME/environment and guards subprocess/file/network +use before pytest collection, including the fixed pinned-validator children. +Only the reviewed closed local fixture inventory and one test-owned loopback +endpoint are available. Dependencies must already be installed; no uv resolution +or download runs through this test entry. Two new regressions cover truncating +opens and recorded projection hashes. The stable corrected ordinary full run +passes **785 tests, four deselected** in 65.43 seconds under synthetic parent +token/HOME/Python/pytest sentinels. Production source, catalog, parameter pins +and original four WP44 task blocks are unchanged. + +The first candidate remains rejected in its frozen review. Its O_TRUNC failure, +stale-provenance counterexample and author intermediate failures are retained. +This completes the known trusted-unit-entry accident-prevention follow-through; +direct pytest bypass and native-extension, non-open-syscall and preopened-file- +descriptor limitations remain explicit. This is not an OS sandbox or evidence of +an isolated actual agent assignment. Native T03/TSA-06 and all sixteen target +gates remain open. Exact root disposition, full result and publication receipts: +`warden-safe-unit-integration-r1/`; source review and correction: +`warden-safe-unit-runner-review-r1/` in the dated packet. diff --git a/docs/netkingdom-target-security-architecture.md b/docs/netkingdom-target-security-architecture.md index cddb984..c090563 100644 --- a/docs/netkingdom-target-security-architecture.md +++ b/docs/netkingdom-target-security-architecture.md @@ -119,6 +119,20 @@ contained mint/delivery, successful sign-in, denied groups, revocation and rollback are verified independently. Acknowledged, implemented, materialized and live are distinct states. A case renderer always remains plan-only. +Admission binds the exact authenticated catalog snapshot and dispatch mode. +Attended run, rollback and reconcile revalidate the current caller and session +before journal writes or owner callbacks; a cached admission does not preserve +expired or changed authority. An attended change resolves its establishing login +in that same catalog. An unresolved owner login remains unavailable. + +The owner configuration inverse remains reachable for public clients, unchanged +secret delivery and failure before a hand-over receipt exists. Receipt deletion +and created-only secret cleanup cannot substitute for that inverse. Its durable +before/after provenance identifies the case's configuration change; it supplies +no execution authority. A fresh authorized inverse session and exclusion of all +release writers protect the current-state check and inverse. Where the target +offers no compare-and-swap operation, a local advisory lock alone is insufficient. + After this case passes, a second consumer proves reuse of its adapters and contracts. Rapp Playwright's human/agent identity and connectivity remain an explicit consumer acceptance obligation under CUST-WP-0080-T05 and NK-WP-0050; diff --git a/workplans/CUST-WP-0080-netkingdom-security-blueprint-and-gap-closure.md b/workplans/CUST-WP-0080-netkingdom-security-blueprint-and-gap-closure.md index 762e6dd..7b5b6af 100644 --- a/workplans/CUST-WP-0080-netkingdom-security-blueprint-and-gap-closure.md +++ b/workplans/CUST-WP-0080-netkingdom-security-blueprint-and-gap-closure.md @@ -1217,3 +1217,33 @@ unadmitted coding-client custody/owner readiness returns remain prerequisites. The parent stays active, T02 waits, and no native gate closes. No new helper protocol or repeated original-client registration approval is required. Evidence: `docs/changes/netkingdom-target-2026-10-17/key39-infra-finalization-r1/`. + + +2026-10-17 T03 implementation checkpoint: independent GPT-6.1-Sol medium +reviewers checked the actual Warden kernel, fixed issuer-reference and Apps +inverse candidates. Reviewed kernel corrections plus the narrow ambient-lookup +unit-test fix are integrated and published at Warden `922c26d`; the normal +allocator's existing completed-task UUID addition is retained separately from +implementation `ae7bc73`. All four WP44 task bindings/statuses remain unchanged. +Platform `104ed90` publishes the proposed exact issuer ESO source; NetKingdom +`342a6f25` and `befb0bc6` carry independent mint regressions, valid catalog +fixtures and exact proposed issuer-template text. Source tests and limitations +are recorded in docs/netkingdom-target-implementation.md. NK48 refinement adds +completed source tasks T32/T33/T35 and keeps T34 progress for the real Apps +inverse/backend; T16 and first native case remain open. The fresh inverse-session +prototype is corrected and tested, but no real owner backend or authority is +invented. Earlier rejected reviews, the possible ambient lookup and failed +preparations remain retained. All six coordinator task blocks/statuses/UUIDs +are unchanged; all sixteen target native gates remain open. + + +2026-10-17 T03 safe-unit follow-through: Warden `b593c879` integrates the +ordinary test-entry guard after independent rejection and root review of the +minimal O_TRUNC/provenance corrections. The corrected full ordinary make test +passes 785 tests, four deselected, under synthetic ambient-token/HOME/Python/ +pytest sentinels. Source, pins, catalog and four WP44 task blocks are unchanged. +The known ambient-lookup accident path has a supported isolated test entry; +this is not an OS sandbox or actual assignment isolation, and original adverse +unknown outcomes remain retained. Evidence: dated warden-safe-unit-integration-r1 +and warden-safe-unit-runner-review-r1 packets. Coordinator task statuses and all +sixteen native acceptance gates remain unchanged.