展示HN:使用WebAssembly沙箱化不受信任的代码
Show HN: Sandboxing untrusted code using WebAssembly

原始链接: https://github.com/mavdol/capsule

## Capsule:安全的AI代理任务协调 Capsule是一个运行时环境,旨在安全地在隔离的环境中执行AI代理任务。它利用WebAssembly (Wasm)沙箱提供安全、资源受控的潜在不受信任代码的执行。 主要特性包括:**隔离**,防止访问主机系统;**资源限制**(CPU、内存、超时);**自动重试**用于故障处理;以及**生命周期跟踪**用于监控任务状态。任务使用简单的装饰器定义(Python中的`@task`,TypeScript/JavaScript中的`task()`),允许配置计算能力、内存和重试次数。 Capsule支持Python和TypeScript/JavaScript,为Python任务提供内置的HTTP客户端(TypeScript/JavaScript中使用标准的`fetch`),并通过`allowed_files`控制文件访问。环境变量也可以安全地访问。 `capsule.toml`文件允许项目范围内的默认配置。运行时需要一个指定的“main”任务作为入口点。Capsule构建于开源项目如`wasmtime`和`WASI`之上,并且正在积极开发中,欢迎贡献。

## Capsule:使用 WebAssembly 对不受信任的代码进行沙箱化 开发者 mavdol 创建了“Capsule”,这是一个使用 WebAssembly (Wasm) 沙箱安全执行不受信任代码的运行时环境。Capsule 使用 Rust、WASI 0.2 和 Wasm 组件模型构建,隔离代码以保护主机系统免受潜在损害。 该项目为 Python 和 TypeScript 提供了简单的 SDK,允许开发者轻松定义和在资源受控的环境中运行任务。这些任务可以限制 CPU、内存、文件系统访问和执行时间,并提供自动重试选项。 Capsule 利用装饰器(Python)和包装器(TypeScript)来定义任务参数,例如资源分配和安全约束。开发者正在寻求关于这个早期阶段项目的反馈,该项目可在 GitHub 上找到:[https://github.com/mavdol/capsule](https://github.com/mavdol/capsule)。它解决了最近关于 Python 中沙箱化挑战的讨论。
相关文章

原文

Capsule is a runtime for coordinating AI agent tasks in isolated environments. It is designed to handle, long-running workflows, large-scale processing, autonomous decision-making securely, 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.

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

For TypeScript and 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 can define the main task itself, but it's recommended to set it manually.

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.

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
  }
}

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)

Create hello.py:

from capsule import task

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

Run it:

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:

Tip

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

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"]
env_variables / envVariables Environment variables accessible in the sandbox list [] ["API_KEY"]

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

[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:

Task-level options always override these defaults when specified.

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:

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

@task(name="http_example", compute="MEDIUM", timeout="30s")
def main() -> dict:
    """Example demonstrating HTTP client usage within a task."""

    # GET request
    response = get("https://api.example.com/data")

    # POST with JSON body
    response = post("https://api.example.com/submit", json={"key": "value"})

    # Response methods
    is_ok = response.ok()           # Returns True if status code is 2xx
    status = response.status_code    # Get the HTTP status code
    data = response.json()           # Parse response as JSON
    text = response.text()           # Get response as text

    return {"status": status, "success": is_ok}

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();
});

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_files supports directory paths, not individual files.

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

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()

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");
});

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}

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 };
});

Note

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

Python: Pure Python packages and standard library modules work. Packages with C extensions (numpy, pandas) are not yet supported.

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.

Contributions are welcome!

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
  1. Fork the repository
  2. Create a feature branch: git checkout -b feature/amazing-feature
  3. Run tests: cargo test
  4. Open a Pull Request

Need help? Open an issue

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

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

联系我们 contact @ memedata.com