رفتن به محتوا

بک‌اندهای سندباکس

بک‌اند مقدار سکو دسترس‌پذیری
VM محفظه‌ای microvm macOS، Linux، Windows (هایپروایزر microVM) مشروط به بررسی روی زمان اجرای microVM؛ به ترمینال‌های تعاملی اعمال می‌شود — پوسته درون یک ریگ اجرا می‌شود، نه روی میزبان
بومی native macOS (Seatbelt)، Linux و WSL2 (bubblewrap) هنگام بوت بررسی می‌شود؛ Linux اجرایی را که به ردهای نوشتن مبتنی بر الگوی نام فایل نیاز دارد رد می‌کند، چیزی که bubblewrap نمی‌تواند اعمال کند
بدون جداسازی none همه پیش‌فرض تجزیه، پیش‌فرض ترمینال، و آنچه در نبود بک‌اند یا با تنظیم --sandbox off به آن بازمی‌گردد

SandboxBackend.fromName برای یک مقدار ذخیره‌شدهٔ null یا ناشناخته none برمی‌گرداند. یک "docker" ذخیره‌شده به native نگاشت می‌شود.

microvm تنها بک‌اندی است که مرز کرنل دارد — بار کاری واقعاً روی میزبان اجرا نمی‌شود. هرگز در هیچ جهتی تنزل خاموش نیست: درخواست صریحش روی میزبانی که نمی‌تواند VM بوت کند با شکستی پرصدا از کار می‌افتد («VM خواستم و لپ‌تاپم را گرفتم» همان تنزلی است که محفظه توان تحملش را ندارد)، و تنها یک پیش‌فرض نامشخص است که به native تنزل می‌کند، با خطی در لاگ که همین را می‌گوید. بک‌اند واقعی نشست به کلاینت گزارش می‌شود و برچسب می‌خورد، پس یک ترمینال هرگز نمی‌تواند ادعا کند محفظه‌ای است در حالی که روی میزبان اجرا می‌شود. چرخهٔ عمر، ایمیج‌ها و خروج خودِ VM در مرجع ریگ‌ها پوشش داده شده‌اند.

Windows سندباکس بومی ندارد. NativeSandboxAdapter.probe روی هر میزبانی که macOS یا Linux/WSL2 نباشد available: false برمی‌گرداند. بنابراین اعزام عامل روی Windows از none استفاده می‌کند. بک‌اند microvm جداگانه است: به بررسی زمان اجرای microVM مشروط است و به ترمینال‌های تعاملی و ریگ‌ها اعمال می‌شود، نه به پیچیدن bash روی میزبان.

بررسی همان چیزی است که اعمال را دروازه‌بندی می‌کند. یک‌بار، هنگام بوت سرور اجرا می‌شود.

میزبان available یادداشت / راهنما
macOS true sandbox-exec (Seatbelt) — ایزوله‌سازی namespace، بدون مرز کرنل
Linux یا WSL2 با bwrap و socat true bubblewrap — ایزوله‌سازی namespace، بدون مرز کرنل
Linux یا WSL2 که هر یک از این دو ابزار را ندارد false requiresInstall: true، همراه با راهنمای نصب که بسته‌های غایب را نام می‌برد
هر سکوی دیگر false سندباکس بومی روی این سکو عرضه نمی‌شود

سرور نتیجه را روی یک خط راه‌اندازی لاگ می‌کند: agent sandbox ON به‌همراه یادداشت، no OS-native agent sandbox on this host به‌همراه راهنما، یا agent sandbox DISABLED by --sandbox off. GET /healthz هیچ فیلد سندباکسی حمل نمی‌کند.

--sandbox / CC_SERVER_SANDBOX مقدار on (پیش‌فرض) یا off را می‌پذیرد. یک انصراف است، نه یک فعال‌سازی: on یعنی «هرگاه میزبان بک‌اندی عرضه کند بپیچ»، و هرگز یعنی «یکی را الزام کن».

از سازوکار سندباکس Seatbelt اپل استفاده می‌کند (همان سیستمی که App Sandbox در macOS به کار می‌برد).

