freshcrate
Skin:/
Home > Security > capsule

capsule

A secure, durable runtime to sandbox AI agent tasks. Run untrusted code in isolated WebAssembly environments.

Why this rank:Strong adoptionRelease freshnessHealthy release cadence

Description

A secure, durable runtime to sandbox AI agent tasks. Run untrusted code in isolated WebAssembly environments.

README

Capsule

A secure, durable runtime for AI agents

CI

Getting Started โ€ข Documentation โ€ข Contributing


Overview

Capsule is a runtime for coordinating AI agent tasks in isolated environments. It is designed to handle untrusted code execution, long-running workflows, large-scale processing, or even multi-agent systems.

Each task runs inside its own WebAssembly sandbox, providing:

  • Isolated execution: Each task runs isolated from your host system
  • Resource limits: Set CPU, memory, and timeout limits per task
  • Automatic retries: Handle failures without manual intervention
  • Lifecycle tracking: Monitor which tasks are running, completed, or failed

This enables safe task-level execution of untrusted code within AI agent systems.

How It Works

With Python

Simply annotate your Python functions with the @task decorator:

from capsule import task

@task(name="analyze_data", compute="MEDIUM", ram="512MB", timeout="30s", max_retries=1)
def analyze_data(dataset: list) -> dict:
    """Process data in an isolated, resource-controlled environment."""
    # Your code runs safely in a Wasm sandbox
    return {"processed": len(dataset), "status": "complete"}

With TypeScript / JavaScript

Use the task() wrapper function with full access to the npm ecosystem:

import { task } from "@capsule-run/sdk";

export const analyzeData = task({
  name: "analyze_data",
  compute: "MEDIUM",
  ram: "512MB",
  timeout: "30s",
  maxRetries: 1
}, (dataset: number[]): object => {
  // Your code runs safely in a Wasm sandbox
  return { processed: dataset.length, status: "complete" };
});

// The "main" task is required as the entrypoint
export const main = task({
    name: "main",
    compute: "HIGH"
}, () => {
  return analyzeData([1, 2, 3, 4, 5]);
});

Note

The runtime requires a task named "main" as the entry point. Python will create one automatically if none is defined, but it's recommended to set it explicitly.

When you run capsule run main.py (or main.ts), your code is compiled into a WebAssembly module and executed in isolated sandboxes.

Each task operates within its own sandbox with configurable resource limits, ensuring that failures are contained and don't cascade to other parts of your workflow. The host system controls every aspect of execution, from CPU allocation via Wasm fuel metering to memory constraints and timeout enforcement.

Getting Started

Python

pip install capsule-run

Create hello.py:

from capsule import task

@task(name="main", compute="LOW", ram="64MB")
def main() -> str:
    return "Hello from Capsule!"

Run it:

capsule run hello.py

TypeScript / JavaScript

npm install -g @capsule-run/cli
npm install @capsule-run/sdk

Create hello.ts:

import { task } from "@capsule-run/sdk";

export const main = task({
  name: "main",
  compute: "LOW",
  ram: "64MB"
}, (): string => {
  return "Hello from Capsule!";
});

Run it:

capsule run hello.ts

Tip

Add --verbose to see real-time task execution details.

Run From Your Code

The run() function lets you execute tasks programmatically from your code instead of using the CLI. The args are automatically forwarded as parameters to the main task.

Python

from capsule import run

result = await run(
    file="./sandbox.py",
    args=["code to execute"]
)

Create sandbox.py:

from capsule import task

@task(name="main", compute="LOW", ram="64MB")
def main(code: str) -> str:
    return eval(code)

TypeScript / JavaScript

Important

You need @capsule-run/cli in your dependencies to use the runner functions in TypeScript.

import { run } from '@capsule-run/sdk/runner';

const result = await run({
  file: './sandbox.ts',
  args: ['code to execute']
});

Create sandbox.ts:

import { task } from "@capsule-run/sdk";

export const main = task({
  name: "main",
  compute: "LOW",
  ram: "64MB"
}, (code: string): string => {
  return eval(code);
});

Tip

If you're looking for a pre-configured, ready-to-use solution, check out the Python adapter or TypeScript adapter.

Documentation

Task Configuration Options

Configure your tasks with these parameters:

