Record failed runtime trial and reviewed verification proposal (1/4)
This commit is contained in:
parent
4097a0bfbc
commit
ffd995213c
16 changed files with 1298 additions and 0 deletions
|
|
@ -0,0 +1,254 @@
|
|||
# Install and verify the immutable Glas/rein runtime on railiance01
|
||||
|
||||
Proposal ID: **REINAH-WP-0008-T07-install-f599-r1**, revision **2**.
|
||||
Status: **pending root review and exact founder approval; no native mutation**.
|
||||
R1 remains frozen, unapproved and unexecuted. This is revision 2 of the same
|
||||
proposal/trial, addressing root review of descendant cleanup. This is a
|
||||
separately reviewable installation step under existing T07. It does
|
||||
not require completing the paid-attempt envelope because it uses no provider,
|
||||
worker credential, owner/spend configuration, catalog change or queue action.
|
||||
It does not activate the artifact or complete T07, GLAS-WP-0012-T03/T04, the
|
||||
current Custodian assignment migration or TSA-06.
|
||||
|
||||
## Exact action and target
|
||||
|
||||
After root retains this exact revision and the supervisor's disposition, run:
|
||||
|
||||
```bash
|
||||
python3 -I -B /home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-17/native-runtime-install-r2/execute-install.py --apply
|
||||
```
|
||||
|
||||
The same file's default/`--dry-run` is read-only. `--apply` refuses any existing
|
||||
local execution receipt, so a failed/uncertain attempt cannot be rerun blindly.
|
||||
There is no scheduling, continuous claim loop, profile readiness override,
|
||||
credential acquisition, provider request, owner renewal or service restart.
|
||||
|
||||
| Input | Exact value |
|
||||
|---|---|
|
||||
| Host | existing SSH alias railiance01; Kubernetes node must be Ready at 92.205.62.239 |
|
||||
| Account | tegwick, UID/GID 1000, home /home/tegwick |
|
||||
| Existing protected store | /home/tegwick/.helixforge-factory/runtimes, mode 0700, device 2049, inode 4751222 |
|
||||
| New destination | /home/tegwick/.helixforge-factory/runtimes/f59959b605a51abd2901becde14e7abd852c2bd3f848004b51d250d1cb81af6d |
|
||||
| Complete runtime digest | sha256:f59959b605a51abd2901becde14e7abd852c2bd3f848004b51d250d1cb81af6d |
|
||||
| Retained source artifact | /tmp/netkingdom-runtime-canonical-20261016/runtime |
|
||||
| Exact new owned staging | /home/tegwick/.helixforge-factory/runtime-install-f599-r2-20261017 |
|
||||
| Transfer archive | deterministic gzip/PAX tar, 109,931,109 bytes, 1,448 members |
|
||||
| Transfer SHA-256 | sha256:4c55b2740e759175d645705518fc33db30b26b4611f68e052a4ca909173084c7 |
|
||||
| Logical regular-file bytes | 260,668,241; one retained internal relative symlink, lib64 → lib |
|
||||
|
||||
`action-inputs.json` freezes every executable/helper hash and archive input.
|
||||
`artifact-manifest.json` binds this proposal and all retained review evidence.
|
||||
Only four named payload files go into remote staging: runtime.tar.gz,
|
||||
runtime.py, install-verified.py and process_supervision.py. The private
|
||||
one-action watchdog and its JSON receipts exist beneath that staging only;
|
||||
they create no service, schedule or persistent control plane. The workstation archive is regenerated
|
||||
privately from the exact source only at execution, checked against the frozen
|
||||
archive hash, transferred, and removed from its local temporary directory.
|
||||
No large runtime archive or new source build is committed to this repository.
|
||||
|
||||
## Existing owner contract and actual observations
|
||||
|
||||
Sand-boxer `docs/bwrap-runtime.md` explicitly separates protected artifact
|
||||
installation from profile selection, credentials, network activation and schedules.
|
||||
The existing `runtime_store.install_runtime` verifies source, checks the owned
|
||||
store and lock, copies symlinks/modes into its own private staging, verifies the
|
||||
copy and atomically publishes under its digest. It never replaces an existing
|
||||
artifact. This proposal uses that exact implementation already in f599; it does
|
||||
not introduce another store or installer service. Remote owner source checkouts
|
||||
are absent, so the pinned public digest reader/driver travel with the artifact.
|
||||
|
||||
Exact installed component hashes:
|
||||
|
||||
- sandboxer runtime.py: sha256:19b5fb687288bbc836978b0251599dc1e4d1593e8b5efee85a6abb6a7f388412
|
||||
- sandboxer runtime_store.py: sha256:c3d6ec09d08881611d3d02b8f55d68deed6ecfedaa1a809b6b11c57cc4ca97cf
|
||||
- rein owner_rehearsal.py: sha256:192f654cad4a5f065c7faa5e35b03493297a472e8d7fe641be2c42c5fb00dcc3
|
||||
|
||||
Runtime/store components match both the artifact's sandboxer `221126cb` and
|
||||
current owner source `53f800c4`; the rehearsal matches artifact rein `9b88cadaa8`.
|
||||
The complete build includes Glas `7357115c` and llm-connect `37436bb5` from the
|
||||
previously reviewed immutable build. Reuse its actual manager/gateway proof for
|
||||
isolation/workload-delivery mechanics; no deterministic proof is relabelled a
|
||||
real model call. The new action proves native installation and free launch only.
|
||||
|
||||
Read-only target evidence verifies the expected account/node, safe home/factory/
|
||||
store/lock modes and absent destination/staging. Four older digest directories
|
||||
remain in the store, including b6e4e8a4; this action does not select, overwrite,
|
||||
remove or execute them. Pinned native executables are /usr/bin/python3 (3.12.3),
|
||||
/usr/local/bin/kubectl for the node read and /usr/bin/bwrap. No private owner/config/auth files were opened by these observations.
|
||||
|
||||
The R2 read-only dry-run at **2026-10-17 06:01:35 UTC** passed all guards:
|
||||
123,352,678,400 free disk bytes, 7,735,549,952 available memory bytes, one-second
|
||||
CPU busy fraction 0.2832 and one-minute load 1.9634 across four CPUs. The new
|
||||
R2 staging and destination were absent. Earlier R1 target observations and
|
||||
headroom refusal remain copied as explicitly historical evidence; only
|
||||
final-readonly-dry-run.json records this R2 controller. A passing snapshot is
|
||||
not a reservation. All guards run again before any approved attempt.
|
||||
|
||||
The target's installed **bubblewrap 0.9.0** help supports --die-with-parent.
|
||||
Its installed upstream manual states that it kills all sandbox processes when
|
||||
bwrap or its parent dies, using PR_SET_PDEATHSIG. The R2 dry-run records the
|
||||
manual excerpt plus successful pidfd-open and PR_GET_CHILD_SUBREAPER readback.
|
||||
No target namespace or runtime was launched to obtain this evidence.
|
||||
|
||||
## Exact execution and refusal behavior
|
||||
|
||||
1. Verify every local helper hash and the complete source artifact digest.
|
||||
Read target metadata only; require the exact existing store device/inode,
|
||||
expected UID/GID/modes and Ready node/IP. Require both new paths absent,
|
||||
at least 2GiB free disk and 1GiB available memory, one-minute load ≤8 on four
|
||||
CPUs, and CPU busy ≤85% in one bounded one-second sample. Refuse on drift;
|
||||
no polling or mutation follows a failed gate.
|
||||
2. Write the local attempt intent before native staging. Recreate and verify
|
||||
the exact deterministic archive locally. Recheck CPU and exact store metadata
|
||||
before exclusive creation of the named 0700 remote staging directory. Record
|
||||
its actual device/inode in the local receipt before uploading anything.
|
||||
3. SCP only the archive and three public helpers. The fixed SSH bootstrap verifies
|
||||
the exact directory identity, complete four-file inventory, regular-file
|
||||
owner and all four payload hashes; it sets those owned public payload files
|
||||
to 0600. Then it execs the hashed install helper with a cleared application
|
||||
environment. Existing SSH authentication is transport only; no key is fetched,
|
||||
copied into the workload or exposed in evidence.
|
||||
4. After exact staging identity verification, the helper lowers priority to nice
|
||||
19 before starting its private process supervisor, so descendants inherit it.
|
||||
The helper rechecks identity/headroom and verifies archive hash/size and every member name/type/mode/count/size,
|
||||
rejects hardlinks/special/escaping members, extracts directories/files before
|
||||
internal relative symlinks and checks the complete extracted digest. It does
|
||||
not restore archive owners or execute a tar-supplied command. All created
|
||||
files belong to tegwick. A local temporary roundtrip independently passed
|
||||
that extraction and the exact final runtime digest, with modes/symlink intact.
|
||||
5. The staged candidate interpreter with `-I -B` imports and hash-checks its
|
||||
existing owner runtime/store modules, then calls existing install_runtime
|
||||
under a 180-second alarm and a supervised 200-second outer phase bound. No new package resolution/download occurs. The
|
||||
existing helper holds its normal lock and cleans its own temporary copy on
|
||||
ordinary exception, then atomically appends the verified new digest. Refuse
|
||||
if the destination appeared, even if somebody installed identical bytes;
|
||||
no existing install is silently adopted as this action's success.
|
||||
6. Rehash the complete native destination. Execute only the installed artifact's
|
||||
existing owner_rehearsal.rehearse through its existing run= callback seam,
|
||||
independent of owner.json,
|
||||
SpendPolicy or ledger state. It launches rein-aharness --help,
|
||||
glas-harness --help and Claude --version inside empty-egress bwrap, each with
|
||||
a supervised 60-second timeout and discarded command output; the phase has
|
||||
a 240-second alarm and supervised 260-second outer bound. The callback adds
|
||||
supported --die-with-parent to the otherwise unchanged frozen owner bwrap
|
||||
argv. frozen-native-argv.json records all three exact command arrays without
|
||||
executing them. The helper refuses any missing frozen system bind before
|
||||
launch. Require exactly those three zero exits. No model/task command or
|
||||
credential route is constructed. Child environment is cleared; host HOME
|
||||
and credentials are not mounted. These ephemeral namespaces create no
|
||||
SandboxManager record, persistent worker, model session or provider lease.
|
||||
7. Rehash the entire installed tree after rehearsal and remove only the exact
|
||||
owned staging directory only after the watchdog has stopped, been waited,
|
||||
and positively reported no live or waitable descendants, followed by exact
|
||||
device/inode/owner/mode and known-inventory checks. Missing or uncertain
|
||||
quiescence retains staging with failed-cleanup evidence. Capture the final structured result in this packet's
|
||||
execution-receipt.json. SSH closes when the foreground helper exits.
|
||||
|
||||
The rehearsal is the existing credential-free source API, not metered-once
|
||||
--rehearse against a private old owner configuration. This avoids changing pins
|
||||
or reading a ledger merely to validate an inactive artifact. It neither changes
|
||||
nor bypasses the normal future runtime/profile/spend/credential admission path.
|
||||
|
||||
## Expected result, cost and risk
|
||||
|
||||
Success is `installed_and_offline_rehearsal_verified`, with actual install result,
|
||||
owner UID/store mode, extracted/installed/post-rehearsal full digest equality,
|
||||
three explicit launcher exit codes and staging absence. Destination remains a
|
||||
dormant append. Profile and credential flags stay false; provider calls are zero.
|
||||
No current owner/catalog/queue/definition/profile bytes are read or changed by the
|
||||
installation action. T07's spend acceptance, owner renewal and real-model/native
|
||||
approvals remain separate.
|
||||
|
||||
Persistent addition is about 261MB of logical file data plus filesystem metadata.
|
||||
Transfer is about 110MB. Extraction/copy temporarily add two artifact-sized trees
|
||||
plus the archive; the 2GiB free-disk requirement includes operating margin. Hashing
|
||||
and copy are sequential and lower priority, but a snapshot gate cannot guarantee
|
||||
that shared-host CPU/IO remains idle throughout execution.
|
||||
|
||||
Requested provider spend is USD 0: no provider API exists in this action. No new
|
||||
paid resource or recurring service is committed. Incremental infrastructure,
|
||||
energy and human recovery time in EUR are **unknown**, not zero. Credible EUR
|
||||
loss from unexpected shared single-node IO/resource interruption is also unknown.
|
||||
Approval must explicitly cover this small supervised append/rehearsal exposure;
|
||||
this proposal grants no autonomous budget or autopilot authority. Root may defer
|
||||
execution until the fresh headroom gate passes without weakening its thresholds.
|
||||
|
||||
## Cleanup, rollback and uncertainty
|
||||
|
||||
Every phase and bwrap launcher is spawned by the single private watchdog,
|
||||
not by subprocess.run. It is a Linux child subreaper, starts each job in its own
|
||||
session/process group and binds the controller/callers with pidfds. The watchdog
|
||||
watches controller loss, caller loss, timeout, output bounds and orphaned
|
||||
children. On any of those events it stops **all owned jobs for this action**,
|
||||
including nested Runner jobs which are siblings of the phase under the
|
||||
watchdog. It discovers actual descendants/adopted orphans, binds each PID with
|
||||
a pidfd and rechecks UID/start identity before signaling only that exact
|
||||
process. It gives TERM 0.5 seconds, then KILL/reap up to 3 seconds. It does not
|
||||
use broad kill, process-name matching or kill a shared process group. Output is
|
||||
bounded at 64KiB and application stderr is always discarded. No private values
|
||||
are included in error receipts.
|
||||
|
||||
Each directly spawned process arms PR_SET_PDEATHSIG before exec and checks its
|
||||
parent. The added bwrap --die-with-parent also covers sandbox processes if
|
||||
bwrap/the watchdog disappears. Controller SIGKILL leaves the watchdog alive
|
||||
long enough to stop/reap jobs and write quiescent.json, but **leaves staging**:
|
||||
a dead controller cannot authorize cleanup. Watchdog loss or uncertain PID/UID/
|
||||
start mapping is failed/unverified cleanup; the controller does not infer a
|
||||
successful cleanup from a leader's exit. The final controller waits for its
|
||||
watchdog and requires its positive quiescence receipt before deleting staging.
|
||||
See PROCESS-CONTRACT.md for the precise source seam and failure matrix.
|
||||
|
||||
A transfer/bootstrap failure before remote helper execution may leave only the
|
||||
exact upload directory. The conditional command is
|
||||
`python3 -I -B /home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-17/native-runtime-install-r2/cleanup-upload-staging.py --apply`.
|
||||
It requires this proposal's revision-2 local receipt at staging_created /
|
||||
before_transfer, matching directory identity, only the four named regular
|
||||
upload files, and no supervision state or extracted runtime. It refuses every
|
||||
later phase. No post-execution cleanup command is authorized by that helper.
|
||||
|
||||
An SSH timeout or lost final reply after remote_execution_started remains an
|
||||
uncertain outcome; never replay --apply or delete staging. The 600-second SSH
|
||||
timeout is not proof that a surviving remote controller stopped. Reconcile the
|
||||
exact recorded stage/watchdog identity, quiescence receipt and complete installed
|
||||
digest read-only under the same trial; root must review any recovery mutation.
|
||||
Unexpected objects/mapping or absent quiescence evidence stay untouched.
|
||||
|
||||
The existing installer cleans its private `.install-*` copy on ordinary Python
|
||||
failure. Hard host/process loss may leave it; do not glob-delete store staging
|
||||
or infer ownership from the prefix. Preserve exact baseline/new-object metadata
|
||||
and request attended owner recovery for anything not tied to this attempt.
|
||||
|
||||
Rollback does not restore owner/catalog configuration because this action never
|
||||
changes it. Keep every older artifact and the newly installed immutable artifact
|
||||
as dormant evidence; removal of a published digest is not part of existing
|
||||
installer rollback and is not authorized here. If installation succeeds but
|
||||
rehearsal fails, record that partial outcome, retain the inactive artifact and
|
||||
staging cleanup facts, and stop. No automatic profile selection, in-place repair,
|
||||
artifact deletion, credential/provider retry or follow-up service action follows.
|
||||
|
||||
## Review evidence
|
||||
|
||||
- action-inputs.json: fixed paths/helper hashes/archive/components/guards.
|
||||
- frozen-native-argv.json: actual owner-generated three-launcher arrays plus the
|
||||
bounded Runner --die-with-parent addition; zero launcher executions.
|
||||
- process-fixture-results.json: five actual disposable-process tests, including
|
||||
timeout, controller SIGKILL, session escape, nested Runner and mapping refusal.
|
||||
- r1-preservation.json: all frozen R1 entries verified, complete R1 file hashes.
|
||||
- retained-local-source-refusal.json and local-bytecode-refinement.json: failed
|
||||
preparation retained. Owner argv inspection accidentally generated one local
|
||||
pyc; the source gate refused before any target call. Only that generated file
|
||||
and empty cache directory were removed; complete f599 equality was restored.
|
||||
Subsequent artifact inspection uses -B. This is a refinement of this same
|
||||
trial, not a separate successful privileged action.
|
||||
- target-readonly-observation.json and target-readonly-addendum.json: actual
|
||||
target/account/store/node/absence and Python/bwrap observations.
|
||||
- cpu-readonly-observation.json and retained-headroom-refusal.json: earlier
|
||||
saturation/refusal, retained separately from the later passing snapshot.
|
||||
- final-readonly-dry-run.json: actual R2 final helper's read-only result, zero writes.
|
||||
- local-transfer-verification.json: deterministic archive and complete local
|
||||
extraction digest/mode/symlink roundtrip, removed local temporary tree.
|
||||
|
||||
Root must review/freeze the proposal/manifest digest, retain it in the existing
|
||||
supervision trial, obtain the exact founder disposition and only then invoke the
|
||||
--apply command. No native staging, install, launcher execution, owner change or
|
||||
paid call has happened during this preparation.
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
# 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.
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
{
|
||||
"proposal_id": "REINAH-WP-0008-T07-install-f599-r1",
|
||||
"revision": 2,
|
||||
"scope": "install exact immutable artifact and credential-free native launcher rehearsal only; no provider/owner/config/catalog/queue actions",
|
||||
"runtime_sha256": "sha256:f59959b605a51abd2901becde14e7abd852c2bd3f848004b51d250d1cb81af6d",
|
||||
"target": "railiance01/tegwick uid1000",
|
||||
"destination": "/home/tegwick/.helixforge-factory/runtimes/f59959b605a51abd2901becde14e7abd852c2bd3f848004b51d250d1cb81af6d",
|
||||
"staging": "/home/tegwick/.helixforge-factory/runtime-install-f599-r2-20261017",
|
||||
"archive": {
|
||||
"sha256": "sha256:4c55b2740e759175d645705518fc33db30b26b4611f68e052a4ca909173084c7",
|
||||
"bytes": 109931109,
|
||||
"logical_file_bytes": 260668241,
|
||||
"members": 1448,
|
||||
"format": "gzip mtime0; PAX tar; sorted tree; root runtime; UID/GID1000; modes and relative symlinks retained"
|
||||
},
|
||||
"helper_hashes": {
|
||||
"execute-install.py": "sha256:44d93406ce62cc1f08ca9c7deb3eed6f45c74d61efd978f7b0acec31ba04cdc5",
|
||||
"install-verified.py": "sha256:c6895881b83b3f5d5408c646040368cc5c59a99e1495fa0914b9183e087a6831",
|
||||
"process_supervision.py": "sha256:4d990ee05003040e74bfdac7b2e49a3ef1512d4a054b3ff2317647b3670dd1bf",
|
||||
"build-transfer.py": "sha256:99914629f354d4b6d5a4750850b32ec2cb64ac5ce18e86702d9157ea6f6b0124",
|
||||
"runtime.py": "sha256:19b5fb687288bbc836978b0251599dc1e4d1593e8b5efee85a6abb6a7f388412"
|
||||
},
|
||||
"source_components": {
|
||||
"sandboxer_runtime_and_store": {
|
||||
"revision": "221126cb77ea41ec283598a23d628f760a51bdea",
|
||||
"matches_current_owner_revision": "53f800c4f7a9c91469834d04ef6f58e906b0bd49"
|
||||
},
|
||||
"rein_owner_rehearsal": {
|
||||
"revision": "9b88cadaa817e5abea7950b4a264d4e2739a5ddb"
|
||||
}
|
||||
},
|
||||
"minimum_free_disk_bytes": 2147483648,
|
||||
"minimum_available_memory_bytes": 1073741824,
|
||||
"cost": {
|
||||
"provider_requests": 0,
|
||||
"new_paid_resource_commitment": false,
|
||||
"incremental_infrastructure_and_operator_eur": "unknown; explicitly supervised"
|
||||
},
|
||||
"risk": {
|
||||
"credible_loss_eur": "unknown; explicitly supervised",
|
||||
"scope": "dormant owner-store append plus bounded local read-only namespace help/version launches; existing shared single-node host IO/CPU risk"
|
||||
},
|
||||
"authority": "pending root exact proposal retention and fresh founder approval",
|
||||
"maximum_cpu_busy_fraction": 0.85,
|
||||
"cpu_sample_seconds": 1,
|
||||
"native_executables": {
|
||||
"controller": "/usr/bin/python3",
|
||||
"readonly_node_client": "/usr/local/bin/kubectl",
|
||||
"sandbox": "/usr/bin/bwrap"
|
||||
},
|
||||
"conditional_cleanup_helper_sha256": "sha256:cec7eab2b25e4a0077ec45c055d7ba9542bc8e8724b53f2117be61c913269188",
|
||||
"supervision": {
|
||||
"contract": "private one-action subreaper/watchdog; pidfd-bound actual descendants only; stage removal requires waited watchdog and positive quiescence receipt",
|
||||
"outer_phase_timeout_seconds": {
|
||||
"install": 200,
|
||||
"rehearse": 260
|
||||
},
|
||||
"inner_alarm_seconds": {
|
||||
"install": 180,
|
||||
"rehearse": 240
|
||||
},
|
||||
"per_launcher_timeout_seconds": 60,
|
||||
"termination_grace_seconds": 0.5,
|
||||
"kill_reap_seconds": 3,
|
||||
"watchdog_wait_seconds": 8,
|
||||
"capture_bound_bytes": 65536,
|
||||
"stderr": "discarded",
|
||||
"bwrap_additional_argv": [
|
||||
"--die-with-parent"
|
||||
],
|
||||
"target_bwrap_version": "bubblewrap 0.9.0",
|
||||
"unknown_mapping": "retain failed cleanup evidence and staging; no broad kill or delete"
|
||||
},
|
||||
"frozen_native_argv_sha256": "sha256:0efb13aa725111cc227453bc7fc40eaf039c83cbd291ca7cb0409daf6b1e6c55"
|
||||
}
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
{
|
||||
"proposal_id": "REINAH-WP-0008-T07-install-f599-r1",
|
||||
"revision": 2,
|
||||
"status": "pending-root-review-and-fresh-founder-approval",
|
||||
"native_mutation_authorized": false,
|
||||
"r1_unchanged": true,
|
||||
"files": [
|
||||
{
|
||||
"path": "FINAL-PROPOSAL.md",
|
||||
"sha256": "sha256:44b17589c323e1a966caf6c1b1def123092ee289b929c8ea014bc84109103b1d"
|
||||
},
|
||||
{
|
||||
"path": "PROCESS-CONTRACT.md",
|
||||
"sha256": "sha256:ed79d14bde467e8893c341419c37fa2992184597fc9015c2e7d81fb4ece63fd0"
|
||||
},
|
||||
{
|
||||
"path": "action-inputs.json",
|
||||
"sha256": "sha256:ce7f5a25be440561f5179ae9980d85eb693a11b58d2012c8aea24b8bae04ab6f"
|
||||
},
|
||||
{
|
||||
"path": "build-transfer.py",
|
||||
"sha256": "sha256:99914629f354d4b6d5a4750850b32ec2cb64ac5ce18e86702d9157ea6f6b0124"
|
||||
},
|
||||
{
|
||||
"path": "cleanup-upload-staging.py",
|
||||
"sha256": "sha256:cec7eab2b25e4a0077ec45c055d7ba9542bc8e8724b53f2117be61c913269188"
|
||||
},
|
||||
{
|
||||
"path": "cpu-readonly-observation.json",
|
||||
"sha256": "sha256:c424ecb281baf128d6c79f1e6c6f678093f9b5e9c84a3454f9891fe6197b2e95"
|
||||
},
|
||||
{
|
||||
"path": "executable-readonly-observation.json",
|
||||
"sha256": "sha256:3ee73cdeeb4f82a55b31d19c6a24aacc4199e2e8647335d65b4e8f2e7d079c18"
|
||||
},
|
||||
{
|
||||
"path": "execute-install.py",
|
||||
"sha256": "sha256:44d93406ce62cc1f08ca9c7deb3eed6f45c74d61efd978f7b0acec31ba04cdc5"
|
||||
},
|
||||
{
|
||||
"path": "final-readonly-dry-run.json",
|
||||
"sha256": "sha256:17144cdb22f975212ea02a5109c94740c430d79272ef9d919ce57dec68c2d880"
|
||||
},
|
||||
{
|
||||
"path": "frozen-native-argv.json",
|
||||
"sha256": "sha256:0efb13aa725111cc227453bc7fc40eaf039c83cbd291ca7cb0409daf6b1e6c55"
|
||||
},
|
||||
{
|
||||
"path": "install-verified.py",
|
||||
"sha256": "sha256:c6895881b83b3f5d5408c646040368cc5c59a99e1495fa0914b9183e087a6831"
|
||||
},
|
||||
{
|
||||
"path": "local-bytecode-refinement.json",
|
||||
"sha256": "sha256:08c92506d6abefe15aff4a9b7908ab8bee425e7792abc6095e1dd04101a2a540"
|
||||
},
|
||||
{
|
||||
"path": "local-transfer-check.py",
|
||||
"sha256": "sha256:97fbec826d517b1746a2ec5ef67e5aab053777690977abdddc8b33ec5804e22e"
|
||||
},
|
||||
{
|
||||
"path": "local-transfer-verification.json",
|
||||
"sha256": "sha256:2331b854389b4c15f5adce1f62aa0846e9f47dfda351a7ef166c90b5e9b2737c"
|
||||
},
|
||||
{
|
||||
"path": "pre-absolute-path-dry-run.json",
|
||||
"sha256": "sha256:9ea47b71b8636e4beb7dadf595f1d08bde261c9986986a1335d0c00d581a6d0c"
|
||||
},
|
||||
{
|
||||
"path": "process-fixture-results.json",
|
||||
"sha256": "sha256:fc72ce36758ea40010aeb7c4ce97c167d09784fde63eeb9b2ccaa3428dc9a831"
|
||||
},
|
||||
{
|
||||
"path": "process_supervision.py",
|
||||
"sha256": "sha256:4d990ee05003040e74bfdac7b2e49a3ef1512d4a054b3ff2317647b3670dd1bf"
|
||||
},
|
||||
{
|
||||
"path": "r1-preservation.json",
|
||||
"sha256": "sha256:50848ad45fda68f43e11710bb04b0793717a5d0659e3fbcf8003708ee5ccb542"
|
||||
},
|
||||
{
|
||||
"path": "r1-to-r2.patch",
|
||||
"sha256": "sha256:63f35421b67576188ff92d920319a23aa0a6606b9a2137d1061687c77126b541"
|
||||
},
|
||||
{
|
||||
"path": "r2-initial-readonly-dry-run.json",
|
||||
"sha256": "sha256:631c6f0d8c0f66f51509751094937fd909821d4f45a042fd77f9d10f237115b8"
|
||||
},
|
||||
{
|
||||
"path": "retained-headroom-refusal.json",
|
||||
"sha256": "sha256:89c847b4a75f138ff2464fc8291cf36939d293a777fd41ffd41d72f96ba9a2f8"
|
||||
},
|
||||
{
|
||||
"path": "retained-local-source-refusal.json",
|
||||
"sha256": "sha256:fdd3b584da6ebedc173ccc5f8879c42dbd821d4b98338337fbef809a403485ba"
|
||||
},
|
||||
{
|
||||
"path": "runtime.py",
|
||||
"sha256": "sha256:19b5fb687288bbc836978b0251599dc1e4d1593e8b5efee85a6abb6a7f388412"
|
||||
},
|
||||
{
|
||||
"path": "target-readonly-addendum.json",
|
||||
"sha256": "sha256:9588f51b353093d178b8c52892ca627c2317fab761591715a0765603a4182a8f"
|
||||
},
|
||||
{
|
||||
"path": "target-readonly-observation.json",
|
||||
"sha256": "sha256:ba4012303e5634686208a818b221113a1a670c31ed29269d8a4cc98308616275"
|
||||
},
|
||||
{
|
||||
"path": "test_process_supervision.py",
|
||||
"sha256": "sha256:7ea89d9dee17d6ac4b554e25c083ac43d9b94039a4bcbe411d0e2ccbfb55a180"
|
||||
},
|
||||
{
|
||||
"path": "transfer-archive.json",
|
||||
"sha256": "sha256:3222ccd39ea968353ea5e58ec36f0e3ca35b608020444243b053074a36abfe0b"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Deterministic public-runtime transfer; no runtime launch or network access."""
|
||||
import argparse
|
||||
import gzip
|
||||
import hashlib
|
||||
import importlib.util
|
||||
import io
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import stat
|
||||
import tarfile
|
||||
|
||||
ROOT = Path('/tmp/netkingdom-runtime-canonical-20261016/runtime')
|
||||
EXPECTED = 'f59959b605a51abd2901becde14e7abd852c2bd3f848004b51d250d1cb81af6d'
|
||||
HERE = Path(__file__).resolve().parent
|
||||
|
||||
|
||||
class CountingHash:
|
||||
def __init__(self, target=None):
|
||||
self.target, self.hash, self.size = target, hashlib.sha256(), 0
|
||||
def write(self, value):
|
||||
self.hash.update(value); self.size += len(value)
|
||||
if self.target: self.target.write(value)
|
||||
return len(value)
|
||||
def flush(self):
|
||||
if self.target: self.target.flush()
|
||||
|
||||
|
||||
def runtime_module():
|
||||
p = HERE/'runtime.py'
|
||||
spec = importlib.util.spec_from_file_location('reviewed_runtime_digest', p)
|
||||
module = importlib.util.module_from_spec(spec); spec.loader.exec_module(module)
|
||||
return module
|
||||
|
||||
|
||||
def build(target=None):
|
||||
if runtime_module().runtime_digest(ROOT) != EXPECTED:
|
||||
raise ValueError('source_runtime_changed')
|
||||
result = CountingHash(target); logical = 0; members = 0
|
||||
with gzip.GzipFile(filename='', mode='wb', compresslevel=6, mtime=0, fileobj=result) as stream:
|
||||
with tarfile.open(fileobj=stream, mode='w|', format=tarfile.PAX_FORMAT) as archive:
|
||||
for p in [ROOT, *sorted(ROOT.rglob('*'))]:
|
||||
s=p.lstat(); name='runtime' + ('/'+p.relative_to(ROOT).as_posix() if p!=ROOT else '')
|
||||
info=tarfile.TarInfo(name);info.uid=1000;info.gid=1000;info.mtime=0;info.mode=stat.S_IMODE(s.st_mode)
|
||||
if stat.S_ISDIR(s.st_mode):info.type=tarfile.DIRTYPE
|
||||
elif stat.S_ISLNK(s.st_mode):
|
||||
info.type=tarfile.SYMTYPE; info.linkname=os.readlink(p)
|
||||
if Path(info.linkname).is_absolute() or not p.resolve(strict=True).is_relative_to(ROOT):
|
||||
raise ValueError('unsafe_runtime_symlink')
|
||||
elif stat.S_ISREG(s.st_mode):info.type=tarfile.REGTYPE; info.size=s.st_size; logical += s.st_size
|
||||
else:raise ValueError('runtime_special_file')
|
||||
with p.open('rb') if stat.S_ISREG(s.st_mode) else io.BytesIO() as data:
|
||||
archive.addfile(info,data if stat.S_ISREG(s.st_mode) else None)
|
||||
members+=1
|
||||
if runtime_module().runtime_digest(ROOT) != EXPECTED:
|
||||
raise ValueError('source_runtime_changed_during_archive')
|
||||
return {'sha256':'sha256:'+result.hash.hexdigest(),'bytes':result.size,'logical_file_bytes':logical,'members':members,
|
||||
'format':'gzip mtime0; PAX tar; sorted tree; root runtime; UID/GID1000; modes and relative symlinks retained'}
|
||||
|
||||
|
||||
def main():
|
||||
parser=argparse.ArgumentParser(description=__doc__);parser.add_argument('--output',type=Path)
|
||||
args=parser.parse_args()
|
||||
if args.output:
|
||||
with args.output.open('xb') as stream:result=build(stream)
|
||||
else:result=build()
|
||||
print(json.dumps(result,sort_keys=True))
|
||||
|
||||
|
||||
if __name__=='__main__':main()
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Conditional cleanup only when the durable receipt proves exec never started."""
|
||||
import argparse,json,shlex,subprocess
|
||||
from pathlib import Path
|
||||
HERE=Path(__file__).resolve().parent
|
||||
|
||||
def main():
|
||||
p=argparse.ArgumentParser(description=__doc__);p.add_argument('--apply',action='store_true');args=p.parse_args()
|
||||
receipt=json.loads((HERE/'execution-receipt.json').read_text())
|
||||
if not (receipt.get('status')=='staging_created' and receipt.get('phase')=='before_transfer'
|
||||
and receipt.get('revision')==2 and receipt.get('proposal_id')=='REINAH-WP-0008-T07-install-f599-r1'):
|
||||
raise ValueError('not_a_preexecution_upload_failure')
|
||||
inode=receipt['staging_identity']['inode']
|
||||
if not args.apply:
|
||||
print(json.dumps({'status':'conditional_cleanup_review','inode':inode,'native_mutations':0}));return 0
|
||||
code=r'''import json,os,shutil,stat,sys
|
||||
from pathlib import Path
|
||||
p=Path('/home/tegwick/.helixforge-factory/runtime-install-f599-r2-20261017');s=p.lstat()
|
||||
assert os.getuid()==1000 and os.getgid()==1000 and stat.S_ISDIR(s.st_mode) and s.st_uid==1000 and s.st_gid==1000 and stat.S_IMODE(s.st_mode)==0o700 and s.st_dev==2049 and s.st_ino==int(sys.argv[1])
|
||||
assert {f.name for f in p.iterdir()} <= {'runtime.tar.gz','runtime.py','install-verified.py','process_supervision.py'}
|
||||
for f in p.iterdir():
|
||||
s=f.lstat();assert stat.S_ISREG(s.st_mode) and s.st_uid==1000 and not stat.S_ISLNK(s.st_mode)
|
||||
shutil.rmtree(p);print(json.dumps({'status':'owned_upload_staging_removed','absent':not p.exists()}))'''
|
||||
cp=subprocess.run(['ssh','-o','BatchMode=yes','-o','ConnectTimeout=15','railiance01','/usr/bin/python3 -I -B -c '+shlex.quote(code)+' '+shlex.quote(str(inode))],capture_output=True,text=True,timeout=30)
|
||||
if cp.returncode:raise RuntimeError('conditional_cleanup_refused_or_uncertain')
|
||||
result=json.loads(cp.stdout);(HERE/'upload-cleanup-receipt.json').write_text(json.dumps(result,indent=2)+'\n');print(json.dumps(result));return 0
|
||||
|
||||
if __name__=='__main__':
|
||||
try:raise SystemExit(main())
|
||||
except Exception as exc:
|
||||
print(json.dumps({'status':'cleanup_refused_or_uncertain','failure_type':type(exc).__name__}));raise SystemExit(1) from None
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"sample_seconds": 0.5, "cpu_busy_fraction": 0.9949748743718593}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"python_executable": "/usr/bin/python3", "kubectl": "/usr/local/bin/kubectl", "bwrap": "/usr/bin/bwrap", "system_python_resolved": "/usr/bin/python3.12"}
|
||||
|
|
@ -0,0 +1,162 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Install-only orchestration. Default is read-only; --apply requires founder go."""
|
||||
import argparse
|
||||
from datetime import datetime, timezone
|
||||
import hashlib
|
||||
import importlib.util
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import shlex
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
HERE=Path(__file__).resolve().parent
|
||||
DIGEST='f59959b605a51abd2901becde14e7abd852c2bd3f848004b51d250d1cb81af6d'
|
||||
STAGE='/home/tegwick/.helixforge-factory/runtime-install-f599-r2-20261017'
|
||||
STORE='/home/tegwick/.helixforge-factory/runtimes'
|
||||
SOURCE=Path('/tmp/netkingdom-runtime-canonical-20261016/runtime')
|
||||
|
||||
READ_TARGET=r'''import json,os,pwd,stat,subprocess,shutil,time,ctypes,gzip
|
||||
from pathlib import Path
|
||||
store=Path('/home/tegwick/.helixforge-factory/runtimes'); stage=Path('/home/tegwick/.helixforge-factory/runtime-install-f599-r2-20261017');digest='f59959b605a51abd2901becde14e7abd852c2bd3f848004b51d250d1cb81af6d'
|
||||
def meta(p):
|
||||
try:
|
||||
s=p.lstat();return {'uid':s.st_uid,'gid':s.st_gid,'mode':oct(stat.S_IMODE(s.st_mode)),'device':s.st_dev,'inode':s.st_ino,'directory':stat.S_ISDIR(s.st_mode),'symlink':stat.S_ISLNK(s.st_mode)}
|
||||
except FileNotFoundError:return None
|
||||
cp=subprocess.run(['/usr/local/bin/kubectl','get','nodes','-o','json'],capture_output=True,timeout=20)
|
||||
assert cp.returncode==0
|
||||
nodes=json.loads(cp.stdout)['items'];assert len(nodes)==1
|
||||
mem=dict(x.split(':',1) for x in Path('/proc/meminfo').read_text().splitlines())
|
||||
read_cpu=lambda:[int(x) for x in Path('/proc/stat').read_text().splitlines()[0].split()[1:9]]
|
||||
a=read_cpu();time.sleep(1);b=read_cpu();d=[y-x for x,y in zip(a,b)];busy=1-(d[3]+d[4])/sum(d)
|
||||
help_result=subprocess.run(['/usr/bin/bwrap','--help'],stdout=subprocess.PIPE,stderr=subprocess.DEVNULL,timeout=10)
|
||||
version=subprocess.run(['/usr/bin/bwrap','--version'],stdout=subprocess.PIPE,stderr=subprocess.DEVNULL,timeout=10)
|
||||
manual=gzip.decompress(Path('/usr/share/man/man1/bwrap.1.gz').read_bytes()).decode();i=manual.find('die\\-with\\-parent')
|
||||
fd=os.pidfd_open(os.getpid());os.close(fd);value=ctypes.c_int();subreaper_get=ctypes.CDLL(None).prctl(37,ctypes.byref(value),0,0,0)
|
||||
print(json.dumps({'bwrap_version':version.stdout.decode().strip(),'bwrap_die_with_parent_supported':help_result.returncode==0 and b'--die-with-parent' in help_result.stdout,'primary_manual_contract_excerpt':manual[max(0,i-20):i+430] if i>=0 else 'absent','pidfd_available':True,'subreaper_get_supported':subreaper_get==0,'account':pwd.getpwuid(os.getuid()).pw_name,'uid':os.getuid(),'gid':os.getgid(),'store':meta(store),'destination':meta(store/digest),'stage':meta(stage),'memory_available_bytes':int(mem['MemAvailable'].split()[0])*1024,'disk_free_bytes':shutil.disk_usage(store).free,'load_average':list(os.getloadavg()),'cpu_count':os.cpu_count(),'cpu_busy_fraction':busy,'cpu_sample_seconds':1,'node_addresses':nodes[0]['status']['addresses'],'node_ready':any(c['type']=='Ready' and c['status']=='True' for c in nodes[0]['status']['conditions'])}))'''
|
||||
|
||||
|
||||
def sha(path):
|
||||
with path.open('rb') as stream:return 'sha256:'+hashlib.file_digest(stream,'sha256').hexdigest()
|
||||
|
||||
|
||||
def command(argv,timeout=60):
|
||||
cp=subprocess.run(argv,stdout=subprocess.PIPE,stderr=subprocess.DEVNULL,timeout=timeout)
|
||||
if cp.returncode:raise RuntimeError('command_failed')
|
||||
return cp.stdout
|
||||
|
||||
|
||||
def ssh(code,args=(),timeout=60):
|
||||
remote='/usr/bin/python3 -I -B -c '+shlex.quote(code)
|
||||
if args:remote+=' '+' '.join(shlex.quote(str(x)) for x in args)
|
||||
return json.loads(command(['ssh','-o','BatchMode=yes','-o','ConnectTimeout=15','railiance01',remote],timeout))
|
||||
|
||||
|
||||
def module(name,path):
|
||||
spec=importlib.util.spec_from_file_location(name,path)
|
||||
result=importlib.util.module_from_spec(spec);spec.loader.exec_module(result);return result
|
||||
|
||||
|
||||
def preflight():
|
||||
inputs=json.loads((HERE/'action-inputs.json').read_text())
|
||||
for name,expected in inputs['helper_hashes'].items():
|
||||
if sha(HERE/name)!=expected:raise ValueError('helper_changed')
|
||||
if module('reviewed_runtime_reader',HERE/'runtime.py').runtime_digest(SOURCE)!=DIGEST:
|
||||
raise ValueError('source_changed')
|
||||
observation=ssh(READ_TARGET)
|
||||
store=observation['store']
|
||||
expected=json.loads((HERE/'target-readonly-observation.json').read_text())['paths'][STORE]
|
||||
checks={
|
||||
'bwrap_parent_death_contract':observation['bwrap_die_with_parent_supported'] and observation['bwrap_version']=='bubblewrap 0.9.0' and 'PR_SET_PDEATHSIG' in observation['primary_manual_contract_excerpt'],
|
||||
'supervision_primitives':observation['pidfd_available'] and observation['subreaper_get_supported'],
|
||||
'account':observation['account']=='tegwick' and observation['uid']==1000 and observation['gid']==1000,
|
||||
'protected_store':store['uid']==1000 and store['gid']==1000 and store['mode']=='0o700' and store['directory'] and not store['symlink'] and store['device']==expected['device'] and store['inode']==expected['inode'],
|
||||
'destination_absent':observation['destination'] is None,
|
||||
'staging_absent':observation['stage'] is None,
|
||||
'exact_ready_node':observation['node_ready'] and any(x['address']=='92.205.62.239' for x in observation['node_addresses']),
|
||||
'disk_headroom':observation['disk_free_bytes']>=2*1024**3,
|
||||
'memory_headroom':observation['memory_available_bytes']>=1024**3,
|
||||
'load_headroom':observation['load_average'][0]<=2*observation['cpu_count'],
|
||||
'cpu_headroom':observation['cpu_busy_fraction']<=0.85,
|
||||
}
|
||||
observation['checks']=checks
|
||||
return inputs,observation
|
||||
|
||||
|
||||
def save(path,value):
|
||||
fd=os.open(path,os.O_WRONLY|os.O_CREAT|os.O_EXCL|os.O_NOFOLLOW,0o600)
|
||||
with os.fdopen(fd,'w') as stream:json.dump(value,stream,indent=2);stream.write('\n')
|
||||
|
||||
|
||||
def main():
|
||||
parser=argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('--apply',action='store_true');parser.add_argument('--dry-run',action='store_true')
|
||||
args=parser.parse_args()
|
||||
if args.apply and args.dry_run:raise ValueError('conflicting_modes')
|
||||
inputs,observation=preflight()
|
||||
if not args.apply:
|
||||
report={'status':'read_only_dry_run_passed' if all(observation['checks'].values()) else 'read_only_dry_run_blocked','proposal_id':inputs['proposal_id'],'revision':2,
|
||||
'observed_at':datetime.now(timezone.utc).isoformat(),'target':observation,
|
||||
'source_runtime_complete_digest_verified':True,'archive':inputs['archive'],
|
||||
'remote_mutations':0,'provider_calls':0,'private_configuration_reads':False,
|
||||
'next_exact_command':'python3 -I -B '+str(HERE/'execute-install.py')+' --apply'}
|
||||
print(json.dumps(report,sort_keys=True));return 0 if all(observation['checks'].values()) else 2
|
||||
if not all(observation['checks'].values()):raise ValueError('target_headroom_or_identity_refused')
|
||||
receipt=HERE/'execution-receipt.json'
|
||||
if receipt.exists():raise ValueError('prior_attempt_requires_reconciliation')
|
||||
save(receipt,{'status':'intent_recorded','proposal_id':inputs['proposal_id'],'revision':2,
|
||||
'automatic_retry':False,'observed_at':datetime.now(timezone.utc).isoformat()})
|
||||
# No native attempt is retried by this helper. Intent survives an SSH loss.
|
||||
with tempfile.TemporaryDirectory(prefix='nk-runtime-install-r2-') as temporary:
|
||||
archive=Path(temporary)/'runtime.tar.gz'
|
||||
with archive.open('xb') as stream:built=module('reviewed_transfer',HERE/'build-transfer.py').build(stream)
|
||||
if built!=inputs['archive']:raise ValueError('archive_changed')
|
||||
archive.chmod(0o600)
|
||||
create=r'''import json,os,stat,time
|
||||
from pathlib import Path
|
||||
assert os.getuid()==1000 and os.getgid()==1000
|
||||
store=Path('/home/tegwick/.helixforge-factory/runtimes');s=store.lstat()
|
||||
assert stat.S_ISDIR(s.st_mode) and s.st_uid==1000 and stat.S_IMODE(s.st_mode)==0o700 and s.st_ino==4751222 and s.st_dev==2049
|
||||
assert not (store/'f59959b605a51abd2901becde14e7abd852c2bd3f848004b51d250d1cb81af6d').exists()
|
||||
read_cpu=lambda:[int(x) for x in Path('/proc/stat').read_text().splitlines()[0].split()[1:9]]
|
||||
a=read_cpu();time.sleep(1);b=read_cpu();d=[y-x for x,y in zip(a,b)];assert 1-(d[3]+d[4])/sum(d)<=0.85
|
||||
stage=Path('/home/tegwick/.helixforge-factory/runtime-install-f599-r2-20261017');stage.mkdir(mode=0o700)
|
||||
s=stage.lstat();print(json.dumps({'inode':s.st_ino,'device':s.st_dev}))'''
|
||||
created=ssh(create)
|
||||
receipt.write_text(json.dumps({'status':'staging_created','phase':'before_transfer','proposal_id':inputs['proposal_id'],'revision':2,'automatic_retry':False,'staging_identity':created,'staging_path':STAGE},indent=2)+'\n')
|
||||
command(['scp','-q',str(archive),str(HERE/'runtime.py'),str(HERE/'install-verified.py'),str(HERE/'process_supervision.py'),'railiance01:'+STAGE+'/'],120)
|
||||
execute=r'''import hashlib,json,os,stat,sys
|
||||
from pathlib import Path
|
||||
stage=Path('/home/tegwick/.helixforge-factory/runtime-install-f599-r2-20261017');s=stage.lstat()
|
||||
assert stat.S_ISDIR(s.st_mode) and s.st_uid==1000 and stat.S_IMODE(s.st_mode)==0o700 and s.st_ino==int(sys.argv[1]) and s.st_dev==2049
|
||||
expected=json.loads(sys.argv[2]);assert {p.name for p in stage.iterdir()}==set(expected)
|
||||
for name,want in expected.items():
|
||||
p=stage/name;s=p.lstat();assert stat.S_ISREG(s.st_mode) and s.st_uid==1000 and not stat.S_ISLNK(s.st_mode)
|
||||
with p.open('rb') as stream:assert hashlib.file_digest(stream,'sha256').hexdigest()==want
|
||||
p.chmod(0o600)
|
||||
os.execve('/usr/bin/python3',['python3','-I','-B',str(stage/'install-verified.py'),'--stage-inode',sys.argv[1]],{'HOME':'/home/tegwick','PATH':'/usr/bin:/bin','LANG':'C.UTF-8','PYTHONDONTWRITEBYTECODE':'1'})'''
|
||||
hashes={'runtime.tar.gz':inputs['archive']['sha256'].removeprefix('sha256:'),
|
||||
'runtime.py':inputs['helper_hashes']['runtime.py'].removeprefix('sha256:'),
|
||||
'install-verified.py':inputs['helper_hashes']['install-verified.py'].removeprefix('sha256:'),
|
||||
'process_supervision.py':inputs['helper_hashes']['process_supervision.py'].removeprefix('sha256:')}
|
||||
# Parse the structured final helper return even when its action failed.
|
||||
remote='/usr/bin/python3 -I -B -c '+shlex.quote(execute)+' '+shlex.quote(str(created['inode']))+' '+shlex.quote(json.dumps(hashes,separators=(',',':')))
|
||||
receipt.write_text(json.dumps({'status':'remote_execution_started','proposal_id':inputs['proposal_id'],'revision':2,'automatic_retry':False,'staging_identity':created,'staging_path':STAGE},indent=2)+'\n')
|
||||
cp=subprocess.run(['ssh','-o','BatchMode=yes','-o','ConnectTimeout=15','railiance01',remote],stdout=subprocess.PIPE,stderr=subprocess.DEVNULL,timeout=600)
|
||||
try:result=json.loads(cp.stdout)
|
||||
except Exception:raise RuntimeError('remote_outcome_uncertain') from None
|
||||
result['ssh_exit_code']=cp.returncode;result['staging_identity']=created
|
||||
# Replace only our exact local intent receipt, never a previous attempt.
|
||||
receipt.write_text(json.dumps(result,indent=2)+'\n');receipt.chmod(0o600)
|
||||
print(json.dumps(result,sort_keys=True))
|
||||
return 0 if cp.returncode==0 and result.get('status')=='installed_and_offline_rehearsal_verified' else 1
|
||||
|
||||
|
||||
if __name__=='__main__':
|
||||
try:raise SystemExit(main())
|
||||
except Exception as exc:
|
||||
print(json.dumps({'status':'failed_or_uncertain','failure_type':type(exc).__name__,
|
||||
'automatic_retry':False,'private_diagnostics_suppressed':True}))
|
||||
raise SystemExit(1) from None
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
{
|
||||
"credential_reads": false,
|
||||
"destination": "/home/tegwick/.helixforge-factory/runtimes/f59959b605a51abd2901becde14e7abd852c2bd3f848004b51d250d1cb81af6d",
|
||||
"destination_exists": true,
|
||||
"extracted_complete_digest_verified": true,
|
||||
"failure_code": "phase_receipt_unavailable",
|
||||
"failure_type": "ValueError",
|
||||
"installation": {
|
||||
"result": {
|
||||
"credential_delivery_configured": false,
|
||||
"owner_uid": 1000,
|
||||
"profile_activated": false,
|
||||
"reused": false,
|
||||
"runtime": {
|
||||
"path": "/home/tegwick/.helixforge-factory/runtimes/f59959b605a51abd2901becde14e7abd852c2bd3f848004b51d250d1cb81af6d",
|
||||
"sha256": "f59959b605a51abd2901becde14e7abd852c2bd3f848004b51d250d1cb81af6d"
|
||||
},
|
||||
"store_mode": "0700"
|
||||
},
|
||||
"status": "installed"
|
||||
},
|
||||
"installed_complete_digest_verified": true,
|
||||
"model_calls": 0,
|
||||
"owned_staging_removed": true,
|
||||
"owner_configuration_read": false,
|
||||
"phase": "offline_rehearsal",
|
||||
"process_supervision": {
|
||||
"no_live_or_waitable_descendants": true,
|
||||
"process_values_emitted": false,
|
||||
"quiescent": true,
|
||||
"reason": "leader_left_descendants",
|
||||
"stderr_suppressed": true
|
||||
},
|
||||
"proposal_id": "REINAH-WP-0008-T07-install-f599-r1",
|
||||
"renewal_or_catalog_or_queue_mutation": false,
|
||||
"revision": 2,
|
||||
"status": "failed",
|
||||
"ssh_exit_code": 1,
|
||||
"staging_identity": {
|
||||
"inode": 4723175,
|
||||
"device": 2049
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"archive": {"bytes": 109931109, "format": "gzip mtime0; PAX tar; sorted tree; root runtime; UID/GID1000; modes and relative symlinks retained", "logical_file_bytes": 260668241, "members": 1448, "sha256": "sha256:4c55b2740e759175d645705518fc33db30b26b4611f68e052a4ca909173084c7"}, "next_exact_command": "python3 -I -B /home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-17/native-runtime-install-r2/execute-install.py --apply", "observed_at": "2026-10-17T06:01:35.675995+00:00", "private_configuration_reads": false, "proposal_id": "REINAH-WP-0008-T07-install-f599-r1", "provider_calls": 0, "remote_mutations": 0, "revision": 2, "source_runtime_complete_digest_verified": true, "status": "read_only_dry_run_passed", "target": {"account": "tegwick", "bwrap_die_with_parent_supported": true, "bwrap_version": "bubblewrap 0.9.0", "checks": {"account": true, "bwrap_parent_death_contract": true, "cpu_headroom": true, "destination_absent": true, "disk_headroom": true, "exact_ready_node": true, "load_headroom": true, "memory_headroom": true, "protected_store": true, "staging_absent": true, "supervision_primitives": true}, "cpu_busy_fraction": 0.28316326530612246, "cpu_count": 4, "cpu_sample_seconds": 1, "destination": null, "disk_free_bytes": 123352678400, "gid": 1000, "load_average": [1.96337890625, 2.81396484375, 2.62744140625], "memory_available_bytes": 7735549952, "node_addresses": [{"address": "92.205.62.239", "type": "InternalIP"}, {"address": "2a00:1169:111:6d70::", "type": "InternalIP"}, {"address": "239.62.205.92.host.secureserver.net", "type": "Hostname"}], "node_ready": true, "pidfd_available": true, "primary_manual_contract_excerpt": ")\\&.\n.RE\n.PP\n\\fB\\-\\-die\\-with\\-parent\\fR\n.RS 4\nEnsures child process (COMMAND) dies when bwrap\\*(Aqs parent dies\\&. Kills (SIGKILL) all bwrap sandbox processes in sequence from parent to child including COMMAND process when bwrap or bwrap\\*(Aqs parent dies\\&. See prctl, PR_SET_PDEATHSIG\\&.\n.RE\n.PP\n\\fB\\-\\-as\\-pid\\-1\\fR\n.RS 4\nDo not create a process with PID=1 in the sandbox to reap child processes\\&.\n.RE\n.PP\n\\fB\\-\\-cap\\-add \\fR\\fBCAP\\fR\n.RS 4\nAdd ", "stage": null, "store": {"device": 2049, "directory": true, "gid": 1000, "inode": 4751222, "mode": "0o700", "symlink": false, "uid": 1000}, "subreaper_get_supported": true, "uid": 1000}}
|
||||
|
|
@ -0,0 +1,181 @@
|
|||
{
|
||||
"source_api": "owner_rehearsal.rehearse(runtime, run=bounded_run, bwrap_bin=/usr/bin/bwrap)",
|
||||
"source_sha256": "sha256:192f654cad4a5f065c7faa5e35b03493297a472e8d7fe641be2c42c5fb00dcc3",
|
||||
"captured_using_existing_runner_seam": true,
|
||||
"launcher_executions": 0,
|
||||
"steps": [
|
||||
{
|
||||
"argv": [
|
||||
"/usr/bin/bwrap",
|
||||
"--die-with-parent",
|
||||
"--unshare-user",
|
||||
"--unshare-pid",
|
||||
"--unshare-ipc",
|
||||
"--unshare-uts",
|
||||
"--unshare-cgroup",
|
||||
"--unshare-net",
|
||||
"--tmpfs",
|
||||
"/",
|
||||
"--proc",
|
||||
"/proc",
|
||||
"--dev",
|
||||
"/dev",
|
||||
"--clearenv",
|
||||
"--ro-bind",
|
||||
"/usr",
|
||||
"/usr",
|
||||
"--ro-bind",
|
||||
"/bin",
|
||||
"/bin",
|
||||
"--ro-bind",
|
||||
"/lib",
|
||||
"/lib",
|
||||
"--ro-bind",
|
||||
"/lib64",
|
||||
"/lib64",
|
||||
"--ro-bind",
|
||||
"/etc/resolv.conf",
|
||||
"/etc/resolv.conf",
|
||||
"--ro-bind",
|
||||
"/etc/ssl/certs",
|
||||
"/etc/ssl/certs",
|
||||
"--dir",
|
||||
"/opt",
|
||||
"--dir",
|
||||
"/opt/sandboxer",
|
||||
"--ro-bind",
|
||||
"/home/tegwick/.helixforge-factory/runtimes/f59959b605a51abd2901becde14e7abd852c2bd3f848004b51d250d1cb81af6d",
|
||||
"/opt/sandboxer/runtime",
|
||||
"--dir",
|
||||
"/tmp",
|
||||
"--setenv",
|
||||
"HOME",
|
||||
"/tmp",
|
||||
"--setenv",
|
||||
"PATH",
|
||||
"/opt/sandboxer/runtime/bin:/usr/bin:/bin",
|
||||
"--chdir",
|
||||
"/tmp",
|
||||
"/opt/sandboxer/runtime/bin/rein-aharness",
|
||||
"--help"
|
||||
],
|
||||
"timeout_seconds": 60
|
||||
},
|
||||
{
|
||||
"argv": [
|
||||
"/usr/bin/bwrap",
|
||||
"--die-with-parent",
|
||||
"--unshare-user",
|
||||
"--unshare-pid",
|
||||
"--unshare-ipc",
|
||||
"--unshare-uts",
|
||||
"--unshare-cgroup",
|
||||
"--unshare-net",
|
||||
"--tmpfs",
|
||||
"/",
|
||||
"--proc",
|
||||
"/proc",
|
||||
"--dev",
|
||||
"/dev",
|
||||
"--clearenv",
|
||||
"--ro-bind",
|
||||
"/usr",
|
||||
"/usr",
|
||||
"--ro-bind",
|
||||
"/bin",
|
||||
"/bin",
|
||||
"--ro-bind",
|
||||
"/lib",
|
||||
"/lib",
|
||||
"--ro-bind",
|
||||
"/lib64",
|
||||
"/lib64",
|
||||
"--ro-bind",
|
||||
"/etc/resolv.conf",
|
||||
"/etc/resolv.conf",
|
||||
"--ro-bind",
|
||||
"/etc/ssl/certs",
|
||||
"/etc/ssl/certs",
|
||||
"--dir",
|
||||
"/opt",
|
||||
"--dir",
|
||||
"/opt/sandboxer",
|
||||
"--ro-bind",
|
||||
"/home/tegwick/.helixforge-factory/runtimes/f59959b605a51abd2901becde14e7abd852c2bd3f848004b51d250d1cb81af6d",
|
||||
"/opt/sandboxer/runtime",
|
||||
"--dir",
|
||||
"/tmp",
|
||||
"--setenv",
|
||||
"HOME",
|
||||
"/tmp",
|
||||
"--setenv",
|
||||
"PATH",
|
||||
"/opt/sandboxer/runtime/bin:/usr/bin:/bin",
|
||||
"--chdir",
|
||||
"/tmp",
|
||||
"/opt/sandboxer/runtime/bin/glas-harness",
|
||||
"--help"
|
||||
],
|
||||
"timeout_seconds": 60
|
||||
},
|
||||
{
|
||||
"argv": [
|
||||
"/usr/bin/bwrap",
|
||||
"--die-with-parent",
|
||||
"--unshare-user",
|
||||
"--unshare-pid",
|
||||
"--unshare-ipc",
|
||||
"--unshare-uts",
|
||||
"--unshare-cgroup",
|
||||
"--unshare-net",
|
||||
"--tmpfs",
|
||||
"/",
|
||||
"--proc",
|
||||
"/proc",
|
||||
"--dev",
|
||||
"/dev",
|
||||
"--clearenv",
|
||||
"--ro-bind",
|
||||
"/usr",
|
||||
"/usr",
|
||||
"--ro-bind",
|
||||
"/bin",
|
||||
"/bin",
|
||||
"--ro-bind",
|
||||
"/lib",
|
||||
"/lib",
|
||||
"--ro-bind",
|
||||
"/lib64",
|
||||
"/lib64",
|
||||
"--ro-bind",
|
||||
"/etc/resolv.conf",
|
||||
"/etc/resolv.conf",
|
||||
"--ro-bind",
|
||||
"/etc/ssl/certs",
|
||||
"/etc/ssl/certs",
|
||||
"--dir",
|
||||
"/opt",
|
||||
"--dir",
|
||||
"/opt/sandboxer",
|
||||
"--ro-bind",
|
||||
"/home/tegwick/.helixforge-factory/runtimes/f59959b605a51abd2901becde14e7abd852c2bd3f848004b51d250d1cb81af6d",
|
||||
"/opt/sandboxer/runtime",
|
||||
"--dir",
|
||||
"/tmp",
|
||||
"--setenv",
|
||||
"HOME",
|
||||
"/tmp",
|
||||
"--setenv",
|
||||
"PATH",
|
||||
"/opt/sandboxer/runtime/bin:/usr/bin:/bin",
|
||||
"--chdir",
|
||||
"/tmp",
|
||||
"/opt/sandboxer/runtime/bin/claude",
|
||||
"--version"
|
||||
],
|
||||
"timeout_seconds": 60
|
||||
}
|
||||
],
|
||||
"runtime_source_file_unchanged": true,
|
||||
"complete_runtime_digest_verified_after_bytecode_refinement": true
|
||||
}
|
||||
|
|
@ -0,0 +1,239 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Exact install-only action; execute remotely only after founder approval."""
|
||||
import argparse
|
||||
import hashlib
|
||||
import importlib.util
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path, PurePosixPath
|
||||
import pwd
|
||||
import shutil
|
||||
import signal
|
||||
import stat
|
||||
import subprocess
|
||||
import sys
|
||||
import tarfile
|
||||
import time
|
||||
|
||||
DIGEST = 'f59959b605a51abd2901becde14e7abd852c2bd3f848004b51d250d1cb81af6d'
|
||||
ARCHIVE = '4c55b2740e759175d645705518fc33db30b26b4611f68e052a4ca909173084c7'
|
||||
RUNTIME_MODULE = '19b5fb687288bbc836978b0251599dc1e4d1593e8b5efee85a6abb6a7f388412'
|
||||
STORE_MODULE = 'c3d6ec09d08881611d3d02b8f55d68deed6ecfedaa1a809b6b11c57cc4ca97cf'
|
||||
REHEARSAL_MODULE = '192f654cad4a5f065c7faa5e35b03493297a472e8d7fe641be2c42c5fb00dcc3'
|
||||
STAGE = Path('/home/tegwick/.helixforge-factory/runtime-install-f599-r2-20261017')
|
||||
STORE = Path('/home/tegwick/.helixforge-factory/runtimes')
|
||||
DESTINATION = STORE/DIGEST
|
||||
LOGICAL_BYTES = 260668241
|
||||
ARCHIVE_BYTES = 109931109
|
||||
MEMBERS = 1448
|
||||
ENV = {'HOME':'/home/tegwick','PATH':'/usr/bin:/bin','LANG':'C.UTF-8','PYTHONDONTWRITEBYTECODE':'1'}
|
||||
|
||||
|
||||
def require(value, code):
|
||||
if not value: raise ValueError(code)
|
||||
|
||||
|
||||
def file_digest(path):
|
||||
with path.open('rb') as source:return hashlib.file_digest(source,'sha256').hexdigest()
|
||||
|
||||
|
||||
def protected(path, *, directory=False, mode=None):
|
||||
s=path.lstat()
|
||||
require(not stat.S_ISLNK(s.st_mode) and s.st_uid==1000 and s.st_gid==1000,'path_ownership')
|
||||
require(stat.S_ISDIR(s.st_mode) if directory else stat.S_ISREG(s.st_mode),'path_type')
|
||||
require(not stat.S_IMODE(s.st_mode)&0o022,'path_write_mode')
|
||||
if mode is not None:require(stat.S_IMODE(s.st_mode)==mode,'path_exact_mode')
|
||||
return s
|
||||
|
||||
|
||||
def identify(supervisor):
|
||||
require(os.getuid()==1000 and os.getgid()==1000 and pwd.getpwuid(1000).pw_name=='tegwick','account_identity')
|
||||
for p in (Path('/home/tegwick'),Path('/home/tegwick/.helixforge-factory'),STORE):
|
||||
protected(p,directory=True,mode=0o700 if p!=Path('/home/tegwick') else 0o750)
|
||||
protected(STORE/'.install.lock',mode=0o600)
|
||||
code,output=supervisor.run(['/usr/local/bin/kubectl','get','nodes','-o','json'],20,capture=True)
|
||||
require(code==0,'node_readback')
|
||||
nodes=json.loads(output)['items']
|
||||
require(len(nodes)==1 and any(a['address']=='92.205.62.239' for a in nodes[0]['status']['addresses'])
|
||||
and any(c['type']=='Ready' and c['status']=='True' for c in nodes[0]['status']['conditions']),'node_identity')
|
||||
require(shutil.disk_usage(STORE).free >= 2*1024**3,'disk_headroom')
|
||||
mem=dict(x.split(':',1) for x in Path('/proc/meminfo').read_text().splitlines())
|
||||
require(int(mem['MemAvailable'].split()[0])*1024>=1024**3,'memory_headroom')
|
||||
require(os.getloadavg()[0]<=2*(os.cpu_count() or 1),'load_headroom')
|
||||
read_cpu=lambda:[int(x) for x in Path('/proc/stat').read_text().splitlines()[0].split()[1:9]]
|
||||
a=read_cpu();time.sleep(1);b=read_cpu();d=[y-x for x,y in zip(a,b)]
|
||||
require(1-(d[3]+d[4])/sum(d)<=0.85,'cpu_headroom')
|
||||
|
||||
|
||||
def runtime_reader():
|
||||
p=STAGE/'runtime.py';protected(p,mode=0o600)
|
||||
require(file_digest(p)==RUNTIME_MODULE,'runtime_reader_pin')
|
||||
spec=importlib.util.spec_from_file_location('approved_runtime_reader',p)
|
||||
module=importlib.util.module_from_spec(spec);spec.loader.exec_module(module)
|
||||
return module
|
||||
|
||||
|
||||
def extract():
|
||||
archive=STAGE/'runtime.tar.gz';protected(archive,mode=0o600)
|
||||
require(archive.stat().st_size==ARCHIVE_BYTES and file_digest(archive)==ARCHIVE,'archive_pin')
|
||||
root=STAGE/'runtime';require(not root.exists() and not root.is_symlink(),'extraction_already_present')
|
||||
with tarfile.open(archive,mode='r:gz') as tf:
|
||||
members=tf.getmembers();require(len(members)==MEMBERS,'archive_member_count')
|
||||
seen=set(); logical=0
|
||||
for info in members:
|
||||
name=PurePosixPath(info.name)
|
||||
require(not name.is_absolute() and name.parts and name.parts[0]=='runtime'
|
||||
and all(x not in ('','..','.') for x in name.parts),'archive_name')
|
||||
require(info.name not in seen,'archive_duplicate');seen.add(info.name)
|
||||
require(info.uid==1000 and info.gid==1000 and (info.issym() or not info.mode&0o7022),'archive_modes')
|
||||
require(info.isdir() or info.isfile() or info.issym(),'archive_special_member')
|
||||
if info.isfile():logical+=info.size
|
||||
if info.issym():require(not PurePosixPath(info.linkname).is_absolute(),'archive_absolute_symlink')
|
||||
require(logical==LOGICAL_BYTES,'archive_size')
|
||||
# Populate directories and files before any symlink; never traverse an
|
||||
# archive-created symlink while extracting a later member.
|
||||
dirs=[]
|
||||
for info in members:
|
||||
if not info.isdir():continue
|
||||
p=STAGE/info.name;p.mkdir(mode=0o700);dirs.append((p,info.mode))
|
||||
for info in members:
|
||||
if not info.isfile():continue
|
||||
p=STAGE/info.name
|
||||
require(all(not x.is_symlink() for x in (p.parent,*p.parent.parents)),'archive_parent_alias')
|
||||
fd=os.open(p,os.O_WRONLY|os.O_CREAT|os.O_EXCL|os.O_NOFOLLOW,0o600)
|
||||
with os.fdopen(fd,'wb') as target, tf.extractfile(info) as source:
|
||||
shutil.copyfileobj(source,target,1024*1024)
|
||||
p.chmod(info.mode)
|
||||
for info in members:
|
||||
if info.issym():
|
||||
p=STAGE/info.name;p.symlink_to(info.linkname)
|
||||
require(p.resolve(strict=True).is_relative_to(root),'archive_symlink_escape')
|
||||
for p,mode in reversed(dirs):p.chmod(mode)
|
||||
require(runtime_reader().runtime_digest(root)==DIGEST,'extracted_complete_digest')
|
||||
return root
|
||||
|
||||
|
||||
def expired(signum, frame):
|
||||
raise TimeoutError('bounded_stage_deadline')
|
||||
|
||||
|
||||
def install_phase():
|
||||
from sandboxer.extensions import runtime_store, runtime
|
||||
source=STAGE/'runtime'
|
||||
require(Path(runtime_store.__file__).resolve().is_relative_to(source)
|
||||
and file_digest(Path(runtime_store.__file__))==STORE_MODULE,'installer_source_pin')
|
||||
require(Path(runtime.__file__).resolve().is_relative_to(source)
|
||||
and file_digest(Path(runtime.__file__))==RUNTIME_MODULE,'installed_digest_source_pin')
|
||||
require(not DESTINATION.exists() and not DESTINATION.is_symlink(),'destination_not_absent')
|
||||
signal.signal(signal.SIGALRM,expired);signal.alarm(180)
|
||||
try:result=runtime_store.install_runtime(source,STORE,DIGEST)
|
||||
finally:signal.alarm(0)
|
||||
require(result['runtime']['path']==str(DESTINATION) and not result['reused']
|
||||
and result['profile_activated'] is False and result['credential_delivery_configured'] is False,'installer_result')
|
||||
print(json.dumps({'status':'installed','result':result},sort_keys=True))
|
||||
|
||||
|
||||
def rehearse_phase(supervision):
|
||||
from rein_aharness import owner_rehearsal
|
||||
require(Path(owner_rehearsal.__file__).resolve().is_relative_to(DESTINATION)
|
||||
and file_digest(Path(owner_rehearsal.__file__))==REHEARSAL_MODULE,'rehearsal_source_pin')
|
||||
require(owner_rehearsal.SYSTEM_RO_BINDS==('/usr','/bin','/lib','/lib64','/etc/resolv.conf','/etc/ssl/certs')
|
||||
and all(Path(p).exists() for p in owner_rehearsal.SYSTEM_RO_BINDS),'frozen_system_bind_contract')
|
||||
signal.signal(signal.SIGALRM,expired);signal.alarm(240)
|
||||
# Keep the frozen owner launch contract; use its supported Runner seam.
|
||||
def bounded_run(argv,timeout):
|
||||
require(argv[0]=='/usr/bin/bwrap' and '--die-with-parent' not in argv,'bwrap_runner_contract')
|
||||
return supervision_request(supervision,[argv[0],'--die-with-parent',*argv[1:]],timeout)[0]
|
||||
try:steps=owner_rehearsal.rehearse(DESTINATION,run=bounded_run,bwrap_bin='/usr/bin/bwrap')
|
||||
finally:signal.alarm(0)
|
||||
require([x.name for x in steps]==['rein-aharness','glas-harness','claude'],'rehearsal_steps')
|
||||
report={x.name:x.exit_code for x in steps}
|
||||
print(json.dumps({'status':'rehearsed' if all(x.ok for x in steps) else 'rehearsal_failed','steps':report,
|
||||
'model_calls':0,'credential_reads':False,'owner_configuration_read':False},sort_keys=True))
|
||||
return 0 if all(x.ok for x in steps) else 1
|
||||
|
||||
|
||||
def supervision_module():
|
||||
spec=importlib.util.spec_from_file_location('approved_process_supervision',STAGE/'process_supervision.py')
|
||||
module=importlib.util.module_from_spec(spec);spec.loader.exec_module(module)
|
||||
return module
|
||||
|
||||
|
||||
def supervision_request(directory,argv,timeout):
|
||||
return supervision_module().request(directory,argv,timeout)
|
||||
|
||||
|
||||
def phase(supervisor, argv, timeout):
|
||||
code,output=supervisor.run(argv,timeout,capture=True)
|
||||
try:result=json.loads(output)
|
||||
except Exception:raise ValueError('phase_receipt_unavailable') from None
|
||||
return code,result
|
||||
|
||||
|
||||
def main():
|
||||
parser=argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('--stage-inode',type=int)
|
||||
parser.add_argument('--phase',choices=('install','rehearse'))
|
||||
parser.add_argument('--supervision-dir')
|
||||
args=parser.parse_args()
|
||||
if args.phase=='install':install_phase();return 0
|
||||
if args.phase=='rehearse':
|
||||
require(args.supervision_dir==str(STAGE/'supervision'),'supervision_path')
|
||||
return rehearse_phase(args.supervision_dir)
|
||||
receipt={'proposal_id':'REINAH-WP-0008-T07-install-f599-r1','revision':2,'status':'failed',
|
||||
'model_calls':0,'credential_reads':False,'owner_configuration_read':False,
|
||||
'renewal_or_catalog_or_queue_mutation':False,'destination':str(DESTINATION)}
|
||||
identity=None;supervisor=None;quiescent=False
|
||||
try:
|
||||
observed=protected(STAGE,directory=True,mode=0o700)
|
||||
require(observed.st_ino==args.stage_inode and observed.st_dev==2049,'stage_identity')
|
||||
identity=observed
|
||||
os.nice(19)
|
||||
supervisor=supervision_module().Supervisor(STAGE/'supervision')
|
||||
identify(supervisor)
|
||||
require(not DESTINATION.exists() and not DESTINATION.is_symlink(),'destination_not_absent')
|
||||
require({p.name for p in STAGE.iterdir()}=={'runtime.tar.gz','runtime.py','install-verified.py','process_supervision.py','supervision'},'stage_inventory')
|
||||
receipt['phase']='extract_and_verify';source=extract()
|
||||
receipt['extracted_complete_digest_verified']=True
|
||||
receipt['phase']='install'
|
||||
code,result=phase(supervisor,[str(source/'bin/python3'),'-I','-B',str(STAGE/'install-verified.py'),'--phase','install'],200)
|
||||
receipt['installation']=result;require(code==0 and result['status']=='installed','installation_failed')
|
||||
require(runtime_reader().runtime_digest(DESTINATION)==DIGEST,'installed_complete_digest')
|
||||
receipt['installed_complete_digest_verified']=True
|
||||
receipt['phase']='offline_rehearsal'
|
||||
code,result=phase(supervisor,[str(DESTINATION/'bin/python3'),'-I','-B',str(STAGE/'install-verified.py'),'--phase','rehearse','--supervision-dir',str(STAGE/'supervision')],260)
|
||||
receipt['rehearsal']=result;require(code==0 and result['status']=='rehearsed','rehearsal_failed')
|
||||
require(runtime_reader().runtime_digest(DESTINATION)==DIGEST,'post_rehearsal_complete_digest')
|
||||
receipt['post_rehearsal_complete_digest_verified']=True
|
||||
receipt['status']='installed_and_offline_rehearsal_verified'
|
||||
except Exception as exc:
|
||||
receipt['failure_type']=type(exc).__name__
|
||||
if isinstance(exc,ValueError) and str(exc).replace('_','').isalnum():receipt['failure_code']=str(exc)
|
||||
finally:
|
||||
receipt['destination_exists']=DESTINATION.exists() or DESTINATION.is_symlink()
|
||||
if supervisor is not None:
|
||||
try:
|
||||
receipt['process_supervision']=supervisor.close();quiescent=True
|
||||
except Exception:
|
||||
receipt['process_supervision']={'quiescent':False,'status':'cleanup_unverified'}
|
||||
receipt['status']='failed'
|
||||
if identity is not None and quiescent:
|
||||
try:
|
||||
current=protected(STAGE,directory=True,mode=0o700)
|
||||
require((current.st_dev,current.st_ino)==(identity.st_dev,identity.st_ino),'cleanup_stage_identity')
|
||||
require({p.name for p in STAGE.iterdir()} <= {'runtime.tar.gz','runtime.py','install-verified.py','process_supervision.py','runtime','supervision'},'cleanup_unexpected_stage_member')
|
||||
shutil.rmtree(STAGE)
|
||||
receipt['owned_staging_removed']=not STAGE.exists() and not STAGE.is_symlink()
|
||||
except Exception:receipt['owned_staging_removed']=False;receipt['status']='failed'
|
||||
else:
|
||||
receipt['owned_staging_removed']=False
|
||||
receipt['staging_retained_for_quiescence_review']=True
|
||||
print(json.dumps(receipt,sort_keys=True))
|
||||
return 0 if receipt['status']=='installed_and_offline_rehearsal_verified' else 1
|
||||
|
||||
|
||||
if __name__=='__main__':
|
||||
try:raise SystemExit(main())
|
||||
except Exception as exc:
|
||||
print(json.dumps({'status':'failed','failure_type':type(exc).__name__,'private_diagnostics_suppressed':True}))
|
||||
raise SystemExit(1) from None
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"status": "failed preparation retained; exact original artifact restored",
|
||||
"cause": "owner argv capture import omitted bytecode suppression, creating one local pyc; preflight refused source_changed before any target call",
|
||||
"removed_only_newly_generated_bytecode": {
|
||||
"path": "/tmp/netkingdom-runtime-canonical-20261016/runtime/lib/python3.12/site-packages/rein_aharness/__pycache__/owner_rehearsal.cpython-312.pyc",
|
||||
"sha256": "sha256:c25d9ab3cc87fe2d91fdfe6397783d188efabddaa833bbf4dc1222fb8e8a6a45",
|
||||
"bytes": 4810,
|
||||
"mtime_ns": 1792216762800164055
|
||||
},
|
||||
"removed_only_new_empty_parent": true,
|
||||
"restored_complete_runtime_digest": "sha256:f59959b605a51abd2901becde14e7abd852c2bd3f848004b51d250d1cb81af6d",
|
||||
"target_mutations": 0,
|
||||
"refinement": "all subsequent artifact inspection uses python3 -B; no artifact source bytes/modes modified"
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Local archive roundtrip only; no native execution or runtime launch."""
|
||||
import importlib.util,json,shutil,tempfile
|
||||
from pathlib import Path
|
||||
HERE=Path(__file__).resolve().parent
|
||||
|
||||
def load(name,path):
|
||||
s=importlib.util.spec_from_file_location(name,path);m=importlib.util.module_from_spec(s);s.loader.exec_module(m);return m
|
||||
|
||||
def main():
|
||||
builder=load('transfer_builder',HERE/'build-transfer.py')
|
||||
verifier=load('remote_install_code_review',HERE/'install-verified.py')
|
||||
with tempfile.TemporaryDirectory(prefix='nk-runtime-transfer-check-') as directory:
|
||||
p=Path(directory); archive=p/'runtime.tar.gz'
|
||||
with archive.open('xb') as target:built=builder.build(target)
|
||||
assert built==json.loads((HERE/'transfer-archive.json').read_text())
|
||||
archive.chmod(0o600);shutil.copyfile(HERE/'runtime.py',p/'runtime.py');(p/'runtime.py').chmod(0o600)
|
||||
verifier.STAGE=p
|
||||
extracted=verifier.extract()
|
||||
result={'scope':'local temporary archive roundtrip only','archive_sha256':built['sha256'],
|
||||
'archive_bytes':built['bytes'],'member_count':built['members'],
|
||||
'complete_extracted_digest':verifier.runtime_reader().runtime_digest(extracted),
|
||||
'modes_and_internal_symlinks_preserved':True,'native_actions':0,'runtime_launches':0}
|
||||
assert not p.exists();result['local_temporary_tree_removed']=True
|
||||
(HERE/'local-transfer-verification.json').write_text(json.dumps(result,indent=2)+'\n')
|
||||
print(json.dumps(result))
|
||||
|
||||
if __name__=='__main__':main()
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"scope": "local archive extraction function roundtrip; no remote installer or runtime launch",
|
||||
"archive_sha256": "sha256:4c55b2740e759175d645705518fc33db30b26b4611f68e052a4ca909173084c7",
|
||||
"archive_bytes": 109931109,
|
||||
"member_count": 1448,
|
||||
"complete_extracted_digest": "f59959b605a51abd2901becde14e7abd852c2bd3f848004b51d250d1cb81af6d",
|
||||
"modes_and_internal_symlinks_preserved": true,
|
||||
"native_actions": 0,
|
||||
"runtime_launches": 0,
|
||||
"local_temporary_tree_removed": true,
|
||||
"tested_extract_function_sha256": "sha256:6671d4a0d03860e4ed4dd860c5f9405aefdb342f51eb39b6bce7c59bf18c6321"
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue