SSD Nodes Learn 🎉 VPS от $5.50/мес
Руководства Matt ConnorАвтор: Matt Connor

Как развернуть SandBase Harness на своем сервере

Руководство по установке SandBase v0.3.2 на VPS. Настройка YAML конфигурации, запуск MCP серверов, выбор режимов песочницы и подключение Anthropic SDK к вашему локальному узлу.

Что вы получаете при самостоятельном хостинге среды выполнения агента SandBase

Самостоятельный хостинг среды выполнения агента SandBase означает запуск SandBase Harness на собственном сервере. В этом случае сессии, учетные данные, память и журналы аудита хранятся на вашем диске, а не на сторонних ресурсах. Это Node-сервис. Он ожидает подключений на 127.0.0.1:3000, предоставляет HTTP API /v1 и веб-консоль, а также сохраняет свое состояние в SQLite рядом с файлами вашего агента.

API /v1 спроектирован по подобию Claude Managed Agents (CMA) — облачного API для управляемых агентов. Именно это делает данную среду выполнения универсальной: вы можете писать код, используя Anthropic SDK, и указывать в качестве baseURL адрес вашего собственного сервера, а впоследствии перенести этот же код в облачное развершивание.

SandBase Harness не поставляется с предустановленной моделью. Он обращается к ней. По состоянию на август 2026 года поддерживаются OpenAI, Anthropic и совместимые с OpenAI конечные точки, что охватывает self-hosted шлюзы и провайдеров, таких как DeepSeek V4. Вам по-прежнему необходимо предоставить API key или использовать локальный сервер, поддерживающий протокол OpenAI API.

Что необходимо перед началом работы

  • VPS под управлением Ubuntu 24.04 с объемом оперативной памяти не менее 2 GB. Сборка TypeScript — самый ресурсоемкий этап установки.
  • Node.js версии 22 или новее и npm версии 10 или новее. Это жесткие минимальные требования, установленные разработчиками проекта.
  • git, а также API-ключ для выбранного вами провайдера моделей.
  • Docker, если вы планируете использовать изолированные контейнеры для каждой сессии.

В репозиториях Ubuntu 24.04 поставляется Node 18.19, что ниже минимально допустимой версии, поэтому установите Node из NodeSource.

curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
sudo apt install -y nodejs git
node -v
npm -v

node -v должна выводить v22 или выше, а npm -v10 или выше. Если node -v по-прежнему выводит v18.19.1, значит, в системе установлен и имеет приоритет в PATH пакет из дистрибутива. Удалите его перед продолжением, так как сборка выполняется с использованием того node, который первым обнаруживается в оболочке.

Установка SandBase из тега v0.3.2

Выполняйте установку из тега, а не из нестабильной ветки. «Голый» клон main предоставит вам код, который был добавлен всего час назад, и приведенные ниже ключи конфигурации могут с ним не совпасть. v0.3.2 — это актуальный тег на 16 августа 2026 года.

sudo install -d -o "$USER" -g "$USER" /opt/sandbase
cd /opt/sandbase
git clone --branch v0.3.2 --depth 1 https://github.com/sandbaseai/sandbase-harness.git
cd sandbase-harness
npm ci
npm run build

Используйте npm ci, а не npm install. ci устанавливает точные версии, зафиксированные в закоммиченном lock-файле, поэтому ваше дерево будет соответствовать дереву, которое тестировали разработчики. npm install разрешает использование более новых версий, из-за чего закрепленный тег может незаметно перестать быть таковым.

Теперь создайте рабочую область. Рабочая область — это отдельный каталог, в котором хранятся файлы агента и все данные времени выполнения. Размещение рабочей области вне исходного кода позволяет обновлять тег, не затрагивая ваши данные.

mkdir -p /opt/sandbase/workspace
cd /opt/sandbase/workspace
node /opt/sandbase/sandbase-harness/dist/index.js init
node /opt/sandbase/sandbase-harness/dist/index.js start

