SandBase agent runtime zelf hosten op een VPS
Installeer SandBase Harness v0.3.2 op uw eigen server. Leer hoe u de agent YAML configureert, MCP servers koppelt en de Anthropic SDK naar uw lokale endpoint laat wijzen.
Wat u krijgt wanneer u de SandBase agent runtime zelf host
Het zelf hosten van de SandBase agent runtime betekent dat u SandBase Harness uitvoert op een server die u beheert. Hierdoor staan sessies, inloggegevens, geheugen en audit-logs op uw eigen schijf in plaats van op die van een externe partij. Het is een Node-service. Deze luistert op 127.0.0.1:3000, biedt een /v1 HTTP API en een webconsole aan, en slaat de status op in SQLite naast uw agent-bestanden.
De /v1 API is gemodelleerd naar Claude Managed Agents (CMA), de gehoste managed-agent API. Dit maakt deze runtime in beide richtingen interessant: u kunt code schrijven voor de Anthropic SDK en de baseURL daarvan naar uw eigen server laten wijzen, om vervolgens dezelfde code later naar een gehoste omgeving te verplaatsen.
SandBase Harness levert geen model mee. Het roept een model aan. Sinds augustus 2026 ondersteunt het OpenAI, Anthropic en OpenAI-compatibele eindpunten. Dit omvat zelf-gehoste gateways en providers zoals DeepSeek V4. U dient zelf een API-sleutel aan te leveren, of een lokale server die de OpenAI API spreekt.
Wat u nodig heeft voordat u begint
- Een VPS met Ubuntu 24.04 en ten minste 2 GB RAM. Het builden van TypeScript is de meest intensieve stap van de installatie.
- Node.js 22 of nieuwer, en npm 10 of nieuwer. Dit zijn de harde minimumvereisten van het project.
git, plus een API-sleutel voor de modelprovider die u wilt gebruiken.- Docker, maar alleen als u per sessie container-sandboxes wilt gebruiken.
Ubuntu 24.04 levert Node 18.19 in de eigen repository, wat onder het minimum ligt. Gebruik daarom Node van NodeSource.
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
sudo apt install -y nodejs git
node -v
npm -vnode -v hoort v22 of hoger weer te geven en npm -v hoort 10 of hoger weer te geven. Als node -v nog steeds v18.19.1 weergeeft, is het distributiepakket nog steeds geïnstalleerd en heeft dit voorrang in de PATH. Verwijder dit pakket voordat u verdergaat, omdat de build wordt uitgevoerd met de node die de shell als eerste vindt.
SandBase installeren vanaf de v0.3.2 tag
Installeer vanaf een tag, nooit vanaf een bewegende branch. Een bare clone van main geeft u alles wat een uur geleden is toegevoegd, en de onderstaande configuratiesleutels komen mogelijk niet overeen. v0.3.2 is de huidige tag per 16 augustus 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 buildGebruik npm ci, niet npm install. ci installeert de exacte versies die zijn vastgelegd in het gecommitteerde lockfile, zodat uw tree overeenkomt met de tree die de beheerders hebben getest. npm install mag nieuwere versies oplossen, waardoor een vastgezette tag stilletjes niet meer vastgezet is.
Maak nu een workspace aan. De workspace is een aparte map die uw agent-bestanden en alle runtime-status bevat. Door deze buiten de source checkout te houden, kunt u een nieuwere tag ophalen zonder uw data aan te raken.
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 schrijft een .managed-agents/ map in de workspace. start start de console op http://127.0.0.1:3000/dashboard en de API op http://127.0.0.1:3000/v1. Beide zijn nog niet bereikbaar vanaf uw laptop; dit is correct en wordt verderop behandeld. Bereik de console voor nu via SSH:
ssh -N -L 3000:127.0.0.1:3000 you@your-serverDat lange node .../dist/index.js pad wordt vermoeiend, dus geef het een naam.
alias sandbase='node /opt/sandbase/sandbase-harness/dist/index.js'De onderstaande commando's zijn op die basis geschreven als sandbase <command>.
Installeer het niet via npm
Het project vermeldt dit in de eigen installatiedocumentatie: het unscoped managed-agents-pakket dat zichtbaar is op npm is niet dit project. Daarom halen npx managed-agents en npm install -g managed-agents iets op dat geen verband houdt met de runtime die u wilt gebruiken. Installeer vanuit de getagde GitHub-broncode totdat de beheerders een officieel scoped pakket aankondigen. Dit is geen onbelangrijke voetnoot in de geschiedenis van het project: v0.3.1 bestaat hoofdzakelijk om de oude npm quick start te vervangen door het pad naar de vastgezette getagde broncode.
Wijs de workspace toe aan een modelprovider
init schrijft .managed-agents/config.yaml. Er wordt één provider geconfigureerd voor de gehele workspace, waarna individuele agents concrete model-ID's kiezen.
model:
provider: openai
api_key: ${OPENAI_API_KEY}
storage:
metadata:
provider: sqlite
options: {}
artifacts:
provider: local
options:
base_path: filesHet ${OPENAI_API_KEY}-formulier haalt de waarde uit de procesomgeving. Hierdoor blijft de sleutel buiten het configuratiebestand en buiten elke back-up die u van dat bestand maakt. Plaats deze in een omgevingsbestand dat alleen door root kan worden gelezen, aangezien systemd EnvironmentFile= als root inleest voordat de rechten worden beperkt.
sudo install -d -m 750 /etc/sandbase
sudo touch /etc/sandbase/runtime.env
sudo chmod 600 /etc/sandbase/runtime.envOpen dat bestand in een editor en voeg één regel toe: OPENAI_API_KEY=sk-.... Provider-sleutels horen hier thuis. Geheimen die een agent tijdens een sessie gebruikt, horen in de credential vaults van de runtime; dit is een ander vraagstuk met een andere impactradius. Het is raadzaam om geheimen buiten AI-agents houden te lezen voordat u een productie-token op een van beide plekken plakt.
De agent YAML: mcp_servers, tools en permissiebeleid
Agents worden gedefinieerd als YAML-bestanden in de agents/-directory van de workspace. Dit is het onderdeel van de runtime waar u daadwerkelijk tijd in zult doorbrengen.
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-commanderLaad het bestand en controleer of het is verwerkt:
sandbase reload
sandbase list
sandbase chat agent_assistant --message "hello"reload importeert de seed YAML in SQLite. list zou nu de agent met een ID moeten weergeven. Als list deze niet toont, is het bestand niet geparseerd en staat de reden hiervoor in .managed-agents/logs/runtime.log.
mcp_servers declareert MCP (model context protocol) endpoints. type: url betekent dat de runtime via HTTP communiceert met een server die elders draait, dus alles wat u al beheert werkt hier, inclusief MCP-servers die op dezelfde VPS worden gehost als de runtime.
Het declareren van een server geeft de tools ervan niet automatisch aan de agent. De lijst tools doet dit via een mcp_toolset-item waarvan de mcp_server_name overeenkomt met de name hierboven. Als de agent zich gedraagt alsof de MCP-tools niet bestaan, vergelijk dan die twee strings teken voor teken voordat u elders gaat zoeken.
agent_toolset_20260401 is de ingebouwde toolset. Het achtervoegsel met de datum is een schemaversie, zodat een agent die hieraan is gekoppeld de tooldefinities behoudt waarvoor deze is geschreven. default_config stelt het beleid in voor elke tool in de set, en elk item onder configs overschrijft één tool op naam, zoals bash in het voorbeeld.
permission_policy is waar een runtime zijn waarde bewijst ten opzichte van een kale model-aanroep. always_ask pauzeert de sessie en wacht op menselijke goedkeuring voordat de aanroep wordt uitgevoerd. always_allow staat de uitvoering toe. Het instellen van bash op always_ask betekent dat de agent geen shell-commando kan uitvoeren zonder dat u eerst het exacte commando ziet; dit is dezelfde controle die u zou toepassen bij het veilig draaien van Claude Code op een VPS.
De drie sandbox-modi en wanneer u welke kiest
Tool-aanroepen die code uitvoeren, draaien binnen een sandbox. De backend wordt per omgeving gekozen via sandbox_provider in het config-object van de omgeving, of via Settings en vervolgens Sandbox in de console. Omgevingen worden via de API aangemaakt op POST /v1/environments.
local voert de code uit als een onderliggend proces van de runtime, op de host, als de eigen gebruiker van de runtime. Dit is de standaardinstelling en is acceptabel zolang u de enige gebruiker bent en de agent alleen bestanden leest die uw eigendom zijn. Er is hier geen sprake van isolatie. Een tool-aanroep die bestanden verwijdert, verwijdert uw bestanden, en een tool-aanroep die /etc/sandbase/runtime.env leest, leest uw provider-sleutel.
docker start één container per sessie.
{
"sandbox_provider": "docker",
"image": "node:22-slim",
"resources": { "memory": "1g", "cpu": 1 }
}De sessie krijgt een eigen bestandssysteem, een eigen geheugenlimiet en een eigen CPU-aandeel; de container wordt verwijderd zodra de sessie eindigt. Schakel hiernaar over zodra een agent code uitvoert die u niet zelf hebt geschreven. Het nadeel is dat de gebruiker van de runtime toegang nodig heeft tot de Docker-socket, en het lidmaatschap van de docker-groep staat gelijk aan root-toegang op de host. Containers per sessie hebben dezelfde structuur als self-hosted agent sandboxes met één container per run, dus de redenering over wat een ontsnapt proces kan bereiken, is hier ongewijzigd van toepassing.
kubernetes voert de sessieworkload uit als een pod en stuurt deze aan met kubectl exec en kubectl cp. De runtime-image moet beschikken over kubectl, en het bijbehorende ServiceAccount heeft RBAC-rechten (role-based access control) nodig om pods in de doel-namespace te kunnen aanmaken, verwijderen, opvragen, weergeven en monitoren, plus het exec-subresource. Deze modus is de configuratie alleen waard als u al over een cluster beschikt.
Waarom is de runtime gebonden aan 127.0.0.1?
Omdat deze start met authenticatie uitgeschakeld. De runtime activeert bearer-token-authenticatie zodra er ten minste één API-sleutel bestaat, en een verse init maakt er geen aan. Binding aan 0.0.0.0 met die standaardinstelling zou een niet-geauthenticeerde agent-runtime, die toegang heeft tot shell-tools en uw provider-sleutel, blootstellen aan het openbare internet.
Wanneer u wilt dat deze bereikbaar is, laat het bind-adres dan ongewijzigd en voer twee andere acties uit.
Ten eerste, schakel authenticatie in. Stel MANAGED_AGENTS_API_KEY in het omgevingsbestand van de service in, of maak een sleutel aan met POST /v1/api-keys, die eenmalig een secret_key-veld retourneert dat daarna nooit meer wordt getoond. Clients sturen vervolgens Authorization: Bearer <key> mee bij elk verzoek.
Ten tweede, plaats een reverse proxy voor de service en handel daar de TLS (transport layer security) af. De runtime serveert standaard plain HTTP en verwacht dat een andere component de certificaten beheert.
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;
}
}Twee van die regels zijn geen decoratie. proxy_buffering off is van belang omdat sessies streamen via server-sent events (SSE); als buffering is ingeschakeld, houdt nginx het antwoord vast totdat de buffer vol is. Hierdoor ziet u niets in de console terwijl de agent werkt, waarna alle output in één keer wordt getoond. proxy_read_timeout 3600s is van belang omdat de standaardwaarde 60 seconden is; een stream die langer dan een minuut stil blijft, wordt door de proxy halverwege een actie gesloten, wat eruitziet alsof de runtime crasht.
Open poort 22 en 443 op de firewall. Laat poort 3000 gesloten, aangezien de proxy deze via loopback bereikt en niets van buitenaf de server direct zou moeten benaderen.
De Anthropic SDK naar uw eigen server verwijzen
De runtime implementeert een CMA-vormig /v1-oppervlak, waardoor een Anthropic SDK-client ermee communiceert met slechts één gewijzigd veld.
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'
});Het accepteert ook de bèta-headers die door Claude Managed Agents-clients worden verzonden, anthropic-beta: managed-agents-2026-04-01 en anthropic-beta: agent-memory-2026-07-22. Deze zijn optioneel bij een lokale runtime. Ze zijn aanwezig zodat code die voor een gehoste implementatie is geschreven, hier ongewijzigd kan draaien.
De compatibiliteit is groot, maar niet volledig. Lees docs/api-matrix.md in de checkout voordat u ervan uitgaat dat een oppervlak bestaat; het project documenteert daar zijn eigen hiaten, inclusief custom tools aan de clientzijde, die nog steeds een benoemde registratie boven het huidige event-result-protocol vereisen.
Plain HTTP werkt net zo goed en is de snelste manier om te verifiëren of de runtime actief is:
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}'Een gezond antwoord is een stroom van events die blijft binnenkomen. Als de verbinding wordt verbroken, hervat dan vanaf het laatste event dat u heeft gezien in plaats van de volledige beurt opnieuw af te spelen:
curl -N http://127.0.0.1:3000/v1/sessions/SESSION_ID/events/stream \
-H "Last-Event-ID: EVENT_ID"Die hervatbare stroom is de reden waarom een sessie overleeft wanneer een laptop wordt gesloten. De events worden op de server opgeslagen, waardoor de client een logbestand opnieuw afspeelt in plaats van de enige kopie vast te houden.
Waar inloggegevens, geheugen en audit-logs op schijf worden opgeslagen
Alles wat de runtime bezit, bevindt zich onder .managed-agents/ in de werkruimte.
.managed-agents/
├── config.yaml
├── data.db
├── logs/runtime.log
├── files/
├── skills/
├── snapshots/
└── sandbox/data.dbbevat de SQLite-metadata: agents, sessies, items in de credential vault, items in het geheugen en API-keys.files/bevat geüploade bestandsbytes enskills/bevat geüploade skill-pakketten.snapshots/bevat snapshots van sessiewerkruimtes ensandbox/bevat de werkmappen van sessies in de lokale modus.logs/runtime.logis de eerste plek om te controleren wanneer iets stilzwijgend niets doet.
Credential vaults zijn groepen geheimen die elk worden toegevoegd met een auth_type, zoals environment_variable, en aan een sessie worden gekoppeld via vault_ids wanneer de sessie wordt aangemaakt. Geheugenopslagplaatsen bevatten benoemde items die u als een memory_store in een sessie koppelt, elk met eigen toegangsinstellingen en instructies. Beide bevinden zich in data.db; dit is precies het verschil tussen dit systeem en een onbewerkte model-aanroep: de runtime onthoudt gegevens tussen sessies door en legt vast wat er is gebeurd.
Omdat het één map is, dient u deze als één geheel te back-uppen.
sudo systemctl stop sandbase
sudo tar czf /root/sandbase-$(date +%F).tgz -C /opt/sandbase/workspace .managed-agents
sudo systemctl start sandbaseStop eerst de service. Het kopiëren van een SQLite-database terwijl de runtime ernaar schrijft, kan resulteren in een bestand dat bij herstel niet kan worden geopend; u komt hier pas achter op de dag dat u het nodig heeft. Als u agent-YAML liever in git beheert en de status elders opslaat, ondersteunt de deployment-documentatie het vastzetten van de statuslocatie met --data-dir in start.
Herstellen werkt in omgekeerde volgorde: check dezelfde tag uit op een nieuwe machine, pak het archief uit in de werkruimte en start de service. Uw provider-key staat niet in het archief als u het ${OPENAI_API_KEY}-formulier heeft gebruikt, dus bewaar deze op een plek waar u er altijd bij kunt.
Uitvoeren onder systemd
Geef de runtime een eigen gebruiker, zodat een tool-aanroep in de lokale sandbox-modus niet onder uw eigen rechten kan handelen.
sudo adduser --system --group --no-create-home --home /opt/sandbase sandbase
sudo chown -R sandbase:sandbase /opt/sandbaseSla dit op als /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.targetHet deployment-voorbeeld van het project roept een managed-agents-binary aan op PATH. Een installatie vanuit een tagged-source maakt deze niet aan, dus voert ExecStart in plaats daarvan node uit tegen het gebouwde entry point.
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/dashboardEen gezond resultaat is active (running) van status en 200 van curl. Lees bij elk ander resultaat eerst journalctl -u sandbase -n 50 en daarna .managed-agents/logs/runtime.log. enable --now is het deel dat ertoe doet, omdat een handmatig gestart proces verdwijnt na de volgende reboot.
Wat er misgaat en de melding die u zult zien
npm run build wordt beëindigd zonder foutmelding van npm. Op een VPS met 1 GB RAM wordt de TypeScript-compilatie gestopt door de out-of-memory killer van de kernel. Deze rapporteert dit aan het kernellogboek in plaats van aan npm. Bevestig dit met journalctl -k | grep -i "out of memory", die een regel afdrukt waarin het beëindigde node-proces wordt genoemd. Voeg swap toe of bouw op een grotere instantie en kopieer dist/ naar de server.
Error: listen EADDRINUSE: address already in use 127.0.0.1:3000. Een ander proces gebruikt de poort al. sudo ss -lntp | grep 3000 toont welk proces dit is. Stop dat proces of start de runtime met --port 3001 en werk de proxy bij.
Het dashboard laadt niet vanaf uw laptop. Dit is het beoogde gedrag, omdat de runtime aan loopback bindt. Gebruik de bovenstaande SSH-tunnel of voltooi de reverse proxy. Probeer dit niet te repareren met --host 0.0.0.0, omdat authenticatie is uitgeschakeld totdat er een sleutel bestaat.
Docker-sandboxes falen met permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock. De gebruiker sandbase zit niet in de groep docker. Los dit op met sudo usermod -aG docker sandbase en herstart de service. Wees u bewust van wat u heeft toegestaan: die groep heeft root-rechten op de host, waardoor een deel van de reden om de runtime een eigen gebruiker te geven teniet wordt gedaan.
Kubernetes-sandboxes falen met Error from server (Forbidden). Het ServiceAccount mist pod-rechten of de subresource exec. Controleer dit direct met kubectl auth can-i create pods/exec -n <namespace>, die yes of no als antwoord geeft.
Elk verzoek geeft een 401-fout nadat u een API-sleutel heeft toegevoegd. Authenticatie wordt ingeschakeld zodra de eerste sleutel bestaat en is van toepassing op zowel de console als de API. Stuur Authorization: Bearer <key>. Als u de sleutel bent kwijtgeraakt, maak dan een nieuwe aan, omdat secret_key slechts eenmalig wordt getoond en niet in leesbare vorm wordt opgeslagen.
De tools van een MCP-server verschijnen nooit in een sessie. Controleer de mcp_server_name in het tools-blok tegen de name in mcp_servers. Controleer vervolgens of de runtime de URL vanaf de server zelf kan bereiken met curl -i <url>. Een MCP-server van het type URL is een netwerkafhankelijkheid; een VPS lost namen op en routeert verkeer anders dan uw laptop.
FAQ
Kan ik SandBase Harness draaien zonder een OpenAI- of Anthropic-key?
Ja, als u een OpenAI-compatibel eindpunt heeft. De runtime ondersteunt OpenAI, Anthropic en OpenAI-compatibele providers; een lokale server die de OpenAI API spreekt, werkt dus. Stel de workspace-provider in via .managed-agents/config.yaml en verwijs api_key en het eindpunt daarnaar. De runtime bevat zelf geen model, dus er moet een bron zijn die de aanroepen beantwoordt.
Is het veilig om de runtime op een publieke poort bloot te stellen?
Niet in de standaardinstallatie. Deze bindt aan 127.0.0.1:3000 en start zonder authenticatie; de oplossing is niet een ander bind-adres. Maak een API-key aan of stel MANAGED_AGENTS_API_KEY in zodat bearer-token-authenticatie wordt ingeschakeld. Plaats vervolgens Nginx of Caddy ervoor voor TLS en houd poort 3000 gesloten op de firewall, zodat de enige toegangsweg via de proxy verloopt.
Wat is het verschil tussen de local, Docker en Kubernetes sandboxes?
local voert tool-code uit als een onderliggend proces van de runtime op de host, met de rechten van de runtime-gebruiker en zonder isolatie. docker geeft elke sessie een eigen container met een eigen bestandssysteem, geheugenlimiet en CPU-aandeel, en verwijdert deze zodra de sessie eindigt. kubernetes voert de sessie uit als een pod en stuurt deze aan met kubectl exec, waarvoor kubectl in de runtime-image en RBAC op pods vereist zijn, evenals de exec subresource in de doel-namespace.
Wat moet ik precies back-uppen?
De map .managed-agents/ in de workspace. Deze bevat config.yaml, de data.db SQLite-database met agents, sessies, entries in de credential vault en geheugen-entries, plus geüploade bestanden, skill-pakketten en sessie-snapshots. Stop de service voordat u deze kopieert, zodat er niet naar de SQLite-database wordt geschreven tijdens het archiveren. Provider API-keys waarnaar wordt verwezen als ${OPENAI_API_KEY} bevinden zich niet in de back-up; sla deze dus apart op.
Waarom de v0.3.2 tag clonen in plaats van main?
Een tag is een vaste boomstructuur, waardoor de configuratie-keys en CLI-commando's waarover u leest, ook daadwerkelijk overeenkomen met wat u krijgt. main verandert en een configuratie-key kan worden hernoemd tussen het moment dat een handleiding wordt geschreven en het moment dat u deze uitvoert. Het project waarschuwt ook dat het niet-geïsoleerde managed-agents pakket op npm niet bij dit project hoort, waardoor npx managed-agents iets ongerelateerds installeert. Release v0.3.1 bestaat voornamelijk om die npm quick start te vervangen door het vastgezette pad naar de getagde broncode.