Parameter Description Type Default Example
name Task identifier str function name (Python) / required (TS) "process_data"
compute CPU allocation level: "LOW", "MEDIUM", or "HIGH" str "MEDIUM" "HIGH"
ram Memory limit for the task str unlimited "512MB", "2GB"
timeout Maximum execution time str unlimited "30s", "5m", "1h"
max_retries / maxRetries Number of retry attempts on failure int 0 3
allowed_files / allowedFiles Folders accessible in the sandbox (with optional access mode) list [] ["./data"], [{"path": "./data", "mode": "ro"}]
allowed_hosts / allowedHosts Domains accessible in the sandbox list [] ["api.openai.com", "*.anthropic.com"]
env_variables / envVariables Environment variables accessible in the sandbox list [] ["API_KEY"]

Compute Levels

Capsule controls CPU usage through WebAssembly's fuel mechanism, which meters instruction execution. The compute level determines how much fuel your task receives.

  • LOW provides minimal allocation for lightweight tasks
  • MEDIUM offers balanced resources for typical workloads
  • HIGH grants maximum fuel for compute-intensive operations
  • CUSTOM to specify an exact fuel value (e.g., compute="1000000") for precise control over execution limits.

Response Format

Every task returns a structured JSON envelope containing both the result and execution metadata:

{
  "success": true,
  "result": "Hello from Capsule!",
  "error": null,
  "execution": {
    "task_name": "data_processor",
    "duration_ms": 1523,
    "retries": 0,
    "fuel_consumed": 45000,
    "ram_used": 1200000,
    "host_requests": [{...}]
  }
}

Response fields:

  • success โ€” Boolean indicating whether the task completed successfully
  • result โ€” The actual return value from your task (json, string, null on failure etc.)
  • error โ€” Error details if the task failed ({ error_type: string, message: string })
  • execution โ€” Performance metrics:
    • task_name โ€” Name of the executed task
    • duration_ms โ€” Execution time in milliseconds
    • retries โ€” Number of retry attempts that occurred
    • fuel_consumed โ€” CPU resources used (see Compute Levels)
    • ram_used โ€” Peak memory used in bytes
    • host_requests โ€” List of host requests made by the task

Network Access

Tasks can make HTTP requests to domains specified in allowed_hosts. By default, no outbound requests are allowed ([]). Provide an allowlist of domains to grant access, or use ["*"] to allow all domains.

Python

import json
from capsule import task
from urllib.request import urlopen

@task(name="main", allowed_hosts=["api.openai.com", "*.anthropic.com"])
def main() -> dict:
    with urlopen("https://api.openai.com/v1/models") as response:
        return json.loads(response.read().decode("utf-8"))

TypeScript / JavaScript

import { task } from "@capsule-run/sdk";

export const main = task({
    name: "main",
    allowedHosts: ["api.openai.com", "*.anthropic.com"]
}, async () => {
    const response = await fetch("https://api.openai.com/v1/models");
    return response.json();
});

File Access

Tasks can read and write files within directories specified in allowed_files. Any attempt to access files outside these directories is not possible.

Note

allowed_files supports directory paths only, not individual files.

Each entry can be a plain path (read-write by default) or a structured object with an explicit mode:

  • "read-only" (or "ro")
  • "read-write" (or "rw")

Python

Python's standard file operations work normally. Use open(), os, pathlib, or any file manipulation library.

from capsule import task

@task(name="main", allowed_files=[
    {"path": "./data", "mode": "read-only"},
    {"path": "./output", "mode": "read-write"},
])
def main() -> str:
    with open("./data/input.txt") as f:
        content = f.read()
    with open("./output/result.txt", "w") as f:
        f.write(content)
    return content

Plain strings are still accepted: allowed_files=["./output"] defaults to read-write.

TypeScript / JavaScript

Common Node.js built-ins are available. Use the standard fs module:

import { task } from "@capsule-run/sdk";
import fs from "fs/promises";

export const main = task({
    name: "main",
    allowedFiles: [
        { path: "./data", mode: "read-only" },
        { path: "./output", mode: "read-write" },
    ]
}, async () => {
    const content = await fs.readFile("./data/input.txt", "utf8");
    await fs.writeFile("./output/result.txt", content);
    return content;
});

