Capsule
A secure, durable runtime for AI agents
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:
"""Process data in an isolated, resource-controlled environment."""
# Your code runs safely in a Wasm sandbox
return
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 a dedicated sandbox to isolate tasks.
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.
Response Format
Every task returns a structured JSON envelope containing both the result and execution metadata:
Response fields:
success— Boolean indicating whether the task completed successfullyresult— 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 taskduration_ms— Execution time in millisecondsretries— Number of retry attempts that occurredfuel_consumed— CPU resources used (see Compute Levels)
Getting Started
Python
Create hello.py:
return
Run it:
TypeScript / JavaScript
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:
[!TIP] Add
--verboseto see real-time task execution details.
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. For use cases where sub-second latency is critical, you should build your tasks ahead of time.
# Generates an optimized hello.wasm file
# Execute the compiled artifact directly
[!NOTE] Or from your existing code:
= awaitSee in-code usage documentation for details on both Python and TypeScript integration.
Executing a .wasm file bypasses the compiler completely, reducing initialization time to milliseconds while using a natively optimized (.cwasm) format behind the scenes.
Documentation (v0.6.3)
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 | list |
[] |
["./data", "./output"] |
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.
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
[]
= "My AI Workflow"
= "1.0.0"
= "src/main.py" # Default file when running `capsule run`
[]
= "MEDIUM"
= "256MB"
= "30s"
= 2
With an entrypoint defined, you can simply run:
Task-level options always override these defaults when specified.
HTTP Client API
Python
The standard Python requests library and socket-based networking aren't natively compatible with WebAssembly's sandboxed I/O model. Capsule provides its own HTTP client that works within the Wasm environment:
"""Example demonstrating HTTP client usage within a task."""
# GET request
=
# POST with JSON body
=
# Response methods
= # Returns True if status code is 2xx
= # Get the HTTP status code
= # Parse response as JSON
= # Get response as text
return
TypeScript / JavaScript
Standard libraries like fetch are already compatible, so no custom HTTP client is needed for TypeScript/JavaScript.
import { task } from "@capsule-run/sdk";
export const main = task({
name: "main",
compute: "MEDIUM"
}, async () => {
const response = await fetch("https://api.example.com/data");
return response.json();
});
Network Access
Tasks can make HTTP requests to domains specified in allowed_hosts. By default, all outbound requests are allowed (["*"]). Restrict access by providing a whitelist of domains.
Python
=
return
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] Currently,
allowed_filessupports directory paths, not individual files.
Python
Python's standard file operations work normally. Use open(), os, pathlib, or any file manipulation library.
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 restrictedWriter = task({
name: "restricted_writer",
allowedFiles: ["./output"]
}, async () => {
await fs.writeFile("./output/result.txt", "result");
});
export const main = task({ name: "main", allowedFiles: ["./data"] }, async () => {
await restrictedWriter();
return await fs.readFile("./data/input.txt", "utf8");
});
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:
=
return
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 };
});
In-Code Usage
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
= await
Create sandbox.py:
return
TypeScript / JavaScript
[!IMPORTANT] You need
@capsule-run/cliin your dependencies to use the runner functions in TypeScript.
import { run } from '@capsule-run/sdk/runner';
const result = await run({
file: './sandbox.ts', // or `sandbox.wasm`
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);
});
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]
.capsuleshould 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:
Compatibility
[!NOTE] TypeScript/JavaScript has broader compatibility than Python since it doesn't rely on native bindings.
Python: Only pure Python is supported in sandboxes (no C extensions like numpy or pandas). However, your host code using run() has access to the full Python ecosystem, 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+
# Build and install CLI
# Python SDK (editable install)
# TypeScript SDK (link for local dev)
&& &&
# Then in your project: npm link @capsule-run/sdk
How to contribute
- Fork the repository
- Create a feature branch:
git checkout -b feature/amazing-feature - Run tests:
cargo test(only needed if modifyingcrates/capsule-cliorcrates/capsule-core) - 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.