Skip to main content

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

FieldTypeDescription
sandbox_idstrUnique identifier for the sandbox
stateSandboxStaterunning, paused, stopped
template_idstrThe base image used to boot this sandbox
created_atdatetimeWhen the sandbox was created
timeoutintSeconds 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),
)

SecurityPolicy fields

FieldTypeDefaultDescription
piiPIIConfigdisabledPII detection and redaction config
injection_defensebool | InjectionDefenseConfigFalsePrompt injection detection config
transformationslist[TransformationRule][]Regex rewrite rules for request/response bodies
networkNetworkPolicy | NoneNoneDomain and IP access rules
auditbool | AuditConfigFalseSecurity event audit logging
env_securityEnvSecurityConfigdefaultEnv 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