דלגו לתוכן

מנועי ארגז חול

בקאנד ערך פלטפורמה זמינות
VM מסוגר microvm macOS, Linux, Windows (היפרוויזר microVM) מותנה בבדיקה של סביבת הריצה של microVM; חל על מסופים אינטראקטיביים — המעטפת רצה בתוך תחנה, לא על המארח
מקורי native macOS (Seatbelt), Linux ו-WSL2 (bubblewrap) נבדק באתחול; Linux מסרב להרצה שדורשת דחיות כתיבה לפי תבניות גלוב של שמות קבצים, שאין ביכולתו של bubblewrap לאכוף
ללא none הכול ברירת המחדל של הפענוח, ברירת המחדל של המסוף והנסיגה כשאף בקאנד אינו זמין או כשמוגדר --sandbox off

SandboxBackend.fromName מחזיר none עבור ערך שמור שהוא null או שאינו מזוהה. ערך שמור של "docker" ממופה ל-native.

microvm הוא הבקאנד היחיד עם גבול קרנל — עומס העבודה באמת אינו רץ על המארח. הוא לעולם אינו נסיגה שקטה לאף אחד מהכיוונים: בקשה מפורשת שלו במארח שאינו יכול להעלות VM נכשלת בקול רם (“ביקשתי VM וקיבלתי את הלפטופ שלי” היא ההידרדרות היחידה שהמתחם הסגור אינו יכול להרשות לעצמו), ורק ברירת מחדל לא מצוינת יורדת אל native, עם שורת יומן שאומרת זאת. הבקאנד בפועל של המושב מדווח חזרה ללקוח ומסומן בתג, כך שמסוף לעולם אינו יכול להצהיר שהוא מסוגר בעודו רץ על המארח. מחזור החיים, התמונות והיציאה של ה-VM עצמו מכוסים במדריך העזר לתחנות.

ב-Windows אין ארגז חול מקורי. NativeSandboxAdapter.probe מחזיר available: false בכל מארח שאינו macOS או Linux/WSL2. לכן שיגור סוכן ב-Windows משתמש ב-none. הבקאנד microvm נפרד: הוא מותנה בבדיקה של סביבת הריצה של microVM וחל על מסופים אינטראקטיביים ועל תחנות, ולא על עטיפת bash במארח.

הבדיקה היא מה שמשער את האכיפה. היא רצה פעם אחת, באתחול השרת.

מארח available הערה / רמז
macOS true sandbox-exec (Seatbelt) — בידוד מרחבי שמות, בלי גבול קרנל
Linux או WSL2 עם bwrap ו-socat true bubblewrap — בידוד מרחבי שמות, בלי גבול קרנל
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 של Apple (אותה מערכת שמשמשת את macOS App Sandbox).

הפרופיל שנוצר הוא מתירני כברירת מחדל ((allow default)) ומגלף ממנו דחיות, משום שפרופיל שדוחה הכול כברירת מחדל הופך מעטפת אינטראקטיבית לבלתי שמישה — יש יותר מדי קריאות dyld, XPC ו-Mach כדי למנות אותן. ההשלכה המעשית היא שסוכן עדיין יכול לקרוא את רוב המארח; הצ׳קאאוטים הרשומים שלכם מוגנים בדחיות כתיבה ושינוי-שם מפורשות, ולא בהיעדר נראות.

  • קריאות ממערכת הקבצים: נדחות עבור נתיבי הסודות (~/.ssh, ~/.aws, ~/.gnupg, ~/.config/gh, ~/Library/Keychains ושאר קבוצת דחיית הקריאה)
  • כתיבה למערכת הקבצים: מאופסת לדחייה, ואז היתרים מפורשים לתת-נתיבים, כשנתיבי הדחייה המחייבת (קובצי rc של המעטפת, .git/hooks, תצורת Claude, צ׳קאאוטים מוגנים) חסומים גם בתוך שורש שניתן לכתיבה. file-write-unlink נדחה בכל נתיב נדחה ובאבותיו, כך ששינוי שם אינו יכול לעקוף דחיית כתיבה
  • תהליכים: דחיות process-exec לקובצי ההרצה המסוכנים תמיד, בתוספת חסימה של הרצת כל דבר מספרייה הניתנת לכתיבה
  • רשת: מוגבלת לפורטי ה-proxy בתהליך בתוספת DNS — loopback אינו מותר באופן גורף ושקעי Unix אינם מותרים. ה-proxy-ים אוכפים את רשימת ההיתר של הדומיינים לכל מושב (ראו יציאה לרשת)
  • תצורה: הפרופילים נוצרים לכל הפעלה מתוך ה-SandboxSpec

דרישות:

  • sandbox-exec (/usr/bin/sandbox-exec), חלק מכל התקנת macOS — לא נדרשים כלים נוספים

