SSD Nodes Learn 🎉 VPS از $5.50/ماه
راهنماها Matt Connorتوسط Matt Connor

آموزش میزبانی شخصی SandBase agent runtime

با اجرای SandBase Harness v0.3.2 روی VPS، کنترل کامل داده‌ها و نشست‌ها را در دست بگیرید. این راهنما شامل تنظیمات agent YAML، پیکربندی MCP و هدایت Anthropic SDK به سرور شخصی است.

آنچه با میزبانی شخصی SandBase agent runtime به دست می‌آورید

میزبانی شخصی SandBase agent runtime به معنای اجرای SandBase Harness روی سروری است که مالکیت آن با شماست؛ بنابراین نشست‌ها، اعتبارنامه‌ها، حافظه و مسیرهای حسابرسی به‌جای ذخیره در سرورهای دیگر، روی دیسک شما قرار می‌گیرند. این یک سرویس Node است. این سرویس روی 127.0.0.1:3000 گوش می‌دهد، یک /v1 HTTP API و یک کنسول وب ارائه می‌دهد و وضعیت خود را در یک فایل SQLite در کنار فایل‌های agent شما نگهداری می‌کند.

رابط /v1 API از الگوی Claude Managed Agents (CMA) پیروی می‌کند که همان API مدیریت‌شدهٔ میزبانی‌شده است. همین موضوع باعث می‌شود این runtime در هر دو جهت جذاب باشد: شما می‌توانید کد خود را با استفاده از Anthropic SDK بنویسید و baseURL آن را به سمت سرور شخصی خود هدایت کنید، سپس بعداً همان کد را به یک محیط میزبانی‌شده منتقل کنید.

SandBase Harness هیچ مدلی را به همراه خود عرضه نمی‌کند. این ابزار مدل‌ها را فراخوانی می‌کند. تا اوت 2026، این ابزار از OpenAI، Anthropic و endpointهای سازگار با OpenAI پشتیبانی می‌کند که شامل gatewayهای میزبانی‌شده توسط خود کاربر و ارائه‌دهندگانی مانند DeepSeek V4 می‌شود. شما همچنان باید API key خود را فراهم کنید یا از یک سرور محلی که از پروتکل OpenAI API پشتیبانی می‌کند، استفاده نمایید.

پیش‌نیازهای شروع کار

  • یک سرور مجازی (VPS) با سیستم‌عامل Ubuntu 24.04 و حداقل 2 گیگابایت رم. مرحله build پروژه TypeScript سنگین‌ترین بخش نصب است.
  • نسخه Node.js 22 یا جدیدتر، و npm 10 یا جدیدتر. این‌ها حداقل‌های سخت‌گیرانه‌ای هستند که توسط پروژه تعیین شده‌اند.
  • git، به همراه یک کلید API برای مدل ارائه‌دهنده‌ای که قصد استفاده از آن را دارید.
  • Docker، اما تنها در صورتی که به sandboxهای کانتینری برای هر نشست (session) نیاز دارید.

سیستم‌عامل 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 -v باید نسخه 10 یا بالاتر را نمایش دهد. اگر node -v همچنان v18.19.1 را نمایش می‌دهد، یعنی بسته توزیع همچنان نصب است و در PATH اولویت دارد. پیش از ادامه، آن را حذف کنید، زیرا فرآیند build با هر نسخه‌ای از node که shell پیدا کند، اجرا می‌شود.

نصب SandBase از تگ v0.3.2

همیشه از تگ نصب کنید، نه از شاخه‌های در حال تغییر. یک clone ساده از main هر چیزی را که یک ساعت پیش به مخزن اضافه شده است به شما می‌دهد و ممکن است کلیدهای پیکربندی زیر با آن مطابقت نداشته باشند. v0.3.2 تگ فعلی تا تاریخ 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

از npm ci استفاده کنید، نه npm install. دستور ci دقیقاً همان نسخه‌های ثبت‌شده در lockfile متعهد شده را نصب می‌کند، بنابراین درخت فایل‌های شما با درختی که توسعه‌دهندگان تست کرده‌اند مطابقت خواهد داشت. دستور npm install اجازه دارد نسخه‌های جدیدتر را جایگزین کند، که این دقیقاً همان روشی است که باعث می‌شود یک تگ ثابت، بی‌سروصدا از حالت ثابت خارج شود.

