open-standards/sandbox/spec/conformance.md
Transaction Science 48372367aa
All checks were successful
standards workspaces build + test / which workspaces this change touches (pull_request) Successful in 7s
standards workspaces build + test / proof-rs (proof/conformance/v1/joulespec.toml, proof-conformance, proof/proof-rs, proof-rs, linux, true) (pull_request) Successful in 1m1s
standards workspaces build + test / joule-ui-rs (joule-ui/joule-ui-rs, joule-ui-rs, linux) (pull_request) Successful in 1m25s
standards workspaces build + test / sandbox-rs (sandbox-conformance, sandbox/sandbox-rs, -p sandbox-tee --features amd-kds,intel-pcs, sandbox-rs, linux) (pull_request) Successful in 1m37s
standards workspaces build + test / jcp-rs (jcp-conformance, jcp/jcp-rs, -p jcp-energy --features nvml, jcp-rs, linux) (pull_request) Successful in 1m46s
repository checks / lockfiles, advisories, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Successful in 3m21s
standards workspaces build + test / jouledesk-rs (jouledesk/jouledesk-rs, jouledesk-rs, macos) (pull_request) Successful in 7m1s
standards workspaces build + test / macOS energy backends (pull_request) Successful in 12m16s
standards workspaces build + test / joulecontract-rs (joulecontract/joulecontract-rs, joulecontract-rs, linux-arm64, true) (pull_request) Successful in 1m15s
standards workspaces build + test / joule-code-rs (joule-code/joule-code-rs, joule-code-rs, linux-arm64, true) (pull_request) Successful in 7m28s
sandbox §13.2: what an executed interval proves about waiting
§13 prices the wait a run spends parked. It says nothing about the wait a run
spends *running*, which is usually the larger number. An interval that dispatches
an outward call and then sits until the result arrives holds its sandbox, and its
receipt charges the sitting as computation. A twenty-minute build, a review that
comes back the next morning, an approval that never comes — each is, in a
blocking runtime, an interval whose receipt is mostly waiting. Two runs doing
identical work bill hours apart, and the record reported both the same way.

So an executed segment may now carry a `CallAccount`: the calls it dispatched
and the results it resolved, each by call id.

A run is **non-blocking** when every executed interval accounts, and every
interval resolves only calls that were outstanding before it began. An interval
that resolves a call it dispatched itself did not hand off and return — it
waited, and the wait is inside its receipt. `RunRecord::wait_proof()` walks the
segments in order holding the outstanding set and returns one of:

- `NonBlocking` — no receipt in the run contains a wait for an outward call, so
  the run's whole cost of waiting is the idle energy §13 already records, and
  which §13.1 makes small for a transcript park. This is the condition under
  which "waiting costs no computation" is a statement about the record rather
  than about the runtime that produced it.
- `Blocked { segment }` — that interval consumed its own dispatch.
- `Malformed { segment }` — the account does not add up: a result answering a
  call never dispatched, a call resolved twice, or one id dispatched twice. A
  record that cannot be read is a different problem from a run that waited, and
  is reported separately.
- `Unproved` — some interval did not account, or the run has no accounted
  interval at all.

Two deliberate pieces of honesty

**An absent account is not an empty one.** Not accounting and accounting for no
calls are different statements, and they do not fold to the same bytes — a test
and a conformance check both hold that, so the marker is not merely a comment.

**A property of no intervals is not a property.** A run with nothing accounted
returns `Unproved`, not `NonBlocking`. Without that, every empty record would
certify the strongest claim in the section.

Backwards compatibility, checked rather than asserted

`calls` is `Option`, `serde(default, skip_serializing_if)`, and folded into the
chain only when present, so an interval recorded by `record_execution` hashes
exactly as it did before — pinned by a test against bytes written out by hand.
The committed vector confirms it: `durable.json` diffs as one added line and no
changed line, so `expect_runrecord_head` and `expect_transcript_head` are
unchanged, and the accounted trace gets its own address at
`b3:FINebhSRpv_1ncmpE3PaRG85MR8Ri1fDy4lmR1yI2DA`. That trace is the transcript
one with the account added and nothing else changed, so an implementation that
dropped the account would produce the transcript head where this one is
expected. Each id set is length-prefixed, so no id boundary is ambiguous.

