Independent project: Cageforge is not affiliated with, sponsored by, or endorsed by OpenAI.
This crate is a supporting component of the cageforge crate, a cross-platform Rust sandbox for AI agents and untrusted code.
cageforge-backend-api
Read the shared configuration guide for TOML profiles, symbolic paths, local IPC, and first-launch resource rules.
cageforge-backend-api is the contract layer between Cageforge's portable
execution values and a native Linux, macOS, or Windows backend. It defines the
typed handoff from a composed command and sandbox policy to the operating-
system integration that will enforce and launch them.
The crate turns a backend's declared enforcement capabilities into a common
preflight contract. BackendRequest::prepare_for accepts a request built from
CommandRequest and EffectiveSandbox, verifies every required capability,
and returns a prepared request or a typed error that identifies the failed
requirement.
The handoff model
Sandbox and SandboxChild define shared preparation, launch, streams, and
child lifecycle operations. Each native crate implements these traits
directly; applications can use them through this crate or through the
cageforge crate. The concrete child and error types remain available.
DynSandbox supports a runtime-selected implementation behind
Box<dyn DynSandbox> or a shared Arc<dyn DynSandbox>. Its launch method
prepares and spawns on the same backend instance. It returns an owned child
with the common SandboxChild interface. SandboxExecutionError preserves
the failing operation and original native error; child destruction follows
the same native cleanup path as a concrete child.
CommandRequest + EffectiveSandbox
│
▼
cageforge-backend-api
capabilities + preflight
│
▼
native backend lowering/launch
BackendRequest accepts CommandRequest and EffectiveSandbox, so policy
composition and the PolicyCeiling intersection are completed before the
backend handoff. The command's EnvironmentSpec is checked against the
requested environment retained by the composed result, keeping command and
policy construction aligned.
Call request.prepare_for(&backend, &base_context) to run the common
capability and working-directory checks. base_context must include the
runtime current directory: it is checked even when the command does not set an
explicit cwd, so a child cannot silently inherit an unchecked directory from
the launching process. The backend trait only supplies its capabilities; it
cannot override this preflight with a broader set. The base context is
narrowed to the effective workspace ceiling, and an effective working
directory is rejected when the filesystem policy denies it. After preparation,
use PreparedBackendRequest::command_spec for the executable and argv values,
PreparedBackendRequest::working_directory for the resolved cwd,
PreparedBackendRequest::path_context to inspect the already narrowed
context, and PreparedBackendRequest::apply_environment with a
backend-selected EnvironmentInput. The filesystem decision helpers use that
same bound context and cannot be given a context from another request. A
symbolic selector is never evaluated without it. Use filesystem_lowering and
network_lowering to obtain every immutable constraint layer needed by native
lowering. Every returned layer is mandatory: the backend must enforce their
conjunction and must not lower only the requested or only the ceiling side.
Use authorize_connection to receive a decision that already combines the
requested and ceiling sides. Every prepared accessor takes &backend and
checks that it is the same instance used by prepare_for.
The returned PreparedBackendRequest<'_, B> is bound to the concrete backend
type B whose capabilities were checked. A native lowering method should
accept PreparedBackendRequest<'_, Self> so a handoff prepared for one backend
implementation cannot be passed to another backend implementation by accident.
It also carries a runtime BackendIdentity; every prepared accessor must be
called with the same backend instance that was passed to prepare_for, or it
returns BackendContractError::BackendIdentityMismatch. This prevents two
instances of one backend type with different enforcement state from sharing a
prepared handoff. The identity is a caller-managed token, not proof that
operating-system enforcement exists. Prepared accessors also compare the
capability snapshot captured during preflight and return
BackendContractError::BackendCapabilitiesMismatch if the backend changes
capabilities afterwards. The backend remains responsible for advertising only
capabilities it can actually enforce.
Capability names describe portable enforcement requirements, not a shared operating-system mechanism. A Linux backend may advertise conventional temporary-scope or pathname local-IPC enforcement while a Windows backend may omit those capabilities and return the same typed unsupported-capability error before lowering. Native path, object, and process mechanisms stay in each backend.
Create BackendIdentity explicitly with BackendIdentity::new(). It has no
Default implementation because each new identity represents a distinct
backend boundary; cloning preserves an existing identity.
When a request needs an unsupported capability, preparation returns
BackendContractError::UnsupportedCapability. The error is matchable by its
BackendCapability variant and its display text names the required
enforcement, for example: filesystem missing-path behavior (error or skip).
Capability checks include implicit requirements: a workspace-relative glob
needs FilesystemScopes so it is evaluated against the narrowed workspace
context, and every deny glob needs FilesystemGlobScanDepth because an absent
explicit depth means unbounded scanning. Concrete scopes also require the
matching selector capability: absolute, workspace, system-root, minimal,
temporary-directory, or the platform's conventional temporary scope. A
backend that cannot enforce any required behavior is rejected before lowering.
use ;
// A real backend constructs its capabilities from the enforcement mechanisms
// it can prove safe, then runs the common preflight before lowering the
// request to native process and filesystem APIs.
Responsibilities
The crate owns:
BackendCapabilityandBackendCapabilities;BackendRequestand the opaquePreparedBackendRequest;- the synchronous
SandboxBackendcapability contract and common preflight; Sandbox,DynSandbox, andSandboxChildexecution contracts;- common unsupported-capability and preparation errors.
The native backend owns:
- translating the effective contract to Landlock, bubblewrap, Seatbelt, Windows ACL/token, or another OS mechanism;
- symlink, junction, reparse-point, mount, and TOCTOU-safe enforcement;
- DNS resolution and exact
SocketAddrconnection authorization; - platform-specific core environment selection; and
- process launch, stdio, timeout, cancellation, and lifecycle handling.
The native backend also owns classification of its typed launch errors. The
shared BackendDiagnosticMetadata and BackendDiagnostic contract is defined
here; each target backend supplies its own mapping, while the cageforge
facade only dispatches to the backend selected by target_os. This metadata
helps adapters present stable categories without replacing the original typed
error.
cageforge provides the ergonomic library API and target-specific backend
selection that connect this contract to a concrete execution flow.
Workspace role
| Crate | Role in the relationship |
|---|---|
cageforge-command |
Supplies validated command and environment intent. |
cageforge-policy-compose |
Supplies the narrowed EffectiveSandbox. |
cageforge-policy |
Supplies portable policy values and decision types used during lowering. |
cageforge-path |
Supplies the shared lexical path semantics used by policy and native integrations. |
cageforge-config |
Produces validated TOML-backed command and policy values for the handoff. |
cageforge |
Re-exports this contract and exposes the common Sandbox launch shape. |
cageforge-linux, cageforge-macos, cageforge-windows |
Implement OS enforcement and process launch after preflight. |
The complete API is documented on docs.rs.