اکنون یک workspace ایجاد کنید. این workspace یک دایرکتوری جداگانه است که فایل‌های agent و تمام وضعیت‌های runtime شما را نگه می‌دارد. نگهداری آن در خارج از مسیر source checkout به شما این امکان را می‌دهد که بدون دست زدن به داده‌هایتان، تگ‌های جدیدتر را pull کنید.

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/ در داخل workspace ایجاد می‌کند. دستور 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 نصب نکنید

پروژه در مستندات نصب خود به این نکته اشاره کرده است: بسته unscoped با نام managed-agents که در npm دیده می‌شود، متعلق به این پروژه نیست. بنابراین، دستورات npx managed-agents و npm install -g managed-agents چیزی غیرمرتبط با runtime مورد نظر شما را دریافت می‌کنند. تا زمانی که نگهدارندگان پروژه یک بسته رسمی scoped معرفی نکرده‌اند، آن را از منبع tagged در GitHub نصب کنید. این موضوع یک یادداشت کوچک در تاریخچه پروژه نیست: نسخه v0.3.1 عمدتاً برای جایگزینی روش قدیمی quick start در npm با مسیر pinned tagged-source ارائه شده است.

تعیین ارائه‌دهنده مدل برای فضای کاری

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} مقدار را از محیط پردازش (process environment) دریافت می‌کند؛ بنابراین کلید در فایل پیکربندی قرار نمی‌گیرد و در هیچ‌کدام از نسخه‌های پشتیبانی که از آن فایل تهیه می‌کنید، وارد نخواهد شد. آن را در یک فایل محیطی قرار دهید که فقط root امکان خواندنش را داشته باشد، زیرا systemd فایل EnvironmentFile= را پیش از کاهش سطح دسترسی (drop privileges) با کاربر 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-... به آن اضافه کنید. کلیدهای ارائه‌دهنده باید در اینجا قرار بگیرند. اسراری که یک ایجنت در طول یک نشست استفاده می‌کند، باید در vaultهای اعتبارسنجی زمان اجرا (runtime) ذخیره شوند که مسئله‌ای متفاوت با شعاع انفجار (blast radius) متفاوت است؛ پیش از آنکه یک توکن عملیاتی (production token) را در هر یک از این دو مکان قرار دهید، مطالعه دور نگه داشتن اسرار از ایجنت‌های هوش مصنوعی توصیه می‌شود.

فایل YAML ایجنت: mcp_servers، ابزارها و سیاست‌های دسترسی

ایجنت‌ها به عنوان فایل‌های YAML در دایرکتوری agents/ فضای کاری تعریف می‌شوند. این بخشی از زمان اجرا (runtime) است که در عمل زمان خود را در آن صرف خواهید کرد.

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 آن را نشان نمی‌دهد، فایل تجزیه (parse) نشده است و .managed-agents/logs/runtime.log جایی است که دلیل آن نوشته شده است.

mcp_servers نقاط پایانی (endpoints) پروتکل MCP (مدل کانتکست پروتکل) را اعلام می‌کند. type: url به این معنی است که زمان اجرا از طریق HTTP با سروری که در جای دیگری اجرا می‌شود صحبت می‌کند، بنابراین هر چیزی که در حال حاضر مدیریت می‌کنید در اینجا کار می‌کند، از جمله سرورهای MCP میزبانی‌شده روی همان VPS که زمان اجرا روی آن قرار دارد.

اعلام یک سرور، ابزارهای آن را به ایجنت نمی‌دهد. لیست tools این کار را از طریق یک ورودی mcp_toolset انجام می‌دهد که mcp_server_name آن با name در بالا مطابقت دارد. اگر ایجنت طوری رفتار می‌کند که انگار ابزارهای MCP وجود ندارند، پیش از بررسی هر جای دیگر، این دو رشته را کاراکتر به کاراکتر با هم مقایسه کنید.

agent_toolset_20260401 مجموعه ابزارهای داخلی است. پسوند تاریخ‌دار، نسخه طرح‌واره (schema) است، بنابراین ایجنتی که به آن متصل (pin) شده باشد، تعاریف ابزاری را که برای آن نوشته شده است حفظ می‌کند. default_config سیاست را برای هر ابزار در این مجموعه تعیین می‌کند و هر ورودی در زیر configs، یک ابزار را بر اساس نام بازنویسی می‌کند، مانند bash در مثال.

