Publish reviewed bounded JWT ready handoff and retained owner history
Assistant: codex Assistant-Model: gpt-6.1-sol Assistant-Session: 01a126e1-76f8-7643-8cf3-e93336539ade
This commit is contained in:
parent
bf6a44db6d
commit
7ceac4372d
11 changed files with 770 additions and 5 deletions
60
docs/reviews/2026-10-11-key39-producer-v3-net-refusal.json
Normal file
60
docs/reviews/2026-10-11-key39-producer-v3-net-refusal.json
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
{
|
||||
"reviewer": "net-kingdom",
|
||||
"formal_role": "native-consumer source owner only",
|
||||
"disposition": "changes_requested",
|
||||
"proposal_id": "KEY-WP-0039-T02/private-producer-handoff-v3",
|
||||
"manifest_sha256": "569672ecdb3f1f5b46122183dc3b82e83ff168c07c87db7211ec26bc1ce6184a",
|
||||
"compatible_consumer_manifest_sha256": "84542ddcff00932fc7ce22dcb2626be8942a3c006c5098a62fdd6b2478d0cd14",
|
||||
"adapter_sha256": "faf63a3e9504dcbb46847a114275f83d508521e095410a47f92f02b86ba82b1b",
|
||||
"launcher_sha256": "2a54568df541c38d43ca2482f037761770e6df206fb1cb88c1222ed7291f5e98",
|
||||
"work_record": {
|
||||
"task": "NK-WP-ADHOC-2026-10-11-T07",
|
||||
"status": "done",
|
||||
"actual_baseline_sha256": "6300d9d47a59a830dd32c4106441771f7227f094f351956502dbff11507b19bb",
|
||||
"staged_refinement_sha256": "37d7eba09c3aa07d9efd369872f77f24522290a88aeaf149058bb84393da8cf0",
|
||||
"preserved_v9_anchor_sha256": "90d34dd5520f998c269529c53de4ebb065328d3f6d8ae79cbb215b19344b1cf7",
|
||||
"anchor_completed_before_formal_review": true
|
||||
},
|
||||
"findings": [
|
||||
{
|
||||
"id": "NET-PRODUCER-V3-F01",
|
||||
"severity": "high",
|
||||
"location": "scripts/openbao_negative_private_launcher.py:read_public/main",
|
||||
"description": "main opens and reads caller contract before entering any total deadline. Actual CLI named FIFO contract stays blocked silently; offline subprocess terminated after 0.254s. Source contract acquisition has no finite bound, and bare open accepts nonregular paths. V2 reproduces too. This does not imply private bytes delivered.",
|
||||
"required_change": "Bound initial contract acquisition and subsequent preparation with one nonrenewable hard elapsed budget; O_NOFOLLOW/O_NONBLOCK regular bounded stable public files should refuse FIFO/symlink/source races before private file access. Add actual CLI FIFO/stalled-read/oversize checks; preserve frozen V3."
|
||||
}
|
||||
],
|
||||
"coverage": {
|
||||
"maker_tests_passed": 33,
|
||||
"independent_paired_tests_passed": 9,
|
||||
"actual_cli_fifo_counterexamples": 2,
|
||||
"exact_pins_matched": 22,
|
||||
"paired_execution": "Local fake SSH replaces transport only; actual pinned node wrapper and actual child main/source/window/ready/bounded-input run. Health function returns a fixture digest, native execute records only receipt boolean, Kubernetes function raises if invoked. No network or native operations.",
|
||||
"cases": [
|
||||
"valid complete pair",
|
||||
"wrong nonce zero private loads",
|
||||
"extra marker key zero private loads",
|
||||
"health failure zero private loads",
|
||||
"expired envelope zero transport and private loads",
|
||||
"changed source pin zero transport and private loads",
|
||||
"child stdout suppressed; completion unknown",
|
||||
"stalled marker zero private loads and local teardown",
|
||||
"actual 4096 capacity partial pipe write; sticky unknown after teardown"
|
||||
],
|
||||
"earlier_refusals_retained": [
|
||||
"a1ed14e6b2db1ae77c3fd638fd7e47b5bd50c25eebba418e31aba89bb4b7f88e",
|
||||
"8353eb8aae11be8ce874887d6a5ca10bf37c6fba73c3f85f0644a7546dd7d33f"
|
||||
]
|
||||
},
|
||||
"accepted_source_properties": [
|
||||
"Actual child-ready marker precedes private file loads/delivery in owner launcher graph. Marker binds fresh nonce/exact pins/health/window but is not authenticated authority.",
|
||||
"Adapter enforces kernel identity distinct stdio/control/data pipes and nonresetting clipped delivery timer.",
|
||||
"V3 propagates unknown private delivery into sticky consumer unknown before early return and local teardown.",
|
||||
"Exclusive same-UID directory custody explicitly required; non-atomic stat/unlink and remaining copies acknowledged. Unsigned JWT decoded shape does not authenticate issuer."
|
||||
],
|
||||
"native_executed": false,
|
||||
"credentials_read": false,
|
||||
"native_or_transport_admitted": false,
|
||||
"founder_go_granted": false,
|
||||
"task_closure_inferred": false
|
||||
}
|
||||
67
docs/reviews/2026-10-11-key39-producer-v4-net-review.json
Normal file
67
docs/reviews/2026-10-11-key39-producer-v4-net-review.json
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
{
|
||||
"reviewer": "net-kingdom",
|
||||
"formal_role": "native-consumer source owner only",
|
||||
"disposition": "approved",
|
||||
"qualification": "Exact paired source agreement only; no transport, private custody, issuer authentication, native authorization or task closure inferred.",
|
||||
"proposal_id": "KEY-WP-0039-T02/private-producer-handoff-v4",
|
||||
"manifest_sha256": "d6f641dc1e4aae7d36658d4199d89af14d2fd7a4bd4e526856eb340234f2d33e",
|
||||
"compatible_consumer_manifest_sha256": "84542ddcff00932fc7ce22dcb2626be8942a3c006c5098a62fdd6b2478d0cd14",
|
||||
"adapter_sha256": "faf63a3e9504dcbb46847a114275f83d508521e095410a47f92f02b86ba82b1b",
|
||||
"launcher_sha256": "46e57504b3185cc13f1656901a0ca02cc3887527cb09566e270f57b76099ab9c",
|
||||
"work_record": {
|
||||
"task": "NK-WP-ADHOC-2026-10-11-T07",
|
||||
"status": "done",
|
||||
"actual_baseline_sha256": "6300d9d47a59a830dd32c4106441771f7227f094f351956502dbff11507b19bb",
|
||||
"staged_refinement_sha256": "ea3cd23484aecb9d2b5fe72356f279cd08d323f8cfd53f2af2d070018fae5391",
|
||||
"preserved_v9_anchor_sha256": "90d34dd5520f998c269529c53de4ebb065328d3f6d8ae79cbb215b19344b1cf7",
|
||||
"completed_before_formal_review": true
|
||||
},
|
||||
"findings": [],
|
||||
"corrected_findings": [
|
||||
{
|
||||
"refusal_sha256": "38abf660701194f85934a076a0c01d3047e6bfe790e02ba1b0e4616e356634e6",
|
||||
"finding": "NET-PRODUCER-V3-F01",
|
||||
"verification": "Actual CLI FIFO, symlink and oversized public contract now refuse promptly; O_NOFOLLOW/O_NONBLOCK regular single-link stable source reads run under original main preparation deadline. Slow acquisition and default launch validation have hard elapsed bounds; acquisition plus launch cannot renew deadline."
|
||||
}
|
||||
],
|
||||
"coverage": {
|
||||
"maker_tests_passed": 40,
|
||||
"independent_tests_passed": 13,
|
||||
"paired_handshake_tests_passed": 9,
|
||||
"preparation_tests_passed": 4,
|
||||
"exact_pins_matched": 23,
|
||||
"paired_execution": "Local fake SSH substitutes transport only. Actual pinned node wrapper and actual child main/source/window/ready/bounded-input run; public health returns fixture digest and native execute records private-pair receipt boolean only. Kubernetes function raises if called. Unsigned synthetic tokens only.",
|
||||
"cases": [
|
||||
"actual wrapper/child readiness and complete private delivery",
|
||||
"wrong nonce zero token-file loads",
|
||||
"extra marker field zero token-file loads",
|
||||
"health failure zero token-file loads",
|
||||
"expired envelope zero transport or private loads",
|
||||
"local source mismatch zero transport or private loads",
|
||||
"child output suppressed with consumer outcome unknown",
|
||||
"stalled marker zero private loads and local teardown",
|
||||
"actual 4096 capacity partial pipe with sticky unknown after teardown",
|
||||
"actual CLI FIFO/symlink/oversize refuse before missing private paths",
|
||||
"default caller slow validation bounded no spawn/private load",
|
||||
"actual main slow acquisition bounded no spawn/private load",
|
||||
"actual main acquisition and launch share original deadline"
|
||||
]
|
||||
},
|
||||
"preserved_history": [
|
||||
"a1ed14e6b2db1ae77c3fd638fd7e47b5bd50c25eebba418e31aba89bb4b7f88e",
|
||||
"38abf660701194f85934a076a0c01d3047e6bfe790e02ba1b0e4616e356634e6",
|
||||
"8353eb8aae11be8ce874887d6a5ca10bf37c6fba73c3f85f0644a7546dd7d33f"
|
||||
],
|
||||
"source_scope_conditions": [
|
||||
"Trusted owner launcher must launch admitted exact pinned child/wrapper/envelope over separately founder-admitted captured SSH peer/profile. Marker synchronizes readiness and does not authenticate authority or peer.",
|
||||
"Exclusive same-UID private directory custody and no concurrent writer required. Stat/unlink is non-atomic; copied credentials may remain and no zeroization/remote cleanup is established.",
|
||||
"Unsigned JWT decoded claims are shape checks only; issuer/client/agent admission remains owner/founder decision.",
|
||||
"Unknown partial delivery remains sticky after local transport teardown. Native remote cleanup/acceptance requires separate owner evidence."
|
||||
],
|
||||
"native_executed": false,
|
||||
"credentials_read": false,
|
||||
"actual_ssh_executed": false,
|
||||
"native_or_transport_admitted": false,
|
||||
"founder_go_granted": false,
|
||||
"task_closure_inferred": false
|
||||
}
|
||||
89
docs/reviews/2026-10-11-key39-ready-v9-net-review.json
Normal file
89
docs/reviews/2026-10-11-key39-ready-v9-net-review.json
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
{
|
||||
"reviewer": "net-kingdom-owner (agent net-kingdom, codex)",
|
||||
"decision": "approved",
|
||||
"review_id": "NK-REVIEW-20261011-KEY39-READY-PROTOCOL-SOURCE",
|
||||
"work_item_id": "NK-WP-ADHOC-2026-10-11-T07",
|
||||
"reviewed_manifest_sha256": "84542ddcff00932fc7ce22dcb2626be8942a3c006c5098a62fdd6b2478d0cd14",
|
||||
"reviewed_proposal_id": "KEY-WP-0039-T02/attended-lifecycle-v9-ready-input",
|
||||
"reviewed_source_hashes": {
|
||||
"sso-mfa/k8s/keycape/openbao-negative-session.py": "24b7b803e4eb7e6e7c0700a069dad0ec315ee7eb5538a88b9e56147f8fab5cdd",
|
||||
"sso-mfa/k8s/keycape/test_openbao_negative_session.py": "01e23f2b4fc77824a161a57acb3a52d19aad846b3b6c0fd3abd81afa1dc2985c",
|
||||
"sso-mfa/k8s/keycape/test_openbao_node_route.py": "1c57053be6e8472174e4034fdfbf34b3e1729f5e239b3a9af89d9e59a08d6cb6",
|
||||
"sso-mfa/k8s/keycape/openbao-negative-node-route.md": "cad3e3dc481d45295cc014e48285e81614911dfb4a10523883f3044f6085f363",
|
||||
"sso-mfa/k8s/keycape/test_openbao_private_input.py": "84277edb5bd7858885173dea802b6bdc4f0f0c7785d6e200ee9a98d8553891a1",
|
||||
"sso-mfa/k8s/keycape/openbao-negative-node-wrapper.py": "76ac013d1a676a0dfa58aea1ce87b0c3e72483ccd21f586a22c5475e9132f6c1",
|
||||
"sso-mfa/k8s/keycape/test_openbao_private_ready.py": "8a7a033b1c509ba21397d1d74580c44f24592045b2b9de50fe87638607224277"
|
||||
},
|
||||
"previous_review_sha256": "b2711c8ff72724751f9c10ce199fa3564b7893aa4c0377df0d3d1611d0a97eda",
|
||||
"comment": "NetKingdom INTENT Principles 6/7 require bounded exposure and explicit owner responsibility. Exact v9 ready child/node-wrapper manifest 84542ddcff00932fc7ce22dcb2626be8942a3c006c5098a62fdd6b2478d0cd14 prepared/reviewed under completed same-trial NK-WP-ADHOC-2026-10-11-T07. Authored owner source agreement for concrete private synchronization and bounded relay. Raw marker or approval JSON is not authenticated authority/peer proof; exact trusted owner launcher/SSH source context and independent KeyCape/Platform reviews are prerequisites. No native, credential, launcher/transport admission or KEY39 closure inferred.",
|
||||
"findings": [],
|
||||
"validation": {
|
||||
"supplied_lifecycle_tests": 46,
|
||||
"retained_node_route_actual_main_tests": 12,
|
||||
"retained_private_receive_tests": 7,
|
||||
"new_ready_wrapper_tests": 9,
|
||||
"retained_independent_tests": 21,
|
||||
"unchanged_runner_tests_previously_verified": 17,
|
||||
"all_passed": true,
|
||||
"synthetic_offline_only": true,
|
||||
"ready_protocol_checks": [
|
||||
"Distinct local writable pipe, marker exact keys/nonce/executor/envelope/health/window pins and child EOF",
|
||||
"Actual duplicate stdout pipe alias refusal before marker",
|
||||
"Wrong nonce/envelope pin produces no ready",
|
||||
"Actual full pipe write interrupted at original remaining action deadline with signal restoration",
|
||||
"Actual main health then marker received before first private read; no missing-ack private read",
|
||||
"Wrapper validates all exact marker fields and typed bounded window; changed/extra fields refuse",
|
||||
"Incomplete private local ready pipe without EOF interrupted under total bound",
|
||||
"Wrapper mocked child launch uses local pass_fds and inherited private stdin, no SSH FD forwarding or token copy"
|
||||
],
|
||||
"retained_main_test_adaptation": "Existing input-only sequencing/receive tests stub the new ready emitter; new ready tests exercise actual pipe emission and main ordering separately.",
|
||||
"native_readiness": false,
|
||||
"compatible_producer_exact_review_pending": true,
|
||||
"source_original_baseline": "Current published v8, with original v5 and v6/v7/v8 frozen provenance retained"
|
||||
},
|
||||
"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",
|
||||
"Concrete required local child-ready FD/nonce/approved raw envelope pin before any private stdin read; aliases of all stdio endpoint identities rejected",
|
||||
"Exact newline marker schema with public source/envelope/health/action-window bindings; hard emission/receive bounds and original action deadline preserved",
|
||||
"Node-local wrapper constructs its own pipe and pinned Python child, validates ready under one preparation bound, forwards control line only, captures/refuses child output, performs bounded termination/capture-close cleanup",
|
||||
"No SSH FD forwarding, token stdin read/copy, raw marker authority, raw credential output or warden silent-lane compatibility claimed",
|
||||
"Independent KeyCape exporter/launcher coordination: newline control framing, no EOF wait on still-open SSHstdout, original producer budget only clips/no reset, no token-file access or delivery before valid exact ready"
|
||||
],
|
||||
"native_prerequisites": [
|
||||
"Fresh exact source-reviewed KeyCape launcher/adapter matching ready schema/source/nonce/envelope/health/window, owning admitted captured SSH peer/profile before accepting marker and before any token-file read or write",
|
||||
"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": "f294f1cf152dd797b913f0f31a5b3f7939fa67de9871580b977c74001c40d168",
|
||||
"retained_correction_history": "V8 receive bounds remain; producer v1 lacks consumer acknowledgment and fails stdio/race claims. Concrete v9 child/wrapper synchronization is a same-trial refinement; compatible KeyCape producer exact source review/admission remains separate. Earlier reviews/stages are unchanged."
|
||||
}
|
||||
|
|
@ -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": "6300d9d47a59a830dd32c4106441771f7227f094f351956502dbff11507b19bb",
|
||||
"staged_path": "/tmp/keycape-jwt-ready-v9-stage/net-kingdom/workplans/ADHOC-2026-10-11.md",
|
||||
"staged_sha256": "90d34dd5520f998c269529c53de4ebb065328d3f6d8ae79cbb215b19344b1cf7",
|
||||
"same_trial": true
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
# KEY39 node-local OpenBao route prerequisite
|
||||
|
||||
Source-only successor `KEY-WP-0039-T02/attended-lifecycle-v8-bounded-input` of frozen
|
||||
Source-only successor `KEY-WP-0039-T02/attended-lifecycle-v9-ready-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
|
||||
|
|
@ -127,3 +127,91 @@ 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.
|
||||
|
||||
## V9 private ready protocol and concrete node wrapper
|
||||
|
||||
The old producer v1 refusal remains history: a child checking health before
|
||||
reading stdin cannot prevent an exporter from buffering or transmitting tokens
|
||||
earlier. V9 execute therefore requires `--ready-fd`, `--session-nonce` and
|
||||
`--ready-envelope-sha256` before it will read private input. The ready FD is a
|
||||
locally inherited writable FIFO >=3 whose inode/device differs from every stdio
|
||||
endpoint; stdout/stderr aliases, regular files and missing FDs refuse.
|
||||
|
||||
After source/approval validation, original-window start and approved public
|
||||
health, the child emits one <=4096-byte newline JSON marker to that private FD
|
||||
and closes it. Emission has a total two-second cap clipped to the original
|
||||
action window. Child stdout and stderr remain quiet. Exact marker fields are:
|
||||
|
||||
```json
|
||||
{
|
||||
"schema": "key39-private-input-ready/v1",
|
||||
"session_nonce": "<fresh 64 lowercase hex characters from trusted launcher>",
|
||||
"proposal_id": "KEY-WP-0039-T02/attended-lifecycle-v9-ready-input",
|
||||
"target": "sso/keycape",
|
||||
"executor_sha256": "<exact reviewed child source hash>",
|
||||
"envelope_sha256": "<exact approved raw envelope byte hash>",
|
||||
"bao_health_identity_sha256": "<exact owner provider identity pin>",
|
||||
"valid_until_epoch": 0,
|
||||
"receive_budget_seconds": 0
|
||||
}
|
||||
```
|
||||
|
||||
Zeros/placeholders illustrate keys only and are never a valid marker. Epoch is
|
||||
the exact finite nonboolean approved envelope expiry, still future; receive
|
||||
budget is finite positive <=15 and clipped at emission to the original action
|
||||
window. The exporter clips its already-running deadline to the advertised
|
||||
budget and *current* approval remainder. It must not reset a deadline or demand
|
||||
that a budget measured at emission equal the smaller remainder after transport.
|
||||
|
||||
`openbao-negative-node-wrapper.py` is the concrete node-local relay. The trusted
|
||||
launcher supplies nonsecret exact child/envelope paths and SHA256 pins, a new
|
||||
nonce and exclusive receipt destination. Wrapper envelope must be owner UID,
|
||||
mode0600, single-link regular file, stable bounded bytes; its child/wrapper
|
||||
source paths must be included in the exact envelope's native source hash map.
|
||||
This is reconstruction of the caller's review context, not authenticated human
|
||||
approval: assertions in JSON cannot create a grant. The founder must separately
|
||||
admit the exact source/profile, target and SSH peer under the applicable lane.
|
||||
|
||||
The wrapper creates an **ordinary local pipe on the node**, starts the pinned
|
||||
Python child with that write endpoint passed locally, and reads its bounded
|
||||
marker plus EOF. The child's private stdin is inherited directly from the SSH
|
||||
input stream: wrapper never reads, copies, logs or writes token bytes. No extra
|
||||
file descriptor is forwarded by SSH or OpsBridge. Within one five-second total
|
||||
preparation deadline, the wrapper validates all marker keys/nonce/pins/window and
|
||||
forwards exactly one value-free marker line on its own stdout control pipe.
|
||||
The admitted workstation launcher must own/capture that SSH stdout separately
|
||||
from its private stdin data pipe, never display/tee/log it, validate the exact
|
||||
marker and then allow the KeyCape adapter to load/consume caller files or write
|
||||
ANY token bytes. The control stream stays open while the child runs; the
|
||||
exporter stops marker reception at newline, not SSH EOF. Missing, partial,
|
||||
wrong-nonce, stale, oversized or pin-mismatched markers mean zero token file
|
||||
reads/writes and retained caller files. The old v1 adapter is incompatible.
|
||||
|
||||
This wrapper emits control stdout and therefore **cannot run inside the
|
||||
warden attended command lane that rejects all child output**. It requires a
|
||||
separately admitted founder-owned direct SSH profile with no PTY, captured
|
||||
stdin/stdout/stderr, pinned peer/host identity, exact wrapper/source/receipt paths
|
||||
and no agent credential access. No such runtime admission is supplied here.
|
||||
A bare marker, caller boolean or downloaded ready file cannot authenticate a
|
||||
peer, source execution or founder decision. A trusted reviewed launcher owning
|
||||
that transport and verifying the context is a prerequisite.
|
||||
|
||||
The wrapper waits only through the original approval remainder plus 300 seconds
|
||||
cleanup. Any child stdout/stderr is captured with bounded reads and refused,
|
||||
never forwarded. Failure terminates the exact launched child with bounded five-
|
||||
second TERM then two-second kill/wait fallback; capture closes have 20 ms caps.
|
||||
No retry or replay occurs. Interruption, partial private delivery, emitted ready
|
||||
followed by child death, unconfirmed revoke or interrupted native cleanup remains
|
||||
unknown/pending owner reconciliation using the separately approved recovery
|
||||
path. Wrapper exit zero is not owner/native acceptance; the child receipt still
|
||||
requires actual owner acceptance. No persistent file/copy on the node is added
|
||||
for token material, and Unix/Python memory cleanup is not secure zeroization.
|
||||
|
||||
Human browser issuance and admitted coding-agent custody remain distinct owner
|
||||
choices. Any new files must be produced by the pinned verified owner CLI under
|
||||
continuous exclusive tmpfs custody after the separately admitted route preflight.
|
||||
They must be prepared before launching this short-lived private receive window;
|
||||
the child cannot wait for a five-minute browser login after emitting ready.
|
||||
Fresh registered-client/MFA/custody evidence, exact independent KeyCape/Platform
|
||||
reviews and the founder's complete route/transport/private-file/native decision
|
||||
remain required. No native handshake, SSH, credential or route was exercised.
|
||||
|
|
|
|||
183
sso-mfa/k8s/keycape/openbao-negative-node-wrapper.py
Normal file
183
sso-mfa/k8s/keycape/openbao-negative-node-wrapper.py
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Founder-admitted node wrapper: private local child-ready pipe -> SSH control stdout.
|
||||
No token read/copy, login, tunnel or approval authentication. Parent must capture
|
||||
stdout over its admitted authenticated SSH peer; never use the silent warden lane.
|
||||
"""
|
||||
import argparse
|
||||
import contextlib
|
||||
import hashlib
|
||||
import json
|
||||
import math
|
||||
import os
|
||||
from pathlib import Path
|
||||
import signal
|
||||
import stat
|
||||
import subprocess
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
|
||||
SCHEMA='key39-private-input-ready/v1'
|
||||
PROPOSAL='KEY-WP-0039-T02/attended-lifecycle-v9-ready-input'
|
||||
TARGET='sso/keycape'
|
||||
MAX_READY=4096
|
||||
MAX_ENVELOPE=65536
|
||||
READY_TOTAL_SECONDS=5
|
||||
|
||||
class Refused(Exception):pass
|
||||
|
||||
def need(ok,code):
|
||||
if not ok:raise Refused(code)
|
||||
|
||||
def finite(value):
|
||||
try:return type(value) in (int,float) and math.isfinite(value)
|
||||
except OverflowError:return False
|
||||
|
||||
def hash_bytes(raw):return hashlib.sha256(raw).hexdigest()
|
||||
|
||||
def hex_pin(value):return isinstance(value,str) and len(value)==64 and all(c in '0123456789abcdef' for c in value)
|
||||
|
||||
@contextlib.contextmanager
|
||||
def deadline(seconds):
|
||||
need(finite(seconds) and 0<seconds<=1200,'wrapper_budget_invalid')
|
||||
need(threading.current_thread() is threading.main_thread()
|
||||
and signal.getitimer(signal.ITIMER_REAL)==(0.0,0.0),'wrapper_timer_required')
|
||||
handler=signal.getsignal(signal.SIGALRM)
|
||||
def expired(*_):raise Refused('wrapper_timeout')
|
||||
signal.signal(signal.SIGALRM,expired);signal.setitimer(signal.ITIMER_REAL,seconds)
|
||||
try:yield
|
||||
finally:signal.setitimer(signal.ITIMER_REAL,0);signal.signal(signal.SIGALRM,handler)
|
||||
|
||||
def decode(raw):
|
||||
def unique(pairs):
|
||||
result={}
|
||||
for key,value in pairs:
|
||||
need(key not in result,'wrapper_duplicate_key');result[key]=value
|
||||
return result
|
||||
try:
|
||||
return json.loads(raw,object_pairs_hook=unique,
|
||||
parse_constant=lambda _:(_ for _ in ()).throw(Refused('wrapper_nonfinite_json')))
|
||||
except Refused:raise
|
||||
except Exception:raise Refused('wrapper_json_invalid') from None
|
||||
|
||||
def pinned_file(path,pin,limit,private=False):
|
||||
p=Path(path);need(p.is_absolute() and p.resolve()==p,'wrapper_absolute_pin_required')
|
||||
need(hex_pin(pin),'wrapper_pin_required')
|
||||
fd=os.open(p,os.O_RDONLY|os.O_NOFOLLOW|os.O_NONBLOCK)
|
||||
try:
|
||||
before=os.fstat(fd)
|
||||
need(stat.S_ISREG(before.st_mode) and 0<before.st_size<=limit,'wrapper_file_invalid')
|
||||
if private:
|
||||
need(before.st_uid==os.getuid() and stat.S_IMODE(before.st_mode)==0o600
|
||||
and before.st_nlink==1,'wrapper_private_envelope_required')
|
||||
raw=os.read(fd,limit+1);after=os.fstat(fd)
|
||||
need(len(raw)<=limit and hash_bytes(raw)==pin,'wrapper_source_changed')
|
||||
need((before.st_dev,before.st_ino,before.st_size,before.st_mtime_ns,before.st_ctime_ns)==
|
||||
(after.st_dev,after.st_ino,after.st_size,after.st_mtime_ns,after.st_ctime_ns),
|
||||
'wrapper_file_changed')
|
||||
return raw
|
||||
finally:os.close(fd)
|
||||
|
||||
def validate_marker(raw,*,nonce,executor_pin,envelope_pin,envelope):
|
||||
need(isinstance(raw,bytes) and 0<len(raw)<=MAX_READY and raw.endswith(b'\n')
|
||||
and raw.count(b'\n')==1,'wrapper_ready_invalid')
|
||||
marker=decode(raw)
|
||||
expected={'schema','session_nonce','proposal_id','target','executor_sha256','envelope_sha256',
|
||||
'bao_health_identity_sha256','valid_until_epoch','receive_budget_seconds'}
|
||||
need(isinstance(marker,dict) and set(marker)==expected,'wrapper_ready_invalid')
|
||||
need(marker['schema']==SCHEMA and marker['session_nonce']==nonce
|
||||
and marker['proposal_id']==PROPOSAL and marker['target']==TARGET
|
||||
and marker['executor_sha256']==executor_pin and marker['envelope_sha256']==envelope_pin
|
||||
and marker['bao_health_identity_sha256']==envelope['bao_health_identity_sha256']
|
||||
and marker['valid_until_epoch']==envelope['valid_until_epoch'], 'wrapper_ready_binding_changed')
|
||||
need(finite(marker['valid_until_epoch']) and time.time()<marker['valid_until_epoch']
|
||||
and finite(marker['receive_budget_seconds']) and 0<marker['receive_budget_seconds']<=15,
|
||||
'wrapper_ready_window_expired')
|
||||
return marker
|
||||
|
||||
def read_ready(fd):
|
||||
raw=b''
|
||||
while len(raw)<=MAX_READY:
|
||||
chunk=os.read(fd,MAX_READY+1-len(raw))
|
||||
if not chunk:break
|
||||
raw+=chunk
|
||||
need(len(raw)<=MAX_READY,'wrapper_ready_oversized')
|
||||
return raw
|
||||
|
||||
def forward_ready(raw):
|
||||
# Wrapper stdout is a captured control pipe. Never child stdout or token data.
|
||||
current=os.fstat(1)
|
||||
need(stat.S_ISFIFO(current.st_mode),'wrapper_private_control_pipe_required')
|
||||
need((current.st_dev,current.st_ino)!=(os.fstat(0).st_dev,os.fstat(0).st_ino),
|
||||
'wrapper_control_input_alias_refused')
|
||||
offset=0
|
||||
while offset<len(raw):
|
||||
written=os.write(1,raw[offset:]);need(type(written) is int and written>0,'wrapper_control_write_failed')
|
||||
offset+=written
|
||||
|
||||
def run(args):
|
||||
proc=None;read_fd=None;write_fd=None
|
||||
try:
|
||||
need(hex_pin(args.session_nonce),'wrapper_nonce_required')
|
||||
with deadline(READY_TOTAL_SECONDS):
|
||||
child=pinned_file(args.child,args.child_sha256,256*1024)
|
||||
envelope_raw=pinned_file(args.envelope,args.envelope_sha256,MAX_ENVELOPE,private=True)
|
||||
envelope=decode(envelope_raw)
|
||||
need(envelope.get('proposal_id')==PROPOSAL and envelope.get('target')==TARGET
|
||||
and envelope.get('disposition')=='approved' and envelope.get('founder_decision_id'),
|
||||
'wrapper_exact_owner_envelope_required')
|
||||
need(envelope.get('source_hashes',{}).get(str(Path(args.child)))==args.child_sha256
|
||||
and envelope.get('source_hashes',{}).get(str(Path(__file__).resolve()))==hash_bytes(Path(__file__).read_bytes()),
|
||||
'wrapper_child_and_wrapper_pins_required')
|
||||
expiry=envelope['valid_until_epoch'];maximum=envelope['maximum_session_seconds']
|
||||
need(finite(expiry) and type(maximum) is int and 420<=maximum<=900
|
||||
and 0<expiry-time.time()<=maximum,'wrapper_action_window_invalid')
|
||||
absolute_end=time.monotonic()+expiry-time.time()+300
|
||||
read_fd,write_fd=os.pipe()
|
||||
proc=subprocess.Popen([sys.executable,args.child,'execute','--envelope',args.envelope,
|
||||
'--receipt',args.receipt,'--ready-fd',str(write_fd),'--session-nonce',args.session_nonce,
|
||||
'--ready-envelope-sha256',args.envelope_sha256],stdin=sys.stdin.buffer,
|
||||
stdout=subprocess.PIPE,stderr=subprocess.PIPE,pass_fds=(write_fd,),close_fds=True)
|
||||
os.close(write_fd);write_fd=None
|
||||
raw=read_ready(read_fd);os.close(read_fd);read_fd=None
|
||||
validate_marker(raw,nonce=args.session_nonce,executor_pin=args.child_sha256,
|
||||
envelope_pin=args.envelope_sha256,envelope=envelope)
|
||||
forward_ready(raw)
|
||||
remaining=absolute_end-time.monotonic();need(remaining>0,'wrapper_session_window_expired')
|
||||
with deadline(remaining):
|
||||
rc=proc.wait(timeout=remaining)
|
||||
# Bounded captures: even accidental child output is never forwarded.
|
||||
unexpected=proc.stdout.read(MAX_READY+1) or proc.stderr.read(MAX_READY+1)
|
||||
need(not unexpected,'wrapper_child_output_refused')
|
||||
return 0 if rc==0 else 1
|
||||
except BaseException:
|
||||
return 1
|
||||
finally:
|
||||
for fd in (read_fd,write_fd):
|
||||
if fd is not None:
|
||||
try:os.close(fd)
|
||||
except OSError:pass
|
||||
if proc is not None:
|
||||
if proc.poll() is None:
|
||||
# Unknown/partial native outcomes remain owner recovery, never retry.
|
||||
try:
|
||||
with deadline(5):
|
||||
proc.terminate();proc.wait(timeout=5)
|
||||
except Exception:
|
||||
try:
|
||||
with deadline(2):
|
||||
proc.kill();proc.wait(timeout=2)
|
||||
except Exception:pass
|
||||
for stream in (proc.stdout,proc.stderr):
|
||||
if stream is not None:
|
||||
try:
|
||||
with deadline(0.02):stream.close()
|
||||
except Exception:pass
|
||||
|
||||
def main():
|
||||
parser=argparse.ArgumentParser(description=__doc__)
|
||||
for name in ('child','child-sha256','envelope','envelope-sha256','receipt','session-nonce'):
|
||||
parser.add_argument('--'+name,required=True)
|
||||
return run(parser.parse_args())
|
||||
|
||||
if __name__=='__main__':raise SystemExit(main())
|
||||
|
|
@ -12,6 +12,8 @@ import hashlib
|
|||
import importlib.util
|
||||
import json
|
||||
import math
|
||||
import fcntl
|
||||
import stat
|
||||
import os
|
||||
from pathlib import Path
|
||||
import secrets
|
||||
|
|
@ -28,7 +30,7 @@ import yaml
|
|||
|
||||
CLUSTER = 'a553c742-0115-43d4-99a4-a5ca56fe0786'
|
||||
TARGET = 'sso/keycape'
|
||||
PROPOSAL = 'KEY-WP-0039-T02/attended-lifecycle-v8-bounded-input'
|
||||
PROPOSAL = 'KEY-WP-0039-T02/attended-lifecycle-v9-ready-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'
|
||||
|
|
@ -44,6 +46,8 @@ HTTP_ABORT_SECONDS = 0.02
|
|||
PRIVATE_INPUT_MAX_BYTES = 65536
|
||||
PRIVATE_TOKEN_MAX_BYTES = 16384
|
||||
PRIVATE_INPUT_TOTAL_SECONDS = 15
|
||||
READY_SCHEMA = 'key39-private-input-ready/v1'
|
||||
READY_MAX_BYTES = 4096
|
||||
|
||||
|
||||
class Refused(Exception):
|
||||
|
|
@ -556,6 +560,49 @@ def recover(envelope, receipt):
|
|||
receipt.update(cleanup_verified=True, recovery_only=True)
|
||||
|
||||
|
||||
def emit_private_ready(fd, nonce, expected_envelope_sha256, envelope_sha256, envelope, receipt):
|
||||
# A value-free synchronization marker, never authentication or a grant.
|
||||
require(type(fd) is int and fd >= 3, 'private_ready_pipe_required')
|
||||
require(isinstance(nonce, str) and len(nonce) == 64
|
||||
and all(c in '0123456789abcdef' for c in nonce), 'ready_nonce_required')
|
||||
require(expected_envelope_sha256 == envelope_sha256, 'ready_envelope_pin_changed')
|
||||
validated = False
|
||||
try:
|
||||
current = os.fstat(fd)
|
||||
require(stat.S_ISFIFO(current.st_mode)
|
||||
and fcntl.fcntl(fd, fcntl.F_GETFL) & os.O_ACCMODE == os.O_WRONLY,
|
||||
'private_ready_pipe_required')
|
||||
identity = (current.st_dev, current.st_ino)
|
||||
require(all(identity != (os.fstat(other).st_dev, os.fstat(other).st_ino)
|
||||
for other in (0, 1, 2)), 'private_ready_stdio_alias_refused')
|
||||
validated = True
|
||||
check_deadline()
|
||||
marker = {'schema': READY_SCHEMA, 'session_nonce': nonce,
|
||||
'proposal_id': PROPOSAL, 'target': TARGET,
|
||||
'executor_sha256': digest(Path(__file__).read_bytes()),
|
||||
'envelope_sha256': envelope_sha256,
|
||||
'bao_health_identity_sha256': envelope['bao_health_identity_sha256'],
|
||||
'valid_until_epoch': envelope['valid_until_epoch'],
|
||||
'receive_budget_seconds': request_budget(PRIVATE_INPUT_TOTAL_SECONDS)}
|
||||
payload = json.dumps(marker, sort_keys=True, separators=(',', ':')).encode() + b'\n'
|
||||
require(len(payload) <= READY_MAX_BYTES, 'private_ready_oversized')
|
||||
with total_timeout(request_budget(2)):
|
||||
offset = 0
|
||||
while offset < len(payload):
|
||||
written = os.write(fd, payload[offset:])
|
||||
require(type(written) is int and written > 0, 'private_ready_delivery_failed')
|
||||
offset += written
|
||||
check_deadline()
|
||||
receipt['private_ready_emitted'] = True
|
||||
except Refused:
|
||||
raise
|
||||
except Exception:
|
||||
raise Refused('private_ready_delivery_failed') from None
|
||||
finally:
|
||||
if validated:
|
||||
os.close(fd)
|
||||
|
||||
|
||||
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.
|
||||
|
|
@ -730,6 +777,9 @@ def main():
|
|||
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('--ready-fd', type=int)
|
||||
ap.add_argument('--session-nonce')
|
||||
ap.add_argument('--ready-envelope-sha256')
|
||||
ap.add_argument('--confirm')
|
||||
ap.add_argument('--receipt', required=True)
|
||||
args = ap.parse_args()
|
||||
|
|
@ -776,7 +826,9 @@ def main():
|
|||
probe_ids_absent=True, probe_env_absent=True, temporary_secret_absent=True)
|
||||
else:
|
||||
require(args.envelope is not None, 'approval_envelope_required')
|
||||
envelope = read_json(Path(args.envelope).read_text())
|
||||
envelope_raw = Path(args.envelope).read_bytes()
|
||||
envelope = read_json(envelope_raw)
|
||||
envelope_sha256 = digest(envelope_raw)
|
||||
require(Path(args.envelope).stat().st_mode & 0o022 == 0, 'approval_envelope_writable')
|
||||
signal.signal(signal.SIGTERM, lambda *_: (_ for _ in ()).throw(Refused('session_interrupted')))
|
||||
if args.mode == 'recover':
|
||||
|
|
@ -787,6 +839,8 @@ def main():
|
|||
start_execution_window(envelope)
|
||||
transport = SessionTransport(envelope['bao_addr'], receipt=receipt)
|
||||
verify_bao_route(transport, envelope, receipt)
|
||||
emit_private_ready(args.ready_fd, args.session_nonce, args.ready_envelope_sha256,
|
||||
envelope_sha256, envelope, receipt)
|
||||
private = read_private_input(__import__('sys').stdin)
|
||||
execute(envelope, private, receipt, checkpoint=checkpoint, window_started=True)
|
||||
receipt['negative_and_cleanup_verified'] = True
|
||||
|
|
|
|||
|
|
@ -177,7 +177,7 @@ class NodeRouteTests(unittest.TestCase):
|
|||
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(sys,'stdin') as stdin,patch.object(s,'emit_private_ready'),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
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ class PrivateInputTests(unittest.TestCase):
|
|||
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,'SessionTransport') as factory,patch.object(s,'emit_private_ready'),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
|
||||
|
|
|
|||
165
sso-mfa/k8s/keycape/test_openbao_private_ready.py
Normal file
165
sso-mfa/k8s/keycape/test_openbao_private_ready.py
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
"""Synthetic ready FD/control transport tests; no SSH/native/credentials."""
|
||||
import importlib.util
|
||||
from pathlib import Path
|
||||
import fcntl
|
||||
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
|
||||
spec=importlib.util.spec_from_file_location('wrapper',Path(__file__).with_name('openbao-negative-node-wrapper.py'))
|
||||
w=importlib.util.module_from_spec(spec);spec.loader.exec_module(w)
|
||||
NONCE='a'*64
|
||||
|
||||
class ReadyTests(unittest.TestCase):
|
||||
def setUp(self):
|
||||
s.CLEANING=False;s.REQUEST_DEADLINE=None;s.BUSINESS_MONOTONIC_DEADLINE=None;s.CLEANUP_MONOTONIC_DEADLINE=None
|
||||
def marker(self,envelope=None):
|
||||
e=envelope or f.envelope()
|
||||
return {'schema':s.READY_SCHEMA,'session_nonce':NONCE,'proposal_id':s.PROPOSAL,'target':s.TARGET,
|
||||
'executor_sha256':s.digest(Path(s.__file__).read_bytes()),'envelope_sha256':'b'*64,
|
||||
'bao_health_identity_sha256':e['bao_health_identity_sha256'],'valid_until_epoch':e['valid_until_epoch'],
|
||||
'receive_budget_seconds':15}
|
||||
|
||||
def test_emit_exact_ready_marker_to_distinct_local_pipe_and_close_endpoint(self):
|
||||
e=f.envelope();s.start_execution_window(e);read_fd,write_fd=os.pipe();receipt={}
|
||||
s.emit_private_ready(write_fd,NONCE,'b'*64,'b'*64,e,receipt)
|
||||
raw=os.read(read_fd,4097);self.assertEqual(os.read(read_fd,1),b'');os.close(read_fd)
|
||||
obj=json.loads(raw)
|
||||
self.assertEqual(set(obj),set(self.marker(e)))
|
||||
self.assertEqual(obj['session_nonce'],NONCE)
|
||||
self.assertEqual(obj['executor_sha256'],s.digest(Path(s.__file__).read_bytes()))
|
||||
self.assertTrue(receipt['private_ready_emitted'])
|
||||
self.assertNotIn('human_token',obj);self.assertNotIn('coding_agent_token',obj)
|
||||
|
||||
def test_stdout_alias_pipe_fd_above_two_refused_without_marker(self):
|
||||
e=f.envelope();s.start_execution_window(e);read_fd,write_fd=os.pipe();saved=os.dup(1);alias=None
|
||||
try:
|
||||
os.dup2(write_fd,1);alias=os.dup(1)
|
||||
with self.assertRaisesRegex(s.Refused,'private_ready_stdio_alias_refused'):
|
||||
s.emit_private_ready(alias,NONCE,'b'*64,'b'*64,e,{})
|
||||
finally:
|
||||
os.dup2(saved,1);os.close(saved);os.close(write_fd)
|
||||
if alias is not None:os.close(alias)
|
||||
self.assertEqual(os.read(read_fd,4096),b'');os.close(read_fd)
|
||||
|
||||
def test_nonce_or_envelope_binding_failure_emits_no_ready(self):
|
||||
e=f.envelope();s.start_execution_window(e)
|
||||
for nonce,pin in [('wrong','b'*64),(NONCE,'c'*64)]:
|
||||
read_fd,write_fd=os.pipe()
|
||||
try:
|
||||
with self.assertRaises(s.Refused):s.emit_private_ready(write_fd,nonce,pin,'b'*64,e,{})
|
||||
finally:
|
||||
try:os.close(write_fd)
|
||||
except OSError:pass
|
||||
self.assertEqual(os.read(read_fd,4096),b'');os.close(read_fd)
|
||||
|
||||
def test_actual_full_ack_pipe_write_times_out_inside_original_window(self):
|
||||
e=f.envelope();s.start_execution_window(e)
|
||||
s.BUSINESS_MONOTONIC_DEADLINE=time.monotonic()+0.015
|
||||
read_fd,write_fd=os.pipe();fcntl.fcntl(write_fd,fcntl.F_SETPIPE_SZ,4096)
|
||||
flags=fcntl.fcntl(write_fd,fcntl.F_GETFL);fcntl.fcntl(write_fd,fcntl.F_SETFL,flags|os.O_NONBLOCK)
|
||||
try:
|
||||
while True:os.write(write_fd,b'x'*4096)
|
||||
except BlockingIOError:pass
|
||||
fcntl.fcntl(write_fd,fcntl.F_SETFL,flags)
|
||||
started=time.monotonic();handler=signal.getsignal(signal.SIGALRM)
|
||||
with self.assertRaisesRegex(s.Refused,'request_total_timeout'):
|
||||
s.emit_private_ready(write_fd,NONCE,'b'*64,'b'*64,e,{})
|
||||
self.assertLess(time.monotonic()-started,0.15);os.close(read_fd)
|
||||
self.assertEqual(signal.getsignal(signal.SIGALRM),handler)
|
||||
self.assertEqual(signal.getitimer(signal.ITIMER_REAL),(0.0,0.0))
|
||||
|
||||
def test_actual_main_health_ready_then_private_read_exact_order(self):
|
||||
previous=signal.getsignal(signal.SIGTERM);read_fd,write_fd=os.pipe();events=[];received={}
|
||||
try:
|
||||
with tempfile.TemporaryDirectory() as folder:
|
||||
e=f.envelope();path=Path(folder)/'approved.json';path.write_text(json.dumps(e));path.chmod(0o600)
|
||||
pin=s.digest(path.read_bytes());receipt=Path(folder)/'receipt.json'
|
||||
argv=['session','execute','--envelope',str(path),'--receipt',str(receipt),'--ready-fd',str(write_fd),
|
||||
'--session-nonce',NONCE,'--ready-envelope-sha256',pin]
|
||||
class Input:
|
||||
first=True
|
||||
def read(self,size):
|
||||
if not self.first:return b''
|
||||
self.first=False
|
||||
received.update(json.loads(os.read(read_fd,4096)))
|
||||
self_test.assertEqual(received['envelope_sha256'],pin)
|
||||
events.append('private-read')
|
||||
return b'{"human_token":"synthetic","coding_agent_token":"synthetic"}'
|
||||
self_test=self;stream=type('Stream',(),{'buffer':Input()})()
|
||||
def health():events.append('health');return e['bao_health_identity_sha256']
|
||||
def execute(*args,**kwargs):events.append('execute');self.assertTrue(kwargs['window_started'])
|
||||
with patch.object(sys,'argv',argv),patch.object(sys,'stdin',stream), \
|
||||
patch.object(s,'SessionTransport') as factory,patch.object(s,'execute',side_effect=execute),patch.object(s,'k') as k:
|
||||
factory.return_value.health_identity.side_effect=health
|
||||
self.assertEqual(s.main(),0);k.assert_not_called()
|
||||
self.assertEqual(events,['health','private-read','execute'])
|
||||
self.assertEqual(received['session_nonce'],NONCE)
|
||||
finally:
|
||||
signal.signal(signal.SIGTERM,previous);os.close(read_fd)
|
||||
try:os.close(write_fd)
|
||||
except OSError:pass
|
||||
|
||||
def test_actual_main_missing_ack_fd_never_reads_private_input(self):
|
||||
previous=signal.getsignal(signal.SIGTERM)
|
||||
try:
|
||||
with tempfile.TemporaryDirectory() as folder:
|
||||
e=f.envelope();path=Path(folder)/'approved.json';path.write_text(json.dumps(e));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') as stdin,patch.object(s,'SessionTransport') as factory,patch.object(s,'k') as k:
|
||||
factory.return_value.health_identity.return_value=e['bao_health_identity_sha256']
|
||||
self.assertEqual(s.main(),1);stdin.buffer.read.assert_not_called();k.assert_not_called()
|
||||
self.assertEqual(json.loads(receipt.read_text())['reason'],'private_ready_pipe_required')
|
||||
finally:signal.signal(signal.SIGTERM,previous)
|
||||
|
||||
def test_wrapper_marker_pin_nonce_fields_and_window_failures_refuse(self):
|
||||
e=f.envelope();marker=self.marker(e)
|
||||
for key,value in [('session_nonce','c'*64),('executor_sha256','c'*64),('envelope_sha256','c'*64),
|
||||
('target','other'),('proposal_id','other'),('bao_health_identity_sha256','c'*64),
|
||||
('valid_until_epoch',True),('receive_budget_seconds',True),('receive_budget_seconds',float('inf'))]:
|
||||
bad=dict(marker,**{key:value})
|
||||
with self.subTest(key=key),self.assertRaises(w.Refused):
|
||||
w.validate_marker(json.dumps(bad).encode()+b'\n',nonce=NONCE,executor_pin=marker['executor_sha256'],envelope_pin='b'*64,envelope=e)
|
||||
with self.assertRaises(w.Refused):
|
||||
w.validate_marker(json.dumps(dict(marker,extra=True)).encode()+b'\n',nonce=NONCE,executor_pin=marker['executor_sha256'],envelope_pin='b'*64,envelope=e)
|
||||
|
||||
def test_wrapper_ready_pipe_no_eof_is_bounded(self):
|
||||
read_fd,write_fd=os.pipe();os.write(write_fd,b'{"incomplete":true}')
|
||||
started=time.monotonic()
|
||||
try:
|
||||
with self.assertRaisesRegex(w.Refused,'wrapper_timeout'):
|
||||
with w.deadline(0.015):w.read_ready(read_fd)
|
||||
finally:os.close(write_fd);os.close(read_fd)
|
||||
self.assertLess(time.monotonic()-started,0.15)
|
||||
|
||||
def test_wrapper_pinned_node_child_launch_is_local_pipe_not_ssh_fd_forward(self):
|
||||
with tempfile.TemporaryDirectory() as folder:
|
||||
child=Path(folder)/'child.py';child.write_text('# synthetic child, never executed\n')
|
||||
envelope=f.envelope();envelope['source_hashes']={str(child):w.hash_bytes(child.read_bytes()),str(Path(w.__file__).resolve()):w.hash_bytes(Path(w.__file__).read_bytes())}
|
||||
env=Path(folder)/'approved.json';env.write_text(json.dumps(envelope));env.chmod(0o600)
|
||||
args=type('Args',(),{'child':str(child),'child_sha256':w.hash_bytes(child.read_bytes()),'envelope':str(env),'envelope_sha256':w.hash_bytes(env.read_bytes()),'receipt':str(Path(folder)/'receipt.json'),'session_nonce':NONCE})()
|
||||
class Process:
|
||||
stdout=io.BytesIO();stderr=io.BytesIO()
|
||||
def wait(self,timeout):return 0
|
||||
def poll(self):return 0
|
||||
def launch(command,**kwargs):
|
||||
self.assertEqual(command[0],sys.executable)
|
||||
self.assertEqual(command[1],str(child));self.assertNotIn('ssh',command)
|
||||
self.assertEqual(kwargs['stdin'],sys.stdin.buffer)
|
||||
fd=int(command[command.index('--ready-fd')+1]);self.assertEqual(kwargs['pass_fds'],(fd,))
|
||||
marker=self.marker(envelope);marker.update(executor_sha256=args.child_sha256,envelope_sha256=args.envelope_sha256)
|
||||
os.write(fd,json.dumps(marker).encode()+b'\n')
|
||||
return Process()
|
||||
with patch.object(w.subprocess,'Popen',side_effect=launch),patch.object(w,'forward_ready') as forward:
|
||||
self.assertEqual(w.run(args),0);self.assertEqual(forward.call_count,1)
|
||||
self.assertNotIn('human_token',json.loads(forward.call_args.args[0]))
|
||||
|
||||
if __name__=='__main__':unittest.main(verbosity=2)
|
||||
|
|
@ -290,3 +290,53 @@ 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.
|
||||
|
||||
T07 same-trial v9 concrete private-ready protocol preparation: exact source
|
||||
manifest 84542ddcff00932fc7ce22dcb2626be8942a3c006c5098a62fdd6b2478d0cd14 follows published v8 while retaining v5/v6/v7/v8 and
|
||||
producer v1 consumer-refusal history. Child emits bounded value-free nonce/pin/
|
||||
window-bound marker via distinct local inherited pipe only after exact source/
|
||||
approval/public-health and before private input; stdio aliases refuse. A node
|
||||
wrapper creates that local pipe, launches pinned child/envelope, relays one
|
||||
marker on captured SSH control stdout and never reads/copies private stdin.
|
||||
No extra FD is forwarded by SSH/Bridge, no marker authenticates authority/peer,
|
||||
and the output-suppressed warden lane is explicitly incompatible. Independent
|
||||
KeyCape producer/launcher source is coordinated but not yet exact reviewed or
|
||||
admitted; it must await marker before loading/consuming files or writing tokens.
|
||||
Forty-six lifecycle, twelve route/main, seven receive, nine new ready/wrapper and
|
||||
twenty-one retained independent tests pass offline; real pipe timeout/alias and
|
||||
actual main ordering checked. No SSH, native child/health/tunnel, credential or
|
||||
owner checkout action occurred. Fresh independent KeyCape/Platform reviews,
|
||||
trusted founder SSH/launcher/private-file/native admission and real owner
|
||||
acceptance remain necessary; same completed T07, no duplicate workplan.
|
||||
|
||||
T07 same-trial independent NetKingdom consumer source review of exact KeyCape
|
||||
producer V3 manifest 569672ecdb3f1f5b46122183dc3b82e83ff168c07c87db7211ec26bc1ce6184a paired with unchanged Net V9 manifest
|
||||
84542ddcff00932fc7ce22dcb2626be8942a3c006c5098a62fdd6b2478d0cd14: completed source-only analysis and offline validation;
|
||||
changes requested because actual launcher CLI contract FIFO acquisition blocks
|
||||
before its preparation timer. V2 also reproduces and is preserved. Thirty-three
|
||||
maker tests and nine independent actual local fake-SSH -> real pinned wrapper ->
|
||||
real child-main handshake checks pass, with public health and native execution
|
||||
explicitly mocked, synthetic unsigned tokens only. Wrong nonce/marker, health
|
||||
failure, expired approval and source mismatch perform zero token-file loads;
|
||||
quietness failure and real partial pipe delivery retain unknown consumer outcome.
|
||||
V3 sticky unknown correction passes. Exclusive same-UID directory custody is
|
||||
required; unlink is non-atomic and no zeroization/copy cleanup is proved. Prior
|
||||
v5-v8 reviews, V9 own anchor and original producer refusal remain unchanged.
|
||||
No KeyCape formal role, authenticated marker authority, native/source admission,
|
||||
credential reads, SSH, tunnel, repository writes or workplan closure inferred.
|
||||
|
||||
T07 same-trial fresh independent NetKingdom consumer source review of exact
|
||||
KeyCape producer V4 manifest d6f641dc1e4aae7d36658d4199d89af14d2fd7a4bd4e526856eb340234f2d33e paired with unchanged Net V9 manifest
|
||||
84542ddcff00932fc7ce22dcb2626be8942a3c006c5098a62fdd6b2478d0cd14: completed source-only qualified agreement. Actual
|
||||
CLI rejects FIFO/symlink/oversize contracts before private reads, slow main
|
||||
acquisition and default caller validation are bounded, and acquisition plus
|
||||
launch retain one original preparation deadline. Forty maker tests and thirteen
|
||||
independent checks pass, including nine actual local fake-SSH/pinned-wrapper/
|
||||
child-main handshake checks with fixture public health and native execution.
|
||||
V1 and V3 refusals, V2/V3 FIFO counterexamples, V3 sticky unknown correction,
|
||||
v5-v8 histories and V9 own preparation anchor remain unchanged. Exclusive
|
||||
same-UID directory custody, admitted pinned owner launcher/SSH host profile,
|
||||
private file allowance and exact founder native authorization remain required;
|
||||
marker and unsigned claims authenticate neither authority nor issuer. No
|
||||
credential read, native command, real SSH, owner checkout write or native/task
|
||||
closure occurred. This source agreement does not approve transport or custody.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue