sandbox: make the tier true, make it checkable, and let S7 locality be falsified #57
Loading…
Reference in a new issue
No description provided.
Delete branch "sandbox/honest-tier"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
sandbox-buildsealed every command step asTier::S5, but the step is run byProcessVmm::new— a bareCommand::spawn. No VMM, no guest kernel, no dedicated vCPU, and no/dev/kvmcheck anywhere on the path. The honest rung isS0("process only; trusted code"), the floor. Build receipts were claiming hardware isolation for work that ran as an ordinary child process on the host.SPEC already forbids this — "A host MUST NOT seal a tier a run did not execute at" — so this is a conformance fix, not a change of meaning.
Why structural rather than a
/dev/kvmcheck at the call siteThe bug shape is the caller asserts a tier. A check bolted onto the one site leaves the next caller free to repeat it. Now the executor reports the tier instead:
Vmm::attained_tier()defaults toTier::S0. The floor is the default deliberately: an implementor who forgets to override under-claims, which is survivable, rather than over-claiming, which makes a receipt a lie.ProcessVmmis genuinely dual-use — supervisingfirecrackerreally is S5, supervisingcargo buildis not — and that cannot be inferred from a program name. So it is declared:newis S0,supervising_vmmmay claim S5, and even then only where the host exposes hardware virtualization.MicroVmExecutor::tier()delegates to its VMM instead of returning a static S5, soHost::run_and_seal's executor-vs-required cross-check can no longer be satisfied by a run that never touched a hypervisor.sandbox-proof::prove_microvmsupervises the real firecracker binary behind an existing/dev/kvmguard, so it declaressupervising_vmmand correctly keeps S5. That path is unchanged in behaviour.Verification
tier.jsonincluded.a_command_step_runs_in_the_microvm_and_seals_at_s5). It failed withleft: S0, right: S5, which is the fix working.Blast radius
Contained to
sandbox-rs; no other workspace path-depends on these crates. Build receipts from this path now read S0 instead of S5 — that is the point, and it is a downgrade of a claim, never of enforcement: nothing that ran before stops running.Second commit: §7.9 enforcement mechanism
The first commit made the sealed tier true. It did not make it checkable — §7.5 says outright that S0–S5 carry no in-band evidence, so the honest
S0it now produces is still just the host'''s word.§7.9 adds an optional
mechanism: the enforcement runtime (name and version) plus a commitment to the policy profile applied and the egress allowlist installed. Carrying one makes the attestationsandbox.attestation/2; without one it stays/1and canonicalizes to byte-identical JSON — the same add-field/bump-profile/dispatch shape used forpayload_hash.What it deliberately does not claim. A mechanism is not proof of isolation, and both the SPEC text and the type docs say so. A host willing to misstate its tier can misstate its profile. What changes is the shape of the claim: an unadorned tier label is unfalsifiable, a
profile_idis checkable against a known-good profile. A party needing proof still requires S6+.Notable decisions:
"seatbelt/14"+"5;deny-net"and"seatbelt/145"+";deny-net"would otherwise collide. C11 asserts they do not.require_mechanism_evidence().Executor::mechanism()defaults toNone— as withattained_tier(), the component that did the work describes it; silence beats invention.WasmExecutoris a real producer, so the field is not decorative. It commits to fuel metering, fuel budget and memory cap — deliberately notuj_per_million_fuel, an accounting coefficient that confines nothing.WASMTIME_VERSIONis drift-tested against Cargo.lock — wasmtime exposes no version constant, and a stale value would attest confinement to a runtime that did not run.Verified: 194 workspace tests pass; conformance passes with no skips across all 11 classes. C4
attestation.json— a published vector — still passes untouched, which is the backward-compatibility claim holding in practice rather than in argument.The two commits are one arc (make the tier true, then make it checkable) and are independent of each other — say the word if you'''d rather they were separate PRs.
Third commit: S7 locality (§7.7)
Locality { region: String, jurisdiction: String }— two free-text strings, supplied by the party under audit, checked for presence and nothing else, at the one tier whose entire purpose is jurisdictional. §7.7 already said a host "MUST NOT seal an S7 run whose execution it cannot bind to the stated locality" — an obligation with no mechanism behind it, and the first thing a data-residency auditor attacks.The physics, and its limit
RTT to an anchor of known position bounds distance: a signal cannot outrun light, so
rtt/2 × cis the farthest the host can be from it. The asymmetry is what makes it worth anything — latency is trivial to add and impossible to remove — so a host can always appear farther away than it is, never closer than physics permits.That yields a test that falsifies but does not verify:
So the passing verdict is named
NotRefuted, there is deliberately nois_proven, and §7.7 forbids reporting a surviving claim as proven, verified, or attested.Three rules that matter more than the arithmetic
Unsubstantiated— and that holds even when they would refute, because the rule is about provenance, not about which answer they happen to give.c, not a fibre constant. Costs sensitivity, buys certainty in the only direction that matters — a refutation under vacuum-c cannot be argued away by an unusual path.It is timing-derived, so by this codebase's own rule (
TimingIsNotAShunt, §6.3) it is its own evidence class: never the standing of the verifiedteeevidence at S6, and carrying noProvenanceat all.Verification
203 workspace tests; conformance passes all 12 classes with no skips (new C12 locality-evidence pins refutation, the unsigned rule, below-S7 rejection, and byte-identical absence); published vectors unchanged on disk; no clippy warnings in the new file.
`sandbox-build` sealed every command step as `Tier::S5`. The step is run by `ProcessVmm::new`, which is a bare `Command::spawn` — no VMM, no guest kernel, no dedicated vCPU, and no `/dev/kvm` check anywhere on the path. The rung it actually reaches is `S0` ("process only; trusted code"), the floor. Build receipts were therefore claiming hardware isolation for work that ran as an ordinary child process on the host. SPEC already forbids this — "A host MUST NOT seal a tier a run did not execute at" — so this is a conformance fix, not a change of meaning. The fix is structural rather than a `/dev/kvm` check bolted onto the one call site, because the bug's shape is "the caller asserts a tier". Now the executor reports one: - `Vmm::attained_tier()` defaults to `Tier::S0`. The default is the floor on purpose: an implementor who forgets to override under-claims, which is survivable, instead of over-claiming, which makes a receipt a lie. - `ProcessVmm` is genuinely dual-use — supervising `firecracker` really is S5, supervising `cargo build` is not — and that cannot be inferred from a program name. It is now declared: `new` is S0, `supervising_vmm` may claim S5, and even then only where the host exposes hardware virtualization. - `MicroVmExecutor::tier()` delegates to its VMM instead of returning a static S5, so `Host::run_and_seal`'s executor-vs-required cross-check can no longer be passed by a run that never touched a hypervisor. - `sandbox-proof::prove_microvm` supervises the real firecracker binary behind an existing `/dev/kvm` guard, so it declares `supervising_vmm` and keeps S5. Verified: 188 workspace tests pass, conformance passes with no skips (C5 tier.json included). The one test that changed is the one that encoded the bug in its name.sandbox: seal the tier a run actually reached, never the one it intendedto sandbox: make the sealed tier true, then make it checkable (§7.9)`Locality { region: String, jurisdiction: String }` — two free-text strings, supplied by the party under audit, checked for presence and nothing else, at the one tier whose entire purpose is jurisdictional. SPEC §7.7 already said a host "MUST NOT seal an S7 run whose execution it cannot bind to the stated locality", which was an obligation with no mechanism behind it. It is the weakest normative claim in the ladder and the first thing a data-residency auditor attacks. Round-trip time to an anchor of known position bounds distance: a signal cannot outrun light, so `rtt/2 × c` is the farthest the host can be from that anchor. The asymmetry is what makes it worth anything — **latency is trivial to add and impossible to remove** — so a host can always appear farther away than it is and never closer than physics permits. What that buys is narrow, and the code is named for it. The evidence **falsifies; it does not verify**: - It can refute a claim. If the claimed position is farther from an anchor than light could travel in the observed time, the claim is impossible, and that conclusion does not rest on trusting anyone. - It can never confirm one. Surviving every bound means "not excluded" and nothing more: the feasible region is an intersection of discs that routinely spans several jurisdictions. So the passing verdict is `NotRefuted`, there is deliberately no `is_proven`, and §7.7 forbids reporting a surviving claim as proven, verified, or attested. Three rules that matter more than the arithmetic: - **Only an independent anchor's signed observation counts.** A round-trip time the host measured about itself is the same unchecked assertion as the region string it was meant to improve on — now with a number attached. Unsigned observations return `Unsubstantiated` rather than being blended in, and that holds even when they would *refute*: the rule is about provenance, not about which answer they happen to give. - **A host cannot seal an attestation its own evidence refutes.** That is not a marginal judgement about trust; it is a physical impossibility the host itself just recorded. - **The bound uses vacuum c, not a fibre constant.** This costs sensitivity and buys certainty in the only direction that matters: a refutation under vacuum-c cannot be argued away by an unusual path. It is timing-derived, so by this codebase's own rule (`TimingIsNotAShunt`, §6.3) it is its own evidence class — it never reaches the standing of the verified `tee` evidence at S6 and carries no `Provenance` at all. Absent, the field is omitted and the canonical bytes are unchanged, so S7 attestations sealed before this section still verify — C12 asserts that alongside the refutation, the unsigned-observation rule, and the below-S7 rejection. Verified: 203 workspace tests pass, conformance passes all 12 classes with no skips, published vectors unchanged on disk, no clippy warnings in the new file.sandbox: make the sealed tier true, then make it checkable (§7.9)to sandbox: make the tier true, make it checkable, and let S7 locality be falsified