
Runtime seguro para isolar tarefas de agentes de IA. Execute código não confiável em ambientes WebAssembly isolados.
O Capsule é um runtime para executar código não confiável em ambientes isolados. Cada tarefa é executada dentro de seu próprio sandbox WebAssembly, fornecendo:
Basta anotar suas funções Python com o decorador @task:
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."""
# Seu código é executado com segurança em um sandbox Wasm
return {"processed": len(dataset), "status": "complete"}
Use a função wrapper task() com acesso total ao ecossistema npm:
import { task } from "@capsule-run/sdk";
export const analyzeData = task({
name: "analyze_data",
compute: "MEDIUM",
ram: "512MB",
timeout: "30s",
maxRetries: 1
}, (dataset: number[]): object => {
// Seu código é executado com segurança em um sandbox Wasm
return { processed: dataset.length, status: "complete" };
});
[!NOTE] O runtime requer uma tarefa chamada
"main"como ponto de entrada. O Python criará uma automaticamente se nenhuma for definida, mas é recomendado defini-la explicitamente.
Quando você executa capsule run main.py (ou main.ts), seu código é compilado em um módulo WebAssembly e executado em sandboxes isolados.
Cada tarefa opera dentro de seu próprio sandbox com limites de recursos configuráveis, garantindo que as falhas sejam contidas e não se propaguem para outras partes do seu fluxo de trabalho. O sistema host controla todos os aspectos da execução, desde a alocação de CPU via medição de combustível Wasm até restrições de memória e cumprimento de tempo limite.
pip install capsule-run
Crie hello.py:
from capsule import task
@task(name="main", compute="LOW", ram="64MB")
def main() -> str:
return "Hello from Capsule!"
Execute:
capsule run hello.py
npm install -g @capsule-run/cli
npm install @capsule-run/sdk
Crie hello.ts:
import { task } from "@capsule-run/sdk";
export const main = task({
name: "main",
compute: "LOW",
ram: "64MB"
}, (): string => {
return "Hello from Capsule!";
});
Execute:
capsule run hello.ts
[!TIP] Adicione
--verbosepara ver detalhes da execução da tarefa em tempo real.
A função run() permite executar tarefas programaticamente a partir do seu código em vez de usar a CLI. Os args são automaticamente encaminhados como parâmetros para a tarefa main.
from capsule import run
result = await run(
file="./sandbox.py",
args=["code to execute"]
)
Crie sandbox.py:
from capsule import task
@task(name="main", compute="LOW", ram="64MB")
def main(code: str) -> str:
return eval(code)
[!IMPORTANT] Você precisa do
@capsule-run/clinas suas dependências para usar as funções runner em TypeScript.
import { run } from '@capsule-run/sdk/runner';
const result = await run({
file: './sandbox.ts',
args: ['code to execute']
});
Crie 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] Se você está procurando uma solução pré-configurada e pronta para uso, confira o adaptador Python ou o adaptador TypeScript.
Configure suas tarefas com estes parâmetros:
O Capsule controla o uso da CPU através do mecanismo de combustível do WebAssembly, que mede a execução de instruções. O nível de computação determina quanto combustível sua tarefa recebe.
compute="1000000") para controle preciso sobre os limites de execução.Cada tarefa retorna um envelope JSON estruturado contendo tanto o resultado quanto os metadados de execução:
{
"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": [{...}]
}
}
Campos da resposta:
success — Booleano indicando se a tarefa foi concluída com sucessoresult — O valor de retorno real da sua tarefa (json, string, null em caso de falha, etc.)error — Detalhes do erro se a tarefa falhou ({ error_type: string, message: string })execution — Métricas de desempenho:
task_name — Nome da tarefa executadaduration_ms — Tempo de execução em milissegundosretries — Número de tentativas de repetição que ocorreramfuel_consumed — Recursos de CPU usados (veja Níveis de Computação)ram_used — Pico de memória usado em byteshost_requests — Lista de requisições ao host feitas pela tarefaAs tarefas podem fazer requisições HTTP para domínios especificados em allowed_hosts. Por padrão, nenhuma requisição de saída é permitida ([]). Forneça uma lista de permissão de domínios para conceder acesso, ou use ["*"] para permitir todos os domínios.
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"))
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();
});
As tarefas podem ler e escrever arquivos dentro de diretórios especificados em allowed_files. Qualquer tentativa de acessar arquivos fora desses diretórios não é possível.
[!NOTE]
allowed_filessuporta apenas caminhos de diretório, não arquivos individuais.
Cada entrada pode ser um caminho simples (leitura-escrita por padrão) ou um objeto estruturado com um mode explícito:
"read-only" (ou "ro")"read-write" (ou "rw")As operações padrão de arquivo do Python funcionam normalmente. Use open(), os, pathlib ou qualquer biblioteca de manipulação de arquivos.
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
Strings simples ainda são aceitas: allowed_files=["./output"] padrão é leitura-escrita.
Os recursos integrados comuns do Node.js estão disponíveis. Use o módulo fs padrão:
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;
});
Strings simples ainda são aceitas: allowedFiles: ["./output"] padrão é leitura-escrita.
--mount)O flag --mount (CLI) ou parâmetro mounts (SDK) monta um diretório do host no sandbox sob um alias. Os mounts propagam para sub-tarefas e adicionam acesso a novos caminhos, eles não alteram o modo de acesso de caminhos já declarados em allowed_files.
Formato: HOST_PATH[::GUEST_PATH][:ro|:rw]
CLI
# Monta um workspace de sessão e o expõe como "workspace" dentro da tarefa
capsule run main.py --mount sessions/abc123_workspace::workspace
# Múltiplos diretórios
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"],
});
Dentro da tarefa, o diretório é acessado pelo caminho convidado:
# task sees it at "workspace/", not at the full session path
with open("workspace/output.txt", "w") as f:
f.write("done")
[!NOTE] Os caminhos
--mountdevem ser relativos e não devem escapar da raiz do projeto. Caminhos absolutos são rejeitados.
As tarefas podem acessar variáveis de ambiente para ler configurações, chaves de API ou outras configurações de runtime.
Use o os.environ padrão do Python para acessar variáveis de ambiente:
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 o process.env padrão para acessar variáveis de ambiente:
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 };
});
Você pode criar um arquivo capsule.toml na raiz do seu projeto para definir opções padrão para todas as tarefas e metadados do workflow:
# capsule.toml
[workflow]
name = "My 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
Com um ponto de entrada definido, você pode simplesmente executar:
capsule run
As opções no nível da tarefa sempre substituem esses padrões quando especificadas.
Quando você executa seu código, o Capsule cria uma pasta .capsule na raiz do seu projeto. Este é o cache de compilação. Ele armazena artefatos compilados para que execuções subsequentes sejam rápidas (de segundos para alguns milissegundos).
[!TIP]
.capsuledeve ser adicionado ao.gitignore. O cache é específico do seu próprio ambiente e será regenerado automaticamente.
.capsule/
├── wasm/
│ ├── main_a1b2c3d4.wasm # Compiled WebAssembly module
│ └── main_a1b2c3d4.cwasm # Native precompiled cache
├── wit/ # Interface definitions
└── trace.db # Execution logs
Use capsule build para pré-compilar antecipadamente e pular o custo de compilação na primeira execução:
capsule build main.ts # or `main.py`
Executar código fonte diretamente (como .py ou .ts) avalia e compila seu arquivo em tempo de execução. Embora seja ótimo para desenvolvimento, esta etapa de compilação adiciona alguns segundos de latência na primeira chamada. Para casos de uso onde a latência abaixo de segundos é crítica, você deve compilar suas tarefas antecipadamente.
# Gera um arquivo hello.wasm otimizado
capsule build hello.py --export
# Executa o artefato compilado diretamente
capsule exec hello.wasm
[!NOTE] Ou a partir do seu código existente:
from capsule import run result = await run( file="./hello.wasm", # or `hello.py` args=[] ) print(f"Task completed: {result['result']}")
Executar um arquivo .wasm contorna o compilador completamente, reduzindo o tempo de inicialização para milissegundos enquanto usa um formato nativamente otimizado (.cwasm) nos bastidores.
[!NOTE] TypeScript/JavaScript tem compatibilidade mais ampla do que Python, pois não depende de bindings nativos.
Python: A maioria das bibliotecas padrão do Python funciona perfeitamente. Pacotes que usam extensões C exigem uma wheel compilada para wasm32-wasi. Muitos pacotes populares como numpy e pandas ainda não fornecem uma, então eles não funcionarão dentro do sandbox. No entanto, seu código host (usando run()) tem acesso ao ecossistema Python completo, incluindo qualquer pacote pip e extensões nativas. veja uso em código
TypeScript/JavaScript: Pacotes npm e módulos ES funcionam. Os recursos integrados comuns do Node.js estão disponíveis. Se você tiver algum problema com um recurso integrado, não hesite em abrir uma issue.
Contribuições são bem-vindas!
Pré-requisitos: Rust (estável mais recente), Python 3.13+, Node.js 22+
git clone https://github.com/capsulerun/capsule.git
cd capsule
# Build e instalação da CLI
cargo install --path crates/capsule-cli
# Python SDK (instalação editável)
pip install -e crates/capsule-sdk/python
# TypeScript SDK (link para desenvolvimento local)
cd crates/capsule-sdk/javascript
npm install && npm run build && npm link
# Em seguida, no seu projeto: npm link @capsule-run/sdk
git checkout -b feature/amazing-featurecargo test (necessário apenas se modificar crates/capsule-cli ou crates/capsule-core)Precisa de ajuda? Abra uma issue
O Capsule se baseia nestes projetos de código aberto:
Este projeto está licenciado sob a Apache License 2.0 - veja o arquivo LICENSE para detalhes.
| Parâmetro | Descrição | Tipo | Padrão | Exemplo |
|---|
name | Identificador da tarefa | str | nome da função (Python) / obrigatório (TS) | "process_data" |
compute | Nível de alocação de CPU: "LOW", "MEDIUM" ou "HIGH" | str | "MEDIUM" | "HIGH" |
ram | Limite de memória para a tarefa | str | ilimitado | "512MB", "2GB" |
timeout | Tempo máximo de execução | str | ilimitado | "30s", "5m", "1h" |
max_retries / maxRetries | Número de tentativas de repetição em caso de falha | int | 0 | 3 |
allowed_files / allowedFiles | Pastas acessíveis no sandbox (com modo de acesso opcional) | list | [] | ["./data"], [{"path": "./data", "mode": "ro"}] |
allowed_hosts / allowedHosts | Domínios acessíveis no sandbox | list | [] | ["api.openai.com", "*.anthropic.com"] |
env_variables / envVariables | Variáveis de ambiente acessíveis no sandbox | list | [] | ["API_KEY"] |
| Parte | Obrigatório | Descrição |
|---|
HOST_PATH | sim | Caminho na máquina host (relativo ao cwd, deve permanecer dentro da raiz do projeto) |
::GUEST_PATH | não | Caminho que a tarefa vê dentro do sandbox. Padrão é HOST_PATH |
:ro / :rw | não | Modo de acesso. Padrão é leitura-escrita |