Some checks failed
repository checks / lockfiles, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Successful in 55s
standards workspaces build + test / which workspaces this change touches (pull_request) Successful in 5s
standards workspaces build + test / joule-code-rs (joule-code/joule-code-rs, joule-code-rs, linux-arm64, true) (pull_request) Successful in 2m2s
standards workspaces build + test / joulecontract-rs (joulecontract/joulecontract-rs, joulecontract-rs, linux-arm64, true) (pull_request) Successful in 1m42s
standards workspaces build + test / macOS energy backends (pull_request) Successful in 6m24s
standards workspaces build + test / jouledesk-rs (jouledesk/jouledesk-rs, jouledesk-rs, macos) (pull_request) Successful in 1m26s
standards workspaces build + test / jcp-rs (jcp-conformance, jcp/jcp-rs, -p jcp-energy --features nvml, jcp-rs, linux) (pull_request) Failing after 1m7s
standards workspaces build + test / joule-ui-rs (joule-ui/joule-ui-rs, joule-ui-rs, linux) (pull_request) Successful in 1m16s
standards workspaces build + test / proof-rs (proof-conformance, proof/proof-rs, proof-rs, linux, true) (pull_request) Successful in 1m32s
standards workspaces build + test / sandbox-rs (sandbox-conformance, sandbox/sandbox-rs, sandbox-rs, linux) (pull_request) Successful in 2m31s
`EnvironmentManifest::is_pinned()` checked the base image's digest alone, while its doc promised "fully pinned" and "same address ⇒ same bytes". Its own test asserted it true for a manifest whose features were `rust:1` and `node:1`. Those are tags, which resolve to whatever was last pushed under them, so two hosts can run different toolchains under one address. The spec repeated the overclaim: it called such a manifest pinned, and so fit for a cross-host "same environment" claim. - `Feature::is_pinned()`: the reference carries an `@sha256:` digest. - `EnvironmentManifest::is_pinned()`: the image and every feature are digest-addressed. `image_pinned()` is the old image-only check. - Its docs, the spec (spec/environment-manifest.md, SPEC §14): pinned means the whole toolchain. Mounted content is data and never part of the address, so a pinned manifest says nothing about what its mounts hold. - C7 conformance checks the image (as it always said it did), that a feature by tag leaves the manifest unpinned, and that digest features pin it. Pinned-ness is not in the manifest core, so no content address or vector changes. Co-Authored-By: Claude Opus 5.5 <[email protected]>
68 lines
3.4 KiB
Markdown
68 lines
3.4 KiB
Markdown
# The environment manifest
|
|
|
|
A receipt says what a unit cost; an attestation says who ran it, where, at
|
|
what tier. Neither says **in what environment** — which base image, which
|
|
tool features, which variables and mounts. For short runs that gap is
|
|
tolerable. For durable runs (§13) it is not: a run suspended on one host and
|
|
resumed on another is only the *same run* if it resumes into the *same
|
|
toolchain*, and "same" must be checkable by hash, not asserted by trust.
|
|
|
|
The **environment manifest** closes the gap: one canonical object whose
|
|
BLAKE3 content address stands for the whole declared environment.
|
|
|
|
## Ingestion: the devcontainer format
|
|
|
|
Environments are declared in the open Development Containers specification
|
|
(`devcontainer.json`) — already the common declaration format across the
|
|
industry. This standard ingests its **declarative subset**:
|
|
|
|
| accepted | meaning |
|
|
|---|---|
|
|
| `image` | base image reference; a `@sha256:` digest pins the image |
|
|
| `features` | tool features, normalized to (id, JCS-canonical options), sorted; a reference with a `@sha256:` digest is pinned, one by tag (`:1`) floats |
|
|
| `containerEnv` | environment variables, sorted |
|
|
| `mounts` | string or object form, normalized to (source, target, kind), sorted by target |
|
|
| `remoteUser` | the user the unit runs as |
|
|
|
|
Cosmetic and editor-facing fields (`name`, `customizations`, …) are accepted
|
|
and excluded from identity. Networking fields (`forwardPorts`, `appPort`, …)
|
|
are likewise excluded: the network surface is governed by the capability
|
|
grant (§5), never by the environment declaration.
|
|
|
|
**Refused fields.** Anything that executes commands or widens the sandbox is
|
|
refused with an error, not skipped: `build`, `dockerFile`,
|
|
`dockerComposeFile`, the five lifecycle `*Command` hooks, `runArgs`,
|
|
`privileged`, `capAdd`, `securityOpt`. Lifecycle automation belongs to the
|
|
unit and its grants, where it is metered and attested — not smuggled into an
|
|
identity object. **Unknown fields are also refused**: silently dropping a
|
|
field a future devcontainer revision makes execution-relevant would mint a
|
|
false identity.
|
|
|
|
## Canonical form, address, signature
|
|
|
|
The manifest core (`schema: "sandbox.env-manifest/1"`) is JCS-canonicalized;
|
|
its BLAKE3 address is the environment's identity; it is signed Ed25519 over
|
|
the canonical bytes, mirroring the attestation convention (§7.3). Key order
|
|
and whitespace in the source document do not affect the address.
|
|
|
|
A manifest is **pinned** only when its image and every feature are addressed
|
|
by digest. Otherwise it is **unpinned**: its address identifies a
|
|
declaration, not bytes — a tag such as `rust:1` resolves to whatever its
|
|
publisher last pushed, so a pinned image beside a feature by tag does not fix
|
|
the toolchain. Hosts SHOULD treat unpinned manifests the way the energy
|
|
receipt treats estimates — usable, but labelled, and never the basis for a
|
|
"same environment" claim across hosts.
|
|
|
|
Mounted content is data, not toolchain, and is never part of the address. A
|
|
pinned manifest fixes what a unit runs with; it says nothing about what its
|
|
mounts hold.
|
|
|
|
## Use in durable runs
|
|
|
|
`SnapshotRef.manifest` (§13) records the manifest address at suspension.
|
|
Resumption MUST be refused when the offered environment's manifest address
|
|
differs — same filesystem plus a drifted toolchain is a different snapshot.
|
|
Conformance class **C7** exercises ingestion determinism, the refusals, and
|
|
this linkage.
|
|
|
|
The reference implementation is `sandbox_attest::manifest`.
|