پروفایل تولیدشده به‌طور پیش‌فرض مجاز است ((allow default)) و ردها را می‌تراشد، چون یک پروفایل کاملاً رد-به‌طور-پیش‌فرض پوستهٔ تعاملی را غیرقابل‌استفاده می‌کند — فراخوانی‌های dyld، XPC و Mach برای برشمردن بیش از حد زیادند. پیامد عملی این است که عامل هنوز می‌تواند بیشتر میزبان را بخواند؛ checkoutهای ثبت‌شدهٔ شما با ردهای صریح نوشتن و تغییر نام محافظت می‌شوند، نه با نامرئی‌بودن.

  • خواندن فایل‌سیستم: برای مسیرهای محرمانه (~/.ssh، ~/.aws، ~/.gnupg، ~/.config/gh، ~/Library/Keychains و بقیهٔ مجموعهٔ رد-خواندن) رد می‌شود
  • نوشتن فایل‌سیستم: به رد بازنشانی می‌شود، سپس اجازه‌های صریح برای زیرمسیرها، در حالی که مسیرهای الزاماً رد‌شده (فایل‌های rc پوسته، .git/hooks، پیکربندی Claude، checkoutهای محافظت‌شده) حتی درون ریشهٔ قابل‌نوشتن هم مسدود می‌مانند. file-write-unlink روی هر مسیر رد‌شده و اجدادش رد می‌شود، پس یک تغییر نام نمی‌تواند از یک رد-نوشتن بگذرد
  • فرایند: ردهای process-exec برای باینری‌های همیشه‌خطرناک، به‌همراه مسدودسازی اجرای هر چیزی از یک دایرکتوری قابل‌نوشتن
  • شبکه: محدود به پورت‌های پروکسی درون‌فرایندی به‌همراه DNS — loopback به‌صورت یکسره مجاز نیست و سوکت‌های Unix مجاز نیستند. پروکسی‌ها فهرست مجاز دامنهٔ هر نشست را اعمال می‌کنند (به خروج شبکه نگاه کنید)
  • پیکربندی: پروفایل‌ها به‌ازای هر راه‌اندازی از SandboxSpec تولید می‌شوند

پیش‌نیازها:

  • sandbox-exec (/usr/bin/sandbox-exec)، بخشی از هر نصب macOS — هیچ ابزار اضافه‌ای لازم نیست

از bubblewrap (bwrap) برای ساخت namespaceهای مانت و PID استفاده می‌کند.

  • فایل‌سیستم: یک namespace مانت می‌سازد که در آن فقط مسیرهای bind‌شده دیده می‌شوند
  • شبکه: در یک namespace شبکهٔ جداگانه اجرا می‌شود؛ ترافیک از طریق پل‌های سوکت Unixی socat به پروکسی‌های میزبان می‌رسد
  • فرایند: ایزوله‌سازی namespace PID

پیش‌نیازها:

  • نصب bubblewrap و socat (روی Debian/Ubuntu: sudo apt-get install bubblewrap socat)
  • پشتیبانی کرنل از namespace (استاندارد در Linux مدرن)

Bubblewrap می‌تواند یک مسیر موجود را سایه بیندازد اما نمی‌تواند یک فایل جدید .env، *.key یا *.pem را درون یک مانت bind قابل‌نوشتن ممنوع کند. سیاست استاندارد اعزام این ردهای مبتنی بر الگوی نام فایل را الزام می‌کند، پس اعزام بومی عامل در حال حاضر روی Linux/WSL2 حتی وقتی بررسی bubblewrap را پیدا می‌کند هم به‌صورت بسته شکست می‌خورد. با سیاستی خاموش‌ضعیف‌تر اجرا نمی‌شود. برای بازگرداندن اعزام بومی Linux به بک‌اندی نیاز است که قواعد نام فایل را در زمان ساخت اعمال کند. خاموش‌کردن سندباکس یک انصراف صریح و بدون محدودیت است، نه یک راه‌حل معادل.

بدون سندباکس. فرایند مستقیماً روی میزبان با دسترسی فایل‌سیستم، شبکه و فرایند خود میزبان اجرا می‌شود. ACP و ابزار bash هارنس همچنان پاک‌سازی محیط و سیاست فرمان را می‌گیرند؛ Claude Code از NoSandboxAdapter محیط میزبان را به ارث می‌برد. این همان چیزی است که اعزام عامل روی میزبانی بدون بک‌اند در دسترس یا با --sandbox off به آن برمی‌گردد، و پیش‌فرض ترمینال است وقتی کلاینت هیچ بک‌اندی را نام نمی‌برد.

ترمینال به قاعدهٔ خودش سندباکس می‌شود، نه با بررسی اعزام. defaultBackend در TerminalSessionService برابر none است و cc_server آن را بازنویسی نمی‌کند، پس یک نشست ترمینال پوستهٔ میزبان را اجرا می‌کند مگر اینکه کلاینت صراحتاً native را بخواهد — پنل ترمینال نسخهٔ رومیزی چنین نمی‌کند.

هنگامی که native درخواست شود، پوسته با یک پروفایل ثابت ترمینال از SandboxManager می‌پیچد: $HOME و دایرکتوری کاری نشست قابل‌نوشتن‌اند، شبکه باز است و خواندن ~/.ssh، ~/.aws، ~/.gnupg، ~/.config/gh و ~/Library/Keychains رد می‌شود. توکن فقط‌خواندنی و دروازهٔ پوش اعمال نمی‌شوند — آن دغدغهٔ اعزام است. اگر پیچش پشتیبانی نشود، نشست شکست می‌خورد به‌جای اینکه روی یک PTY پوستهٔ میزبان برچسب بومی را نمایش دهد.