משתמש ב-bubblewrap (bwrap) כדי ליצור מרחבי שמות של עיגון ו-PID.

  • מערכת הקבצים: יוצר מרחב שמות של עיגון שבו רק הנתיבים המעוגנים נראים
  • רשת: רץ במרחב שמות רשת לא משותף; התעבורה מגיעה ל-proxy-ים של המארח דרך גשרי שקעי Unix של socat
  • תהליכים: בידוד מרחב שמות של PID

דרישות:

  • bubblewrap ו-socat מותקנים (sudo apt-get install bubblewrap socat ב-Debian/Ubuntu)
  • תמיכה במרחבי שמות של הקרנל (תקנית ב-Linux מודרני)

Bubblewrap יכול להסתיר נתיב קיים אבל אינו יכול לאסור קובץ .env, *.key או *.pem חדש בתוך עיגון שניתן לכתיבה. מדיניות השיגור התקנית דורשת את דחיות תבניות-הגלוב האלה של שמות קבצים, ולכן שיגור סוכן מקורי נכשל באופן סגור ב-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 של מעטפת המארח.

כשארגז חול מופעל, הגישה היוצאת לרשת נאכפת בידי proxy-ים בתהליך, ולא בכללי חומת אש של הקרנל. במארח שאין בו בקאנד זמין שום דבר מזה אינו חל, ותהליך בן מגיע לרשת בדיוק כפי שמשתמש השרת היה מגיע.

  • סביבת הריצה של ארגז החול מריצה proxy של HTTP/HTTPS ו-proxy של SOCKS5 על 127.0.0.1 בתוך תהליך ה-cc_server, זוג אחד לכל מושב מוגבל (http_proxy.dart, socks_proxy.dart). הרצות במקביל אינן חולקות זוג.
  • תהליכים בארגז חול מקבלים משתני סביבה של proxy (HTTP_PROXY/HTTPS_PROXY → ה-proxy של HTTP, ALL_PROXY → ה-proxy של SOCKS; GIT_SSH_COMMAND מנתב git-over-SSH דרך ה-proxy של SOCKS). ב-Linux מרחב השמות של הרשת אינו משותף, ולכן ה-proxy-ים הם הדרך היחידה החוצה; ב-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 עדיין מפעיל את ה-proxy-ים של המושב, אבל רשימת ההיתר ריקה ולכן כל חיבור יוצא נדחה. השיגור קובע את הערך הזה בתחילת ההרצה לפי הכלל גישה לרשת: חסום מכבה את הרשת, ואפשר ושאל קודם משאירים אותה דלוקה.

המפרט שמועבר אל SandboxPort.launch:

שדה סוג תיאור
sessionId String מזהה מושב יציב (בדרך כלל מזהה המרחב)
workspaceId String מזהה סביבת העבודה הבעלים
agentId String? הסוכן המשויך כרגע לארגז החול, אם יש כזה
bindMounts List<SandboxBindMount> עיגונים ממארח לאורח; ארגז החול רואה רק את נתיבי המארח הרשומים כאן
networkEnabled bool (ברירת מחדל true) נקבע בשיגור לפי כלל התעבורה היוצאת לרשת (כבוי רק כשהוא מוכרע כחסום). כשהוא כבוי, ה-proxy-ים של המושב עדיין עולים אבל רשימת ההיתר ריקה ולכן כל חיבור יוצא נדחה
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 מחזיק את הצ׳קאאוטים המקוריים של המאגרים הרשומים, הנפתרים לכל שיגור מתוך המאגרים של סביבת העבודה. סוכנים עובדים אך ורק בעצי עבודה בהעתקה בעת כתיבה לכל שיחה, ולכן המקוריים אסור שלעולם יהיו ניתנים לכתיבה מתוך ארגז החול — ב-macOS, היתר הכתיבה הגורף של Seatbelt ל-$HOME היה אחרת משאיר אותם ניתנים לכתיבה דרך bash. הם מצטרפים לקבוצת דחיית הכתיבה של הסודות בכל מצב, כולל chat.

כל נתיב של דחיית קריאה ושל דחיית כתיבה נפלט גם בצורתו המילולית וגם בצורתו המפוענחת-סימבולית. הקרנל מתאים כלל דחייה לנתיב המפוענח, ולכן כלל שנכתב כנגד נתיב עם קישור סימבולי לעולם אינו פועל — וב-macOS זה המקרה השכיח ולא החריג, משום ש-/tmp נפתר ל-/private/tmp ו-/var/folders/… ל-/private/var/folders/…. רשומות שמכילות * או ? מטופלות כגלובים ונשארות כפי שהן; נתיב שאינו נפתר נשמר כפי שנכתב.

שדה סוג תיאור
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 המפרט נוצר אבל ה-VM טרם עלה
warm רץ ומוכן להריץ פקודות
active מריץ פקודה כעת
suspended מושהה / נשמר כנקודת ביקורת לדיסק כדי לחסוך בזיכרון
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? שורת יומן גולמית, שימושית לניפוי באגים