From b53eed40736d06d6db11f29236c03bf0b8ce75ca Mon Sep 17 00:00:00 2001 From: codex Date: Sun, 1 Nov 2026 22:46:55 +0100 Subject: [PATCH] docs: preserve reviewed NetKingdom session artifacts --- .../validation.json | 27 + .../approval-trusted-source-review/README.md | 57 ++ .../actual-proof/original-approval-claim.json | 40 ++ .../actual-proof/receipt.json | 27 + .../actual-proof/request.json | 144 ++++ .../actual-proof/response.json | 232 ++++++ .../actual-proof/signature-verification.json | 1 + .../actual-proof/source-record.json | 232 ++++++ .../actual-proof/synthetic-public-trust.json | 5 + ...tual-python-source-owner-negative-proof.py | 109 +++ .../owner-review-record.patch | 71 ++ .../owner-review.json | 33 + .../retained-fixture-failure.json | 12 + .../source-closure-after-review.patch | 43 ++ .../source-closure-manifest.json | 33 + .../source-closure-owner-baseline.md | 87 +++ .../source-closure-preview.md | 138 ++++ .../validation.json | 17 + .../admission-packet-r1.md | 134 ++++ .../admission-packet-r2.md | 183 +++++ .../implementation-r1.patch | 257 +++++++ .../implementation-r2.patch | 316 +++++++++ .../audit-native-admission/normal-sync.json | 17 + .../audit-native-admission/validation-r1.json | 11 + .../audit-native-admission/validation-r2.json | 14 + .../implementation.patch | 265 +++++++ .../browser-adapter-next/README.md | 52 ++ .../finalize_receipt_r3.py | 32 + .../browser-adapter-next/implementation.patch | 669 ++++++++++++++++++ .../browser-adapter-next/integrate_r3.py | 28 + .../integrated-source-hashes.json | 6 + .../normal-final.exit.json | 1 + .../normal-final.stderr.txt | 1 + .../normal-final.stdout.json | 321 +++++++++ .../normal-sync.exit.json | 1 + .../normal-sync.stderr.txt | 1 + .../normal-sync.stdout.json | 427 +++++++++++ .../normal-writeback.exit.json | 1 + .../normal-writeback.stderr.txt | 1 + .../normal-writeback.stdout.json | 354 +++++++++ .../browser-adapter-next/owner-progress.json | 14 + .../keycape/keycape-registration-adapter.py | 180 +++++ .../k8s/keycape/registration-adapter.md | 59 ++ 43 files changed, 4653 insertions(+) create mode 100644 docs/changes/netkingdom-target-2026-10-16/approval-timestamp-precision/validation.json create mode 100644 docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/README.md create mode 100644 docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/actual-proof/original-approval-claim.json create mode 100644 docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/actual-proof/receipt.json create mode 100644 docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/actual-proof/request.json create mode 100644 docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/actual-proof/response.json create mode 100644 docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/actual-proof/signature-verification.json create mode 100644 docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/actual-proof/source-record.json create mode 100644 docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/actual-proof/synthetic-public-trust.json create mode 100644 docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/actual-python-source-owner-negative-proof.py create mode 100644 docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/owner-review-record.patch create mode 100644 docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/owner-review.json create mode 100644 docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/retained-fixture-failure.json create mode 100644 docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/source-closure-after-review.patch create mode 100644 docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/source-closure-manifest.json create mode 100644 docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/source-closure-owner-baseline.md create mode 100644 docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/source-closure-preview.md create mode 100644 docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/validation.json create mode 100644 docs/changes/netkingdom-target-2026-10-16/audit-native-admission/admission-packet-r1.md create mode 100644 docs/changes/netkingdom-target-2026-10-16/audit-native-admission/admission-packet-r2.md create mode 100644 docs/changes/netkingdom-target-2026-10-16/audit-native-admission/implementation-r1.patch create mode 100644 docs/changes/netkingdom-target-2026-10-16/audit-native-admission/implementation-r2.patch create mode 100644 docs/changes/netkingdom-target-2026-10-16/audit-native-admission/normal-sync.json create mode 100644 docs/changes/netkingdom-target-2026-10-16/audit-native-admission/validation-r1.json create mode 100644 docs/changes/netkingdom-target-2026-10-16/audit-native-admission/validation-r2.json create mode 100644 docs/changes/netkingdom-target-2026-10-16/browser-acceptance-next/implementation.patch create mode 100644 docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/README.md create mode 100644 docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/finalize_receipt_r3.py create mode 100644 docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/implementation.patch create mode 100644 docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/integrate_r3.py create mode 100644 docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/integrated-source-hashes.json create mode 100644 docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/normal-final.exit.json create mode 100644 docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/normal-final.stderr.txt create mode 100644 docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/normal-final.stdout.json create mode 100644 docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/normal-sync.exit.json create mode 100644 docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/normal-sync.stderr.txt create mode 100644 docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/normal-sync.stdout.json create mode 100644 docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/normal-writeback.exit.json create mode 100644 docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/normal-writeback.stderr.txt create mode 100644 docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/normal-writeback.stdout.json create mode 100644 docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/owner-progress.json create mode 100644 docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/postimages/sso-mfa/k8s/keycape/keycape-registration-adapter.py create mode 100644 docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/postimages/sso-mfa/k8s/keycape/registration-adapter.md diff --git a/docs/changes/netkingdom-target-2026-10-16/approval-timestamp-precision/validation.json b/docs/changes/netkingdom-target-2026-10-16/approval-timestamp-precision/validation.json new file mode 100644 index 00000000..29f063b5 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-16/approval-timestamp-precision/validation.json @@ -0,0 +1,27 @@ +{ + "baseline_regressions": { + "failed": 5, + "passed": 2 + }, + "focused": { + "passed": 89, + "command": "python3 -m pytest -q tests/test_timestamp_precision.py tests/test_consume_provenance.py tests/test_execution_confirmation.py tests/test_cas.py tests/test_outbox.py tests/test_reconciliation.py" + }, + "full": { + "passed": 298, + "failed": 1, + "failure": "tests/test_deploy_manifest.py::test_pinned_image_schema_drift_is_acknowledged", + "preexisting": "same 16e5d manifest vs 251941 historical release evidence mismatch previously reproduced unchanged" + }, + "retained_refinement": "initial expiry regression issued already-expired object after repaired clock; corrected fixture issues before expiry and then advances; original failure not scored as code regression", + "patch_check_exit": 0, + "native_accepted": false, + "access_r3_review": { + "full_patch_sha256": "46bfeb706d3a88bf6054309cc9303eb360a91e57dda3b0fcbb7b80d551a75db6", + "source_hashes_matched": 31, + "independent_command": "go test -race -count=1 ./internal/decision ./cmd/access-engine -run TestTrusted", + "result": "passed", + "source_normalized_preapproval_proof": "../access-native-source/b2-b3/r3/actual-retained-python-source/receipt.json", + "independent_actual_immediate_cas_full_chain": "pending Secrets/Platform remeasurement; not claimed" + } +} diff --git a/docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/README.md b/docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/README.md new file mode 100644 index 00000000..a4699e87 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/README.md @@ -0,0 +1,57 @@ +# Approval owner review of Access trusted source join + +Disposition: **approved for Approval's source-fact contract only**, exact Access +patch `6079ed2ff40b131aa2c5efc8d33608ecb3475c8158f0486b44ec4469e9f222c1`. +All 30 frozen source file hashes match; vendored Approval helper files match +their owner pins. Access policy, other-owner behavior, trust/custody admission, +deployment and native effect are outside this review approval. + +`owner-review.json` records the INTENT basis and live native gates. +`owner-review-record.patch` adds the Approval-owned T04 work record and formal +review after the helper package. It applies to that owner source without +canonical mutation; registrar-generated identifiers must be retained during +coordinator integration. The review ran from an isolated Approval checkout with +its instructions/intent loaded and work item created before the review. + +Then apply `source-closure-after-review.patch` to close scoped WP0005 source +delivery. Sequential application was checked against the current canonical +workplan and preserves all four registrar-written UUIDs. It cites the exact +published helper commit and normal 7/7 reconciliation, including the receipt's +dirty-workplan/null revision and generated-only commit refusal. This closes no +native gate. `source-closure-manifest.json` records the exact evidence and hashes. + +The source consumes the existing fixed GET claim endpoint with Bearer read +authority. Actual App enforces exact tenant before lookup. The exact shared +helper validates original and fetched claims separately at a post-I/O instant, +with stable act/PDP/declarations/validity correspondence and no refreshed request. +Source provenance stays outside the immutable submitted context and binds fetched +bytes, source/configuration and deadlines. Signing/deadline clamps do not turn +an observation into permission to execute. + +Independent disposable actual Python App/TLS → actual Access serve proof: + +- Service creates an approval, configured synthetic human binds it, and the + existing read route supplies a valid human-controlled PDP claim. +- Successful Check leaves Approval approved and emits no `use` event. +- Changing native target `1.0` to `1` keeps the legacy PDP digest equal but + refuses the native correspondence; no new decision record is written. +- A stale original observation cannot be rescued by a fresh valid provider. +- A read-scoped identity in a foreign store tenant is refused. +- Actual source revocation is fetched and refused despite the carried claim. +- Existing producer DecodeEnvelope/Verify validates the returned signature and + canonical equality with the recorded envelope; no new verifier algorithm. + +The actual proof's source, binary and result hashes are pinned in `manifest.json` +and `actual-proof/source-dependencies.json`. Go race taskexecution/HTTP command +tests passed. An initial test-fixture0440 replacement permission failure is +retained; corrected test-only rotation passed without any reviewed source fix. +The local signature verifier's shared Go cache sandbox refusal is also retained +in validation evidence; the authorized retry passed. + +This is synthetic source validation. StaticTokenAuthenticator and a disposable CA +prove mechanism, not KeyCape issuer admission or actual native source identity. +Installed source labels/paths are configuration, not proof of owner custody. +Exact native read client/profile/tenant/CA/server/image/config pins and actual +served PEP/CAS/journal/effect/cleanup remain existing APPROVAL-WP-0002-T01/T04/T05 +and Secrets/Platform obligations. Same-digest historical consume evidence cannot +renew an expired original request or authorize replay after an unknown effect. diff --git a/docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/actual-proof/original-approval-claim.json b/docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/actual-proof/original-approval-claim.json new file mode 100644 index 00000000..4bafbc6d --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/actual-proof/original-approval-claim.json @@ -0,0 +1,40 @@ +{ + "approval_id": "aaaaaaaa-bbbb-4ccc-8ddd-eeeeeeeeeeee", + "binding": { + "action": "execute-ccr", + "actor": "secrets-engine", + "digest": "sha256:7639448d8f6c32fb677047a346206c521f3cb765c63b98bfac6043bf5eb0762b", + "human_control": true, + "pdp_digest": "sha256:7b91ba0487409fdbad42764fb95d10432bdb151a8a716ab1e947a3f861cad680", + "pdp_path": true, + "principal": "secrets-engine", + "purpose": "actual Python App source-only synthetic proof", + "target": { + "attributes": { + "numeric_kind_fixture": 1.0, + "stage": "prod" + }, + "id": "catalog:synthetic", + "system": "secrets-engine", + "tenant": "tenant:platform", + "type": "secret-catalog-lane" + } + }, + "consumed": false, + "freshness": { + "not_after": "2026-10-16T14:17:31+00:00", + "observed_at": "2026-10-16T14:16:31+00:00", + "ttl_seconds": 60 + }, + "issuer": "approval-engine", + "kind": "approval-claim", + "reason_code": "ok", + "schema_version": "0.1", + "state": "valid", + "valid_now": true, + "validity": { + "expires_at": "2026-10-16T14:19:30.803007+00:00", + "not_before": "2026-10-16T14:15:30.803007+00:00" + }, + "yields_to": "net-kingdom taxonomy request-claim schema (statute \u00a717; unassigned)" +} diff --git a/docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/actual-proof/receipt.json b/docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/actual-proof/receipt.json new file mode 100644 index 00000000..8b537fd3 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/actual-proof/receipt.json @@ -0,0 +1,27 @@ +{ + "scope": "disposable synthetic only", + "actual_source": "Approval App over TLS with authenticated create, configured synthetic human entry, read; actual SQLite engine", + "actual_access": "production serve binary from frozen B2/B3 R1", + "allow": true, + "original_context_unchanged": true, + "published_digest_match": true, + "returned_source_record_match": true, + "actual_source_revoke_refused": true, + "native_float_to_integer_drift_refused_despite_equal_pdp_digest": true, + "check_did_not_consume_approval": true, + "fresh_source_cannot_rescue_stale_original": true, + "foreign_store_tenant_read_refused": true, + "added_negative_http_statuses": { + "native_numeric_kind_drift": 500, + "stale_original": 500, + "foreign_store_tenant": 500 + }, + "no_record_after_refusal": true, + "native_admission": false, + "files_sha256": { + "source-record.json": "sha256:b4174adf927e4a9d1350d3c0f74657e6abd664ad4ffbd5066376e1d0b80da6e4", + "response.json": "sha256:b4174adf927e4a9d1350d3c0f74657e6abd664ad4ffbd5066376e1d0b80da6e4", + "original-approval-claim.json": "sha256:bc6ef42913c7e0ac55de722acf7643f8451f4f7ab484734a54de5bec310c15b6", + "request.json": "sha256:5da56e054c699627fd55b38156c6c91a50f9febc966fb9519207e9240bea5706" + } +} diff --git a/docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/actual-proof/request.json b/docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/actual-proof/request.json new file mode 100644 index 00000000..2dd3c4e9 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/actual-proof/request.json @@ -0,0 +1,144 @@ +{ + "tenant": "tenant:platform", + "subject": { + "id": "secrets-engine", + "type": "service", + "tenant": "tenant:platform" + }, + "action": "execute-ccr", + "resource": { + "id": "catalog:synthetic", + "type": "secret-catalog-lane", + "system": "secrets-engine", + "attributes": { + "stage": "prod", + "numeric_kind_fixture": 1.0 + }, + "tenant": "tenant:platform" + }, + "context": { + "execution_contract": { + "backend_budget": { + "ttl_seconds": 120, + "uses": 12 + }, + "backend_origin": "http://127.0.0.1:18200", + "catalog_sha256": "sha256:290e55b2258d66980f9443ba7d1436adfc77b8814bd161004d5af2ba272bc9cc", + "ccr_id": "CCR-2099-0001", + "ccr_immutable_sha256": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "cluster_uid": "synthetic-not-native", + "contract_version": "SECRETS-EXEC-20261011-v1", + "execution_actor": "service:secrets-engine", + "execution_grant_ccr_id": "CCR-2099-0002", + "expected_after": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "expected_before": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "grant_immutable_sha256": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "operation": "metadata-apply-ccr", + "operation_deadline": "2026-10-16T14:18:11.115366+00:00", + "operation_id": "cbfbced0-09d5-48c8-a1e7-a2ea4dc69153", + "operations": [ + { + "method": "GET", + "path": "sys/policies/acl/synthetic", + "payload_sha256": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + { + "method": "PUT", + "path": "sys/policies/acl/synthetic", + "payload_sha256": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + { + "method": "GET", + "path": "sys/policies/acl/synthetic", + "payload_sha256": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + { + "method": "GET", + "path": "auth/synthetic/role/synthetic", + "payload_sha256": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + { + "method": "PUT", + "path": "auth/synthetic/role/synthetic", + "payload_sha256": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + { + "method": "GET", + "path": "auth/synthetic/role/synthetic", + "payload_sha256": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ], + "source_bundle": { + "engine_dispatcher": { + "path": "/synthetic/engine_dispatcher.py", + "sha256": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "platform_mediator": { + "path": "/synthetic/platform_mediator.py", + "sha256": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "policy_auth": { + "path": "/synthetic/policy_auth.py", + "sha256": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "typed_ccr": { + "path": "/synthetic/typed_ccr.py", + "sha256": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + } + }, + "catalog_target": { + "lane": "synthetic", + "stage": "prod" + }, + "human_control": true, + "service_actor_binding": { + "version": "proposed-service-task-caller/v1", + "transport_principal": "system:serviceaccount:secrets-engine:secrets-engine", + "subject_id": "secrets-engine", + "subject_type": "service", + "tenant": "tenant:platform" + }, + "purpose": "actual Python App source-only synthetic proof", + "approval": { + "approval_id": "aaaaaaaa-bbbb-4ccc-8ddd-eeeeeeeeeeee", + "binding": { + "action": "execute-ccr", + "actor": "secrets-engine", + "digest": "sha256:7639448d8f6c32fb677047a346206c521f3cb765c63b98bfac6043bf5eb0762b", + "human_control": true, + "pdp_digest": "sha256:7b91ba0487409fdbad42764fb95d10432bdb151a8a716ab1e947a3f861cad680", + "pdp_path": true, + "principal": "secrets-engine", + "purpose": "actual Python App source-only synthetic proof", + "target": { + "attributes": { + "numeric_kind_fixture": 1.0, + "stage": "prod" + }, + "id": "catalog:synthetic", + "system": "secrets-engine", + "tenant": "tenant:platform", + "type": "secret-catalog-lane" + } + }, + "consumed": false, + "freshness": { + "not_after": "2026-10-16T14:17:31+00:00", + "observed_at": "2026-10-16T14:16:31+00:00", + "ttl_seconds": 60 + }, + "issuer": "approval-engine", + "kind": "approval-claim", + "reason_code": "ok", + "schema_version": "0.1", + "state": "valid", + "valid_now": true, + "validity": { + "expires_at": "2026-10-16T14:19:30.803007+00:00", + "not_before": "2026-10-16T14:15:30.803007+00:00" + }, + "yields_to": "net-kingdom taxonomy request-claim schema (statute \u00a717; unassigned)" + } + } +} diff --git a/docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/actual-proof/response.json b/docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/actual-proof/response.json new file mode 100644 index 00000000..8c34d659 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/actual-proof/response.json @@ -0,0 +1,232 @@ +{ + "id": "decision:cd3bf7bad0422143", + "contract_version": "flex-auth.decision-record.v1", + "effect": "allow", + "reason": "exact_task_execution_matched", + "matched_policy_version": "v2", + "matched_rule": "exact_task_execution_matched", + "resource": { + "id": "catalog:synthetic", + "type": "secret-catalog-lane", + "system": "secrets-engine", + "tenant": "tenant:platform", + "attributes": { + "numeric_kind_fixture": 1.0, + "stage": "prod" + } + }, + "subject": { + "id": "secrets-engine", + "type": "service", + "tenant": "tenant:platform" + }, + "binding": { + "tenant": "tenant:platform", + "subject": { + "id": "secrets-engine", + "type": "service", + "tenant": "tenant:platform" + }, + "action": "execute-ccr", + "resource": { + "id": "catalog:synthetic", + "type": "secret-catalog-lane", + "system": "secrets-engine", + "tenant": "tenant:platform", + "attributes": { + "numeric_kind_fixture": 1.0, + "stage": "prod" + } + }, + "context": { + "approval": { + "approval_id": "aaaaaaaa-bbbb-4ccc-8ddd-eeeeeeeeeeee", + "binding": { + "action": "execute-ccr", + "actor": "secrets-engine", + "digest": "sha256:7639448d8f6c32fb677047a346206c521f3cb765c63b98bfac6043bf5eb0762b", + "human_control": true, + "pdp_digest": "sha256:7b91ba0487409fdbad42764fb95d10432bdb151a8a716ab1e947a3f861cad680", + "pdp_path": true, + "principal": "secrets-engine", + "purpose": "actual Python App source-only synthetic proof", + "target": { + "attributes": { + "numeric_kind_fixture": 1.0, + "stage": "prod" + }, + "id": "catalog:synthetic", + "system": "secrets-engine", + "tenant": "tenant:platform", + "type": "secret-catalog-lane" + } + }, + "consumed": false, + "freshness": { + "not_after": "2026-10-16T14:17:31+00:00", + "observed_at": "2026-10-16T14:16:31+00:00", + "ttl_seconds": 60 + }, + "issuer": "approval-engine", + "kind": "approval-claim", + "reason_code": "ok", + "schema_version": "0.1", + "state": "valid", + "valid_now": true, + "validity": { + "expires_at": "2026-10-16T14:19:30.803007+00:00", + "not_before": "2026-10-16T14:15:30.803007+00:00" + }, + "yields_to": "net-kingdom taxonomy request-claim schema (statute \u00a717; unassigned)" + }, + "catalog_target": { + "lane": "synthetic", + "stage": "prod" + }, + "execution_contract": { + "backend_budget": { + "ttl_seconds": 120, + "uses": 12 + }, + "backend_origin": "http://127.0.0.1:18200", + "catalog_sha256": "sha256:290e55b2258d66980f9443ba7d1436adfc77b8814bd161004d5af2ba272bc9cc", + "ccr_id": "CCR-2099-0001", + "ccr_immutable_sha256": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "cluster_uid": "synthetic-not-native", + "contract_version": "SECRETS-EXEC-20261011-v1", + "execution_actor": "service:secrets-engine", + "execution_grant_ccr_id": "CCR-2099-0002", + "expected_after": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "expected_before": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "grant_immutable_sha256": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "operation": "metadata-apply-ccr", + "operation_deadline": "2026-10-16T14:18:11.115366+00:00", + "operation_id": "cbfbced0-09d5-48c8-a1e7-a2ea4dc69153", + "operations": [ + { + "method": "GET", + "path": "sys/policies/acl/synthetic", + "payload_sha256": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + { + "method": "PUT", + "path": "sys/policies/acl/synthetic", + "payload_sha256": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + { + "method": "GET", + "path": "sys/policies/acl/synthetic", + "payload_sha256": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + { + "method": "GET", + "path": "auth/synthetic/role/synthetic", + "payload_sha256": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + { + "method": "PUT", + "path": "auth/synthetic/role/synthetic", + "payload_sha256": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + { + "method": "GET", + "path": "auth/synthetic/role/synthetic", + "payload_sha256": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ], + "source_bundle": { + "engine_dispatcher": { + "path": "/synthetic/engine_dispatcher.py", + "sha256": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "platform_mediator": { + "path": "/synthetic/platform_mediator.py", + "sha256": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "policy_auth": { + "path": "/synthetic/policy_auth.py", + "sha256": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "typed_ccr": { + "path": "/synthetic/typed_ccr.py", + "sha256": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + } + }, + "human_control": true, + "purpose": "actual Python App source-only synthetic proof", + "service_actor_binding": { + "subject_id": "secrets-engine", + "subject_type": "service", + "tenant": "tenant:platform", + "transport_principal": "system:serviceaccount:secrets-engine:secrets-engine", + "version": "proposed-service-task-caller/v1" + } + }, + "request_digest": "sha256:5a6a2d1f4b2a6a791bf1da63cb0c9dfc107d6a00d5be80570be418a7afbdf469", + "approval_binding_digest": "sha256:7b91ba0487409fdbad42764fb95d10432bdb151a8a716ab1e947a3f861cad680", + "submitted_request_digest": "sha256:5a6a2d1f4b2a6a791bf1da63cb0c9dfc107d6a00d5be80570be418a7afbdf469" + }, + "lifetime": { + "kind": "ttl", + "ttl": "15s", + "not_before": "2026-10-16T14:16:31Z", + "expires_at": "2026-10-16T14:16:46Z" + }, + "diagnostics": { + "action": "execute-ccr", + "matched_relationship": "", + "policy_package": "secrets-engine.task-execution.service-pilot", + "policy_status": "proposed", + "registry_overrode": [], + "registry_resource": false, + "registry_subject": false + }, + "provenance": { + "evaluator": "flex-auth/local", + "mode": "standalone", + "policy_package": "secrets-engine.task-execution.service-pilot", + "policy_version": "v2", + "policy_package_digest": "sha256:fc5a8769886b72006ba298bc0799741d0f1ed327dde9e36a6c35d26be4ea0bdc", + "registry_snapshot_digest": "sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a", + "input_claim_digests": { + "context": "sha256:3d24a8e36f181f5843138bdc7388cea7673e34405662fadb2d2c02e6b976e41f" + }, + "decision_time": "2026-10-16T14:16:31Z", + "approval_observation": { + "source_id": "synthetic-actual-approval-app", + "source_revision": "actual-owner-python-source-fixture", + "configuration_digest": "sha256:1dbfcebaa4267654828237f382d5b9070eaa91bdab43649923c8493629943dcf", + "claim_digest": "sha256:0b48130564c61d2ef2753e37c66763adb9c311b7e50d14ae9387f3c7ed9bb376", + "native_binding_digest": "sha256:7639448d8f6c32fb677047a346206c521f3cb765c63b98bfac6043bf5eb0762b", + "observed_at": "2026-10-16T14:16:31Z", + "not_after": "2026-10-16T14:17:31Z", + "validated_at": "2026-10-16T14:16:31.234881787Z" + }, + "caller": { + "mode": "enforce", + "principal": "system:serviceaccount:secrets-engine:secrets-engine", + "audience": "flex-auth", + "not_after": "2026-10-16T14:18:30Z" + } + }, + "signature": { + "mode": "signed", + "alg": "ed25519", + "kid": "synthetic-v2", + "value": "98SuexFSF4rnN0GHrZLQym-Jd5hI-Zq5UZuHcNXPKMw7qTqoI5DphubdYpITsvvhhpdE8BOR8PicMF50cx6TCg" + }, + "caring": { + "profile": "caring-0.4.0-rc2", + "conformance_findings": [ + { + "code": "CARING-DESCRIPTOR-MISSING", + "severity": "warning", + "message": "no CARING descriptor matched the request", + "fields": [ + "caring_context" + ] + } + ] + } +} diff --git a/docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/actual-proof/signature-verification.json b/docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/actual-proof/signature-verification.json new file mode 100644 index 00000000..750e14fc --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/actual-proof/signature-verification.json @@ -0,0 +1 @@ +{"existing_producer_signature_verified": true, "independent_synthetic_public_fixture": true, "returned_record_canonical_bytes_match": true, "new_signature_implementation": false} diff --git a/docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/actual-proof/source-record.json b/docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/actual-proof/source-record.json new file mode 100644 index 00000000..8c34d659 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/actual-proof/source-record.json @@ -0,0 +1,232 @@ +{ + "id": "decision:cd3bf7bad0422143", + "contract_version": "flex-auth.decision-record.v1", + "effect": "allow", + "reason": "exact_task_execution_matched", + "matched_policy_version": "v2", + "matched_rule": "exact_task_execution_matched", + "resource": { + "id": "catalog:synthetic", + "type": "secret-catalog-lane", + "system": "secrets-engine", + "tenant": "tenant:platform", + "attributes": { + "numeric_kind_fixture": 1.0, + "stage": "prod" + } + }, + "subject": { + "id": "secrets-engine", + "type": "service", + "tenant": "tenant:platform" + }, + "binding": { + "tenant": "tenant:platform", + "subject": { + "id": "secrets-engine", + "type": "service", + "tenant": "tenant:platform" + }, + "action": "execute-ccr", + "resource": { + "id": "catalog:synthetic", + "type": "secret-catalog-lane", + "system": "secrets-engine", + "tenant": "tenant:platform", + "attributes": { + "numeric_kind_fixture": 1.0, + "stage": "prod" + } + }, + "context": { + "approval": { + "approval_id": "aaaaaaaa-bbbb-4ccc-8ddd-eeeeeeeeeeee", + "binding": { + "action": "execute-ccr", + "actor": "secrets-engine", + "digest": "sha256:7639448d8f6c32fb677047a346206c521f3cb765c63b98bfac6043bf5eb0762b", + "human_control": true, + "pdp_digest": "sha256:7b91ba0487409fdbad42764fb95d10432bdb151a8a716ab1e947a3f861cad680", + "pdp_path": true, + "principal": "secrets-engine", + "purpose": "actual Python App source-only synthetic proof", + "target": { + "attributes": { + "numeric_kind_fixture": 1.0, + "stage": "prod" + }, + "id": "catalog:synthetic", + "system": "secrets-engine", + "tenant": "tenant:platform", + "type": "secret-catalog-lane" + } + }, + "consumed": false, + "freshness": { + "not_after": "2026-10-16T14:17:31+00:00", + "observed_at": "2026-10-16T14:16:31+00:00", + "ttl_seconds": 60 + }, + "issuer": "approval-engine", + "kind": "approval-claim", + "reason_code": "ok", + "schema_version": "0.1", + "state": "valid", + "valid_now": true, + "validity": { + "expires_at": "2026-10-16T14:19:30.803007+00:00", + "not_before": "2026-10-16T14:15:30.803007+00:00" + }, + "yields_to": "net-kingdom taxonomy request-claim schema (statute \u00a717; unassigned)" + }, + "catalog_target": { + "lane": "synthetic", + "stage": "prod" + }, + "execution_contract": { + "backend_budget": { + "ttl_seconds": 120, + "uses": 12 + }, + "backend_origin": "http://127.0.0.1:18200", + "catalog_sha256": "sha256:290e55b2258d66980f9443ba7d1436adfc77b8814bd161004d5af2ba272bc9cc", + "ccr_id": "CCR-2099-0001", + "ccr_immutable_sha256": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "cluster_uid": "synthetic-not-native", + "contract_version": "SECRETS-EXEC-20261011-v1", + "execution_actor": "service:secrets-engine", + "execution_grant_ccr_id": "CCR-2099-0002", + "expected_after": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "expected_before": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "grant_immutable_sha256": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "operation": "metadata-apply-ccr", + "operation_deadline": "2026-10-16T14:18:11.115366+00:00", + "operation_id": "cbfbced0-09d5-48c8-a1e7-a2ea4dc69153", + "operations": [ + { + "method": "GET", + "path": "sys/policies/acl/synthetic", + "payload_sha256": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + { + "method": "PUT", + "path": "sys/policies/acl/synthetic", + "payload_sha256": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + { + "method": "GET", + "path": "sys/policies/acl/synthetic", + "payload_sha256": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + { + "method": "GET", + "path": "auth/synthetic/role/synthetic", + "payload_sha256": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + { + "method": "PUT", + "path": "auth/synthetic/role/synthetic", + "payload_sha256": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + { + "method": "GET", + "path": "auth/synthetic/role/synthetic", + "payload_sha256": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ], + "source_bundle": { + "engine_dispatcher": { + "path": "/synthetic/engine_dispatcher.py", + "sha256": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "platform_mediator": { + "path": "/synthetic/platform_mediator.py", + "sha256": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "policy_auth": { + "path": "/synthetic/policy_auth.py", + "sha256": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "typed_ccr": { + "path": "/synthetic/typed_ccr.py", + "sha256": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + } + }, + "human_control": true, + "purpose": "actual Python App source-only synthetic proof", + "service_actor_binding": { + "subject_id": "secrets-engine", + "subject_type": "service", + "tenant": "tenant:platform", + "transport_principal": "system:serviceaccount:secrets-engine:secrets-engine", + "version": "proposed-service-task-caller/v1" + } + }, + "request_digest": "sha256:5a6a2d1f4b2a6a791bf1da63cb0c9dfc107d6a00d5be80570be418a7afbdf469", + "approval_binding_digest": "sha256:7b91ba0487409fdbad42764fb95d10432bdb151a8a716ab1e947a3f861cad680", + "submitted_request_digest": "sha256:5a6a2d1f4b2a6a791bf1da63cb0c9dfc107d6a00d5be80570be418a7afbdf469" + }, + "lifetime": { + "kind": "ttl", + "ttl": "15s", + "not_before": "2026-10-16T14:16:31Z", + "expires_at": "2026-10-16T14:16:46Z" + }, + "diagnostics": { + "action": "execute-ccr", + "matched_relationship": "", + "policy_package": "secrets-engine.task-execution.service-pilot", + "policy_status": "proposed", + "registry_overrode": [], + "registry_resource": false, + "registry_subject": false + }, + "provenance": { + "evaluator": "flex-auth/local", + "mode": "standalone", + "policy_package": "secrets-engine.task-execution.service-pilot", + "policy_version": "v2", + "policy_package_digest": "sha256:fc5a8769886b72006ba298bc0799741d0f1ed327dde9e36a6c35d26be4ea0bdc", + "registry_snapshot_digest": "sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a", + "input_claim_digests": { + "context": "sha256:3d24a8e36f181f5843138bdc7388cea7673e34405662fadb2d2c02e6b976e41f" + }, + "decision_time": "2026-10-16T14:16:31Z", + "approval_observation": { + "source_id": "synthetic-actual-approval-app", + "source_revision": "actual-owner-python-source-fixture", + "configuration_digest": "sha256:1dbfcebaa4267654828237f382d5b9070eaa91bdab43649923c8493629943dcf", + "claim_digest": "sha256:0b48130564c61d2ef2753e37c66763adb9c311b7e50d14ae9387f3c7ed9bb376", + "native_binding_digest": "sha256:7639448d8f6c32fb677047a346206c521f3cb765c63b98bfac6043bf5eb0762b", + "observed_at": "2026-10-16T14:16:31Z", + "not_after": "2026-10-16T14:17:31Z", + "validated_at": "2026-10-16T14:16:31.234881787Z" + }, + "caller": { + "mode": "enforce", + "principal": "system:serviceaccount:secrets-engine:secrets-engine", + "audience": "flex-auth", + "not_after": "2026-10-16T14:18:30Z" + } + }, + "signature": { + "mode": "signed", + "alg": "ed25519", + "kid": "synthetic-v2", + "value": "98SuexFSF4rnN0GHrZLQym-Jd5hI-Zq5UZuHcNXPKMw7qTqoI5DphubdYpITsvvhhpdE8BOR8PicMF50cx6TCg" + }, + "caring": { + "profile": "caring-0.4.0-rc2", + "conformance_findings": [ + { + "code": "CARING-DESCRIPTOR-MISSING", + "severity": "warning", + "message": "no CARING descriptor matched the request", + "fields": [ + "caring_context" + ] + } + ] + } +} diff --git a/docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/actual-proof/synthetic-public-trust.json b/docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/actual-proof/synthetic-public-trust.json new file mode 100644 index 00000000..234e3d7f --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/actual-proof/synthetic-public-trust.json @@ -0,0 +1,5 @@ +{ + "scope": "synthetic disposable fixture only; no native key admission", + "kid": "synthetic-v2", + "public_key": "Zs1gi5KLiOUODv6qM_rxxDzv4HKUsLh-n-CrpqPPdjM" +} diff --git a/docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/actual-python-source-owner-negative-proof.py b/docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/actual-python-source-owner-negative-proof.py new file mode 100644 index 00000000..e90b0b9b --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/actual-python-source-owner-negative-proof.py @@ -0,0 +1,109 @@ +"""Disposable synthetic proof: actual Approval App HTTPS -> actual Access serve. +No native config, credential environment, approval or backend effect is used. +""" +import base64, hashlib, http.server, io, json, os, pathlib, socket, ssl, subprocess, sys, tempfile, threading, time +from datetime import datetime, timedelta, timezone +from dataclasses import replace +from wsgiref.simple_server import make_server, WSGIRequestHandler +from cryptography import x509 +from cryptography.x509.oid import NameOID +from cryptography.hazmat.primitives import hashes, serialization +from cryptography.hazmat.primitives.asymmetric import rsa +sys.path.insert(0, '/home/worsch/approval-engine') +sys.path.insert(0, '/home/worsch/secrets-engine/src') +from approval_engine.binding import binding_digest +from approval_engine.api import App +from approval_engine.auth import Identity, StaticTokenAuthenticator +from approval_engine.store import Engine +from secrets_engine.authorization import request_digest, approval_binding_digest +from urllib.request import Request, urlopen +from urllib.error import HTTPError + +OUT=pathlib.Path(sys.argv[1]);OUT.mkdir(exist_ok=True) +def digest(raw):return 'sha256:'+hashlib.sha256(raw).hexdigest() +def write(path,raw,mode=0o600): + if path.exists():path.chmod(0o600) + path.write_bytes(raw);path.chmod(mode) +def identity(subject,kind,scopes):return Identity(subject=subject,issuer='synthetic-test-only',audiences=('approval-engine',),principal_type=kind,tenant='tenant:platform',roles=frozenset(),scopes=frozenset(scopes),assurance={'level':'synthetic'},evidence_ref='synthetic-test-only',tenant_source='directory') +class Quiet(WSGIRequestHandler): + def log_message(self,*args):pass +class Review(http.server.BaseHTTPRequestHandler): + def do_POST(self): + raw=self.rfile.read(int(self.headers['Content-Length']));body=json.loads(raw) + assert self.path=='/apis/authentication.k8s.io/v1/tokenreviews' + assert self.headers['Authorization']=='Bearer synthetic-reviewer' + assert body['spec']['audiences']==['flex-auth','access-engine'] + self.send_response(200);self.send_header('Content-Type','application/json');self.end_headers();self.wfile.write(json.dumps({'status':{'authenticated':True,'audiences':['flex-auth'],'user':{'username':'system:serviceaccount:secrets-engine:secrets-engine'}}}).encode()) + def log_message(self,*args):pass +with tempfile.TemporaryDirectory(prefix='access-actual-python-source-') as folder: + root=pathlib.Path(folder);now=datetime.now(timezone.utc) + key=rsa.generate_private_key(public_exponent=65537,key_size=2048);name=x509.Name([x509.NameAttribute(NameOID.COMMON_NAME,'localhost')]);cert=(x509.CertificateBuilder().subject_name(name).issuer_name(name).public_key(key.public_key()).serial_number(x509.random_serial_number()).not_valid_before(now-timedelta(minutes=1)).not_valid_after(now+timedelta(hours=1)).add_extension(x509.SubjectAlternativeName([x509.DNSName('localhost')]),critical=False).sign(key,hashes.SHA256())) + ca=root/'ca.pem';private=root/'tls-key.pem';write(ca,cert.public_bytes(serialization.Encoding.PEM));write(private,key.private_bytes(serialization.Encoding.PEM,serialization.PrivateFormat.PKCS8,serialization.NoEncryption())) + tls=ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER);tls.load_cert_chain(ca,private) + ctx=ssl.create_default_context(cafile=str(ca)) + def start(server):server.socket=tls.wrap_socket(server.socket,server_side=True);threading.Thread(target=server.serve_forever,daemon=True).start();return 'https://localhost:'+str(server.server_port) + review=http.server.HTTPServer(('127.0.0.1',0),Review);review_origin=start(review) + engine=Engine(root/'approval.sqlite3',freshness_ttl=60) + auth=StaticTokenAuthenticator({'synthetic-creator':identity('secrets-engine','service',{'approval:create'}),'synthetic-human':identity('synthetic-human','human',{'approval:approve'}),'synthetic-reader':identity('synthetic-access-reader','service',{'approval:read'}),'synthetic-foreign-reader':replace(identity('synthetic-foreign-reader','service',{'approval:read'}),tenant='tenant:foreign')}) + app=App(engine,auth) + source=make_server('127.0.0.1',0,app,handler_class=Quiet);origin=start(source) + def source_call(method,path,body,token): + request=Request(origin+path,data=json.dumps(body).encode() if body is not None else None,method=method,headers={'Authorization':'Bearer '+token,'Content-Type':'application/json'}) + with urlopen(request,context=ctx,timeout=3) as response:return response.status,json.load(response) + # Reuse existing owner interoperability contract generator; it has synthetic metadata only. + generated=subprocess.check_output([sys.executable,'/tmp/access-trusted-b2-b3/tools/service_task_execution_fixture.py'],env={'PATH':os.defpath}) + request=json.loads(generated)['request'];request['subject']['tenant']='tenant:platform';request['resource']['tenant']='tenant:platform';request['context']['purpose']='actual Python App source-only synthetic proof';del request['context']['approval'] + request['resource']['attributes']['numeric_kind_fixture']=1.0 + native={'action':request['action'],'actor':'secrets-engine','principal':'secrets-engine','purpose':request['context']['purpose'],'target':request['resource']} + aid='aaaaaaaa-bbbb-4ccc-8ddd-eeeeeeeeeeee' + status,created=source_call('POST','/v1/approvals',{'id':aid,'binding':native,'validity':{'not_before':(now-timedelta(minutes=1)).isoformat(),'expires_at':(now+timedelta(minutes=3)).isoformat()},'required_count':1,'pdp_digest':approval_binding_digest(request),'pdp_path':True,'human_control':True},'synthetic-creator');assert status==201 + status,_=source_call('POST',f'/v1/approvals/{aid}/entries',{},'synthetic-human');assert status==200 + status,claim=source_call('GET',f'/v1/approvals/{aid}/claim',None,'synthetic-reader');assert status==200 and claim['valid_now'] is True and claim['binding']['human_control'] is True + request['context']['approval']=claim + seed=root/'seed';write(seed,base64.urlsafe_b64encode(bytes([19])*32).rstrip(b'='),0o440) + reviewer_token=root/'review-token';write(reviewer_token,b'synthetic-reviewer',0o440) + reader_token=root/'reader-token';write(reader_token,b'synthetic-reader',0o440) + registry=root/'registry.json';write(registry,b'{}') + config=root/'source.json';write(config,json.dumps({'source_id':'synthetic-actual-approval-app','source_revision':'actual-owner-python-source-fixture','origin':origin,'store_tenant':'tenant:platform','ca_file':str(ca),'token_file':str(reader_token)}).encode()) + listener=socket.socket();listener.bind(('127.0.0.1',0));port=listener.getsockname()[1];listener.close() + record=root/'record.jsonl';url=f'http://127.0.0.1:{port}' + command=['/tmp/approval-owner-access-review-bin','serve','--addr',f'127.0.0.1:{port}','--registry',str(registry),'--policy','/tmp/access-trusted-b2-b3/examples/secrets-engine-service-task-execution-v2/policy_package.md','--log',str(record),'--caller-auth-mode','enforce','--caller-audience','flex-auth','--caller-kubernetes-url',review_origin,'--caller-reviewer-token-file',str(reviewer_token),'--caller-ca-file',str(ca),'--caller-binding','secrets-engine=system:serviceaccount:secrets-engine:secrets-engine','--envelope-signing-key-file',str(seed),'--envelope-signing-kid','synthetic-v2','--approval-source-file',str(config)] + child=subprocess.Popen(command,env={},stdout=subprocess.DEVNULL,stderr=subprocess.DEVNULL) + try: + for _ in range(100): + try: + with urlopen(url+'/healthz',timeout=.2):break + except Exception:time.sleep(.02) + jwt='synthetic.'+base64.urlsafe_b64encode(json.dumps({'exp':int((now+timedelta(minutes=2)).timestamp())}).encode()).decode().rstrip('=')+'.synthetic' + def check(value): + req=Request(url+'/v1/check',data=json.dumps(value).encode(),headers={'Authorization':'Bearer '+jwt,'Content-Type':'application/json'}) + try: + with urlopen(req,timeout=5) as response:return response.status,response.read() + except HTTPError as error:return error.code,error.read() + status,raw=check(request);assert status==200,(status,raw) + response=json.loads(raw);assert response['effect']=='allow' and response['signature']['mode']=='signed' + assert response['binding']['context']==request['context'];assert response['binding']['submitted_request_digest']==request_digest(request);assert response['binding']['approval_binding_digest']==approval_binding_digest(request) + assert response['provenance']['approval_observation']['source_id']=='synthetic-actual-approval-app' + saved=record.read_bytes();assert json.loads(saved)==response + assert engine.get(aid).status=='approved' + assert not any(row['class']=='use' for row in engine.undrained()) + # Integer/float drift is invisible to the legacy PDP digest, but native act must still refuse. + numeric=json.loads(json.dumps(request));numeric['resource']['attributes']['numeric_kind_fixture']=1 + assert approval_binding_digest(numeric)==approval_binding_digest(request) + assert binding_digest({**native,'target':numeric['resource']})!=binding_digest(native) + numeric_status,_=check(numeric);assert numeric_status!=200 and record.read_bytes()==saved + # Independent Approval owner negative: valid fresh provider cannot renew a stale original observation. + stale=json.loads(json.dumps(request));stale['context']['approval']['freshness']={'observed_at':(now-timedelta(seconds=120)).isoformat(),'ttl_seconds':60,'not_after':(now-timedelta(seconds=60)).isoformat()} + stale_status,_=check(stale);assert stale_status!=200 and record.read_bytes()==saved + # Correct read scope but wrong store tenant is denied by actual App before object lookup. + write(reader_token,b'synthetic-foreign-reader',0o440) + tenant_status,_=check(request);assert tenant_status!=200 and record.read_bytes()==saved + write(reader_token,b'synthetic-reader',0o440) + # Actual producer state transition is fetched anew and cannot be rescued by carried claim. + engine.revoke(aid) + refused,_=check(request);assert refused!=200 and record.read_bytes()==saved + for name,value in [('request.json',request),('response.json',response),('source-record.json',json.loads(saved)),('original-approval-claim.json',claim)]:write(OUT/name,(json.dumps(value,indent=2)+'\n').encode(),0o644) + receipt={'scope':'disposable synthetic only','actual_source':'Approval App over TLS with authenticated create, configured synthetic human entry, read; actual SQLite engine','actual_access':'production serve binary from frozen B2/B3 R1','allow':True,'original_context_unchanged':True,'published_digest_match':True,'returned_source_record_match':True,'actual_source_revoke_refused':True,'native_float_to_integer_drift_refused_despite_equal_pdp_digest':True,'check_did_not_consume_approval':True,'fresh_source_cannot_rescue_stale_original':True,'foreign_store_tenant_read_refused':True,'added_negative_http_statuses':{'native_numeric_kind_drift':numeric_status,'stale_original':stale_status,'foreign_store_tenant':tenant_status},'no_record_after_refusal':True,'native_admission':False,'files_sha256':{p.name:digest(p.read_bytes()) for p in OUT.iterdir() if p.suffix=='.json'}} + write(OUT/'receipt.json',(json.dumps(receipt,indent=2)+'\n').encode(),0o644);print(json.dumps(receipt,indent=2)) + finally: + child.terminate();child.wait(timeout=5);source.shutdown();review.shutdown();engine.close() diff --git a/docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/owner-review-record.patch b/docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/owner-review-record.patch new file mode 100644 index 00000000..55bf0848 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/owner-review-record.patch @@ -0,0 +1,71 @@ +--- a/workplans/APPROVAL-WP-0005-access-claim-fact-contract.md ++++ b/workplans/APPROVAL-WP-0005-access-claim-fact-contract.md +@@ -81,3 +81,32 @@ + digest substitution, foreign review, runtime enable or live proof is invented. + T02 still owns independent source integration/handoff; existing T01/T05 native + residual obligations and actual Access conformance/integration remain unchanged. ++ ++## Review Access trusted source join against Approval's fact boundary ++ ++```task ++id: APPROVAL-WP-0005-T04 ++status: done ++priority: high ++``` ++ ++Explicit bounded CUST-WP-0080 owner crossreview of Access B2/B3 frozen patch ++6079ed2ff40b131aa2c5efc8d33608ecb3475c8158f0486b44ec4469e9f222c1. ++Approval scope only: authenticated fixed claim endpoint/store isolation, ++original/fresh fact correspondence, exact helper pinning and factual deadline/ ++provenance/consume boundary. Access policy, native trust and other-owner assent ++are excluded. Review and disposable actual App proof use synthetic identities; ++no canonical owner source, live credentials, approval or runtime is mutated. ++ ++Approval-only disposition: approved for the exact source fact boundary, not ++Access policy, deployment, identity/custody admission or protected execution. ++Review entry: docs/reviews/2026-10-16-access-trusted-source-approval-owner-review.json. ++Frozen source/hash and exact helper pins verified. Independent actual Python ++App over TLS to actual Access serve passes, with numeric 1.0-to-1 drift refused ++despite equal legacy PDP digest, stale original refused despite fresh provider, ++foreign tenant read refused and actual source revocation refused. Denials append ++no decision record; successful Check leaves approval unconsumed/no use event. ++Existing producer signature verification and source record match pass. Go race ++taskexecution and actual HTTP command suites pass. Initial synthetic0440 token ++replacement fixture failure was corrected and retained; no reviewed source fix ++was required. Native T01/T04/T05 obligations and T02 integration stay open. +--- /dev/null ++++ b/docs/reviews/2026-10-16-access-trusted-source-approval-owner-review.json +@@ -0,0 +1,33 @@ ++{ ++ "reviewer": "approval-engine-owner (agent approval-engine, codex)", ++ "decision": "approved", ++ "comment": "INTENT Purpose, The Claim Shape and Consumption Ordering: the exact reviewed Access source reads factual Approval claims from the existing authenticated fixed endpoint and validates exact human/PDP act correspondence while preserving the submitted claim/context and original deadlines. Review under APPROVAL-WP-0005-T04 of Access patch SHA256 6079ed2ff40b131aa2c5efc8d33608ecb3475c8158f0486b44ec4469e9f222c1, with all manifest source hashes independently matched. This judgement approves only Approval-owned fact-source/correspondence boundaries; it grants no Access policy assent, native identity/custody admission, human approval, deployment or protected effect.", ++ "exact_reviewed_patch_sha256": "6079ed2ff40b131aa2c5efc8d33608ecb3475c8158f0486b44ec4469e9f222c1", ++ "source_file_hashes_matched": 30, ++ "approval_helper_files_exact": { ++ "canonical.go": "d48588fb26e9037c8e83421586b8a818f4c0a52afb874f3fadf7229fc971c282", ++ "claim.go": "007a2494f79c47c104635402407e9e68f17d8599d23e0f2c8ebf6cb1b47e3621", ++ "go.mod": "bb09c53ffea926fbfba6e580ed3bd71ca358575401717389b3c7c49c0c674b43", ++ "README.md": "5d936b7f3de87bbf4e946f0dc44823d2f69b62b4122b55b9064791b7a19fd4f5" ++ }, ++ "approval_contract_findings": [ ++ "GET /v1/approvals/{UUID}/claim with existing Bearer approval:read scope; exact service store tenant checked by actual App before lookup", ++ "Installed fixed HTTPS origin/customCA, no redirect/proxy and bounded strict JSON; configuration/source labels describe owner admission and cannot establish native admission by themselves", ++ "Both original and fetched facts independently validate at one post-I/O instant; exact nonfreshness authority matches; fetched observation never replaces carried context or renews original freshness", ++ "Native five-field digest independently recomputed; original/fetched digest plus actual approval-free PDP correspondence checked; native1.0/1 drift is refused even where legacy PDP digest conflates it", ++ "Decision provenance binds fetched-byte digest/source/config/native digest/observation/validation/deadline outside unchanged request; signature includes that provenance", ++ "Effective fact deadline is min original/source freshness and approval validity; Access further caps identity/operation/policy deadlines and rechecks before return", ++ "Access Check does not consume; approvals remain PIP state and PEP still owns one CAS before effect with original decision correspondence and separate live one-shot journal" ++ ], ++ "independent_proof": "actual-proof/receipt.json", ++ "independent_signature_proof": "actual-proof/signature-verification.json", ++ "retained_fixture_failure": "retained-fixture-failure.json", ++ "native_gates_still_open": [ ++ "APPROVAL-WP-0002-T01 exact issuer/human-profile, requester/actor mapping and admitted schema/image/migration; synthetic StaticTokenAuthenticator is not native KeyCape evidence", ++ "Actual approval:read service identity/audience/storetenant custody and trustedCA/server/image/config source admission; source labels/config alone prove none of these", ++ "APPROVAL-WP-0002-T04 source/use outbox and actual audit receiver lifecycle/readback/reconciliation acceptance", ++ "APPROVAL-WP-0002-T05 named actual PEP, strict Secrets successor producer-format trust acceptance and Platform original CAS/journal/effect/cleanup proof at final integrated source pins" ++ ], ++ "source_policy_scope": "Access eligibility/policy and other-owner implementation are outside Approval approval", ++ "native_accepted": false ++} diff --git a/docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/owner-review.json b/docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/owner-review.json new file mode 100644 index 00000000..bc858d99 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/owner-review.json @@ -0,0 +1,33 @@ +{ + "reviewer": "approval-engine-owner (agent approval-engine, codex)", + "decision": "approved", + "comment": "INTENT Purpose, The Claim Shape and Consumption Ordering: the exact reviewed Access source reads factual Approval claims from the existing authenticated fixed endpoint and validates exact human/PDP act correspondence while preserving the submitted claim/context and original deadlines. Review under APPROVAL-WP-0005-T04 of Access patch SHA256 6079ed2ff40b131aa2c5efc8d33608ecb3475c8158f0486b44ec4469e9f222c1, with all manifest source hashes independently matched. This judgement approves only Approval-owned fact-source/correspondence boundaries; it grants no Access policy assent, native identity/custody admission, human approval, deployment or protected effect.", + "exact_reviewed_patch_sha256": "6079ed2ff40b131aa2c5efc8d33608ecb3475c8158f0486b44ec4469e9f222c1", + "source_file_hashes_matched": 30, + "approval_helper_files_exact": { + "canonical.go": "d48588fb26e9037c8e83421586b8a818f4c0a52afb874f3fadf7229fc971c282", + "claim.go": "007a2494f79c47c104635402407e9e68f17d8599d23e0f2c8ebf6cb1b47e3621", + "go.mod": "bb09c53ffea926fbfba6e580ed3bd71ca358575401717389b3c7c49c0c674b43", + "README.md": "5d936b7f3de87bbf4e946f0dc44823d2f69b62b4122b55b9064791b7a19fd4f5" + }, + "approval_contract_findings": [ + "GET /v1/approvals/{UUID}/claim with existing Bearer approval:read scope; exact service store tenant checked by actual App before lookup", + "Installed fixed HTTPS origin/customCA, no redirect/proxy and bounded strict JSON; configuration/source labels describe owner admission and cannot establish native admission by themselves", + "Both original and fetched facts independently validate at one post-I/O instant; exact nonfreshness authority matches; fetched observation never replaces carried context or renews original freshness", + "Native five-field digest independently recomputed; original/fetched digest plus actual approval-free PDP correspondence checked; native1.0/1 drift is refused even where legacy PDP digest conflates it", + "Decision provenance binds fetched-byte digest/source/config/native digest/observation/validation/deadline outside unchanged request; signature includes that provenance", + "Effective fact deadline is min original/source freshness and approval validity; Access further caps identity/operation/policy deadlines and rechecks before return", + "Access Check does not consume; approvals remain PIP state and PEP still owns one CAS before effect with original decision correspondence and separate live one-shot journal" + ], + "independent_proof": "actual-proof/receipt.json", + "independent_signature_proof": "actual-proof/signature-verification.json", + "retained_fixture_failure": "retained-fixture-failure.json", + "native_gates_still_open": [ + "APPROVAL-WP-0002-T01 exact issuer/human-profile, requester/actor mapping and admitted schema/image/migration; synthetic StaticTokenAuthenticator is not native KeyCape evidence", + "Actual approval:read service identity/audience/storetenant custody and trustedCA/server/image/config source admission; source labels/config alone prove none of these", + "APPROVAL-WP-0002-T04 source/use outbox and actual audit receiver lifecycle/readback/reconciliation acceptance", + "APPROVAL-WP-0002-T05 named actual PEP, strict Secrets successor producer-format trust acceptance and Platform original CAS/journal/effect/cleanup proof at final integrated source pins" + ], + "source_policy_scope": "Access eligibility/policy and other-owner implementation are outside Approval approval", + "native_accepted": false +} diff --git a/docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/retained-fixture-failure.json b/docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/retained-fixture-failure.json new file mode 100644 index 00000000..523d0b5c --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/retained-fixture-failure.json @@ -0,0 +1,12 @@ +{ + "stage": "first independent proof attempt", + "failure": "PermissionError writing synthetic reader-token after fixture initially set0440", + "cause": "proof attempted in-place replacement without restoring fixture-owner write permission", + "repair": "test-only writer chmods own synthetic file0600, replaces bytes, then restores0440; no provider/Access source changed", + "passed_before_fixture_failure": [ + "actual source positive signed response", + "native float/int drift refused despite equal PDP digest", + "stale original refused despite fresh provider" + ], + "result": "not scored as complete; rerun required" +} diff --git a/docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/source-closure-after-review.patch b/docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/source-closure-after-review.patch new file mode 100644 index 00000000..db9d5d81 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/source-closure-after-review.patch @@ -0,0 +1,43 @@ +--- a/workplans/APPROVAL-WP-0005-access-claim-fact-contract.md ++++ b/workplans/APPROVAL-WP-0005-access-claim-fact-contract.md +@@ -6,3 +6,3 @@ + repo: approval-engine +-status: active ++status: finished + flavor: implementation +@@ -43,3 +43,3 @@ + id: APPROVAL-WP-0005-T02 +-status: todo ++status: done + priority: high +@@ -48,4 +48,19 @@ + +-Coordinator reviews/integrates these source records. Access owns the thin Go +-reader/normalization and conformance implementation under FLEX-WP-0024-T06; ++Completed source integration and handoff: coordinator independently reviewed ++and integrated the exact contract/helper combined patch ++096de94db8afff6b429215d951a98b94cd501cc6a7e1c374420e546169df2179 as owner commit ++3485415b823faf4f3b682079195e186666b0ed2b. Integration receipt: ++the-custodian/docs/changes/netkingdom-target-2026-10-16/approval-go-fact/ ++canonical-integration.json. Independent origin HEAD readback matched that commit. ++Normal State Hub reconciliation in the same directory's normal-sync.json ++completed bindings 7/7 and pushed the source commit, with zero failures/errors ++and seven warnings. The receipt's null source_revision/dirty_workplans and ++refused generated-only writeback commit are retained; registrar-written UUIDs ++are preserved. No clean exact-revision DB acknowledgement is inferred from that ++receipt. Coordinator Go suite, 14,114-case actual Python differential and two ++producer vector checks passed. Access received the agreed API, exact source pins ++and golden vectors; Approval T04 independently reviewed its frozen successor. ++Source publication, normal reconciliation and conformance handoff are complete; ++this closure update still follows coordinator review/commit/synchronization. ++Access owns successor reader/normalization integration under FLEX-WP-0024-T06; + Secrets owns strict immutable handoff and Platform owns provider origin/custody +@@ -116 +131,8 @@ + was required. Native T01/T04/T05 obligations and T02 integration stay open. ++ ++No scoped source work remains in APPROVAL-WP-0005 after T01-T04 delivery. ++Native issuer/provenance, audit delivery and actual served-PEP/CAS/effect gates ++remain existing live APPROVAL-WP-0002-T01/T04/T05 and Secrets/Access/Platform ++records. They are not newly discovered leftovers and need no duplicate residual ++record or broad promotion. This closure authorizes no credential, source trust, ++policy deployment, approval mutation or protected operation. diff --git a/docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/source-closure-manifest.json b/docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/source-closure-manifest.json new file mode 100644 index 00000000..a50cdb16 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/source-closure-manifest.json @@ -0,0 +1,33 @@ +{ + "source_commit": "3485415b823faf4f3b682079195e186666b0ed2b", + "publication_readback": "origin HEAD matched independently", + "sync_receipt": "../approval-go-fact/normal-sync.json", + "normal_sync_result": { + "bindings": "7/7", + "fail": 0, + "automation_error": 0, + "warn": 7, + "source_revision": null, + "source_revision_reason": "dirty_workplans", + "generated_only_writeback_commit": "refused; preserved" + }, + "patch_sha256": "e74adc6f02c5400da083e071736c4814769678489c46043362d37a94c578a6e7", + "apply_after": "owner-review-record.patch", + "preserved_generated_ids": [ + "777b17de-2c1e-5ce5-aa82-c99e5137e956", + "24bf384a-fdd0-566d-a1d3-5e89023a7521", + "3275354d-d781-53cf-aec6-975c0d4a601a", + "362bfe75-1cd7-5081-971b-b55ce483c802" + ], + "final_task_statuses": [ + "T01 done", + "T02 done", + "T03 done", + "T04 done" + ], + "final_workplan_status": "finished", + "sequential_apply_check": "passed against exact current canonical workplan including all four generated UUIDs", + "canonical_written": false, + "native_accepted": false, + "residual_promotion": "none; existing WP0002 native obligations remain live" +} diff --git a/docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/source-closure-owner-baseline.md b/docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/source-closure-owner-baseline.md new file mode 100644 index 00000000..ba0b9af9 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/source-closure-owner-baseline.md @@ -0,0 +1,87 @@ +--- +id: APPROVAL-WP-0005 +type: workplan +title: "Approval fact contract and reusable Go data adapter" +domain: infotech +repo: approval-engine +status: active +flavor: implementation +owner: codex +topic_slug: netkingdom +created: "2026-10-16" +updated: "2026-10-16" +depends_on: [] +state_hub_workstream_id: "777b17de-2c1e-5ce5-aa82-c99e5137e956" +--- + +CUST-WP-0080 explicitly selects the necessary Approval-WP-0002-T01/T05 source +contract subset for the Access service HTTP B2 join. Initial scope is owner +review and producer fixtures; subsequent explicit CUST-WP-0080 demand selects +T03's pure reusable data adapter, agreed with Access before implementation. +No broad residual activation, credential transport runtime, trust admission, +policy eligibility or live action is included. + +## Review authoritative claim guarantees and publish thin consumer vectors + +```task +id: APPROVAL-WP-0005-T01 +status: done +priority: high +state_hub_task_id: "24bf384a-fdd0-566d-a1d3-5e89023a7521" +``` + +INTENT basis: factual exact-bound approval claims supplied to Access, never a +second PDP. Source contract documents verified human-control semantics and their +issuer admission limit, both original/subsequent observation bounds, exact +native/PDP correspondence and immutable original submitted context. Producer +vectors cover current lifecycle and canonical types. The superseded proposal's +request-refresh design requires change; fresh facts are verified out-of-band. + +## Independently integrate owner contract and coordinate Access reader conformance + +```task +id: APPROVAL-WP-0005-T02 +status: todo +priority: high +state_hub_task_id: "3275354d-d781-53cf-aec6-975c0d4a601a" +``` + +Coordinator reviews/integrates these source records. Access owns the thin Go +reader/normalization and conformance implementation under FLEX-WP-0024-T06; +Secrets owns strict immutable handoff and Platform owns provider origin/custody +and final client pin. Native issuer/profile and served-PEP obligations remain +live under APPROVAL-WP-0002-T01/T05, with audit under T04. No new native residual +is created, promoted or marked complete by these source-only fixtures. + +## Supply producer-owned reusable Go claim and native binding data adapter + +```task +id: APPROVAL-WP-0005-T03 +status: done +priority: high +state_hub_task_id: "362bfe75-1cd7-5081-971b-b55ce483c802" +``` + +Explicit bounded implementation demand after Access confirmed the exact module +and raw-JSON API: adapters/go/approvalfact contains the existing Python-native +five-field canonicalization port and strict human/PDP-path typed claim validation +plus original/fresh fact matching. It is pure data code, not a source transport, +credential runtime, policy evaluator or consume service. Fact means validated +shape/correspondence, never authenticated provider origin. + +Fourteen pinned native vectors and 24 claim cases pass in Go, with strict +duplicate/Unicode/type/expiry defenses and detached original bytes. A 14,114-case +deterministic differential corpus matches actual Python producer canonical bytes +and digests; Go race tests pass. This is source sample evidence, not exhaustive +proof or native acceptance. An initial module toolchain selection failure and +shared cache sandbox refusal are retained in preparation evidence; final module +uses Access's existing Go1.26.7 and completed tests. + +Access explicitly retains the expected-native-raw-source correspondence gate: +its current decoded float64 maps cannot recover Python float/int identity. +The current pilot's raw resource may be used only with proof of no active target +registry override plus exact existing tenant normalization. Other enrichment +needs authoritative raw source/compiler representation. No numeric restriction, +digest substitution, foreign review, runtime enable or live proof is invented. +T02 still owns independent source integration/handoff; existing T01/T05 native +residual obligations and actual Access conformance/integration remain unchanged. diff --git a/docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/source-closure-preview.md b/docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/source-closure-preview.md new file mode 100644 index 00000000..2f1449ae --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/source-closure-preview.md @@ -0,0 +1,138 @@ +--- +id: APPROVAL-WP-0005 +type: workplan +title: "Approval fact contract and reusable Go data adapter" +domain: infotech +repo: approval-engine +status: finished +flavor: implementation +owner: codex +topic_slug: netkingdom +created: "2026-10-16" +updated: "2026-10-16" +depends_on: [] +state_hub_workstream_id: "777b17de-2c1e-5ce5-aa82-c99e5137e956" +--- + +CUST-WP-0080 explicitly selects the necessary Approval-WP-0002-T01/T05 source +contract subset for the Access service HTTP B2 join. Initial scope is owner +review and producer fixtures; subsequent explicit CUST-WP-0080 demand selects +T03's pure reusable data adapter, agreed with Access before implementation. +No broad residual activation, credential transport runtime, trust admission, +policy eligibility or live action is included. + +## Review authoritative claim guarantees and publish thin consumer vectors + +```task +id: APPROVAL-WP-0005-T01 +status: done +priority: high +state_hub_task_id: "24bf384a-fdd0-566d-a1d3-5e89023a7521" +``` + +INTENT basis: factual exact-bound approval claims supplied to Access, never a +second PDP. Source contract documents verified human-control semantics and their +issuer admission limit, both original/subsequent observation bounds, exact +native/PDP correspondence and immutable original submitted context. Producer +vectors cover current lifecycle and canonical types. The superseded proposal's +request-refresh design requires change; fresh facts are verified out-of-band. + +## Independently integrate owner contract and coordinate Access reader conformance + +```task +id: APPROVAL-WP-0005-T02 +status: done +priority: high +state_hub_task_id: "3275354d-d781-53cf-aec6-975c0d4a601a" +``` + +Completed source integration and handoff: coordinator independently reviewed +and integrated the exact contract/helper combined patch +096de94db8afff6b429215d951a98b94cd501cc6a7e1c374420e546169df2179 as owner commit +3485415b823faf4f3b682079195e186666b0ed2b. Integration receipt: +the-custodian/docs/changes/netkingdom-target-2026-10-16/approval-go-fact/ +canonical-integration.json. Independent origin HEAD readback matched that commit. +Normal State Hub reconciliation in the same directory's normal-sync.json +completed bindings 7/7 and pushed the source commit, with zero failures/errors +and seven warnings. The receipt's null source_revision/dirty_workplans and +refused generated-only writeback commit are retained; registrar-written UUIDs +are preserved. No clean exact-revision DB acknowledgement is inferred from that +receipt. Coordinator Go suite, 14,114-case actual Python differential and two +producer vector checks passed. Access received the agreed API, exact source pins +and golden vectors; Approval T04 independently reviewed its frozen successor. +Source publication, normal reconciliation and conformance handoff are complete; +this closure update still follows coordinator review/commit/synchronization. +Access owns successor reader/normalization integration under FLEX-WP-0024-T06; +Secrets owns strict immutable handoff and Platform owns provider origin/custody +and final client pin. Native issuer/profile and served-PEP obligations remain +live under APPROVAL-WP-0002-T01/T05, with audit under T04. No new native residual +is created, promoted or marked complete by these source-only fixtures. + +## Supply producer-owned reusable Go claim and native binding data adapter + +```task +id: APPROVAL-WP-0005-T03 +status: done +priority: high +state_hub_task_id: "362bfe75-1cd7-5081-971b-b55ce483c802" +``` + +Explicit bounded implementation demand after Access confirmed the exact module +and raw-JSON API: adapters/go/approvalfact contains the existing Python-native +five-field canonicalization port and strict human/PDP-path typed claim validation +plus original/fresh fact matching. It is pure data code, not a source transport, +credential runtime, policy evaluator or consume service. Fact means validated +shape/correspondence, never authenticated provider origin. + +Fourteen pinned native vectors and 24 claim cases pass in Go, with strict +duplicate/Unicode/type/expiry defenses and detached original bytes. A 14,114-case +deterministic differential corpus matches actual Python producer canonical bytes +and digests; Go race tests pass. This is source sample evidence, not exhaustive +proof or native acceptance. An initial module toolchain selection failure and +shared cache sandbox refusal are retained in preparation evidence; final module +uses Access's existing Go1.26.7 and completed tests. + +Access explicitly retains the expected-native-raw-source correspondence gate: +its current decoded float64 maps cannot recover Python float/int identity. +The current pilot's raw resource may be used only with proof of no active target +registry override plus exact existing tenant normalization. Other enrichment +needs authoritative raw source/compiler representation. No numeric restriction, +digest substitution, foreign review, runtime enable or live proof is invented. +T02 still owns independent source integration/handoff; existing T01/T05 native +residual obligations and actual Access conformance/integration remain unchanged. + +## Review Access trusted source join against Approval's fact boundary + +```task +id: APPROVAL-WP-0005-T04 +status: done +priority: high +``` + +Explicit bounded CUST-WP-0080 owner crossreview of Access B2/B3 frozen patch +6079ed2ff40b131aa2c5efc8d33608ecb3475c8158f0486b44ec4469e9f222c1. +Approval scope only: authenticated fixed claim endpoint/store isolation, +original/fresh fact correspondence, exact helper pinning and factual deadline/ +provenance/consume boundary. Access policy, native trust and other-owner assent +are excluded. Review and disposable actual App proof use synthetic identities; +no canonical owner source, live credentials, approval or runtime is mutated. + +Approval-only disposition: approved for the exact source fact boundary, not +Access policy, deployment, identity/custody admission or protected execution. +Review entry: docs/reviews/2026-10-16-access-trusted-source-approval-owner-review.json. +Frozen source/hash and exact helper pins verified. Independent actual Python +App over TLS to actual Access serve passes, with numeric 1.0-to-1 drift refused +despite equal legacy PDP digest, stale original refused despite fresh provider, +foreign tenant read refused and actual source revocation refused. Denials append +no decision record; successful Check leaves approval unconsumed/no use event. +Existing producer signature verification and source record match pass. Go race +taskexecution and actual HTTP command suites pass. Initial synthetic0440 token +replacement fixture failure was corrected and retained; no reviewed source fix +was required. Native T01/T04/T05 obligations and T02 integration stay open. + +No scoped source work remains in APPROVAL-WP-0005 after T01-T04 delivery. +Native issuer/provenance, audit delivery and actual served-PEP/CAS/effect gates +remain existing live APPROVAL-WP-0002-T01/T04/T05 and Secrets/Access/Platform +records. They are not newly discovered leftovers and need no duplicate residual +record or broad promotion. This closure authorizes no credential, source trust, +policy deployment, approval mutation or protected operation. diff --git a/docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/validation.json b/docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/validation.json new file mode 100644 index 00000000..2ad2e79a --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-16/approval-trusted-source-review/validation.json @@ -0,0 +1,17 @@ +{ + "reviewed_patch_sha256": "6079ed2ff40b131aa2c5efc8d33608ecb3475c8158f0486b44ec4469e9f222c1", + "reviewed_source_hashes_matched": 30, + "go_race": { + "command": "go test -race -count=1 ./internal/taskexecution ./cmd/access-engine", + "result": "passed" + }, + "actual_producer_proof": "actual-proof/receipt.json", + "signature_verifier": "Access existing internal/sign DecodeEnvelope+Verify; same synthetic public key fixture; response/source canonical equality", + "owner_review_record_patch_applies_after": "approval-go-fact/implementation.patch", + "patch_check_exit": 0, + "canonical_written": false, + "native_accepted": false, + "binary_sha256": "e0d0f4860363a468e82bc54eb4ae9e270bd508b582949206b58fb7e2e8ce0dd0", + "proof_script_sha256": "8b17be2da79cd1c458cc92e3a79b2247f0d6c8f0d90b59f349386369c5fca1eb", + "retained_tool_failure": "Signature verifier initially refused shared Go cache sandbox write; authorized local retry passed" +} diff --git a/docs/changes/netkingdom-target-2026-10-16/audit-native-admission/admission-packet-r1.md b/docs/changes/netkingdom-target-2026-10-16/audit-native-admission/admission-packet-r1.md new file mode 100644 index 00000000..6530d7ff --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-16/audit-native-admission/admission-packet-r1.md @@ -0,0 +1,134 @@ +# First protected workflow: Audit Core admission packet + +Prepared 2026-10-16 for TSA-09, AUDIT-WP-0013-T02, FLEX-WP-0031-T02/T06/T08, +SECRETS-WP-0013 and RPF-WP-0035-T12. This packet grants no sender, observer, +credential, deployment or protected effect authority. T02 remains waiting. + +## Exact source and receiver compatibility + +Reviewed Access source `0a05383195213e7eaacd08388ce1d7892ce756df`, particularly +`internal/emission/outbox.go` and `delivery.go`; Audit base +`2e97f37c01b583ac779310a2aa0d938bb9816604`. The protected consumer pin is +`flex-auth.secrets-engine`, distinct from generic `flex-auth` and other pins. +Access emits the existing eight-field contract in `docs/event-envelope.md`. +No translating adapter or second event schema is needed. Preserve event id, +Idempotency-Key, source, tenant, correlation, original time and data; the archive +does not authenticate a decision signature or establish approval validity. + +Inventory: `flex-auth.decision.allow`, `.deny`, `.redact`, `.not_applicable`, +`.audit_only`, plus transport class `audit-core.heartbeat`. Heartbeat data.class +names one exact rare class, with assertion, committed and released_uncommitted +counts. Four heartbeats are four retained events, not four decision counts. +Outbox sequence and committed_at are local entry fields; the current wire event +has no stream_instance_id or sequence. Do not promise native receiver instance +filtering or wire sequence-gap detection for this source merely because the +receiver has that optional filter. Scope, lifetime and boundary are required +before comparing cumulative source counts. + +The new isolated receiver tests exercise all five decision types: first202, +same-id duplicate200, changed-body409, write-only read403, independent fixture +readback, exact source/tenant, once-only counts and half-open acceptance-time +boundaries. They exercise wrong scope refusals and class-specific silence. +The identities and narrowed tenant:platform in tests are synthetic fixtures, +not proposed production grants. No live record or real decision validation is +claimed by these tests. + +## Existing deployed evidence and its limit + +Audit `docs/evidence/2026-10-15-cadence-admission/receiver-cadence-release-native.json` +records image `sha256:b479801b0bc76df66a36a85376c45aaf399c6d82feade9ba81430b774c4a8271`, +Deployment UID `b85fe3d0-75c9-4e0d-8c34-c6f1df0881bb`, source5227fb1 and +synthetic native cadence/heartbeat/exact-scope/SQLite/PostgreSQL feature probes. +The same folder records migration0007 and admitted Qonto/Approval metadata. +These are historical receiver capability receipts, not fresh flex-auth admission +or independent protected-workflow acceptance. Revalidate deployed image, readyz, +configuration metadata and migration compatibility before using them for native +acceptance. No production inspection was performed for this packet. + +## Metadata prerequisites before any custody change + +Access owner must return one reviewed per-pin registration payload and canonical +cadence bytes/digest for this pin, plus authoritative retained deployment source, +image, namespace `flex-auth`, exact AND namespace/pod selectors and configured +emission source. Do not derive pod selectors solely from a Helm release name. +Existing six-pin commitment in docs/cross-repo-evidence-admission.md requests +write true/read false, exact per-pin source, tenants ["*"] on the recorded +deny-before-tenant-creation justification, explicit redact and load-bearing. +This packet neither narrows that accepted scope to the fixture tenant nor widens +it; a revised scope requires its owner review. Retention/access/export policy +and owner must be explicit; an intake accepting custody is not that policy. + +Current `docs/contracts/decision-emission-cadence.json` declares generic +source flex-auth, four heartbeat intervals86400 and allow expected_min1 per3600; +its source state and observer status remain pending. It is not a final per-pin +payload. Its daily source heartbeat cannot be registered as hourly f3600 without +an Access-owned runtime/declaration update and provenance. If hourly frame is +adopted, record f3600/wait7200/effective10800 and prove actual runtime adoption. +Do not silently apply Approval Engine's `cadence_registration.prepare`: that +helper deliberately validates source=approval-engine and its own class set. +The generic SenderRegistry parser already supports the reviewed flex-auth +metadata; custody tooling can validate an in-memory candidate with it. + +Platform owns contained sender seed/delivery, registry CAS/readback/ESO reload, +transport and PVC admission. Resolve the actual usable custody route with +`warden route find` and `show` before any credential/access request. Historical +AUDIT-WP-0013-T02 names an unusable draft audit-core-senders route; do not assume +it is corrected. Obtain current route evidence and required scoped approval. +Never copy a registry/token to this packet or give a writer archive-read scope. +Receiver ingress must pair namespace AND exact pod selector to port8080; +source egress and receiver service DNS are separately admitted. Source-owned +single-writer flock/64MiB bound is source progress; actual mounted durable PVC, +restart, full/fenced failure, bounded drain and retained cursor need native proof. + +## Native writer acceptance, followed by independent readback + +1. Retain source/deployment/configuration pins, registry metadata version/digest, + source declaration digest, original exact change authorization and rollback. + Check accepted source/class/tenant/retention/secret policy inventory against + actual receiver configuration without exporting credentials. +2. Owner runs the approved bounded fixture through its contained existing lane. + Record durable event IDs, original correlation/decision/approval/operation + references, source class/tenant, local outbox identity/lifetime and entry + sequence/committed_at; retain before/after cursor, pending, lag, counters and + sanitized first202/duplicate200/refusal receipts. No new metadata operation + is authorized by this packet; where native effect is unadmitted, retain wait. +3. Exercise wrong source/tenant400, invalid/expired bearer401, writer read403, + payload conflict409 and formerly-valid bearer refusal after owner revocation. + A dead letter is not accepted custody and a source stdout202 is not readback. +4. Existing independently authorized read-only owner retrieves each exact ID via + GET /v1/events/{id}; compare source, tenant, action, correlation and normalized + original payload/hash, plus accepted_at. GET /v1/events?correlation_id=... can + enumerate related records within that reader's actual authorized scope. + Record reader identity/role separation metadata and original receiver receipt + references; credentials stay contained. Existing operator reader is evidence + of a past lane, not automatic current access. If no admitted scoped reader + exists, this gate waits on that owner; never upgrade the source writer. +5. Compare exact event-ID sets first: local committed records versus independently + retained records, after explicitly declared bounded drain lag. Missing pending + IDs are lag until the bound expires, then a finding; acknowledged-but-missing + IDs, conflicts and unexpected IDs are separate findings. Exclude four heartbeat + transport events from decision-effect totals. Preserve duplicate attempt counts + while accepted IDs count once. Keep nonzero released_uncommitted as a finding; + zero observed after restart does not establish its historical absence. +6. For aggregate GET /v1/reconciliation name exact source/tenant and explicit + [since,until) accepted_at bounds. Derive the local comparison set by the same + independently read accepted_at of the exact retained IDs, not occurred_at, + local commit time or a cumulative GET /v1/emission snapshot. Report unresolved + boundary arrivals separately and repeat after bounded drain. Source status + lacks tenant/window splits and persisted remote acceptance times; its raw + total cannot be compared to an arbitrary receiver time-window total. This + event-ID/accepted_at method closes a bounded admitted run without claiming a + general unattended reconciliation implementation. Concurrent unrelated writes + must be enumerated or isolated by an admitted outbox lifetime and exact IDs. +7. Independently observe four distinct rare-class heartbeats, actual configured + cadence and lag. A controlled withheld-heartbeat check belongs in an admitted + non-production exercise; do not suppress production evidence. Keep never-seen, + stale, missing-class, duplicate, count and boundary findings distinct. King's + Guard owns independent operational observer acceptance under KG-WP-0005. + +Rollback restores only the exact earlier admitted scope/configuration, records +uncertain delivery and retains outbox/cursor/events. It never deletes evidence, +replays an uncertain protected effect, accepts unsigned decisions, widens a grant +or revives a revoked credential. Audit arrival proves custody after arrival; +neither arrival nor an empty finding set proves source completeness. Independent +chain-head/offsite custody remains AUDIT-WP-0015-T01's separate open boundary. diff --git a/docs/changes/netkingdom-target-2026-10-16/audit-native-admission/admission-packet-r2.md b/docs/changes/netkingdom-target-2026-10-16/audit-native-admission/admission-packet-r2.md new file mode 100644 index 00000000..a998e683 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-16/audit-native-admission/admission-packet-r2.md @@ -0,0 +1,183 @@ +# First protected workflow: Audit Core admission packet + +Prepared 2026-10-16 for TSA-09, AUDIT-WP-0013-T02, FLEX-WP-0031-T02/T06/T08, +SECRETS-WP-0013 and RPF-WP-0035-T12. This packet grants no sender, observer, +credential, deployment or protected effect authority. T02 remains waiting. + +## Exact source and receiver compatibility + +Reviewed Access source `0a05383195213e7eaacd08388ce1d7892ce756df`, particularly +`internal/emission/outbox.go` and `delivery.go`; Audit base +`2e97f37c01b583ac779310a2aa0d938bb9816604`. The protected consumer pin is +`flex-auth.secrets-engine`, distinct from generic `flex-auth` and other pins. +Access emits the existing eight-field contract in `docs/event-envelope.md`. +No translating adapter or second event schema is needed. Preserve event id, +Idempotency-Key, source, tenant, correlation, original time and data; the archive +does not authenticate a decision signature or establish approval validity. + +Inventory: `flex-auth.decision.allow`, `.deny`, `.redact`, `.not_applicable`, +`.audit_only`, plus transport class `audit-core.heartbeat`. Heartbeat data.class +names one exact rare class, with assertion, committed and released_uncommitted +counts. Four heartbeats are four retained events, not four decision counts. +Outbox sequence and committed_at are local entry fields; the current wire event +has no stream_instance_id or sequence. Do not promise native receiver instance +filtering or wire sequence-gap detection for this source merely because the +receiver has that optional filter. Scope, lifetime and boundary are required +before comparing cumulative source counts. + +The new isolated receiver tests exercise all five decision types: first202, +same-id duplicate200, changed-body409, write-only read403, independent fixture +readback, exact source/tenant, once-only counts and half-open acceptance-time +boundaries. They exercise wrong scope refusals and class-specific silence. +The identities and narrowed tenant:platform in tests are synthetic fixtures, +not proposed production grants. No live record or real decision validation is +claimed by these tests. + +## Existing deployed evidence and its limit + +Audit `docs/evidence/2026-10-15-cadence-admission/receiver-cadence-release-native.json` +records image `sha256:b479801b0bc76df66a36a85376c45aaf399c6d82feade9ba81430b774c4a8271`, +Deployment UID `b85fe3d0-75c9-4e0d-8c34-c6f1df0881bb`, source5227fb1 and +synthetic native cadence/heartbeat/exact-scope/SQLite/PostgreSQL feature probes. +The same folder records migration0007 and admitted Qonto/Approval metadata. +These are historical receiver capability receipts, not fresh flex-auth admission +or independent protected-workflow acceptance. Revalidate deployed image, readyz, +configuration metadata and migration compatibility before using them for native +acceptance. No production inspection was performed for this packet. + +## Metadata prerequisites before any custody change + +Access owner must return one reviewed per-pin registration payload and canonical +cadence bytes/digest for this pin, plus authoritative retained deployment source, +image, namespace `flex-auth`, exact AND namespace/pod selectors and configured +emission source. Do not derive pod selectors solely from a Helm release name. +Existing six-pin commitment in docs/cross-repo-evidence-admission.md requests +write true/read false, exact per-pin source, tenants ["*"] on the recorded +deny-before-tenant-creation justification, explicit redact and load-bearing. +This packet neither narrows that accepted scope to the fixture tenant nor widens +it; a revised scope requires its owner review. Retention/access/export policy +and owner must be explicit; an intake accepting custody is not that policy. + +Current `docs/contracts/decision-emission-cadence.json` declares generic +source flex-auth, four heartbeat intervals86400 and allow expected_min1 per3600; +its source state and observer status remain pending. It is not a final per-pin +payload. Its daily source heartbeat cannot be registered as hourly f3600 without +an Access-owned runtime/declaration update and provenance. If hourly frame is +adopted, record f3600/wait7200/effective10800 and prove actual runtime adoption. +Do not silently apply Approval Engine's `cadence_registration.prepare`: that +helper deliberately validates source=approval-engine and its own class set. +The generic SenderRegistry parser already supports the reviewed flex-auth +metadata; custody tooling can validate an in-memory candidate with it. + +Platform owns contained sender seed/delivery, registry CAS/readback/ESO reload, +transport and PVC admission. Resolve the actual usable custody route with +`warden route find` and `show` before any credential/access request. Historical +AUDIT-WP-0013-T02 names an unusable draft audit-core-senders route; do not assume +it is corrected. Obtain current route evidence and required scoped approval. +Never copy a registry/token to this packet or give a writer archive-read scope. +Receiver ingress must pair namespace AND exact pod selector to port8080; +source egress and receiver service DNS are separately admitted. Source-owned +single-writer flock/64MiB bound is source progress; actual mounted durable PVC, +restart, full/fenced failure, bounded drain and retained cursor need native proof. + +## Native writer acceptance, followed by independent readback + +1. Retain source/deployment/configuration pins, registry metadata version/digest, + source declaration digest, original exact change authorization and rollback. + Check accepted source/class/tenant/retention/secret policy inventory against + actual receiver configuration without exporting credentials. +2. Owner runs the approved bounded fixture through its contained existing lane. + Record durable event IDs, original correlation/decision/approval/operation + references, source class/tenant, local outbox identity/lifetime and entry + sequence/committed_at; retain before/after cursor, pending, lag, counters and + sanitized first202/duplicate200/refusal receipts. No new metadata operation + is authorized by this packet; where native effect is unadmitted, retain wait. +3. Exercise wrong source/tenant400, invalid/expired bearer401, writer read403, + payload conflict409 and formerly-valid bearer refusal after owner revocation. + A dead letter is not accepted custody and a source stdout202 is not readback. +4. Existing independently authorized read-only owner retrieves each exact ID via + GET /v1/events/{id}; compare source, tenant, action, correlation and normalized + original payload/hash, plus accepted_at. GET /v1/events?correlation_id=... can + enumerate related records within that reader's actual authorized scope. + Record reader identity/role separation metadata and original receiver receipt + references; credentials stay contained. Existing operator reader is evidence + of a past lane, not automatic current access. If no admitted scoped reader + exists, this gate waits on that owner; never upgrade the source writer. +5. Establish the full producer denominator before matching any chosen IDs. The + independent owner retains the complete producer outbox.jsonl snapshot, source + revision, outbox/PVC lifetime identity and bytes/SHA256/EOF high-water H. Check + every newline-terminated entry, contiguous seq1..H, exact source, unique event + IDs, valid committed_at and complete inventory including zero classes. Pin + drain cursor seq/offset, committed counters and pending/lag to that snapshot; + cursor must refer to a real entry boundary and never exceed H. This is an + independently retained full ledger, not a producer-selected list of successful + IDs. An arbitrary chosen-ID readback cannot satisfy this denominator. Retain + independent transition/effect-owner receipts too: an omitted transition never + queued by a compromised producer remains outside the archive guarantee. + Compare all ledger IDs versus independently retained receiver records, after + explicitly declared bounded drain lag. Missing pending + IDs are lag until the bound expires, then a finding; acknowledged-but-missing + IDs, conflicts and unexpected IDs are separate findings. Exclude four heartbeat + transport events from decision-effect totals. Preserve duplicate attempt counts + while accepted IDs count once. Keep nonzero released_uncommitted as a finding; + zero observed after restart does not establish its historical absence. +6. For aggregate GET /v1/reconciliation name exact source/tenant and explicit + [since,until) accepted_at bounds. Derive the local comparison set by the same + independently read accepted_at of the exact retained IDs, not occurred_at, + local commit time or a cumulative GET /v1/emission snapshot. Report unresolved + boundary arrivals separately and repeat after bounded drain. Source status + lacks tenant/window splits and persisted remote acceptance times; its raw + total cannot be compared to an arbitrary receiver time-window total. This + event-ID/accepted_at method supports a bounded admitted run without claiming a + general unattended reconciliation implementation. Compare the complete map of + all classes, including unexpected classes, against receiver aggregate counts: + all expected IDs must exist, and any receiver count above the independently + enumerated expected map is an unaccounted-record finding. This detects extras + without pretending GET-by-ID itself enumerates the receiver. Correlation search + has a1000 ceiling and no pagination; it is not a complete source export. + Concurrent arrivals require a second independently retained complete ledger + watermark H2 covering the same receiver window, or an approved quiescent + bounded run. Do not report agreement while that boundary remains unsettled. + Independently retained full snapshots, repeated cadence execution, actual + missing-class observation and independent observer adoption remain required + for operational TSA-09 acceptance; this packet alone closes none of them. +7. Independently observe four distinct rare-class heartbeats, actual configured + cadence and lag. A controlled withheld-heartbeat check belongs in an admitted + non-production exercise; do not suppress production evidence. Keep never-seen, + stale, missing-class, duplicate, count and boundary findings distinct. King's + Guard owns independent operational observer acceptance under KG-WP-0005. + +Rollback restores only the exact earlier admitted scope/configuration, records +uncertain delivery and retains outbox/cursor/events. It never deletes evidence, +replays an uncertain protected effect, accepts unsigned decisions, widens a grant +or revives a revoked credential. Audit arrival proves custody after arrival; +neither arrival nor an empty finding set proves source completeness. Independent +chain-head/offsite custody remains AUDIT-WP-0015-T01's separate open boundary. + +## Cross-owner accounting review — 2026-10-16 + +Access owner confirms the initial lifecycle signer slice keeps auditDelivery +disabled. Its current native package has no wire/accounting extension and makes +no G2/hourly-adoption claim. Audit owner accepts the complete independently +retained ledger + exact-ID readback + all-class receiver-count protocol above +for a bounded source-to-receiver reconciliation. A source self-selected receipt +list is insufficient. This is an evidence protocol using existing APIs, not a +new assertion format or permission. Audit has no current source-wide paginated +export endpoint or canonical producer-accounting receipt schema. Original owner +ledger bytes and receiver records are authoritative; a Custodian summary is not. + +For future unattended monitoring, Access may separately propose durable +outbox-lifetime stream identity and sequence metadata, but must preserve signed +DecisionEnvelope bytes. A receiver-compatible wrapper would need a source-owned +versioned wire contract and fixture changes; post-sign insertion into decision +data is refused. An emitter restart is not automatically a new outbox lifetime. +No producer extension is mandatory for the bounded full-ledger protocol above; +repeated complete producer ledger capture/independent retention and source-owned +per-pin cadence/runtime adoption are mandatory operational work still missing. + +The previously skipped existing cross-owner test was run directly from the real +canonical Audit checkout using its actual sibling NetKingdom/Approval fixtures: +`python3 -m pytest -q tests/test_cadence_registration.py::test_real_owner_proposal_when_checkout_available` +passed1. This makes114 tested cases across the focused isolated run and the +canonical owner-fixture check; the isolated run itself remains113 pass/1 skip. +No foreign source was changed (pytest cache writes were denied by the sandbox). diff --git a/docs/changes/netkingdom-target-2026-10-16/audit-native-admission/implementation-r1.patch b/docs/changes/netkingdom-target-2026-10-16/audit-native-admission/implementation-r1.patch new file mode 100644 index 00000000..8464ebe4 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-16/audit-native-admission/implementation-r1.patch @@ -0,0 +1,257 @@ +diff --git a/docs/flex-auth-secrets-engine-admission.md b/docs/flex-auth-secrets-engine-admission.md +new file mode 100644 +index 0000000..6530d7f +--- /dev/null ++++ b/docs/flex-auth-secrets-engine-admission.md +@@ -0,0 +1,134 @@ ++# First protected workflow: Audit Core admission packet ++ ++Prepared 2026-10-16 for TSA-09, AUDIT-WP-0013-T02, FLEX-WP-0031-T02/T06/T08, ++SECRETS-WP-0013 and RPF-WP-0035-T12. This packet grants no sender, observer, ++credential, deployment or protected effect authority. T02 remains waiting. ++ ++## Exact source and receiver compatibility ++ ++Reviewed Access source `0a05383195213e7eaacd08388ce1d7892ce756df`, particularly ++`internal/emission/outbox.go` and `delivery.go`; Audit base ++`2e97f37c01b583ac779310a2aa0d938bb9816604`. The protected consumer pin is ++`flex-auth.secrets-engine`, distinct from generic `flex-auth` and other pins. ++Access emits the existing eight-field contract in `docs/event-envelope.md`. ++No translating adapter or second event schema is needed. Preserve event id, ++Idempotency-Key, source, tenant, correlation, original time and data; the archive ++does not authenticate a decision signature or establish approval validity. ++ ++Inventory: `flex-auth.decision.allow`, `.deny`, `.redact`, `.not_applicable`, ++`.audit_only`, plus transport class `audit-core.heartbeat`. Heartbeat data.class ++names one exact rare class, with assertion, committed and released_uncommitted ++counts. Four heartbeats are four retained events, not four decision counts. ++Outbox sequence and committed_at are local entry fields; the current wire event ++has no stream_instance_id or sequence. Do not promise native receiver instance ++filtering or wire sequence-gap detection for this source merely because the ++receiver has that optional filter. Scope, lifetime and boundary are required ++before comparing cumulative source counts. ++ ++The new isolated receiver tests exercise all five decision types: first202, ++same-id duplicate200, changed-body409, write-only read403, independent fixture ++readback, exact source/tenant, once-only counts and half-open acceptance-time ++boundaries. They exercise wrong scope refusals and class-specific silence. ++The identities and narrowed tenant:platform in tests are synthetic fixtures, ++not proposed production grants. No live record or real decision validation is ++claimed by these tests. ++ ++## Existing deployed evidence and its limit ++ ++Audit `docs/evidence/2026-10-15-cadence-admission/receiver-cadence-release-native.json` ++records image `sha256:b479801b0bc76df66a36a85376c45aaf399c6d82feade9ba81430b774c4a8271`, ++Deployment UID `b85fe3d0-75c9-4e0d-8c34-c6f1df0881bb`, source5227fb1 and ++synthetic native cadence/heartbeat/exact-scope/SQLite/PostgreSQL feature probes. ++The same folder records migration0007 and admitted Qonto/Approval metadata. ++These are historical receiver capability receipts, not fresh flex-auth admission ++or independent protected-workflow acceptance. Revalidate deployed image, readyz, ++configuration metadata and migration compatibility before using them for native ++acceptance. No production inspection was performed for this packet. ++ ++## Metadata prerequisites before any custody change ++ ++Access owner must return one reviewed per-pin registration payload and canonical ++cadence bytes/digest for this pin, plus authoritative retained deployment source, ++image, namespace `flex-auth`, exact AND namespace/pod selectors and configured ++emission source. Do not derive pod selectors solely from a Helm release name. ++Existing six-pin commitment in docs/cross-repo-evidence-admission.md requests ++write true/read false, exact per-pin source, tenants ["*"] on the recorded ++deny-before-tenant-creation justification, explicit redact and load-bearing. ++This packet neither narrows that accepted scope to the fixture tenant nor widens ++it; a revised scope requires its owner review. Retention/access/export policy ++and owner must be explicit; an intake accepting custody is not that policy. ++ ++Current `docs/contracts/decision-emission-cadence.json` declares generic ++source flex-auth, four heartbeat intervals86400 and allow expected_min1 per3600; ++its source state and observer status remain pending. It is not a final per-pin ++payload. Its daily source heartbeat cannot be registered as hourly f3600 without ++an Access-owned runtime/declaration update and provenance. If hourly frame is ++adopted, record f3600/wait7200/effective10800 and prove actual runtime adoption. ++Do not silently apply Approval Engine's `cadence_registration.prepare`: that ++helper deliberately validates source=approval-engine and its own class set. ++The generic SenderRegistry parser already supports the reviewed flex-auth ++metadata; custody tooling can validate an in-memory candidate with it. ++ ++Platform owns contained sender seed/delivery, registry CAS/readback/ESO reload, ++transport and PVC admission. Resolve the actual usable custody route with ++`warden route find` and `show` before any credential/access request. Historical ++AUDIT-WP-0013-T02 names an unusable draft audit-core-senders route; do not assume ++it is corrected. Obtain current route evidence and required scoped approval. ++Never copy a registry/token to this packet or give a writer archive-read scope. ++Receiver ingress must pair namespace AND exact pod selector to port8080; ++source egress and receiver service DNS are separately admitted. Source-owned ++single-writer flock/64MiB bound is source progress; actual mounted durable PVC, ++restart, full/fenced failure, bounded drain and retained cursor need native proof. ++ ++## Native writer acceptance, followed by independent readback ++ ++1. Retain source/deployment/configuration pins, registry metadata version/digest, ++ source declaration digest, original exact change authorization and rollback. ++ Check accepted source/class/tenant/retention/secret policy inventory against ++ actual receiver configuration without exporting credentials. ++2. Owner runs the approved bounded fixture through its contained existing lane. ++ Record durable event IDs, original correlation/decision/approval/operation ++ references, source class/tenant, local outbox identity/lifetime and entry ++ sequence/committed_at; retain before/after cursor, pending, lag, counters and ++ sanitized first202/duplicate200/refusal receipts. No new metadata operation ++ is authorized by this packet; where native effect is unadmitted, retain wait. ++3. Exercise wrong source/tenant400, invalid/expired bearer401, writer read403, ++ payload conflict409 and formerly-valid bearer refusal after owner revocation. ++ A dead letter is not accepted custody and a source stdout202 is not readback. ++4. Existing independently authorized read-only owner retrieves each exact ID via ++ GET /v1/events/{id}; compare source, tenant, action, correlation and normalized ++ original payload/hash, plus accepted_at. GET /v1/events?correlation_id=... can ++ enumerate related records within that reader's actual authorized scope. ++ Record reader identity/role separation metadata and original receiver receipt ++ references; credentials stay contained. Existing operator reader is evidence ++ of a past lane, not automatic current access. If no admitted scoped reader ++ exists, this gate waits on that owner; never upgrade the source writer. ++5. Compare exact event-ID sets first: local committed records versus independently ++ retained records, after explicitly declared bounded drain lag. Missing pending ++ IDs are lag until the bound expires, then a finding; acknowledged-but-missing ++ IDs, conflicts and unexpected IDs are separate findings. Exclude four heartbeat ++ transport events from decision-effect totals. Preserve duplicate attempt counts ++ while accepted IDs count once. Keep nonzero released_uncommitted as a finding; ++ zero observed after restart does not establish its historical absence. ++6. For aggregate GET /v1/reconciliation name exact source/tenant and explicit ++ [since,until) accepted_at bounds. Derive the local comparison set by the same ++ independently read accepted_at of the exact retained IDs, not occurred_at, ++ local commit time or a cumulative GET /v1/emission snapshot. Report unresolved ++ boundary arrivals separately and repeat after bounded drain. Source status ++ lacks tenant/window splits and persisted remote acceptance times; its raw ++ total cannot be compared to an arbitrary receiver time-window total. This ++ event-ID/accepted_at method closes a bounded admitted run without claiming a ++ general unattended reconciliation implementation. Concurrent unrelated writes ++ must be enumerated or isolated by an admitted outbox lifetime and exact IDs. ++7. Independently observe four distinct rare-class heartbeats, actual configured ++ cadence and lag. A controlled withheld-heartbeat check belongs in an admitted ++ non-production exercise; do not suppress production evidence. Keep never-seen, ++ stale, missing-class, duplicate, count and boundary findings distinct. King's ++ Guard owns independent operational observer acceptance under KG-WP-0005. ++ ++Rollback restores only the exact earlier admitted scope/configuration, records ++uncertain delivery and retains outbox/cursor/events. It never deletes evidence, ++replays an uncertain protected effect, accepts unsigned decisions, widens a grant ++or revives a revoked credential. Audit arrival proves custody after arrival; ++neither arrival nor an empty finding set proves source completeness. Independent ++chain-head/offsite custody remains AUDIT-WP-0015-T01's separate open boundary. +diff --git a/tests/test_flex_auth_native_contract.py b/tests/test_flex_auth_native_contract.py +new file mode 100644 +index 0000000..93f7559 +--- /dev/null ++++ b/tests/test_flex_auth_native_contract.py +@@ -0,0 +1,90 @@ ++"""AUDIT-WP-0013-T02: wire compatibility, never live sender admission. ++ ++Fixture field mapping is pinned in docs/flex-auth-secrets-engine-admission.md. ++All identities/tokens below exist only in the isolated test receiver. ++""" ++from datetime import datetime, timedelta, timezone ++from urllib.parse import urlencode ++ ++import pytest ++ ++from audit_core.ingestion import IngestionApplication ++from audit_core.senders import SenderIdentity, SenderRegistry ++from audit_core.sqlite_backend import SQLiteAuditBackend ++from audit_core.stream_findings import evaluate, NEVER_HEARTBEAT ++from test_stream_findings import invoke ++ ++SOURCE = "flex-auth.secrets-engine" ++RARE = tuple("flex-auth.decision." + name for name in ("deny", "redact", "not_applicable", "audit_only")) ++CLASSES = RARE + ("flex-auth.decision.allow",) ++ ++ ++@pytest.fixture ++def receiver(tmp_path): ++ writer = SenderIdentity(name=SOURCE, tokens=("fixture-writer",), sources=frozenset({SOURCE}), ++ tenants=frozenset({"tenant:platform"}), may_read=False, ++ evidence_kind="load-bearing", heartbeat_classes=tuple((c, 86400) for c in RARE)) ++ reader = SenderIdentity(name="fixture-independent-reader", tokens=("fixture-reader",), ++ sources=frozenset({SOURCE}), tenants=frozenset({"tenant:platform"}), ++ may_write=False, may_read=True) ++ return IngestionApplication(SQLiteAuditBackend(str(tmp_path / "events.db")), SenderRegistry([writer, reader])), writer ++ ++ ++def envelope(kind, event_id="fixture-event"): ++ # outbox.go Event + event(): no receiver-derived fields or invented sequence. ++ return dict(id=event_id, type=kind, source=SOURCE, subject="openbao/metadata/fixture", ++ tenant="tenant:platform", correlation_id="fixture-decision", ++ occurred_at="2026-10-16T00:00:00.123456789Z", ++ data={"id": "fixture-decision", "effect": kind.rsplit(".", 1)[-1]}) ++ ++ ++def post(app, event): ++ return invoke(app, "/v1/events", token="fixture-writer", method="POST", payload=event, key=event["id"]) ++ ++ ++def count(app, since, until, source=SOURCE, tenant="tenant:platform"): ++ return invoke(app, "/v1/reconciliation?" + urlencode(dict(source=source, tenant=tenant, since=since, until=until)), token="fixture-writer") ++ ++ ++@pytest.mark.parametrize("kind", CLASSES) ++def test_exact_pin_wire_duplicate_and_independent_readback(receiver, kind): ++ app, _ = receiver ++ event = envelope(kind) ++ assert post(app, event)[0].startswith("202") ++ assert post(app, event)[0].startswith("200") ++ assert post(app, dict(event, subject="changed"))[0].startswith("409") ++ assert invoke(app, "/v1/events/fixture-event", token="fixture-writer")[0].startswith("403") ++ status, retained = invoke(app, "/v1/events/fixture-event", token="fixture-reader") ++ assert status.startswith("200") ++ assert (retained["source"], retained["tenant"], retained["action"]) == (SOURCE, "tenant:platform", kind) ++ assert retained["details"]["data"] == event["data"] ++ accepted = datetime.fromisoformat(retained["accepted_at"]) ++ before, after = ((accepted + timedelta(seconds=n)).isoformat() for n in (-1, 1)) ++ status, counts = count(app, before, after) ++ assert status.startswith("200") ++ assert counts["counts"] == [{"class": kind, "count": 1}] ++ # Acceptance-time [since, until): source occurred_at is deliberately older. ++ assert count(app, before, accepted.isoformat())[1]["counts"] == [] ++ assert count(app, accepted.isoformat(), after)[1]["counts"] == [{"class": kind, "count": 1}] ++ ++ ++def test_wrong_scope_refusals_are_not_empty_counts(receiver): ++ app, _ = receiver ++ assert post(app, dict(envelope(CLASSES[0]), source="flex-auth.other"))[0].startswith("400") ++ assert post(app, dict(envelope(CLASSES[0]), tenant="tenant:other"))[0].startswith("400") ++ window = ("2026-10-15T00:00:00Z", "2027-01-01T00:00:00Z") ++ assert count(app, *window, source="flex-auth.other")[0].startswith("403") ++ assert count(app, *window, tenant="tenant:other")[0].startswith("403") ++ ++ ++def test_each_rare_class_must_have_its_own_positive_claim(receiver): ++ app, writer = receiver ++ now = datetime.now(timezone.utc) ++ covered = RARE[0] ++ event = dict(envelope("audit-core.heartbeat"), subject=SOURCE, ++ data={"class": covered, "assertion": "nothing-to-report", "committed": 0, "released_uncommitted": 0}) ++ assert post(app, event)[0].startswith("202") ++ # Source/class finding evaluator; a heartbeat for deny cannot cover redact. ++ findings = evaluate([writer], {(SOURCE, covered): now.isoformat()}, now=now) ++ assert {f.event_class for f in findings} == set(RARE) - {covered} ++ assert all(f.kind == NEVER_HEARTBEAT for f in findings) +diff --git a/workplans/AUDIT-WP-0013-cross-repo-sender-admission.md b/workplans/AUDIT-WP-0013-cross-repo-sender-admission.md +index a663791..07e4fff 100644 +--- a/workplans/AUDIT-WP-0013-cross-repo-sender-admission.md ++++ b/workplans/AUDIT-WP-0013-cross-repo-sender-admission.md +@@ -131,3 +131,16 @@ not independent receiver readback. Real elapsed hourly observation, independent + active-instance/class-silence/sequence/count/lag assessment and Approval lifecycle + acceptance remain with NK-WP-0035/KG-WP-0005. Source-global receiver findings must + not misclassify intentional Qonto scale-to-zero as an active-instance omission. ++ ++### First protected pin admission preparation — 2026-10-16 ++ ++T02 remains wait. Prepared docs/flex-auth-secrets-engine-admission.md against ++Access0a05383195213e7eaacd08388ce1d7892ce756df and Audit2e97f37. Existing wire ++contracts need no adapter. Seven local tests cover all five decision classes, ++retry/conflict, independent fixture readback, writer-read and wrong-scope refusal, ++half-open acceptance-time accounting and rare-class silence. This does not grant ++or admit a sender. Per-pin source/cadence/policy/selectors, usable attended custody ++route, source deployment/PVC and independent native readback remain owner gates. ++Generic source cadence remains daily and cannot stand in for admitted hourly ++runtime. Source outbox sequence is not on the wire; exact event-ID/accepted_at ++comparison is the bounded readback method, not cumulative-time-window equality. diff --git a/docs/changes/netkingdom-target-2026-10-16/audit-native-admission/implementation-r2.patch b/docs/changes/netkingdom-target-2026-10-16/audit-native-admission/implementation-r2.patch new file mode 100644 index 00000000..07ab03ad --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-16/audit-native-admission/implementation-r2.patch @@ -0,0 +1,316 @@ +diff --git a/docs/flex-auth-secrets-engine-admission.md b/docs/flex-auth-secrets-engine-admission.md +new file mode 100644 +index 0000000..a998e68 +--- /dev/null ++++ b/docs/flex-auth-secrets-engine-admission.md +@@ -0,0 +1,183 @@ ++# First protected workflow: Audit Core admission packet ++ ++Prepared 2026-10-16 for TSA-09, AUDIT-WP-0013-T02, FLEX-WP-0031-T02/T06/T08, ++SECRETS-WP-0013 and RPF-WP-0035-T12. This packet grants no sender, observer, ++credential, deployment or protected effect authority. T02 remains waiting. ++ ++## Exact source and receiver compatibility ++ ++Reviewed Access source `0a05383195213e7eaacd08388ce1d7892ce756df`, particularly ++`internal/emission/outbox.go` and `delivery.go`; Audit base ++`2e97f37c01b583ac779310a2aa0d938bb9816604`. The protected consumer pin is ++`flex-auth.secrets-engine`, distinct from generic `flex-auth` and other pins. ++Access emits the existing eight-field contract in `docs/event-envelope.md`. ++No translating adapter or second event schema is needed. Preserve event id, ++Idempotency-Key, source, tenant, correlation, original time and data; the archive ++does not authenticate a decision signature or establish approval validity. ++ ++Inventory: `flex-auth.decision.allow`, `.deny`, `.redact`, `.not_applicable`, ++`.audit_only`, plus transport class `audit-core.heartbeat`. Heartbeat data.class ++names one exact rare class, with assertion, committed and released_uncommitted ++counts. Four heartbeats are four retained events, not four decision counts. ++Outbox sequence and committed_at are local entry fields; the current wire event ++has no stream_instance_id or sequence. Do not promise native receiver instance ++filtering or wire sequence-gap detection for this source merely because the ++receiver has that optional filter. Scope, lifetime and boundary are required ++before comparing cumulative source counts. ++ ++The new isolated receiver tests exercise all five decision types: first202, ++same-id duplicate200, changed-body409, write-only read403, independent fixture ++readback, exact source/tenant, once-only counts and half-open acceptance-time ++boundaries. They exercise wrong scope refusals and class-specific silence. ++The identities and narrowed tenant:platform in tests are synthetic fixtures, ++not proposed production grants. No live record or real decision validation is ++claimed by these tests. ++ ++## Existing deployed evidence and its limit ++ ++Audit `docs/evidence/2026-10-15-cadence-admission/receiver-cadence-release-native.json` ++records image `sha256:b479801b0bc76df66a36a85376c45aaf399c6d82feade9ba81430b774c4a8271`, ++Deployment UID `b85fe3d0-75c9-4e0d-8c34-c6f1df0881bb`, source5227fb1 and ++synthetic native cadence/heartbeat/exact-scope/SQLite/PostgreSQL feature probes. ++The same folder records migration0007 and admitted Qonto/Approval metadata. ++These are historical receiver capability receipts, not fresh flex-auth admission ++or independent protected-workflow acceptance. Revalidate deployed image, readyz, ++configuration metadata and migration compatibility before using them for native ++acceptance. No production inspection was performed for this packet. ++ ++## Metadata prerequisites before any custody change ++ ++Access owner must return one reviewed per-pin registration payload and canonical ++cadence bytes/digest for this pin, plus authoritative retained deployment source, ++image, namespace `flex-auth`, exact AND namespace/pod selectors and configured ++emission source. Do not derive pod selectors solely from a Helm release name. ++Existing six-pin commitment in docs/cross-repo-evidence-admission.md requests ++write true/read false, exact per-pin source, tenants ["*"] on the recorded ++deny-before-tenant-creation justification, explicit redact and load-bearing. ++This packet neither narrows that accepted scope to the fixture tenant nor widens ++it; a revised scope requires its owner review. Retention/access/export policy ++and owner must be explicit; an intake accepting custody is not that policy. ++ ++Current `docs/contracts/decision-emission-cadence.json` declares generic ++source flex-auth, four heartbeat intervals86400 and allow expected_min1 per3600; ++its source state and observer status remain pending. It is not a final per-pin ++payload. Its daily source heartbeat cannot be registered as hourly f3600 without ++an Access-owned runtime/declaration update and provenance. If hourly frame is ++adopted, record f3600/wait7200/effective10800 and prove actual runtime adoption. ++Do not silently apply Approval Engine's `cadence_registration.prepare`: that ++helper deliberately validates source=approval-engine and its own class set. ++The generic SenderRegistry parser already supports the reviewed flex-auth ++metadata; custody tooling can validate an in-memory candidate with it. ++ ++Platform owns contained sender seed/delivery, registry CAS/readback/ESO reload, ++transport and PVC admission. Resolve the actual usable custody route with ++`warden route find` and `show` before any credential/access request. Historical ++AUDIT-WP-0013-T02 names an unusable draft audit-core-senders route; do not assume ++it is corrected. Obtain current route evidence and required scoped approval. ++Never copy a registry/token to this packet or give a writer archive-read scope. ++Receiver ingress must pair namespace AND exact pod selector to port8080; ++source egress and receiver service DNS are separately admitted. Source-owned ++single-writer flock/64MiB bound is source progress; actual mounted durable PVC, ++restart, full/fenced failure, bounded drain and retained cursor need native proof. ++ ++## Native writer acceptance, followed by independent readback ++ ++1. Retain source/deployment/configuration pins, registry metadata version/digest, ++ source declaration digest, original exact change authorization and rollback. ++ Check accepted source/class/tenant/retention/secret policy inventory against ++ actual receiver configuration without exporting credentials. ++2. Owner runs the approved bounded fixture through its contained existing lane. ++ Record durable event IDs, original correlation/decision/approval/operation ++ references, source class/tenant, local outbox identity/lifetime and entry ++ sequence/committed_at; retain before/after cursor, pending, lag, counters and ++ sanitized first202/duplicate200/refusal receipts. No new metadata operation ++ is authorized by this packet; where native effect is unadmitted, retain wait. ++3. Exercise wrong source/tenant400, invalid/expired bearer401, writer read403, ++ payload conflict409 and formerly-valid bearer refusal after owner revocation. ++ A dead letter is not accepted custody and a source stdout202 is not readback. ++4. Existing independently authorized read-only owner retrieves each exact ID via ++ GET /v1/events/{id}; compare source, tenant, action, correlation and normalized ++ original payload/hash, plus accepted_at. GET /v1/events?correlation_id=... can ++ enumerate related records within that reader's actual authorized scope. ++ Record reader identity/role separation metadata and original receiver receipt ++ references; credentials stay contained. Existing operator reader is evidence ++ of a past lane, not automatic current access. If no admitted scoped reader ++ exists, this gate waits on that owner; never upgrade the source writer. ++5. Establish the full producer denominator before matching any chosen IDs. The ++ independent owner retains the complete producer outbox.jsonl snapshot, source ++ revision, outbox/PVC lifetime identity and bytes/SHA256/EOF high-water H. Check ++ every newline-terminated entry, contiguous seq1..H, exact source, unique event ++ IDs, valid committed_at and complete inventory including zero classes. Pin ++ drain cursor seq/offset, committed counters and pending/lag to that snapshot; ++ cursor must refer to a real entry boundary and never exceed H. This is an ++ independently retained full ledger, not a producer-selected list of successful ++ IDs. An arbitrary chosen-ID readback cannot satisfy this denominator. Retain ++ independent transition/effect-owner receipts too: an omitted transition never ++ queued by a compromised producer remains outside the archive guarantee. ++ Compare all ledger IDs versus independently retained receiver records, after ++ explicitly declared bounded drain lag. Missing pending ++ IDs are lag until the bound expires, then a finding; acknowledged-but-missing ++ IDs, conflicts and unexpected IDs are separate findings. Exclude four heartbeat ++ transport events from decision-effect totals. Preserve duplicate attempt counts ++ while accepted IDs count once. Keep nonzero released_uncommitted as a finding; ++ zero observed after restart does not establish its historical absence. ++6. For aggregate GET /v1/reconciliation name exact source/tenant and explicit ++ [since,until) accepted_at bounds. Derive the local comparison set by the same ++ independently read accepted_at of the exact retained IDs, not occurred_at, ++ local commit time or a cumulative GET /v1/emission snapshot. Report unresolved ++ boundary arrivals separately and repeat after bounded drain. Source status ++ lacks tenant/window splits and persisted remote acceptance times; its raw ++ total cannot be compared to an arbitrary receiver time-window total. This ++ event-ID/accepted_at method supports a bounded admitted run without claiming a ++ general unattended reconciliation implementation. Compare the complete map of ++ all classes, including unexpected classes, against receiver aggregate counts: ++ all expected IDs must exist, and any receiver count above the independently ++ enumerated expected map is an unaccounted-record finding. This detects extras ++ without pretending GET-by-ID itself enumerates the receiver. Correlation search ++ has a1000 ceiling and no pagination; it is not a complete source export. ++ Concurrent arrivals require a second independently retained complete ledger ++ watermark H2 covering the same receiver window, or an approved quiescent ++ bounded run. Do not report agreement while that boundary remains unsettled. ++ Independently retained full snapshots, repeated cadence execution, actual ++ missing-class observation and independent observer adoption remain required ++ for operational TSA-09 acceptance; this packet alone closes none of them. ++7. Independently observe four distinct rare-class heartbeats, actual configured ++ cadence and lag. A controlled withheld-heartbeat check belongs in an admitted ++ non-production exercise; do not suppress production evidence. Keep never-seen, ++ stale, missing-class, duplicate, count and boundary findings distinct. King's ++ Guard owns independent operational observer acceptance under KG-WP-0005. ++ ++Rollback restores only the exact earlier admitted scope/configuration, records ++uncertain delivery and retains outbox/cursor/events. It never deletes evidence, ++replays an uncertain protected effect, accepts unsigned decisions, widens a grant ++or revives a revoked credential. Audit arrival proves custody after arrival; ++neither arrival nor an empty finding set proves source completeness. Independent ++chain-head/offsite custody remains AUDIT-WP-0015-T01's separate open boundary. ++ ++## Cross-owner accounting review — 2026-10-16 ++ ++Access owner confirms the initial lifecycle signer slice keeps auditDelivery ++disabled. Its current native package has no wire/accounting extension and makes ++no G2/hourly-adoption claim. Audit owner accepts the complete independently ++retained ledger + exact-ID readback + all-class receiver-count protocol above ++for a bounded source-to-receiver reconciliation. A source self-selected receipt ++list is insufficient. This is an evidence protocol using existing APIs, not a ++new assertion format or permission. Audit has no current source-wide paginated ++export endpoint or canonical producer-accounting receipt schema. Original owner ++ledger bytes and receiver records are authoritative; a Custodian summary is not. ++ ++For future unattended monitoring, Access may separately propose durable ++outbox-lifetime stream identity and sequence metadata, but must preserve signed ++DecisionEnvelope bytes. A receiver-compatible wrapper would need a source-owned ++versioned wire contract and fixture changes; post-sign insertion into decision ++data is refused. An emitter restart is not automatically a new outbox lifetime. ++No producer extension is mandatory for the bounded full-ledger protocol above; ++repeated complete producer ledger capture/independent retention and source-owned ++per-pin cadence/runtime adoption are mandatory operational work still missing. ++ ++The previously skipped existing cross-owner test was run directly from the real ++canonical Audit checkout using its actual sibling NetKingdom/Approval fixtures: ++`python3 -m pytest -q tests/test_cadence_registration.py::test_real_owner_proposal_when_checkout_available` ++passed1. This makes114 tested cases across the focused isolated run and the ++canonical owner-fixture check; the isolated run itself remains113 pass/1 skip. ++No foreign source was changed (pytest cache writes were denied by the sandbox). +diff --git a/tests/test_flex_auth_native_contract.py b/tests/test_flex_auth_native_contract.py +new file mode 100644 +index 0000000..93f7559 +--- /dev/null ++++ b/tests/test_flex_auth_native_contract.py +@@ -0,0 +1,90 @@ ++"""AUDIT-WP-0013-T02: wire compatibility, never live sender admission. ++ ++Fixture field mapping is pinned in docs/flex-auth-secrets-engine-admission.md. ++All identities/tokens below exist only in the isolated test receiver. ++""" ++from datetime import datetime, timedelta, timezone ++from urllib.parse import urlencode ++ ++import pytest ++ ++from audit_core.ingestion import IngestionApplication ++from audit_core.senders import SenderIdentity, SenderRegistry ++from audit_core.sqlite_backend import SQLiteAuditBackend ++from audit_core.stream_findings import evaluate, NEVER_HEARTBEAT ++from test_stream_findings import invoke ++ ++SOURCE = "flex-auth.secrets-engine" ++RARE = tuple("flex-auth.decision." + name for name in ("deny", "redact", "not_applicable", "audit_only")) ++CLASSES = RARE + ("flex-auth.decision.allow",) ++ ++ ++@pytest.fixture ++def receiver(tmp_path): ++ writer = SenderIdentity(name=SOURCE, tokens=("fixture-writer",), sources=frozenset({SOURCE}), ++ tenants=frozenset({"tenant:platform"}), may_read=False, ++ evidence_kind="load-bearing", heartbeat_classes=tuple((c, 86400) for c in RARE)) ++ reader = SenderIdentity(name="fixture-independent-reader", tokens=("fixture-reader",), ++ sources=frozenset({SOURCE}), tenants=frozenset({"tenant:platform"}), ++ may_write=False, may_read=True) ++ return IngestionApplication(SQLiteAuditBackend(str(tmp_path / "events.db")), SenderRegistry([writer, reader])), writer ++ ++ ++def envelope(kind, event_id="fixture-event"): ++ # outbox.go Event + event(): no receiver-derived fields or invented sequence. ++ return dict(id=event_id, type=kind, source=SOURCE, subject="openbao/metadata/fixture", ++ tenant="tenant:platform", correlation_id="fixture-decision", ++ occurred_at="2026-10-16T00:00:00.123456789Z", ++ data={"id": "fixture-decision", "effect": kind.rsplit(".", 1)[-1]}) ++ ++ ++def post(app, event): ++ return invoke(app, "/v1/events", token="fixture-writer", method="POST", payload=event, key=event["id"]) ++ ++ ++def count(app, since, until, source=SOURCE, tenant="tenant:platform"): ++ return invoke(app, "/v1/reconciliation?" + urlencode(dict(source=source, tenant=tenant, since=since, until=until)), token="fixture-writer") ++ ++ ++@pytest.mark.parametrize("kind", CLASSES) ++def test_exact_pin_wire_duplicate_and_independent_readback(receiver, kind): ++ app, _ = receiver ++ event = envelope(kind) ++ assert post(app, event)[0].startswith("202") ++ assert post(app, event)[0].startswith("200") ++ assert post(app, dict(event, subject="changed"))[0].startswith("409") ++ assert invoke(app, "/v1/events/fixture-event", token="fixture-writer")[0].startswith("403") ++ status, retained = invoke(app, "/v1/events/fixture-event", token="fixture-reader") ++ assert status.startswith("200") ++ assert (retained["source"], retained["tenant"], retained["action"]) == (SOURCE, "tenant:platform", kind) ++ assert retained["details"]["data"] == event["data"] ++ accepted = datetime.fromisoformat(retained["accepted_at"]) ++ before, after = ((accepted + timedelta(seconds=n)).isoformat() for n in (-1, 1)) ++ status, counts = count(app, before, after) ++ assert status.startswith("200") ++ assert counts["counts"] == [{"class": kind, "count": 1}] ++ # Acceptance-time [since, until): source occurred_at is deliberately older. ++ assert count(app, before, accepted.isoformat())[1]["counts"] == [] ++ assert count(app, accepted.isoformat(), after)[1]["counts"] == [{"class": kind, "count": 1}] ++ ++ ++def test_wrong_scope_refusals_are_not_empty_counts(receiver): ++ app, _ = receiver ++ assert post(app, dict(envelope(CLASSES[0]), source="flex-auth.other"))[0].startswith("400") ++ assert post(app, dict(envelope(CLASSES[0]), tenant="tenant:other"))[0].startswith("400") ++ window = ("2026-10-15T00:00:00Z", "2027-01-01T00:00:00Z") ++ assert count(app, *window, source="flex-auth.other")[0].startswith("403") ++ assert count(app, *window, tenant="tenant:other")[0].startswith("403") ++ ++ ++def test_each_rare_class_must_have_its_own_positive_claim(receiver): ++ app, writer = receiver ++ now = datetime.now(timezone.utc) ++ covered = RARE[0] ++ event = dict(envelope("audit-core.heartbeat"), subject=SOURCE, ++ data={"class": covered, "assertion": "nothing-to-report", "committed": 0, "released_uncommitted": 0}) ++ assert post(app, event)[0].startswith("202") ++ # Source/class finding evaluator; a heartbeat for deny cannot cover redact. ++ findings = evaluate([writer], {(SOURCE, covered): now.isoformat()}, now=now) ++ assert {f.event_class for f in findings} == set(RARE) - {covered} ++ assert all(f.kind == NEVER_HEARTBEAT for f in findings) +diff --git a/workplans/AUDIT-WP-0013-cross-repo-sender-admission.md b/workplans/AUDIT-WP-0013-cross-repo-sender-admission.md +index a663791..4867973 100644 +--- a/workplans/AUDIT-WP-0013-cross-repo-sender-admission.md ++++ b/workplans/AUDIT-WP-0013-cross-repo-sender-admission.md +@@ -131,3 +131,26 @@ not independent receiver readback. Real elapsed hourly observation, independent + active-instance/class-silence/sequence/count/lag assessment and Approval lifecycle + acceptance remain with NK-WP-0035/KG-WP-0005. Source-global receiver findings must + not misclassify intentional Qonto scale-to-zero as an active-instance omission. ++ ++### First protected pin admission preparation — 2026-10-16 ++ ++T02 remains wait. Prepared docs/flex-auth-secrets-engine-admission.md against ++Access0a05383195213e7eaacd08388ce1d7892ce756df and Audit2e97f37. Existing wire ++contracts need no adapter. Seven local tests cover all five decision classes, ++retry/conflict, independent fixture readback, writer-read and wrong-scope refusal, ++half-open acceptance-time accounting and rare-class silence. This does not grant ++or admit a sender. Per-pin source/cadence/policy/selectors, usable attended custody ++route, source deployment/PVC and independent native readback remain owner gates. ++Generic source cadence remains daily and cannot stand in for admitted hourly ++runtime. Source outbox sequence is not on the wire; exact event-ID/accepted_at ++comparison is the bounded readback method, not cumulative-time-window equality. ++ ++Cross-owner follow-up: Access confirms auditDelivery remains disabled in the ++initial signer slice. The packet now requires independently retained complete ++producer JSONL with contiguous sequence/high-water and lifetime identity, not a ++selected-ID list. Existing exact-ID reads plus all-class accepted-time count ++comparison support bounded full-ledger reconciliation; correlation search is ++capped and cannot serve as complete export. Operational repeated ledger custody, ++per-pin source cadence/runtime adoption and independent observation still wait. ++The canonical sibling-owner fixture check passed1, resolving the isolated ++checkout's expected skip without modifying foreign source. diff --git a/docs/changes/netkingdom-target-2026-10-16/audit-native-admission/normal-sync.json b/docs/changes/netkingdom-target-2026-10-16/audit-native-admission/normal-sync.json new file mode 100644 index 00000000..897bd2c6 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-16/audit-native-admission/normal-sync.json @@ -0,0 +1,17 @@ +{ + "session_id": 2244, + "exit_code": 0, + "source_revision": "64509d2c4039ca1249f9e8aabf2bae030e3640cf", + "summary": { + "fail": 0, + "automation_error": 0, + "warn": 10, + "info": 0 + }, + "fixes_applied": [ + "bindings: synced 18/18", + "C-17 cleared: pushed 1 backlogged commit(s)", + "C-22 fixed: task 76b59808… description updated", + "push: pushed" + ] +} diff --git a/docs/changes/netkingdom-target-2026-10-16/audit-native-admission/validation-r1.json b/docs/changes/netkingdom-target-2026-10-16/audit-native-admission/validation-r1.json new file mode 100644 index 00000000..4e4b3af4 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-16/audit-native-admission/validation-r1.json @@ -0,0 +1,11 @@ +{ + "new_command": "python3 -m pytest -q tests/test_flex_auth_native_contract.py", + "new_result": "7 passed in 0.37s", + "focused_command": "python3 -m pytest -q tests/test_flex_auth_native_contract.py tests/test_stream_findings.py tests/test_emission_cadence.py tests/test_cadence_registration.py tests/test_senders.py", + "focused_result": "113 passed, 1 skipped in 1.57s", + "postgres_current_run": false, + "native_acceptance": false, + "diff_check": "passed", + "canonical_apply_check": "passed against /home/worsch/audit-core; no canonical writes", + "skip_reason": "tests/test_cadence_registration.py:74: owner checkouts unavailable (isolated /tmp path)" +} diff --git a/docs/changes/netkingdom-target-2026-10-16/audit-native-admission/validation-r2.json b/docs/changes/netkingdom-target-2026-10-16/audit-native-admission/validation-r2.json new file mode 100644 index 00000000..ad54ce62 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-16/audit-native-admission/validation-r2.json @@ -0,0 +1,14 @@ +{ + "new_command": "python3 -m pytest -q tests/test_flex_auth_native_contract.py", + "new_result": "7 passed in 0.37s", + "focused_command": "python3 -m pytest -q tests/test_flex_auth_native_contract.py tests/test_stream_findings.py tests/test_emission_cadence.py tests/test_cadence_registration.py tests/test_senders.py", + "focused_result": "113 passed, 1 skipped in 1.57s", + "postgres_current_run": false, + "native_acceptance": false, + "diff_check": "passed", + "canonical_apply_check": "passed against /home/worsch/audit-core; no canonical writes", + "skip_reason": "tests/test_cadence_registration.py:74: owner checkouts unavailable (isolated /tmp path)", + "canonical_cross_owner_command": "python3 -m pytest -q tests/test_cadence_registration.py::test_real_owner_proposal_when_checkout_available", + "canonical_cross_owner_result": "1 passed in0.04s; cache write warnings only; no source changed", + "r2_diff_check": "passed" +} diff --git a/docs/changes/netkingdom-target-2026-10-16/browser-acceptance-next/implementation.patch b/docs/changes/netkingdom-target-2026-10-16/browser-acceptance-next/implementation.patch new file mode 100644 index 00000000..7c058156 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-16/browser-acceptance-next/implementation.patch @@ -0,0 +1,265 @@ +diff --git a/canon/schemas/security-case-type_v0.1.schema.json b/canon/schemas/security-case-type_v0.1.schema.json +index 22ab31c..3575d8a 100644 +--- a/canon/schemas/security-case-type_v0.1.schema.json ++++ b/canon/schemas/security-case-type_v0.1.schema.json +@@ -132,6 +132,24 @@ + "items": { + "type": "object" + } ++ }, ++ "execution_descriptors": { ++ "type": "object", ++ "maxProperties": 128, ++ "propertyNames": { ++ "type": "string", ++ "pattern": "^[a-z0-9][a-z0-9-]*$", ++ "not": { ++ "pattern": "[^a-z0-9-]" ++ } ++ }, ++ "additionalProperties": { ++ "type": "string", ++ "pattern": "^sha256:[0-9a-f]{64}$", ++ "minLength": 71, ++ "maxLength": 71 ++ }, ++ "description": "Optional exact owner machine-descriptor byte digests keyed by declared follow-through step. A pin alone is not owner acknowledgment, execution authority or native evidence." + } + } + } +diff --git a/tools/security-case-types/README.md b/tools/security-case-types/README.md +index dc4fc91..d25d2e9 100644 +--- a/tools/security-case-types/README.md ++++ b/tools/security-case-types/README.md +@@ -95,3 +95,18 @@ Integration tests invoke actual sibling Mason `prepare` and Warden `Journal.save + with metadata fixtures only. They need those owner source checkouts, and existing + type suites need the public KeyCape request file and provider declarations beside + the checkout. `NET_KINGDOM_ROOT` selects the source root for the 1.2 package tests. ++ ++## Machine descriptor source pins ++ ++The optional `spec.execution_descriptors` map uses the existing Warden kernel's ++step-to-exact-descriptor-byte SHA-256 contract. The JSON schema accepts at most ++128 lowercase `sha256:<64 hex>` pins. The semantic validator additionally requires ++every key to name a declared follow-through step. Malformed maps are refused by ++the direct evidence API too. Absent and partial maps remain legal for source ++preparation; missing steps remain explicit in the evidence report. ++ ++A valid pin is source metadata. It does not authenticate its owner, acknowledge ++a machine seam, admit an adapter or change the implemented-type requirement. ++The existing browser types contain no descriptors; their bytes and lifecycle ++states are unchanged. Owner descriptor/provenance acceptance and actual native ++receipts remain prerequisites to execution and acceptance. +diff --git a/tools/security-case-types/security_case_evidence.py b/tools/security-case-types/security_case_evidence.py +index 0bf0a84..5f25577 100644 +--- a/tools/security-case-types/security_case_evidence.py ++++ b/tools/security-case-types/security_case_evidence.py +@@ -135,6 +135,11 @@ def check_steps(rows, case_source, type_source, rendered): + "journal_schema", "evidence_digest", "session_revoked", "session_revocation_digest", + "grant_revocation_digest", "attempt_id", "operation"} + type_spec = yaml.safe_load(type_source)["spec"] ++ # The direct API must refuse malformed maps too, without AttributeError or ++ # treating an undeclared step as an admitted descriptor. ++ from security_case_validator import validate_execution_descriptors ++ if validate_execution_descriptors(type_spec): ++ raise EvidenceRefused("invalid type machine descriptor pins") + descriptors = type_spec.get("execution_descriptors", {}) + seen, result = set(), [] + for row in rows: +diff --git a/tools/security-case-types/security_case_validator.py b/tools/security-case-types/security_case_validator.py +index 1dc63d6..06cfff4 100644 +--- a/tools/security-case-types/security_case_validator.py ++++ b/tools/security-case-types/security_case_validator.py +@@ -151,6 +151,7 @@ def validate_type(data: dict[str, Any], type_dir: Path | None = None) -> list[Is + + params = _validate_type_parameters(spec["parameters"], issues) + step_ids = _validate_follow_through(spec["follow_through"], issues) ++ issues.extend(validate_execution_descriptors(spec)) + _validate_composition(spec["composition"], params, issues) + _validate_materialization(spec["materialization"], params, issues) + _validate_verification(spec["verification"], params, issues) +@@ -190,6 +191,30 @@ def validate_type(data: dict[str, Any], type_dir: Path | None = None) -> list[Is + return issues + + ++def validate_execution_descriptors(spec: dict[str, Any]) -> list[Issue]: ++ """Validate optional source pins; never infer acknowledgment or authority. ++ ++ Incomplete/absent maps remain legal for plan-only types. Admission and ++ provenance remain the executing owner's responsibility. ++ """ ++ if "execution_descriptors" not in spec: ++ return [] ++ path = "type.spec.execution_descriptors" ++ pins = spec["execution_descriptors"] ++ if type(pins) is not dict or len(pins) > 128: ++ return [err(path, "must be a mapping of at most 128 step descriptor pins")] ++ steps = spec.get("follow_through") ++ declared = {step.get("id") for step in steps ++ if type(step) is dict and type(step.get("id")) is str} if type(steps) is list else set() ++ issues = [] ++ for step, pin in pins.items(): ++ if type(step) is not str or not ID.fullmatch(step) or step not in declared: ++ issues.append(err(path, "descriptor key must identify a declared follow-through step")) ++ if type(pin) is not str or re.fullmatch(r"sha256:[0-9a-f]{64}", pin) is None: ++ issues.append(err(path, "descriptor pin must be a lowercase SHA-256 byte digest")) ++ return issues ++ ++ + def _validate_type_parameters(params: Any, issues: list[Issue]) -> dict[str, dict]: + out: dict[str, dict] = {} + if not isinstance(params, list) or not params: +diff --git a/workplans/NK-WP-0048-security-case-types.md b/workplans/NK-WP-0048-security-case-types.md +index 074efd1..72f1a9b 100644 +--- a/workplans/NK-WP-0048-security-case-types.md ++++ b/workplans/NK-WP-0048-security-case-types.md +@@ -9,7 +9,7 @@ flavor: implementation + owner: claude-code + topic_slug: netkingdom + created: "2026-10-08" +-updated: "2026-10-10" ++updated: "2026-10-16" + depends_on: [] + related_repos: + - ops-mason +@@ -323,7 +323,7 @@ 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, MASON-WP-0010-T03, WARDEN-WP-0044-T03] ++depends_on: [NK-WP-0048-T05, NK-WP-0048-T08, NK-WP-0048-T09, NK-WP-0048-T12, 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" +@@ -584,3 +584,61 @@ 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 ++``` ++ ++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 ++``` ++ ++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 ++``` ++ ++Owner: NetKingdom. Next action: wrap the existing contained `keycape-client.py` ++registration/config/env and identity-provisioner ensure/revoke entry points in ++bounded owner adapters or receipt-only attended steps for Warden's closed kernel. ++Reuse the existing caller/session boundary, CAS/readback and status-only outputs; ++never acquire user-engine's runtime identity or expose registry/client secrets. ++Completion requires exact adapter/source descriptors, positive and refusal source ++checks, uncertainty/created-only rollback behavior and a separate owner review of ++the proposed machine seam. Source preparation can begin without credentials. ++Warden independently owns global provenance/admission integration; Platform owns ++mint/broker and native structural receipts; Apps owns consumer delivery/group ++acceptance. Their assent is not supplied by this task. Pinning descriptors into ++an acknowledged type remains a separately reviewed type-source change. Actual ++attended execution still requires its existing owner authorization and native ++receipts. T06 retains all native/reference lifecycle acceptance scope. +diff --git atools/security-case-types/tests/test_descriptor_pins.py btools/security-case-types/tests/test_descriptor_pins.py +new file mode 100644 +index 0000000..8980425 +--- /dev/null ++++ btools/security-case-types/tests/test_descriptor_pins.py +@@ -0,0 +1,64 @@ ++"""Owner pin contract parity; synthetic pins never establish native acceptance.""" ++import copy ++import json ++from pathlib import Path ++import sys ++ ++import jsonschema ++import pytest ++import yaml ++ ++TOOL = Path(__file__).resolve().parents[1] ++REPO = TOOL.parents[1] ++sys.path.insert(0, str(TOOL)) ++import security_case_validator as v ++import security_case_evidence as e ++ ++TYPE = yaml.safe_load((REPO / 'security-case-types/_template/type.yaml').read_bytes()) ++SCHEMA = json.loads((REPO / 'canon/schemas/security-case-type_v0.1.schema.json').read_bytes()) ++PIN = 'sha256:' + 'a' * 64 ++STEP = TYPE['spec']['follow_through'][0]['id'] ++ ++ ++def with_pins(pins): ++ doc = copy.deepcopy(TYPE) ++ doc['spec']['execution_descriptors'] = pins ++ return doc ++ ++ ++@pytest.mark.parametrize('pins', [{}, {STEP: PIN}]) ++def test_optional_partial_pin_contract_has_schema_semantic_parity(pins): ++ doc = with_pins(pins) ++ jsonschema.validate(doc, SCHEMA) ++ assert not v.has_errors(v.validate_type(doc)) ++ assert doc['spec']['status'] == TYPE['spec']['status'] ++ ++ ++@pytest.mark.parametrize('pins', [None, [], 'digest', {STEP: True}, {STEP: None}, ++ {STEP: 17}, {STEP: 'a' * 64}, {STEP: PIN.upper()}, {STEP: PIN + '\n'}, ++ {STEP + '\n': PIN}, {7: PIN}, {'../escape': PIN}, ++ {f'step-{i}': PIN for i in range(129)}]) ++def test_malformed_descriptor_contract_refused_by_both_validators(pins): ++ doc = with_pins(pins) ++ assert v.has_errors(v.validate_type(doc)) ++ with pytest.raises(jsonschema.ValidationError): ++ jsonschema.validate(doc, SCHEMA) ++ ++ ++def test_well_shaped_unknown_step_is_semantic_refusal(): ++ doc = with_pins({'not-a-declared-step': PIN}) ++ jsonschema.validate(doc, SCHEMA) ++ assert v.has_errors(v.validate_type(doc)) ++ ++ ++@pytest.mark.parametrize('pins', [None, [], {STEP: 'bad'}, {'undeclared-step': PIN}]) ++def test_direct_evidence_api_refuses_bad_map_without_crashing_or_needing_rows(pins): ++ with pytest.raises(e.EvidenceRefused, match='machine descriptor pins'): ++ e.check_steps((), b'case', yaml.safe_dump(with_pins(pins)).encode(), {'follow_through': []}) ++ ++ ++def test_partial_pin_cannot_bypass_ordinary_case_admission(): ++ doc = with_pins({STEP: PIN}) ++ case = yaml.safe_load((REPO / 'security-case-types/_template/examples/example-operator.yaml').read_bytes()) ++ issues = v.validate_case(case, doc) ++ assert any('only implemented types' in issue.message for issue in issues) diff --git a/docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/README.md b/docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/README.md new file mode 100644 index 00000000..c560678a --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/README.md @@ -0,0 +1,52 @@ +# NetKingdom registration adapter source candidate R3 + +Source-only candidate awaiting independent root review. No canonical owner file, +type/case bytes or status changed; no credential or live action was used. +Exercise status: unexercised. + +The patch adds a fixed attended registration adapter around actual maintained +`keycape-client.run`, its contract notes and 16 new fixture checks. The 20 +existing helper checks also pass. Three new checks exercise actual Warden +admission/dispatch/rollback and uncertain transitions with synthetic owner trust +with unchanged published Warden d97652a producer pins. No replacement of adapter +callables after admission is used: the proposed Invocation is built first, and +normal admission independently derives the same full binding. + +R1's root review found an unbound config-checker executable: the caller could +supply another binary and its matching hash under the same descriptor. That +rejected revision remains in `rejected-r1/`; the superseded R2 source/fixture +packet remains in `reviewed-r2/`. R2 corrected the custody finding; R3 retains that correction and includes the exact selected +checker hash in the adapter source-composition pin and descriptor. Substitution +under the original descriptor is now refused before tooling. The descriptor +retained here uses a synthetic checker digest only; native executable selection +and owner acknowledgment remain outstanding. + +Reproduce from current pinned owner dependencies: + +```bash +python3 docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/verify_candidate.py +``` + +`implementation.patch` adds three files and proposes an exact owner workplan edit +against retained `preimages/`. No canonical workplan changed. `postimages/` holds the exact proposed +bytes. `manifest.json` binds all source/fixture hashes and the observed owner +state at packaging; this is not a reconstructed session-start dirty-state +snapshot. `tests.txt` retains the 36 passing checks. Warden/source owner heads +and current canonical changes remain outside this patch. + +The owner contract in `postimages/sso-mfa/k8s/keycape/registration-adapter.md` +records required native callbacks and remaining scope. In particular: published Warden d97652a now admits the exact producer source +bytes through its normal pins, without attesting this adapter; group ensure +has no created-membership provenance; group revoke can remove a pre-existing +membership; env/config/restart adapters remain; durable post-restart creation +reconciliation is absent. Platform/Apps native acceptance remains separately +owned. None of these gaps is supplied by the fixture proof. + +The exact workplan proposal preserves existing T10/T11/T12 Hub UUIDs and all +other prior bytes outside T06 dependencies/T12 prose. T13 records source +implementation and fixture proof as done, explicitly excluding independent +review/publication. T12 retains env/config/restart work, while T14 tracks group +creation provenance, T15 durable registration recovery and T16 actual descriptor/ +checker artifact selection and owner review. T06 retains all source dependencies +and native acceptance scope. No acknowledged type/case bytes or statuses change. +Root review and publication receipts must be added when integration is approved. diff --git a/docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/finalize_receipt_r3.py b/docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/finalize_receipt_r3.py new file mode 100644 index 00000000..808f5da8 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/finalize_receipt_r3.py @@ -0,0 +1,32 @@ +from pathlib import Path +from collections import Counter +import hashlib,json,re,subprocess,urllib.request +import yaml +p=Path(__file__).resolve().parent;owner=Path('/home/worsch/net-kingdom');backup=p/'publication-backup';manifest=json.loads((p/'manifest.json').read_text());wp='e31f3f9d-0748-5137-b0a3-7b3ca46a66eb' +sha=lambda data:'sha256:'+hashlib.sha256(data).hexdigest() +git=lambda *args:subprocess.check_output(['git','-C',str(owner),*args]) +get=lambda path:json.load(urllib.request.urlopen('http://127.0.0.1:8000'+path,timeout=10)) +final=json.loads((p/'normal-final.stdout.json').read_text()); +if final['summary']['fail'] or final['summary']['automation_error']:raise SystemExit('final sync failed') +text=(owner/'workplans/NK-WP-0048-security-case-types.md').read_text();blocks=[yaml.safe_load(block) for block in re.findall(r'```task\n(.*?)```',text,re.S)];rows=get('/tasks/?workplan_id='+wp);by_uuid={r['id']:r for r in rows} +for b in blocks: + if b['state_hub_task_id'] not in by_uuid or by_uuid[b['state_hub_task_id']]['status']!=b['status']:raise SystemExit('task status or UUID mismatch '+b['id']) +original=(p/'preimages/workplans/NK-WP-0048-security-case-types.md').read_text();old={b['id']:b for b in [yaml.safe_load(t) for t in re.findall(r'```task\n(.*?)```',original,re.S)]} +for name in ('NK-WP-0048-T10','NK-WP-0048-T11','NK-WP-0048-T12'): + actual=next(b for b in blocks if b['id']==name) + if actual['state_hub_task_id']!=old[name]['state_hub_task_id']:raise SystemExit('prior UUID changed') +if git('show',':.custodian-brief.md') != (backup/'brief.index').read_bytes() or (owner/'.custodian-brief.md').read_bytes() != (backup/'brief.worktree').read_bytes():raise SystemExit('brief not preserved') +for rel,h in manifest['postimages'].items(): + if not rel.startswith('workplans/') and sha((owner/rel).read_bytes())!=h:raise SystemExit('reviewed source drift') +source_commit='0775c2bfa41af236301155bb5a69bbf088bdf823';current=git('rev-parse','HEAD').decode().strip();published=git('rev-parse','origin/main').decode().strip() +if current!=published:raise SystemExit('owner not published') +deps=get('/workplans/'+wp+'/dependencies/') +(p/'projection-tasks.json').write_text(json.dumps([{'id':b['id'],'uuid':b['state_hub_task_id'],'status':b['status'],'depends_on':b.get('depends_on',[])} for b in blocks],indent=2)+'\n') +(p/'projection-dependencies.json').write_text(json.dumps(deps,indent=2)+'\n') +summary='NetKingdom reviewed registration adapter source published as '+source_commit+'; source task/projection receipt '+current+'. 36 fixtures pass with canonical Warden d97652a producer pins. T13 source done; T12/T14/T15/T16 remain todo and T06 native acceptance remains wait. R1 checker-binding refusal preserved, corrected R2/R3. Both generated-only sync hook refusals and exact staged/working brief preservation retained; no credentials or native action.' +task=next(b for b in blocks if b['id']=='NK-WP-0048-T13')['state_hub_task_id'] +req=urllib.request.Request('http://127.0.0.1:8000/progress/',data=json.dumps({'summary':summary,'event_type':'note','author':'codex','workplan_id':wp,'task_id':task}).encode(),headers={'Content-Type':'application/json'},method='POST') +progress=json.load(urllib.request.urlopen(req,timeout=10));(p/'owner-progress.json').write_text(json.dumps(progress,indent=2)+'\n') +syncs={name:{'exit_code':json.loads((p/(name+'.exit.json')).read_text())['exit_code'],'summary':json.loads((p/(name+'.stdout.json')).read_text())['summary'],'result':json.loads((p/(name+'.stdout.json')).read_text())['result'],'stderr':(p/(name+'.stderr.txt')).read_text()} for name in ('normal-sync','normal-writeback','normal-final')} +receipt={'schema':'custodian.netkingdom.registration-source-integration.v1','source_commit':source_commit,'publication_record_commit':current,'published_origin':published,'reviewed_r3_manifest_sha256':sha((p/'reviewed-r3/manifest.json').read_bytes()),'root_review':'coordinator-review-r3.json','source_integration':True,'normal_syncs':syncs,'file_task_count':len(blocks),'projected_task_count':len(rows),'status_counts':dict(Counter(b['status'] for b in blocks)),'prior_T10_T11_T12_UUIDs_preserved':True,'reviewed_source_hashes_preserved':True,'exact_original_staged_brief_preserved':True,'exact_original_working_brief_preserved':True,'final_source_hashes':{rel:sha((owner/rel).read_bytes()) for rel in manifest['postimages']},'status_after':git('status','--porcelain=v1').decode(),'tests':{'passed':36,'synthetic':True,'producer_pins_overridden':False},'native_acceptance':False,'new_credential_or_live_action':False,'next_tasks':['NK-WP-0048-T12','NK-WP-0048-T14','NK-WP-0048-T15','NK-WP-0048-T16'],'retained_failed_revision':'rejected-r1/','progress_id':progress.get('id')} +(p/'canonical-integration.json').write_text(json.dumps(receipt,indent=2)+'\n');print(json.dumps({'source_commit':source_commit,'final_commit':current,'count':len(blocks),'statuses':receipt['status_counts'],'progress_id':progress.get('id')})) diff --git a/docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/implementation.patch b/docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/implementation.patch new file mode 100644 index 00000000..c2c7b7e4 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/implementation.patch @@ -0,0 +1,669 @@ +--- /dev/null ++++ b/sso-mfa/k8s/keycape/keycape-registration-adapter.py +@@ -0,0 +1,180 @@ ++"""Unexercised, injection-only attended registration seam for Warden's v1 kernel. ++ ++No CLI, credentials, evidence trust root or live registration is installed here. ++Owner publication/authentication and parent-session revocation are mandatory ++injected capabilities. The kernel must independently admit both evidence kinds. ++""" ++from __future__ import annotations ++ ++import argparse ++from dataclasses import asdict, replace ++import json ++import importlib.util ++from pathlib import Path ++ ++import yaml ++ ++from warden.case_run import Adapter, Descriptor, Invocation, OwnerResult, contained_output, digest, IDENTIFIER, DIGEST ++from warden.case_receipt_state import Outcome, Refused ++ ++ROOT = Path(__file__).resolve().parent ++SOURCE_FILES = ('keycape-registration-adapter.py', 'keycape-client.py', ++ 'portal-client-rollout.py', 'openbao-client-config.py', ++ 'register-user-engine-portal.py') ++REFERENCE = 'net-kingdom/keycape-registration@v1' ++ ++# Importing the maintained helper performs no tooling operation. ++_spec = importlib.util.spec_from_file_location('registration_owner_helper', ROOT / 'keycape-client.py') ++helper = importlib.util.module_from_spec(_spec) ++_spec.loader.exec_module(helper) ++ ++ ++def source_digest(check_config_digest): ++ """Pin adapter plus the complete local helper import closure, in filename order.""" ++ if not isinstance(check_config_digest, str) or not DIGEST.fullmatch(check_config_digest): ++ raise Refused('exact reviewed checker digest required') ++ composition = {'files': {name: digest((ROOT / name).read_bytes()) for name in SOURCE_FILES}, ++ 'check_config_binary': check_config_digest} ++ return digest(json.dumps(composition, sort_keys=True, separators=(',', ':')).encode()) ++ ++ ++def descriptor(check_config_digest): ++ return Descriptor('warden.case.execution.v1', 'register-client', 'net-kingdom', ++ REFERENCE, source_digest(check_config_digest), 'human', 'key-cape-oidc-login', ++ 'client_id', 'change', ('keycape-client',), False, ++ ('parent-session',)) ++ ++ ++def build(*, admitted: Invocation, case_source: bytes, expected_uid: str, ++ expected_resource_version: str, case_acceptance: str, ++ check_config: Path, check_config_digest: str, publish, authenticate, ++ revoke_session): ++ """Bind one admitted case and observed revision; return a closed kernel Adapter. ++ ++ publish(bytes)->identifier durably retains only the closed metadata payload. ++ authenticate(identifier, bytes)->True must verify independent owner custody. ++ revoke_session(invocation)->identifier must revoke this attempt's parent ++ session; Warden independently authenticates it using its own evidence verifier. ++ These callbacks are capabilities, never command strings or case parameters. ++ ++ Only a verified creation in this process may be rolled back. Process loss, ++ write/readback ambiguity, publication/revocation failure and source drift ++ require owner reconciliation; none are converted to retry-safe failures. ++ """ ++ reviewed_source = source_digest(check_config_digest) ++ desc = descriptor(check_config_digest) ++ if type(admitted) is not Invocation or not all( ++ isinstance(value, str) and IDENTIFIER.fullmatch(value) ++ for value in (admitted.step, admitted.owner, admitted.target, ++ admitted.actor, admitted.principal, admitted.lane)): ++ raise Refused('bounded invocation required') ++ if desc.adapter_source != reviewed_source: ++ raise Refused('owner source changed during binding') ++ if (admitted.step, admitted.owner, admitted.actor, admitted.lane) != ( ++ desc.step, desc.owner, desc.actor, desc.lane): ++ raise Refused('registration invocation boundary differs') ++ if admitted.operation != 'admission' or admitted.attempt_id: ++ raise Refused('expected admission invocation') ++ if type(case_source) is not bytes or len(case_source) > 262144 or digest(case_source) != admitted.pins.case: ++ raise Refused('case source pin differs') ++ # Pins.adapter is the descriptor digest, not the owner source digest. ++ descriptor_bytes = json.dumps(asdict(desc), sort_keys=True, separators=(',', ':')).encode() ++ if admitted.pins.adapter != digest(descriptor_bytes): ++ raise Refused('descriptor pin differs') ++ if not all(isinstance(x, str) and 0 < len(x) <= 192 for x in ++ (expected_uid, expected_resource_version, case_acceptance)): ++ raise Refused('bounded attended binding required') ++ if digest(case_acceptance.encode()) != admitted.pins.acceptance: ++ raise Refused('acceptance evidence binding differs') ++ if not all(callable(x) for x in (publish, authenticate, revoke_session)): ++ raise Refused('owner evidence and revocation capabilities required') ++ with contained_output(): ++ case = yaml.safe_load(case_source) ++ client = helper.client_from_case(case) ++ if admitted.target != client['clientId']: ++ raise Refused('case target differs') ++ check_config = Path(check_config) ++ records = {} ++ creation = None ++ ++ def binding(inv, operation): ++ if (replace(inv, attempt_id='', operation='admission') != admitted ++ or inv.operation != operation or not IDENTIFIER.fullmatch(inv.attempt_id)): ++ raise Refused('attempt binding differs') ++ if source_digest(check_config_digest) != reviewed_source: ++ raise Refused('owner source changed') ++ if digest(check_config.read_bytes()) != check_config_digest: ++ raise Refused('check-config binary changed') ++ ++ def execute(inv, operation, created=()): ++ nonlocal creation ++ try: ++ with contained_output(): ++ binding(inv, 'run' if operation == 'add' else 'rollback') ++ uid, revision = expected_uid, expected_resource_version ++ if operation == 'remove': ++ if (created != ('keycape-client',) or creation is None ++ or not verify(*creation)): ++ raise Refused('no authenticated case-created client') ++ original_payload = json.loads(records[creation[1].evidence][1]) ++ uid, revision = original_payload['after']['uid'], original_payload['after']['resource_version'] ++ args = argparse.Namespace(mode='apply', operation=operation, ++ expected_uid=uid, expected_resource_version=revision, ++ case_acceptance=case_acceptance, check_config=str(check_config)) ++ receipt = helper.run(args, client) ++ # The helper has completed CAS, whole-data readback and preservation. ++ # Never retain check-config diagnostics or arbitrary helper fields. ++ if not (receipt['schema'] == 'netkingdom.keycape-client.v1' ++ and receipt['target'] == 'sso/keycape-config' ++ and receipt['client_id'] == admitted.target ++ and receipt['mode'] == 'apply' and receipt['operation'] == operation ++ and receipt['values_emitted'] is False ++ and type(receipt['changed']) is bool ++ and receipt['unrelated_config_bytes_preserved'] is True ++ and receipt['other_secret_data_unchanged'] is True ++ and receipt['before'] == {'uid': uid, 'resource_version': revision} ++ and receipt['after']['uid'] == uid ++ and receipt['check_config']['proposed']['valid'] is True): ++ raise Refused('owner readback contract differs') ++ after = receipt['after'] ++ if (set(after) != {'uid', 'resource_version'} or ++ not all(isinstance(v, str) and 0 < len(v) <= 192 for v in after.values())): ++ raise Refused('owner revision contract differs') ++ changed = receipt['changed'] ++ outcome = (Outcome.ROLLED_BACK if operation == 'remove' else ++ Outcome.SUCCESS if changed else Outcome.NO_CHANGE) ++ objects = ('keycape-client',) if operation == 'add' and changed else () ++ finally: ++ # Even helper failure must end the parent session. Failures propagate ++ # contained to the kernel and become uncertain, never safe to replay. ++ with contained_output(): ++ revocation = revoke_session(inv) ++ if not isinstance(revocation, str) or not IDENTIFIER.fullmatch(revocation): ++ raise Refused('session revocation evidence missing') ++ payload = json.dumps({'schema': 'netkingdom.keycape-registration.attempt.v1', ++ 'invocation': asdict(inv), 'source': reviewed_source, ++ 'outcome': outcome.value, 'created': objects, 'before': receipt['before'], ++ 'after': after, 'session_revocation': revocation}, ++ sort_keys=True, separators=(',', ':')).encode() ++ with contained_output(): ++ evidence = publish(payload) ++ if not isinstance(evidence, str) or not IDENTIFIER.fullmatch(evidence) or evidence in records: ++ raise Refused('owner evidence identifier invalid or reused') ++ result = OwnerResult(outcome, evidence, objects, True, revocation, ++ attempt_id=inv.attempt_id, operation=inv.operation) ++ records[evidence] = (inv, payload, result) ++ if operation == 'add' and changed: ++ creation = (inv, result) ++ elif operation == 'remove': ++ creation = None ++ return result ++ ++ def verify(inv, result): ++ record = records.get(result.evidence) if type(result) is OwnerResult else None ++ if record is None or record[0] != inv or record[2] != result: ++ return False ++ with contained_output(): ++ return authenticate(result.evidence, record[1]) is True ++ ++ return Adapter(REFERENCE, reviewed_source, lambda inv: execute(inv, 'add'), ++ verify, lambda inv, created: execute(inv, 'remove', created)) +--- /dev/null ++++ b/sso-mfa/k8s/keycape/test_keycape_registration_adapter.py +@@ -0,0 +1,289 @@ ++"""Actual helper/CAS execution against synthetic Kubernetes objects only.""" ++from dataclasses import asdict, replace ++import copy ++import importlib.util ++import json ++from pathlib import Path ++import tempfile ++import unittest ++from unittest.mock import patch ++ ++from warden.case_run import Invocation, digest ++from warden.case_receipt_state import Outcome, Pins, Refused ++import yaml ++ ++ROOT = Path(__file__).parent ++spec = importlib.util.spec_from_file_location('registration_adapter', ROOT / 'keycape-registration-adapter.py') ++a = importlib.util.module_from_spec(spec) ++spec.loader.exec_module(a) ++spec = importlib.util.spec_from_file_location('registration_fixture', ROOT / 'test_keycape_client.py') ++f = importlib.util.module_from_spec(spec) ++spec.loader.exec_module(f) ++ ++ ++class RegistrationTests(unittest.TestCase): ++ def setUp(self): ++ self.temp = tempfile.TemporaryDirectory() ++ self.binary = Path(self.temp.name) / 'keycape' ++ self.binary.write_bytes(b'synthetic checker binary') ++ self.case = yaml.safe_dump({'spec': {'type': 'oidc-browser-relying-party@1.1', ++ 'parameters': f.FORGEJO_PARAMS}}).encode() ++ desc = a.descriptor(digest(self.binary.read_bytes())) ++ self.desc_bytes = json.dumps(asdict(desc), sort_keys=True, separators=(',', ':')).encode() ++ pins = Pins(digest(self.case), digest(b'type'), digest(self.desc_bytes), ++ digest(b'catalog'), digest(b'case-acceptance:test'), digest(b'run sheet')) ++ self.admitted = Invocation('register-client', 'net-kingdom', 'forgejo', 'human', ++ 'founder', 'key-cape-oidc-login', pins) ++ self.inv = replace(self.admitted, attempt_id='attempt-one', operation='run') ++ self.secret, self.raw = f.fixture() ++ self.effects = [] ++ self.evidence = {} ++ self.revocations = [] ++ self.kw = dict(admitted=self.admitted, case_source=self.case, ++ expected_uid=f.UID, expected_resource_version='7', case_acceptance='case-acceptance:test', ++ check_config=self.binary, check_config_digest=digest(self.binary.read_bytes()), ++ publish=self.publish, authenticate=lambda ref, payload: self.evidence.get(ref) == payload, ++ revoke_session=self.revoke) ++ self.check_patch = patch.object(a.helper, 'check_config', return_value={'valid': True}) ++ self.kube_patch = patch.object(a.helper.lane, 'kubectl', side_effect=self.kube) ++ self.check_patch.start() ++ self.kube_patch.start() ++ self.addCleanup(self.check_patch.stop) ++ self.addCleanup(self.kube_patch.stop) ++ self.addCleanup(self.temp.cleanup) ++ ++ def publish(self, payload): ++ ref = 'owner-evidence:attempt-' + str(len(self.evidence)) ++ self.evidence[ref] = payload ++ return ref ++ ++ def revoke(self, inv): ++ self.revocations.append(inv) ++ return 'session-revoked:' + inv.operation + ':' + inv.attempt_id ++ ++ def kube(self, args, payload=None): ++ if args[:3] == ['get', 'namespace', 'kube-system']: ++ return {'metadata': {'uid': a.helper.lane.CLUSTER_UID}} ++ if args[:5] == ['-n', 'sso', 'get', 'secret', 'keycape-config']: ++ return copy.deepcopy(self.secret) ++ if args[:5] == ['-n', 'sso', 'get', 'deployment', 'keycape']: ++ return {'spec': {'template': {'spec': {'containers': [{'name': 'keycape', ++ 'env': [a.helper.env_binding(f.FORGEJO)]}]}}}} ++ if args[:5] == ['-n', 'sso', 'patch', 'secret', 'keycape-config']: ++ self.assertEqual(payload[:2], [ ++ {'op': 'test', 'path': '/metadata/uid', 'value': self.secret['metadata']['uid']}, ++ {'op': 'test', 'path': '/metadata/resourceVersion', 'value': self.secret['metadata']['resourceVersion']}]) ++ self.effects.append(copy.deepcopy(payload)) ++ self.secret['data']['config.yaml'] = payload[2]['value'] ++ self.secret['metadata']['resourceVersion'] = str(int(self.secret['metadata']['resourceVersion']) + 1) ++ return copy.deepcopy(self.secret) ++ raise AssertionError('unexpected tooling operation') ++ ++ def test_actual_add_and_created_only_rollback_preserve_original_bytes(self): ++ adapter = a.build(**self.kw) ++ original = copy.deepcopy(self.secret['data']) ++ result = adapter.invoke(self.inv) ++ self.assertEqual(result.outcome, Outcome.SUCCESS) ++ self.assertEqual(result.created, ('keycape-client',)) ++ self.assertTrue(adapter.verify(self.inv, result)) ++ undone = adapter.rollback(replace(self.inv, attempt_id='attempt-two', operation='rollback'), result.created) ++ self.assertEqual(undone.outcome, Outcome.ROLLED_BACK) ++ self.assertEqual(self.secret['data'], original) ++ self.assertEqual(len(self.effects), 2) ++ self.assertEqual(len(self.revocations), 2) ++ for payload in self.evidence.values(): ++ self.assertNotIn(b'synthetic-marker', payload) ++ self.assertNotIn(b'config.yaml', payload) ++ self.assertNotIn(b'signing-key', payload) ++ ++ def test_identical_client_is_no_change_and_cannot_be_removed(self): ++ self.secret, _ = f.fixture(f.EXISTING + [f.FORGEJO]) ++ adapter = a.build(**self.kw) ++ result = adapter.invoke(self.inv) ++ self.assertEqual(result.outcome, Outcome.NO_CHANGE) ++ self.assertEqual(result.created, ()) ++ with self.assertRaises(Refused): ++ adapter.rollback(replace(self.inv, operation='rollback'), ('keycape-client',)) ++ self.assertFalse(self.effects) ++ ++ def test_existing_drift_refused_and_session_revoked(self): ++ self.secret, _ = f.fixture(f.EXISTING + [dict(f.FORGEJO, displayName='Drift')]) ++ with self.assertRaises(Refused): ++ a.build(**self.kw).invoke(self.inv) ++ self.assertFalse(self.effects) ++ self.assertEqual(self.revocations, [self.inv]) ++ ++ def test_invocation_rebinding_refused_before_tooling(self): ++ for changes in ({'target': 'other'}, {'principal': 'other'}, {'actor': 'agent'}, ++ {'lane': 'other'}, {'operation': 'rollback'}, {'attempt_id': ''}, ++ {'pins': replace(self.inv.pins, case=digest(b'other'))}): ++ with self.subTest(changes=changes), self.assertRaises(Refused): ++ a.build(**self.kw).invoke(replace(self.inv, **changes)) ++ self.assertFalse(self.effects) ++ ++ def test_initial_case_descriptor_and_target_pins_are_required(self): ++ for changes in ({'case_source': b'{}'}, {'case_acceptance': 'other'}, {'admitted': replace(self.admitted, target='other')}, ++ {'admitted': replace(self.admitted, actor='agent')}, ++ {'admitted': replace(self.admitted, pins=replace(self.admitted.pins, adapter=digest(b'wrong')))}): ++ with self.subTest(changes=changes), self.assertRaises(Refused): ++ a.build(**dict(self.kw, **changes)) ++ self.assertFalse(self.effects) ++ ++ def test_binary_or_source_drift_refused_before_tooling(self): ++ adapter = a.build(**self.kw) ++ self.binary.write_bytes(b'changed') ++ with self.assertRaises(Refused): ++ adapter.invoke(self.inv) ++ self.binary.write_bytes(b'synthetic checker binary') ++ with patch.object(a, 'source_digest', return_value=digest(b'changed')): ++ with self.assertRaises(Refused): ++ adapter.invoke(self.inv) ++ self.assertFalse(self.effects) ++ ++ def test_substituting_caller_selected_checker_under_same_descriptor_is_refused(self): ++ substitute = Path(self.temp.name) / 'other-checker' ++ substitute.write_bytes(b'caller-selected executable') ++ with self.assertRaisesRegex(Refused, 'descriptor pin differs'): ++ a.build(**dict(self.kw, check_config=substitute, ++ check_config_digest=digest(substitute.read_bytes()))) ++ self.assertFalse(self.effects) ++ ++ def test_stale_revision_refuses_mutation(self): ++ adapter = a.build(**self.kw) ++ self.secret['metadata']['resourceVersion'] = '8' ++ with self.assertRaises(Refused): ++ adapter.invoke(self.inv) ++ self.assertFalse(self.effects) ++ ++ def test_rollback_requires_exact_provenance_and_postcreation_revision(self): ++ adapter = a.build(**self.kw) ++ result = adapter.invoke(self.inv) ++ rollback = replace(self.inv, attempt_id='attempt-two', operation='rollback') ++ for created in ((), ('other',), ('keycape-client', 'other')): ++ with self.subTest(created=created), self.assertRaises(Refused): ++ adapter.rollback(rollback, created) ++ self.secret['metadata']['resourceVersion'] = '9' ++ with self.assertRaises(Refused): ++ adapter.rollback(rollback, result.created) ++ self.assertEqual(len(self.effects), 1) ++ ++ def test_missing_or_tampered_evidence_never_verifies_or_authorizes_undo(self): ++ adapter = a.build(**self.kw) ++ result = adapter.invoke(self.inv) ++ self.assertFalse(adapter.verify(replace(self.inv, attempt_id='other'), result)) ++ self.assertFalse(adapter.verify(self.inv, replace(result, revoked=False))) ++ self.evidence[result.evidence] = b'forged' ++ self.assertFalse(adapter.verify(self.inv, result)) ++ with self.assertRaises(Refused): ++ adapter.rollback(replace(self.inv, operation='rollback'), result.created) ++ self.assertEqual(len(self.effects), 1) ++ ++ def test_process_loss_does_not_reconstruct_created_authority(self): ++ result = a.build(**self.kw).invoke(self.inv) ++ fresh = a.build(**self.kw) ++ with self.assertRaises(Refused): ++ fresh.rollback(replace(self.inv, operation='rollback'), result.created) ++ self.assertIsNone(fresh.reconcile) ++ self.assertEqual(len(self.effects), 1) ++ ++ def test_failed_readback_is_uncertain_and_never_provides_creation(self): ++ original_kube = self.kube ++ def lost(args, payload=None): ++ if self.effects and args[:5] == ['-n', 'sso', 'get', 'secret', 'keycape-config']: ++ raise RuntimeError('synthetic sensitive detail') ++ return original_kube(args, payload) ++ with patch.object(a.helper.lane, 'kubectl', side_effect=lost): ++ adapter = a.build(**self.kw) ++ with self.assertRaisesRegex(Refused, '^owner callback failed$'): ++ adapter.invoke(self.inv) ++ self.assertEqual(len(self.effects), 1) ++ self.assertFalse(self.evidence) ++ self.assertEqual(self.revocations, [self.inv]) ++ ++ def test_revocation_or_publication_failure_cannot_produce_verified_success(self): ++ for capability in ('publish', 'revoke_session'): ++ self.secret, _ = f.fixture() ++ with self.subTest(capability=capability): ++ kw = dict(self.kw, **{capability: lambda *args: (_ for _ in ()).throw(RuntimeError('private'))}) ++ adapter = a.build(**kw) ++ with self.assertRaisesRegex(Refused, '^owner callback failed$'): ++ adapter.invoke(self.inv) ++ self.assertFalse(self.evidence) ++ ++ def kernel_fixture(self): ++ # Fixture-only proposed type/case; canonical files are never edited. ++ # Warden's published producer pins apply unchanged. Owner trust and ++ # proposed descriptor/type/case are fixture-only, never native admission. ++ import warden.case_inputs as inputs ++ from warden.case_run import Executor, Journal ++ root = ROOT.parents[2] ++ case = yaml.safe_load((root / 'security-cases/forgejo-netkingdom-oidc.yaml').read_bytes()) ++ case['spec']['status'] = 'materialized' ++ 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)} ++ type_source = yaml.safe_dump(typ).encode() ++ catalog = b'fixture-only-catalog' ++ sheet = json.dumps(['register-client', 'net-kingdom', 'forgejo', 'human', ++ 'founder', 'key-cape-oidc-login', 'fixture-acceptance', 'fixture-materialization', ++ 'fixture-descriptor', 'fixture-session'], separators=(',', ':')).encode() ++ pins = Pins(digest(case_source), digest(type_source), digest(self.desc_bytes), ++ digest(catalog), digest(b'fixture-acceptance'), digest(sheet)) ++ invocation = replace(self.admitted, pins=pins) ++ adapter = a.build(**dict(self.kw, admitted=invocation, case_source=case_source, ++ case_acceptance='fixture-acceptance')) ++ def fixture_verifier(purpose, source, evidence): ++ if purpose.startswith('session-revocation:'): ++ _, lane, operation, attempt = purpose.split(':') ++ return source == digest(case_source) and lane == invocation.lane and evidence == ( ++ 'session-revoked:' + operation + ':' + attempt) ++ return evidence in {'fixture-acceptance', 'fixture-materialization', ++ 'fixture-descriptor', 'fixture-session'} ++ executor = Executor(Journal(Path(self.temp.name) / 'private'), ++ {adapter.reference: adapter}, fixture_verifier, browser_inputs=inputs.BrowserInputs(root)) ++ admitted = executor.admit(case_source, type_source, self.desc_bytes, catalog, ++ principal='founder', actor='human', lane=invocation.lane, ++ acceptance_evidence='fixture-acceptance', materialization_evidence='fixture-materialization', ++ descriptor_evidence='fixture-descriptor', session_evidence='fixture-session') ++ self.assertEqual(admitted[1], invocation) ++ return executor, admitted ++ ++ def test_actual_kernel_admission_dispatch_and_rollback_with_fixture_only_trust(self): ++ executor, admitted = self.kernel_fixture() ++ result = executor.run(*admitted) ++ self.assertEqual(result.last.outcome, Outcome.SUCCESS) ++ self.assertEqual(result.last.created, ('keycape-client',)) ++ undone = executor.rollback(*admitted) ++ self.assertEqual(undone.last.outcome, Outcome.ROLLED_BACK) ++ self.assertEqual(len(self.effects), 2) ++ self.assertEqual(len(self.revocations), 2) ++ ++ def test_kernel_lost_readback_retains_uncertain_and_refuses_replay_and_undo(self): ++ executor, admitted = self.kernel_fixture() ++ original_kube = self.kube ++ def lost(args, payload=None): ++ if self.effects and args[:5] == ['-n', 'sso', 'get', 'secret', 'keycape-config']: ++ raise RuntimeError('synthetic sensitive detail') ++ return original_kube(args, payload) ++ with patch.object(a.helper.lane, 'kubectl', side_effect=lost): ++ result = executor.run(*admitted) ++ self.assertEqual(result.last.outcome, Outcome.UNCERTAIN) ++ self.assertFalse(result.last.created) ++ with self.assertRaises(Refused): ++ executor.run(*admitted, retry=True) ++ with self.assertRaises(Refused): ++ executor.rollback(*admitted) ++ self.assertEqual(len(self.effects), 1) ++ self.assertEqual(len(self.revocations), 1) ++ ++ def test_kernel_external_revision_change_retains_uncertain_rollback(self): ++ executor, admitted = self.kernel_fixture() ++ self.assertEqual(executor.run(*admitted).last.outcome, Outcome.SUCCESS) ++ self.secret['metadata']['resourceVersion'] = '9' ++ self.assertEqual(executor.rollback(*admitted).last.outcome, Outcome.ROLLBACK_UNCERTAIN) ++ self.assertEqual(len(self.effects), 1) ++ self.assertEqual(len(self.revocations), 2) ++ ++ ++if __name__ == '__main__': ++ unittest.main() +--- /dev/null ++++ b/sso-mfa/k8s/keycape/registration-adapter.md +@@ -0,0 +1,59 @@ ++# Attended registration adapter candidate ++ ++Exercise status: unexercised ++ ++This is a source-only, injection-only adapter for the existing Warden closed ++kernel. It installs no command, runtime dispatcher, credential provider, evidence ++trust root or new service. NetKingdom type/case status and bytes remain unchanged. ++The generated descriptor is a proposal; it is not owner acknowledgment. ++ ++`keycape-registration-adapter.py` wraps maintained `keycape-client.run` in apply ++mode. A fixed invocation binds principal, human actor, lane, case/type/descriptor/ ++catalog/acceptance/run-sheet digests and target. The descriptor pins this adapter ++and its complete local helper import closure together with the exact reviewed ++`check-config` executable digest. Changing that digest changes the descriptor ++source pin and descriptor bytes, requiring independently reviewed admission. ++The fixed executable is rehashed before each attempt. The caller constructs the ++proposed invocation from exact source inputs, installs the fixed adapter once, ++and calls normal `Executor.admit`; independently derived admission must match. ++No callable replacement after admission is required or supported by this recipe. ++ ++The operation uses existing cluster/issuer checks, byte-preserving merge, ++Deployment secret-env prerequisite, config validation, UID/resourceVersion CAS ++and whole-data readback. Only closed metadata is passed to the injected durable ++owner publisher. The owner authenticator must independently authenticate those ++exact bytes. The session-revocation capability must end the same attempt's ++parent session; Warden separately verifies its exact revocation evidence. ++These capabilities are owner-selected callables, never case-supplied commands. ++No implementation of these native capabilities is supplied or implied here. ++ ++An identical existing client returns no change and creates no rollback authority. ++Removal requires this process's independently authenticated successful creation ++receipt, exact created-object label, unchanged postcreation UID/resourceVersion, ++and the original exact client record. External drift, a lost readback, receipt ++publication/revocation failure or process loss requires owner reconciliation. ++No uncertainty is labeled retry-safe. No automatic reconciliation is installed. ++Rollback after restart intentionally remains unavailable until an owner supplies ++independently authenticated durable creation provenance and reviews recovery. ++ ++The source tests execute actual maintained helper CAS/readback against synthetic ++Kubernetes objects. Three checks exercise actual Warden admission/run/rollback ++and uncertainty transitions with synthetic owner trust. Warden d97652a's exact ++published producer source pins apply unchanged through actual BrowserInputs. ++This is a source compatibility fixture, not acknowledgment, owner provenance, ++normal native admission or deployment acceptance. ++ ++Remaining work belongs in live work records before T12 closure: ++ ++- Warden owns global evidence admission and exact native evidence/session ++ capabilities. Published d97652a refreshes BrowserInputs producer pins for ++ NetKingdom 015694ee; that source assent does not attest this new adapter. ++- NetKingdom owns separately reviewed descriptor/type pin selection, the env ++ binding/config/restart steps, and group ensure/revoke provenance. Current group ++ ensure does not report newly added memberships; current revoke can remove ++ memberships that predate the case. Wrapping these as reversible creation would ++ be unsound. Preserve them until a reviewed created-only contract exists. ++- Platform owns structural/mint/broker and secret delivery evidence; Apps owns ++ actual consumer login and group enforcement. No assent is supplied here. ++- Attended use still requires existing owner authorization, current caller ++ identity, native source/config/image pins, native receipts and cleanup proof. +--- a/workplans/NK-WP-0048-security-case-types.md ++++ b/workplans/NK-WP-0048-security-case-types.md +@@ -323,7 +323,7 @@ + ```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, MASON-WP-0010-T03, WARDEN-WP-0044-T03] ++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, 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" +@@ -631,17 +631,106 @@ + state_hub_task_id: "7ce4eee3-cd47-55a9-bfa5-4f1835d4d6fd" + ``` + +-Owner: NetKingdom. Next action: wrap the existing contained `keycape-client.py` +-registration/config/env and identity-provisioner ensure/revoke entry points in +-bounded owner adapters or receipt-only attended steps for Warden's closed kernel. +-Reuse the existing caller/session boundary, CAS/readback and status-only outputs; +-never acquire user-engine's runtime identity or expose registry/client secrets. +-Completion requires exact adapter/source descriptors, positive and refusal source +-checks, uncertainty/created-only rollback behavior and a separate owner review of +-the proposed machine seam. Source preparation can begin without credentials. +-Warden independently owns global provenance/admission integration; Platform owns +-mint/broker and native structural receipts; Apps owns consumer delivery/group +-acceptance. Their assent is not supplied by this task. Pinning descriptors into +-an acknowledged type remains a separately reviewed type-source change. Actual +-attended execution still requires its existing owner authorization and native +-receipts. T06 retains all native/reference lifecycle acceptance scope. ++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 ++``` ++ ++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. Exact root review/publication receipts are required on integration. ++ ++## T14 — Preserve created-only provenance for group ensure and revoke ++ ++```task ++id: NK-WP-0048-T14 ++status: todo ++priority: high ++``` ++ ++Owner: NetKingdom. Next action: extend or wrap the existing identity-provisioner ++ensure/revoke contract with independently retained prestate and exact newly ++created groups/memberships before exposing reversible owner callbacks. Current ++ensure returns no membership-creation inventory; current revoke may remove a ++membership that predated this case. Completion requires actual helper-backed ++positive/refusal fixtures, all validation before mutation, per-attempt evidence ++and revocation, readback, ambiguity handling, and rollback that preserves every ++pre-existing/shared group and membership. No existing endpoint or service token ++is treated as new authorization. ++ ++## T15 — Define authenticated recovery for lost registration process state ++ ++```task ++id: NK-WP-0048-T15 ++status: todo ++priority: high ++``` ++ ++Owner: NetKingdom for registration state, with Warden owning kernel evidence ++admission. Next action: select the exact existing owner evidence holder/readback ++contract and define authenticated lookup for the original creation attempt. ++Completion requires restart/lost-receipt fixtures that bind case/source/principal, ++original created-only provenance and current native revision before any undo; ++uncertain outcomes remain non-replayable. The T13 adapter deliberately refuses ++post-restart rollback and installs no reconciliation callback. A new broker or ++unverified JSON receipt is not a recovery contract. Actual evidence-holder ++acceptance and attended recovery remain separately authorized native work. ++ ++## T16 — Select and review the actual machine descriptor composition ++ ++```task ++id: NK-WP-0048-T16 ++status: todo ++priority: high ++``` ++ ++Owner: NetKingdom for descriptor/type source and exact checker artifact; Warden ++independently owns global provenance/admission. Next action: identify the exact ++reviewed checker executable artifact and native owner evidence/session callback ++contracts, then assemble the complete step descriptor set and a proposed explicit ++type version selection. T13's retained descriptor pins a synthetic checker only. ++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. diff --git a/docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/integrate_r3.py b/docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/integrate_r3.py new file mode 100644 index 00000000..c60e969d --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/integrate_r3.py @@ -0,0 +1,28 @@ +from pathlib import Path +import hashlib,json,subprocess +packet=Path(__file__).resolve().parent +owner=Path('/home/worsch/net-kingdom') +manifest=json.loads((packet/'manifest.json').read_text()) +review=json.loads((packet/'coordinator-review-r3.json').read_text()) +sha=lambda data:'sha256:'+hashlib.sha256(data).hexdigest() +if sha((packet/'manifest.json').read_bytes()) != review['manifest_sha256']:raise SystemExit('review manifest drift') +for rel,expected in manifest['postimages'].items(): + if sha((packet/'postimages'/rel).read_bytes()) != expected: raise SystemExit('postimage drift '+rel) + current=owner/rel + if rel in manifest['preimages']: + if sha(current.read_bytes()) != manifest['preimages'][rel]:raise SystemExit('preimage drift '+rel) + elif current.exists():raise SystemExit('unexpected existing path '+rel) +backup=packet/'publication-backup';backup.mkdir(exist_ok=False) +git=lambda *args:subprocess.check_output(['git','-C',str(owner),*args]) +(backup/'brief.worktree').write_bytes((owner/'.custodian-brief.md').read_bytes()) +(backup/'brief.index').write_bytes(git('show',':.custodian-brief.md')) +(backup/'index-record.txt').write_bytes(git('ls-files','--stage','.custodian-brief.md')) +(backup/'status.txt').write_bytes(git('status','--porcelain=v1')) +(backup/'work-records.preimage').write_bytes((owner/'WORK-RECORDS.md').read_bytes()) +for rel in manifest['postimages']: + data=(packet/'postimages'/rel).read_bytes() + if rel.startswith('workplans/'): + data=data.replace(b'Exact root review/publication receipts are required on integration.',b'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.') + dest=owner/rel;dest.parent.mkdir(parents=True,exist_ok=True);dest.write_bytes(data) +(packet/'integrated-source-hashes.json').write_text(json.dumps({rel:sha((owner/rel).read_bytes()) for rel in manifest['postimages']},indent=2)+'\n') +print('Integrated four reviewed source files; retained exact brief/index and work-record preimages.') diff --git a/docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/integrated-source-hashes.json b/docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/integrated-source-hashes.json new file mode 100644 index 00000000..65e73708 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/integrated-source-hashes.json @@ -0,0 +1,6 @@ +{ + "sso-mfa/k8s/keycape/keycape-registration-adapter.py": "sha256:8a6bd0ea111d67a0df89c7e3cd968e7bf81d29c2f2a70adf08eb747e1d1a5bdc", + "sso-mfa/k8s/keycape/test_keycape_registration_adapter.py": "sha256:beabe9fe3d2c43d296fcac475d1d0c97ebb9829debdf68846c7f32b15958641c", + "sso-mfa/k8s/keycape/registration-adapter.md": "sha256:5dc28ecc87d029d42a7bd5a02501ee2665b956e1201b8fceddb5854214d26bf8", + "workplans/NK-WP-0048-security-case-types.md": "sha256:c6ef142267f4232fe99d13edc9c9b02eb78ea89e2f3aff0bddf5436d09440e30" +} diff --git a/docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/normal-final.exit.json b/docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/normal-final.exit.json new file mode 100644 index 00000000..43623d14 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/normal-final.exit.json @@ -0,0 +1 @@ +{"exit_code": 0} diff --git a/docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/normal-final.stderr.txt b/docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/normal-final.stderr.txt new file mode 100644 index 00000000..3345563f --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/normal-final.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-16/browser-adapter-next/normal-final.stdout.json b/docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/normal-final.stdout.json new file mode 100644 index 00000000..1d9c544d --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/normal-final.stdout.json @@ -0,0 +1,321 @@ +{ + "repo_slug": "net-kingdom", + "repo_path": "/home/worsch/net-kingdom", + "source_revision": "0394484b924c82abaaa4b01716d84a0930cc4fdf", + "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-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-22", + "message": "Task description drift 'NK-WP-0048-T13': file content differs from DB (file wins)", + "file_path": "workplans/NK-WP-0048-security-case-types.md#NK-WP-0048-T13", + "db_id": "b264c10b-565c-5d29-b788-03cf5fbd2883", + "file_value": "Completed 2026-10-16, source implementation and synthetic fixture proof only.\nAdded an injection-only adapter around act", + "db_value": "Completed 2026-10-16, source implementation and synthetic fixture proof only.\nAdded an injection-only adapter around act", + "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-22 fixed: task b264c10b\u2026 description updated", + "brief: .custodian-brief.md updated", + "push: pushed" + ], + "summary": { + "fail": 0, + "automation_error": 0, + "warn": 25, + "info": 5 + }, + "result": "warn" +} diff --git a/docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/normal-sync.exit.json b/docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/normal-sync.exit.json new file mode 100644 index 00000000..43623d14 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/normal-sync.exit.json @@ -0,0 +1 @@ +{"exit_code": 0} diff --git a/docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/normal-sync.stderr.txt b/docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/normal-sync.stderr.txt new file mode 100644 index 00000000..3345563f --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/normal-sync.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-16/browser-adapter-next/normal-sync.stdout.json b/docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/normal-sync.stdout.json new file mode 100644 index 00000000..01d6cddd --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/normal-sync.stdout.json @@ -0,0 +1,427 @@ +{ + "repo_slug": "net-kingdom", + "repo_path": "/home/worsch/net-kingdom", + "source_revision": "0775c2bfa41af236301155bb5a69bbf088bdf823", + "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-20", + "message": "Missing DB dependency edge: task 'NK-WP-0048-T06' starts after task NK-WP-0048-T12", + "file_path": "workplans/NK-WP-0048-security-case-types.md#NK-WP-0048-T06", + "db_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "file_value": "NK-WP-0048-T12", + "db_value": "", + "fixable": true + }, + { + "severity": "WARN", + "check_id": "C-20", + "message": "Task dependency target 'NK-WP-0048-T13' of task 'NK-WP-0048-T06' is not linked to State Hub", + "file_path": "workplans/NK-WP-0048-security-case-types.md#NK-WP-0048-T06", + "db_id": "", + "file_value": "NK-WP-0048-T13", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-20", + "message": "Task dependency target 'NK-WP-0048-T14' of task 'NK-WP-0048-T06' is not linked to State Hub", + "file_path": "workplans/NK-WP-0048-security-case-types.md#NK-WP-0048-T06", + "db_id": "", + "file_value": "NK-WP-0048-T14", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-20", + "message": "Task dependency target 'NK-WP-0048-T15' of task 'NK-WP-0048-T06' is not linked to State Hub", + "file_path": "workplans/NK-WP-0048-security-case-types.md#NK-WP-0048-T06", + "db_id": "", + "file_value": "NK-WP-0048-T15", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-20", + "message": "Task dependency target 'NK-WP-0048-T16' of task 'NK-WP-0048-T06' is not linked to State Hub", + "file_path": "workplans/NK-WP-0048-security-case-types.md#NK-WP-0048-T06", + "db_id": "", + "file_value": "NK-WP-0048-T16", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-22", + "message": "Task description drift 'NK-WP-0048-T12': file content differs from DB (file wins)", + "file_path": "workplans/NK-WP-0048-security-case-types.md#NK-WP-0048-T12", + "db_id": "7ce4eee3-cd47-55a9-bfa5-4f1835d4d6fd", + "file_value": "Owner: NetKingdom. Retained scope: prepare bounded attended Deployment env,\nconfig verification and restart/readiness st", + "db_value": "Owner: NetKingdom. Next action: wrap the existing contained `keycape-client.py`\nregistration/config/env and identity-pro", + "fixable": true + }, + { + "severity": "WARN", + "check_id": "C-11", + "message": "Task 'NK-WP-0048-T13' has no state_hub_task_id", + "file_path": "workplans/NK-WP-0048-security-case-types.md#NK-WP-0048-T13", + "db_id": "", + "file_value": "", + "db_value": "", + "fixable": true + }, + { + "severity": "WARN", + "check_id": "C-11", + "message": "Task 'NK-WP-0048-T14' has no state_hub_task_id", + "file_path": "workplans/NK-WP-0048-security-case-types.md#NK-WP-0048-T14", + "db_id": "", + "file_value": "", + "db_value": "", + "fixable": true + }, + { + "severity": "WARN", + "check_id": "C-11", + "message": "Task 'NK-WP-0048-T15' has no state_hub_task_id", + "file_path": "workplans/NK-WP-0048-security-case-types.md#NK-WP-0048-T15", + "db_id": "", + "file_value": "", + "db_value": "", + "fixable": true + }, + { + "severity": "WARN", + "check_id": "C-11", + "message": "Task 'NK-WP-0048-T16' has no state_hub_task_id", + "file_path": "workplans/NK-WP-0048-security-case-types.md#NK-WP-0048-T16", + "db_id": "", + "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-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": "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": "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-33 fixed: regenerated WORK-RECORDS.md", + "C-20 fixed: dependency e31f3f9d\u2026 starts_after \u2192 7ce4eee3\u2026", + "C-22 fixed: task 7ce4eee3\u2026 description updated", + "C-11 fixed: task 'NK-WP-0048-T13' \u2192 b264c10b\u2026", + "C-11 fixed: task 'NK-WP-0048-T14' \u2192 fce95440\u2026", + "C-11 fixed: task 'NK-WP-0048-T15' \u2192 1b825705\u2026", + "C-11 fixed: task 'NK-WP-0048-T16' \u2192 200bf500\u2026", + "brief: .custodian-brief.md updated", + "push: pushed" + ], + "summary": { + "fail": 0, + "automation_error": 0, + "warn": 35, + "info": 5 + }, + "result": "warn" +} diff --git a/docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/normal-writeback.exit.json b/docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/normal-writeback.exit.json new file mode 100644 index 00000000..43623d14 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/normal-writeback.exit.json @@ -0,0 +1 @@ +{"exit_code": 0} diff --git a/docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/normal-writeback.stderr.txt b/docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/normal-writeback.stderr.txt new file mode 100644 index 00000000..3345563f --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/normal-writeback.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-16/browser-adapter-next/normal-writeback.stdout.json b/docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/normal-writeback.stdout.json new file mode 100644 index 00000000..fa2a40f2 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/normal-writeback.stdout.json @@ -0,0 +1,354 @@ +{ + "repo_slug": "net-kingdom", + "repo_path": "/home/worsch/net-kingdom", + "source_revision": null, + "source_revision_reason": "dirty_workplans", + "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-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-20", + "message": "Missing DB dependency edge: task 'NK-WP-0048-T06' starts after task NK-WP-0048-T13", + "file_path": "workplans/NK-WP-0048-security-case-types.md#NK-WP-0048-T06", + "db_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "file_value": "NK-WP-0048-T13", + "db_value": "", + "fixable": true + }, + { + "severity": "WARN", + "check_id": "C-20", + "message": "Missing DB dependency edge: task 'NK-WP-0048-T06' starts after task NK-WP-0048-T14", + "file_path": "workplans/NK-WP-0048-security-case-types.md#NK-WP-0048-T06", + "db_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "file_value": "NK-WP-0048-T14", + "db_value": "", + "fixable": true + }, + { + "severity": "WARN", + "check_id": "C-20", + "message": "Missing DB dependency edge: task 'NK-WP-0048-T06' starts after task NK-WP-0048-T15", + "file_path": "workplans/NK-WP-0048-security-case-types.md#NK-WP-0048-T06", + "db_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "file_value": "NK-WP-0048-T15", + "db_value": "", + "fixable": true + }, + { + "severity": "WARN", + "check_id": "C-20", + "message": "Missing DB dependency edge: task 'NK-WP-0048-T06' starts after task NK-WP-0048-T16", + "file_path": "workplans/NK-WP-0048-security-case-types.md#NK-WP-0048-T06", + "db_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "file_value": "NK-WP-0048-T16", + "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 '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 '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-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-20 fixed: dependency e31f3f9d\u2026 starts_after \u2192 b264c10b\u2026", + "C-20 fixed: dependency e31f3f9d\u2026 starts_after \u2192 fce95440\u2026", + "C-20 fixed: dependency e31f3f9d\u2026 starts_after \u2192 1b825705\u2026", + "C-20 fixed: dependency e31f3f9d\u2026 starts_after \u2192 200bf500\u2026", + "brief: .custodian-brief.md updated", + "push: pushed" + ], + "summary": { + "fail": 0, + "automation_error": 0, + "warn": 28, + "info": 5 + }, + "result": "warn" +} diff --git a/docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/owner-progress.json b/docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/owner-progress.json new file mode 100644 index 00000000..99f3412b --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/owner-progress.json @@ -0,0 +1,14 @@ +{ + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "id": "500aa2a3-21ca-4bb5-93ab-278f24fcaf32", + "topic_id": null, + "task_id": "b264c10b-565c-5d29-b788-03cf5fbd2883", + "decision_id": null, + "event_type": "note", + "summary": "NetKingdom reviewed registration adapter source published as 0775c2bfa41af236301155bb5a69bbf088bdf823; source task/projection receipt 0394484b924c82abaaa4b01716d84a0930cc4fdf. 36 fixtures pass with canonical Warden d97652a producer pins. T13 source done; T12/T14/T15/T16 remain todo and T06 native acceptance remains wait. R1 checker-binding refusal preserved, corrected R2/R3. Both generated-only sync hook refusals and exact staged/working brief preservation retained; no credentials or native action.", + "detail": null, + "author": "codex", + "session_id": null, + "created_at": "2026-10-16T19:53:37.620699Z", + "workstream_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" +} diff --git a/docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/postimages/sso-mfa/k8s/keycape/keycape-registration-adapter.py b/docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/postimages/sso-mfa/k8s/keycape/keycape-registration-adapter.py new file mode 100644 index 00000000..995dd263 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/postimages/sso-mfa/k8s/keycape/keycape-registration-adapter.py @@ -0,0 +1,180 @@ +"""Unexercised, injection-only attended registration seam for Warden's v1 kernel. + +No CLI, credentials, evidence trust root or live registration is installed here. +Owner publication/authentication and parent-session revocation are mandatory +injected capabilities. The kernel must independently admit both evidence kinds. +""" +from __future__ import annotations + +import argparse +from dataclasses import asdict, replace +import json +import importlib.util +from pathlib import Path + +import yaml + +from warden.case_run import Adapter, Descriptor, Invocation, OwnerResult, contained_output, digest, IDENTIFIER, DIGEST +from warden.case_receipt_state import Outcome, Refused + +ROOT = Path(__file__).resolve().parent +SOURCE_FILES = ('keycape-registration-adapter.py', 'keycape-client.py', + 'portal-client-rollout.py', 'openbao-client-config.py', + 'register-user-engine-portal.py') +REFERENCE = 'net-kingdom/keycape-registration@v1' + +# Importing the maintained helper performs no tooling operation. +_spec = importlib.util.spec_from_file_location('registration_owner_helper', ROOT / 'keycape-client.py') +helper = importlib.util.module_from_spec(_spec) +_spec.loader.exec_module(helper) + + +def source_digest(check_config_digest): + """Pin adapter plus the complete local helper import closure, in filename order.""" + if not isinstance(check_config_digest, str) or not DIGEST.fullmatch(check_config_digest): + raise Refused('exact reviewed checker digest required') + composition = {'files': {name: digest((ROOT / name).read_bytes()) for name in SOURCE_FILES}, + 'check_config_binary': check_config_digest} + return digest(json.dumps(composition, sort_keys=True, separators=(',', ':')).encode()) + + +def descriptor(check_config_digest): + return Descriptor('warden.case.execution.v1', 'register-client', 'net-kingdom', + REFERENCE, source_digest(check_config_digest), 'human', 'key-cape-oidc-login', + 'client_id', 'change', ('keycape-client',), False, + ('parent-session',)) + + +def build(*, admitted: Invocation, case_source: bytes, expected_uid: str, + expected_resource_version: str, case_acceptance: str, + check_config: Path, check_config_digest: str, publish, authenticate, + revoke_session): + """Bind one admitted case and observed revision; return a closed kernel Adapter. + + publish(bytes)->identifier durably retains only the closed metadata payload. + authenticate(identifier, bytes)->True must verify independent owner custody. + revoke_session(invocation)->identifier must revoke this attempt's parent + session; Warden independently authenticates it using its own evidence verifier. + These callbacks are capabilities, never command strings or case parameters. + + Only a verified creation in this process may be rolled back. Process loss, + write/readback ambiguity, publication/revocation failure and source drift + require owner reconciliation; none are converted to retry-safe failures. + """ + reviewed_source = source_digest(check_config_digest) + desc = descriptor(check_config_digest) + if type(admitted) is not Invocation or not all( + isinstance(value, str) and IDENTIFIER.fullmatch(value) + for value in (admitted.step, admitted.owner, admitted.target, + admitted.actor, admitted.principal, admitted.lane)): + raise Refused('bounded invocation required') + if desc.adapter_source != reviewed_source: + raise Refused('owner source changed during binding') + if (admitted.step, admitted.owner, admitted.actor, admitted.lane) != ( + desc.step, desc.owner, desc.actor, desc.lane): + raise Refused('registration invocation boundary differs') + if admitted.operation != 'admission' or admitted.attempt_id: + raise Refused('expected admission invocation') + if type(case_source) is not bytes or len(case_source) > 262144 or digest(case_source) != admitted.pins.case: + raise Refused('case source pin differs') + # Pins.adapter is the descriptor digest, not the owner source digest. + descriptor_bytes = json.dumps(asdict(desc), sort_keys=True, separators=(',', ':')).encode() + if admitted.pins.adapter != digest(descriptor_bytes): + raise Refused('descriptor pin differs') + if not all(isinstance(x, str) and 0 < len(x) <= 192 for x in + (expected_uid, expected_resource_version, case_acceptance)): + raise Refused('bounded attended binding required') + if digest(case_acceptance.encode()) != admitted.pins.acceptance: + raise Refused('acceptance evidence binding differs') + if not all(callable(x) for x in (publish, authenticate, revoke_session)): + raise Refused('owner evidence and revocation capabilities required') + with contained_output(): + case = yaml.safe_load(case_source) + client = helper.client_from_case(case) + if admitted.target != client['clientId']: + raise Refused('case target differs') + check_config = Path(check_config) + records = {} + creation = None + + def binding(inv, operation): + if (replace(inv, attempt_id='', operation='admission') != admitted + or inv.operation != operation or not IDENTIFIER.fullmatch(inv.attempt_id)): + raise Refused('attempt binding differs') + if source_digest(check_config_digest) != reviewed_source: + raise Refused('owner source changed') + if digest(check_config.read_bytes()) != check_config_digest: + raise Refused('check-config binary changed') + + def execute(inv, operation, created=()): + nonlocal creation + try: + with contained_output(): + binding(inv, 'run' if operation == 'add' else 'rollback') + uid, revision = expected_uid, expected_resource_version + if operation == 'remove': + if (created != ('keycape-client',) or creation is None + or not verify(*creation)): + raise Refused('no authenticated case-created client') + original_payload = json.loads(records[creation[1].evidence][1]) + uid, revision = original_payload['after']['uid'], original_payload['after']['resource_version'] + args = argparse.Namespace(mode='apply', operation=operation, + expected_uid=uid, expected_resource_version=revision, + case_acceptance=case_acceptance, check_config=str(check_config)) + receipt = helper.run(args, client) + # The helper has completed CAS, whole-data readback and preservation. + # Never retain check-config diagnostics or arbitrary helper fields. + if not (receipt['schema'] == 'netkingdom.keycape-client.v1' + and receipt['target'] == 'sso/keycape-config' + and receipt['client_id'] == admitted.target + and receipt['mode'] == 'apply' and receipt['operation'] == operation + and receipt['values_emitted'] is False + and type(receipt['changed']) is bool + and receipt['unrelated_config_bytes_preserved'] is True + and receipt['other_secret_data_unchanged'] is True + and receipt['before'] == {'uid': uid, 'resource_version': revision} + and receipt['after']['uid'] == uid + and receipt['check_config']['proposed']['valid'] is True): + raise Refused('owner readback contract differs') + after = receipt['after'] + if (set(after) != {'uid', 'resource_version'} or + not all(isinstance(v, str) and 0 < len(v) <= 192 for v in after.values())): + raise Refused('owner revision contract differs') + changed = receipt['changed'] + outcome = (Outcome.ROLLED_BACK if operation == 'remove' else + Outcome.SUCCESS if changed else Outcome.NO_CHANGE) + objects = ('keycape-client',) if operation == 'add' and changed else () + finally: + # Even helper failure must end the parent session. Failures propagate + # contained to the kernel and become uncertain, never safe to replay. + with contained_output(): + revocation = revoke_session(inv) + if not isinstance(revocation, str) or not IDENTIFIER.fullmatch(revocation): + raise Refused('session revocation evidence missing') + payload = json.dumps({'schema': 'netkingdom.keycape-registration.attempt.v1', + 'invocation': asdict(inv), 'source': reviewed_source, + 'outcome': outcome.value, 'created': objects, 'before': receipt['before'], + 'after': after, 'session_revocation': revocation}, + sort_keys=True, separators=(',', ':')).encode() + with contained_output(): + evidence = publish(payload) + if not isinstance(evidence, str) or not IDENTIFIER.fullmatch(evidence) or evidence in records: + raise Refused('owner evidence identifier invalid or reused') + result = OwnerResult(outcome, evidence, objects, True, revocation, + attempt_id=inv.attempt_id, operation=inv.operation) + records[evidence] = (inv, payload, result) + if operation == 'add' and changed: + creation = (inv, result) + elif operation == 'remove': + creation = None + return result + + def verify(inv, result): + record = records.get(result.evidence) if type(result) is OwnerResult else None + if record is None or record[0] != inv or record[2] != result: + return False + with contained_output(): + return authenticate(result.evidence, record[1]) is True + + return Adapter(REFERENCE, reviewed_source, lambda inv: execute(inv, 'add'), + verify, lambda inv, created: execute(inv, 'remove', created)) diff --git a/docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/postimages/sso-mfa/k8s/keycape/registration-adapter.md b/docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/postimages/sso-mfa/k8s/keycape/registration-adapter.md new file mode 100644 index 00000000..1ace7940 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/postimages/sso-mfa/k8s/keycape/registration-adapter.md @@ -0,0 +1,59 @@ +# Attended registration adapter candidate + +Exercise status: unexercised + +This is a source-only, injection-only adapter for the existing Warden closed +kernel. It installs no command, runtime dispatcher, credential provider, evidence +trust root or new service. NetKingdom type/case status and bytes remain unchanged. +The generated descriptor is a proposal; it is not owner acknowledgment. + +`keycape-registration-adapter.py` wraps maintained `keycape-client.run` in apply +mode. A fixed invocation binds principal, human actor, lane, case/type/descriptor/ +catalog/acceptance/run-sheet digests and target. The descriptor pins this adapter +and its complete local helper import closure together with the exact reviewed +`check-config` executable digest. Changing that digest changes the descriptor +source pin and descriptor bytes, requiring independently reviewed admission. +The fixed executable is rehashed before each attempt. The caller constructs the +proposed invocation from exact source inputs, installs the fixed adapter once, +and calls normal `Executor.admit`; independently derived admission must match. +No callable replacement after admission is required or supported by this recipe. + +The operation uses existing cluster/issuer checks, byte-preserving merge, +Deployment secret-env prerequisite, config validation, UID/resourceVersion CAS +and whole-data readback. Only closed metadata is passed to the injected durable +owner publisher. The owner authenticator must independently authenticate those +exact bytes. The session-revocation capability must end the same attempt's +parent session; Warden separately verifies its exact revocation evidence. +These capabilities are owner-selected callables, never case-supplied commands. +No implementation of these native capabilities is supplied or implied here. + +An identical existing client returns no change and creates no rollback authority. +Removal requires this process's independently authenticated successful creation +receipt, exact created-object label, unchanged postcreation UID/resourceVersion, +and the original exact client record. External drift, a lost readback, receipt +publication/revocation failure or process loss requires owner reconciliation. +No uncertainty is labeled retry-safe. No automatic reconciliation is installed. +Rollback after restart intentionally remains unavailable until an owner supplies +independently authenticated durable creation provenance and reviews recovery. + +The source tests execute actual maintained helper CAS/readback against synthetic +Kubernetes objects. Three checks exercise actual Warden admission/run/rollback +and uncertainty transitions with synthetic owner trust. Warden d97652a's exact +published producer source pins apply unchanged through actual BrowserInputs. +This is a source compatibility fixture, not acknowledgment, owner provenance, +normal native admission or deployment acceptance. + +Remaining work belongs in live work records before T12 closure: + +- Warden owns global evidence admission and exact native evidence/session + capabilities. Published d97652a refreshes BrowserInputs producer pins for + NetKingdom 015694ee; that source assent does not attest this new adapter. +- NetKingdom owns separately reviewed descriptor/type pin selection, the env + binding/config/restart steps, and group ensure/revoke provenance. Current group + ensure does not report newly added memberships; current revoke can remove + memberships that predate the case. Wrapping these as reversible creation would + be unsound. Preserve them until a reviewed created-only contract exists. +- Platform owns structural/mint/broker and secret delivery evidence; Apps owns + actual consumer login and group enforcement. No assent is supplied here. +- Attended use still requires existing owner authorization, current caller + identity, native source/config/image pins, native receipts and cleanup proof.