وقتی سندباکسی راه‌اندازی می‌شود، دسترسی شبکهٔ خروجی توسط پروکسی‌های درون‌فرایندی اعمال می‌شود، نه با قواعد فایروال کرنل. روی میزبانی بدون بک‌اند در دسترس هیچ‌کدام از این‌ها اعمال نمی‌شود و یک فرایند spawn‌شده دقیقاً همان‌طور که کاربر سرور دسترسی داشت به شبکه می‌رسد.

  • زمان اجرای سندباکس یک پروکسی HTTP/HTTPS و یک پروکسی SOCKS5 روی 127.0.0.1 درون فرایند cc_server اجرا می‌کند، یک جفت به‌ازای هر نشست محدودشده (http_proxy.dart، socks_proxy.dart). اجراهای هم‌زمان یک جفت را به اشتراک نمی‌گذارند.
  • فرایندهای سندباکس‌شده متغیرهای محیطی پروکسی را دریافت می‌کنند (HTTP_PROXY/HTTPS_PROXY → پروکسی HTTP، ALL_PROXY → پروکسی SOCKS؛ GIT_SSH_COMMAND مسیر git روی SSH را از پروکسی SOCKS می‌گذراند). روی Linux namespace شبکه جدا است، پس پروکسی‌ها تنها راه بیرون‌اند؛ روی macOS سندباکس loopback میزبان را به اشتراک می‌گذارد و مستقیم با آن‌ها حرف می‌زند.
  • میزبان هدف هر اتصال با فهرست مجاز دامنهٔ نشست تطبیق داده می‌شود؛ هر چیز دیگری رد می‌شود. دو شکل وایلدکارد، هر کدام با دقیقاً یک *: یک *.example.com پیشرو با هر زیردامنه‌ای تطبیق می‌یابد (شامل a.b.example.com) اما نه با خود دامنهٔ ریشه؛ یک bedrock.*.amazonaws.com با برچسب میانی دقیقاً با یک برچسب تطبیق می‌یابد (bedrock.us-east-1.amazonaws.com، نه bedrock.evil.attacker.amazonaws.com).
  • فهرست مجاز به‌ازای هر نشست از خط پایهٔ گزینش‌شده حل می‌شود (kBaselineAllowedDomains: ارائه‌دهندگان LLM، میزبانی Git، رجیستری‌های بسته)، به‌همراه دامنه‌های GitHub (kGithubDomains)، به‌علاوهٔ SandboxSpec.egressAllowlist. مسیر اعزام هرگز ورودی‌های اضافی egressAllowlist را نمی‌فرستد، پس در عمل هر اجرا خط پایه به‌علاوهٔ GitHub را می‌گیرد.
  • یک فهرست رد ثابت (kBaselineDeniedDomains: نقاط پایانی فرادادهٔ ابری، مقصدهای تلمتری) بر همهٔ مجوزها تقدم دارد.
  • networkEnabled: false همچنان پروکسی‌های هر نشست را شروع می‌کند، اما فهرست مجاز خالی است، پس هر اتصال خروجی رد می‌شود. اعزام این مقدار را در شروع اجرا از قاعدهٔ دسترسی به شبکه تعیین می‌کند: رد شبکه را خاموش می‌کند، اجازه و ابتدا بپرس آن را روشن نگه می‌دارند.

specی که به SandboxPort.launch داده می‌شود:

فیلد نوع توضیح
sessionId String شناسهٔ پایدار نشست (معمولاً شناسهٔ فضا)
workspaceId String شناسهٔ فضای کاری مالک
agentId String? عاملی که در حال حاضر به سندباکس بسته شده، در صورت وجود
bindMounts List<SandboxBindMount> مانت‌های میزبان→مهمان؛ سندباکس فقط مسیرهای میزبانی را می‌بیند که اینجا فهرست شده‌اند
networkEnabled bool (پیش‌فرض true) در اعزام از قاعدهٔ خروج شبکه تعیین می‌شود (فقط وقتی به رد حل شود خاموش است). با خاموش بودن، پروکسی‌های هر نشست همچنان شروع می‌شوند اما فهرست مجاز خالی است، پس هر اتصال خروجی رد می‌شود
egressAllowlist List<String> (پیش‌فرض []) دامنه‌های خروج مجاز اضافی؛ وایلدکاردها پشتیبانی می‌شوند (*.example.com، به‌همراه شکل میانی یک‌برچسبی). اعزام هرگز این را پر نمی‌کند، پس یک اجرای پیچیده خط پایهٔ گزینش‌شده به‌علاوهٔ دامنه‌های GitHub را می‌گیرد
guestWorkdir String? دایرکتوری کاری پیش‌فرض درون سندباکس؛ فراخوانی‌های تک exec می‌توانند آن را بازنویسی کنند
mode Mode (پیش‌فرض chat) قواعد نوشتن فایل‌سیستم را می‌تراشد: chat مانت‌های bind را قابل‌نوشتن نگه می‌دارد؛ review، plan و orchestrate فقط‌خواندنی‌اند
protectedPaths List<String> (پیش‌فرض []) مسیرهای میزبان که هرگز نباید درون سندباکس، در هیچ حالتی، قابل‌نوشتن باشند
runnerStateDirs List<String> (پیش‌فرض []) دایرکتوری‌های میزبان که رانر وضعیت خودش را در آن‌ها نگه می‌دارد، در هر حالت قابل‌نوشتن (یک CLAUDE_CONFIG_DIR مدیریت‌شده با Control Center مورد کنونی است)
execGrantRoots List<String> (پیش‌فرض []) درخت‌های دایرکتوری تأییدشدهٔ اپراتور که مسدودسازی exec دایرکتوری قابل‌نوشتن را روی macOS بازگشایی می‌کنند. Linux آن‌ها را نادیده می‌گیرد — bwrap هیچ مسدودسازی execی روی $HOME برای بازگشایی ندارد

