Extend and embed

Sandbox Policy adapter

Map canonical MIR into Executor plans without sharing private representations or weakening capabilities.

The two plan types intentionally describe different stages and therefore have different names in Executor ABI 9:

CODE
canonical MIR + trusted HostContext
                 |
                 v
      maelys_sandbox_policy_plan_t
       resolved policy decision
                 |
       Warden Policy adapter
                 |
                 v
       maelys_executor_plan_t
       mechanical launch contract
                 |
        backend prepare/spawn

libmaelys-sandbox-policy owns maelys_sandbox_policy_plan_t. It resolves symbolic MIR roots and carries the MIR digest and trusted mediator identifier. libmaelys-executor owns maelys_executor_plan_t. It adds backend-facing mount vocabulary, exact missing-path behaviour and the capability set used for selection. Neither representation is cast, aliased or passed to the other's functions.

The optional libmaelys-warden-policy-adapter is the only component that includes both public headers. It copies every resolved rule and seals a new Executor plan. The source plan remains owned by its caller. The adapter copies the canonical MIR digest into Executor's generic decision_digest; the Executor plan digest then binds this source identity to the mechanical rules, and both digests are available in execution receipts.

The current adapter resolves workspace, temp and minimal-runtime against the local host and is therefore the product path for local Seatbelt or Bubblewrap targets. It is not an OCI guest-path compiler. Apple Container integrators must construct a trusted Executor plan whose mount destinations and recursive filesystem rules refer to the intended guest paths; Warden must not reinterpret a host-resolved rule as guest authority silently.

Network mapping

maelys-sandbox-policy decisionExecutor planCurrent status
NONENETWORK_NONEenforced only by a backend claiming strong denial
DIRECTNETWORK_INHERITsupported
MEDIATED + maelys-egress/fd4-v1NETWORK_EXTERNAL_CHANNEL + copied destinationsfd4 broker → Egress native connector
MEDIATED + maelys-egress/proxy-v1NETWORK_MEDIATED_PROXY + copied destinationsEgress adapter + Bubblewrap relay or exact Seatbelt loopback endpoint

NETWORK_IP_NONE has no portable maelys-sandbox-policy equivalent. Bubblewrap satisfies MIR NONE only when both its network namespace and native seccomp socket denial were proven by the backend probe; the namespace-only capability remains distinct. The capability converter advertises NETWORK_MEDIATED when the selected backend claims either exact mediated frontend. The adapter copies every canonical TCP hostname/port destination and the source MIR digest into the Executor plan. It requires the trusted HostContext mediator identifier to equal MAELYS_WARDEN_POLICY_FD4_MEDIATOR_ID or MAELYS_WARDEN_POLICY_EGRESS_MEDIATOR_ID; an arbitrary mediator name is never silently discarded or rebound. This explicit split is adapter ABI 4. Both frontends terminate in Egress; the split is client compatibility, not separate enforcement.

Root mapping

Sandbox Policy ABI 4 carries the MIR v3 root disposition into its opaque SandboxPlan. The adapter maps READ_ONLY to an Executor readonly root and EPHEMERAL_WRITE to MAELYS_EXECUTOR_ROOT_EPHEMERAL_WRITE, which requires the ABI 9 capability bit. Scratch size is deliberately absent from MIR and SandboxPlan: the trusted RunSpec and execution request must agree on that deployment/resource ceiling exactly.

Process-tree mapping

MIR process_tree_required means confinement-policy inheritance by descendants and maps to MAELYS_BACKEND_CAP_PROCESS_CONFINEMENT. It does not set Executor's process_tree_required flag, which deliberately means the stronger lifecycle promise that wait/stop covers an adversarial descendant tree, including a descendant that creates a new session.

Capability handshake

The trusted registry should:

  1. query the selected backend with maelys_executor_backend_capabilities(executor, name, ...);
  2. convert that exact mask with maelys_warden_policy_capabilities_from_executor(...);
  3. pass the result to maelys_sandbox_policy_compile;
  4. lower the returned plan with maelys_warden_plan_from_sandbox_policy;
  5. set the same backend name on the execution request before spawn.

Using the same name in steps 1 and 5 prevents a capability check against one backend followed by enforcement through another. POSIX maps to no portable sandbox capabilities. Seatbelt currently maps READ/WRITE, direct/none network, both mediated frontends and descendant policy inheritance. Bubblewrap maps portable recursive-directory and literal-file READ/WRITE rules to pinned --ro-bind/--bind operations. Explicit DENY is lowered to a final mode-000 read-only mount overlay. Literal directory rules and create-protection for absent paths remain rejected because Bubblewrap mounts cannot represent those narrower vnode semantics exactly.

Build

With sibling checkouts:

CODE
make warden-policy-adapter-check MAELYS_SANDBOX_POLICY_DIR=../maelys-sandbox-policy

This test compiles and links both static libraries in one executable, proving that their public and internal symbols no longer collide.