SSD Nodes Learn Hosting plans →
Guias Matt ConnorPor Matt Connor · Atualizado 2026-08-28

Como hospedar o runtime de agentes SandBase

Execute o SandBase Harness v0.3.2 na sua VPS com instalacao por tag, YAML de agentes, servidores MCP, modos sandbox e Anthropic SDK apontando para seu servidor.

O que obtém ao alojar o runtime de agentes SandBase

Alojamento próprio do runtime de agentes SandBase significa executar o SandBase Harness num servidor que controla, para que as sessões, credenciais, memória e registos de auditoria fiquem no seu disco, em vez de ficarem no servidor 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 do Claude Managed Agents (CMA), a API de agentes geridos alojada. É isso que torna este runtime interessante nos dois sentidos: pode escrever código com o Anthropic SDK e apontar o seu baseURL para o seu próprio servidor, passando depois o mesmo código para uma implementação alojada.

O SandBase Harness não inclui um modelo. Faz chamadas para um modelo. Em agosto de 2026, suporta OpenAI, Anthropic e endpoints compatíveis com OpenAI, abrangendo gateways alojados localmente e fornecedores como 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 do 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. Esta 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 uma versão mais recente, e npm -v deve apresentar 10 ou uma versão mais recente. Se node -v ainda apresentar v18.19.1, o pacote da distribuição continua instalado e a ser escolhido através de PATH. Remova-o antes de continuar, porque a compilação utiliza a versão de node que a shell encontrar.

Instalar o SandBase a partir da tag v0.3.2

Instale a partir de uma tag, nunca de uma branch móvel. Um clone simples de main fornece o que foi integrado 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 de agosto de 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 cria um diretório .managed-agents/ no workspace. start disponibiliza a consola em http://127.0.0.1:3000/dashboard e a API em http://127.0.0.1:3000/v1. Nenhuma delas 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 longo node .../dist/index.js torna-se incómodo, 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 a partir do npm

O projeto informa isto na própria documentação de instalação: o pacote managed-agents sem escopo disponível no npm não pertence a este projeto. Por isso, npx managed-agents e npm install -g managed-agents obtêm algo que não tem relação com o runtime pretendido. Instale a partir do código-fonte marcado no GitHub até os mantenedores anunciarem um pacote oficial com escopo. Isto não é uma pequena nota na história do projeto: a v0.3.1 existe principalmente para substituir o início rápido antigo do npm pelo caminho fixado do código-fonte marcado.

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 mantenha 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. É nesta parte do runtime que vai passar a maior parte do tempo. As chaves ficam mais claras depois de escrever manualmente um pequeno ciclo de agente, porque cada uma controla algo que, de outra forma, teria de programar: o prompt do sistema, a lista de ferramentas e a verificação executada antes de uma ferramenta ser chamada.

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 deve agora mostrar o agente com um ID. Se list não o mostrar, o ficheiro não foi analisado, e .managed-agents/logs/runtime.log regista o motivo.

mcp_servers declara endpoints do MCP (model context protocol). type: url significa que o runtime comunica por HTTP com um servidor executado noutro local, por isso tudo o que já administra funciona aqui, incluindo servidores MCP alojados no mesmo VPS que o runtime. A pesquisa na Web costuma ser a primeira ferramenta que as pessoas procuram, e vale a pena ler sobre disponibilizar ao agente a sua própria instância SearXNG antes de configurar uma, porque uma ferramenta que devolve páginas escritas por terceiros coloca texto não fiável diretamente no contexto do modelo. A integração inicial mais segura tem a forma oposta: um endpoint só de leitura sobre dados que já possui. É isso que openGym disponibiliza junto do próprio registo de treinos, para que o agente possa responder a perguntas sobre o seu histórico de treino sem o poder alterar.

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 estas duas strings carácter a carácter antes de procurar a causa noutro local.

agent_toolset_20260401 é o conjunto de ferramentas integrado. O sufixo com a data é uma versão do esquema, por isso um agente fixado nessa versão mantém as definições de 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 identificada pelo nome, bash no exemplo.

permission_policy é onde um runtime se torna mais útil do que uma simples chamada ao modelo. always_ask interrompe a sessão e aguarda que uma pessoa aprove a chamada antes de a executar. always_allow permite a execução. Definir bash como always_ask significa que o agente não pode executar um comando de shell sem que veja primeiro o comando exato, que é o mesmo controlo que usaria ao executar o Claude Code com segurança num VPS. Se também executar o DeepSeek Harness, os mesmos controlos estão disponíveis como add-ons, e não como chaves YAML; os plugins que limitam os orçamentos e controlam as chamadas de ferramentas são o equivalente mais próximo deste bloco.

Os três modos de sandbox e quando utilizar cada um

As chamadas de ferramentas que executam código são realizadas 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 for o único utilizador e o agente apenas ler ficheiros que lhe pertencem. Não fornece isolamento. Uma chamada de ferramenta que elimine ficheiros elimina os seus ficheiros, e uma chamada de ferramenta que leia /etc/sandbase/runtime.env lê a sua chave do provider.

docker inicia um container por sessão.

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

A sessão tem o seu próprio sistema de ficheiros, limite de memória e quota de CPU, e o container é 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 containers por sessão têm a mesma estrutura que sandboxes de agentes self-hosted com um container por execução, pelo que o raciocínio sobre o que um processo que escapasse poderia alcançar 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, e a sua 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á ligado a 127.0.0.1?