init создает каталог .managed-agents/ внутри рабочей области. start запускает консоль на http://127.0.0.1:3000/dashboard и API на http://127.0.0.1:3000/v1. Пока ни то, ни другое недоступно с вашего ноутбука, что является ожидаемым поведением и будет рассмотрено далее. На текущий момент подключайтесь к консоли через SSH:

ssh -N -L 3000:127.0.0.1:3000 you@your-server

Длинный путь node .../dist/index.js утомляет, поэтому присвойте ему имя.

alias sandbase='node /opt/sandbase/sandbase-harness/dist/index.js'

Приведенные ниже команды написаны как sandbase <command> с учетом этого сокращения.

Не устанавливайте пакет из npm

В документации по установке проекта прямо указано: пакет managed-agents без области видимости (unscoped), доступный в npm, не относится к данному проекту. Поэтому команды npx managed-agents и npm install -g managed-agents загружают компоненты, не имеющие отношения к нужной вам среде выполнения. Выполняйте установку из помеченного тегом исходного кода в GitHub, пока разработчики не выпустят официальный пакет с областью видимости. Это не просто незначительная сноска в истории проекта: версия v0.3.1 была выпущена главным образом для того, чтобы заменить старый способ быстрого запуска через npm на путь к зафиксированной версии в исходном коде.

Настройка провайдера моделей для рабочей области

init записывает .managed-agents/config.yaml. Для всей рабочей области настраивается один провайдер, а конкретные идентификаторы моделей выбираются уже на уровне отдельных агентов.

model:
  provider: openai
  api_key: ${OPENAI_API_KEY}
storage:
  metadata:
    provider: sqlite
    options: {}
  artifacts:
    provider: local
    options:
      base_path: files

Форма ${OPENAI_API_KEY} считывает значение из переменных окружения процесса, поэтому ключ не попадает в конфигурационный файл и в его резервные копии. Разместите его в файле окружения, доступном для чтения только пользователю root, так как systemd считывает EnvironmentFile= от имени root до понижения привилегий.

sudo install -d -m 750 /etc/sandbase
sudo touch /etc/sandbase/runtime.env
sudo chmod 600 /etc/sandbase/runtime.env

Откройте этот файл в редакторе и добавьте одну строку: OPENAI_API_KEY=sk-.... Здесь должны храниться ключи провайдеров. Секреты, которые агент использует во время сессии, следует хранить в хранилищах учетных данных среды выполнения. Это другая задача с иным радиусом поражения, и статью как не передавать секреты AI-агентам стоит прочитать до того, как вы вставите рабочий токен в любое из этих мест.

YAML-файл агента: mcp_servers, инструменты и политики разрешений

Агенты определяются как YAML-файлы в директории рабочего пространства agents/. Это та часть среды выполнения, с которой вы будете работать непосредственно.

name: Incident commander
description: Triages alerts and coordinates response.
model: gpt-4o
system: |-
  You are an on-call incident commander.
mcp_servers:
  - name: sentry
    type: url
    url: https://mcp.sentry.dev/mcp
tools:
  - type: agent_toolset_20260401
    default_config:
      permission_policy: { type: always_ask }
    configs:
      - name: bash
        permission_policy: { type: always_ask }
  - type: mcp_toolset
    mcp_server_name: sentry
metadata:
  template: incident-commander

Загрузите его и проверьте, что он был успешно добавлен:

sandbase reload
sandbase list
sandbase chat agent_assistant --message "hello"

reload импортирует исходный YAML в SQLite. list теперь должен вывести агента с присвоенным ID. Если list не отображает его, значит, файл не был проанализирован, и причина ошибки записана в .managed-agents/logs/runtime.log.

mcp_servers объявляет конечные точки MCP (model context protocol). type: url означает, что среда выполнения взаимодействует по HTTP с сервером, запущенным в другом месте, поэтому здесь работает всё, что вы уже используете, включая MCP-серверы, размещенные на том же VPS, что и среда выполнения.

