key-cape/docs/openbao-negative-token-producer-decision.md
tegwick 0f47e0c666 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
2026-10-11 18:31:04 +02:00

23 KiB

KEY39 private token producer and node-route decision packet

Proposal KEY-WP-0039-T02/private-producer-handoff-v1, source preparation only. Lifecycle V5 is unchanged (manifest d1f997624db46693a7126a6eb6435f6b3a353d5bd67dcc8a539683e4b7147285). This packet names concrete supported flows and a reviewable local handoff. It supplies no admitted credential route, authenticated approval or native readiness. T01 stays done and T02 open; no new workplan or task is created.

Existing flows and exact owner choice

Choose existing netkingdom-bootstrap-console for the human class refusal if NetKingdom/founder approve this one attended use of their existing client and literal http://127.0.0.1:8876/oidc/callback listener. Its source registration is public authorization_code, scopes openid/profile/email/groups, default audience netkingdom-bootstrap-console. Request only openid; do not request approval or OpenBao permissions. keycape login verifies RS256 issuer/access audience/scopes, ID-token client audience/nonce and matching subject. It binds before emitting a browser URL, validates exact callback host/path/state, uses S256 PKCE, and has a five-minute attempt bound. The provider handles password/MFA. Fresh deployed MFA/registration evidence is required; the handoff requires human aal2/mfa=true. Do not infer MFA from the client fragment: it has no per-client mfaRequired pin. Do not run the bootstrap console simultaneously on port8876. The same client's localhost callback is not substituted: the CLI requires a literal loopback IP.

Concrete producer invocation after that decision, on the attended workstation:

keycape login --issuer https://kc.coulomb.social \
  --client-id netkingdom-bootstrap-console --audience netkingdom-bootstrap-console \
  --scope openid --redirect-uri http://127.0.0.1:8876/oidc/callback \
  --out /OWNER-PRIVATE-TMPFS-DIR/key39-human.json

The private directory is new owner-controlled mode0700 tmpfs outside Git; the CLI creates the file mode0600 exclusively. No invocation is performed here. Current browser access-token lifetime is server TokenLifetime; per-client TokenLifetime is used only for client_credentials. Do not promise a new one-minute human lifetime by adding that field to a browser registration.

Alternative only if the owner rejects existing-client use: request one separately reviewed public client key39-negative-human, audience of the same name, exact callback http://127.0.0.1:8877/oidc/callback, scopes [openid], grantTypes [authorization_code], mfaRequired true, no role/serviceSubject/secretRef. That requires a new source registration, reviewed native merge/rollback and changed handoff audience binding. It is not part of V5's six temporary clients and must not silently enter that mutation. The current adapter intentionally refuses it. Reuse of informed-decision-approver is unsuitable for this CLI: its registered callback is HTTPS decisions.coulomb.social, not literal loopback. OpenBao admin is another source-compatible callback but its admin relying-party purpose is unnecessary for this probe and is not the selected plan.

Coding-agent producer is exactly the existing accepted source identity:

keycape service-token --issuer https://kc.coulomb.social \
  --client-id codex-railiance-platform --audience codex-railiance-platform \
  --scope openbao:login --secret-env KEYCAPE_CODEX_RAILIANCE_PLATFORM_CLIENT_SECRET \
  --out /OWNER-PRIVATE-TMPFS-DIR/key39-agent.json

Subject service:codex:railiance-platform, tenant:platform, roles [coding-agent], principal_type service; fifteen-minute JWT, no refresh or ID token. The secret must arrive from an existing admitted owner-controlled delivery. No client secret request or generation occurs here. A source fragment is not live registration or consumer entitlement. The inspected Platform operational identity is AppRole; its OpenBao token cannot substitute for this JWT. Its historical coding-agent prose still names tenant:coulomb, whereas current KeyCape source uses tenant:platform. Platform must reconcile its exact proposed/installed metadata, not use that historical prose as evidence.