Plain strings are still accepted: allowedFiles: ["./output"] defaults to read-write.

Dynamic directory aliases (--mount)

The --mount flag (CLI) or mounts parameter (SDK) mount a host directory into the sandbox under an alias. Mounts propagate to sub-tasks and add access to new paths, they don't change the access mode of paths already declared in allowed_files.

Format: HOST_PATH[::GUEST_PATH][:ro|:rw]

Part Required Description
HOST_PATH yes Path on the host machine (relative to cwd, must stay inside project root)
::GUEST_PATH no Path the task sees inside the sandbox. Defaults to HOST_PATH
:ro / :rw no Access mode. Defaults to read-write

CLI

# Mount a session workspace and expose it as "workspace" inside the task
capsule run main.py --mount sessions/abc123_workspace::workspace

# Multiple directories
capsule run main.py \
  --mount sessions/abc123_workspace::workspace \
  --mount sessions/bce456_workspace::workspace:ro

Python SDK

from capsule import run

result = await run(
    file="main.py",
    mounts=[".capsule/sessions/abc123_workspace::workspace"],
)

TypeScript / JavaScript SDK

import { run } from "@capsule-run/sdk";

const result = await run({
    file: "main.py",
    mounts: [".capsule/sessions/abc123_workspace::workspace"],
});

Inside the task, the directory is accessed via the guest path:

# task sees it at "workspace/", not at the full session path
with open("workspace/output.txt", "w") as f:
    f.write("done")

Note

--mount paths must be relative and must not escape the project root. Absolute paths are rejected.

Environment Variables

Tasks can access environment variables to read configuration, API keys, or other runtime settings.

Python

Use Python's standard os.environ to access environment variables:

from capsule import task
import os

@task(name="main", env_variables=["API_KEY"])
def main() -> dict:
    api_key = os.environ.get("API_KEY")
    return {"api_key": api_key}

TypeScript / JavaScript

Use the standard process.env to access environment variables:

import { task } from "@capsule-run/sdk";

export const main = task({
    name: "main",
    envVariables: ["API_KEY"]
}, () => {
    const apiKey = process.env.API_KEY;
    return { apiKeySet: apiKey !== undefined };
});

Project Configuration (Optional)

You can create a capsule.toml file in your project root to set default options for all tasks and define workflow metadata:

# capsule.toml

[workflow]
name = "My AI Workflow"
version = "1.0.0"
entrypoint = "src/main.py"  # Default file when running `capsule run`

[tasks]
default_compute = "MEDIUM"
default_ram = "256MB"
default_timeout = "30s"
default_max_retries = 2

With an entrypoint defined, you can simply run:

capsule run

Task-level options always override these defaults when specified.

Cache Management

When you run your code, Capsule creates a .capsule folder in your project root. This is the build cache. It stores compiled artifacts so subsequent runs are fast (from seconds to few milliseconds).

Tip

.capsule should be added to .gitignore. The cache is specific to your own environment and will be regenerated automatically.

.capsule/
โ”œโ”€โ”€ wasm/
โ”‚   โ”œโ”€โ”€ main_a1b2c3d4.wasm    # Compiled WebAssembly module
โ”‚   โ””โ”€โ”€ main_a1b2c3d4.cwasm   # Native precompiled cache
โ”œโ”€โ”€ wit/                       # Interface definitions
โ””โ”€โ”€ trace.db                   # Execution logs

Use capsule build to precompile ahead of time and skip the compilation cost on the first run:

capsule build main.ts # or `main.py`

Production

Running source code directly (like .py or .ts) evaluates and compiles your file at runtime. While great for development, this compilation step adds a few seconds of latency on first call. For use cases where sub-second latency is critical, you should build your tasks ahead of time.

# Generates an optimized hello.wasm file
capsule build hello.py --export

# Execute the compiled artifact directly
capsule exec hello.wasm

Note

Or from your existing code:

from capsule import run

result = await run(
   file="./hello.wasm", # or `hello.py`
   args=[]
)

print(f"Task completed: {result['result']}")

Executing a .wasm file bypasses the compiler completely, reducing initialization time to milliseconds while using a natively optimized (.cwasm) format behind the scenes.

Compatibility

Note

