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:

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

CODE
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 order

TargetSpec 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: local or oci, 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:

  1. 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.
  2. Enumerate compatible composites. Candidate names are stable public identities; a composite is not inferred from two independently selected backends.
  3. 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.
  4. 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:

CODE
{
  "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

  1. 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.
  2. R2 — planner API. Add requirements, candidate reports, deterministic scoring and explicit selection policy. Gate: permutation tests yield the same winner and complete rejection reasons.
  3. R3 — artifact v2. Bind candidate, target and probe evidence; spawn never reselects. Gate: runtime/helper/image identity changes fail closed.
  4. 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.
  5. 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.
  6. 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:none has no route.
  7. A4 — guest bootstrap. Signed/pinned guest helper, readiness-before- workload, signal/exit fidelity, bounded cleanup. Gate: workload never execs after partial setup.
  8. 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.
  9. 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_RIGHTS cannot cross the VM boundary.
  10. R4 — receipt v2. The C RunSpec path, JSON schema and candidate diagnostics are delivered. OCI options for maelys-warden run and the Python/Node SDKs are deferred; 0.18.0 publishes no --target-oci option.
  11. 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:

CODE
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 workload

The 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/arm64 as native;
  • Apple candidates accept linux/amd64 only as translated and only when the Rosetta probe is positive;
  • future Krun/crun candidates can use the same seam without contaminating MIR.

The planner order becomes:

CODE
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 score

Add 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 = native availability;
  • linux/amd64 = rosetta availability;
  • 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 observed marker.

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:

CODE
libexec/maelys-warden/guest/linux-arm64/
├── bwrap
├── maelys-guest-supervisor
├── probe-amd64
└── manifest.json

Both 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:

CODE
--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:

CODE
{
  "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:

TargetCandidateNetworkRequired result
arm64Apple simplenonenative success, no Rosetta layer
amd64Apple simplenonetranslated success
arm64Apple + bwrapnoneinjected bwrap, direct network denied
amd64Apple + bwrapnonearm64 bwrap then translated workload
amd64Apple + bwrapmediated proxystandard 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

AreaPrimary filesChange
ABI and opaque target evidenceinclude/maelys/executor.h, version generatorABI 8 target-aware probe, borrowed TargetSpec, stable rejection code/getters
planner and sealed artifactsrc/internal.h, src/planner.c, src/runspec.c, src/executor.cplatform predicate, evidence digest, ordered receipt v3 layers, no reselection
Apple C backendsrc/backend_apple_container.c, src/warden_driver_protocol.cdriver proof parsing, platform checks, helper identity and target evidence
Swift driverdriver/apple-container/Sources/MaelysAppleContainerDriver/main.swiftexplicit arch/Rosetta request, availability proof, helper injection and guest preflight
guest TCBhelpers/maelys-guest-supervisor.c, new pinned Bubblewrap build recipestatic arm64 relay/bwrap bundle and runtime probes
packagingscripts/build-warden-guest-helpers.sh, scripts/package-apple-container-driver.shreproducible build, manifest, ELF/digest/mode validation
product façade and CLIinclude/maelys/warden.h, Warden façade sources, cli/maelys-warden.clocal RunSpec migration, OCI setters/parser, candidate diagnostics, v1 compatibility and receipt v3
teststests/test_executor.c, Apple mocked and physical E2E testsplatform matrix, Rosetta failures, bwrap injection, Egress and cleanup
SDKs and docssdk/python, sdk/node, docs/cli-reference.md, tutorialsexpose only the final C/CLI contract after gates pass