Объявление сервера не передает его инструменты агенту автоматически. За это отвечает список tools через запись mcp_toolset, где mcp_server_name совпадает с name, указанным выше. Если агент ведет себя так, будто инструменты MCP отсутствуют, сравните эти две строки посимвольно, прежде чем искать проблему в другом месте.

agent_toolset_20260401 — это встроенный набор инструментов. Суффикс с датой является версией схемы, поэтому агент, привязанный к ней, сохраняет те определения инструментов, для которых он был написан. default_config задает политику для каждого инструмента в наборе, а каждая запись в configs переопределяет конкретный инструмент по имени, например bash.

permission_policy — это то, за счет чего среда выполнения превосходит обычный вызов модели. always_ask приостанавливает сессию и ожидает подтверждения от человека перед выполнением вызова. always_allow разрешает выполнение. Установка bash в значение always_ask означает, что агент не сможет выполнить shell-команду, пока вы не увидите её точный текст. Это тот же уровень контроля, который вы используете при безопасном запуске Claude Code на VPS.

Три режима песочницы и сценарии их использования

Вызовы инструментов, выполняющие код, запускаются внутри песочницы. Бэкенд выбирается для каждой среды через sandbox_provider в объекте config среды или в консоли в разделе Settings, а затем Sandbox. Среды создаются через API по адресу POST /v1/environments.

local запускает код как дочерний процесс среды выполнения на хосте от имени пользователя, под которым работает сама среда. Это режим по умолчанию; он приемлем, если вы единственный пользователь, а агент только читает файлы, принадлежащие вам. Это не изоляция. Вызов инструмента, удаляющий файлы, удалит ваши файлы, а вызов, читающий /etc/sandbase/runtime.env, прочитает ваш ключ провайдера.

docker запускает один контейнер на сессию.

{
  "sandbox_provider": "docker",
  "image": "node:22-slim",
  "resources": { "memory": "1g", "cpu": 1 }
}

Сессия получает собственную файловую систему, ограничение по памяти и долю ресурсов CPU, а контейнер удаляется вместе с сессией. Переключайтесь на этот режим, как только агент начинает выполнять код, написанный не вами. Минус в том, что пользователю среды выполнения нужен доступ к Docker socket, а членство в группе docker эквивалентно правам root на хосте. Контейнеры для каждой сессии имеют ту же структуру, что и самостоятельно размещаемые песочницы агентов с одним контейнером на запуск, поэтому рассуждения о том, к чему может получить доступ сбежавший процесс, применимы здесь в полной мере.

kubernetes запускает рабочую нагрузку сессии как под и управляет ею с помощью kubectl exec и kubectl cp. В образе среды выполнения должен присутствовать kubectl, а его ServiceAccount должен иметь права RBAC (управление доступом на основе ролей) на создание, удаление, получение, перечисление и отслеживание подов в целевом пространстве имен, а также доступ к подресурсу exec. Этот режим стоит настраивать только в том случае, если вы уже используете кластер.

Почему среда выполнения привязана к 127.0.0.1?

Потому что она запускается с отключенной аутентификацией. Среда выполнения включает аутентификацию по токену (bearer-token), когда существует хотя бы один API-ключ, а при чистой установке init их не создает. Привязка к 0.0.0.0 по умолчанию сделала бы неаутентифицированную среду выполнения агента, имеющую доступ к инструментам командной оболочки и вашему ключу провайдера, доступной из публичного интернета.

Поэтому, если вы хотите сделать её доступной, оставьте адрес привязки без изменений и выполните два других действия.

Во-первых, включите аутентификацию. Установите MANAGED_AGENTS_API_KEY в файле окружения службы или создайте ключ с помощью POST /v1/api-keys, который один раз вернет поле secret_key и больше никогда его не покажет. После этого клиенты должны отправлять Authorization: Bearer <key> в каждом запросе.

