System overview
When your code calls Sandbox.create(), the following sequence happens:
Every subsequent SDK call — running commands, reading files, writing files — goes through the API server to envd running inside the VM. All outbound network traffic from the VM passes through the security proxy before reaching the internet.
Sandboxes
A sandbox is a Firecracker microVM. It is the unit of isolation in Declaw.
Each sandbox has:
- An independent rootfs — a full copy of the base filesystem image. Changes made inside one sandbox are never visible in another.
- An independent process tree — there is no shared PID namespace between sandboxes or between a sandbox and the host.
- An independent network namespace — the sandbox has its own network stack, IP address, routing table, and iptables rules.
- A configurable timeout — the server destroys the sandbox when the timeout expires, even if the client process has crashed.
Sandboxes are ephemeral by design. When you call sbx.kill(), the VM is immediately destroyed and all data inside it is gone. If you need to persist state, write files out via sbx.files.read() before killing, or use snapshots.
Sandbox lifecycle
create() -> running -> (timeout or kill()) -> destroyed
-> pause() -> paused -> (resume) -> running
-> create_snapshot() -> snapshot stored
SandboxInfo fields
| Field | Type | Description |
|---|
sandbox_id | str | Unique identifier for the sandbox |
state | SandboxState | running, paused, stopped |
template_id | str | The base image used to boot this sandbox |
created_at | datetime | When the sandbox was created |
timeout | int | Seconds until automatic destruction |
envd
envd is a lightweight HTTP daemon that runs inside every Firecracker VM on port 49983. It exposes three API groups:
- Filesystem API — read, write, list, watch files inside the VM
- Process API — run commands, stream stdout/stderr, send stdin, kill processes
- PTY API — open interactive terminal sessions
The SDK communicates with envd through the Declaw API server. You never talk to envd directly — the API server proxies requests and handles authentication and routing.
envd starts automatically as part of the VM boot process. The orchestrator waits for envd to become healthy before marking the sandbox as running and returning the sandbox ID to the SDK.
envd traffic between the API server and the VM travels over the private veth pair connecting the host to the network namespace. This traffic is never routed through the security proxy, so envd API calls are not subject to PII scanning or network policies.
Network namespaces
Every sandbox runs inside a dedicated Linux network namespace. This is the mechanism that provides network isolation between sandboxes and between sandboxes and the host.
The structure for a single sandbox looks like this:
Host network stack
|
|- veth0 (host side) <----> veth1 (namespace side)
|
|- TAP device
|
|- Firecracker VM (eth0)
|- Your code
The veth pair bridges the host network stack and the namespace. The TAP device connects the namespace to the VM’s virtual NIC. The VM sees a normal network interface (eth0) and has no awareness that it is inside a namespace.
Traffic routing
Outbound packets from the VM travel: VM eth0 -> TAP -> veth1 -> veth0 -> host. Before they reach the host’s default route, an iptables REDIRECT rule intercepts them and sends them to the security proxy’s listening port. The proxy inspects the packet, applies the sandbox’s policy, and either forwards or drops the connection.
This REDIRECT rule is scoped to the sandbox’s network namespace, so it only affects traffic from that specific sandbox.
Security Proxy
The security proxy is a transparent MITM TLS interceptor. It sits on the network path between every sandbox and the internet.
For HTTP traffic, the proxy reads the Host header to identify the destination.
For HTTPS traffic, the proxy reads the TLS SNI field during the handshake to identify the destination, then terminates the TLS connection, inspects the plaintext request body, applies security policies, and re-encrypts the traffic to forward it. The VM must trust the proxy’s dynamically-generated certificate authority for this to work — the CA is installed in the VM rootfs at build time.
The proxy only activates full TLS interception when a SecurityPolicy that requires it is attached to the sandbox. Specifically, if pii.enabled, injection_defense.enabled, or any transformations are configured, TLS interception is active. Network-only policies (domain allowlists/denylists) are enforced at the TCP layer without decryption.
Security pipeline
When TLS interception is active, each intercepted request passes through this pipeline:
SecurityPolicy
SecurityPolicy is the single object that configures all security controls for a sandbox. It is passed to Sandbox.create() and attached to the sandbox for its entire lifetime.
from declaw import (
SecurityPolicy,
PIIConfig,
InjectionDefenseConfig,
NetworkPolicy,
AuditConfig,
TransformationRule,
TransformDirection,
ALL_TRAFFIC,
)
policy = SecurityPolicy(
# PII scanning and redaction on outbound HTTP bodies
pii=PIIConfig(
enabled=True,
types=["ssn", "credit_card", "email", "phone"],
action="redact", # "redact", "block", or "log_only"
rehydrate_response=True, # restore original values in responses
),
# Prompt injection detection on LLM API calls
injection_defense=InjectionDefenseConfig(
enabled=True,
action="block", # "block" or "audit"
threshold=0.8, # detection confidence threshold
),
# Domain-level network access control
network=NetworkPolicy(
allow_out=["api.openai.com", "pypi.org"],
deny_out=[ALL_TRAFFIC], # block everything not in allow_out
),
# Regex-based request/response body transformations
transformations=[
TransformationRule(
pattern=r"sk-[A-Za-z0-9]{32,}",
replacement="[REDACTED_API_KEY]",
direction=TransformDirection.REQUEST,
)
],
# Audit logging for all security events
audit=AuditConfig(enabled=True),
)
import {
createSecurityPolicy,
createPIIConfig,
createInjectionDefenseConfig,
createNetworkPolicy,
createAuditConfig,
createTransformationRule,
PIIType,
RedactionAction,
InjectionAction,
TransformDirection,
ALL_TRAFFIC,
} from "@declaw/sdk";
const policy = createSecurityPolicy({
// PII scanning and redaction on outbound HTTP bodies
pii: createPIIConfig({
enabled: true,
types: [PIIType.SSN, PIIType.CreditCard, PIIType.Email, PIIType.Phone],
action: RedactionAction.Redact,
rehydrateResponse: true,
}),
// Prompt injection detection on LLM API calls
injectionDefense: createInjectionDefenseConfig({
enabled: true,
action: InjectionAction.Block,
threshold: 0.8,
}),
// Domain-level network access control
network: createNetworkPolicy({
allowOut: ["api.openai.com", "pypi.org"],
denyOut: [ALL_TRAFFIC],
}),
// Regex-based request/response body transformations
transformations: [
createTransformationRule({
pattern: "sk-[A-Za-z0-9]{32,}",
replacement: "[REDACTED_API_KEY]",
direction: TransformDirection.Request,
}),
],
// Audit logging for all security events
audit: createAuditConfig({ enabled: true }),
});
SecurityPolicy fields
| Field | Type | Default | Description |
|---|
pii | PIIConfig | disabled | PII detection and redaction config |
injection_defense | bool | InjectionDefenseConfig | False | Prompt injection detection config |
transformations | list[TransformationRule] | [] | Regex rewrite rules for request/response bodies |
network | NetworkPolicy | None | None | Domain and IP access rules |
audit | bool | AuditConfig | False | Security event audit logging |
env_security | EnvSecurityConfig | default | Env var masking and secret handling |
Templates
A template is the base filesystem image used when creating a sandbox. Templates allow you to pre-install dependencies, configure the environment, and avoid reinstalling packages on every sandbox creation.
The built-in ubuntu template is a minimal Ubuntu environment with Python 3, bash, curl, and common utilities. You can build custom templates from a TemplateBase specification that lists packages to install and files to copy.
Custom templates are built once and stored server-side. When a sandbox is created with template="my-template", the orchestrator boots the VM from that template’s rootfs snapshot instead of the default image.
See Templates for the full build API and CopyItem reference.
ALL_TRAFFIC constant
ALL_TRAFFIC is a sentinel string ("*") used in network policies to represent all destinations. Use it in deny_out to create a default-deny policy:
from declaw import ALL_TRAFFIC, SecurityPolicy
policy = SecurityPolicy(
network={"allow_out": ["api.openai.com"], "deny_out": [ALL_TRAFFIC]}
)
This allows connections to api.openai.com and blocks everything else. The allow_out list is evaluated before deny_out, so allowlist entries always take precedence.
Further reading