SSD Nodes Learn Hosting plans →
راهنماها Matt Connorتوسط Matt Connor · به‌روزرسانی شده 2026-08-28

آموزش میزبانی شخصی SandBase Harness روی سرور

راهنمای کامل نصب SandBase Harness v0.3.2 روی VPS. یاد بگیرید چگونه با تنظیم فایل YAML، پیکربندی MCP و تغییر آدرس Anthropic SDK، محیط اجرای اختصاصی خود را مدیریت کنید.

مزایای میزبانی شخصی (Self-hosting) محیط اجرای SandBase agent

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

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

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

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

  • یک سرور مجازی (VPS) با سیستم‌عامل Ubuntu 24.04 و حداقل 2 GB رم. مرحله build پروژه TypeScript سنگین‌ترین بخش نصب است.
  • نسخه Node.js 22 یا جدیدتر و npm 10 یا جدیدتر. این‌ها حداقل‌های الزامی اعلام‌شده توسط پروژه هستند.
  • git، به همراه یک API key برای مدل ارائه‌دهنده‌ای که قصد استفاده از آن را دارید.
  • 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

همیشه از یک تگ نصب کنید، نه از یک شاخه (branch) در حال تغییر. یک 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 اجازه دارد نسخه‌های جدیدتر را جایگزین کند، که این دقیقاً همان روشی است که باعث می‌شود یک تگ ثابت (pinned)، به‌طور بی‌سروصدا از حالت ثابت خارج شود.

اکنون یک 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 نصب نکنید

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

تنظیم فضای کاری برای استفاده از یک ارائه‌دهنده مدل

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

فایل 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 نقاط پایانی MCP (model context protocol) را تعریف می‌کند. type: url یعنی runtime از طریق HTTP با سروری ارتباط دارد که در مکان دیگری اجرا می‌شود؛ بنابراین هر چیزی را که از قبل اجرا می‌کنید می‌توان در اینجا نیز استفاده کرد، از جمله سرورهای MCP میزبانی‌شده روی همان VPS که runtime روی آن اجرا می‌شود. جست‌وجوی وب معمولاً نخستین ابزاری است که افراد به سراغ آن می‌روند، اما پیش از راه‌اندازی چنین ابزاری، مطالعهٔ اختصاص دادن نمونهٔ SearXNG خودتان به یک agent ارزش دارد؛ زیرا ابزاری که صفحاتی نوشته‌شده توسط افراد ناشناس را برمی‌گرداند، متن غیرقابل‌اعتماد را مستقیماً وارد context مدل می‌کند. گزینهٔ ساده‌تر برای شروع، ساختاری معکوس دارد: یک endpoint فقط‌خواندنی روی داده‌هایی که مالک آن‌ها هستید. همین کاری است که openGym در کنار خود workout tracker ارائه می‌کند؛ بنابراین agent می‌تواند دربارهٔ سابقهٔ تمرین شما پاسخ دهد، بدون آن‌که بتواند هیچ بخشی از آن را بازنویسی کند.

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

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

permission_policy جایی است که یک runtime برتری خود را نسبت به فراخوانی مستقیم مدل ثابت می‌کند. always_ask نشست را متوقف کرده و منتظر می‌ماند تا یک انسان پیش از اجرا، فراخوانی را تأیید کند. always_allow اجازه عبور به آن می‌دهد. تنظیم bash روی always_ask به این معنی است که عامل نمی‌تواند یک دستور shell را بدون اینکه ابتدا دستور دقیق را ببینید اجرا کند؛ این همان کنترلی است که هنگام اجرای ایمن Claude Code روی یک VPS به دنبال آن خواهید بود. اگر از DeepSeek Harness نیز استفاده می‌کنید، همان کنترل‌ها به جای کلیدهای YAML به عنوان افزونه (add-on) ارائه می‌شوند و پلاگین‌هایی که بودجه را محدود کرده و فراخوانی ابزارها را کنترل می‌کنند، نزدیک‌ترین معادل برای این بلوک هستند.

سه حالت 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 }
}

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

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

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

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

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

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

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

روی فایروال، پورت‌های 22 و 443 را باز کنید. پورت 3000 را بسته نگه دارید، زیرا proxy از طریق 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'
});

این سیستم همچنین هدرهای بتا که کلاینت‌های 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"

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

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

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

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

مخازن اعتبارنامه‌ها (Credential vaults) گروه‌هایی از اسرار هستند که هر کدام با یک auth_type مانند environment_variable اضافه شده و هنگام ایجاد نشست، از طریق vault_ids به آن متصل می‌شوند. حافظه‌ها (Memory stores) ورودی‌های نام‌گذاری‌شده‌ای را نگه می‌دارند که شما آن‌ها را به عنوان یک 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 پشتیبانی می‌کند.

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

اجرای آن تحت systemd

برای محیط runtime یک کاربر اختصاصی ایجاد کنید تا فراخوانی ابزار در حالت 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

نمونه deployment خود پروژه، یک باینری managed-agents را در مسیر PATH فراخوانی می‌کند. نصب از طریق tagged-source چنین فایلی ایجاد نمی‌کند، بنابراین ExecStart دستور node را روی نقطه ورود (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/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 گیگابایت رم، فرآیند کامپایل TypeScript توسط قابلیت out-of-memory 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 می‌تواند از خودِ سرور به آن URL دسترسی داشته باشد یا خیر؛ این کار را با curl -i <url> انجام دهید. یک سرور MCP از نوع URL یک وابستگی شبکه محسوب می‌شود و یک VPS نام‌ها را حل کرده و ترافیک را متفاوت از لپ‌تاپ شما مسیریابی می‌کند.

FAQ

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

بله، اگر یک endpoint سازگار با OpenAI داشته باشید. این runtime از ارائه‌دهندگان OpenAI، Anthropic و هر سرویس سازگار با OpenAI پشتیبانی می‌کند، بنابراین یک سرور محلی که از API استاندارد OpenAI استفاده می‌کند، کارساز خواهد بود. ارائه‌دهنده فضای کاری (workspace provider) را در .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 به عنوان reverse proxy برای 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 اعتبارنامه‌ها و حافظه است؛ همچنین فایل‌های آپلود شده، بسته‌های مهارت (skill packages) و snapshotهای نشست‌ها در آن قرار دارند. پیش از کپی کردن، سرویس را متوقف کنید تا SQLite در حین آرشیو کردن در حال نوشتن نباشد. کلیدهای API ارائه‌دهندگان که به عنوان ${OPENAI_API_KEY} ارجاع داده می‌شوند، در این نسخه پشتیبان نیستند، بنابراین آن‌ها را جداگانه ذخیره کنید.

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

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