From bf6a44db6da558bdde1fb13692dc59eb9b997f42 Mon Sep 17 00:00:00 2001 From: tegwick Date: Sun, 11 Oct 2026 17:30:10 +0200 Subject: [PATCH] Bound JWT private input and gate native effects on public health Assistant: codex Assistant-Model: gpt-6.1-sol Assistant-Session: 01a126e1-76f8-7643-8cf3-e93336539ade --- ...-11-key39-private-producer-net-review.json | 64 ++++++ ...2026-10-11-key39-v6-net-source-review.json | 56 +++++ .../2026-10-11-key39-v6-net-work-record.json | 9 + ...2026-10-11-key39-v7-net-source-review.json | 74 +++++++ .../2026-10-11-key39-v7-net-work-record.json | 9 + ...2026-10-11-key39-v8-net-source-review.json | 82 ++++++++ .../2026-10-11-key39-v8-net-work-record.json | 9 + .../keycape/openbao-negative-node-route.md | 129 ++++++++++++ .../k8s/keycape/openbao-negative-session.py | 112 +++++++++- .../keycape/test_openbao_negative_session.py | 2 + .../k8s/keycape/test_openbao_node_route.py | 198 ++++++++++++++++++ .../k8s/keycape/test_openbao_private_input.py | 111 ++++++++++ workplans/ADHOC-2026-10-11.md | 57 +++++ 13 files changed, 903 insertions(+), 9 deletions(-) create mode 100644 docs/reviews/2026-10-11-key39-private-producer-net-review.json create mode 100644 docs/reviews/2026-10-11-key39-v6-net-source-review.json create mode 100644 docs/reviews/2026-10-11-key39-v6-net-work-record.json create mode 100644 docs/reviews/2026-10-11-key39-v7-net-source-review.json create mode 100644 docs/reviews/2026-10-11-key39-v7-net-work-record.json create mode 100644 docs/reviews/2026-10-11-key39-v8-net-source-review.json create mode 100644 docs/reviews/2026-10-11-key39-v8-net-work-record.json create mode 100644 sso-mfa/k8s/keycape/openbao-negative-node-route.md create mode 100644 sso-mfa/k8s/keycape/test_openbao_node_route.py create mode 100644 sso-mfa/k8s/keycape/test_openbao_private_input.py diff --git a/docs/reviews/2026-10-11-key39-private-producer-net-review.json b/docs/reviews/2026-10-11-key39-private-producer-net-review.json new file mode 100644 index 0000000..752ba6c --- /dev/null +++ b/docs/reviews/2026-10-11-key39-private-producer-net-review.json @@ -0,0 +1,64 @@ +{ + "reviewer": "net-kingdom-owner (agent net-kingdom, codex)", + "decision": "changes_requested", + "review_id": "NK-REVIEW-20261011-KEY39-PRIVATE-PRODUCER-CONSUMER", + "work_item_id": "NK-WP-ADHOC-2026-10-11-T07", + "reviewed_proposal_id": "KEY-WP-0039-T02/private-producer-handoff-v1", + "reviewed_manifest_sha256": "239aaac8541ba05e8bba5f9ecdc8b555a02fd9d0ad1762fdfc4d4bae7e4ef463", + "reviewed_adapter_sha256": "78108d7ab9573e7b284c0276a7bb01b5dbe61400bdb5856f112b04d879ea9109", + "consumer_lifecycle_v7_manifest_sha256": "90aabec336008d7d51e37c04b5ff96ba8ed6630821cca1a4c333d803367247f7", + "comment": "NetKingdom INTENT Principles 6/7 require bounded exposure and explicit caller/owner responsibility. Completed same-trial NK-WP-ADHOC-2026-10-11-T07 reviews exact Key private-producer manifest 239aaac8541ba05e8bba5f9ecdc8b555a02fd9d0ad1762fdfc4d4bae7e4ef463 only as NetKingdom native consumer. Private bytes must not enter the remote pipe before that exact child acknowledges its source/authority/public-health gate. Changes requested below; no KeyCape formal owner review, authenticated JWT proof, purpose reuse/custody admission, launcher authority or native readiness supplied.", + "findings": [ + { + "id": "NK-PRODUCER-V1-F01", + "severity": "high", + "location": "openbao_negative_private_handoff.py:deliver and decision packet launcher sequence", + "counterexample": "Calling deliver with two fixture files and a pipe succeeds and unlinks files with no child or preflight acknowledgment instantiated. V7 checks health before reading stdin, but a producer can fill/transmit the pipe before that check runs; there is no existing exact-child acknowledgment protocol.", + "requested_change": "Provide a reviewed bounded child-ready handshake binding exact consumer proposal/source/envelope/window/health result and pipe session before reading/consuming caller token files or writing ANY token bytes. No ready or failed/expired/mismatched ready must mean zero private bytes and retained caller files. Update V5-only packet references to exact compatible consumer revision and keep launcher/admission ungranted." + }, + { + "id": "NK-PRODUCER-V1-F02", + "severity": "medium", + "location": "openbao_negative_private_handoff.py:pipe", + "counterexample": "An actual duplicate FD>=3 of stdout when stdout is a pipe passes validation and carries both synthetic tokens; descriptor-number restriction does not establish a distinct private endpoint.", + "requested_change": "Reject aliases of stdin/stdout/stderr using device/inode identity and validate any declared private data/control endpoint identities. Add actual duplicate-stdout/stderr pipe regressions; retain explicit trusted-peer/launcher boundary." + }, + { + "id": "NK-PRODUCER-V1-F03", + "severity": "medium", + "location": "openbao_negative_private_handoff.py:consume and exact-inode cleanup wording", + "counterexample": "Replacing human.json after os.stat passes but before os.unlink deletes the replacement and deliver reports owner_files_consumed=True. The two syscalls are not an atomic inode-conditioned delete.", + "requested_change": "Use a reviewed transactional file-capture/cleanup strategy that preserves unexpected replacements, or explicitly qualify the guarantee as dependent on continuously exclusive same-UID directory custody with no concurrent writers and remove precise-inode/atomic implications. Retain the stat/unlink race regression and conservative cleanup evidence; do not claim secure zeroization." + } + ], + "validation": { + "supplied_tests": 18, + "independent_tests": 5, + "all_passed": true, + "counterexamples_reproduced": true, + "real_partial_pipe_timeout_verified": true, + "source_pins_matched": true + }, + "coverage": [ + "Bounded actual 4096-byte pipe capacity causes real partial delivery timeout; receipt unknown and not confirmed; both caller files consumed before partial write", + "Mode0700/0600, ownership, no symlinks/hardlinks/Git, bounded stable-file read and exact two-file identity checks inspected", + "stdout/stderr numeric rejection is present but pipe aliases defeat intended output boundary", + "Fixed error/boolean receipts; signal restoration and EOF/FD closure inspected", + "Packet explicitly separates owner choices/current registration/MFA/custody/launcher admission and leaves readiness false", + "Decoded JWT payload shape/lifetime checks are not authentication; synthetic unsigned shapes accepted without native acceptance", + "No exact-child ready handshake exists; V7 pre-input health cannot stop producer-side pipe buffering/transport before consumer reads" + ], + "authority_boundary": { + "source_review_only": true, + "native_actions": false, + "owner_checkout_writes": false, + "credential_access": false, + "secret_reads": false, + "foreign_reviews": false, + "KeyCape_owner_review": false, + "founder_approval": false, + "native_acceptance": false, + "KEY39_task_closure": false + }, + "work_record_anchor_sha256": "8bd81ce921d66c373d5a17c4e2188d7203597d86f8aec2c1318dbdec5a3c1542" +} diff --git a/docs/reviews/2026-10-11-key39-v6-net-source-review.json b/docs/reviews/2026-10-11-key39-v6-net-source-review.json new file mode 100644 index 0000000..bd7b999 --- /dev/null +++ b/docs/reviews/2026-10-11-key39-v6-net-source-review.json @@ -0,0 +1,56 @@ +{ + "reviewer": "net-kingdom-owner (agent net-kingdom, codex)", + "decision": "approved", + "review_id": "NK-REVIEW-20261011-KEY39-NODE-ROUTE-SOURCE", + "work_item_id": "NK-WP-ADHOC-2026-10-11-T07", + "reviewed_manifest_sha256": "23a972abc5175e121e594726d2b7f7a28c62109253f58f12623eaf2e5564a035", + "reviewed_proposal_id": "KEY-WP-0039-T02/attended-lifecycle-v6-node-route", + "reviewed_source_hashes": { + "sso-mfa/k8s/keycape/openbao-negative-session.py": "11df42c873ee7ab4cf4a42fa2326ada4661703632b07745220e75a61f7c7fadf", + "sso-mfa/k8s/keycape/test_openbao_negative_session.py": "01e23f2b4fc77824a161a57acb3a52d19aad846b3b6c0fd3abd81afa1dc2985c", + "sso-mfa/k8s/keycape/test_openbao_node_route.py": "00e370b0c3892e16939b7e34ee070df7fe6183b8037741a33b7a6f2814847a9d", + "sso-mfa/k8s/keycape/openbao-negative-node-route.md": "b1184d349ad8166804253edeb7932b82d1bd30eb5fe46fcc5bbda8f88593eb9d" + }, + "previous_review_sha256": "32a62bd1ca1bd7311965d016d4d6a6e4d9177f3423c63add436d9d309865eacc", + "comment": "NetKingdom INTENT Principles 6/7 require bounded exposure and explicit owner responsibility. Exact node-route successor manifest 23a972abc5175e121e594726d2b7f7a28c62109253f58f12623eaf2e5564a035 prepared and reviewed in NetKingdom under completed same-trial NK-WP-ADHOC-2026-10-11-T07. Source agreement for public health readiness and bounded founder-held route design; this is authored owner source preparation, not a substitute for fresh independent Platform/KeyCape source acknowledgments or founder native authority. Prior v5 stays unchanged and live route availability is unproved.", + "findings": [], + "validation": { + "supplied_lifecycle": 46, + "new_node_route": 8, + "retained_independent": 21, + "unchanged_runner": 17, + "all_passed": true, + "synthetic_offline_only": true + }, + "coverage": [ + "Workstation bridge configuration routing metadata and actual -L implementation distinguish node/workstation listener", + "No node Pod DNS assumption; bounded founder foreground node-local gateway Service port-forward proposal and process-specific teardown", + "Public unauth GET only; strict active initialized/unsealed body and digest-only cluster identity evidence", + "Execute initial route refusal before native/Secret access; repeated route loss after dry-runs before first mutation", + "Separate recovery route gate before native/Secret reads; health identity must match exact envelope pin", + "No auth headers, private stdin or Kubernetes in public route-preflight mode; current-process diagnostic does not attest node placement", + "HTTP health shares all existing no-proxy/no-redirect/byte/total-timer/abort guards; slow-health real timer refusal", + "All earlier CAS/deadline/deletion/recovery/known-revoke/unknown-outcome regressions remain" + ], + "native_prerequisites": [ + "Fresh exact source reviews by Platform and KeyCape", + "Founder exact source/runtime/route/window/cleanup decision", + "Actual same-node route preflight with owner-admitted route and independently confirmed provider identity", + "Exact envelope bao_health_identity_sha256 from current founder preflight; fresh owner role/policy/leeway observation", + "Admitted private human/coding-agent token producer remains separate gap", + "Current headroom/quiet window and actual native negative/revoke/restoration/cleanup acceptance" + ], + "authority_boundary": { + "source_preparation_only": true, + "native_actions": false, + "tunnel_opened": false, + "node_reachability_observed": false, + "owner_checkout_writes": false, + "credential_access": false, + "secret_reads": false, + "foreign_reviews": false, + "founder_approval": false, + "KEY39_task_closure": false + }, + "work_record_anchor_sha256": "10af5b4bdc5392b7d6b1c941b921171ba52734099a2a20203b7fe2aa604882ec" +} diff --git a/docs/reviews/2026-10-11-key39-v6-net-work-record.json b/docs/reviews/2026-10-11-key39-v6-net-work-record.json new file mode 100644 index 0000000..f709ce7 --- /dev/null +++ b/docs/reviews/2026-10-11-key39-v6-net-work-record.json @@ -0,0 +1,9 @@ +{ + "repo": "net-kingdom", + "work_item_id": "NK-WP-ADHOC-2026-10-11-T07", + "original_path": "/home/worsch/net-kingdom/workplans/ADHOC-2026-10-11.md", + "original_sha256": "5a6b046d08a3a2e98b2e1e71520bb2c6e7b0969635dd830a6b4dc6c2f7ab122f", + "staged_path": "/tmp/keycape-jwt-node-route-stage/net-kingdom/workplans/ADHOC-2026-10-11.md", + "staged_sha256": "3efc3ec5365431ad165955f6cb0af76dedc3469606cd4b1ca0f0de4063bfefda", + "same_trial": true +} diff --git a/docs/reviews/2026-10-11-key39-v7-net-source-review.json b/docs/reviews/2026-10-11-key39-v7-net-source-review.json new file mode 100644 index 0000000..1015a46 --- /dev/null +++ b/docs/reviews/2026-10-11-key39-v7-net-source-review.json @@ -0,0 +1,74 @@ +{ + "reviewer": "net-kingdom-owner (agent net-kingdom, codex)", + "decision": "approved", + "review_id": "NK-REVIEW-20261011-KEY39-NODE-ROUTE-INPUT-SOURCE", + "work_item_id": "NK-WP-ADHOC-2026-10-11-T07", + "reviewed_manifest_sha256": "90aabec336008d7d51e37c04b5ff96ba8ed6630821cca1a4c333d803367247f7", + "reviewed_proposal_id": "KEY-WP-0039-T02/attended-lifecycle-v7-node-route-input", + "reviewed_source_hashes": { + "sso-mfa/k8s/keycape/openbao-negative-session.py": "4c90edb541b5ea2ff131c3e674c80197c358c2f81140187028e57de0ac8dbf81", + "sso-mfa/k8s/keycape/test_openbao_negative_session.py": "01e23f2b4fc77824a161a57acb3a52d19aad846b3b6c0fd3abd81afa1dc2985c", + "sso-mfa/k8s/keycape/test_openbao_node_route.py": "02a715dcc028d0cacbf3b0ffdf705758438717f064e290b283f8e773540dd2d7", + "sso-mfa/k8s/keycape/openbao-negative-node-route.md": "ff3604380a616dfaa86cd9aaa1f131136ee4fc751397a1ebda3a8d737e3c5346" + }, + "previous_review_sha256": "d011b8945dab70b4d7965baea826b8983993e8b8c04ca72d43f52b7c039e84ce", + "comment": "NetKingdom INTENT Principles 6/7 require bounded exposure and explicit owner responsibility. Exact v7 manifest 90aabec336008d7d51e37c04b5ff96ba8ed6630821cca1a4c333d803367247f7 prepared/reviewed under completed same-trial NK-WP-ADHOC-2026-10-11-T07. Corrects prior v6 documentation overstatement by gating actual main private-input consumption on source/approval/public-health readiness and preserving the original execution deadline. Authored owner source preparation only; fresh independent KeyCape/Platform exact reviews and founder native authority remain required.", + "findings": [], + "validation": { + "supplied_lifecycle_tests": 46, + "node_route_and_actual_main_tests": 12, + "retained_independent_tests": 21, + "unchanged_runner_tests_previously_verified": 17, + "all_passed": true, + "synthetic_offline_only": true, + "new_main_checks": [ + "failed public health leaves private stdin unread in execute/recover", + "missing approved health pin leaves stdin unread", + "successful public health occurs before stdin.read; execution receives already-started window", + "expired preinput monotonic window refuses before runner/native access and cannot renew" + ], + "pre_freeze_refinements": [ + "Initial test fixture accidentally included absent synthetic runner source pin; corrected fixture to exact local source envelope.", + "New actual preinput budget test caught missing global CLEANING declaration after window initialization refactor; fixed before source freeze. All final suites pass." + ], + "original_checkout_source_pins": { + "sso-mfa/k8s/keycape/openbao-negative-session.py": "3bcceebc845b17492040c76223d77088257c547a2b890198e7b5c2b04a0cd360", + "sso-mfa/k8s/keycape/test_openbao_negative_session.py": "a428a4a5ea60bd6c4ad10481e133227ad696a4a08653bf39b554b47431fb9f8c" + } + }, + "coverage": [ + "Workstation bridge configuration routing metadata and actual -L implementation distinguish node/workstation listener", + "No node Pod DNS assumption; bounded founder foreground node-local gateway Service port-forward proposal and process-specific teardown", + "Public unauth GET only; strict active initialized/unsealed body and digest-only cluster identity evidence", + "Execute initial route refusal before native/Secret access; repeated route loss after dry-runs before first mutation", + "Separate recovery route gate before native/Secret reads; health identity must match exact envelope pin", + "No auth headers, private stdin or Kubernetes in public route-preflight mode; current-process diagnostic does not attest node placement", + "HTTP health shares all existing no-proxy/no-redirect/byte/total-timer/abort guards; slow-health real timer refusal", + "All earlier CAS/deadline/deletion/recovery/known-revoke/unknown-outcome regressions remain", + "Actual main failure tests, not only execute-function mocks: unavailable route and missing health pin prevent stdin.read; recover also consumes no stdin", + "Actual main success ordering verifies health before stdin before execution; original pre-input monotonic window is preserved", + "Source/role/authority revalidation follows private input; expired original monotonic window refuses before runner/native access" + ], + "native_prerequisites": [ + "Fresh exact source reviews by Platform and KeyCape", + "Founder exact source/runtime/route/window/cleanup decision", + "Actual same-node route preflight with owner-admitted route and independently confirmed provider identity", + "Exact envelope bao_health_identity_sha256 from current founder preflight; fresh owner role/policy/leeway observation", + "Admitted private human/coding-agent token producer remains separate gap", + "Current headroom/quiet window and actual native negative/revoke/restoration/cleanup acceptance" + ], + "authority_boundary": { + "source_preparation_only": true, + "native_actions": false, + "tunnel_opened": false, + "node_reachability_observed": false, + "owner_checkout_writes": false, + "credential_access": false, + "secret_reads": false, + "foreign_reviews": false, + "founder_approval": false, + "KEY39_task_closure": false + }, + "work_record_anchor_sha256": "717b8c9c80553be09fbdc8ddc6e8aaf72be830fe7763da227b8695e463bea631", + "retained_correction_history": "V6 and its original reviews remain unchanged; its main had read private stdin before execute health, despite broader documentation. V7 is a same-trial refinement, not a backfilled unchanged success." +} diff --git a/docs/reviews/2026-10-11-key39-v7-net-work-record.json b/docs/reviews/2026-10-11-key39-v7-net-work-record.json new file mode 100644 index 0000000..40d7753 --- /dev/null +++ b/docs/reviews/2026-10-11-key39-v7-net-work-record.json @@ -0,0 +1,9 @@ +{ + "repo": "net-kingdom", + "work_item_id": "NK-WP-ADHOC-2026-10-11-T07", + "original_path": "/home/worsch/net-kingdom/workplans/ADHOC-2026-10-11.md", + "original_sha256": "5a6b046d08a3a2e98b2e1e71520bb2c6e7b0969635dd830a6b4dc6c2f7ab122f", + "staged_path": "/tmp/keycape-jwt-node-route-v7-stage/net-kingdom/workplans/ADHOC-2026-10-11.md", + "staged_sha256": "71894864101daf3fb9174291a6cccc10307cc0c70367c63fc3443d8caac5d795", + "same_trial": true +} diff --git a/docs/reviews/2026-10-11-key39-v8-net-source-review.json b/docs/reviews/2026-10-11-key39-v8-net-source-review.json new file mode 100644 index 0000000..c9e3b9f --- /dev/null +++ b/docs/reviews/2026-10-11-key39-v8-net-source-review.json @@ -0,0 +1,82 @@ +{ + "reviewer": "net-kingdom-owner (agent net-kingdom, codex)", + "decision": "approved", + "review_id": "NK-REVIEW-20261011-KEY39-BOUNDED-INPUT-SOURCE", + "work_item_id": "NK-WP-ADHOC-2026-10-11-T07", + "reviewed_manifest_sha256": "316fd6005cc4cb7fe14ea69a5fd408aed13e6122afef60afd13d97952b27cf4a", + "reviewed_proposal_id": "KEY-WP-0039-T02/attended-lifecycle-v8-bounded-input", + "reviewed_source_hashes": { + "sso-mfa/k8s/keycape/openbao-negative-session.py": "da4e0ab767e0c3bebb48bcf5ab578b50845d9fa7211a6771661a0e1baa4314ab", + "sso-mfa/k8s/keycape/test_openbao_negative_session.py": "01e23f2b4fc77824a161a57acb3a52d19aad846b3b6c0fd3abd81afa1dc2985c", + "sso-mfa/k8s/keycape/test_openbao_node_route.py": "9f3d8d4297c5ef68f6c6bf7ca21e981e37d7eceb836a60bc254d33b0f5129047", + "sso-mfa/k8s/keycape/openbao-negative-node-route.md": "a04c94512efec7a524b15a3eec61d86de5a41df872e59a3114093063a11e75a0", + "sso-mfa/k8s/keycape/test_openbao_private_input.py": "a59c2cd4ec895c1718d52fd12f64666f1be45ae423fde476755b6d77a89280c2" + }, + "previous_review_sha256": "a0c65c6655e3152f209ad83dc69ecba0dcba10e21f723fba4ab3a58501f358ff", + "comment": "NetKingdom INTENT Principles 6/7 require bounded exposure and explicit owner responsibility. Exact v8 manifest 316fd6005cc4cb7fe14ea69a5fd408aed13e6122afef60afd13d97952b27cf4a prepared/reviewed under completed same-trial NK-WP-ADHOC-2026-10-11-T07. Source agreement for bounded complete private JSON receive within original pre-input approval/business deadlines. Authored owner preparation only; fresh independent KeyCape/Platform exact reviews, producer/child readiness handshake and founder native authority remain necessary. Existing v5 actual source and immutable v6/v7 remain unchanged.", + "findings": [], + "validation": { + "supplied_lifecycle_tests": 46, + "node_route_actual_main_tests": 12, + "new_bounded_private_receive_tests": 7, + "retained_independent_tests": 21, + "unchanged_runner_tests_previously_verified": 17, + "all_passed": true, + "synthetic_offline_only": true, + "bounded_receive_checks": [ + "Actual main real pipe with no data/no EOF interrupted at remaining original approval window", + "Actual main complete JSON but absent EOF still fails under hard elapsed deadline", + "At most 65536 receive bytes in sized chunks; oversized input fixed refusal before execution", + "Empty/incomplete/invalid/excessive-token input fixed refusal", + "Expired window after health performs zero binary reads", + "Late EOF fails before decode/execute under original monotonic window", + "Complete bounded exact JSON plus EOF reaches execution once with already-started window" + ], + "original_checkout_source_pins": { + "sso-mfa/k8s/keycape/openbao-negative-session.py": "3bcceebc845b17492040c76223d77088257c547a2b890198e7b5c2b04a0cd360", + "sso-mfa/k8s/keycape/test_openbao_negative_session.py": "a428a4a5ea60bd6c4ad10481e133227ad696a4a08653bf39b554b47431fb9f8c" + }, + "native_readiness": false, + "producer_ready_handshake_missing": true + }, + "coverage": [ + "Workstation bridge configuration routing metadata and actual -L implementation distinguish node/workstation listener", + "No node Pod DNS assumption; bounded founder foreground node-local gateway Service port-forward proposal and process-specific teardown", + "Public unauth GET only; strict active initialized/unsealed body and digest-only cluster identity evidence", + "Execute initial route refusal before native/Secret access; repeated route loss after dry-runs before first mutation", + "Separate recovery route gate before native/Secret reads; health identity must match exact envelope pin", + "No auth headers, private stdin or Kubernetes in public route-preflight mode; current-process diagnostic does not attest node placement", + "HTTP health shares all existing no-proxy/no-redirect/byte/total-timer/abort guards; slow-health real timer refusal", + "All earlier CAS/deadline/deletion/recovery/known-revoke/unknown-outcome regressions remain", + "Actual main failure tests, not only execute-function mocks: unavailable route and missing health pin prevent stdin.read; recover also consumes no stdin", + "Actual main success ordering verifies health before stdin before execution; original pre-input monotonic window is preserved", + "Source/role/authority revalidation follows private input; expired original monotonic window refuses before runner/native access", + "Actual main binary receive cap 65536 bytes, sized chunk reads and explicit EOF; exact two-token object with per-token UTF-8 byte limits", + "Linux main-thread total receive timer at most 15 seconds clipped to original wall and monotonic remaining budget; no renewal", + "Actual local pipe stalled/no EOF and complete JSON/no EOF regressions; fixed oversized/invalid/late failures before execution", + "Producer-side missing exact-child ready handshake retained as separate explicit native prerequisite; exporter write timeout is not treated as consumer read bound" + ], + "native_prerequisites": [ + "Fresh source-reviewed exact-child preflight-ready acknowledgment bound to private pipe/session before producer reads/consumes files or writes ANY token bytes", + "Fresh exact source reviews by Platform and KeyCape", + "Founder exact source/runtime/route/window/cleanup decision", + "Actual same-node route preflight with owner-admitted route and independently confirmed provider identity", + "Exact envelope bao_health_identity_sha256 from current founder preflight; fresh owner role/policy/leeway observation", + "Admitted private human/coding-agent token producer remains separate gap", + "Current headroom/quiet window and actual native negative/revoke/restoration/cleanup acceptance" + ], + "authority_boundary": { + "source_preparation_only": true, + "native_actions": false, + "tunnel_opened": false, + "node_reachability_observed": false, + "owner_checkout_writes": false, + "credential_access": false, + "secret_reads": false, + "foreign_reviews": false, + "founder_approval": false, + "KEY39_task_closure": false + }, + "work_record_anchor_sha256": "76f5ed2ac91c8486f5ac52db55572be53ac9fcc7bcd880d8e6effcae8200db2d", + "retained_correction_history": "V7 correctly gated private consumption on health but had unbounded stdin.read; KeyCape identified that adjacent gap. V8 bounds receive. V6/V7 and their original reviews remain unchanged; these refinements are not backfilled unchanged successes." +} diff --git a/docs/reviews/2026-10-11-key39-v8-net-work-record.json b/docs/reviews/2026-10-11-key39-v8-net-work-record.json new file mode 100644 index 0000000..64906cf --- /dev/null +++ b/docs/reviews/2026-10-11-key39-v8-net-work-record.json @@ -0,0 +1,9 @@ +{ + "repo": "net-kingdom", + "work_item_id": "NK-WP-ADHOC-2026-10-11-T07", + "original_path": "/home/worsch/net-kingdom/workplans/ADHOC-2026-10-11.md", + "original_sha256": "5a6b046d08a3a2e98b2e1e71520bb2c6e7b0969635dd830a6b4dc6c2f7ab122f", + "staged_path": "/tmp/keycape-jwt-node-route-v8-stage/net-kingdom/workplans/ADHOC-2026-10-11.md", + "staged_sha256": "6300d9d47a59a830dd32c4106441771f7227f094f351956502dbff11507b19bb", + "same_trial": true +} diff --git a/sso-mfa/k8s/keycape/openbao-negative-node-route.md b/sso-mfa/k8s/keycape/openbao-negative-node-route.md new file mode 100644 index 0000000..05db701 --- /dev/null +++ b/sso-mfa/k8s/keycape/openbao-negative-node-route.md @@ -0,0 +1,129 @@ +# KEY39 node-local OpenBao route prerequisite + +Source-only successor `KEY-WP-0039-T02/attended-lifecycle-v8-bounded-input` of frozen +v5. No native preflight, port-forward, login, Secret access or execution has run. +Previous v5 source and every review remain unchanged. The new executor and +health endpoint need fresh exact source-owner acknowledgments and a founder +native decision; previous v5 agreement is historical compatibility only. + +The child runs on railiance01 and uses the explicit node k3s kubeconfig. +The environment orientation's port 18200 is a **workstation** address. The +installed `openbao-ui-railiance01` routing metadata is `direction: local`, local +port 18200, node destination `10.43.5.152:8080`. OpsBridge's supported local +forward builds `-L 127.0.0.1:18200:10.43.5.152:8080`; its listener belongs to the +machine invoking Bridge. It does not establish a node-local listener. No current +node DNS evidence establishes that `openbao.openbao.svc` resolves there. Do not +infer node reachability from workstation role observations or Bridge status. + +OpsBridge supports named `up`, `down`, `status`, `check`, and local/reverse +configurations. There is no existing named KEY39 node-local reverse route in the +inspected routing metadata. Do not invent `bridge forward`, silently modify the +shared workstation tunnel, repurpose its port, or request a new SSH certificate +for this preparation. A new Bridge-managed route needs its own owner-reviewed +configuration and authority; this source does not create one. + +## Proposed founder-held route on the child node + +The Platform source publishes Service `openbao/openbao-ui-gateway`, port 8080, +which proxies the OpenBao API. The proposed attended route uses this existing +service and node loopback; it requires no pod DNS or hardcoded ClusterIP: + +```bash +# Proposed only: run in a dedicated founder terminal ON railiance01, after exact +# route/session authority and current cluster/service identity are confirmed. +# Lifetime is capped at 25 minutes, including preparation/action/cleanup. +timeout --foreground 1500s kubectl --kubeconfig=/etc/rancher/k3s/k3s.yaml --request-timeout=20s -n openbao port-forward --address=127.0.0.1 --pod-running-timeout=20s service/openbao-ui-gateway 18200:8080 >/dev/null 2>&1 +``` + +Keep that exact foreground process alive through issuer restoration, removed +client refusal and Secret absence verification. Stop only the new process with +Ctrl-C when those obligations are verified; timeout also stops it. Do not kill +an arbitrary existing listener, run `bridge down` against shared tunnels, or +restart a shared gateway. Port conflict, unavailable service, route loss or +non-ready OpenBao means stop and retain any pending custody for separately +approved recovery. Recovery requires re-establishing the same admitted node +route under its own founder recovery decision; no automatic tunnel is opened. +The lifetime estimate is a finite preparation ceiling, not an authority grant. + +## Public preflight and exact envelope pin + +The separate diagnostic command reads only the exact unauthenticated health +endpoint, never Kubernetes, Secrets, a token producer or a credential. Run from +the same founder-owned node execution context as the intended child: + +```bash +python3 /OWNER-PINNED-PATH/openbao-negative-session.py route-preflight --bao-addr http://127.0.0.1:18200 --receipt /OWNER-PRIVATE-PATH/key39-node-route.json +``` + +The receipt is exclusive and mode 0600. It records only booleans and SHA256 of +public `cluster_id`; `probe_location=current_process_only` expressly avoids +claiming that an arbitrary workstation run proves node readiness. It accepts +only HTTP 200 with strictly boolean initialized=true, sealed=false, +standby=false, performance_standby=false when supplied, and a nonempty bounded +cluster identity. The official endpoint defines these health states: +[OpenBao health API](https://openbao.org/api-docs/system/health/). + +Then run the existing founder preflight with the explicit same Bao origin: + +```bash +python3 /OWNER-PINNED-PATH/openbao-negative-session.py founder-preflight --confirm 'FOUNDER ATTENDED KEY39 PREFLIGHT' --bao-addr http://127.0.0.1:18200 --receipt /OWNER-PRIVATE-PATH/key39-preflight.json +``` + +This retains all previous runtime/registry/headroom/quiet-window obligations. +Copy its `bao_health_identity_sha256` into the exact reviewed execute/recovery +envelope. The attending founder/Platform owner must verify the route target and +provider identity against their authoritative facts. Public health is not +cryptographic service authentication, role/policy proof or execution admission; +its hash detects accidental provider changes and does not replace that review. + +Execute probes this pinned health identity before protected token input/native +object reads, then again after dry-runs immediately before the first temporary +Secret create. Recovery probes it before native object/Secret reads. A missing +pin, changed identity, redirect, unreachable/oversized/malformed response or +inactive provider refuses. The existing no-proxy/no-redirect/byte/total-elapsed +bounds and exception abort obligations apply to this new GET too. No local +helper opens or repairs the route. Loss after the first mutation still invokes +the existing bounded owner cleanup path; unproved cleanup stays pending. + +The admitted human/coding-agent private stdin producer and all Platform/Mason/ +Secrets Engine native acceptance prerequisites remain unresolved separately. +This preflight never fetches or produces those tokens and closes no owner task. + +## V7 actual entrypoint sequencing correction + +V6's statement that health preceded private token input was too broad: its +`main` read private stdin before calling `execute`, though health still preceded +all native reads/mutations. Platform identified that exact source/documentation +mismatch. V6 and its reviews remain immutable history. + +V7 `main` verifies the exact approval/source envelope, starts the bounded action +window and checks the approved public health identity before reading private +stdin. On route failure, stdin is never read and no native access occurs. After +input arrives, `execute` revalidates source/authority and expiry, preserving the +original pre-input monotonic deadline rather than restarting it; its existing +health checks before native access and immediately before mutation remain. +Recovery never reads private stdin and still checks the same public health pin +before native Secret/config/deployment reads. Public route-preflight likewise +consumes no private input. These are separately tested through the actual `main` +entrypoint, with synthetic data and zero native/network calls. + +## V8 bounded private JSON receive correction + +V7 health preceded private stdin but `stdin.read()` had no byte or elapsed bound. +That issuer-owner finding remains history; exporter write bounds do not bound a +consumer read. V8 reads binary chunks of at most 4096 bytes, at most 65536 total +bytes, and requires actual EOF before decoding the exact two-token JSON object. +Each token is capped at 16384 UTF-8 bytes. The entire receive has a Linux main- +thread total timer capped at 15 seconds and clipped to the original remaining +wall-clock/monotonic action window; waiting never renews that window. Expiry is +checked before receiving, after EOF, and after decoding. Empty, oversized, +incomplete, invalid, stalled or late input cannot reach native execution. +Receipts contain only fixed failure codes/booleans, never bytes or parser errors. + +This child-side read gate still does not establish producer-side readiness. +Before ANY token bytes enter a pipe/SSH transport, a separately reviewed trusted +launcher/exporter must receive an exact-child source/approval/public-health ready +acknowledgment bound to that pipe session and window. The current producer v1 +has no such protocol; the producer consumer review requests it. V8 supplies no +handshake, launcher admission or native readiness and must not be piped to the +old exporter as though its health-before-read check prevents early transmission. diff --git a/sso-mfa/k8s/keycape/openbao-negative-session.py b/sso-mfa/k8s/keycape/openbao-negative-session.py index 6159cdb..6123fc8 100644 --- a/sso-mfa/k8s/keycape/openbao-negative-session.py +++ b/sso-mfa/k8s/keycape/openbao-negative-session.py @@ -28,7 +28,7 @@ import yaml CLUSTER = 'a553c742-0115-43d4-99a4-a5ca56fe0786' TARGET = 'sso/keycape' -PROPOSAL = 'KEY-WP-0039-T02/attended-lifecycle-v5' +PROPOSAL = 'KEY-WP-0039-T02/attended-lifecycle-v8-bounded-input' IDS = ['openbao-probe-' + c for c in ['aud', 'sub', 'tenant', 'roles', 'scope', 'expiry']] ENVS = ['KEYCAPE_' + c.upper().replace('-', '_') + '_CLIENT_SECRET' for c in IDS] TEMP = 'keycape-openbao-negative-key39' @@ -41,6 +41,9 @@ MAX_RESPONSE = 256 * 1024 HTTP_TOTAL_SECONDS = 15 CLEANUP_TOTAL_SECONDS = 300 HTTP_ABORT_SECONDS = 0.02 +PRIVATE_INPUT_MAX_BYTES = 65536 +PRIVATE_TOKEN_MAX_BYTES = 16384 +PRIVATE_INPUT_TOTAL_SECONDS = 15 class Refused(Exception): @@ -146,9 +149,10 @@ class SessionTransport: allowed = {'https://kc.coulomb.social/token', 'https://kc.coulomb.social/.well-known/openid-configuration', self.bao_origin + '/v1/auth/keycape-services/login', - self.bao_origin + '/v1/auth/token/revoke-self'} + self.bao_origin + '/v1/auth/token/revoke-self', + self.bao_origin + '/v1/sys/health'} require(url in allowed, 'http_target_not_reviewed') - require(method == ('GET' if url.endswith('/.well-known/openid-configuration') else 'POST'), + require(method == ('GET' if url.endswith(('/.well-known/openid-configuration', '/v1/sys/health')) else 'POST'), 'http_method_not_reviewed') cleanup = url.endswith('/v1/auth/token/revoke-self') budget = request_budget(min(timeout, HTTP_TOTAL_SECONDS), cleanup=cleanup) @@ -247,6 +251,23 @@ class SessionTransport: headers['Content-Type'] = 'application/json' return self.request(url, data=data, headers=headers, timeout=timeout) + def health_identity(self): + # Public endpoint only: no token/header, login, Secret or route creation. + status, raw = self.request(self.bao_origin + '/v1/sys/health', method='GET') + require(status == 200, 'bao_public_health_unavailable') + try: + body = read_json(raw) + require(isinstance(body, dict) and body.get('initialized') is True + and body.get('sealed') is False and body.get('standby') is False + and body.get('performance_standby', False) is False, + 'bao_public_health_not_active') + cluster_id = body.get('cluster_id') + require(isinstance(cluster_id, str) and 0 < len(cluster_id) <= 128, + 'bao_public_health_identity_required') + return digest(cluster_id.encode()) + except (TypeError, ValueError): + raise Refused('bao_public_health_invalid') from None + def discovery(self): status, raw = self.request('https://kc.coulomb.social/.well-known/openid-configuration', method='GET') require(status == 200, 'discovery_failed') @@ -477,6 +498,15 @@ def delete_temporary(created): 'temporary_secret_still_present') +def verify_bao_route(transport, envelope, receipt): + expected = envelope.get('bao_health_identity_sha256') + require(isinstance(expected, str) and len(expected) == 64 + and all(c in '0123456789abcdef' for c in expected), + 'approved_bao_health_identity_required') + require(transport.health_identity() == expected, 'bao_public_health_identity_changed') + receipt['bao_public_route_verified'] = True + + def recover(envelope, receipt): global REQUEST_DEADLINE, BUSINESS_MONOTONIC_DEADLINE, CLEANUP_MONOTONIC_DEADLINE, CLEANING verify_sources(envelope, recovery=True) @@ -487,6 +517,7 @@ def recover(envelope, receipt): begin_cleanup() CLEANING = True transport = SessionTransport(envelope['bao_addr'], receipt=receipt) + verify_bao_route(transport, envelope, receipt) require(k(['get', 'namespace', 'kube-system', '-o', 'json'])['metadata']['uid'] == CLUSTER, 'cluster_identity_changed') config = get('secret', 'keycape-config') @@ -525,14 +556,59 @@ def recover(envelope, receipt): receipt.update(cleanup_verified=True, recovery_only=True) -def execute(envelope, private, receipt, checkpoint=lambda: None): +def read_private_input(stream): + # Complete private JSON plus EOF, within this original approved action window. + # No unbounded read, text buffering, token logging or renewed deadline. + check_deadline() + budget = request_budget(PRIVATE_INPUT_TOTAL_SECONDS) + binary = getattr(stream, 'buffer', None) + require(binary is not None, 'private_binary_input_required') + chunks, size = [], 0 + with total_timeout(budget): + while True: + chunk = binary.read(min(4096, PRIVATE_INPUT_MAX_BYTES + 1 - size)) + require(isinstance(chunk, bytes), 'private_binary_input_required') + if not chunk: + break + size += len(chunk) + require(size <= PRIVATE_INPUT_MAX_BYTES, 'private_input_oversized') + chunks.append(chunk) + check_deadline() + require(size > 0, 'private_input_empty') + try: + private = read_json(b''.join(chunks)) + require(isinstance(private, dict) + and set(private) == {'human_token', 'coding_agent_token'} + and all(isinstance(value, str) and + 0 < len(value.encode('utf-8')) <= PRIVATE_TOKEN_MAX_BYTES + for value in private.values()), 'private_input_invalid') + except (TypeError, ValueError, UnicodeError, Refused): + raise Refused('private_input_invalid') from None + check_deadline() + return private + + +def start_execution_window(envelope): global REQUEST_DEADLINE, BUSINESS_MONOTONIC_DEADLINE, CLEANUP_MONOTONIC_DEADLINE, CLEANING - leeway = verify_sources(envelope) REQUEST_DEADLINE = envelope['valid_until_epoch'] BUSINESS_MONOTONIC_DEADLINE = time.monotonic() + min( REQUEST_DEADLINE - time.time(), envelope['maximum_session_seconds'] - 180) CLEANUP_MONOTONIC_DEADLINE = None CLEANING = False + + +def execute(envelope, private, receipt, checkpoint=lambda: None, window_started=False): + global CLEANING + leeway = verify_sources(envelope) + if not window_started: + start_execution_window(envelope) + else: + # main already began this exact window before consuming private stdin. + # Preserve its original monotonic budget; input waiting cannot renew it. + require(REQUEST_DEADLINE == envelope['valid_until_epoch'] + and BUSINESS_MONOTONIC_DEADLINE is not None and not CLEANING, + 'preinput_execution_window_required') + check_deadline() runner_path = envelope['runner_path'] require(runner_path in envelope['source_hashes'], 'runner_pin_required') probe = load_probe(runner_path) @@ -541,6 +617,7 @@ def execute(envelope, private, receipt, checkpoint=lambda: None): require(discovery.get('issuer') == 'https://kc.coulomb.social' and discovery.get('token_endpoint') == 'https://kc.coulomb.social/token', 'issuer_discovery_changed') + verify_bao_route(transport, envelope, receipt) require(isinstance(private.get('human_token'), str) and private['human_token'] and isinstance(private.get('coding_agent_token'), str) and private['coding_agent_token'], 'founder_private_tokens_required') @@ -572,6 +649,7 @@ def execute(envelope, private, receipt, checkpoint=lambda: None): try: # Set before API call: uncertain response must still trigger reconciliation. check_deadline() + verify_bao_route(transport, envelope, receipt) mutations_attempted = True created = k(['create', '-f', '/dev/stdin', '-o', 'json'], temporary) receipt['temporary_secret_uid'] = created['metadata']['uid'] @@ -649,8 +727,9 @@ def execute(envelope, private, receipt, checkpoint=lambda: None): def main(): ap = argparse.ArgumentParser(description=__doc__.splitlines()[0]) - ap.add_argument('mode', choices=['inspect', 'founder-preflight', 'execute', 'recover']) + ap.add_argument('mode', choices=['inspect', 'route-preflight', 'founder-preflight', 'execute', 'recover']) ap.add_argument('--envelope') + ap.add_argument('--bao-addr', choices=sorted(BAO_ORIGINS)) ap.add_argument('--confirm') ap.add_argument('--receipt', required=True) args = ap.parse_args() @@ -665,7 +744,13 @@ def main(): os.fsync(fd) rc = 1 try: - if args.mode in ['inspect', 'founder-preflight']: + if args.mode == 'route-preflight': + require(args.bao_addr is not None, 'bao_origin_required') + transport = SessionTransport(args.bao_addr, receipt=receipt) + receipt.update(bao_health_identity_sha256=transport.health_identity(), + bao_public_route_verified=True, mode='public_route_preflight', + probe_location='current_process_only') + elif args.mode in ['inspect', 'founder-preflight']: require(k(['get', 'namespace', 'kube-system', '-o', 'json'])['metadata']['uid'] == CLUSTER, 'cluster_identity_changed') dep = get('deployment', 'keycape') @@ -674,6 +759,10 @@ def main(): if c['name'] == 'keycape'), mode='metadata_only') if args.mode == 'founder-preflight': require(args.confirm == 'FOUNDER ATTENDED KEY39 PREFLIGHT', 'founder_attendance_required') + require(args.bao_addr is not None, 'bao_origin_required') + transport = SessionTransport(args.bao_addr, receipt=receipt) + receipt.update(bao_health_identity_sha256=transport.health_identity(), + bao_public_route_verified=True) config = get('secret', 'keycape-config') require(not config['metadata'].get('ownerReferences'), 'controller_owned_config') raw = base64.b64decode(config['data']['config.yaml'], validate=True).decode() @@ -693,8 +782,13 @@ def main(): if args.mode == 'recover': recover(envelope, receipt) else: - private = read_json(__import__('sys').stdin.read()) - execute(envelope, private, receipt, checkpoint=checkpoint) + # Public route/source readiness precedes private token consumption. + verify_sources(envelope) + start_execution_window(envelope) + transport = SessionTransport(envelope['bao_addr'], receipt=receipt) + verify_bao_route(transport, envelope, receipt) + private = read_private_input(__import__('sys').stdin) + execute(envelope, private, receipt, checkpoint=checkpoint, window_started=True) receipt['negative_and_cleanup_verified'] = True rc = 0 except Refused as error: diff --git a/sso-mfa/k8s/keycape/test_openbao_negative_session.py b/sso-mfa/k8s/keycape/test_openbao_negative_session.py index 88ecadf..a0fc7cb 100644 --- a/sso-mfa/k8s/keycape/test_openbao_negative_session.py +++ b/sso-mfa/k8s/keycape/test_openbao_negative_session.py @@ -31,6 +31,7 @@ def envelope(): ['net-kingdom', 'key-cape', 'railiance-platform']}, 'valid_until_epoch': time.time() + 900, 'maximum_session_seconds': 900, 'bao_addr': 'http://openbao.openbao.svc:8200', + 'bao_health_identity_sha256': s.digest(b'fixture-health-cluster'), 'role_observation': {'mount': 'keycape-services', 'role': 'secrets-engine-openbao', 'issuer': 'https://kc.coulomb.social', 'algorithm': 'RS256', 'bindings_exact': True, 'policy_exact': True, 'raw_expiration_leeway': 0, @@ -582,6 +583,7 @@ class Tests(unittest.TestCase): patch.object(s.subprocess, 'run', side_effect=delete): transport_factory.return_value.discovery.return_value = { 'issuer':'https://kc.coulomb.social', 'token_endpoint':'https://kc.coulomb.social/token'} + transport_factory.return_value.health_identity.return_value = e['bao_health_identity_sha256'] transport_factory.return_value.post.side_effect = Probe().http_post if drift or not probe_pass: with self.assertRaises(s.Refused): diff --git a/sso-mfa/k8s/keycape/test_openbao_node_route.py b/sso-mfa/k8s/keycape/test_openbao_node_route.py new file mode 100644 index 0000000..8617234 --- /dev/null +++ b/sso-mfa/k8s/keycape/test_openbao_node_route.py @@ -0,0 +1,198 @@ +"""Offline route readiness: public responses and lifecycle mocks only.""" +import importlib.util +import json +from pathlib import Path +import unittest +from unittest.mock import patch +import copy +import time +import tempfile +import sys +import signal +import io + +spec=importlib.util.spec_from_file_location('fixtures',Path(__file__).with_name('test_openbao_negative_session.py')) +f=importlib.util.module_from_spec(spec);spec.loader.exec_module(f);s=f.s +HEALTH={'initialized':True,'sealed':False,'standby':False,'performance_standby':False,'cluster_id':'synthetic-public-cluster'} +PIN=s.digest(HEALTH['cluster_id'].encode()) + +class NodeRouteTests(unittest.TestCase): + def setUp(self): + s.CLEANING=False;s.REQUEST_DEADLINE=None + s.BUSINESS_MONOTONIC_DEADLINE=None;s.CLEANUP_MONOTONIC_DEADLINE=None + + def test_public_health_get_has_no_authentication_and_only_digest_retained(self): + transport=s.SessionTransport('http://127.0.0.1:18200');receipt={} + response=type('Response',(),{'getcode':lambda self:200,'read':lambda self,size:json.dumps(HEALTH).encode(),'close':lambda self:None})() + with patch.object(transport.opener,'open',return_value=response) as calls: + s.verify_bao_route(transport,{'bao_health_identity_sha256':PIN},receipt) + req=calls.call_args.args[0] + self.assertEqual(req.full_url,'http://127.0.0.1:18200/v1/sys/health') + self.assertEqual(req.method,'GET');self.assertEqual(req.headers,{}) + self.assertEqual(receipt,{'bao_public_route_verified':True}) + self.assertNotIn(HEALTH['cluster_id'],json.dumps(receipt)) + + def test_unreachable_redirect_sealed_standby_uninitialized_and_malformed_health_refuse(self): + transport=s.SessionTransport('http://openbao.openbao.svc:8200') + cases=[(None,b''),(302,b'{}'),(503,json.dumps(HEALTH).encode()),(200,b'not-json'), + (200,b'[]'),(200,json.dumps(dict(HEALTH,sealed=True)).encode()), + (200,json.dumps(dict(HEALTH,initialized=False)).encode()), + (200,json.dumps(dict(HEALTH,standby=True)).encode()), + (200,json.dumps(dict(HEALTH,sealed=0)).encode()), + (200,json.dumps(dict(HEALTH,cluster_id='')).encode())] + for status,body in cases: + with self.subTest(status=status,body_length=len(body)),patch.object(transport,'request',return_value=(status,body)): + with self.assertRaises(s.Refused):transport.health_identity() + + def test_identity_drift_or_missing_approved_pin_refuse(self): + transport=s.SessionTransport('http://127.0.0.1:18200') + with patch.object(transport,'health_identity',return_value=PIN) as health: + with self.assertRaisesRegex(s.Refused,'bao_public_health_identity_changed'): + s.verify_bao_route(transport,{'bao_health_identity_sha256':'0'*64},{}) + health.reset_mock() + with self.assertRaisesRegex(s.Refused,'approved_bao_health_identity_required'): + s.verify_bao_route(transport,{}, {}) + health.assert_not_called() + + def setup_execute(self): + raw=f.baseline() + spec={'replicas':1,'strategy':{'type':'Recreate'},'template':{'spec':{'containers':[{'name':'keycape','env':[]}]}}} + config={'metadata':{'name':'keycape-config','uid':'cfg','resourceVersion':'1'},'data':{'config.yaml':s.base64.b64encode(raw.encode()).decode()}} + dep={'metadata':{'name':'keycape','uid':'dep','resourceVersion':'1'},'spec':spec} + e=f.envelope();e.update(runner_path='fixture-runner',bao_health_identity_sha256=PIN, + config_metadata=s.meta(config),deployment_metadata=s.meta(dep),registry_sha256=s.digest(raw.encode()),deployment_spec_sha256=s.canonical(spec)) + e['source_hashes']['fixture-runner']='fixture' + return e,config,dep + + def test_unavailable_initial_node_route_stops_before_any_native_or_secret_access(self): + e,config,dep=self.setup_execute() + with patch.object(s,'verify_sources',return_value=150),patch.object(s,'load_probe'), \ + patch.object(s,'SessionTransport') as factory,patch.object(s,'k') as k,patch.object(s,'get') as get: + factory.return_value.discovery.return_value={'issuer':'https://kc.coulomb.social','token_endpoint':'https://kc.coulomb.social/token'} + factory.return_value.health_identity.side_effect=s.Refused('bao_public_health_unavailable') + with self.assertRaisesRegex(s.Refused,'bao_public_health_unavailable'): + s.execute(e,{'human_token':'synthetic','coding_agent_token':'synthetic'}, {}) + k.assert_not_called();get.assert_not_called() + + def test_route_loss_after_dry_runs_stops_before_temporary_create_or_restarts(self): + e,config,dep=self.setup_execute() + with patch.object(s,'verify_sources',return_value=150),patch.object(s,'load_probe'), \ + patch.object(s,'SessionTransport') as factory, \ + patch.object(s,'k',return_value={'metadata':{'uid':s.CLUSTER}}) as k, \ + patch.object(s,'get',side_effect=[config,dep]),patch.object(s,'patch') as patches,patch.object(s,'ready',return_value=True): + factory.return_value.discovery.return_value={'issuer':'https://kc.coulomb.social','token_endpoint':'https://kc.coulomb.social/token'} + factory.return_value.health_identity.side_effect=[PIN,s.Refused('bao_public_health_unavailable')] + with self.assertRaisesRegex(s.Refused,'bao_public_health_unavailable'): + s.execute(e,{'human_token':'synthetic','coding_agent_token':'synthetic'}, {}) + self.assertEqual(factory.return_value.health_identity.call_count,2) + self.assertTrue(all(c.kwargs.get('dry') is True for c in patches.call_args_list)) + self.assertTrue(all(c.args[0][0]!='create' or '--dry-run=server' in c.args[0] for c in k.call_args_list)) + + def test_recovery_route_refusal_precedes_native_reads(self): + e,config,dep=self.setup_execute();e['disposition']='recovery-approved' + with patch.object(s,'verify_sources',return_value=None),patch.object(s,'SessionTransport') as factory, \ + patch.object(s,'k') as k,patch.object(s,'get') as get: + factory.return_value.health_identity.side_effect=s.Refused('bao_public_health_unavailable') + with self.assertRaisesRegex(s.Refused,'bao_public_health_unavailable'): + s.recover(e,{}) + k.assert_not_called();get.assert_not_called() + + def test_public_health_uses_total_elapsed_limit(self): + transport=s.SessionTransport('http://127.0.0.1:18200') + class Slow: + def getcode(self):return 200 + def read(self,size):time.sleep(0.5);return json.dumps(HEALTH).encode() + def close(self):pass + started=time.monotonic() + with patch.object(s,'HTTP_TOTAL_SECONDS',0.008),patch.object(transport.opener,'open',return_value=Slow()): + with self.assertRaisesRegex(s.Refused,'request_total_timeout'):transport.health_identity() + self.assertLess(time.monotonic()-started,0.08) + + def test_public_route_mode_has_no_native_secret_or_private_input(self): + with tempfile.TemporaryDirectory() as folder: + receipt=Path(folder)/'receipt.json' + argv=['session','route-preflight','--bao-addr','http://127.0.0.1:18200','--receipt',str(receipt)] + with patch.object(sys,'argv',argv),patch.object(s,'SessionTransport') as factory, \ + patch.object(s,'k') as k,patch.object(s,'get') as get,patch.object(sys,'stdin') as stdin: + factory.return_value.health_identity.return_value=PIN + self.assertEqual(s.main(),0) + k.assert_not_called();get.assert_not_called();stdin.read.assert_not_called() + data=json.loads(receipt.read_text()) + self.assertEqual(data['bao_health_identity_sha256'],PIN) + self.assertEqual(data['probe_location'],'current_process_only') + self.assertFalse(data['native_accepted']) + + def main_fixture(self,folder,mode): + envelope=f.envelope() + envelope['bao_health_identity_sha256']=PIN + if mode=='recover':envelope['disposition']='recovery-approved' + path=Path(folder)/'approved.json';path.write_text(json.dumps(envelope));path.chmod(0o600) + receipt=Path(folder)/'receipt.json' + argv=['session',mode,'--envelope',str(path),'--receipt',str(receipt)] + return envelope,receipt,argv + + def test_actual_main_health_failure_never_reads_private_stdin_execute_or_recover(self): + previous=signal.getsignal(signal.SIGTERM) + try: + for mode in ['execute','recover']: + with self.subTest(mode=mode),tempfile.TemporaryDirectory() as folder: + e,receipt,argv=self.main_fixture(folder,mode) + with patch.object(sys,'argv',argv),patch.object(s,'SessionTransport') as factory, \ + patch.object(sys,'stdin') as stdin,patch.object(s,'k') as k,patch.object(s,'get') as get: + factory.return_value.health_identity.side_effect=s.Refused('bao_public_health_unavailable') + self.assertEqual(s.main(),1) + stdin.read.assert_not_called();stdin.buffer.read.assert_not_called();k.assert_not_called();get.assert_not_called() + data=json.loads(receipt.read_text()) + self.assertEqual(data['reason'],'bao_public_health_unavailable') + self.assertFalse(data['cleanup_verified']) + s.CLEANING=False;s.CLEANUP_MONOTONIC_DEADLINE=None + finally:signal.signal(signal.SIGTERM,previous) + + def test_actual_main_missing_approved_health_pin_never_reads_stdin(self): + previous=signal.getsignal(signal.SIGTERM) + try: + with tempfile.TemporaryDirectory() as folder: + e,receipt,argv=self.main_fixture(folder,'execute') + del e['bao_health_identity_sha256'] + Path(argv[3]).write_text(json.dumps(e)) + with patch.object(sys,'argv',argv),patch.object(sys,'stdin') as stdin, \ + patch.object(s,'SessionTransport') as factory,patch.object(s,'k') as k: + self.assertEqual(s.main(),1) + stdin.read.assert_not_called();stdin.buffer.read.assert_not_called();factory.return_value.health_identity.assert_not_called();k.assert_not_called() + self.assertEqual(json.loads(receipt.read_text())['reason'],'approved_bao_health_identity_required') + finally:signal.signal(signal.SIGTERM,previous) + + def test_actual_main_health_succeeds_before_stdin_and_preserves_started_window(self): + previous=signal.getsignal(signal.SIGTERM);events=[];started={} + try: + with tempfile.TemporaryDirectory() as folder: + e,receipt,argv=self.main_fixture(folder,'execute') + def health(): + events.append('health');started['deadline']=s.BUSINESS_MONOTONIC_DEADLINE;return PIN + payloads=iter([b'{"human_token":"synthetic","coding_agent_token":"synthetic"}',b'']) + def private_read(size): + if 'stdin' not in events:events.append('stdin') + self.assertGreater(size,0);return next(payloads) + def execute(*args,**kwargs): + events.append('execute');self.assertTrue(kwargs['window_started']) + self.assertEqual(s.BUSINESS_MONOTONIC_DEADLINE,started['deadline']) + with patch.object(sys,'argv',argv),patch.object(s,'SessionTransport') as factory, \ + patch.object(sys,'stdin') as stdin,patch.object(s,'execute',side_effect=execute), \ + patch.object(s,'k') as k: + factory.return_value.health_identity.side_effect=health + stdin.buffer.read.side_effect=private_read + self.assertEqual(s.main(),0);k.assert_not_called() + self.assertEqual(events,['health','stdin','execute']) + finally:signal.signal(signal.SIGTERM,previous) + + def test_execute_does_not_renew_expired_preinput_monotonic_window(self): + e=f.envelope();s.start_execution_window(e) + original=s.BUSINESS_MONOTONIC_DEADLINE + with patch.object(s.time,'monotonic',return_value=original+0.01),patch.object(s,'load_probe') as load, \ + patch.object(s,'k') as k: + with self.assertRaisesRegex(s.Refused,'session_business_budget_expired'): + s.execute(e,{'human_token':'synthetic','coding_agent_token':'synthetic'}, {},window_started=True) + load.assert_not_called();k.assert_not_called() + self.assertEqual(s.BUSINESS_MONOTONIC_DEADLINE,original) + +if __name__=='__main__':unittest.main(verbosity=2) diff --git a/sso-mfa/k8s/keycape/test_openbao_private_input.py b/sso-mfa/k8s/keycape/test_openbao_private_input.py new file mode 100644 index 0000000..f27644a --- /dev/null +++ b/sso-mfa/k8s/keycape/test_openbao_private_input.py @@ -0,0 +1,111 @@ +"""Actual main bounded private receive; synthetic local binary pipes only.""" +import importlib.util +from pathlib import Path +import io +import json +import os +import signal +import sys +import tempfile +import time +import unittest +from unittest.mock import patch + +spec=importlib.util.spec_from_file_location('routes',Path(__file__).with_name('test_openbao_node_route.py')) +r=importlib.util.module_from_spec(spec);spec.loader.exec_module(r);s=r.s;f=r.f +PAYLOAD=b'{"human_token":"synthetic-human","coding_agent_token":"synthetic-agent"}' + +class PrivateInputTests(unittest.TestCase): + def setUp(self): + s.CLEANING=False;s.REQUEST_DEADLINE=None + s.BUSINESS_MONOTONIC_DEADLINE=None;s.CLEANUP_MONOTONIC_DEADLINE=None + + def main_with(self,stream,*,lifetime=0.05,health=None): + previous=signal.getsignal(signal.SIGTERM) + try: + with tempfile.TemporaryDirectory() as folder: + envelope=f.envelope();envelope['valid_until_epoch']=time.time()+lifetime + envelope['bao_health_identity_sha256']=r.PIN + path=Path(folder)/'approved.json';path.write_text(json.dumps(envelope));path.chmod(0o600) + receipt=Path(folder)/'receipt.json' + argv=['session','execute','--envelope',str(path),'--receipt',str(receipt)] + with patch.object(sys,'argv',argv),patch.object(sys,'stdin',stream), \ + patch.object(s,'SessionTransport') as factory,patch.object(s,'execute') as execute, \ + patch.object(s,'k') as k,patch.object(s,'get') as get: + factory.return_value.health_identity.side_effect=health + factory.return_value.health_identity.return_value=r.PIN + rc=s.main() + k.assert_not_called();get.assert_not_called() + return rc,json.loads(receipt.read_text()),execute.call_args,execute.call_count + finally:signal.signal(signal.SIGTERM,previous) + + def test_actual_main_real_pipe_stall_has_original_window_total_bound(self): + read_fd,write_fd=os.pipe() + binary=os.fdopen(read_fd,'rb',buffering=0) + stream=type('Input',(),{'buffer':binary})() + started=time.monotonic();previous=signal.getsignal(signal.SIGALRM) + try: + rc,receipt,args,count=self.main_with(stream,lifetime=0.02) + finally:os.close(write_fd);binary.close() + self.assertEqual(rc,1);self.assertEqual(count,0) + self.assertEqual(receipt['reason'],'request_total_timeout') + self.assertLess(time.monotonic()-started,0.15) + self.assertEqual(signal.getitimer(signal.ITIMER_REAL),(0.0,0.0)) + self.assertEqual(signal.getsignal(signal.SIGALRM),previous) + + def test_actual_main_complete_json_without_eof_still_refuses(self): + read_fd,write_fd=os.pipe();os.write(write_fd,PAYLOAD) + binary=os.fdopen(read_fd,'rb',buffering=0);stream=type('Input',(),{'buffer':binary})() + try:rc,receipt,args,count=self.main_with(stream,lifetime=0.02) + finally:os.close(write_fd);binary.close() + self.assertEqual(rc,1);self.assertEqual(count,0) + self.assertEqual(receipt['reason'],'request_total_timeout') + + def test_actual_main_oversized_receive_refuses_without_execution(self): + stream=type('Input',(),{'buffer':io.BytesIO(b'x'*(s.PRIVATE_INPUT_MAX_BYTES+1))})() + rc,receipt,args,count=self.main_with(stream) + self.assertEqual(rc,1);self.assertEqual(count,0) + self.assertEqual(receipt['reason'],'private_input_oversized') + + def test_actual_main_empty_partial_invalid_or_excessive_token_refuses(self): + inputs=[(b'','private_input_empty'),(b'{"human_token":','private_input_invalid'), + (b'not-json','private_input_invalid'),(b'{"human_token":true,"coding_agent_token":"a"}','private_input_invalid'), + (json.dumps({'human_token':'x'*(s.PRIVATE_TOKEN_MAX_BYTES+1),'coding_agent_token':'a'}).encode(),'private_input_invalid')] + for raw,reason in inputs: + with self.subTest(reason=reason,length=len(raw)): + stream=type('Input',(),{'buffer':io.BytesIO(raw)})() + rc,receipt,args,count=self.main_with(stream) + self.assertEqual(rc,1);self.assertEqual(count,0);self.assertEqual(receipt['reason'],reason) + + def test_actual_main_expiry_after_health_never_starts_private_receive(self): + stream=type('Input',(),{'buffer':io.BytesIO(PAYLOAD)})() + def late_health(): + s.REQUEST_DEADLINE=time.time()-1 + return r.PIN + with patch.object(stream.buffer,'read',wraps=stream.buffer.read) as read: + rc,receipt,args,count=self.main_with(stream,health=late_health) + self.assertEqual(rc,1);self.assertEqual(count,0) + self.assertEqual(receipt['reason'],'approved_window_expired');read.assert_not_called() + + def test_actual_main_late_eof_never_decodes_or_executes(self): + class LateInput: + reads=0 + def read(self,size): + self.reads+=1 + if self.reads==1:return PAYLOAD + s.BUSINESS_MONOTONIC_DEADLINE=time.monotonic()-1 + return b'' + stream=type('Input',(),{'buffer':LateInput()})() + rc,receipt,args,count=self.main_with(stream) + self.assertEqual(rc,1);self.assertEqual(count,0) + self.assertEqual(receipt['reason'],'session_business_budget_expired') + + def test_actual_main_complete_bounded_json_plus_eof_reaches_execution_once(self): + stream=type('Input',(),{'buffer':io.BytesIO(PAYLOAD)})() + rc,receipt,args,count=self.main_with(stream) + self.assertEqual(rc,0);self.assertEqual(count,1) + self.assertEqual(args.args[1],json.loads(PAYLOAD)) + self.assertTrue(args.kwargs['window_started']) + self.assertNotIn('synthetic-human',json.dumps(receipt)) + +if __name__=='__main__':unittest.main(verbosity=2) diff --git a/workplans/ADHOC-2026-10-11.md b/workplans/ADHOC-2026-10-11.md index 7c0552e..6945d94 100644 --- a/workplans/ADHOC-2026-10-11.md +++ b/workplans/ADHOC-2026-10-11.md @@ -233,3 +233,60 @@ unknown outcomes and prior deadline/delete/recovery guards pass. Forty-six supplied lifecycle tests, seventeen unchanged-runner tests and twenty-one independent checks pass. NetKingdom exact source agreement only; same trial, no new task, native authority, credential access, runtime acceptance or closure. + +T07 same-trial node-route source followthrough: prepared exact successor +manifest 23a972abc5175e121e594726d2b7f7a28c62109253f58f12623eaf2e5564a035 from unchanged published/frozen v5. Source inspection +confirms the named OpenBao bridge binds workstation loopback, not node loopback; +node Pod DNS/reachability is unobserved. Proposed founder-held bounded node-local +port-forward of existing openbao/openbao-ui-gateway includes targeted teardown. +A public-only route diagnostic and strict health identity digest now gate execute +before protected/native reads and again before temporary mutation; recovery +checks first as well. Forty-six retained lifecycle, eight new route, twenty-one +retained independent and seventeen unchanged-runner tests pass offline. +No tunnel, native health probe, credential/Secret read or mutation ran. Current +source agreement is only NetKingdom's; fresh independent Platform/KeyCape +acknowledgments and exact founder route/native authority remain prerequisites. +Same completed T07, all previous source/review bytes intact, no new workplan. + +T07 same-trial v7 private-input sequencing correction: exact successor +manifest 90aabec336008d7d51e37c04b5ff96ba8ed6630821cca1a4c333d803367247f7 retains immutable v6 source and NetKingdom/Platform +reviews. V6 health gated native access but main read stdin first; its broader +pre-private-input description was incorrect. V7 main now validates the exact +source/approval, starts the bounded window and checks pinned public health before +reading stdin. Execute revalidates source/expiry and preserves that original +monotonic budget, while retaining health before native access and first mutation. +Actual main tests prove failed/missing-pin health never consumes stdin; recovery +remains stdin-free. Forty-six lifecycle, twelve route/main and twenty-one retained +independent checks pass; unchanged runner remains pinned and previously verified. +Pre-freeze local refactor/test failures were corrected and retained in validation +evidence. Source preparation only; no owner checkout changes, native operation, +credentials, tunnel, health observation or new workplan. Fresh exact independent +KeyCape/Platform reviews and founder native authority remain required. + +T07 same-trial private-producer consumer source review: exact KeyCape proposal +manifest 239aaac8541ba05e8bba5f9ecdc8b555a02fd9d0ad1762fdfc4d4bae7e4ef463 +reviewed as NetKingdom consumer only. Eighteen supplied and five independent +synthetic checks pass, including actual partial-write timeout/unknown outcome. +Changes requested: no child-ready preflight acknowledgement before private pipe +write, writable stdout-pipe aliases pass FD>=3 guard, and final stat/unlink is +non-atomic despite precise-inspected-inode removal wording. Exclusive caller +custody is an assumption, not an authenticated peer or JWT signature proof. +No KeyCape owner role, existing-client reuse, coding-agent custody admission, +launcher/transport grant or native readiness is inferred. No private credentials, +native operation or owner checkout writes occurred; staged v6/v7 remain history. + +T07 same-trial v8 bounded private receive correction: exact frozen successor +manifest 316fd6005cc4cb7fe14ea69a5fd408aed13e6122afef60afd13d97952b27cf4a retains v7 agreement and the subsequent KeyCape +unbounded-input finding as same-trial history. Complete private JSON receive is +now binary/chunk/65536-byte/15-second capped, clipped to the original remaining +action window; requires actual EOF, exact two-token object and per-token byte +limits, checks expiry again before decoding/execute and emits no private errors. +Actual main real stalled pipe and complete JSON without EOF time out; oversized, +incomplete and late input refuse before execution. Forty-six lifecycle, twelve +route/main, seven new receive and twenty-one retained independent checks pass. +Producer-side exact-child ready acknowledgment still does not exist; child read +bounds do not authorize early exporter write. Earlier producer changes-requested +review remains live source feedback. Fresh exact independent KeyCape/Platform +reviews, producer/launcher handoff and founder native authority remain required. +No original source/stage/review changed, no owner checkout writes or native/ +credential/Secret/tunnel operation; same completed T07, no duplicate workplan.