TypeScript/JavaScript has broader compatibility than Python since it doesn't rely on native bindings.

Python: Most standard Python libraries work perfectly. Packages that use C extensions require a wasm32-wasi compiled wheel. Many popular packages like numpy and pandas don't ship one yet, so they won't work inside the sandbox. However, your host code (using run()) has access to the full Python ecosystem, including any pip package and native extensions. see in-code usage

TypeScript/JavaScript: npm packages and ES modules work. Common Node.js built-ins are available. If you have any trouble with a built-in, do not hesitate to open an issue.

Contributing

Contributions are welcome!

Development setup

Prerequisites: Rust (latest stable), Python 3.13+, Node.js 22+

git clone https://github.com/mavdol/capsule.git
cd capsule

# Build and install CLI
cargo install --path crates/capsule-cli

# Python SDK (editable install)
pip install -e crates/capsule-sdk/python

# TypeScript SDK (link for local dev)
cd crates/capsule-sdk/javascript
npm install && npm run build && npm link

# Then in your project: npm link @capsule-run/sdk

How to contribute

  1. Fork the repository
  2. Create a feature branch: git checkout -b feature/amazing-feature
  3. Run tests: cargo test (only needed if modifying crates/capsule-cli or crates/capsule-core)
  4. Open a Pull Request

Need help? Open an issue

Credits

Capsule builds on these open source projects:

  • componentize-py โ€“ Python to WebAssembly Component compilation
  • jco โ€“ JavaScript toolchain for WebAssembly Components
  • wasmtime โ€“ WebAssembly runtime
  • WASI โ€“ WebAssembly System Interface

License

This project is licensed under the Apache License 2.0 - see the LICENSE file for details.

Release History