No reviewed coding-agent client-side custody reader/consumer delivery receipt was found in the inspected CCR/registry sources. Platform must either return exact existing admission coordinates and fresh live registration/delivery evidence or own a new narrow custody/issuer/consumer admission proposal. Do not borrow secrets-engine's credential, widen its role, read raw KV, or mint an ephemeral coding-agent lookalike to manufacture this case. If unavailable, the joint session remains blocked and coding-agent refusal inconclusive; V5 requires both private token inputs and cannot silently skip the case.

Private handoff adapter

scripts/openbao_negative_private_handoff.py adapts the two existing CLI files to V5 private stdin JSON keys human_token and coding_agent_token. It makes no network request, login, secret retrieval, registration, deployment or executor launch. The source is Linux main-thread only. Before file consumption it requires an inherited writable pipe FD >=3, never stdout/stderr, a new owner mode0700 non-symlink directory outside Git, single-link mode0600 regular files owned by the current UID, bounded reads and unchanged inode/size/timestamps. JSON rejects duplicate/nonfinite keys; token length is bounded. Both files must be distinct. A second inode/metadata compare immediately precedes each unlink.

The adapter checks decoded issuer/audience/principal/agent claims and at least 300 seconds remaining lifetime, plus human MFA shape. These are not signature verification or authenticated custody: its tests deliberately use unsigned synthetic JWT shapes. The founder must run the pinned current KeyCape CLI and retain exclusive custody continuously; arbitrary downloaded/preexisting files are inadmissible despite matching mode or claims. CLI signature verification is the issuer-verification boundary. Require a fresh remaining-lifetime check before native execution and ensure token validity through the actual presentation; the five-minute adapter floor is not a grant for a longer session.

Explicit --consume-owner-files authorizes removal of precisely these inspected caller-owned files. After both load successfully, the adapter consumes them before writing the pipe and closes its inherited endpoint on every exit. Partial unlink failure records possible retained files; partial/broken/timed-out delivery records unknown delivery and must stop before native mutation, with no retry. Delivery has a hard five-second SIGALRM bound; preexisting timer refuses and handler/timer state is restored. Receipts contain booleans and fixed failure codes only; a pipe write completion does not prove downstream validation or native acceptance. No stdout token path exists. A private pipe still requires a trusted consumer: the adapter cannot authenticate its peer, approval JSON or native go.

The founder-reviewed launcher must create the pipe and start a pinned destination wrapper before running the adapter with that endpoint inherited as FD3. The wrapper may invoke the already approved SSH transport to railiance01 and forward stdin without PTY, shell tracing, tee, terminal/stdout copies, token argv/env, logging, agent custody or persistent remote files. The destination reads the private JSON with V5; no additional file copy. Exact workstation producer and node wrapper paths, hashes, SSH host identity/transport grant, consumer startup order, refusal gating and receipt handling must be owner-reviewed first. This packet does not ship or admit such a launcher and cannot claim a full handoff. Token files must be consumed before starting V5; on failed preparation the owner removes remaining exact files. This packet requires founder choice to admit the two bounded caller-owned JWT files: V5's no-copy rule for generated six client secrets is unchanged. Python memory/Unix unlink are not secure-zeroization claims; use an owner tmpfs directory and process termination. Issued JWTs expire normally. No issuer-side JWT revocation is claimed. Any OpenBao acceptance remains V5's bounded self-revoke/Platform reconciliation obligation.

Mandatory route and decision ordering

The child is a Linux main-thread process on railiance01 using its k3s kubeconfig. Workstation loopback18200 tunnel evidence does not prove node loopback18200; openbao.openbao.svc normally resolves from Pods, not necessarily the host. Keep native proposal gated until Platform/NetKingdom verify one of V5's exact origins from the actual node process placement, using a separately reviewed founder port-forward path if required. Do not expand V5 origins or use workstation role observations as route proof.

Before any token exchange, temporary credential generation, Secret creation, registry/env mutation or restart, the owner preflight must run a bounded public Bao health request from that exact placement to the selected origin. It follows no redirect or ambient proxy, caps response bytes and total elapsed time, sends no auth header and prints only normalized initialized/unsealed/health booleans. Verify acceptable active/standby health semantics independently, current TLS or local port-forward ownership, no unintended public bind and non-secret issuer connectivity. No auth, KV/data path or raw error output. The V5 transport currently allows login/revoke only; this additional public health/route preflight belongs in the reviewed launcher/NetKingdom guard, not an unreviewed V5 edit.

