Protocols
Apple driver protocol
Frames, descriptors, lifecycle, failure ownership and evidence at the C-to-Swift boundary.Status: the common MWDP/1 frame protocol, frozen in 0.20.0, and Apple LAUNCH
payload version 6 are implemented. Release 0.19.0
used the predecessor MWAC/1 magic; 0.20.0 migrated both ends together without
a compatibility alias.
Physical macOS 26 conformance remains a release gate.
Discovery requires the exact proof MAELYS-APPLE-PROBE/6 with
control=mwdp/1, launch=6, resource-contract=abi10-warden-cgroup for the
composite and inner-procfs=private. A driver reporting probe v5 or older is rejected during probe before an
execution can be prepared;
Warden never discovers an old driver and then fails late at LAUNCH.
Warden is C. Apple Container 1.3.0 is a Swift/XPC API. The optional
maelys-warden-apple-container-driver keeps this language and framework boundary out
of libmaelys-executor.
Processes and descriptors
This section describes descriptors in the host Swift driver process. It
does not assign descriptors in the guest workload. See
warden-driver-protocol.md for the three separate
descriptor spaces and the common Apple/Krun envelope.
The control channel is a dedicated Unix stream socket. It is not stdin:
the driver's standard descriptors are forwarded to the guest workload.
SOCK_SEQPACKET is deliberately not required because it is not available for
Unix socket pairs on every supported macOS version. Explicit framing preserves
message boundaries while stream EOF gives the driver a reliable parent-death
signal.
Both ends use FD_CLOEXEC, bounded payloads and deadlines. The child receives
only fd 0, 1, 2 and the control socket mapped to fd 4. Secrets, OCI credentials
and network credentials never appear in argv.
More concretely, Warden creates a private socket pair and installs one end as
fd 4 when it execs the driver. The Swift code reads MWDP/1 frames from
that fd. This is not a socket inherited by the guest and it is not made visible
to the workload.
The native workload convention also calls its optional network control channel "fd 4", but that channel is created by a different spawn and installed in a different process. A descriptor number is only an index into the descriptor table of one process. The two fd 4 values therefore have no identity, lifetime or protocol in common. Neither Apple candidate currently advertises the workload fd-4 frontend.
Frame
Every integer is unsigned big-endian:
offset size field
0 4 ASCII "MWDP"
4 4 protocol version (1)
8 4 message type
12 4 flags (zero in v1)
16 8 request id
24 4 payload size, maximum 1 MiB
28 N payloadThe legal sequence is:
Request identifiers are nonzero and increase strictly for controller
requests. Each response repeats the request identifier it answers; terminal
EXIT repeats the LAUNCH identifier. ACK confirms command admission and
therefore precedes application of a signal or forced stop, preventing an
immediate workload exit from overtaking the response on the stream.
READY means the guest workload was started, not merely that the driver was
spawned. Any error before READY causes Warden to kill/reap the driver and
return spawn failure. There is no reselection or native fallback.
The current LAUNCH payload version is 6, and it is numbered independently of
the Krun driver's own payload 11. It carries the selected candidate,
immutable image reference and digest, platform, guest cwd, argv, normalized
environment, canonical plan digest, the complete sealed ABI 10 execution
budget and its digest, mounts, filesystem decisions, network
destinations and the five attested guest-bundle paths/digests (manifest,
Bubblewrap, maelys-init, maelys-guest-supervisor and amd64 probe).
The plain backend rejects filesystem access rules but accepts view mappings,
tmpfs and the mediated proxy bridge. The composite additionally accepts
recursive READ/WRITE rules with ERROR/SKIP missing semantics. Both reject fd-4
external-channel networking; the composite also rejects DENY, PROTECT_CREATE
and forms that require an inner seccomp layer.
For the composite, payload 6 drives two exact enforcement layers: service VM
sizing and the Warden-owned nested workload cgroup. The private MWAR/2
report returns workload exit, resource observations and verified cleanup. The
simple candidate reports resource-contract=none and remains ineligible. See
the resource decision record.
The driver hashes every guest-bundle object again immediately before mounting
it. It places the bundle on a Warden-reserved read-only mount after all user
mounts and runs a bounded Bubblewrap preflight before READY. For an amd64
target, that preflight executes the pinned amd64 ELF through Rosetta from
inside the final namespace.
Lifecycle and failure ownership
PREPARED -> DRIVER_STARTING -> GUEST_STARTING -> READY -> EXITED
| | |
+------------------+--------------+-> CLEANING
-> CLEANED
-> CLEANUP_FAILED- normal signals are protocol events, not host signals;
- Warden owns deadlines;
- if the driver stops answering, Warden uses host
SIGKILLas the final containment action; - if Warden dies, fd 4 reaches EOF and the driver calls
stopthendelete; - the driver uses Apple Container's
useInit, vminitd supervision and container-domain stop. It does not invent a second guest PID 1; - inner Bubblewrap uses its normal PID namespace reaper.
bwrap --as-pid-1must not be used: in Bubblewrap that option disables installation of the reaper process; - a forced VM stop makes the workload exit result unknown. The receipt records containment completion separately from outcome knowledge.
Protocol stop does not yet prove domain deletion after an unresponsive driver
has to be killed on the host. Consequently the backend does not advertise
PROCESS_TREE_LIFECYCLE; a RunSpec requiring it is rejected rather than
receiving a weaker lifecycle promise.
Host PR_SET_PDEATHSIG is Linux-only and cannot solve a macOS driver/VM
failure. Inside the guest it can be defense in depth, but cgroup/container
domain lifecycle remains authoritative.
Identity and evidence
At registration Warden canonicalizes, stats and SHA-256 hashes the driver. It
rechecks device, inode, permissions and content before probe, prepare and
spawn. The DeploymentContext observation must contain this exact evidence
digest. The OCI target requires a resolved sha256: digest; mutable tags never
decide a prepared artifact. MWDP transports the repository reference and digest
as separate canonical fields; the driver joins them as reference@digest only
when addressing Apple's image store, then rejects any different resolved
digest before container creation.
Apple's APIs used by the driver are pinned through SwiftPM to
apple/container 1.3.0 and apple/containerization 0.41.0. No CLI output is
scraped.