permission_policy جایی است که یک زمان اجرا، برتری خود را نسبت به فراخوانی مستقیم مدل نشان می‌دهد. always_ask نشست را متوقف می‌کند و منتظر می‌ماند تا یک انسان پیش از اجرا، فراخوانی را تأیید کند. always_allow اجازه عبور به آن می‌دهد. تنظیم bash روی always_ask به این معنی است که ایجنت نمی‌تواند بدون اینکه شما ابتدا دستور دقیق را ببینید، یک دستور shell اجرا کند؛ این همان کنترلی است که هنگام اجرای ایمن Claude Code روی یک VPS به دنبال آن خواهید بود.

سه حالت sandbox و موارد استفاده از هر کدام

فراخوانی‌های ابزار که کد اجرا می‌کنند، درون یک sandbox انجام می‌شوند. backend برای هر محیط از طریق sandbox_provider در شیء config محیط، یا در کنسول از مسیر Settings و سپس Sandbox انتخاب می‌شود. محیط‌ها از طریق API در POST /v1/environments ایجاد می‌شوند.

حالت local کد را به عنوان یک child process از runtime، روی میزبان و با کاربری خودِ runtime اجرا می‌کند. این حالت پیش‌فرض است و زمانی که شما تنها کاربر هستید و agent فقط فایل‌های متعلق به شما را می‌خواند، منطقی است. این حالت ایزولاسیون نیست. فراخوانی ابزاری که فایل‌ها را حذف می‌کند، فایل‌های شما را حذف خواهد کرد و فراخوانی ابزاری که /etc/sandbase/runtime.env را می‌خواند، کلید provider شما را می‌خواند.

حالت docker برای هر نشست (session) یک container راه‌اندازی می‌کند.

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

نشست، سیستم فایل، سقف حافظه و سهم CPU مختص به خود را دارد و container همراه با نشست حذف می‌شود. به محض اینکه agent کدی را اجرا کرد که شما آن را ننوشته‌اید، به این حالت سوییچ کنید. هزینه این کار این است که کاربرِ runtime به دسترسی به Docker socket نیاز دارد و عضویت در گروه docker معادل دسترسی root روی میزبان است. containerهای هر نشست، ساختاری مشابه sandboxهای agent خود-میزبان با یک container در هر اجرا دارند، بنابراین استدلال درباره آنچه یک پردازش فرار کرده می‌تواند به آن دسترسی داشته باشد، در اینجا نیز بدون تغییر صدق می‌کند.

حالت kubernetes بار کاری نشست را به عنوان یک pod اجرا کرده و آن را با kubectl exec و kubectl cp هدایت می‌کند. image مربوط به runtime به حضور kubectl نیاز دارد و ServiceAccount آن باید مجوز RBAC (کنترل دسترسی مبتنی بر نقش) برای ایجاد، حذف، دریافت، لیست کردن و نظارت بر podها در namespace هدف، به علاوه زیرمنبع exec را داشته باشد. این حالت تنها در صورتی ارزش راه‌اندازی دارد که از قبل یک cluster در حال اجرا داشته باشید.

چرا runtime به 127.0.0.1 متصل (bind) شده است؟

زیرا این سرویس در ابتدا با احراز هویت غیرفعال شروع به کار می‌کند. runtime زمانی احراز هویت با bearer-token را فعال می‌کند که حداقل یک API key وجود داشته باشد، اما یک init تازه هیچ کلیدی ایجاد نمی‌کند. اتصال به 0.0.0.0 در آن حالت پیش‌فرض، باعث می‌شود یک runtime عامل (agent) بدون احراز هویت، که دارای ابزارهای shell و کلید ارائه‌دهنده شماست، روی اینترنت عمومی قرار بگیرد.

بنابراین وقتی می‌خواهید این سرویس در دسترس باشد، آدرس bind را تغییر ندهید و دو کار دیگر انجام دهید.

نخست، احراز هویت را فعال کنید. مقدار MANAGED_AGENTS_API_KEY را در فایل environment سرویس تنظیم کنید، یا با استفاده از POST /v1/api-keys یک کلید بسازید که یک فیلد secret_key را فقط یک‌بار نمایش می‌دهد و دیگر هرگز آن را نشان نخواهد داد. سپس کلاینت‌ها باید در هر درخواست Authorization: Bearer <key> را ارسال کنند.

