The forkd Python SDK exposes two complementary surfaces.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/deeplethe/forkd/llms.txt
Use this file to discover all available pages before exploring further.
Controller is the host-side lifecycle client: it talks to the forkd-controller daemon’s REST API to create and delete snapshots, spawn sandboxes from them, branch a running sandbox into a new snapshot tag, and kill individual VMs. Sandbox is the in-guest execution client: it connects over TCP to the agent running inside one specific child VM so you can exec commands and eval expressions without shelling out to the CLI. Most agent runtimes use both — Controller to spawn, branch, and kill; Sandbox to drive code execution inside the VMs.
Installation
Controller
Controller wraps the /v1/snapshots and /v1/sandboxes REST endpoints. All network I/O uses Python’s stdlib urllib; no extra dependencies are required.
Constructor
Daemon base URL. Resolved in order: the
base_url argument, the FORKD_URL environment variable, then the hardcoded default. Trailing slashes are stripped.Bearer token sent as
Authorization: Bearer <token>. Defaults to the FORKD_TOKEN environment variable. Required only when the daemon was started with --token-file.Per-request timeout in seconds. Branching a large source VM can take 0.5–8 s for a full snapshot; the generous default accommodates that. Reduce it for latency-sensitive read-only calls if you want faster failure.
Snapshot methods
list_snapshots
GET /v1/snapshots — returns every snapshot registered with the daemon.
A list of
SnapshotInfo dicts. Each dict contains tag, dir, created_at_unix, and optionally branched_from, pause_ms, and status.delete_snapshot
DELETE /v1/snapshots/:tag — removes the snapshot from the registry and deletes its on-disk files.
The snapshot tag to delete.
Sandbox methods
spawn_sandboxes
POST /v1/sandboxes — fork n children from a registered snapshot tag.
Name of a registered snapshot to fork from. Must exist in
list_snapshots().Number of child sandboxes to spawn, 1–1000. All children share the parent snapshot’s memory image via copy-on-write.
When
True, each child is placed in a dedicated network namespace named forkd-child-<i>. The host must have run scripts/netns-setup.sh N beforehand. Required for per-child network isolation.Sets the cgroup
memory.max for each child in MiB. When None, no memory limit is applied.v0.2.5+. When
True, each child performs a throwaway snapshot to scratch storage immediately after restore. This faults in all guest pages upfront, trading ~170 ms / 512 MiB of extra spawn time for a predictable BRANCH latency on the first user-visible BRANCH — avoiding the 2–9× cold-cache penalty on first access. Useful when you have a BRANCH SLO and are fanning out 3 or more sandboxes from the same source.v0.4+. Boots the sandbox with a memfd-backed RAM region so later BRANCHes from it can use
mode="live" (UFFD_WP). Requires Linux kernel 5.7+ and the vendored Firecracker fork. No cost at spawn time beyond the backend swap; the overhead shows up on the first live BRANCH.v0.4+. Backs the memfd with 2 MiB hugepages (
MFD_HUGETLB | MFD_HUGE_2MB). Only meaningful when live_fork=True. Reduces TLB pressure during spawn-many and live BRANCH bulk-copy. Requires non-zero HugePages_Free in /proc/meminfo — forkd doctor checks availability. Falls back to 4 KiB pages with a warning when the pool is exhausted.A list of
SandboxInfo dicts. Each dict contains: id, snapshot_tag, netns, guest_addr, created_at_unix, pid, and optionally memory_limit_mib.list_sandboxes
GET /v1/sandboxes — returns every live sandbox the daemon is tracking.
A list of
SandboxInfo dicts.get_sandbox
GET /v1/sandboxes/:id — fetch metadata for one sandbox.
The sandbox id (e.g.
"sb-67a1b3-0000").A single
SandboxInfo dict.kill_sandbox
DELETE /v1/sandboxes/:id — terminate one sandbox. Kills the Firecracker process and removes its cgroup leaf.
The sandbox id to terminate.
branch_sandbox
POST /v1/sandboxes/:id/branch — pause the source sandbox, write a snapshot, resume the source. The returned snapshot is independent of the source’s lifecycle; pass its tag back to spawn_sandboxes to fan out grandchildren that inherit the source’s exact state.
Id of the sandbox to branch from.
Optional tag for the new snapshot. When unset, the daemon generates
branch-<sandbox-id>-<unix-ts>.v0.4+ canonical mode selector. One of
"full", "diff", or "live". When set, takes precedence over the legacy diff boolean. Passing both mode and diff=True raises ControllerError (HTTP 400). Prefer mode in new code."full"— copy entire guest RAM under pause (0.5–8 s, default for v0.x)."diff"— Firecracker Diff snapshot (v0.3+). ~200 ms source pause for idle sources; 6–15× speedup on typical agent workloads."live"— UFFD_WP live BRANCH (v0.4+). Sub-50 ms source pause; memory streams from the running parent. Source must have been spawned withlive_fork=True.
Legacy. Equivalent to
mode="diff". Kept so this SDK can drive v0.3.x daemons that don’t understand the mode field. Mutually exclusive with mode (server returns HTTP 400 if both are set).v0.3+. Measurement-only hook. Takes a Diff snapshot inside the existing Full pause to report what diff would have cost, without changing semantics. Mutually exclusive with
diff (daemon returns 400 if both are True).v0.4+, only meaningful with
mode="live". Default True blocks until the background memory copy finishes and the returned snapshot has status="ready". Set to False for fire-and-forget: returns as soon as the source resumes (~10 ms) with status="writing". Poll list_snapshots() to detect when the snapshot becomes status="ready".A
SnapshotInfo dict. Contains tag, dir, created_at_unix, branched_from, and pause_ms. When mode="live", also includes status ("writing" or "ready").exec_command
POST /v1/sandboxes/:id/exec — run a subprocess inside the sandbox and return its output.
The target sandbox id.
Argv list. The first element is the executable path; subsequent elements are its arguments (e.g.
["python3", "-c", "print(2+2)"]).Maximum seconds to wait for the command to complete before the daemon kills it.
{"stdout": str, "stderr": str, "exit_code": int}eval_code
POST /v1/sandboxes/:id/eval — evaluate code against the sandbox’s warmed PID-1 process. Bypasses subprocess overhead: the parent VM’s runtime is already warm, so eval returns in single-digit milliseconds instead of ~100 ms for a fresh python3 -c "..." subprocess.
The target sandbox id.
Python expression to evaluate against the warmed interpreter. Imports such as
numpy are already in scope if the parent snapshot pre-imported them.{"result": str | None, "error": str | None, "exit_code": int}ping_sandbox
POST /v1/sandboxes/:id/ping — round-trip health check to the in-guest agent.
The target sandbox id.
Dict with at minimum
"pong" and "numpy_version". Exact shape is recipe-specific.Sandbox
Sandbox provides an E2B-compatible in-guest execution API. It connects directly to the TCP agent running inside one specific child VM (default 10.42.0.2:8888) and sends JSON messages to exec commands or eval expressions.
Constructor
Snapshot tag to fork from. Resolved from
FORKD_TAG env var, then "pyagent". Only used when spawn=True.host:port of the in-guest TCP agent. Resolved from FORKD_TARGET, then the default. When driving a Controller-spawned sandbox, pass the guest_addr field from SandboxInfo.Seconds to wait for a response from the in-guest agent per call.
When
True, invokes the forkd CLI to fork a new child at construction time. Set to False when attaching to an already-running sandbox (e.g. one spawned via Controller.spawn_sandboxes).Context manager
__exit__ calls kill(). Use this pattern to guarantee cleanup even on exception.
Sandbox.create
Sandbox(...). Mirrors E2B’s Sandbox.create() style for drop-in compatibility.
commands.run
cmd is executed via sh -c; a list is executed directly as argv.
Command to run. Pass a string for shell expansion or a list for direct argv execution.
Seconds before the in-guest agent kills the subprocess.
A
CommandResult dataclass with fields stdout: str, stderr: str, and exit_code: int. Mirrors E2B’s CommandResult shape.eval
eval returns in single-digit milliseconds (vs ~100 ms for a fresh subprocess spawned by commands.run).
Semantics depend on the recipe:
- Python recipes (default):
codeis a Python expression evaluated in the agent’s interpreter.numpyis in scope when the image has it installed. Returns therepr()of the evaluated value as a string. - Node recipes (
FORKD_AGENT_LANG=node, e.g.playwright-browser):codeis an async-function body run with(browser, context, page)in scope. Top-levelawaitis supported; usereturnto send a value back. Returns the JSON-decoded result as a native Python object.
Expression or statement body to evaluate.
The
repr() string for Python recipes, or a JSON-decoded native Python value for Node recipes. Raises RuntimeError on eval error.ping
"pong" and "numpy_version".
kill
__exit__.
Types
BranchMode
| Value | Pause window | Notes |
|---|---|---|
"full" | 0.5–8 s | Copies entire guest RAM. Default for v0.x. |
"diff" | ~200 ms idle | Firecracker Diff snapshot (v0.3+). 6–15× faster on typical agent workloads, 143× ceiling on 4 GiB SSD. |
"live" | sub-50 ms | UFFD_WP live BRANCH (v0.4+). Source must be spawned with live_fork=True. Combine with wait=False to return after ~10 ms. |
CommandResult
Sandbox.commands.run(). Mirrors E2B’s CommandResult API.
Exception: ControllerError
Raised on any non-2xx response from the forkd-controller daemon.
status to distinguish errors:
| Status | Meaning |
|---|---|
| 404 | Sandbox or snapshot not found |
| 409 | Tag collision (snapshot with that name already exists) |
| 400 | Bad request (e.g. mode and diff both set) |
| 500 | Internal daemon error |
Full example
This example uses both SDK surfaces together.Controller handles VM lifecycle; Sandbox drives in-guest execution. It then demonstrates a live BRANCH with fire-and-forget to fan out grandchildren.