
AI 에이전트 작업을 샌드박싱하는 보안 런타임. 격리된 WebAssembly 환경에서 신뢰할 수 없는 코드를 실행합니다.
Capsule은 신뢰할 수 없는 코드를 격리된 환경에서 실행하기 위한 런타임입니다. 각 태스크는 자체 WebAssembly 샌드박스 내에서 실행되며, 다음을 제공합니다:
Python 함수를 @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."""
# Your code runs safely in a Wasm sandbox
return {"processed": len(dataset), "status": "complete"}
npm 생태계에 완전히 접근할 수 있는 task() 래퍼 함수를 사용하세요:
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" };
});
[!NOTE] 런타임은 진입점으로
"main"이라는 이름의 태스크가 필요합니다. Python은 정의되지 않은 경우 자동으로 하나를 생성하지만, 명시적으로 설정하는 것이 좋습니다.
capsule run main.py (또는 main.ts)를 실행하면 코드가 WebAssembly 모듈로 컴파일되어 격리된 샌드박스에서 실행됩니다.
각 태스크는 구성 가능한 리소스 제한이 있는 자체 샌드박스 내에서 작동하여, 실패가 격리되고 워크플로의 다른 부분으로 전파되지 않습니다. 호스트 시스템은 Wasm 연료 측정을 통한 CPU 할당부터 메모리 제약 및 타임아웃 적용까지 실행의 모든 측면을 제어합니다.
pip install capsule-run
hello.py 생성:
from capsule import task
@task(name="main", compute="LOW", ram="64MB")
def main() -> str:
return "Hello from Capsule!"
실행:
capsule run hello.py
npm install -g @capsule-run/cli
npm install @capsule-run/sdk
hello.ts 생성:
import { task } from "@capsule-run/sdk";
export const main = task({
name: "main",
compute: "LOW",
ram: "64MB"
}, (): string => {
return "Hello from Capsule!";
});
실행:
capsule run hello.ts
[!TIP]
--verbose를 추가하면 실시간 태스크 실행 세부 정보를 볼 수 있습니다.
run() 함수를 사용하면 CLI 대신 코드에서 프로그래밍 방식으로 태스크를 실행할 수 있습니다. args는 자동으로 main 태스크의 매개변수로 전달됩니다.
from capsule import run
result = await run(
file="./sandbox.py",
args=["code to execute"]
)
sandbox.py 생성:
from capsule import task
@task(name="main", compute="LOW", ram="64MB")
def main(code: str) -> str:
return eval(code)
[!IMPORTANT] TypeScript에서 러너 함수를 사용하려면 의존성에
@capsule-run/cli가 필요합니다.
import { run } from '@capsule-run/sdk/runner';
const result = await run({
file: './sandbox.ts',
args: ['code to execute']
});
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] 미리 구성된 바로 사용 가능한 솔루션을 찾고 있다면 Python 어댑터 또는 TypeScript 어댑터를 확인하세요.
다음 매개변수로 태스크를 구성하세요:
| 매개변수 | 설명 | 타입 | 기본값 | 예시 |
|---|---|---|---|---|
name | 태스크 식별자 | str | 함수 이름 (Python) / 필수 (TS) | "process_data" |
compute | CPU 할당 수준: "LOW", "MEDIUM", 또는 "HIGH" | str | "MEDIUM" | "HIGH" |
ram | 태스크의 메모리 제한 | str | 무제한 | "512MB", "2GB" |
timeout | 최대 실행 시간 | str | 무제한 | "30s", "5m", "1h" |
max_retries / maxRetries | 실패 시 재시도 횟수 | int | 0 | 3 |
allowed_files / allowedFiles | 샌드박스에서 접근 가능한 폴더 (선택적 접근 모드 포함) | list | [] | ["./data"], [{"path": "./data", "mode": "ro"}] |
allowed_hosts / allowedHosts | 샌드박스에서 접근 가능한 도메인 | list | [] | ["api.openai.com", "*.anthropic.com"] |
env_variables / envVariables | 샌드박스에서 접근 가능한 환경 변수 | list | [] | ["API_KEY"] |
Capsule은 WebAssembly의 연료 메커니즘을 통해 CPU 사용량을 제어합니다. 이 메커니즘은 명령어 실행을 계량합니다. 컴퓨트 수준은 태스크가 받는 연료량을 결정합니다.
compute="1000000")모든 태스크는 결과와 실행 메타데이터를 모두 포함하는 구조화된 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": [{...}]
}
}
응답 필드:
success — 태스크가 성공적으로 완료되었는지 나타내는 부울 값result — 태스크의 실제 반환 값 (json, string, 실패 시 null 등)error — 태스크 실패 시 오류 세부 정보 ({ error_type: string, message: string })execution — 성능 메트릭:
task_name — 실행된 태스크의 이름duration_ms — 실행 시간 (밀리초)retries — 발생한 재시도 횟수fuel_consumed — 사용된 CPU 리소스 (컴퓨트 수준 참조)ram_used — 최대 사용 메모리 (바이트)host_requests — 태스크가 수행한 호스트 요청 목록태스크는 allowed_hosts에 지정된 도메인으로 HTTP 요청을 보낼 수 있습니다. 기본적으로 아웃바운드 요청은 허용되지 않습니다 ([]). 도메인을 허용 목록으로 제공하거나, ["*"]를 사용하여 모든 도메인을 허용할 수 있습니다.
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();
});
태스크는 allowed_files에 지정된 디렉토리 내에서 파일을 읽고 쓸 수 있습니다. 이러한 디렉토리 외부의 파일에 접근하는 시도는 불가능합니다.
[!NOTE]
allowed_files는 개별 파일이 아닌 디렉토리 경로만 지원합니다.
각 항목은 일반 경로 (기본적으로 읽기-쓰기) 또는 명시적인 mode가 있는 구조화된 객체일 수 있습니다:
"read-only" (또는 "ro")"read-write" (또는 "rw")Python의 표준 파일 연산이 정상적으로 작동합니다. open(), os, pathlib 또는 파일 조작 라이브러리를 사용하세요.
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
일반 문자열도 허용됩니다: allowed_files=["./output"]는 기본적으로 읽기-쓰기로 처리됩니다.
일반적인 Node.js 내장 모듈을 사용할 수 있습니다. 표준 fs 모듈을 사용하세요:
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;
});
일반 문자열도 허용됩니다: allowedFiles: ["./output"]는 기본적으로 읽기-쓰기로 처리됩니다.
--mount)