دوم، یک reverse proxy در مقابل آن قرار دهید و TLS (امنیت لایه انتقال) را در آنجا خاتمه دهید. runtime طبق طراحی، 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) جریان می‌یابند و با فعال بودن buffering، nginx پاسخ را تا پر شدن بافر نگه می‌دارد؛ در نتیجه کنسول در حین کار عامل چیزی نشان نمی‌دهد و در پایان همه چیز را یکجا نمایش می‌دهد. proxy_read_timeout 3600s اهمیت دارد زیرا مقدار پیش‌فرض 60 ثانیه است، بنابراین جریانی که بیش از یک دقیقه ساکت بماند توسط پروکسی در میانه کار بسته می‌شود و این خطا مانند کرش کردن runtime به نظر می‌رسد.

روی فایروال، پورت‌های 22 و 443 را باز کنید. پورت 3000 را بسته نگه دارید، زیرا پروکسی از طریق loopback به آن دسترسی دارد و هیچ چیز خارج از سرور نباید به آن متصل شود.

اشاره کردن Anthropic SDK به سرور شخصی خود

این runtime یک سطح به شکل 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'
});

این runtime همچنین هدرهای بتا که کلاینت‌های Claude Managed Agents ارسال می‌کنند، یعنی anthropic-beta: managed-agents-2026-04-01 و anthropic-beta: agent-memory-2026-07-22 را می‌پذیرد. این هدرها در برابر یک runtime محلی اختیاری هستند. وجود آن‌ها برای این است که کدی که برای یک deployment میزبانی‌شده نوشته شده، در اینجا بدون تغییر اجرا شود.

سازگاری نزدیک است، اما کامل نیست. پیش از آنکه فرض کنید یک سطح وجود دارد، docs/api-matrix.md را در checkout مطالعه کنید، زیرا پروژه شکاف‌های خود را در آنجا مستند کرده است؛ از جمله ابزارهای سفارشی سمت کلاینت که همچنان به ثبت نامی (named registration) فراتر از پروتکل فعلی event-result نیاز دارند.

استفاده از HTTP ساده نیز به همان خوبی کار می‌کند و سریع‌ترین راه برای اطمینان از فعال بودن runtime است:

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"

دلیل اینکه یک نشست با بستن لپ‌تاپ زنده می‌ماند، همین جریان قابل‌ادامه (resumable) است. رویدادها روی سرور ذخیره می‌شوند، بنابراین کلاینت به‌جای نگهداری تنها نسخه موجود، در حال بازپخش یک لاگ است.

محل ذخیره‌سازی اعتبارنامه‌ها، حافظه و لاگ‌های حسابرسی روی دیسک

هر چیزی که runtime مالک آن است، در مسیر .managed-agents/ در فضای کاری قرار دارد.

.managed-agents/
├── config.yaml
├── data.db
├── logs/runtime.log
├── files/
├── skills/
├── snapshots/
└── sandbox/
  • data.db شامل متادیتای SQLite است: ایجنت‌ها، نشست‌ها، ورودی‌های vault اعتبارنامه‌ها، ورودی‌های حافظه و API keyها.
  • files/ بایت‌های فایل‌های آپلود شده را نگه می‌دارد و skills/ بسته‌های skill آپلود شده را در خود جای می‌دهد.
  • snapshots/ اسنپ‌شات‌های فضای کاری نشست‌ها را نگه می‌دارد و sandbox/ دایرکتوری‌های کاری نشست‌های local-mode را در بر می‌گیرد.
  • logs/runtime.log اولین جایی است که باید هنگام بروز رفتارهای غیرمنتظره یا عدم پاسخ‌دهی سرویس بررسی کنید.

Credential vaultها گروه‌هایی از اسرار هستند که هر کدام با یک auth_type مانند environment_variable اضافه می‌شوند و هنگام ایجاد نشست، از طریق vault_ids به آن متصل می‌گردند. Memory storeها ورودی‌های نام‌گذاری‌شده‌ای را نگه می‌دارند که شما آن‌ها را به عنوان یک memory_store با تنظیمات دسترسی و دستورالعمل‌های خاص خود به نشست mount می‌کنید. هر دوی این موارد در data.db قرار دارند؛ این دقیقاً همان تفاوتی است که این سیستم با یک فراخوانی مدل خام دارد: runtime رویدادها را در نشست‌های مختلف به خاطر می‌سپارد و آنچه رخ داده است را ثبت می‌کند.