Owner sequence: select source plan -> independent KeyCape/NetKingdom/Platform reviews -> approved exact launcher plus route/health preflight -> admitted producer/custody and human browser attempt -> private-pipe delivery -> destination validates complete stdin/envelope/source/runtime/role receipts -> native action only under fresh exact founder authorization -> V5 cleanup/revoke plus producer cleanup. Any unavailable route/custody/client/complete input keeps T02 open.

Exact acknowledgments still required

  • KeyCape: selected client/claims/CLI verification, lifetime and MFA source; this packet is preparation, not a formal source acknowledgment of a launcher.
  • NetKingdom: actual existing registration/image, client reuse and callback listener ownership, browser/workstation/node placement, guarded public health preflight ordering and exact restart/cleanup runtime pins.
  • Platform: coding-agent existing custody/consumer entitlement or new proposal, corrected tenant metadata, route/port-forward ownership, fresh bounded role/ leeway/effective-policy observation, session revoke and unknown reconciliation.
  • Founder: existing-client purpose reuse versus new registration, explicit private tmpfs JWT-file allowance, admitted exact launcher/transport/target/window/cleanup obligation and actual supervised custody. Source acceptance is not that go.

18 synthetic adapter checks pass: successful private write and file consumption, stdout/stderr/regular/read-only FD refusal, explicit consumption, symlink/hardlink/ public mode/parent/Git/oversize/duplicate JSON refusal, wrong audience/MFA/tenant/ role/AppRole/expiry refusal, replacement before unlink, hard write timeout with signal restoration and value-safe partial/broken delivery. Original V5/failed review histories and all source/current owner records remain retained.

V2 correction and exact child-ready integration

The V1 packet text above is retained history. Proposal KEY-WP-0039-T02/private-producer-handoff-v2 supersedes its unsafe no-ready adapter and its precise-inode-removal wording. V1's immutable manifest239aaa and NetKingdom consumer refusal a1ed14e6b2db1ae77c3fd638fd7e47b5bd50c25eebba418e31aba89bb4b7f88e remain recorded as the same correction trial. The exact compatible consumer is KEY-WP-0039-T02/attended-lifecycle-v9-ready-input, separately pinned below; V5/V6/V7/V8 source/review history is never implicitly repinned. No owner admission or founder go is supplied by V2.

The KeyCape launcher is scripts/openbao_negative_private_launcher.py. It validates its own and its adjacent adapter's exact source hashes plus the local copy of the exact remote envelope. The remote child/wrapper paths and hashes must occur in that envelope; proposal/target/source owner receipts, finite window/health pin and founder/file-allowance receipt references must match the reviewed contract. These fields are procedural constraints only: editable JSON cannot authenticate approval. The founder must authenticate/retain the actual exact disposition and own this entire launcher/custody/host context.

The source launcher alone creates SSH stdin/stdout pipes, a fresh random256-bit nonce and fixed direct SSH argv to railiance01. It uses no PTY, strict existing host-key checking, batch mode, no agent forwarding, no remote forwarding, finite connection/keepalive bounds, and a minimal environment that excludes confidential client credentials. It does not create or request a certificate, token, credential or route. This is a proposed separate founder-admitted direct SSH profile: the normal warden silent lane has different stdout rules and is not silently repurposed. The founder/transport owner must review actual node host identity, SSH certificate/session entitlement and pinned remote paths before execution. No SSH command was invoked during preparation.

On railiance01 the independently NetKingdom-owned pinned wrapper creates a local dedicated child-ready pipe and launches the exact pinned Python child with inherited SSH stdin. No invented extra SSH file-descriptor forwarding is used. Only after exact source/envelope checks, original action-window start and pinned public Bao health succeeds does the child emit a bounded value-free marker to that local pipe, close it, and enter the V8 bounded private receiver. The wrapper validates that local marker and forwards exactly one newline JSON marker on SSH stdout. SSH stdout stays open until child completion; Key reads only to the first newline, never waits for SSH stdout EOF. Child stdout/stderr stay suppressed; protected receipt remains exclusive on the node.

