Engineering records
Apple Container design record
The reviewed architecture, implementation milestones and physical conformance gates behind the Apple backend.This document preserves the staged design and its security rationale. For the
current user-facing contract, supported entry points and exact capability
matrix, read apple-container.md and
backend-capabilities.md.
Documentation convention: internal design records and wire-protocol specifications are written in English; user-facing guides and CLI tutorials are written in French.
Status: MP1–MP8 are implemented and shipped. The initial ABI 8 rollout used
receipt v3; since 0.26.0 OCI product executions use receipt v4 to bind the
selected runtime closure as well. Executor ABI 10 made both service candidates
ineligible in 0.38 because they did not prove the mandatory resource budget.
Version 0.40 restores only apple-container-bubblewrap: exact VM sizing is
service-attested, while a Warden-owned nested cgroup protected by Bubblewrap is
observed through MWAR/2 and serialized in receipt v7. The simple candidate
remains intentionally ineligible because root can mutate its cgroup.
Target-aware planning, sealed artifacts,
bounded lifecycle, the plain backend,
the injected Bubblewrap composite, explicit arm64/amd64 selection, Rosetta evidence
and the CLI/C/Python/Node OCI surfaces are green. The native/translated,
simple/composite and Egress physical macOS 26 / Apple Container 1.3.0 gates pass
locally; they remain explicit and intentionally outside make check.
Apple Container is an optional runtime. If the trusted Swift driver is absent,
registration returns MAELYS_EXECUTOR_ERR_UNSUPPORTED. If the driver exists
but Apple Container 1.3.0 is absent or unconfigured, its exact probe exits 78;
Warden returns the same unsupported status, keeps capabilities at zero and
records a diagnostic naming the missing runtime. Native Seatbelt executions
continue normally. An explicitly requested Apple candidate never falls back to
Seatbelt, POSIX or another OCI runtime.
The distribution keeps this optionality physical. The core package contains no
Swift binary. A separate macOS-arm64 release artifact contains the
ad-hoc-signed driver and a machine-readable requirement for Apple
Container 1.3.0. maelys_executor_create_default() discovers a safe installed
driver and registers the plain apple-container candidate, but does not probe
or select it for local TargetSpecs. It also registers the composite when the
installed Warden guest bundle passes ownership, mode, architecture and digest
verification. Compiling the driver requires Xcode; installing and running the
packaged artifact does not.
The target is Apple Container 1.3.0, pinned by version and executable
identity. Apple describes container as an Apple-Silicon/macOS-26 tool that
runs each OCI container in a lightweight Linux VM; the lower-level
Containerization package uses Virtualization.framework and a vminitd gRPC
control plane over vsock:
- Apple Container 1.3.0 release
- Apple Container requirements and OCI contract
- Containerization architecture and vminitd
The VM boundary changes Warden's deployment mechanics, but not MIR semantics. A canonical MIR remains a resolved decision. A trusted RunSpec says what is to be executed and where selection may place it.
Target architecture
Trusted RunSpec
├── TargetSpec
│ ├── local
│ │ └── canonical host executable + argv
│ └── oci
│ ├── image reference + mandatory resolved digest
│ ├── platform
│ ├── command / entrypoint
│ └── guest cwd
├── canonical MIR
├── DeploymentContext
└── SelectionPolicy
├── allowed backend/candidate names
├── VM preference or requirement
├── cost/latency ceilings
└── deterministic tie-break orderTargetSpec is not policy and does not belong in MIR. A local target cannot
silently become an OCI image. An oci target cannot silently execute a host
binary. Any conversion or image build is an explicit trusted preprocessing
step with its own digest.
New opaque model
The public surface is opaque from its first release:
maelys_target_spec_t:localoroci, immutable after construction;maelys_deployment_context_t: host OS/arch, available runtimes, verified helper identities, path roots, cache and resource availability;maelys_selection_policy_t: allow/deny candidates, VM preference, security floor, startup-cost ceiling and deterministic weighting;maelys_trusted_runspec_t: retains the three objects plus canonical MIR;maelys_candidate_report_t: ordered accepted/rejected candidates with exact reasons, safe for diagnostics;maelys_run_artifact_t: immutable selected target, layer plan and pinned identities; one-shot so selection and probe evidence cannot drift between repeated spawns.
Getters expose borrowed views only. Every setter copies. Seal computes a RunSpec digest. ABI growth uses new getters/entry points, not exposed structs.
Candidate planning, not first-match selection
The current Executor computes required capability bits and chooses the first backend that is a superset. The replacement planner has four stages:
- Derive semantic requirements from MIR and TargetSpec: filesystem rule fidelity, view mappings, tmpfs, direct/none/mediated networking, process-tree semantics, OCI, VM boundary, architecture and resource requirements.
- Enumerate compatible composites. Candidate names are stable public identities; a composite is not inferred from two independently selected backends.
- Probe and reject fail-closed. Version, platform, helper/image digest, kernel feature and every semantic capability must be proven. A candidate with a close approximation is rejected rather than scored.
- Score eligible candidates deterministically using a lexicographic tuple: policy priority, requested VM preference, semantic fidelity, isolation strength, warm availability, startup cost, then stable candidate name.
No fallback occurs after prepare. If the chosen runtime disappears or its identity changes before spawn, spawn fails. It does not select a weaker candidate behind the caller's back.
Explicit composite backends
apple-container
This candidate accepts only an OCI TargetSpec. It provides the VM boundary, OCI image execution, a verified image digest, guest cwd/entrypoint, supported volume mappings, tmpfs, resource limits and lifecycle properties that are actually proven against 1.3.0. It may satisfy coarse filesystem decisions by constructing the container root/view, but it must reject MIR rules whose literal/tree/DENY semantics it cannot reproduce exactly.
Network lowering is explicit:
none: no container network attachment;direct: Apple Container networking, only when the selection policy permits ambient direct access;mediated: no ambient attachment plus a guest bridge to host Egress;- no network rule is implemented merely by proxy environment variables.
apple-container-bubblewrap
This is one composite candidate, not apple-container followed by a second
late choice. As staged here it accepted an OCI TargetSpec whose pinned
Linux/arm64 image contained /usr/bin/bwrap; since 0.19.0 the image supplies
no sandbox binary at all and the candidate uses the Warden-owned guest bundle
described under “Next milestone” below. Missing or unusable Bubblewrap fails
the selected execution; Warden never falls back to the plain candidate.
The outer layer supplies VM/OCI isolation, the guest filesystem and the
route-less network domain. The initial implementation incorrectly attributed
a nested procfs failure to Apple Container 1.3.0 itself. Release 0.27 proves
the actual condition: the outer procfs must be fully visible. The driver sets
maskedPaths: [] and readonlyPaths: [], after which Bubblewrap retains its
user and PID namespaces and mounts a fresh procfs without ambient
CAP_SYS_ADMIN in the outer user namespace. Bubblewrap uses only the
namespace-local setup authority created by --unshare-user; the workload
retains no capabilities.
This remains independent from filesystem rule fidelity: the release lowers
only recursive READ/WRITE rules with ERROR/SKIP missing semantics and does
not claim DENY, PROTECT_CREATE or an inner seccomp layer. Bubblewrap
receives SETUID, SETGID and SETFCAP only for namespace setup and applies
--cap-drop ALL before the workload.
C/Swift boundary
Warden remains pure C. Apple Container and Containerization are Swift. The
production seam is an optional content-pinned
maelys-warden-apple-container-driver with a small versioned local control protocol,
not Swift types in Executor's public ABI.
The driver owns OCI resolution/pull, VM creation, vminitd/vsock calls and Apple
runtime lifecycle. The C backend owns validation, the sealed artifact,
timeouts, descriptor contract, supervision of the driver and mapping errors to
Warden results. The Apple container 1.3.0 CLI can be used for early
conformance experiments, but a production backend must not scrape human text
or infer readiness from process existence.
The MWDP/1 frame protocol (frozen in 0.20.0, previously MWAC/1), Apple
launch payload v5, driver SHA-256 pinning and the host lifecycle are
implemented in src/warden_driver_protocol.c,
src/backend_apple_container.c and
driver/apple-container. The pinned OCI digest is verified before READY.
The first live runtime probe is cached as immutable backend evidence. The
target-specific /usr/bin/bwrap check described here was the bootstrapping
stage only; it was replaced in 0.19.0 by the attested Warden guest bundle.
Secrets never appear in argv. See docs/apple-driver-protocol.md.
The plain backend intentionally does not advertise
PROCESS_TREE_LIFECYCLE yet. stop is protocol-driven and bounded, but a
host-side SIGKILL of an unresponsive driver does not itself prove that the
Apple Container service deleted the VM domain. Plans requiring that stronger
guarantee are rejected by capability selection. VirtioFS sources are
canonicalized and device/inode-pinned at prepare, then rechecked before spawn;
the residual race after that check assumes host-trusted source ancestors until
Apple exposes an fd-based mount API.
Network mediation across the VM
SCM_RIGHTS works only between Unix processes in the same kernel; it cannot
cross the Apple VM boundary. Egress remains the unique host enforcement engine.
For standard clients, Containerization 1.3.0 translates a host AF_UNIX mount
into its UDS/vsock relay. A Warden guest helper exposes one loopback proxy port
inside the otherwise route-less VM. This path is delivered:
- standard client: guest loopback blind L4 relay → Containerization UDS/vsock transport → authenticated host AF_UNIX listener → Egress proxy core;
The Maelys-aware path below is a future design, not an advertised capability:
guest fd-4 broker → connect RPC over authenticated vsock → host Egress native
connector. The guest would create a local socketpair, relay one end over vsock
and transfer the other end to the workload with guest-local SCM_RIGHTS.
The vsock identity, VM instance id and Egress invocation id are bound in the
sealed artifact and receipt. A guest cannot present another execution's
credential. network:none starts neither Egress nor a guest bridge.
Receipt v2
Receipt v2 is a clean schema break. It records the selected deployment and the selected backend/composite identity rather than only a legacy backend string:
{
"schema": "maelys.execution-receipt/v2",
"invocationId": "build-42",
"digests": {
"mir": "sha256:...",
"plan": "sha256:...",
"runSpec": "sha256:...",
"artifact": "sha256:...",
"target": "sha256:...",
"evidence": "sha256:..."
},
"target": {
"kind": "oci",
"reference": "ghcr.io/acme/builder",
"imageDigest": "sha256:...",
"platform": "linux/arm64",
"workingDirectory": "/workspace"
},
"selection": {
"candidate": "apple-container-bubblewrap",
"candidates": [
{"name": "apple-container-bubblewrap", "state": "eligible", "reason": "eligible", "diagnostic": "eligible"},
{"name": "apple-container", "state": "rejected", "reason": "capabilities", "diagnostic": "candidate lacks required filesystem semantics"}
]
},
"layers": [
{
"name": "apple-container-bubblewrap",
"version": "apple-container-bubblewrap-1.3.0",
"capabilities": 16607,
"artifactDigest": "sha256:..."
}
],
"network": {
"mode": "mediated",
"frontend": "egress-proxy",
"mediator": "maelys-egress/proxy-v1"
},
"startedUnixMs": 0,
"lifecycle": {
"forcedStopAttempted": false,
"domainCompleted": true,
"outcomeKnown": true
},
"outcome": {"exitCode": 0, "termSignal": 0}
}The normative machine-readable form and a complete conforming sample live in
docs/schemas/execution-receipt-v2.schema.json and
docs/examples/execution-receipt-v2.json.
The receipt excludes credentials, private socket/vsock tokens, host temporary
paths and raw environment. Rejection reasons are stable codes plus optional
diagnostic text. In 0.18.0 the selected composite is one backend identity and
therefore one layers entry; the receipt does not pretend that the inner
Bubblewrap was independently selected. The artifact digest covers candidate
identity, target/image digest, compiled plan, network frontend and
probe-evidence digest.
Receipt v2 is frozen with that meaning: it contains exactly one layers item,
layers[0].name equals selection.candidate, and its integer capability mask
is interpreted against Executor ABI 7. A future ordered mechanism chain is a
new receipt schema, not a semantic rewrite of v2.
Implementation phases and merge gates
- R1 — RunSpec/TargetSpec. Add opaque builders, canonical encoding and digest tests. Gate: local/OCI round trips, malformed image/platform/path inputs rejected, C++ header and ABI checks.
- R2 — planner API. Add requirements, candidate reports, deterministic scoring and explicit selection policy. Gate: permutation tests yield the same winner and complete rejection reasons.
- R3 — artifact v2. Bind candidate, target and probe evidence; spawn never reselects. Gate: runtime/helper/image identity changes fail closed.
- A1 — Apple 1.3.0 probe. Verify macOS 26+, arm64, canonical executable, safe permissions, pinned SHA-256 identity, exact version and virtualization readiness. Gate: missing/older/newer unapproved runtime is rejected with no fallback.
- A2 — optional driver protocol. Implement bounded ready/spawn/signal/ wait/stop/release and exact error mapping. Gate: crash, hang, malformed frame, fd leak and cancellation tests.
- A3 —
apple-container. OCI digest pinning, platform, cwd/entrypoint, mounts/tmpfs, resource and lifecycle lowering. Gate: image-tag mutation cannot change a prepared artifact;network:nonehas no route. - A4 — guest bootstrap. Signed/pinned guest helper, readiness-before- workload, signal/exit fidelity, bounded cleanup. Gate: workload never execs after partial setup.
- A5 —
apple-container-bubblewrap. Delivered for recursive read/write MIR rules. Release 0.27 adds a physically proven fresh procfs in the nested PID namespace. DENY, PROTECT_CREATE and inner seccomp remain explicit future work because they are separate enforcement semantics. - A6 — Egress/vsock. Delivered for the standard proxy frontend through
Containerization's UDS/vsock transport, with no ambient fallback. The
Maelys-aware fd4 guest broker remains future work because
SCM_RIGHTScannot cross the VM boundary. - R4 — receipt v2. The C RunSpec path, JSON schema and candidate
diagnostics are delivered. OCI options for
maelys-warden runand the Python/Node SDKs are deferred; 0.18.0 publishes no--target-ocioption. - Release gate. Physical Apple-Silicon macOS-26 tests with Apple Container 1.3.0, cold/warm runs, ASan/UBSan for C host code, TSan for planner/adapter, signed artifacts and no virtualization claim based solely on hosted CI.
Capability claims follow delivered behavior, never the roadmap. Physical gates
are test-apple-container-e2e, test-apple-container-egress-e2e,
test-apple-container-bubblewrap-e2e and test-apple-container-cli-e2e.
Next milestone: multiplatform OCI and Warden-owned guest TCB
This milestone replaces the image-owned /usr/bin/bwrap assumption and turns
the latent linux/amd64 path into an explicit, fail-closed Warden guarantee.
Nothing in this section is a claim about 0.18.0 until every physical gate below
passes.
Fixed architecture
The OCI target platform describes the workload, not Warden's trusted components. On Apple Silicon both supported target variants use an arm64 guest kernel and the same static arm64 helper bundle:
Mac Apple Silicon
└── Warden arm64
└── Apple Container driver arm64
└── VM Linux with arm64 kernel
└── static arm64 Bubblewrap
├── creates namespaces and mounts
├── applies confinement natively
└── execve(amd64 workload)
│
▼
Linux detects amd64 ELF
│
▼
binfmt_misc (CF)
│
▼
Rosetta for Linux
│
▼
translated amd64 workloadThe F binfmt flag is load-bearing: the kernel opens the interpreter when the
format is registered, so it remains available across the mount namespaces and
root changes created by Bubblewrap. The release test must inspect the live
guest entry instead of assuming Containerization keeps this implementation
detail forever.
MP1 — Platform-aware candidate contract
Implementation: complete.
Target compatibility becomes a first-class planner predicate, evaluated
separately from MIR capability bits. Do not add misleading capabilities named
AMD64 or ROSETTA.
The backend contract gains a target-aware probe that consumes a borrowed
opaque TargetSpec and returns sealed target evidence. Because the backend
vtable is public and checked by strict ABI equality, adding this callback is an
explicit Executor ABI 8 revision, not a tail read hidden behind ABI 7.
All in-tree backends are migrated in the same change:
- POSIX, Seatbelt and host Bubblewrap accept only compatible local targets;
- Apple candidates accept
linux/arm64asnative; - Apple candidates accept
linux/amd64only astranslatedand only when the Rosetta probe is positive; - future Krun/crun candidates can use the same seam without contaminating MIR.
The planner order becomes:
target kind
-> target platform and execution mode
-> SelectionPolicy allowlist / VM requirement
-> sealed DeploymentContext evidence
-> live generic probe
-> live target-aware probe
-> exact MIR capabilities
-> deterministic scoreAdd a stable target-platform rejection code. Candidate reports name the
requested platform and one of: unsupported, native unavailable, translation
unsupported, translation not installed, or target evidence changed. The
selected artifact binds the target-evidence digest. Spawn never probes another
candidate or falls back to another image variant.
Merge gate: permutation tests keep the same winner; an arm64-only fake backend
is rejected for linux/amd64 before prepare; target evidence mutation between
planning and spawn fails without reselection.
MP2 — Explicit Rosetta proof in the Apple driver
Implementation: complete.
Extend the host driver protocol proof so HELLO_OK/probe evidence reports, in
machine-readable form:
- host architecture and exact Apple Container version;
linux/arm64 = nativeavailability;linux/amd64 = rosettaavailability;- a non-empty canonical translation implementation identity used in the
evidence digest. It is derived from an Apple-exposed version or signed
system-package/OS build identity, never a generic
observedmarker.
For linux/amd64, the driver sets the requested architecture and Rosetta mode
explicitly. It must not rely solely on the current implicit behavior of
Utility.containerConfigFromFlags. On macOS 26, notInstalled is a hard
pre-spawn error with installation instructions; Warden never triggers the
interactive Rosetta installer. On systems where translation is integrated,
the driver still reports the observed availability rather than inferring it
from the OS version.
The OCI resolver must prove that the pinned digest has the requested platform
variant. linux/amd64 never selects an arm64 manifest and linux/arm64 never
selects an amd64 manifest.
Merge gate: mocked driver tests cover installed, not-installed, unsupported and malformed proofs; a driver hardcoded to arm64 can no longer claim amd64.
MP3 — Reproducible static Linux/arm64 guest bundle
Implementation: complete; the bundle also contains the pinned amd64 probe.
Build and package Warden-owned guest helpers under a reserved immutable path:
libexec/maelys-warden/guest/linux-arm64/
├── bwrap
├── maelys-guest-supervisor
├── probe-amd64
└── manifest.jsonBoth executables must be static Linux/arm64 ELF files. The build pins Bubblewrap source, compiler/sysroot and every dependency; the package manifest records version, architecture, size, mode and SHA-256. Packaging rejects dynamic ELF, wrong architecture, unsafe owner/mode, symlinks and a digest mismatch. Release provenance covers the helper bundle alongside the host driver.
The relay remains outside the inner Bubblewrap but inside the route-less guest. It is one private process per mediated execution. The bwrap and relay files are shared read-only package artifacts; their processes and state are never shared between executions.
Merge gate: reproducible double-build digest, ELF/static checks on a native Linux arm64 runner, tamper tests and packaging/install/uninstall tests.
MP4 — Inject and attest Bubblewrap
Implementation: complete using a trusted tmpfs plus a single read-only bundle mount appended after user mappings and rehashed immediately before mount.
The driver mounts the trusted bundle read-only on a reserved guest path after all user mounts, preventing a requested mapping from masking it. Ordering is not sufficient: the OCI root is untrusted and any ancestor of the apparent destination may be a symlink, magic link or mount trap.
The mount point is therefore created from a trusted guest root descriptor, not
resolved by pathname through the image. The guest bootstrap walks or creates
every component with openat2(RESOLVE_BENEATH | RESOLVE_NO_SYMLINKS | RESOLVE_NO_MAGICLINKS) or an equivalent openat(O_NOFOLLOW) component walk,
pins device/inode identity, and performs the bind/move through the verified
object. A trusted tmpfs mounted over the reserved subtree is also acceptable.
If Containerization cannot provide a construction with equivalent guarantees,
the composite is unavailable. It must never fall back to a normal path mount.
That verifier is part of the guest TCB before the OCI root is admitted. It is a Warden-owned, content-pinned bootstrap delivered and attested with the driver/guest bundle from A4; it is launched by the trusted guest init path and must not be loaded from the image, the user mount set or the bundle it is about to verify. Its identity is included in probe evidence. If Containerization cannot place and execute that bootstrap outside the untrusted image namespace, the composite candidate is unavailable.
The driver then invokes the verified bwrap object instead of
/usr/bin/bwrap; the image no longer supplies a trusted sandbox binary.
Before admitting a workload, a bounded guest preflight proves the exact
namespace and mount operations Warden announces. For an amd64 target the same
preflight also executes a minimal pinned amd64 ELF through binfmt/Rosetta from
inside the constructed Bubblewrap namespace. Readiness failure prevents the
workload execve.
Once helper identity and guest conformance are attributable to the candidate,
maelys_executor_create_default() may register both apple-container and
apple-container-bubblewrap. Until then, explicit composite registration
remains the safe behavior.
Merge gate: image without /usr/bin/bwrap succeeds using the injected helper;
a user mount cannot mask the helper; an image whose reserved-path ancestor is a
symlink is rejected; an image-provided fake bootstrap is never executed;
missing/tampered bootstrap or helper and failed guest preflight reject before
workload start.
MP5 — CLI and high-level façade use TrustedRunSpec
Implementation: complete for the CLI, C façade, Python and Node SDKs.
First migrate local CLI executions to TrustedRunSpec without changing their
syntax. Then add the explicit OCI target surface:
--image REFERENCE@sha256:DIGEST mandatory for OCI
--platform linux/arm64|linux/amd64 inferred only from one proven native platform
--guest-workspace /ABSOLUTE/PATH default /workspace
--guest-cwd /ABSOLUTE/PATH default guest workspace
--backend NAME exact optional candidate constraint
--receipt-schema v1|v3 explicit output contract--workspace remains the trusted host source. For OCI it is mapped to
--guest-workspace; --ro/--rw decide its portable access semantics. Mount
mappings remain mechanical SandboxPlan/ExecutionRequest data and do not enter
MIR. The command after -- is the exact guest argv and must be non-empty.
In the first OCI CLI release, relative --allow-read/--allow-write paths are
resolved below the trusted host workspace and mapped to the corresponding
relative path below --guest-workspace. Absolute allow paths and user JSON/MIR
filesystem rules with an absolute host root are rejected; trusted symbolic
runtime and temporary roots remain separate compiler inputs. There is
deliberately no implicit host-absolute to guest-absolute mapping. A later explicit
--mount-ro HOST:GUEST / --mount-rw HOST:GUEST contract may add that power;
it is not part of MP5. Low-level C integrators continue to express reviewed
mechanical mappings directly in the Executor plan.
Platform omission is accepted only when the sealed DeploymentContext observes
exactly one native target platform. This yields linux/arm64 for the current
Apple-Silicon backend. If several native platforms or only translated
platforms are eligible, the CLI requires --platform; it never guesses from
an image tag or silently chooses emulation.
For Apple targets, mediated auto selects proxy; explicit fd4 and
isolated-duplex are rejected until their VM transports exist. --backend
constrains selection to one candidate and never means try-then-fallback.
Merge gate: parser matrix covers local/OCI option conflicts, immutable digest, default and explicit platforms, guest path normalization, exact argv, backend pinning, absolute-path rejection, receipt schema selection and every forbidden fd3/fd4 combination. CLI failure prints the full candidate report without exposing secrets.
MP6 — Receipt v3 attests native versus translated execution
Implementation: complete. The v2 schema and golden example remain frozen.
Do not reinterpret receipt v2. ABI 8 introduces
maelys.execution-receipt/v3, with an explicit Executor ABI, explicit
candidate capabilities and an ordered outermost-to-innermost mechanism chain:
{
"schema": "maelys.execution-receipt/v3",
"executor": {
"abi": 8,
"capabilityRegistry": "maelys.executor-capabilities/8"
},
"target": {"kind": "oci", "platform": "linux/amd64"},
"selection": {
"candidate": "apple-container-bubblewrap",
"capabilities": {
"required": {
"mask": "0x000000000000001f",
"names": ["execution", "filesystem", "network-none",
"process-confinement", "fs-access-rules"]
},
"effective": {
"mask": "0x00000000000040df",
"names": ["execution", "filesystem", "network-none",
"process-confinement", "fs-access-rules",
"fs-view-mapping", "fs-tmpfs",
"network-mediated-proxy"]
}
}
},
"layers": [
{
"name": "apple-container",
"version": "1.3.0",
"platform": {"mode": "native", "execution": "linux/arm64"},
"capabilityContribution": {
"mask": "0x00000000000040cf",
"names": ["execution", "filesystem", "network-none",
"process-confinement", "fs-view-mapping", "fs-tmpfs",
"network-mediated-proxy"]
}
},
{
"name": "bubblewrap",
"version": "warden-static-linux-arm64/<sha256>",
"platform": {"mode": "native", "execution": "linux/arm64"},
"capabilityContribution": {
"mask": "0x0000000000000010",
"names": ["fs-access-rules"]
}
},
{
"name": "apple-rosetta-linux",
"version": "<translationIdentity>",
"platform": {
"mode": "translated",
"source": "linux/amd64",
"destination": "linux/arm64"
},
"capabilityContribution": {
"mask": "0x0000000000000000",
"names": []
}
}
]
}For every capability object, mask is normative under the declared
capabilityRegistry. It is encoded as the canonical lowercase, zero-padded
64-bit hexadecimal string 0x[0-9a-f]{16}, never as a JSON number; this avoids
the JavaScript 53-bit integer limit. names is its required canonical
expansion, ordered by ascending bit number with no duplicates. A missing name,
an extra name, an unknown set bit or any mask/name disagreement makes the
receipt invalid. This keeps machine evaluation exact while preserving a stable
human-readable audit surface.
selection.capabilities.effective is the capability set against which
eligibility was decided, and required.mask must be a subset of
effective.mask. A layer records only its independently attributable
capabilityContribution, which must itself be a subset of the effective set.
The union of layer contributions is not a schema invariant: some guarantees
can arise only from composition or from a cross-layer construction. Auditors
must therefore use the explicit candidate-level effective set, never infer it
by OR-ing the layer masks. The example happens to have an equal union; that is
not a portable property.
Layer platforms are structured rather than overloaded strings. A native layer
uses {"mode":"native","execution": PLATFORM}. A translation layer uses
{"mode":"translated","source": PLATFORM,"destination": PLATFORM},
where source is the workload platform and destination is the native guest
execution platform. PLATFORM uses the canonical lowercase TargetSpec grammar
os/architecture[/variant]; ABI 8 initially recognizes linux/arm64 and
linux/amd64. Values such as linux/amd64-on-linux/arm64 are never accepted as
a platform token.
The fragment omits the existing digest, lifecycle and outcome members only for
readability. <translationIdentity> is a non-empty canonical value returned by
the trusted driver proof, never the placeholder observed. It names the
translation provider and an Apple-exposed version or signed system-package/OS
build identity. If Warden cannot obtain a stable identity, the translated
candidate is not attestable and is rejected.
The evidence digest covers driver identity, Apple runtime proof, guest helper manifest, live binfmt entry and that translation identity. Native arm64 receipts omit the Rosetta layer. No Rosetta installation path, private mount path or Egress credential is recorded.
Local CLI compatibility is explicit: --receipt FILE continues to emit v1
after the internal RunSpec migration unless the caller requests
--receipt-schema v3. New OCI commands emit v3 and reject
--receipt-schema v1; they never silently emit v2. Receipt v2 remains the
frozen ABI 7 C RunSpec format.
Merge gate: schema tests validate native/simple, native/composite, translated/simple and translated/composite v3 receipts; mask/name disagreement, unknown bits, required-not-subset-of-effective and malformed platform variants are rejected; v2 golden files remain byte-for-byte stable; artifact or layer order changes alter the v3 digest.
MP7 — Physical conformance matrix
Implementation: complete on Apple Silicon with Apple Container 1.3.0 and Rosetta installed; the Make targets remain outside portable CI.
The feature remains non-advertised until an Apple-Silicon machine runs all of:
| Target | Candidate | Network | Required result |
|---|---|---|---|
| arm64 | Apple simple | none | native success, no Rosetta layer |
| amd64 | Apple simple | none | translated success |
| arm64 | Apple + bwrap | none | injected bwrap, direct network denied |
| amd64 | Apple + bwrap | none | arm64 bwrap then translated workload |
| amd64 | Apple + bwrap | mediated proxy | standard HTTP and SOCKS via Egress |
Negative gates cover missing Rosetta, missing amd64 manifest, tampered helper, wrong helper architecture, direct egress in mediated mode, another loopback port, user-mount masking, driver crash before readiness and forced lifecycle escalation. Each failure proves that no workload ran and no VM, relay, Egress server, socket or private directory remains.
test-apple-container-cli-e2e closes the product-surface gap: it runs both
platforms through maelys-warden run, writes the real receipt v3 JSON and
validates its candidate and ordered layers. It also exercises the OCI defaults
for an empty environment and bounded cold-start timeout.
Run make check, clang/GCC builds, ASan+UBSan and TSan for host code. The
virtualization guarantees require physical tests; Docker and mocked-driver
coverage are complementary, never substitutes.
MP8 — Documentation, SDKs and release
Implementation: documentation, SDK and packaging work is complete. Release publication remains a separate maintainer-authorized operation.
After the C/CLI contract is green, expose the same explicit fields in Python and Node: image reference plus digest, platform, guest workspace/cwd and exact backend/receipt-schema constraint. SDKs must not invent image resolution, auto-install Rosetta or silently rewrite a local command as OCI.
Update tutorials with one arm64 and one amd64 example, publish helper checksums and provenance, and change the capability matrix from “latent/non-attested” to “supported” only in the release commit that includes the physical evidence.
Definition of done: both Apple candidates are auto-discoverable from trusted installed artifacts, the planner rejects unsupported platforms before prepare, amd64 execution is visibly attested in receipt v3, and every post-prepare failure remains fail-closed without candidate or platform fallback.
Expected implementation touchpoints
| Area | Primary files | Change |
|---|---|---|
| ABI and opaque target evidence | include/maelys/executor.h, version generator | ABI 8 target-aware probe, borrowed TargetSpec, stable rejection code/getters |
| planner and sealed artifact | src/internal.h, src/planner.c, src/runspec.c, src/executor.c | platform predicate, evidence digest, ordered receipt v3 layers, no reselection |
| Apple C backend | src/backend_apple_container.c, src/warden_driver_protocol.c | driver proof parsing, platform checks, helper identity and target evidence |
| Swift driver | driver/apple-container/Sources/MaelysAppleContainerDriver/main.swift | explicit arch/Rosetta request, availability proof, helper injection and guest preflight |
| guest TCB | helpers/maelys-guest-supervisor.c, new pinned Bubblewrap build recipe | static arm64 relay/bwrap bundle and runtime probes |
| packaging | scripts/build-warden-guest-helpers.sh, scripts/package-apple-container-driver.sh | reproducible build, manifest, ELF/digest/mode validation |
| product façade and CLI | include/maelys/warden.h, Warden façade sources, cli/maelys-warden.c | local RunSpec migration, OCI setters/parser, candidate diagnostics, v1 compatibility and receipt v3 |
| tests | tests/test_executor.c, Apple mocked and physical E2E tests | platform matrix, Rosetta failures, bwrap injection, Egress and cleanup |
| SDKs and docs | sdk/python, sdk/node, docs/cli-reference.md, tutorials | expose only the final C/CLI contract after gates pass |