از آنجا که همه این موارد در یک دایرکتوری قرار دارند، از کل آن به عنوان یک واحد پشتیبان‌گیری کنید.

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

ابتدا سرویس را متوقف کنید. کپی کردن دیتابیس SQLite در حالی که runtime در حال نوشتن روی آن است، ممکن است فایلی ایجاد کند که هنگام بازیابی باز نشود؛ و شما این موضوع را دقیقاً در روزی که به آن نیاز دارید متوجه خواهید شد. اگر ترجیح می‌دهید فایل‌های YAML ایجنت را در git و وضعیت (state) را در جای دیگری نگه دارید، مستندات deployment از تعیین محل ذخیره state با استفاده از --data-dir در start پشتیبانی می‌کند.

بازیابی برعکس این فرآیند است: همان تگ را روی یک سیستم جدید checkout کنید، آرشیو را در فضای کاری باز کنید و سرویس را استارت بزنید. اگر از فرم ${OPENAI_API_KEY} استفاده کرده باشید، کلید provider شما در آرشیو نیست؛ بنابراین آن را در جایی امن نگه دارید که همیشه به آن دسترسی داشته باشید.

اجرای آن تحت systemd

برای محیط اجرا یک کاربر اختصاصی بسازید تا فراخوانی ابزار در حالت sandbox محلی نتواند با دسترسی شما عمل کند.

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 فراخوانی می‌کند. نصب از طریق tagged-source چنین فایلی ایجاد نمی‌کند، بنابراین 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 بخشی است که اهمیت دارد، زیرا فرآیندی که به‌صورت دستی شروع شده باشد، پس از reboot بعدی از بین می‌رود.

چه چیزی از کار می‌افتد و پیامی که مشاهده خواهید کرد

npm run build بدون هیچ خطایی از سمت npm متوقف می‌شود. در یک VPS با 1 GB رم، فرآیند کامپایل TypeScript توسط OOM Killer (قاتل حافظه) هسته سیستم‌عامل متوقف می‌شود؛ این اتفاق در لاگ هسته گزارش می‌شود و نه در npm. با استفاده از journalctl -k | grep -i "out of memory" موضوع را تأیید کنید؛ این دستور خطی را چاپ می‌کند که نام فرآیند node کشته‌شده را نشان می‌دهد. فضای Swap اضافه کنید یا پروژه را روی یک نمونه (instance) بزرگ‌تر بیلد کرده و dist/ را منتقل کنید.

Error: listen EADDRINUSE: address already in use 127.0.0.1:3000. فرآیند دیگری در حال حاضر از پورت استفاده می‌کند. sudo ss -lntp | grep 3000 نام آن فرآیند را نشان می‌دهد. یا آن فرآیند را متوقف کنید و یا runtime را با --port 3001 اجرا کرده و پروکسی را به‌روزرسانی کنید.

داشبورد از روی لپ‌تاپ شما بارگذاری نمی‌شود. این رفتار مورد انتظار است، زیرا runtime به 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 برطرف کرده و سرویس را ری‌استارت کنید. توجه داشته باشید که چه دسترسی‌ای داده‌اید: این گروه در میزبان (host) دسترسی root دارد، بنابراین بخشی از دلیلی که باعث شد برای runtime یک کاربر مجزا تعریف کنید، خنثی می‌شود.

سندباکس‌های Kubernetes با Error from server (Forbidden) شکست می‌خورند. حساب کاربری ServiceAccount فاقد مجوزهای pod یا زیرمنبع exec است. این موضوع را مستقیماً با kubectl auth can-i create pods/exec -n <namespace> بررسی کنید که پاسخ yes یا no را برمی‌گرداند.

پس از افزودن API key، تمام درخواست‌ها خطای 401 برمی‌گردانند. احراز هویت با ایجاد اولین کلید فعال می‌شود و هم برای کنسول و هم برای API اعمال می‌گردد. هدر Authorization: Bearer <key> را ارسال کنید. اگر کلید را گم کرده‌اید، یک کلید جدید بسازید، زیرا secret_key فقط یک‌بار نمایش داده می‌شود و به شکل قابل خواندن ذخیره نمی‌گردد.