Ready schema key39-private-input-ready/v1 has exactly these keys: schema, session_nonce, proposal_id, target, executor_sha256, envelope_sha256, bao_health_identity_sha256, valid_until_epoch, receive_budget_seconds. Marker must bind the new nonce and exact consumer proposal/target/source/envelope/health pin and epoch; receive budget must be finite nonboolean, positive and <=15s. Marker size is <=4096 bytes. An old/missing/malformed/mismatched/failed/expired ready marker produces zero token file loads, zero consumption, zero private bytes, then closes local endpoints. A readiness marker is not authentication, JWT verification, custody admission, routing authority or a live grant. It is trusted only because the reviewed launcher captures the intended pinned wrapper under the separately admitted authenticated host/session context. There is no standalone adapter CLI accepting a ready file, boolean or user-provided marker.

The adapter validates two distinct kernel pipe identities: private data endpoint write-only, control endpoint read-only, each FD>=3 and each device/inode distinct from stdin/stdout/stderr and from each other. Actual duplicates of stdout/stderr are refused even when their descriptor number is >=3. Captured SSH stdout is a separate control pipe owned by the launcher; it never becomes token output.

The launch preparation cap is5s. The adapter has one total5s deadline covering ready wait, caller-file load, cleanup and delivery. After ready this existing timer only clips downward against its remaining budget, received child budget and current remaining approval lifetime. Advertised child budget is not compared as <=current remaining wall time (transport necessarily consumes time); the actual delivery timer clips to both. Before each write and after completion the same absolute expiry/monotonic delivery bound is rechecked. No retries or clock reset; stdin closes to provide complete EOF. A partial write, transport break or expiry remains unknown and cannot be native proof. Launcher waits only for a value-free child/transport exit under remaining action +300s cleanup +30s margin; local transport teardown never proves remote custody or process reconciliation. Native receipt/unknown-session acceptance remains Platform/NetKingdom's.

Honest file-cleanup boundary

Owner mode0700/0600, no symlink/hardlink/Git and unchanged identity/size/time checks remain. The compare and unlink are not atomic inode-conditioned delete. Continuously exclusive same-UID custody of the directory, no concurrent writers and no attacker sharing that UID are mandatory for this proposed file approach. No helper boolean can prove that condition. The founder must control the new private tmpfs directory/process context and retain exclusive custody; no shared agent/shell/daemon may modify it. Any known interference requires abort and owner reconciliation before another attempt.

V2 removes V1's exact-inode/atomic implications. Its retained stat-to-unlink race check deliberately shows that a concurrent same-UID replacement can still be removed; that is outside the admitted exclusive-custody assumption and is not represented as a fixed filesystem capability. Even after nominal path removal, receipts retain atomic_inode_delete_guaranteed=false, exclusive_same_uid_custody_required=true, credential_copy_cleanup_verified=false and credential_files_remain_possible=true. caller_path_removal_completed means only these unlink calls completed under that assumption. It is not token copy inventory, secure erase or cleanup proof. Owner verification/process termination/tmpfs teardown remains required; issued JWTs expire normally. If that exclusive context cannot be admitted, do not use these private files; separately implement/review a process-to-process in-memory producer instead.

The existing human-client reuse versus new registration, live MFA, coding-agent client-side custody/consumer delivery, exact authenticated launcher/SSH profile, private tmpfs files/exclusive custody, actual node route/provider/role/leeway, quiet restart window and fresh founder disposition are still unapproved choices. This source packet does not resolve them. Source-pinned readiness eliminates premature token transport only within the admitted trusted launcher context.

Exact compatible consumer manifest SHA256 84542ddcff00932fc7ce22dcb2626be8942a3c006c5098a62fdd6b2478d0cd14; child 24b7b803e4eb7e6e7c0700a069dad0ec315ee7eb5538a88b9e56147f8fab5cdd; node wrapper 76ac013d1a676a0dfa58aea1ce87b0c3e72483ccd21f586a22c5475e9132f6c1. 31 distinct supplied producer/launcher synthetic checks pass; 74 supplied compatible consumer source checks pass. No actual SSH, login, tokens, custody or native route was used. Fresh independent consumer and Platform source reviews of this exact V2 and V9 remain necessary before founder choice.

