SSD Nodes Learn 🎉 VPS desde $5.50/mês
Guias Matt ConnorPor Matt Connor

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 -v

node -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 build

Use 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 start

init 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-server

Esse 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: files

O 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.env

Abra 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-commander

Carregue-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 e skills/ contém os pacotes de skills carregados.
  • snapshots/ contém os snapshots dos workspaces das sessões e sandbox/ 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 sandbase

Pare 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/sandbase

Guarde 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.target

O 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/dashboard

Um 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.