Skip to main content

Secure WASM runtime to isolate and manage AI agent tasks

Project description

capsule-run

A secure, durable runtime for agentic workflows

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.

Installation

pip install capsule-run

Getting started

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

Use --verbose to display real-time task execution details.

Production

Running source code directly (like .py) 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
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']}")

See Integrate Into an Existing Project for details.

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

Integrate Into an Existing Project

The run() function lets you execute tasks programmatically from your application code, no CLI needed.

from capsule import run

result = await run(
    file="./capsule.py", # or `capsule.wasm`
    args=["code to execute"]
)

Create capsule.py:

from capsule import task

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

How It Works

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."""
    return {"processed": len(dataset), "status": "complete"}

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, your code is compiled into a WebAssembly module and executed in a dedicated sandbox.

Response Format

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

{
  "success": true,
  "result": { "processed": 5, "status": "complete" },
  "error": null,
  "execution": {
    "task_name": "data_processor",
    "duration_ms": 1523,
    "retries": 0,
    "fuel_consumed": 45000
  }
}

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)

Documentation

Task Configuration Options

Parameter Description Type Default Example
name Task identifier str function name "process_data"
compute CPU level: "LOW", "MEDIUM", "HIGH" str "MEDIUM" "HIGH"
ram Memory limit str unlimited "512MB", "2GB"
timeout Maximum execution time str unlimited "30s", "5m"
max_retries Retry attempts on failure int 0 3
allowed_files Folders accessible in the sandbox list [] ["./data", "./output"]
allowed_hosts Domains accessible in the sandbox list ["*"] ["api.openai.com", "*.anthropic.com"]
env_variables Environment variables accessible in the sandbox list [] ["API_KEY"]

Compute Levels

  • LOW: Minimal allocation for lightweight tasks
  • MEDIUM: Balanced resources for typical workloads
  • HIGH: Maximum fuel for compute-intensive operations
  • CUSTOM: Specify exact fuel value (e.g., compute="1000000")

Project Configuration (Optional)

Create a capsule.toml file in your project root to set default options:

[workflow]
name = "My AI Workflow"
version = "1.0.0"
entrypoint = "src/main.py"  # Run `capsule run` without specifying a file

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

Task-level options always override these defaults.

HTTP Client

Standard requests library isn't compatible with WASM. Use Capsule's HTTP client:

from capsule import task
from capsule.http import get, post

@task(name="fetch", compute="MEDIUM", timeout="30s")
def main() -> dict:
    response = get("https://api.example.com/data")
    return {"status": response.status_code, "ok": response.ok()}

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.

from capsule import task

@task(name="restricted_writer", allowed_files=["./output"])
def restricted_writer() -> None:
    with open("./output/result.txt", "w") as f:
        f.write("result")

@task(name="main")
def main() -> str:
    restricted_writer()

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.

Wildcards are supported: *.example.com matches all subdomains of example.com.

from capsule import task
from capsule.http import get

@task(name="main", allowed_hosts=["api.openai.com", "*.anthropic.com"])
def main() -> dict:
    response = get("https://api.openai.com/v1/models")
    return response.json()

Environment Variables

Tasks can access environment variables to read configuration, API keys, or other runtime settings. 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}

Compatibility

Supported:

  • Pure Python packages and standard library
  • json, math, re, datetime, collections, etc.

⚠️ Not yet supported (inside the sandbox):

  • Packages with C extensions (e.g. numpy, pandas)

These limitations only apply to the task file executed in the sandbox. Your host code using run() has access to the full Python ecosystem, any pip package, native extensions, everything. (see Integrate Into an Existing Project)

Links

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

capsule_run-0.6.4.tar.gz (134.7 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

capsule_run-0.6.4-py3-none-win_amd64.whl (10.2 MB view details)

Uploaded Python 3Windows x86-64

capsule_run-0.6.4-py3-none-manylinux_2_39_x86_64.whl (10.5 MB view details)

Uploaded Python 3manylinux: glibc 2.39+ x86-64

capsule_run-0.6.4-py3-none-macosx_11_0_arm64.whl (9.5 MB view details)

Uploaded Python 3macOS 11.0+ ARM64

File details

Details for the file capsule_run-0.6.4.tar.gz.

File metadata

  • Download URL: capsule_run-0.6.4.tar.gz
  • Upload date:
  • Size: 134.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: maturin/1.12.6

File hashes

Hashes for capsule_run-0.6.4.tar.gz
Algorithm Hash digest
SHA256 a283ea4f6eab39e4889170c6a514ed527eb6a56b26cea8fae57ce6411d9ab099
MD5 4f74c45e49b77717d0acc334dfa46331
BLAKE2b-256 8f3e55fef386e2636838693cf34d8d7a9096dd01e10ad26ac49e2ba4a6785445

See more details on using hashes here.

File details

Details for the file capsule_run-0.6.4-py3-none-win_amd64.whl.

File metadata

File hashes

Hashes for capsule_run-0.6.4-py3-none-win_amd64.whl
Algorithm Hash digest
SHA256 25cd452c9b04f59748eae3a21c5583deb898617c0323d04af389c93d7925cc45
MD5 f8eafad7196c6f924a92740c04e55d9c
BLAKE2b-256 9c58b26182f626ac9951a4cf0937746049b23efcbc347c42abf7fe4065e7653b

See more details on using hashes here.

File details

Details for the file capsule_run-0.6.4-py3-none-manylinux_2_39_x86_64.whl.

File metadata

File hashes

Hashes for capsule_run-0.6.4-py3-none-manylinux_2_39_x86_64.whl
Algorithm Hash digest
SHA256 fefa4acc09db15a2c11372017fd56e01c0606b00674596ddbf64b569c18c1dcd
MD5 1c03ae5e82774badd43fe79830c2ef59
BLAKE2b-256 9b9edf650d5d009d04f3d538c45dd7ba11b935533a911a500a01de209419fdc5

See more details on using hashes here.

File details

Details for the file capsule_run-0.6.4-py3-none-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for capsule_run-0.6.4-py3-none-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 dc217f7dfaa1424c4c4dddf1be0efee1a3869a02e2d2e1730d06112ba36776c1
MD5 c2c058660c0e24b9a03f6f58ad6b9a7c
BLAKE2b-256 d096ba09644381ff4dd916c8ce4474cbe6a0f4399f2b92af4f07e6832316e2c3

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page