INTEGRITY Документация

Мост песочницы

Мост песочницы представляет собой эталонную реализацию Worker Cloudflare, которая предоставляет Sandbox SDK как HTTP API. Любой HTTP-клиент (Python-скрипт, сервис на Node.js, CI-пайплайн) может создавать sandbox и управлять ими без написания Worker. Вы развёртываете Worker в ваш аккаунт. Это не общий API, размещённый на стороне Cloudflare.

Зачем использовать мост

Sandbox SDK предназначен для использования внутри Cloudflare Workers. Если ваше приложение работает вне экосистемы Workers, оно не может напрямую взаимодействовать с песочницами.

Мост предоставляет Sandbox SDK в виде стандартного HTTP API, поэтому вы можете создавать песочницы и управлять ими на любом языке и любой платформе.

Ключ Методы Sandbox SDK сопоставляются с отдельными HTTP-эндпоинтами. Мост добавляет аутентификацию, проверку входных данных, ограничение пути рабочей области и опциональный тёплый пул для мгновенного запуска контейнера.

Развернуть

Разверните bridge Worker в своём аккаунте Cloudflare:

Deploy to Cloudflare

Кнопка разворачивает Worker и создаёт SANDBOX_API_KEY секрет для аутентификации. После завершения развертывания запишите URL Worker и API-ключ: они используются во всех примерах на этой странице.

Ручное развёртывание

Если вы предпочитаете разворачивать поэтапно, создайте структуру проекта и разверните его вручную.

Предварительные требования:

Шаги:

  1. Создайте каркас проекта моста:

    npm create cloudflare -- sandbox-bridge --template=cloudflare/sandbox-sdk/bridge/worker
    cd sandbox-bridge
  2. Аутентификация в Cloudflare:

    npx wrangler login
  3. Задайте секрет с API-ключом. Выберите любое надёжное значение токена: клиенты должны передавать его в виде Bearer-токена:

    openssl rand -hex 32 | tee /dev/stderr | npx wrangler secret put SANDBOX_API_KEY

    Ключ выводится в ваш терминал и передаётся через конвейер в Wrangler. Сохраните его: он понадобится для аутентификации запросов к API.

  4. Разверните Worker:

    npx wrangler deploy
  5. Проверьте развёртывание:

    curl https://cloudflare-sandbox-bridge.<your-subdomain>.workers.dev/health

    Вы должны увидеть {"ok":true}.

Образ контейнера

Мост Dockerfile расширяет cloudflare/sandbox ↗ базовый образ и заранее устанавливает распространённые инструменты для агентов:

Настроить Dockerfile чтобы добавить языки, системные пакеты или инструменты, нужные вашим рабочим нагрузкам.

Использование

Во всех примерах предполагается, что заданы следующие переменные окружения:

export SANDBOX_API_URL=https://cloudflare-sandbox-bridge.<your-subdomain>.workers.dev
export SANDBOX_API_KEY=<your-token>

Создайте песочницу и выполните команду

# Create a sandbox
SANDBOX_ID=$(curl -s -X POST "$SANDBOX_API_URL/v1/sandbox" \
  -H "Authorization: Bearer $SANDBOX_API_KEY" | jq -r '.id')

echo "Sandbox ID: $SANDBOX_ID"

# Run a command
curl -s -X POST "$SANDBOX_API_URL/v1/sandbox/$SANDBOX_ID/exec" \
  -H "Authorization: Bearer $SANDBOX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"argv": ["sh", "-lc", "echo hello from the sandbox"], "timeout_ms": 10000}'

# Destroy the sandbox when done
curl -s -X DELETE "$SANDBOX_API_URL/v1/sandbox/$SANDBOX_ID" \
  -H "Authorization: Bearer $SANDBOX_API_KEY"
const API_URL = process.env.SANDBOX_API_URL;
const API_KEY = process.env.SANDBOX_API_KEY;

const headers = {
  Authorization: `Bearer ${API_KEY}`,
  "Content-Type": "application/json",
};

// Create a sandbox
const { id } = await fetch(`${API_URL}/v1/sandbox`, {
  method: "POST",
  headers,
}).then((r) => r.json());