Porque ele 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. Ligar 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 provider.

Por isso, quando quiser que ele seja acessível, mantenha o endereço de ligaçã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, que devolve um campo secret_key uma única vez e nunca o mostra novamente. Depois, os clientes enviam Authorization: Bearer <key> em todos os pedidos. Uma chave corresponde a uma identidade partilhada. Se o que pretende é um agente isolado separado por colega, com as chaves dos providers mantidas num único gateway, OneCLI foi criado para esse modelo.

Segundo, coloque um reverse proxy à frente e termine o TLS (transport layer security) nesse proxy. O runtime serve 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 usam server-sent events (SSE) e, com o buffering ativado, o nginx mantém a resposta até o buffer ficar cheio. Assim, a consola não mostra nada enquanto o agente trabalha e depois apresenta tudo de uma vez no fim. proxy_read_timeout 3600s é importante porque a predefinição é 60 segundos. Por isso, um stream que fique silencioso durante mais de um minuto é fechado pelo proxy a meio de uma interação, e a falha parece um crash do runtime.

Na firewall, abra 22 e 443. Mantenha 3000 fechado, porque o proxy acede a essa porta através do loopback e nada fora do servidor deve fazê-lo.

Aponte o Anthropic SDK para o seu próprio servidor

O runtime implementa uma superfície com o formato CMA, /v1, portanto 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 quando se utiliza um 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. Consulte 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 lado 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 é a razão pela qual uma sessão sobrevive ao encerramento do portátil. Os eventos são persistidos no servidor, portanto o cliente está a reproduzir um log, em vez de manter a única cópia.

Onde ficam as credenciais, a memória e os registos de auditoria no disco

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 contém os metadados SQLite: agentes, sessões, entradas do cofre de credenciais, entradas do armazenamento de memória e chaves de API.
  • files/ contém os bytes dos ficheiros carregados e skills/ contém os pacotes de skills carregados.
  • snapshots/ contém os snapshots do workspace 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 segredo é 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 identificadas por nome, que são montadas numa sessão como um memory_store, com a sua própria definiçã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 tudo está num único diretório, faça uma cópia de segurança desse diretório como um todo.

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 está a escrever nela pode produzir um ficheiro que não abre durante a restauração. Só descobrirá isso quando 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 segue o processo inverso: faça checkout da mesma tag numa máquina nova, extraia o arquivo para o workspace e inicie o serviço. A chave do provider não estará no arquivo se tiver usado a forma ${OPENAI_API_KEY}. Por isso, guarde-a num local ao qual ainda terá acesso.

Execute-o com systemd

Dê ao runtime um utilizador próprio para que uma chamada de ferramenta no modo sandbox local não possa atuar 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 próprio projeto chama um binário managed-agents em PATH. Uma instalação a partir de código-fonte marcado não cria esse binário. Por isso, ExecStart executa node diretamente no 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 correto é 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 após o próximo reboot.

O que falha e a mensagem apresentada

npm run build é terminado sem qualquer erro do npm. Num VPS de 1 GB, a compilação TypeScript é interrompida pelo mecanismo de falta de memória do kernel, que regista o evento no log do kernel em vez de o comunicar ao npm. Confirme com journalctl -k | grep -i "out of memory", que apresenta uma linha com o nome do processo node terminado. Adicione swap ou compile numa instância maior e copie dist/.

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 ao loopback. Use o túnel SSH acima ou conclua a configuração do reverse proxy. Não corrija isto com --host 0.0.0.0, porque a autenticação está 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 para pods ou para 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. Depois, confirme que o runtime consegue alcançar o URL a partir do próprio servidor com curl -i <url>. Um servidor MCP do tipo URL é uma dependência de rede, e um VPS resolve nomes e encaminha tráfego de forma diferente do seu portátil.

FAQ

Posso executar o SandBase Harness sem uma chave da OpenAI ou da Anthropic?

Sim, se tiver um endpoint compatível com a OpenAI. O runtime suporta fornecedores OpenAI, Anthropic e compatíveis com a OpenAI, por isso um servidor local que implemente a API da OpenAI funciona. Defina o fornecedor do workspace em .managed-agents/config.yaml e indique api_key e o endpoint nessa configuração. 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 associa-se a 127.0.0.1:3000 e inicia com a autenticação desativada. A correção não é usar outro endereço de associação. Crie uma chave de API ou defina MANAGED_AGENTS_API_KEY para ativar a autenticação com bearer token. Depois, coloque nginx ou Caddy à frente para tratar do TLS e mantenha a porta 3000 fechada na firewall, para que o único acesso seja através do proxy.

Qual é a diferença entre as 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 contentor, 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 gere-a com kubectl exec. Isto requer kubectl dentro da imagem do runtime, além de RBAC para pods e para o subrecurso exec no namespace de destino.

O que preciso exatamente de incluir na cópia de segurança?

O 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 alterado durante a criação do arquivo. As chaves de API dos fornecedores referenciadas como ${OPENAI_API_KEY} não ficam dentro da cópia de segurança, por isso armazene-as separadamente.

Por que motivo deve clonar a tag v0.3.2 em vez de main?

Uma tag representa 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, por isso npx managed-agents instala algo não relacionado. A release v0.3.1 existe principalmente para substituir esse quick start do npm pelo caminho baseado no código-fonte da tag fixada.