V3 remote-unknown reporting correction

Proposal KEY-WP-0039-T02/private-producer-handoff-v3. Exact NetKingdom V9 manifest84542ddc and child/wrapper bytes are unchanged; this is only a new Key launcher reporting revision in the same preparation trial. Platform V2 pair review finding RPF-KEY39-PAIR-V2-F01 is retained unchanged, SHA256 8353eb8aae11be8ce874887d6a5ca10bf37c6fba73c3f85f0644a7546dd7d33f.

After copying adapter outcome, private_input_delivery_unknown=true immediately sets sticky consumer_outcome_unknown=true and consumer_process_completed=false before any failure/early return. A complete payload can reach the remote child before local write/EOF confirmation times out. Terminating local SSH cannot prove that no remote operation occurred, that the child stopped or that remote custody/cleanup completed. No retry or subsequent local success rewrites this attempt. Native acceptance and unknown resolution stay independent owner duties.

33 distinct checks pass, including actual full kernel-pipe JSON write followed by SIGALRM interruption before confirmation, and an actual4096-byte partial write/timeout; both retain remote unknown after local terminate. No actual SSH, protected consumer or credentials were involved. All V2/V1 source, unsuccessful reviews and previous narrow issuer acknowledgments remain frozen history. Exact source return and all prior native/client/custody/transport/file choices remain required. Source reporting correction is not runtime admission.

V4 source-acquisition and shared preparation deadline correction

Proposal KEY-WP-0039-T02/private-producer-handoff-v4, same trial. The exact Net V9 manifest84542ddc and child/wrapper are unchanged. NetKingdom V3 consumer refusal NET-PRODUCER-V3-F01 stays frozen, SHA256 38abf660701194f85934a076a0c01d3047e6bfe790e02ba1b0e4616e356634e6. V1/V2/V3 source and earlier acknowledgments/refusals remain history.

Actual CLI now starts one absolute monotonic5-second preparation deadline before contract acquisition. Initial source read, source/envelope/hash checks and SSH spawn then ready/file/consumption/write phase all use only remaining time from that original deadline. Phase timers may disarm/rearm, but each is clipped to original remaining time; launch never restarts the budget spent reading contract. After ready, child receive budget and absolute approval expiry only clip further. Once private input is delivered, existing separate action/cleanup completion wait remains; it cannot renew preparation or mutation authority.

Contract/envelope reads use bounded4096-byte chunks, at most65536 bytes, absolute paths, no observed symlink ancestry, O_NOFOLLOW/O_NONBLOCK, single-link regular files with no group/world write, and stable device/inode/UID/mode/link/size/time comparisons. FIFOs/devices/symlinks/empty/oversized files refuse before private file access/SSH. Source digest reads share that path with at most262144 bytes. All actual CLI source acquisition runs under the finite remaining preparation alarm; a stalled filesystem call cannot escape this total deadline. Observed path/source races refuse; continuously trusted exclusive custody remains required and no atomic pathname/exec protection or authenticated metadata grant is claimed.

40 distinct producer/launcher checks pass, including actual CLI FIFO/symlink/ oversize refusal, delayed regular read under the hard timer with restored signals, a main contract-read delay followed by launch delay that cannot renew the budget, source drift/chunk bounds and earlier actual full/partialwrite stickyunknown. Nine retained real paired handshake regressions also pass using actual pinned Net wrapper and child main with only local fake SSH/health/execute substitutions: no network, Kubernetes, protected credentials or native mutation. The original Net fixture files are copied unchanged except the Key stage path, solely as source regression evidence, never a new foreign formal owner review.

Fresh exact independent pair source returns and all unadmitted human/MFA/client/ coding-agent custody/consumer/directSSH/exclusive tmpfs/route/provider/role/leeway/ readiness-Argo/maintenance-exception/founder/native-acceptance duties still gate use. No source-only helper choice, ready marker or editable approval JSON supplies that authority. T01done/T02open; actual native outcome remains pending.