docs: preserve reviewed NetKingdom session artifacts
This commit is contained in:
parent
3add9f8790
commit
b53eed4073
43 changed files with 4653 additions and 0 deletions
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
@ -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.
|
||||
|
|
@ -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)"
|
||||
}
|
||||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
@ -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)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"existing_producer_signature_verified": true, "independent_synthetic_public_fixture": true, "returned_record_canonical_bytes_match": true, "new_signature_implementation": false}
|
||||
|
|
@ -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"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"scope": "synthetic disposable fixture only; no native key admission",
|
||||
"kid": "synthetic-v2",
|
||||
"public_key": "Zs1gi5KLiOUODv6qM_rxxDzv4HKUsLh-n-CrpqPPdjM"
|
||||
}
|
||||
|
|
@ -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()
|
||||
|
|
@ -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
|
||||
+}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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"
|
||||
}
|
||||
|
|
@ -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.
|
||||
|
|
@ -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"
|
||||
}
|
||||
|
|
@ -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.
|
||||
|
|
@ -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.
|
||||
|
|
@ -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"
|
||||
}
|
||||
|
|
@ -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.
|
||||
|
|
@ -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).
|
||||
|
|
@ -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.
|
||||
|
|
@ -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.
|
||||
|
|
@ -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"
|
||||
]
|
||||
}
|
||||
|
|
@ -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)"
|
||||
}
|
||||
|
|
@ -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"
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
@ -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.
|
||||
|
|
@ -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')}))
|
||||
|
|
@ -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.
|
||||
|
|
@ -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.')
|
||||
|
|
@ -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"
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"exit_code": 0}
|
||||
|
|
@ -0,0 +1 @@
|
|||
WARN: git commit failed for writeback: Generated-only commits rejected: staged
|
||||
|
|
@ -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"
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"exit_code": 0}
|
||||
|
|
@ -0,0 +1 @@
|
|||
WARN: git commit failed for writeback: Generated-only commits rejected: staged
|
||||
|
|
@ -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"
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"exit_code": 0}
|
||||
|
|
@ -0,0 +1 @@
|
|||
WARN: git commit failed for writeback: Generated-only commits rejected: staged
|
||||
|
|
@ -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"
|
||||
}
|
||||
|
|
@ -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"
|
||||
}
|
||||
|
|
@ -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))
|
||||
|
|
@ -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.
|
||||
Loading…
Add table
Add a link
Reference in a new issue