Во-вторых, установите перед ней обратный прокси-сервер и настройте там TLS (transport layer security) termination. Среда выполнения по своей архитектуре работает по обычному HTTP и предполагает, что обработкой сертификатов будет заниматься что-то другое.

server {
    listen 443 ssl;
    server_name agents.example.com;

    ssl_certificate     /etc/letsencrypt/live/agents.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/agents.example.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Connection "";
        proxy_buffering off;
        proxy_read_timeout 3600s;
    }
}

Две из этих строк — не просто оформление. proxy_buffering off важна, так как сессии передаются через server-sent events (SSE), и при включенной буферизации nginx удерживает ответ до заполнения буфера, из-за чего консоль ничего не показывает во время работы агента, а затем выводит всё сразу в конце. proxy_read_timeout 3600s важна, так как значение по умолчанию составляет 60 секунд, поэтому поток, в котором нет активности дольше минуты, закрывается прокси-сервером в середине выполнения, и этот сбой выглядит как падение среды выполнения.

На межсетевом экране откройте порты 22 и 443. Порт 3000 оставьте закрытым, так как прокси обращается к нему через loopback, и никто извне сервера не должен иметь к нему доступ.

Настройка Anthropic SDK на ваш собственный сервер

Среда выполнения реализует интерфейс в формате CMA /v1, поэтому клиент Anthropic SDK взаимодействует с ней при изменении всего одного поля.

import Anthropic from '@anthropic-ai/sdk';

const client = new Anthropic({
  apiKey: process.env.MANAGED_AGENTS_API_KEY ?? 'local-dev-key',
  baseURL: 'http://127.0.0.1:3000'
});

Она также принимает бета-заголовки, которые отправляют клиенты Claude Managed Agents, anthropic-beta: managed-agents-2026-04-01 и anthropic-beta: agent-memory-2026-07-22. Для локальной среды выполнения они необязательны. Они существуют для того, чтобы код, написанный для облачного развертывания, работал здесь без изменений.

Совместимость близка к полной, но не абсолютна. Ознакомьтесь с docs/api-matrix.md в локальной копии репозитория, прежде чем предполагать наличие какого-либо интерфейса, так как проект документирует свои ограничения, включая пользовательские инструменты на стороне клиента, которые всё ещё требуют именованной регистрации поверх текущего протокола событий и результатов.

Обычный HTTP работает так же хорошо и является самым быстрым способом убедиться, что среда выполнения активна:

curl -N -X POST http://127.0.0.1:3000/v1/sessions/SESSION_ID/messages \
  -H "Content-Type: application/json" \
  -d '{"content": "Hello", "stream": true}'

Корректный ответ представляет собой поток событий, который продолжает поступать. Если соединение прервалось, возобновите получение с последнего увиденного события вместо повторного воспроизведения всего цикла:

curl -N http://127.0.0.1:3000/v1/sessions/SESSION_ID/events/stream \
  -H "Last-Event-ID: EVENT_ID"

Этот возобновляемый поток — причина, по которой сессия сохраняется при закрытии ноутбука. События сохраняются на сервере, поэтому клиент воспроизводит журнал, а не хранит единственную копию данных.

Где на диске хранятся учетные данные, память и журналы аудита

Все, чем управляет среда выполнения, находится в каталоге .managed-agents/ в рабочей области.

.managed-agents/
├── config.yaml
├── data.db
├── logs/runtime.log
├── files/
├── skills/
├── snapshots/
└── sandbox/
  • data.db — это метаданные SQLite: агенты, сессии, записи хранилища учетных данных, записи хранилища памяти и API keys.
  • files/ содержит байты загруженных файлов, а skills/ — загруженные пакеты навыков (skill packages).
  • snapshots/ содержит снимки рабочих областей сессий, а sandbox/ — рабочие каталоги сессий в локальном режиме.
  • logs/runtime.log — первое место, куда стоит заглянуть, если что-то молча перестает работать.

Хранилища учетных данных — это группы секретов, каждая из которых добавляется с помощью auth_type, например environment_variable, и привязывается к сессии через vault_ids при создании сессии. Хранилища памяти содержат именованные записи, которые вы подключаете к сессии как memory_store с собственными настройками доступа и инструкциями. И то, и другое находится в data.db, что и составляет разницу между этим решением и обычным вызовом модели: среда выполнения запоминает данные между сессиями и записывает произошедшие события.

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

sudo systemctl stop sandbase
sudo tar czf /root/sandbase-$(date +%F).tgz -C /opt/sandbase/workspace .managed-agents
sudo systemctl start sandbase

Сначала остановите сервис. Копирование базы данных SQLite во время записи в нее средой выполнения может привести к созданию файла, который не откроется при восстановлении, и вы узнаете об этом в тот самый день, когда данные понадобятся. Если вы предпочитаете хранить YAML-файлы агентов в git, а состояние в другом месте, документация по развертыванию позволяет зафиксировать расположение состояния с помощью --data-dir в start.

Восстановление выполняется в обратном порядке: разверните ту же версию (tag) на чистом сервере, распакуйте архив в рабочую область и запустите сервис. Ваш ключ провайдера не попадет в архив, если вы использовали форму ${OPENAI_API_KEY}, поэтому храните его там, где он будет доступен.

Запуск под управлением systemd

Создайте для среды выполнения отдельного пользователя, чтобы вызовы инструментов в режиме локальной «песочницы» не выполнялись от вашего имени.

sudo adduser --system --group --no-create-home --home /opt/sandbase sandbase
sudo chown -R sandbase:sandbase /opt/sandbase

Сохраните этот файл как /etc/systemd/system/sandbase.service.

[Unit]
Description=SandBase Harness runtime
After=network-online.target

[Service]
User=sandbase
Group=sandbase
WorkingDirectory=/opt/sandbase/workspace
EnvironmentFile=/etc/sandbase/runtime.env
ExecStart=/usr/bin/node /opt/sandbase/sandbase-harness/dist/index.js start --host 127.0.0.1 --port 3000
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

В примере развертывания от разработчиков проекта вызывается бинарный файл managed-agents по пути PATH. При установке из исходного кода с тегами такой файл не создается, поэтому ExecStart запускает node непосредственно через точку входа сборки.

sudo systemctl daemon-reload
sudo systemctl enable --now sandbase
sudo systemctl status sandbase
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:3000/dashboard

Корректным результатом является active (running) из status и 200 из curl. При любом другом выводе сначала изучите journalctl -u sandbase -n 50, а затем .managed-agents/logs/runtime.log. enable --now — это критически важная часть, так как процесс, запущенный вручную, завершится после следующей перезагрузки системы.

Что может пойти не так и какие сообщения вы увидите

npm run build завершается без ошибки со стороны npm. На VPS с 1 GB оперативной памяти процесс компиляции TypeScript принудительно завершается OOM-killer (out-of-memory killer) ядра. Информация об этом попадает в системный журнал, а не в npm. Проверьте это с помощью journalctl -k | grep -i "out of memory": команда выведет строку с названием завершенного процесса node. Добавьте swap или выполните сборку на более мощном сервере, а затем скопируйте dist/.

Error: listen EADDRINUSE: address already in use 127.0.0.1:3000. Порт уже занят другим процессом. sudo ss -lntp | grep 3000 покажет, какой именно процесс его использует. Остановите этот процесс или запустите среду выполнения с --port 3001 и обновите настройки прокси.

Дашборд не загружается с вашего ноутбука. Это ожидаемое поведение, так как среда выполнения привязывается к интерфейсу loopback. Используйте SSH-туннель, описанный выше, или завершите настройку reverse proxy. Не пытайтесь исправить это с помощью --host 0.0.0.0, так как аутентификация отключена до тех пор, пока не создан ключ.

Docker-песочницы завершаются с ошибкой permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock. Пользователь sandbase не входит в группу docker. Исправьте это командой sudo usermod -aG docker sandbase и перезапустите сервис. Помните: эта группа имеет права root на хосте, поэтому данное действие частично нивелирует смысл использования отдельного пользователя для среды выполнения.

Kubernetes-песочницы завершаются с ошибкой Error from server (Forbidden). У ServiceAccount отсутствуют права на работу с подами или подресурсом exec. Проверьте это напрямую с помощью kubectl auth can-i create pods/exec -n <namespace>, которая вернет yes или no.

Каждый запрос возвращает 401 после добавления API-ключа. Аутентификация включается при создании первого ключа и применяется как к консоли, так и к API. Отправляйте Authorization: Bearer <key>. Если вы потеряли ключ, создайте новый, так как secret_key отображается только один раз и не сохраняется в читаемом виде.

Инструменты MCP-сервера не появляются в сессии. Сверьте mcp_server_name в блоке tools с name в mcp_servers, затем проверьте, доступен ли URL для среды выполнения с самого сервера с помощью curl -i <url>. MCP-сервер типа URL является сетевой зависимостью, а VPS разрешает имена и маршрутизирует трафик иначе, чем ваш ноутбук.

FAQ

Можно ли запустить SandBase Harness без ключа OpenAI или Anthropic?

Да, если у вас есть эндпоинт, совместимый с OpenAI. Среда выполнения поддерживает OpenAI, Anthropic и провайдеров, совместимых с API OpenAI, поэтому локальный сервер, поддерживающий API OpenAI, будет работать. Укажите провайдера рабочей области в .managed-agents/config.yaml и настройте api_key и эндпоинт на него. Среда выполнения не содержит встроенных моделей, поэтому для обработки вызовов необходим внешний сервис.

Безопасно ли открывать среду выполнения на публичном порту?

В стандартной конфигурации — нет. Она привязывается к 127.0.0.1:3000 и запускается без аутентификации, и простое изменение адреса привязки не решит проблему. Создайте API-ключ или задайте MANAGED_AGENTS_API_KEY, чтобы включить аутентификацию через bearer-токен. Затем установите nginx или Caddy перед приложением для TLS и закройте порт 3000 на межсетевом экране, чтобы единственный путь доступа проходил через прокси.

В чем разница между локальной песочницей, Docker и Kubernetes?

local запускает код инструментов как дочерний процесс среды выполнения на хосте, с правами пользователя, запустившего среду, и без изоляции. docker предоставляет каждой сессии отдельный контейнер с собственной файловой системой, лимитами памяти и долей CPU, который удаляется после завершения сессии. kubernetes запускает сессию как под и управляет ею с помощью kubectl exec, что требует наличия kubectl внутри образа среды выполнения, а также RBAC для подов и доступа к подресурсу exec в целевом пространстве имен.

Что именно нужно резервировать?

Каталог .managed-agents/ в рабочей области. В нем хранятся config.yaml, база данных SQLite data.db с агентами, сессиями, записями хранилища учетных данных и памяти, а также загруженные файлы, пакеты навыков и снимки сессий. Остановите службу перед копированием, чтобы SQLite не записывался в процессе создания архива. API-ключи провайдеров, указанные как ${OPENAI_API_KEY}, не входят в резервную копию, поэтому храните их отдельно.

Почему стоит клонировать тег v0.3.2, а не ветку main?

Тег — это зафиксированное состояние дерева исходного кода, поэтому ключи конфигурации и команды CLI, о которых вы читаете, будут соответствовать тому, что вы получите. main меняется, и ключ конфигурации может быть переименован в период между написанием руководства и его выполнением. Проект также предупреждает, что пакет managed-agents в npm не относится к данному проекту, поэтому npx managed-agents установит что-то другое. Релиз v0.3.1 существует в основном для того, чтобы заменить этот быстрый старт через npm на путь к зафиксированному исходному коду с тегом.