the-custodian/docs/changes/netkingdom-target-2026-10-17/native-runtime-install-r2/PROCESS-CONTRACT.md
codex ffd995213c
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Reject generated-only commits / generated-commit-guard (push) Successful in 8s
Record failed runtime trial and reviewed verification proposal (1/4)
2026-10-17 12:56:53 +02:00

4.8 KiB

Revision 2 process ownership and cleanup contract

Proposal REINAH-WP-0008-T07-install-f599-r1, revision 2. No execution approval. R1 is retained unchanged. No owner repository or immutable runtime is edited.

The public artifact retains its exact f599 digest. The pinned existing rein_aharness.owner_rehearsal.rehearse(runtime, *, run=..., bwrap_bin=...) API already admits a Runner callback. R2 supplies that callback rather than using the default _run, whose subprocess.run timeout only waits/kills its immediate process. The callback passes the same owner-built readonly/empty-egress argv with /usr/bin/bwrap and one added --die-with-parent flag to the private bounded runner. The source hash and all three generated arrays are frozen separately. No default owner behavior elsewhere is claimed repaired. A future owner-wide cleanup fix is separate; no source/authority migration is made here.

The one-action watchdog is not an installed service. Its code and private state live only in the exact revision-2 staging directory. It is detached from the SSH/controller session so controller SIGKILL does not prevent cleanup. It binds the root controller with pidfd, becomes a child subreaper before spawning, and spawns every phase plus every nested launcher itself in a distinct session. All native job argv remain public. No shell, credentials, configurations, provider, catalog, lease, ledger or queue is admitted to the runner.

A private JSON request includes caller PID/start/UID, argv, deadline and capture choice. The caller must be the root or an actual watchdog descendant. Each job is owned/waited by the watchdog; a nested launcher is a sibling of its requesting phase, so outer timeout must abort all owned jobs rather than merely killing the phase process group. PR_SET_PDEATHSIG prevents pre-exec parent-loss races. The bwrap parent-death flag is defense for its sandbox descendants if their watchdog/bwrap parent dies; the target's 0.9.0 help/manual confirm its support.

On timeout, root/caller loss, abandoned descendants, excess output or mapping refusal, the watchdog stops the action. As a subreaper it adopts session-escaped orphans. It enumerates actual PPID descendants, binds each with pidfd, checks UID and start-time mapping, sends TERM, then KILL and reaps. It never signals by name, guessed PID, shared session or global process group. Any uncertain mapping fails the cleanup proof even if best-effort stopping appears complete. No further launch can occur after that failure. New requests after abort are refused.

The controller closes the watchdog before staging deletion and requires BOTH its waited successful exit and quiescent=true / no_live_or_waitable_descendants=true. Exact staging identity and inventory checks still apply. A missing/negative receipt, stuck watchdog or unexpected object retains staging. Abrupt watchdog loss has no independent positive quiescence proof, so cannot enable deletion. An SSH timeout does not establish controller death or permit cleanup.

Event Proof retained / allowed cleanup
Three launchers succeed and watchdog shuts down Structured exits, full runtime rehash, waited positive quiescence; exact owned stage may be deleted
Phase/launcher timeout All owned phase/nested descendants stopped/reaped first; positive proof permits only exact stage deletion; inactive artifact retained
Controller SIGKILL Watchdog reacts through root pidfd, stops/reaps descendants, writes evidence; staging remains
Watchdog loss, uncertain mapping, missing receipt Failed/unverified outcome, stage retained; no broad kill/delete
Upload failure proven before any exec Separate conditional helper accepts only exact revision-2 before_transfer receipt/inode and four regular upload files
Remote exec started but SSH response lost No retry or cleanup command; reconcile exact identities/quiescence/digest read-only and seek root recovery review

The five local fixture cases use actual disposable Python descendants, including setsid escape and a nested phase-to-runner request. Controller loss uses SIGKILL. An unrelated sibling must remain alive. Results check positive/negative cleanup receipts and discarded application stderr. The fixture does not call bwrap, SSH, the runtime, a provider or any native owner interface. It tests process ownership/cleanup behavior; native compatibility remains the proposed action.

Bounds: install phase 200 seconds / inner alarm 180; rehearsal phase 260 / inner alarm 240; each unchanged launcher 60; output 64KiB; TERM grace 0.5 seconds; KILL/reap 3 seconds; final watchdog wait 8 seconds. Scheduler or kernel failure can exceed wall-clock expectations; missing proof fails closed. Host loss may also leave the owner's independent .install-* copy. Its exact ownership cannot be inferred from its prefix, so this packet authorizes no store sweep/removal.