Expand description
Backend capability negotiation and preflight for Cageforge execution.
This crate is the typed boundary between cageforge_command and
cageforge_policy_compose values and a native execution backend. It does
not implement native filesystem or network I/O, resolve DNS, or select an
operating-system sandbox. Sandbox and DynSandbox delegate execution
to the selected native backend while SandboxChild exposes its lifecycle.
Start with BackendRequest and BackendCapabilities. A native backend
implements SandboxBackend, advertises the capabilities it can enforce,
and calls BackendRequest::prepare_for before lowering the prepared
request to its operating-system API. The backend owns process launch and
lifecycle after this preflight boundary.
⚠️ Independent project
Cageforge is not affiliated with, sponsored by, or endorsed by OpenAI. This crate adapts sandbox boundary ideas from open-source OpenAI Codex into an independent library API and contains no copied Codex source.
This crate is a supporting component of the cageforge crate, a cross-platform Rust sandbox for AI agents and untrusted code.
§cageforge-backend-api
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 facade. 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/launchBackendRequest 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 cageforge_backend_api::{
BackendCapabilities, BackendRequest, SandboxBackend,
};
struct ExampleBackend {
capabilities: BackendCapabilities,
identity: cageforge_backend_api::BackendIdentity,
}
impl ExampleBackend {
fn new(capabilities: BackendCapabilities) -> Self {
Self {
capabilities,
identity: cageforge_backend_api::BackendIdentity::new(),
}
}
}
impl SandboxBackend for ExampleBackend {
fn identity(&self) -> &cageforge_backend_api::BackendIdentity {
&self.identity
}
fn capabilities(&self) -> BackendCapabilities {
self.capabilities.clone()
}
}
// 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.
cageforge provides the ergonomic facade 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.
Structs§
- Backend
Capabilities - The capabilities advertised by one backend.
- Backend
Identity - Identity of one backend enforcement instance.
- Backend
Request - A portable command and effective policy submitted for backend preflight.
- Prepared
Backend Request - A request that passed backend capability preflight.
Enums§
- Backend
Capability - One capability that a native backend may advertise.
- Backend
Contract Error - Common failures at the portable backend contract boundary.
- Sandbox
Execution Error - A failed dynamic execution operation, retaining its concrete native cause.
Traits§
- DynSandbox
- Execution through a reusable backend without naming its native child or error type.
- Sandbox
- Common preparation and process-launch contract for native Cageforge backends.
- Sandbox
Backend - The capability-discovery contract implemented by a native backend.
- Sandbox
Child - Common lifecycle operations for one native sandbox instance.