ابزارهای یک سرور MCP در نشست (session) ظاهر نمی‌شوند. مقدار mcp_server_name در بلوک tools را با name در mcp_servers مطابقت دهید، سپس بررسی کنید که آیا runtime می‌تواند از خودِ سرور با استفاده از curl -i <url> به آن URL دسترسی داشته باشد یا خیر. یک سرور MCP از نوع URL یک وابستگی شبکه محسوب می‌شود و یک VPS نام‌ها را متفاوت از لپ‌تاپ شما حل (resolve) کرده و ترافیک را مسیردهی می‌کند.

FAQ

آیا می‌توانم SandBase Harness را بدون کلید OpenAI یا Anthropic اجرا کنم؟

بله، اگر یک endpoint سازگار با OpenAI داشته باشید. این runtime از ارائه‌دهندگان OpenAI، Anthropic و هر ارائه‌دهندهٔ سازگار با OpenAI پشتیبانی می‌کند، بنابراین یک سرور محلی که از API استاندارد OpenAI استفاده می‌کند، کارساز خواهد بود. ارائه‌دهندهٔ فضای کاری را در .managed-agents/config.yaml تنظیم کنید و api_key و endpoint را به سمت آن هدایت نمایید. این runtime به خودی خود هیچ مدلی ندارد، بنابراین برای پاسخ‌دهی به فراخوانی‌ها باید سرویس دیگری در دسترس باشد.

آیا قرار دادن runtime روی یک پورت عمومی امن است؟

به شکلی که نصب شده است، خیر. این سرویس روی 127.0.0.1:3000 گوش می‌دهد و بدون احراز هویت شروع به کار می‌کند؛ راه حل این مشکل صرفاً تغییر آدرس bind نیست. یک API key ایجاد کنید یا MANAGED_AGENTS_API_KEY را تنظیم کنید تا احراز هویت از طریق bearer-token فعال شود. سپس Nginx یا Caddy را برای TLS در مقابل آن قرار دهید و پورت 3000 را در فایروال ببندید تا تنها مسیر دسترسی، از طریق proxy باشد.

تفاوت بین sandboxهای محلی، Docker و Kubernetes چیست؟

local کد ابزار را به عنوان یک child process از runtime روی میزبان اجرا می‌کند، با مجوزهای کاربرِ runtime و بدون هیچ‌گونه ایزولاسیون. docker برای هر نشست (session) یک container مجزا با فایل‌سیستم، محدودیت حافظه و سهم CPU اختصاصی ایجاد می‌کند و پس از پایان نشست، آن را حذف می‌نماید. kubernetes نشست را به صورت یک pod اجرا کرده و آن را با kubectl exec مدیریت می‌کند که نیازمند kubectl در داخل image مربوط به runtime و همچنین RBAC روی podها به همراه subresource مربوط به exec در namespace هدف است.

دقیقاً از چه چیزی باید نسخه پشتیبان (backup) تهیه کنم؟

دایرکتوری .managed-agents/ در فضای کاری. این دایرکتوری شامل config.yaml، دیتابیس SQLite با نام data.db است که حاوی agentها، نشست‌ها، ورودی‌های vault اعتبارنامه‌ها و حافظه است؛ علاوه بر این، فایل‌های آپلود شده، بسته‌های مهارت و snapshotهای نشست‌ها نیز در آن قرار دارند. پیش از کپی کردن، سرویس را متوقف کنید تا SQLite در حین آرشیو کردن در حال نوشتن نباشد. کلیدهای API ارائه‌دهنده که به عنوان ${OPENAI_API_KEY} ارجاع داده می‌شوند، در داخل نسخه پشتیبان نیستند، بنابراین آن‌ها را جداگانه ذخیره کنید.

چرا باید به جای main، تگ v0.3.2 را clone کنم؟

یک تگ یک درخت ثابت است، بنابراین کلیدهای پیکربندی و دستورات CLI که درباره آن‌ها می‌خوانید، همان‌هایی هستند که واقعاً دریافت می‌کنید. main تغییر می‌کند و ممکن است یک کلید پیکربندی بین زمانی که راهنما نوشته شده و زمانی که شما آن را اجرا می‌کنید، تغییر نام یابد. این پروژه همچنین هشدار می‌دهد که بسته بدون scope با نام managed-agents در npm مربوط به این پروژه نیست، بنابراین npx managed-agents چیز بی‌ارتباطی را نصب می‌کند. نسخه v0.3.1 عمدتاً برای جایگزینی آن روش شروع سریع npm با مسیر منبعِ تگ‌شده و ثابت ارائه شده است.