Conformance class C15, and the pack's deliberate class count moves 14 → 15.

Evidence, on the build host: clippy `-D warnings` clean over the workspace, the
workspace's tests pass, and the full pack reports all 15 classes passed with no
skips. `cargo fmt` was not run: these workspaces are not rustfmt-clean.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-11-02 13:37:56 -05:00

8.8 KiB
Raw Permalink Blame History

Conformance

Normative companion to SPEC.md §10. RFC 2119 keywords apply.

A host is conformant if, for every isolation tier it advertises, it satisfies every REQUIRED clause of SPEC.md §1–§7 and round-trips the conformance vectors in ../conformance/. A conforming host MUST pass the full pack with no skips for its advertised tiers. The vectors — not any brand — define what is correct.

Vector classes

C1 — Canonicality

The content addressing (b3: + base64url over BLAKE3-256) and the canonical forms (det-CBOR for the receipt core, JCS for the attestation core) are reproduced byte-for-byte. A unit descriptor, a grant set, a receipt core, and an attestation core each address to the fixed ContentId in the vector. No float appears in any canonical form.

C2 — Capability enforcement (deny-by-default)

Given the canonical grant set, the Enforcer returns the fixed allow/deny decision for each query in the vector, including the negatives: a dispatch to an ungranted device class is denied; a write to a read-only path is denied; an egress to an unlisted endpoint is denied; a kv access outside granted prefixes is denied; a host service that was not granted is denied. The empty grant set denies everything.

C3 — Receipt integrity and per-device provenance

A receipt round-trips through its JCR-1 envelope to byte-identical bytes and verifies under the steward key. The per-device records sum to total (field-wise), total.measured_uj is present iff every used class carries one, and the (provenance, measured_uj) coupling holds for every record (HwShunt ⇒ present, Estimator ⇒ absent). The pack includes adversarial negatives: an accelerator run that folds GPU energy into the CPU record (rejected), a HwShunt record with no measured_uj (rejected), an Estimator record carrying a measured_uj (rejected), a total that does not equal the device sum (rejected).

C4 — Attestation signing and verification

An attestation core canonicalizes to the fixed JCS bytes and produces the fixed Ed25519 signature under the known signing key; verification accepts it and rejects a single-bit mutation of any field. The attestation's devices classes match the bound receipt's devices classes. A journal_root present in the attestation replays Consistent against the vector's journal; a tampered journal replays Diverged.

C5 — Tier minimization

Given a grant set, the host selects the lowest tier whose guarantees satisfy it (the maximum of the per-grant minimums in tiers.md). The pack fixes the required tier for a set of grant sets: CPU-only trusted → S0/S1; untrusted code → S2; accelerator via brokered interface → S2; multi-tenant → S3; accelerator via direct assignment → S5; confidential → S6. A host that runs above or below the required tier fails C5.

C6 — Durable run records (§13)

A run record chains execute → suspend → resume segments; the head advances and recomputes from genesis. Resuming a snapshot that differs from the one recorded at suspension is refused, as is an execution while parked or an append after close. Idle energy accumulates across parks; a tampered segment (via the wire form) fails verification.

C7 — Environment manifest (§14)

A devcontainer.json ingests to a canonical, content-addressed manifest whose address is invariant to key order and whitespace. Fields that execute commands or widen the sandbox are refused, and unknown fields are refused rather than silently dropped. A resume offering a drifted manifest address is refused.

C8 — Content-addressed process control (§5.5)

Process control decides on the BLAKE3 of the executable's bytes. A denylisted hash is refused regardless of the binary's name (survives rename / move / wrapper) and overrides every allow rule; when a hash allowlist is set it is authoritative over names; exec is still required.

C9 — Warm pools (§15)