protectedPaths نگه‌دارندهٔ checkoutهای اصلی مخزن‌های ثبت‌شده است که به‌ازای هر اعزام از مخزن‌های فضای کاری حل می‌شوند. عامل‌ها منحصراً در worktreeهای کپی-در-نوشت هر گفتگو کار می‌کنند، پس اصل‌ها هرگز نباید از درون سندباکس قابل‌نوشتن باشند — روی macOS، مجوز نوشتن یکسرهٔ $HOME در Seatbelt در غیر این صورت آن‌ها را از طریق bash قابل‌نوشتن رها می‌کرد. آن‌ها در هر حالت، از جمله chat، به مجموعهٔ رد-نوشتن رازها می‌پیوندند.

هر مسیر رد-خواندن و رد-نوشتن در هر دو صورت نوشتاری‌اش منتشر می‌شود: صورت تحت‌اللفظی و صورت حل‌شدهٔ symlink. کرنل یک قاعدهٔ رد را در برابر مسیر حل‌شده تطبیق می‌دهد، پس قاعده‌ای که در برابر یک مسیر symlinkدار نوشته شده باشد هرگز فعال نمی‌شود — و روی macOS این حالت رایج است نه نامتعارف، چون /tmp به /private/tmp و /var/folders/… به /private/var/folders/… حل می‌شود. ورودی‌هایی که * یا ? دارند به‌عنوان glob در نظر گرفته می‌شوند و دست‌نخورده می‌مانند؛ مسیری که حل نمی‌شود همان‌طور که نوشته شده نگه داشته می‌شود.

فیلد نوع توضیح
hostPath String مسیر مطلق روی میزبان
guestPath String مسیر درون سندباکس (در سندباکس بومی برابر hostPath است؛ برای تقارن آداپتور نگه داشته شده)
readOnly bool (پیش‌فرض false) در صورت true فقط‌خواندنی مانت می‌شود

بازگشتی از SandboxPort.launch:

فیلد نوع توضیح
sessionId String شناسهٔ نشست
backend SandboxBackend کدام بک‌اند در حال اجراست
state SandboxState وضعیت چرخهٔ عمر
error Object? وقتی state برابر error باشد تنظیم می‌شود
details Map<String, Object?> حسابداری خصوصی آداپتور (دایرکتوری کاری، PID، مسیر پروفایل و غیره)
وضعیت توضیح
created spec ساخته شده اما VM هنوز راه نیفتاده است
warm در حال اجرا و آماده برای اجرای فرمان‌ها
active در حال اجرای یک فرمان
suspended متوقف‌شده / نقطه‌گذاری‌شده روی دیسک برای صرفه‌جویی در RAM
destroyed برچیده شده؛ دستگیره دیگر معتبر نیست
error راه‌اندازی شکست خورد — SandboxHandle.error را ببینید
نوع رویداد توضیح
stdout خروجی استاندارد سندباکس
stderr خطای استاندارد سندباکس
exit فرایند سندباکس خارج شد (exitCode را حمل می‌کند)
starting سندباکس در حال راه‌اندازی است
ready سندباکس آماده پذیرش فراخوانی‌های exec است
killed فرایند توسط میزبان کشته شد (OOM، مهلت، توقف دستی)
violation یک سیاست سندباکس نقض شد (یک SandboxViolation را حمل می‌کند)
فیلد نوع توضیح
action String فعل رد‌شده (مثلاً file-read*، network-outbound)
target String مسیر یا میزبانی که هدف عملیات رد‌شده بود
raw String? خط خام لاگ، مفید برای اشکال‌زدایی