VersionChangesUrgencyDate
v0.8.10## Improvements ### TypeScript SDK Polyfills - Add more built-in methods to fs - Add a real Wasiโ€‘based stats system for files and folders - Add support for symlinks inside sandboxes High4/27/2026
v0.8.9# Release notes ### Rust Core improvements - Fix cache issues for multi-language projects - Improve task reporter to have better logs ### Typescript SDK - Fix worker process issue for `run()` command - Fix absolute path issue for fs polyfills ### Python SDK - Fix worker process issue for `run()` commandHigh4/22/2026
v0.8.8# Smaller Python bundle, worker mode, and path fixes ## Improvements ### Core - **Worker mode**: Added a `--worker` option to reduce child process overhead when using the `run()` function. - **Trace rebuild fix**: Fixed trace rebuild for projects using both Python and TypeScript sandboxes. ### SDKs - **Smaller Python bundle**: Only import the necessary built-ins, reducing the overall size. - **TypeScript path fixes**: Resolved absolute path issues with `fs` polyfills.High4/17/2026
v0.8.7# Path resolution, polyfills, and large argument support > [!NOTE] > This release bundles several improvements made since v0.8.0 (versions 0.8.1 to 0.8.6 were internal steps). ## Core Improvements - **`--args-file` option**: Bypass inline argument limits by passing arguments via a file. ```bash capsule run main.ts --args-file /tmp/tmp-file.txt # avoid inline limit ``` With this improvement, the `run()` function now handles large inputs more reliably. - **Virtual CWD support** :High4/14/2026
v0.8.0# Richer execution metrics This release adds more visibility into each task's behavior. ## What's New ### Core improvements You now get memory usage and outbound network requests for every task. Example response: ```json { "success": true, "result": "Hello from Capsule!", "error": null, "execution": { "task_name": "data_processor", "duration_ms": 1523, "retries": 0, "fuel_consumed": 45000, "ram_used": 1200000, "host_requests": [{ ... }High4/9/2026
v0.7.2# Dynamic Directory Aliases (`--mount`) & Adapter Sessions ## Core Improvements Added `--mount` flag (CLI) and `mounts` parameter (SDK) to mount a host directory into the sandbox under an alias. ```bash # Mount a session workspace and expose it as "workspace" inside the task capsule run main.py --mount restricted-folder/sessions/abc123_workspace::workspace # Multiple directories capsule run main.py \ --mount restricted-folder/sessions/abc123_workspace::workspace \ --mount restMedium3/30/2026
v0.7.1# Restricted Network Access by Default ### Improvements - Explicit allowed hosts are now required `allowed_hosts: ["anthropic.com"]` or `["*"]` to allow all hostsMedium3/24/2026
v0.7.0# Prebuilt Sandbox Adapters & Python Component toolchain Improvements This release adds prebuilt sandbox adapters for Python and TypeScript, plus improvements to Python's component toolchain. ## What's New ### TypeScript #### Prebuilt Sandbox for Python & JavaScript The `@capsule-run/adapter` package offers the same capabilities for TypeScript/JavaScript projects. ```javascript import { runPython, runJavaScript } from '@capsule-run/adapter'; const pythonSandbox = await runMedium3/23/2026
v0.6.5# Extend linux compatibility to older version ### improvements - Support glibc 2.28+ Low3/9/2026
v0.6.4# Linux universal compatibility ## What's new ### Linux improvements - Rebuilt `linux-x64` binary as a static MUSL binary โ€” no more GLIBC version requirements, works on Debian Bookworm, Ubuntu 22.04, Alpine, and any Linux distro - Added `linux-arm64` support (AWS Graviton, Apple Silicon Docker, Raspberry Pi)Low3/9/2026
v0.6.3# Direct Execution This release introduces direct execution of pre-compiled WebAssembly artifacts, allowing you to skip compilation entirely. ### What's New ``` capsule exec sandbox.wasm ``` Use `capsule build --export` to copy the compiled artifact to your source directory, then execute it directly anytime without recompiling. ``` capsule build sandbox.py --export # ~5s (one-time compilation, exports ./sandbox.wasm) capsule exec sandbox.wasm # ~5ms capsule exec sLow3/2/2026
v0.6.2# AOT Precompilation This release adds a new command to precompile your tasks ahead of time, so every `capsule run` starts instantly. ### What's New ```bash capsule build main.py ``` #### Before The first call always pays the compilation cost. ```bash capsule run main.py # ~6s (Build + run) capsule run main.py # ~5ms (cached) capsule run main.py # ~5ms (cached) ``` #### After ```bash capsule build main.py # ~5s (one-time precompilation) capsule run main.py Low2/24/2026
v0.6.1## Bug Fixes ### Windows - Windows compatibility fix ๐ŸŽ‰ New features available in version [v0.6.0](https://github.com/mavdol/capsule/releases/tag/v0.6.0)Low2/16/2026
v0.6.0# SDK Runner & Network Controls This release enables seamless integration of Capsule into existing applications and adds network-level security controls for sandboxed tasks. ### What's New #### SDK Runner โ€” `run()` Function Both SDKs now include an async `run()` function that executes Capsule tasks as a subprocess, making it easy to integrate sandboxed execution into any existing project, no CLI orchestration needed. Just call `run()` from your application code and execute it with `pLow2/16/2026
v0.5.2## Features ### Core - Add `--json` flag for third-party integrations (`capsule run xxx.ts --json`) - Display main task returned value in stdout ### Typescript SDK - Use idiomatic CLI argument pattern (arguments start from third position in `process.argv`) ## Bug fixes ### Core - Fix incorrect `duration_ms` calculation - Return final result in stderr (from main task) ### Typescript SDK - Fix file resolution in deeply nested folder structuresLow2/9/2026
v0.5.1## Bug Fixes ### Typescript SDK - Fix polyfills import issuesLow2/2/2026
v0.5.0# Node.js Ecosystem Compatibility Extended This release focuses on enabling natural Node.js development patterns and improving npm package compatibility within the WASM sandbox. ### What's New Node.js Built-in Polyfills Common Node.js built-in modules are now available in the WASM environment: | Module | Description | |--------|-------------| | `fs` / `fs/promises` | File system operations (sandboxed to `allowedFiles`) | | `path` | Path manipulation utilities | | `process` | ELow2/2/2026
v0.4.2# Environment Variables & TypeScript Improvements ### Environment Variables Support Control which environment variables are accessible in your sandboxes with the new `env_variables` option. ```env # your .env API_KEY=xxx DATABASE_URL=xxxx ``` **Python:** ```python from capsule import task import os @task(name="api_client", env_variables=["API_KEY", "DATABASE_URL"]) def api_client(): api_key = os.environ.get("API_KEY") return {"connected": api_key is not None} Low1/26/2026
v0.4.1## Bug Fixes ### Python SDK - Fix python sdk incompatibilitiesLow1/24/2026
v0.4.0# Configuration & Observability ### Project Configuration (`capsule.toml`) Define project-wide settings in a `capsule.toml` file at your project root. **Workflow metadata and entrypoint:** ```toml [workflow] name = "Voice customer engine" version = "0.1.0" entrypoint = "src/main.py" ``` **Default execution policies:** ```toml [tasks] default_compute = "MEDIUM" default_ram = "256MB" default_timeout = "30s" default_max_retries = 3 default_allowed_files = ["./data"] ``` Low1/24/2026
v0.3.2## Bug Fixes ### Windows - Windows compatibility fixLow1/19/2026
v0.3.1### Windows - Fixed npx command not found on Windows (now uses npx.cmd) - Fixed Python command detection on Windows (uses python instead of python3) - Fixed missing capsule Python module in PyPI Windows wheels ### JavaScript/TypeScript - Fixed TypeScript compilation error "This is not the tsc command you are looking for". Moved typescript and esbuild to runtime dependencies - Fixed WASM output filename mismatch (agent.wasm โ†’ capsule.wasm) ### Improvements - Added ```--versiLow1/19/2026
v0.3.0## File Access Tasks can now read and write files within the sandbox. Use `allowed_files` to specify which directories a sub-task can access. ### Python Standard file operations work naturally: ```python @task(name="restricted_writer", allowed_files=["./output"]) def restricted_writer() -> None: with open("./output/result.txt", "w") as f: f.write("result") ``` ### TypeScript Added new files API for the SDK (since Node.js fs isn't available in WASM): ``` impLow1/19/2026
v0.2.2## Bug Fixes - Fixed npm CLI not registering `capsule` command - Fixed Windows build (bundled SQLite) - Fixed crates.io packaging (WIT files) - Added a README to packages (npm, PyPI, and crates.io)Low1/4/2026
v0.2.1## TypeScript/JavaScript Support - New SDK with task() wrapper function for defining tasks - Full npm ecosystem compatibility - Requires a task named "main" as entrypoint ## Distribution - Automated releases to PyPI, npm, and crates.io - Platform binaries: macOS (arm64, x64), Linux (x64), Windows (x64) - Install via ```pip install capsule-run``` or ```npm install -g @capsule-run/cli``` Developer Experience ## Improved task lifecycle logging - Cleaner README and documentation Low1/4/2026
v0.1.0### Core Runtime & Architecture * **Rust-Based Host:** A robust host capable of loading and executing Wasm Components. * **Structured Logging (WAL):** Integrated SQLite-backed Write-Ahead Logging for reliable persistence, debugging, and replayable execution logs. * **Type-Safe State Management:** Direct state management via `capsule-wit` and bindgen, ensuring strict type safety between the host and guest. * **Fine-Grained Policies:** Enables per-task configuration and isolation rules for exeLow12/16/2025

Dependencies & License Audit

Loading dependencies...

Similar Packages

graphbitGraphBit is the worldโ€™s first enterprise-grade Agentic AI framework, built on a Rust core with a Python wrapper for unmatched speed, security, and scalability. It enables reliable multi-agent workflowGraphbit_Python_v0.6.7
E2BOpen-source, secure environment with real-world tools for enterprise-grade agents.e2b@2.28.0
autopoeA structured multi-agent framework for coordinated AI collaborationv0.2.0
langchain-sandboxSafely run untrusted Python code using Pyodide and Denolangchain_sandbox==0.0.6
deer-flowAn open-source long-horizon SuperAgent harness that researches, codes, and creates. With the help of sandboxes, memories, tools, skill, subagents and message gateway, it handles different levels of tamain@2026-06-06

More from mavdol

@capsule-run/cliSecure WASM runtime to isolate and manage AI agent tasks

More in Security

clineAutonomous coding agent right in your IDE, capable of creating/editing files, executing commands, using the browser, and more with your permission every step of the way.
E2BOpen-source, secure environment with real-world tools for enterprise-grade agents.
OpenSandboxSecure, Fast, and Extensible Sandbox runtime for AI agents.
vm0the easiest way to run natural language-described workflows automatically