console.log(`Sandbox ID: ${id}`);

// Run a command
// Response is a text/event-stream with the following SSE events:
//   event: stdout  — data is a base64-encoded output chunk
//   event: stderr  — data is a base64-encoded error chunk
//   event: exit    — data is JSON: {"exit_code": 0}
//   event: error   — data is JSON: {"error": "...", "code": "..."}
const execRes = await fetch(`${API_URL}/v1/sandbox/${id}/exec`, {
  method: "POST",
  headers,
  body: JSON.stringify({
    argv: ["sh", "-lc", "echo hello from the sandbox"],
    timeout_ms: 10000,
  }),
});

console.log(await execRes.text());

// Destroy the sandbox when done
await fetch(`${API_URL}/v1/sandbox/${id}`, {
  method: "DELETE",
  headers,
});
# /// script
# dependencies = ["httpx"]
# ///
import os
import httpx

API_URL = os.environ["SANDBOX_API_URL"]
API_KEY = os.environ["SANDBOX_API_KEY"]

headers = {"Authorization": f"Bearer {API_KEY}"}

# Create a sandbox
resp = httpx.post(f"{API_URL}/v1/sandbox", headers=headers)
sandbox_id = resp.json()["id"]
print(f"Sandbox ID: {sandbox_id}")

# Run a command
# Response is a text/event-stream with the following SSE events:
#   event: stdout  — data is a base64-encoded output chunk
#   event: stderr  — data is a base64-encoded error chunk
#   event: exit    — data is JSON: {"exit_code": 0}
#   event: error   — data is JSON: {"error": "...", "code": "..."}
exec_resp = httpx.post(
    f"{API_URL}/v1/sandbox/{sandbox_id}/exec",
    headers=headers,
    json={
        "argv": ["sh", "-lc", "echo hello from the sandbox"],
        "timeout_ms": 10000,
    },
)
print(exec_resp.text)

# Destroy the sandbox when done
httpx.delete(f"{API_URL}/v1/sandbox/{sandbox_id}", headers=headers)

Запись и чтение файлов

# Write a file
curl -s -X PUT "$SANDBOX_API_URL/v1/sandbox/$SANDBOX_ID/file/workspace/hello.py" \
  -H "Authorization: Bearer $SANDBOX_API_KEY" \
  --data-binary 'print("hello world")'

# Read a file
curl -s "$SANDBOX_API_URL/v1/sandbox/$SANDBOX_ID/file/workspace/hello.py" \
  -H "Authorization: Bearer $SANDBOX_API_KEY"
// Write a file
await fetch(`${API_URL}/v1/sandbox/${id}/file/workspace/hello.py`, {
  method: "PUT",
  headers,
  body: 'print("hello world")',
});

// Read a file
const content = await fetch(
  `${API_URL}/v1/sandbox/${id}/file/workspace/hello.py`,
  { headers },
).then((r) => r.text());

console.log(content);
# /// script
# dependencies = ["httpx"]
# ///
import os
import httpx

API_URL = os.environ["SANDBOX_API_URL"]
API_KEY = os.environ["SANDBOX_API_KEY"]
SANDBOX_ID = os.environ["SANDBOX_ID"]  # from the "Create a sandbox" step
headers = {"Authorization": f"Bearer {API_KEY}"}

# Write a file
httpx.put(
    f"{API_URL}/v1/sandbox/{SANDBOX_ID}/file/workspace/hello.py",
    headers=headers,
    content=b'print("hello world")',
)

# Read a file
content = httpx.get(
    f"{API_URL}/v1/sandbox/{SANDBOX_ID}/file/workspace/hello.py",
    headers=headers,
).text
print(content)

Поддерживайте мост в актуальном состоянии

Основная часть логики моста находится в @cloudflare/sandbox пакет. Чтобы получить последние улучшения:

  1. Обновите зависимость SDK:

    npm update @cloudflare/sandbox
  2. Разверните заново:

    npx wrangler deploy

Проверьте релизы sandbox-sdk ↗ при изменениях в Dockerfile или конфигурацию моста, которая может потребовать ручного обновления.

Исходный код и примеры

Исходный код моста и примеры доступны на GitHub: