Como alojar o runtime de agentes SandBase
Execute o SandBase Harness v0.3.2 na sua VPS com instalação fixa, YAML de agentes, servidores MCP, modos sandbox e Anthropic SDK apontado para o seu servidor.
O que você obtém ao alojar o runtime de agentes SandBase
Alojamento próprio do runtime de agentes SandBase significa executar o SandBase Harness num servidor seu. Assim, as sessões, as credenciais, a memória e os registos de auditoria ficam no seu disco, e não no disco de terceiros. É um serviço Node. Escuta em 127.0.0.1:3000, disponibiliza uma API HTTP /v1 e uma consola web, e mantém o estado numa base de dados SQLite junto dos ficheiros dos agentes.
A API /v1 segue o modelo da Claude Managed Agents (CMA), a API alojada de agentes geridos. É isso que torna este runtime interessante nos dois sentidos: pode escrever código com o Anthropic SDK e apontar o respetivo baseURL para o seu próprio servidor. Mais tarde, pode mover o mesmo código para uma implementação alojada.
O SandBase Harness não inclui um modelo. Faz chamadas a um modelo. Em agosto de 2026, é compatível com OpenAI, Anthropic e endpoints compatíveis com OpenAI. Isto inclui gateways alojados localmente e fornecedores como o DeepSeek V4. Continua a precisar de uma chave de API ou de um servidor local que disponibilize a API OpenAI.
O que é necessário antes de começar
- Uma VPS com Ubuntu 24.04 e pelo menos 2 GB de RAM. A compilação de TypeScript é a etapa mais pesada da instalação.
- Node.js 22 ou mais recente e npm 10 ou mais recente. Estes são os requisitos mínimos definidos pelo projeto.
git, além de uma chave de API para o fornecedor do modelo que pretende utilizar.- Docker, mas apenas se quiser sandboxes de contentor por sessão.
O Ubuntu 24.04 disponibiliza Node 18.19 no seu próprio repositório. Essa versão é inferior ao mínimo exigido, por isso instale o Node a partir do NodeSource.
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
sudo apt install -y nodejs git
node -v
npm -vnode -v deve apresentar v22 ou superior, e npm -v deve apresentar 10 ou superior. Se node -v ainda apresentar v18.19.1, o pacote da distribuição continua instalado e a ser encontrado primeiro em PATH. Remova-o antes de continuar, porque a compilação utiliza a versão de node encontrada pela shell.
Instalar o SandBase a partir da tag v0.3.2
Instale a partir de uma tag, nunca de um branch que muda. Um clone simples de main fornece o que foi incluído há uma hora, e as chaves de configuração abaixo podem não corresponder a essa versão. v0.3.2 é a tag atual em 16 August 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 buildUse npm ci, não npm install. ci instala as versões exatas registadas no lockfile submetido, para que a sua árvore corresponda à árvore testada pelos mantenedores. npm install pode resolver versões mais recentes. É assim que uma tag fixada deixa silenciosamente de estar fixada.
Agora crie um workspace. O workspace é um diretório separado que contém os ficheiros do agente e todo o estado de execução. Mantê-lo fora do checkout do código-fonte permite obter uma tag mais recente sem alterar os seus dados.
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 startinit escreve um diretório .managed-agents/ no workspace. start inicia a consola em http://127.0.0.1:3000/dashboard e a API em http://127.0.0.1:3000/v1. Nenhuma das duas está acessível a partir do seu portátil, o que está correto e é explicado mais abaixo. Por enquanto, aceda à consola através de SSH:
ssh -N -L 3000:127.0.0.1:3000 you@your-serverEsse caminho node .../dist/index.js longo torna-se cansativo, por isso atribua-lhe um nome.
alias sandbase='node /opt/sandbase/sandbase-harness/dist/index.js'Os comandos abaixo são escritos como sandbase <command> com base nisso.
Não instale pelo npm
O próprio projeto informa isto na documentação de instalação: o pacote managed-agents sem escopo disponível no npm não pertence a este projeto. Portanto, npx managed-agents e npm install -g managed-agents obtêm algo que não está relacionado com o runtime pretendido. Instale a partir do código-fonte identificado por uma tag no GitHub até os mantenedores anunciarem um pacote oficial com escopo. Isto não é uma observação secundária na história do projeto: a v0.3.1 existe principalmente para substituir o início rápido antigo do npm pelo caminho baseado no código-fonte identificado por uma tag fixa.
Aponte o workspace para um fornecedor de modelos
init escreve .managed-agents/config.yaml. É configurado um fornecedor para todo o workspace, e cada agente escolhe depois IDs de modelos concretos.
model:
provider: openai
api_key: ${OPENAI_API_KEY}
storage:
metadata:
provider: sqlite
options: {}
artifacts:
provider: local
options:
base_path: filesO formulário ${OPENAI_API_KEY} obtém o valor do ambiente do processo. Assim, a chave não fica no ficheiro de configuração nem em nenhuma cópia de segurança desse ficheiro. Coloque-a num ficheiro de ambiente que apenas root possa ler, porque o systemd lê EnvironmentFile= como root antes de remover privilégios.
sudo install -d -m 750 /etc/sandbase
sudo touch /etc/sandbase/runtime.env
sudo chmod 600 /etc/sandbase/runtime.envAbra esse ficheiro num editor e adicione uma linha, OPENAI_API_KEY=sk-.... As chaves dos fornecedores devem ficar aqui. Os segredos que um agente utiliza durante uma sessão devem ficar nos cofres de credenciais do runtime. Esse é um problema diferente, com um raio de impacto diferente. Vale a pena ler manter os segredos fora dos agentes de IA antes de colar um token de produção em qualquer um dos locais.
O YAML do agente: mcp_servers, tools e políticas de permissões
Os agentes são definidos como ficheiros YAML no diretório agents/ da workspace. Esta é a parte do runtime onde passará efetivamente a maior parte do tempo.
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-commanderCarregue-o e confirme que foi importado:
sandbase reload
sandbase list
sandbase chat agent_assistant --message "hello"reload importa o YAML inicial para o SQLite. list deverá agora mostrar o agente com um ID. Se list não o mostrar, o ficheiro não foi analisado, e o motivo é registado em .managed-agents/logs/runtime.log.
mcp_servers declara endpoints MCP (model context protocol). type: url significa que o runtime comunica por HTTP com um servidor executado noutro local. Por isso, qualquer servidor que já opere pode ser usado aqui, incluindo servidores MCP alojados no mesmo VPS que o runtime.
Declarar um servidor não disponibiliza as respetivas ferramentas ao agente. A lista tools faz isso, através de uma entrada mcp_toolset cujo mcp_server_name corresponde ao name acima. Se o agente se comportar como se as ferramentas MCP não existissem, compare essas duas strings caractere a caractere antes de procurar noutro local.
agent_toolset_20260401 é o conjunto de ferramentas integrado. O sufixo com a data é uma versão do schema. Assim, um agente fixado nessa versão mantém as definições das ferramentas para as quais foi escrito. default_config define a política para todas as ferramentas do conjunto, e cada entrada em configs substitui essa política para uma ferramenta específica pelo nome, bash no exemplo.
permission_policy é onde um runtime se distingue de uma simples chamada ao modelo. always_ask interrompe a sessão e aguarda que uma pessoa aprove a chamada antes da sua execução. always_allow permite a execução. Definir bash como always_ask significa que o agente não pode executar um comando shell sem que veja primeiro o comando exato. Este é o mesmo controlo que usaria ao executar o Claude Code com segurança num VPS.
Os três modos de sandbox e quando usar cada um
As chamadas de ferramentas que executam código são executadas dentro de uma sandbox. O backend é escolhido por ambiente, através de sandbox_provider no objeto config do ambiente ou em Settings e depois Sandbox na consola. Os ambientes são criados através da API em POST /v1/environments.
local executa o código como um processo filho do runtime, no host, com o utilizador do próprio runtime. É o modo predefinido e é razoável enquanto só existir um utilizador e o agente ler apenas ficheiros que lhe pertencem. Não fornece isolamento. Uma chamada de ferramenta que elimina ficheiros elimina os seus ficheiros, e uma chamada de ferramenta que lê /etc/sandbase/runtime.env lê a sua chave do fornecedor.
docker inicia um contentor por sessão.
{
"sandbox_provider": "docker",
"image": "node:22-slim",
"resources": { "memory": "1g", "cpu": 1 }
}A sessão recebe o seu próprio sistema de ficheiros, o seu próprio limite de memória e a sua própria quota de CPU, e o contentor é removido com a sessão. Mude para este modo assim que um agente executar código que não foi escrito por si. O custo é que o utilizador do runtime precisa de acesso ao socket do Docker, e a pertença ao grupo docker equivale a root no host. Os contentores por sessão têm a mesma estrutura que as sandboxes de agentes autoalojadas com um contentor por execução, pelo que o raciocínio sobre aquilo a que um processo que escape poderia aceder se aplica aqui sem alterações.
kubernetes executa a carga de trabalho da sessão como um pod e controla-a com kubectl exec e kubectl cp. A imagem do runtime precisa de ter kubectl disponível, e a respetiva ServiceAccount precisa de permissões RBAC (controlo de acesso baseado em funções) para criar, eliminar, obter, listar e monitorizar pods no namespace de destino, além do subrecurso exec. Este modo só compensa a configuração se já executar um cluster.
Por que o runtime está vinculado a 127.0.0.1?
Porque inicia com a autenticação desativada. O runtime ativa a autenticação com bearer token quando existe pelo menos uma chave de API, e uma init nova não cria nenhuma. Vincular a 0.0.0.0 com essa predefinição exporia na Internet pública um runtime de agente sem autenticação, com ferramentas de shell e a chave do seu provedor.
Quando quiser que ele seja acessível, mantenha o endereço de vinculação e faça outras duas coisas.
Primeiro, ative a autenticação. Defina MANAGED_AGENTS_API_KEY no ficheiro de ambiente do serviço ou crie uma chave com POST /v1/api-keys. O comando devolve um campo secret_key uma vez e nunca o mostra novamente. Os clientes passam então Authorization: Bearer <key> em cada pedido.
Segundo, coloque um reverse proxy à frente e termine o TLS (transport layer security) nele. O runtime disponibiliza HTTP simples por conceção e espera que outro componente trate dos certificados.
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;
}
}Duas dessas linhas não são decorativas. proxy_buffering off é importante porque as sessões são transmitidas por server-sent events (SSE). Com o buffering ativado, o nginx mantém a resposta até o seu buffer ficar cheio. Por isso, a consola não mostra nada enquanto o agente trabalha e depois apresenta tudo de uma vez no final. proxy_read_timeout 3600s é importante porque a predefinição é 60 segundos. Assim, um stream que permaneça inativo durante mais de um minuto é encerrado pelo proxy a meio de uma execução. A falha parece então um crash do runtime.
Na firewall, abra 22 e 443. Mantenha 3000 fechado, porque o proxy o acede através do loopback e nada fora do servidor deve aceder a essa porta.
Aponte o Anthropic SDK para o seu próprio servidor
O runtime implementa uma superfície compatível com CMA /v1, por isso um cliente do Anthropic SDK comunica com ele alterando apenas um campo.
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'
});Também aceita os cabeçalhos beta enviados pelos clientes Claude Managed Agents, anthropic-beta: managed-agents-2026-04-01 e anthropic-beta: agent-memory-2026-07-22. Eles são opcionais num runtime local. Existem para que o código escrito para uma implementação alojada funcione aqui sem alterações.
A compatibilidade é próxima, mas não total. Leia docs/api-matrix.md no checkout antes de assumir que uma determinada superfície existe, porque o projeto documenta aí as suas próprias lacunas, incluindo as ferramentas personalizadas do cliente, que ainda precisam de um registo com nome acima do protocolo atual de resultados de eventos.
O HTTP simples também funciona e é a forma mais rápida de confirmar que o runtime está ativo:
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}'Uma resposta saudável é um fluxo de eventos que continua a chegar. Se a ligação cair, retome a partir do último evento recebido em vez de repetir todo o turno:
curl -N http://127.0.0.1:3000/v1/sessions/SESSION_ID/events/stream \
-H "Last-Event-ID: EVENT_ID"Esse fluxo retomável permite que uma sessão sobreviva ao encerramento de um portátil. Os eventos são persistidos no servidor, por isso o cliente está a reproduzir um log em vez de manter a única cópia.
Onde ficam no disco as credenciais, a memória e os registos de auditoria
Tudo o que o runtime gere fica em .managed-agents/ no workspace.
.managed-agents/
├── config.yaml
├── data.db
├── logs/runtime.log
├── files/
├── skills/
├── snapshots/
└── sandbox/data.dbé a base de dados SQLite com os metadados: agentes, sessões, entradas do cofre de credenciais, entradas do armazenamento de memória e chaves de API.files/contém os dados binários dos ficheiros carregados eskills/contém os pacotes de skills carregados.snapshots/contém os snapshots dos workspaces das sessões esandbox/contém os diretórios de trabalho das sessões em modo local.logs/runtime.logé o primeiro local a consultar quando algo não faz nada silenciosamente.
Os cofres de credenciais são grupos de segredos. Cada grupo é adicionado com um auth_type, como environment_variable, e associado a uma sessão através de vault_ids quando a sessão é criada. Os armazenamentos de memória contêm entradas nomeadas que monta numa sessão como um memory_store, com a sua própria configuração de acesso e instruções. Ambos ficam em data.db. Esta é precisamente a diferença entre este sistema e uma chamada direta ao modelo: o runtime mantém informação entre sessões e regista o que aconteceu.
Como está tudo num único diretório, faça uma cópia de segurança desse diretório como uma unidade.
sudo systemctl stop sandbase
sudo tar czf /root/sandbase-$(date +%F).tgz -C /opt/sandbase/workspace .managed-agents
sudo systemctl start sandbasePare primeiro o serviço. Copiar uma base de dados SQLite enquanto o runtime escreve nela pode produzir um ficheiro que não abre quando for restaurado. Só descobrirá isso no dia em que precisar da cópia. Se preferir manter o YAML dos agentes no git e o estado noutro local, a documentação de deployment permite fixar a localização do estado com --data-dir em start.
A restauração é o processo inverso: faça checkout da mesma tag num servidor novo, extraia o arquivo para o workspace e inicie o serviço. A chave do provider não fica no arquivo se tiver usado a forma ${OPENAI_API_KEY}. Guarde-a num local ao qual continue a ter acesso.
Execute-o no systemd
Dê ao runtime o seu próprio utilizador para que uma chamada de ferramenta no modo sandbox local não possa agir como o seu utilizador.
sudo adduser --system --group --no-create-home --home /opt/sandbase sandbase
sudo chown -R sandbase:sandbase /opt/sandbaseGuarde isto como /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.targetO exemplo de implementação do projeto chama um binário managed-agents em PATH. Uma instalação a partir do código-fonte marcado não cria esse binário, por isso ExecStart executa node diretamente contra o ponto de entrada compilado.
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/dashboardUm resultado saudável é active (running) de status e 200 de curl. Para qualquer outro resultado, leia primeiro journalctl -u sandbase -n 50 e depois .managed-agents/logs/runtime.log. enable --now é a parte que importa, porque um processo iniciado manualmente desaparece depois do próximo reboot.
O que falha e a mensagem apresentada
npm run build é terminado sem erro do npm. Num VPS com 1 GB, a compilação TypeScript é interrompida pelo mecanismo do kernel que termina processos por falta de memória. O kernel regista o evento no log, não o npm. Confirme com journalctl -k | grep -i "out of memory", que apresenta uma linha com o nome do processo node terminado. Adicione swap ou faça a compilação numa instância maior e copie dist/ para o VPS.
Error: listen EADDRINUSE: address already in use 127.0.0.1:3000. Outro processo já está a utilizar a porta. sudo ss -lntp | grep 3000 identifica-o. Pare esse processo ou inicie o runtime com --port 3001 e atualize o proxy.
O dashboard não carrega no seu portátil. Esse é o comportamento esperado, porque o runtime está associado à interface de loopback. Use o túnel SSH acima ou conclua a configuração do reverse proxy. Não corrija o problema com --host 0.0.0.0, porque a autenticação fica desativada até existir uma chave.
As sandboxes Docker falham com permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock. O utilizador sandbase não pertence ao grupo docker. Corrija com sudo usermod -aG docker sandbase e reinicie o serviço. Tenha em conta o que concedeu: esse grupo equivale a root no host, anulando parte do motivo para atribuir ao runtime um utilizador próprio.
As sandboxes Kubernetes falham com Error from server (Forbidden). A ServiceAccount não tem permissões sobre pods ou sobre o subrecurso exec. Verifique diretamente com kubectl auth can-i create pods/exec -n <namespace>, que responde yes ou no.
Todos os pedidos devolvem 401 depois de adicionar uma chave de API. A autenticação é ativada quando existe a primeira chave e aplica-se tanto à consola como à API. Envie Authorization: Bearer <key>. Se perdeu a chave, crie outra, porque secret_key é devolvida uma única vez e não é armazenada num formato legível.
As ferramentas de um servidor MCP nunca aparecem numa sessão. Compare o mcp_server_name no bloco tools com o name em mcp_servers. Em seguida, confirme com curl -i <url> que o runtime consegue aceder ao URL a partir do próprio servidor. Um servidor MCP baseado num URL é uma dependência de rede. Um VPS resolve nomes e encaminha tráfego de forma diferente do seu portátil.
FAQ
Posso executar o SandBase Harness sem uma chave OpenAI ou Anthropic?
Sim, se tiver um endpoint compatível com OpenAI. O runtime suporta fornecedores OpenAI, Anthropic e compatíveis com OpenAI, portanto um servidor local que implemente a API OpenAI funciona. Defina o fornecedor do workspace em .managed-agents/config.yaml e indique api_key e o endpoint para esse fornecedor. O runtime não inclui nenhum modelo próprio, por isso algum serviço tem de responder às chamadas.
É seguro expor o runtime numa porta pública?
Não com a configuração instalada. Ele faz bind em 127.0.0.1:3000 e inicia com a autenticação desativada; a correção não é usar um endereço de bind diferente. Crie uma chave de API ou defina MANAGED_AGENTS_API_KEY para ativar a autenticação por bearer token. Depois, coloque nginx ou Caddy à frente para gerir o TLS e mantenha a porta 3000 bloqueada no firewall, para que o único acesso seja através do proxy.
Qual é a diferença entre os sandboxes local, Docker e Kubernetes?
local executa o código das ferramentas como um processo filho do runtime no host, com as permissões do utilizador do runtime e sem isolamento. docker atribui a cada sessão o seu próprio container, com o seu próprio sistema de ficheiros, limite de memória e quota de CPU, e remove-o quando a sessão termina. kubernetes executa a sessão como um pod e controla-a com kubectl exec, que requer kubectl dentro da imagem do runtime e RBAC para pods, além do subrecurso exec no namespace de destino.
Do que preciso exatamente de fazer backup?
Do diretório .managed-agents/ no workspace. Ele contém config.yaml, a base de dados SQLite data.db com agentes, sessões, entradas do cofre de credenciais e entradas de memória, além de ficheiros carregados, pacotes de skills e snapshots de sessões. Pare o serviço antes de o copiar, para que o SQLite não seja escrito durante a criação do arquivo. As chaves de API dos fornecedores referenciadas como ${OPENAI_API_KEY} não ficam incluídas no backup, por isso armazene-as separadamente.
Por que motivo devo clonar a tag v0.3.2 em vez de main?
Uma tag é uma árvore fixa, por isso as chaves de configuração e os comandos CLI descritos são os que irá efetivamente obter. main muda, e uma chave de configuração pode ser renomeada entre o momento em que um guia é escrito e o momento em que o executa. O projeto também avisa que o pacote managed-agents sem escopo no npm não é este projeto, portanto npx managed-agents instala algo sem relação. A versão v0.3.1 existe principalmente para substituir esse início rápido do npm pelo caminho baseado no código-fonte da tag fixada.