A claim attributes the slot's accrued idle energy to the run and is refused unless the slot's environment address matches. An evicted unclaimed slot records its idle energy as overhead, never zero. The pool's total idle energy equals claimed + overhead + pending — nothing hidden.

C10 — Fleet records (§16)

Every item in a fleet proves against the merkle aggregate root with a logarithmic proof, across odd and even leaf counts. A foreign item on a real proof path fails to verify, and any change to a leaf changes the root.

C11 — Enforcement mechanism (§7.9)

An attestation without a mechanism canonicalizes to byte-identical JSON under the /1 profile, so historical signatures and published vectors still verify; one carrying a mechanism is /2, and the commitment is inside the signed bytes. The commitment is injective across field boundaries — moving bytes between the runtime name and the profile changes the digest. A mechanism at S0 is refused, and the relying-party requirement rejects a bare S1–S5 tier label while an evidenced one satisfies it.

C13 — Flow across a run's checkpoints (§5.8)

A run's calls, in order: a read from an untrusted source adds its origins to the run's taint, and a sink is decided against everything read before it. A clean run reaches every sink its capabilities allow. An untrusted read closes the egress and launch that follow it, except where the flow grant opens a sink to every origin the run carries. The taint only grows, so a later clean read restores nothing, and a second origin closes a sink opened to the first. A grant set without a flow grant is never tainted. The order is part of the vector, since no single query can show what a run may do after what it did.

C14 — What a park retains (§13.1)

A park retains host state or a transcript, and a record says which. A transcript park chains, verifies and accumulates idle energy like any other — nothing is resident, so the figure it owes is the draw of the bytes it kept, and zero is not that figure. A reference naming neither form, both, or a transcript beside a resident memory image is refused before anything is hashed and leaves no segment behind; edited into a record afterwards, through the wire form, it fails verification rather than hashing to something. The two forms over the same inner address are different parks, so offering one to resume the other is refused. The image form's bytes are pinned against a hand-written layout, and its committed head is unchanged from before the second form existed — a backwards-compatible extension is one whose old addresses still recompute, which is checkable rather than asserted.

C15 — What an interval proves about waiting (§13.2)

A run whose every executed interval accounts for its outward calls, and whose every interval resolves only calls outstanding before it began, is non-blocking: no receipt in it contains a wait for an outward call, so its whole cost of waiting is the idle energy §13 already records. The hand-off shape — dispatch, end the interval, resolve in a later one — proves it. An interval that resolves a call it dispatched itself is reported as blocking, because the wait is inside its receipt. An account that does not add up (a result answering a call never dispatched, a call resolved twice, one id dispatched twice) is reported as a malformed record, distinctly from blocking. An absent account proves nothing, and neither does a run with no accounted interval: a property of no intervals is not a property, and the claim is unavailable rather than vacuously true. An interval recorded without an account hashes exactly as it did before the section existed.

Adversarial obligation

The most valuable vectors are negatives — runs a conformant host must reject. A standard whose vectors only assert the happy path certifies nothing. The committed pack (../conformance/v1/negative.json) carries a C-neg class the harness asserts is rejected:

  • a HwShunt reading with no measured figure, an Estimator carrying one, a total that does not equal the device sum, and a measured-coverage mismatch (SPEC §6);
  • an attestation that omits an accelerator the receipt metered (SPEC §7.2), and an S6 attestation without TEE evidence (SPEC §7.6);
  • a replay journal that diverges and one that is truncated — neither may compare Consistent (SPEC §8);
  • a mid-run grant-set mutation, whose before/after content addresses MUST differ so a host can detect it (SPEC §1.2).

Growing this set (tier-minimization violations, anti-evasion probing) is the highest-value contribution to this directory (see ../CONTRIBUTING.md).

Running the pack

The reference harness (sandbox-conformance) loads the vectors and checks each class; the sandbox CLI exposes it as sandbox conformance. A run that skips a class for an advertised tier is a fail, not a pass: silent truncation reads as "covered" when it is not.