[{"data":1,"prerenderedAt":2003},["ShallowReactive",2],{"navigation_docs":3,"-reference-configuration-sandbox-os-sandbox":559,"-reference-configuration-sandbox-os-sandbox-surround":1998},[4,32,55,206,237,524],{"title":5,"path":6,"stem":7,"children":8},"Get started","\u002Fget-started","1.get-started",[9,12,16,20,24,28],{"title":10,"path":6,"stem":11},"Overview","1.get-started\u002Findex",{"title":13,"path":14,"stem":15},"Why Kraft","\u002Fget-started\u002Fwhy-kraft","1.get-started\u002F0.why-kraft",{"title":17,"path":18,"stem":19},"Install","\u002Fget-started\u002Finstall","1.get-started\u002F1.install",{"title":21,"path":22,"stem":23},"Your first work item","\u002Fget-started\u002Ffirst-work-item","1.get-started\u002F2.first-work-item",{"title":25,"path":26,"stem":27},"The real chain","\u002Fget-started\u002Fthe-real-chain","1.get-started\u002F3.the-real-chain",{"title":29,"path":30,"stem":31},"FAQ","\u002Fget-started\u002Ffaq","1.get-started\u002F4.faq",{"title":33,"path":34,"stem":35,"children":36},"Concepts","\u002Fconcepts","2.concepts",[37,39,43,47,51],{"title":10,"path":34,"stem":38},"2.concepts\u002Findex",{"title":40,"path":41,"stem":42},"How a work item runs","\u002Fconcepts\u002Fhow-a-work-item-runs","2.concepts\u002F1.how-a-work-item-runs",{"title":44,"path":45,"stem":46},"Caps and budgets","\u002Fconcepts\u002Fcaps-and-budgets","2.concepts\u002F2.caps-and-budgets",{"title":48,"path":49,"stem":50},"Why a permission gate","\u002Fconcepts\u002Fpermission-gate","2.concepts\u002F3.permission-gate",{"title":52,"path":53,"stem":54},"Vocabulary","\u002Fconcepts\u002Fvocabulary","2.concepts\u002F4.vocabulary",{"title":56,"path":57,"stem":58,"children":59},"Guides","\u002Fguides","3.guides",[60,62,100,126,148,202],{"title":10,"path":57,"stem":61},"3.guides\u002Findex",{"title":63,"path":64,"stem":65,"children":66},"Day to day","\u002Fguides\u002Fday-to-day","3.guides\u002F1.day-to-day\u002Findex",[67,68,72,76,80,84,88,92,96],{"title":10,"path":64,"stem":65},{"title":69,"path":70,"stem":71},"The setup wizard on first start","\u002Fguides\u002Fday-to-day\u002Ffirst-run","3.guides\u002F1.day-to-day\u002F2.first-run",{"title":73,"path":74,"stem":75},"File a work item","\u002Fguides\u002Fday-to-day\u002Ffile-a-work-item","3.guides\u002F1.day-to-day\u002F3.file-a-work-item",{"title":77,"path":78,"stem":79},"Use Kraft from your agent","\u002Fguides\u002Fday-to-day\u002Fagent-integration","3.guides\u002F1.day-to-day\u002F4.agent-integration",{"title":81,"path":82,"stem":83},"Reviewing a change","\u002Fguides\u002Fday-to-day\u002Freview-a-change","3.guides\u002F1.day-to-day\u002F5.review-a-change",{"title":85,"path":86,"stem":87},"Raise a cap on a stopped item","\u002Fguides\u002Fday-to-day\u002Fraise-a-cap","3.guides\u002F1.day-to-day\u002F6.raise-a-cap",{"title":89,"path":90,"stem":91},"Kraft for VS Code","\u002Fguides\u002Fday-to-day\u002Fvscode","3.guides\u002F1.day-to-day\u002F7.vscode",{"title":93,"path":94,"stem":95},"Kraft on a phone","\u002Fguides\u002Fday-to-day\u002Fkraft-on-a-phone","3.guides\u002F1.day-to-day\u002F8.kraft-on-a-phone",{"title":97,"path":98,"stem":99},"Get notified when an item needs you","\u002Fguides\u002Fday-to-day\u002Fnotifications","3.guides\u002F1.day-to-day\u002F9.notifications",{"title":101,"path":102,"stem":103,"children":104},"Customize and automate","\u002Fguides\u002Fcustomize","3.guides\u002F2.customize\u002Findex",[105,106,110,114,118,122],{"title":10,"path":102,"stem":103},{"title":107,"path":108,"stem":109},"Write your own chain","\u002Fguides\u002Fcustomize\u002Fwrite-your-own-chain","3.guides\u002F2.customize\u002F1.write-your-own-chain",{"title":111,"path":112,"stem":113},"Add a security review or a gate reviewer","\u002Fguides\u002Fcustomize\u002Fadd-review-agents","3.guides\u002F2.customize\u002F2.add-review-agents",{"title":115,"path":116,"stem":117},"Schedule or webhook work","\u002Fguides\u002Fcustomize\u002Fschedule-and-webhook-work","3.guides\u002F2.customize\u002F3.schedule-and-webhook-work",{"title":119,"path":120,"stem":121},"Share chains with add-ons","\u002Fguides\u002Fcustomize\u002Fshare-chains-with-addons","3.guides\u002F2.customize\u002F4.share-chains-with-addons",{"title":123,"path":124,"stem":125},"Publish an add-on","\u002Fguides\u002Fcustomize\u002Fpublish-an-add-on","3.guides\u002F2.customize\u002F5.publish-an-add-on",{"title":127,"path":128,"stem":129,"children":130},"Agents and harnesses","\u002Fguides\u002Fharnesses","3.guides\u002F3.harnesses\u002Findex",[131,132,136,140,144],{"title":10,"path":128,"stem":129},{"title":133,"path":134,"stem":135},"Switch a task to another harness","\u002Fguides\u002Fharnesses\u002Fswitch-harness","3.guides\u002F3.harnesses\u002F1.switch-harness",{"title":137,"path":138,"stem":139},"Add or override a harness","\u002Fguides\u002Fharnesses\u002Fadding-a-harness","3.guides\u002F3.harnesses\u002F2.adding-a-harness",{"title":141,"path":142,"stem":143},"Build a worker Kit","\u002Fguides\u002Fharnesses\u002Fworker-kit","3.guides\u002F3.harnesses\u002F3.worker-kit",{"title":145,"path":146,"stem":147},"Give a worker its login","\u002Fguides\u002Fharnesses\u002Fgive-a-worker-its-login","3.guides\u002F3.harnesses\u002F4.give-a-worker-its-login",{"title":149,"path":150,"stem":151,"children":152},"Run the server","\u002Fguides\u002Frun","3.guides\u002F4.run\u002Findex",[153,154,158,162,166,170,174,178,182,186,190,194,198],{"title":10,"path":150,"stem":151},{"title":155,"path":156,"stem":157},"Remote access","\u002Fguides\u002Frun\u002Fremote-access","3.guides\u002F4.run\u002F01.remote-access",{"title":159,"path":160,"stem":161},"Put a proxy in front of Kraft","\u002Fguides\u002Frun\u002Fput-a-proxy-in-front-of-kraft","3.guides\u002F4.run\u002F02.put-a-proxy-in-front-of-kraft",{"title":163,"path":164,"stem":165},"Restart Kraft without ending running agents","\u002Fguides\u002Frun\u002Frestart-kraft","3.guides\u002F4.run\u002F03.restart-kraft",{"title":167,"path":168,"stem":169},"Back up and restore the database","\u002Fguides\u002Frun\u002Fback-up-and-restore","3.guides\u002F4.run\u002F04.back-up-and-restore",{"title":171,"path":172,"stem":173},"Logs and disk space","\u002Fguides\u002Frun\u002Flogs-and-disk-space","3.guides\u002F4.run\u002F05.logs-and-disk-space",{"title":175,"path":176,"stem":177},"Run Kraft as a service","\u002Fguides\u002Frun\u002Frun-as-a-service","3.guides\u002F4.run\u002F06.run-as-a-service",{"title":179,"path":180,"stem":181},"Upgrade Kraft","\u002Fguides\u002Frun\u002Fupgrade-kraft","3.guides\u002F4.run\u002F07.upgrade-kraft",{"title":183,"path":184,"stem":185},"Upgrade your configuration","\u002Fguides\u002Frun\u002Fupgrade-your-configuration","3.guides\u002F4.run\u002F08.upgrade-your-configuration",{"title":187,"path":188,"stem":189},"Upgrade from 1.4","\u002Fguides\u002Frun\u002Fupgrade-from-1-4","3.guides\u002F4.run\u002F09.upgrade-from-1-4",{"title":191,"path":192,"stem":193},"Upgrade from 0.x or a release candidate","\u002Fguides\u002Frun\u002Fupgrade-from-0-x-or-a-release-candidate","3.guides\u002F4.run\u002F10.upgrade-from-0-x-or-a-release-candidate",{"title":195,"path":196,"stem":197},"Roll back to 1.4","\u002Fguides\u002Frun\u002Froll-back","3.guides\u002F4.run\u002F11.roll-back",{"title":199,"path":200,"stem":201},"Run items in your own checkout","\u002Fguides\u002Frun\u002Frun-items-in-place","3.guides\u002F4.run\u002F12.run-items-in-place",{"title":203,"path":204,"stem":205},"Kraft Lite","\u002Fguides\u002Fkraft-lite","3.guides\u002F5.kraft-lite",{"title":207,"path":208,"stem":209,"children":210},"Troubleshooting","\u002Ftroubleshooting","4.troubleshooting",[211,213,217,221,225,229,233],{"title":10,"path":208,"stem":212},"4.troubleshooting\u002Findex",{"title":214,"path":215,"stem":216},"Why did my item stop?","\u002Ftroubleshooting\u002Fwhy-did-my-item-stop","4.troubleshooting\u002F1.why-did-my-item-stop",{"title":218,"path":219,"stem":220},"Stop reasons in detail","\u002Ftroubleshooting\u002Fstop-reasons-in-detail","4.troubleshooting\u002F2.stop-reasons-in-detail",{"title":222,"path":223,"stem":224},"Starting and reaching Kraft","\u002Ftroubleshooting\u002Fstarting-and-reaching-kraft","4.troubleshooting\u002F3.starting-and-reaching-kraft",{"title":226,"path":227,"stem":228},"Doctor failures","\u002Ftroubleshooting\u002Fdoctor-failures","4.troubleshooting\u002F4.doctor-failures",{"title":230,"path":231,"stem":232},"Sandbox and Kit stops","\u002Ftroubleshooting\u002Fsandbox-and-kit-stops","4.troubleshooting\u002F5.sandbox-and-kit-stops",{"title":234,"path":235,"stem":236},"Add-on problems","\u002Ftroubleshooting\u002Faddon-problems","4.troubleshooting\u002F6.addon-problems",{"title":238,"path":239,"stem":240,"children":241},"Reference","\u002Freference","5.reference",[242,244,286,308,312,316,422,440,466,470,474,508,512,516,520],{"title":10,"path":239,"stem":243},"5.reference\u002Findex",{"title":245,"path":246,"stem":247,"children":248},"Web UI","\u002Freference\u002Fweb-ui","5.reference\u002F00.web-ui\u002Findex",[249,250,254,258,262,266,270,274,278,282],{"title":10,"path":246,"stem":247},{"title":251,"path":252,"stem":253},"Sidebar, search, and keyboard","\u002Freference\u002Fweb-ui\u002Fsidebar-search-and-keyboard","5.reference\u002F00.web-ui\u002F0.sidebar-search-and-keyboard",{"title":255,"path":256,"stem":257},"The board","\u002Freference\u002Fweb-ui\u002Fboard","5.reference\u002F00.web-ui\u002F1.board",{"title":259,"path":260,"stem":261},"An item's page","\u002Freference\u002Fweb-ui\u002Fitem-page","5.reference\u002F00.web-ui\u002F2.item-page",{"title":263,"path":264,"stem":265},"Fix loops and test scopes","\u002Freference\u002Fweb-ui\u002Ffix-loops-and-test-scopes","5.reference\u002F00.web-ui\u002F3.fix-loops-and-test-scopes",{"title":267,"path":268,"stem":269},"Config before an item starts","\u002Freference\u002Fweb-ui\u002Fconfig-before-start","5.reference\u002F00.web-ui\u002F4.config-before-start",{"title":271,"path":272,"stem":273},"The review page","\u002Freference\u002Fweb-ui\u002Freview-page","5.reference\u002F00.web-ui\u002F5.review-page",{"title":275,"path":276,"stem":277},"Templates and Settings","\u002Freference\u002Fweb-ui\u002Ftemplates-and-settings","5.reference\u002F00.web-ui\u002F6.templates-and-settings",{"title":279,"path":280,"stem":281},"Analytics","\u002Freference\u002Fweb-ui\u002Fanalytics","5.reference\u002F00.web-ui\u002F7.analytics",{"title":283,"path":284,"stem":285},"Cap stops and raises","\u002Freference\u002Fweb-ui\u002Fcap-stops-and-raises","5.reference\u002F00.web-ui\u002F8.cap-stops-and-raises",{"title":287,"path":288,"stem":289,"children":290},"CLI","\u002Freference\u002Fcli","5.reference\u002F01.cli\u002Findex",[291,292,296,300,304],{"title":10,"path":288,"stem":289},{"title":293,"path":294,"stem":295},"Item verbs","\u002Freference\u002Fcli\u002Fitem","5.reference\u002F01.cli\u002F2.item",{"title":297,"path":298,"stem":299},"View verbs","\u002Freference\u002Fcli\u002Fview","5.reference\u002F01.cli\u002F3.view",{"title":301,"path":302,"stem":303},"Repo verbs","\u002Freference\u002Fcli\u002Frepo","5.reference\u002F01.cli\u002F4.repo",{"title":305,"path":306,"stem":307},"Admin verbs","\u002Freference\u002Fcli\u002Fadmin","5.reference\u002F01.cli\u002F5.admin",{"title":309,"path":310,"stem":311},"Console","\u002Freference\u002Fconsole","5.reference\u002F01.console",{"title":313,"path":314,"stem":315},"Work item statuses","\u002Freference\u002Fstatuses","5.reference\u002F02.statuses",{"title":317,"path":318,"stem":319,"children":320},"Configuration","\u002Freference\u002Fconfiguration","5.reference\u002F03.configuration\u002Findex",[321,322,340,344,348,352,356,360,364,368,406,410,414,418],{"title":10,"path":318,"stem":319},{"title":323,"path":324,"stem":325,"children":326},"repos.yaml","\u002Freference\u002Fconfiguration\u002Frepos","5.reference\u002F03.configuration\u002F02.repos\u002Findex",[327,328,332,336],{"title":10,"path":324,"stem":325},{"title":329,"path":330,"stem":331},"How connect proposes commands","\u002Freference\u002Fconfiguration\u002Frepos\u002Fconnect-proposals","5.reference\u002F03.configuration\u002F02.repos\u002F1.connect-proposals",{"title":333,"path":334,"stem":335},"detectors.yaml","\u002Freference\u002Fconfiguration\u002Frepos\u002Fdetectors","5.reference\u002F03.configuration\u002F02.repos\u002F2.detectors",{"title":337,"path":338,"stem":339},"Workspaces","\u002Freference\u002Fconfiguration\u002Frepos\u002Fworkspaces","5.reference\u002F03.configuration\u002F02.repos\u002F3.workspaces",{"title":341,"path":342,"stem":343},"policy.yaml","\u002Freference\u002Fconfiguration\u002Fpolicy","5.reference\u002F03.configuration\u002F03.policy",{"title":345,"path":346,"stem":347},"Library and chains","\u002Freference\u002Fconfiguration\u002Flibrary-and-chains","5.reference\u002F03.configuration\u002F04.library-and-chains",{"title":349,"path":350,"stem":351},"harnesses.yaml","\u002Freference\u002Fconfiguration\u002Fharnesses-file","5.reference\u002F03.configuration\u002F05.harnesses-file",{"title":353,"path":354,"stem":355},"access.yaml","\u002Freference\u002Fconfiguration\u002Faccess","5.reference\u002F03.configuration\u002F06.access",{"title":357,"path":358,"stem":359},"intake.yaml","\u002Freference\u002Fconfiguration\u002Fintake","5.reference\u002F03.configuration\u002F07.intake",{"title":361,"path":362,"stem":363},"notify.yaml","\u002Freference\u002Fconfiguration\u002Fnotify","5.reference\u002F03.configuration\u002F08.notify",{"title":365,"path":366,"stem":367},"theme.yaml","\u002Freference\u002Fconfiguration\u002Ftheme","5.reference\u002F03.configuration\u002F09.theme",{"title":369,"path":370,"stem":371,"children":372},"Sandboxed workers","\u002Freference\u002Fconfiguration\u002Fsandbox","5.reference\u002F03.configuration\u002F10.sandbox\u002Findex",[373,374,378,382,386,390,394,398,402],{"title":10,"path":370,"stem":371},{"title":375,"path":376,"stem":377},"Resource limits","\u002Freference\u002Fconfiguration\u002Fsandbox\u002Fresource-limits","5.reference\u002F03.configuration\u002F10.sandbox\u002F1.resource-limits",{"title":379,"path":380,"stem":381},"Callbacks from a sandbox","\u002Freference\u002Fconfiguration\u002Fsandbox\u002Fcallbacks","5.reference\u002F03.configuration\u002F10.sandbox\u002F2.callbacks",{"title":383,"path":384,"stem":385},"Network policy","\u002Freference\u002Fconfiguration\u002Fsandbox\u002Fnetwork-policy","5.reference\u002F03.configuration\u002F10.sandbox\u002F3.network-policy",{"title":387,"path":388,"stem":389},"Credentials","\u002Freference\u002Fconfiguration\u002Fsandbox\u002Fcredentials","5.reference\u002F03.configuration\u002F10.sandbox\u002F4.credentials",{"title":391,"path":392,"stem":393},"Kits","\u002Freference\u002Fconfiguration\u002Fsandbox\u002Fkits","5.reference\u002F03.configuration\u002F10.sandbox\u002F5.kits",{"title":395,"path":396,"stem":397},"sandbox.yaml","\u002Freference\u002Fconfiguration\u002Fsandbox\u002Fsandbox-yaml","5.reference\u002F03.configuration\u002F10.sandbox\u002F6.sandbox-yaml",{"title":399,"path":400,"stem":401},"Git in a sandbox","\u002Freference\u002Fconfiguration\u002Fsandbox\u002Fgit-in-a-sandbox","5.reference\u002F03.configuration\u002F10.sandbox\u002F7.git-in-a-sandbox",{"title":403,"path":404,"stem":405},"The OS sandbox","\u002Freference\u002Fconfiguration\u002Fsandbox\u002Fos-sandbox","5.reference\u002F03.configuration\u002F10.sandbox\u002F8.os-sandbox",{"title":407,"path":408,"stem":409},"Environment variables","\u002Freference\u002Fconfiguration\u002Fenvironment-variables","5.reference\u002F03.configuration\u002F11.environment-variables",{"title":411,"path":412,"stem":413},"Add-ons and collections","\u002Freference\u002Fconfiguration\u002Faddons","5.reference\u002F03.configuration\u002F12.addons",{"title":415,"path":416,"stem":417},"Repo libraries","\u002Freference\u002Fconfiguration\u002Frepo-libraries","5.reference\u002F03.configuration\u002F13.repo-libraries",{"title":419,"path":420,"stem":421},"update.yaml","\u002Freference\u002Fconfiguration\u002Fupdate","5.reference\u002F03.configuration\u002F14.update",{"title":423,"path":424,"stem":425,"children":426},"Chain file keys","\u002Freference\u002Fchain-nodes","5.reference\u002F04.chain-nodes\u002Findex",[427,428,432,436],{"title":10,"path":424,"stem":425},{"title":429,"path":430,"stem":431},"Subprocess tasks","\u002Freference\u002Fchain-nodes\u002Fsubprocess-tasks","5.reference\u002F04.chain-nodes\u002F2.subprocess-tasks",{"title":433,"path":434,"stem":435},"Fix loop and judge","\u002Freference\u002Fchain-nodes\u002Ffix-loop","5.reference\u002F04.chain-nodes\u002F3.fix-loop",{"title":437,"path":438,"stem":439},"Result file","\u002Freference\u002Fchain-nodes\u002Fresult-file","5.reference\u002F04.chain-nodes\u002F4.result-file",{"title":441,"path":442,"stem":443,"children":444},"Agent harnesses","\u002Freference\u002Fharnesses","5.reference\u002F05.harnesses\u002Findex",[445,446,450,454,458,462],{"title":10,"path":442,"stem":443},{"title":447,"path":448,"stem":449},"Unattended runs","\u002Freference\u002Fharnesses\u002Funattended-runs","5.reference\u002F05.harnesses\u002F2.unattended-runs",{"title":451,"path":452,"stem":453},"Agent profiles","\u002Freference\u002Fharnesses\u002Fagent-profiles","5.reference\u002F05.harnesses\u002F3.agent-profiles",{"title":455,"path":456,"stem":457},"Harness definition files","\u002Freference\u002Fharnesses\u002Fharness-files","5.reference\u002F05.harnesses\u002F4.harness-files",{"title":459,"path":460,"stem":461},"Fallback and escalation","\u002Freference\u002Fharnesses\u002Ffallback-and-escalation","5.reference\u002F05.harnesses\u002F5.fallback-and-escalation",{"title":463,"path":464,"stem":465},"Cost reporting","\u002Freference\u002Fharnesses\u002Fcost-reporting","5.reference\u002F05.harnesses\u002F6.cost-reporting",{"title":467,"path":468,"stem":469},"Permission gate","\u002Freference\u002Fpermissions","5.reference\u002F06.permissions",{"title":471,"path":472,"stem":473},"Inbound triggers","\u002Freference\u002Ftriggers","5.reference\u002F07.triggers",{"title":475,"path":476,"stem":477,"children":478},"HTTP API","\u002Freference\u002Fhttp-api","5.reference\u002F08.http-api\u002Findex",[479,480,484,488,492,496,500,504],{"title":10,"path":476,"stem":477},{"title":481,"path":482,"stem":483},"Filing, reading and changing work items","\u002Freference\u002Fhttp-api\u002Fwork-items","5.reference\u002F08.http-api\u002F2.work-items",{"title":485,"path":486,"stem":487},"Gates, run control, ending and retry","\u002Freference\u002Fhttp-api\u002Fgates-run-control-and-ending","5.reference\u002F08.http-api\u002F3.gates-run-control-and-ending",{"title":489,"path":490,"stem":491},"The work item object","\u002Freference\u002Fhttp-api\u002Fwork-item-object","5.reference\u002F08.http-api\u002F4.work-item-object",{"title":493,"path":494,"stem":495},"Budget, events, diff and documents","\u002Freference\u002Fhttp-api\u002Fbudget-and-events","5.reference\u002F08.http-api\u002F5.budget-and-events",{"title":497,"path":498,"stem":499},"Repos, chains, harnesses and the server","\u002Freference\u002Fhttp-api\u002Frepos-chains-harnesses-and-server","5.reference\u002F08.http-api\u002F6.repos-chains-harnesses-and-server",{"title":501,"path":502,"stem":503},"Drafts","\u002Freference\u002Fhttp-api\u002Fdrafts","5.reference\u002F08.http-api\u002F7.drafts",{"title":505,"path":506,"stem":507},"Ops for each draft area","\u002Freference\u002Fhttp-api\u002Fdraft-ops","5.reference\u002F08.http-api\u002F8.draft-ops",{"title":509,"path":510,"stem":511},"MCP tools","\u002Freference\u002Fmcp-tools","5.reference\u002F09.mcp-tools",{"title":513,"path":514,"stem":515},"Events","\u002Freference\u002Fevents","5.reference\u002F10.events",{"title":517,"path":518,"stem":519},"VS Code extension","\u002Freference\u002Fvscode-extension","5.reference\u002F11.vscode-extension",{"title":521,"path":522,"stem":523},"Changes from 1.4 to 2.0","\u002Freference\u002Fchanges-from-1-4","5.reference\u002F12.changes-from-1-4",{"title":525,"path":526,"stem":527,"children":528},"Project","\u002Fproject","6.project",[529,531,535,539,543,547,551,555],{"title":10,"path":526,"stem":530},"6.project\u002Findex",{"title":532,"path":533,"stem":534},"Security","\u002Fproject\u002Fsecurity","6.project\u002F1.security",{"title":536,"path":537,"stem":538},"Data and privacy","\u002Fproject\u002Fdata-and-privacy","6.project\u002F2.data-and-privacy",{"title":540,"path":541,"stem":542},"What's new in 2.0","\u002Fproject\u002Fwhats-new","6.project\u002F3.whats-new",{"title":544,"path":545,"stem":546},"Status and support","\u002Fproject\u002Fstatus-and-support","6.project\u002F4.status-and-support",{"title":548,"path":549,"stem":550},"Architecture","\u002Fproject\u002Farchitecture","6.project\u002F5.architecture",{"title":552,"path":553,"stem":554},"Source map","\u002Fproject\u002Fsource-map","6.project\u002F6.source-map",{"title":556,"path":557,"stem":558},"Contributing","\u002Fproject\u002Fcontributing","6.project\u002F7.contributing",{"id":560,"title":403,"body":561,"description":1991,"extension":1992,"links":1993,"meta":1994,"navigation":1995,"path":404,"seo":1996,"stem":405,"__hash__":1997},"docs\u002F5.reference\u002F03.configuration\u002F10.sandbox\u002F8.os-sandbox.md",{"type":562,"value":563,"toc":1975},"minimark",[564,571,589,737,742,835,857,861,868,963,989,991,1002,1009,1029,1032,1126,1131,1194,1203,1206,1226,1292,1296,1303,1358,1367,1370,1384,1491,1501,1512,1516,1540,1552,1556,1648,1651,1664,1785,1792,1796,1840,1844,1847,1926,1961,1971],[565,566,567],"p",{},[568,569,570],"em",{},"Added in 2.3",[565,572,573,577,578,583,584,588],{},[574,575,576],"code",{},"kind: os"," runs a ",[579,580,582],"a",{"href":581},"\u002Fconcepts\u002Fvocabulary#worker","worker"," as a process of your own machine, inside a sandbox the operating system enforces (bubblewrap on Linux, Seatbelt on macOS), with no container image and no container runtime. This page describes Linux; ",[579,585,587],{"href":586},"#on-macos","On macOS"," says what differs. The agent CLI is the one installed on the host. It sees a small, read-only part of the host, its worktree, and a way to reach Kraft that acts as its own session.",[590,591,597],"pre",{"className":592,"code":593,"filename":594,"language":595,"meta":596,"style":596},"language-yaml shiki shiki-themes github-light-high-contrast github-light-high-contrast github-dark-high-contrast","repos:\n  - path: \u002Fhome\u002Fyou\u002Fcode\u002Fmy-service\n    sandbox:\n      kind: os\n      network:\n        install: {allow: [crates.io, static.crates.io]}\n        runtime: {allow: [api.github.com]}\n      expose: [~\u002F.cargo\u002Fbin, ~\u002F.rustup]\n    env:\n      RUSTUP_HOME: \u002Fhome\u002Fyou\u002F.rustup\n","config\u002Frepos.yaml","yaml","",[574,598,599,612,628,636,647,655,682,699,718,726],{"__ignoreMap":596},[600,601,604,608],"span",{"class":602,"line":603},"line",1,[600,605,607],{"class":606},"sb7M8","repos",[600,609,611],{"class":610},"s7iLA",":\n",[600,613,615,618,621,624],{"class":602,"line":614},2,[600,616,617],{"class":610},"  - ",[600,619,620],{"class":606},"path",[600,622,623],{"class":610},": ",[600,625,627],{"class":626},"semJd","\u002Fhome\u002Fyou\u002Fcode\u002Fmy-service\n",[600,629,631,634],{"class":602,"line":630},3,[600,632,633],{"class":606},"    sandbox",[600,635,611],{"class":610},[600,637,639,642,644],{"class":602,"line":638},4,[600,640,641],{"class":606},"      kind",[600,643,623],{"class":610},[600,645,646],{"class":626},"os\n",[600,648,650,653],{"class":602,"line":649},5,[600,651,652],{"class":606},"      network",[600,654,611],{"class":610},[600,656,658,661,664,667,670,673,676,679],{"class":602,"line":657},6,[600,659,660],{"class":606},"        install",[600,662,663],{"class":610},": {",[600,665,666],{"class":606},"allow",[600,668,669],{"class":610},": [",[600,671,672],{"class":626},"crates.io",[600,674,675],{"class":610},", ",[600,677,678],{"class":626},"static.crates.io",[600,680,681],{"class":610},"]}\n",[600,683,685,688,690,692,694,697],{"class":602,"line":684},7,[600,686,687],{"class":606},"        runtime",[600,689,663],{"class":610},[600,691,666],{"class":606},[600,693,669],{"class":610},[600,695,696],{"class":626},"api.github.com",[600,698,681],{"class":610},[600,700,702,705,707,710,712,715],{"class":602,"line":701},8,[600,703,704],{"class":606},"      expose",[600,706,669],{"class":610},[600,708,709],{"class":626},"~\u002F.cargo\u002Fbin",[600,711,675],{"class":610},[600,713,714],{"class":626},"~\u002F.rustup",[600,716,717],{"class":610},"]\n",[600,719,721,724],{"class":602,"line":720},9,[600,722,723],{"class":606},"    env",[600,725,611],{"class":610},[600,727,729,732,734],{"class":602,"line":728},10,[600,730,731],{"class":606},"      RUSTUP_HOME",[600,733,623],{"class":610},[600,735,736],{"class":626},"\u002Fhome\u002Fyou\u002F.rustup\n",[738,739,741],"h2",{"id":740},"keys","Keys",[743,744,745,760],"table",{},[746,747,748],"thead",{},[749,750,751,755],"tr",{},[752,753,754],"th",{},"Key",[752,756,757,758],{},"Under ",[574,759,576],{},[761,762,763,777,793,806],"tbody",{},[749,764,765,771],{},[766,767,768],"td",{},[574,769,770],{},"kind",[766,772,773,776],{},[574,774,775],{},"os",".",[749,778,779,784],{},[766,780,781],{},[574,782,783],{},"network",[766,785,786,787,790,791,776],{},"Required, and not empty: ",[574,788,789],{},"network: {}"," is none. Without it nothing would keep the worker from Kraft's API, so the entry does not load. See ",[579,792,383],{"href":384},[749,794,795,800],{},[766,796,797],{},[574,798,799],{},"expose",[766,801,802,803,776],{},"Optional. Host paths the sandbox may read. See ",[579,804,799],{"href":805},"#expose",[749,807,808,828],{},[766,809,810,675,813,675,816,675,819,675,822,675,825],{},[574,811,812],{},"image",[574,814,815],{},"runtime",[574,817,818],{},"kit",[574,820,821],{},"resources",[574,823,824],{},"unrestricted_network",[574,826,827],{},"credentials",[766,829,830,831,834],{},"Refused: ",[574,832,833],{},"kind: os takes no ...",", and the entry does not load.",[565,836,837,838,841,842,844,845,848,849,852,853,776],{},"An extra ",[574,839,840],{},"ca_bundle"," in ",[579,843,395],{"href":396}," is not applied either. The sandbox trusts the host's own trust store, and the CA files named by the daemon's CA variables (",[574,846,847],{},"SSL_CERT_FILE"," and the like) are mounted read-only. A CA path set only in a repository's ",[574,850,851],{},"env:"," is not one of the daemon's: it must also be listed in ",[579,854,855],{"href":805},[574,856,799],{},[738,858,860],{"id":859},"what-is-mounted","What is mounted",[565,862,863,864,867],{},"Only these reach the sandbox, and nothing of ",[574,865,866],{},"$KRAFT_HOME"," beyond them:",[869,870,871,915,923,929,939,950],"ul",{},[872,873,874,877,878,881,882,675,885,675,888,877,891,894,895,675,898,901,902,877,905,908,909,912,913,776],"li",{},[574,875,876],{},"\u002Fusr"," and ",[574,879,880],{},"\u002Fetc",", read-only, with ",[574,883,884],{},"\u002Fbin",[574,886,887],{},"\u002Fsbin",[574,889,890],{},"\u002Flib",[574,892,893],{},"\u002Flib64"," where the host has them. A fresh ",[574,896,897],{},"\u002Fproc",[574,899,900],{},"\u002Fdev",", and a ",[574,903,904],{},"\u002Ftmp",[574,906,907],{},"\u002Fdev\u002Fshm"," of their own, capped at 2 GiB and 256 MiB. ",[574,910,911],{},"\u002Fvar\u002Ftmp"," is ",[574,914,904],{},[872,916,917,918,922],{},"The worktree, read-write, at its resolved path, with the same session files, documents, results and private copy of the repository's refs that ",[579,919,921],{"href":920},"\u002Freference\u002Fconfiguration\u002Fsandbox#what-reaches-the-container","a container sandbox gets",". The session's own folder (its log and exit file) is never mounted.",[872,924,925,926,776],{},"The item's sandbox home, read-write, as ",[574,927,928],{},"HOME",[872,930,931,932,935,936,776],{},"The directory of the session's channel socket, and the ",[574,933,934],{},"kraft"," shim, read-only at ",[574,937,938],{},"\u002Fopt\u002Fkraft\u002Fbin",[872,940,941,942,945,946,949],{},"The init (",[574,943,944],{},"os_init.py",") and, when ",[574,947,948],{},"\u002Fusr\u002Fbin\u002Fpython3"," is missing or older than 3.10, the prefix of the Python that runs Kraft itself, read-only.",[872,951,952,953,957,958,962],{},"For the agent CLI and the command, the directory each one's real file lives in (see ",[579,954,956],{"href":955},"#how-a-command-is-found","How a command is found","), and each ",[579,959,960],{"href":805},[574,961,799],{}," path, read-only.",[565,964,965,966,968,969,675,972,974,975,675,978,675,981,984,985,988],{},"Anything in a command's directory or an ",[574,967,799],{}," path named ",[574,970,971],{},"credentials.toml",[574,973,827],{}," (cargo's older name for it), ",[574,976,977],{},".npmrc",[574,979,980],{},".netrc",[574,982,983],{},".pypirc"," or ",[574,986,987],{},".git-credentials",", up to three directories down, reads as an empty file. That is a backstop, not the boundary: do not expose a tree because its secrets are named differently.",[738,990,799],{"id":799},[565,992,993,995,996,999,1000,776],{},[574,994,799],{}," lists host paths, each absolute or starting ",[574,997,998],{},"~\u002F",", that the sandbox may read. Use it for a toolchain outside ",[574,1001,876],{},[565,1003,1004,1008],{},[1005,1006,1007],"strong",{},"Every file under an exposed path is shared, secrets included."," Give the narrowest path that works:",[590,1010,1012],{"className":592,"code":1011,"language":595,"meta":596,"style":596},"expose: [~\u002F.cargo\u002Fbin]      # not ~\u002F.cargo, which holds cargo's credentials, its config and the registry cache\n",[574,1013,1014],{"__ignoreMap":596},[600,1015,1016,1018,1020,1022,1025],{"class":602,"line":603},[600,1017,799],{"class":606},[600,1019,669],{"class":610},[600,1021,709],{"class":626},[600,1023,1024],{"class":610},"]      ",[600,1026,1028],{"class":1027},"sq92B","# not ~\u002F.cargo, which holds cargo's credentials, its config and the registry cache\n",[565,1030,1031],{},"A path that does not exist is an error. Some tools need more than their binary:",[869,1033,1034,1047,1066,1107,1120],{},[872,1035,1036,1039,1040,1043,1044,776],{},[1005,1037,1038],{},"A uv virtualenv."," Its ",[574,1041,1042],{},"python"," leads to a uv-managed interpreter, so add ",[574,1045,1046],{},"expose: [~\u002F.local\u002Fshare\u002Fuv\u002Fpython]",[872,1048,1049,1059,1060,1062,1063,1065],{},[1005,1050,1051,1052,1055,1056,776],{},"A virtualenv outside the worktree, or any prefix whose ",[574,1053,1054],{},"bin\u002F"," scripts need its ",[574,1057,1058],{},"lib\u002F"," Expose the whole prefix, not its ",[574,1061,1054],{},": an exposed ",[574,1064,1054],{}," mounts only itself, and its scripts then fail to import.",[872,1067,1068,1071,1072,1074,1075,1077,1078,1081,1082,1084,1085,1088,1089,1091,1092,1095,1096,1098,1099,1102,1103,1106],{},[1005,1069,1070],{},"rustup and cargo."," Their state lives under ",[574,1073,928],{},", and the sandbox's ",[574,1076,928],{}," is its own. Set ",[574,1079,1080],{},"RUSTUP_HOME"," in the repo's ",[574,1083,851],{}," beside ",[574,1086,1087],{},"expose: [~\u002F.cargo\u002Fbin, ~\u002F.rustup]",", as the example above does, so the proxies in ",[574,1090,709],{}," find your toolchains. Leave ",[574,1093,1094],{},"CARGO_HOME"," unset: cargo then keeps its registry cache in the sandbox's own ",[574,1097,928],{},", where it can write, and never sees the rest of your ",[574,1100,1101],{},"~\u002F.cargo",", its credentials and ",[574,1104,1105],{},"config.toml"," included.",[872,1108,1109,1114,1115,1117,1118,776],{},[1005,1110,1111,1112],{},"Any toolchain whose state lives under ",[574,1113,928],{}," (an nvm, a pyenv, a gradle home) needs the variable that points it there in ",[574,1116,851],{},", and its directory in ",[574,1119,799],{},[872,1121,1122,1125],{},[1005,1123,1124],{},"Nix and Homebrew on Linux."," Their CLIs are ELF binaries whose loader and shared libraries live in a store outside the system directories, and the sandbox does not follow those. Expose the store path.",[1127,1128,1130],"h3",{"id":1129},"protected-paths","Protected paths",[565,1132,1133,1134,1136,1137,877,1140,1143,1144,675,1147,675,1150,675,1153,675,1156,675,1159,675,1162,675,1165,675,1168,675,1171,675,1174,877,1177,1180,1181,675,1183,675,1185,675,1187,675,1189,984,1191,1193],{},"A path is refused when it is or holds your home directory, or is, holds or is inside any of ",[574,1135,866],{},", the run and config directories wherever ",[574,1138,1139],{},"KRAFT_RUN_DIR",[574,1141,1142],{},"KRAFT_CONFIG_DIR"," put them, ",[574,1145,1146],{},"~\u002F.ssh",[574,1148,1149],{},"~\u002F.gnupg",[574,1151,1152],{},"~\u002F.config\u002Fgh",[574,1154,1155],{},"~\u002F.config\u002Fglab-cli",[574,1157,1158],{},"~\u002F.aws",[574,1160,1161],{},"~\u002F.kube",[574,1163,1164],{},"~\u002F.docker",[574,1166,1167],{},"~\u002F.claude",[574,1169,1170],{},"~\u002F.codex",[574,1172,1173],{},"~\u002F.local\u002Fshare\u002Fkeyrings",[574,1175,1176],{},"~\u002FLibrary\u002FKeychains",[574,1178,1179],{},"~\u002FLibrary\u002FCookies",". A path whose last component is ",[574,1182,971],{},[574,1184,827],{},[574,1186,977],{},[574,1188,980],{},[574,1190,983],{},[574,1192,987],{}," is refused too.",[565,1195,1196,1197,1199,1200,1202],{},"The same rule applies to a directory Kraft would mount for a command, and ",[574,1198,323],{}," does not load with a protected ",[574,1201,799],{}," path.",[738,1204,956],{"id":1205},"how-a-command-is-found",[565,1207,1208,1209,1212,1213,1215,1216,1219,1220,1222,1223,1225],{},"The agent CLI and the repo's commands run under the names they have on the host ",[574,1210,1211],{},"PATH",", not renamed copies. For each one Kraft follows its symlinks and mounts the directory the real file is in, or the install prefix above it when that directory is a ",[574,1214,1054],{},". A script's ",[574,1217,1218],{},"#!"," interpreter is followed one level. A file under ",[574,1221,876],{}," or inside an ",[574,1224,799],{}," path needs no mount of its own.",[869,1227,1228,1252,1261,1274],{},[872,1229,1230,1239,1240,1242,1243,1245,1246,1248,1249,1251],{},[1005,1231,1232,1233,1235,1236,1238],{},"A ",[574,1234,1054],{}," no ",[574,1237,799],{}," path covers widens to its prefix."," A command in ",[574,1241,709],{}," mounts all of ",[574,1244,1101],{},", with only its credential files shadowed, so a token kept in its ",[574,1247,1105],{}," is readable. Expose ",[574,1250,709],{}," to keep the mount to that directory.",[872,1253,1254,1257,1258,1260],{},[1005,1255,1256],{},"Nothing in the checkout may choose what is mounted."," A file inside the worktree is the repo's own: it adds no mount, and its ",[574,1259,1218],{}," line is not followed.",[872,1262,1263,1264,1267,1268,1270,1271,776],{},"A path-named command (",[574,1265,1266],{},".\u002Fgradlew",") whose symlink leads outside the worktree, the system directories and the ",[574,1269,799],{}," paths is refused: ",[574,1272,1273],{},"\u003Ccommand> leads to \u003Cpath>, outside the sandbox's view; add its directory to the sandbox's expose list",[872,1275,1276,1277,1279,1280,1282,1283,1285,1286,1288,1289,1291],{},"A bare command is looked up on ",[574,1278,938],{},", then the Kraft server's own ",[574,1281,1211],{},", and one found nowhere is refused before launch. ",[574,1284,799],{}," mounts a path but never adds it to ",[574,1287,1211],{},": put the command's directory on the ",[574,1290,1211],{}," the server starts with.",[738,1293,1295],{"id":1294},"the-launcher-and-the-init","The launcher and the init",[565,1297,1298,1299,1302],{},"Each session starts a small launcher on the host, then bubblewrap, then an init that runs the command as the sandbox's process 1. Both are one stdlib-only Python file, started with ",[574,1300,1301],{},"-I -S"," so no environment or current directory can steer them.",[869,1304,1305,1330,1340],{},[872,1306,1307,1308,1311,1312,1315,1316,675,1319,877,1322,1325,1326,1329],{},"The launcher joins a new session keyring, so the daemon's keys are not the worker's. On a host whose architecture it has no ",[574,1309,1310],{},"keyctl"," number for it refuses to start (exit 125). It passes bubblewrap only the environment variables the session needs, by name (",[574,1313,1314],{},"--keep","), so a secret never appears on a command line. It ignores ",[574,1317,1318],{},"SIGINT",[574,1320,1321],{},"SIGTERM",[574,1323,1324],{},"SIGHUP",", and ",[574,1327,1328],{},"--die-with-parent"," ends the sandbox with it, so a killed launcher takes its sandbox down.",[872,1331,1332,1333,675,1335,877,1337,1339],{},"The init relays the session's channel to a loopback port, forwards ",[574,1334,1318],{},[574,1336,1321],{},[574,1338,1324],{}," to the command only when it has left the group the signal already reached, reaps what it leaves, and returns the command's exit code.",[872,1341,1342,1343,1346,1347,1350,1351,1354,1355,776],{},"A command that cannot start sends no started byte, so the session ends as ",[574,1344,1345],{},"config_error"," (a launch that failed), not as a task failure. A command the exec refuses with ",[574,1348,1349],{},"PermissionError"," exits 126 (",[574,1352,1353],{},"kraft: \u003Ccommand> is not executable","). Any other exec failure exits 127, as does the command itself, and prints ",[574,1356,1357],{},"kraft: a command was not found inside the os sandbox; add its directory to the sandbox's expose list",[565,1359,1360,1361,1363,1364,776],{},"A launch Kraft refuses to wrap, for example a path-named command that leads outside the sandbox's view, ends the session as ",[574,1362,1345],{}," with the reason in its log, and does not retry on its own. See ",[579,1365,230],{"href":1366},"\u002Ftroubleshooting\u002Fsandbox-and-kit-stops#a-kind-os-sandbox-is-refused",[738,1368,1369],{"id":1369},"seccomp",[565,1371,1372,1373,984,1376,1379,1380,1383],{},"A filter for the host's architecture (",[574,1374,1375],{},"x86_64",[574,1377,1378],{},"aarch64",") is loaded into every sandbox. It answers ",[574,1381,1382],{},"EPERM"," to:",[869,1385,1386,1396,1407,1418,1426,1437,1448,1475],{},[872,1387,1388,675,1390,675,1393],{},[574,1389,1310],{},[574,1391,1392],{},"add_key",[574,1394,1395],{},"request_key",[872,1397,1398,675,1401,675,1404],{},[574,1399,1400],{},"io_uring_setup",[574,1402,1403],{},"io_uring_enter",[574,1405,1406],{},"io_uring_register",[872,1408,1409,675,1412,675,1415],{},[574,1410,1411],{},"bpf",[574,1413,1414],{},"perf_event_open",[574,1416,1417],{},"userfaultfd",[872,1419,1420,675,1423],{},[574,1421,1422],{},"kexec_load",[574,1424,1425],{},"kexec_file_load",[872,1427,1428,675,1431,675,1434],{},[574,1429,1430],{},"init_module",[574,1432,1433],{},"finit_module",[574,1435,1436],{},"delete_module",[872,1438,1439,675,1442,675,1445],{},[574,1440,1441],{},"mount",[574,1443,1444],{},"umount2",[574,1446,1447],{},"pivot_root",[872,1449,1450,1451,675,1454,675,1457,675,1460,675,1463,675,1466,675,1469,675,1472],{},"the newer mount calls: ",[574,1452,1453],{},"open_tree",[574,1455,1456],{},"move_mount",[574,1458,1459],{},"fsopen",[574,1461,1462],{},"fsconfig",[574,1464,1465],{},"fsmount",[574,1467,1468],{},"fspick",[574,1470,1471],{},"mount_setattr",[574,1473,1474],{},"open_tree_attr",[872,1476,1477,1480,1481,984,1484,1487,1488,1490],{},[574,1478,1479],{},"ioctl"," with ",[574,1482,1483],{},"TIOCSTI",[574,1485,1486],{},"TIOCLINUX",", the requests that push input into a terminal. Every other ",[574,1489,1479],{}," is allowed.",[565,1492,1493,1494,1497,1498,1500],{},"The short runs Kraft makes before a launch, to ask the agent CLI its version or which hooks it trusts, also start in a session of their own (",[574,1495,1496],{},"--new-session","), off the terminal a foreground ",[574,1499,934],{}," holds.",[565,1502,1503,1504,1507,1508,1511],{},"Any other system-call ABI, 32-bit and x32 included, is answered ",[574,1505,1506],{},"ENOSYS",": no 32-bit binary runs. User namespaces inside the sandbox are disabled too (",[574,1509,1510],{},"--disable-userns","), and every capability is dropped.",[738,1513,1515],{"id":1514},"login-by-token","Login by token",[565,1517,1518,1519,1521,1522,1525,1526,1529,1530,1533,1534,1537,1538,776],{},"The sandbox does not mount the agent's own config, so a login kept in ",[574,1520,1167],{}," or the keychain does not reach a worker. Use a token: ",[574,1523,1524],{},"CLAUDE_CODE_OAUTH_TOKEN"," (from ",[574,1527,1528],{},"claude setup-token",") or ",[574,1531,1532],{},"ANTHROPIC_API_KEY",". Both cross by name, as in a container sandbox, and other agents' keys go in ",[574,1535,1536],{},"env_passthrough",". See ",[579,1539,145],{"href":146},[565,1541,1542,1543,1546,1547,1551],{},"A server run as a ",[574,1544,1545],{},"systemd --user"," service has only the environment its unit carries: put the token in the unit, as ",[579,1548,1550],{"href":1549},"\u002Fguides\u002Frun\u002Frun-as-a-service#give-the-service-an-environment","Give the service an environment"," shows.",[738,1553,1555],{"id":1554},"what-it-does-not-do","What it does not do",[869,1557,1558,1566,1575,1587,1599,1606,1618,1623,1636],{},[872,1559,1560,1563,1564,776],{},[1005,1561,1562],{},"It runs on Linux and macOS, and never as root."," On any other system, or when Kraft runs as root, the sandbox refuses to start. The items below are Linux's; macOS's are under ",[579,1565,587],{"href":586},[872,1567,1568,1574],{},[1005,1569,1570,1571,1573],{},"The agent's own ",[574,1572,1167],{}," config is absent."," The worker starts from the sandbox home, so settings kept there are not the ones you use by hand.",[872,1576,1577,1583,1584,1586],{},[1005,1578,1579,1580,776],{},"Key names are visible in ",[574,1581,1582],{},"\u002Fproc\u002Fkeys"," The daemon's keys cannot be read or changed (the launcher joins a new keyring and ",[574,1585,1310],{}," is denied), but their names can be listed.",[872,1588,1589,1595,1596,1598],{},[1005,1590,1591,1592,1594],{},"User-readable files in ",[574,1593,880],{}," are visible."," ",[574,1597,880],{}," is mounted whole, read-only.",[872,1600,1601],{},[1005,1602,1603,1604,776],{},"Nix and Homebrew-on-Linux CLIs need ",[574,1605,799],{},[872,1607,1608,1614,1615,1617],{},[1005,1609,1610,1611,776],{},"Browser tests need ",[574,1612,1613],{},"--no-sandbox"," A browser cannot make its own sandbox here, because user namespaces and ",[574,1616,1441],{}," are denied.",[872,1619,1620],{},[1005,1621,1622],{},"No 32-bit binaries.",[872,1624,1625,1635],{},[1005,1626,1627,1628,675,1630,1632,1633,776],{},"No ",[574,1629,821],{},[574,1631,827],{}," or extra ",[574,1634,840],{}," No CPU, memory or process limits; no credential held by the proxy; the host's trust store only.",[872,1637,1638,1641,1642,1644,1645,1647],{},[1005,1639,1640],{},"A service restart ends its sessions."," Restarting a ",[574,1643,1545],{}," service stops every ",[574,1646,576],{}," session it started: the generated unit's default kill mode stops every process of the service, launchers included, and a sandbox dies with its launcher. Kraft restarted from a shell leaves its sessions running, and reattach adopts them.",[738,1649,587],{"id":1650},"on-macos",[565,1652,1653,1654,1656,1657,1660,1661,1663],{},"On a Mac, ",[574,1655,576],{}," runs the worker under Seatbelt (",[574,1658,1659],{},"\u002Fusr\u002Fbin\u002Fsandbox-exec",") instead of bubblewrap, from the same ",[574,1662,323],{}," entry. The guarantee that matters holds: a worker reaches Kraft only through its session's channel, and cannot decide its own gates. The fence is weaker than Linux's in these ways, which come first because they change what you can run:",[869,1665,1666,1680,1689,1695,1709,1734,1747,1763],{},[872,1667,1668,1671,1672,1675,1676,1679],{},[1005,1669,1670],{},"No local TCP servers."," The worker can connect to nothing on loopback but its session's proxy, and cannot listen on a port. A test suite that starts a server on ",[574,1673,1674],{},"127.0.0.1"," fails. Unix sockets under the session's ",[574,1677,1678],{},"TMPDIR"," work.",[872,1681,1682,1685,1686,1688],{},[1005,1683,1684],{},"Other processes' environment and arguments are readable."," macOS lets any process of your user read them, and Seatbelt cannot close that. A worker can see another session's ",[574,1687,851],{}," values, the daemon's agent token and whatever your shells export. The gate guarantee is not affected.",[872,1690,1691,1694],{},[1005,1692,1693],{},"The proxy URL carries a per-session token."," The proxy listens on loopback, so it answers a request without the token with 407. Other processes of your user can read the token the same way as the environment; other users cannot.",[872,1696,1697,1700,1701,1704,1705,1708],{},[1005,1698,1699],{},"Some names are shared within your user."," Python's ",[574,1702,1703],{},"\u002Fpsm_"," shared-memory and ",[574,1706,1707],{},"\u002Fmp-"," semaphore names are global to your processes, and guessable like the environment.",[872,1710,1711,1595,1716,877,1718,1721,1722,1724,1725,1727,1728,1730,1731,776],{},[1005,1712,1713,1714,776],{},"No private ",[574,1715,904],{},[574,1717,904],{},[574,1719,1720],{},"\u002Fprivate\u002Fvar\u002Ffolders"," are neither readable nor writable. ",[574,1723,1678],{}," is a folder of the session's own, not size-capped and removed when the session ends. A tool that writes to ",[574,1726,904],{}," by name fails. Claude Code is pointed at ",[574,1729,1678],{}," through ",[574,1732,1733],{},"CLAUDE_CODE_TMPDIR",[872,1735,1736,1739,1740,877,1743,1746],{},[1005,1737,1738],{},"No private copy of the refs."," The worker writes its own branch in the repository, and Kraft checks it after the session and puts it back if it holds anything but a commit. A branch moved on the host during the session is not compared first. Creating a branch or a tag, ",[574,1741,1742],{},"git stash",[574,1744,1745],{},"git pack-refs"," fail inside the sandbox; listing works.",[872,1748,1749,1595,1752,877,1755,1758,1759,1762],{},[1005,1750,1751],{},"No fetch or pull inside.",[574,1753,1754],{},"git fetch",[574,1756,1757],{},"git pull"," fail with ",[574,1760,1761],{},"cannot lock ref 'refs\u002Fremotes\u002Forigin\u002F...'",": the remote-tracking refs are in the repository's read-only common directory. Kraft fetches and rebases on the host.",[872,1764,1765,1768,1769,1772,1773,1776,1777,1780,1781,1784],{},[1005,1766,1767],{},"A mach-service allowlist."," The worker reaches the system services a real agent CLI needs, and not LaunchServices, Apple Events, the pasteboard or the keychain. A CLI that needs another service fails until Kraft adds it. TLS is known to work through the system's trust service, which is allowed: a Go CLI's verifier (checked with Go's ",[574,1770,1771],{},"crypto\u002Fx509",", which ",[574,1774,1775],{},"gh"," uses) and ",[574,1778,1779],{},"security verify-cert"," get the host's answer, and OpenSSL-style clients such as Python's ",[574,1782,1783],{},"ssl"," read the host's CA files.",[565,1786,1787,1788,1791],{},"Every commit prints ",[574,1789,1790],{},"error: Unable to create '...\u002Fpacked-refs.lock': Operation not permitted",". That is expected and harmless: git tries to delete pseudo-refs that do not exist at the end of a commit, and the commit lands.",[1127,1793,1795],{"id":1794},"what-it-needs","What it needs",[869,1797,1798,1810,1827],{},[872,1799,1800,1801,984,1804,1806,1807,1809],{},"A Mac of ",[574,1802,1803],{},"arm64",[574,1805,1375],{},", with ",[574,1808,1659],{}," (it ships with macOS), Kraft not running as root, and Kraft not itself inside a sandbox.",[872,1811,1812,1818,1819,1822,1823,1826],{},[1005,1813,1814,1815,776],{},"Homebrew CLIs need ",[574,1816,1817],{},"expose: [\u002Fopt\u002Fhomebrew]"," Their libraries are not followed, as a Linux binary's are not. ",[574,1820,1821],{},"node"," from Homebrew is one. On an Intel Mac Homebrew lives in ",[574,1824,1825],{},"\u002Fusr\u002Flocal",", which is not readable otherwise: expose it.",[872,1828,1829,1832,1833,984,1835,1837,1838,776],{},[1005,1830,1831],{},"A token, not a login."," The keychain is denied, so use ",[574,1834,1524],{},[574,1836,1532],{},". A server run by launchd has only the environment its plist carries: ",[579,1839,1550],{"href":1549},[1127,1841,1843],{"id":1842},"what-the-worker-sees","What the worker sees",[565,1845,1846],{},"The profile denies everything and allows back what a session needs. The worker can read:",[869,1848,1849,1864,1886,1897,1918],{},[872,1850,1851,1853,1854,1856,1857,675,1859,877,1861,776],{},[574,1852,876],{}," (not ",[574,1855,1825],{},"), ",[574,1858,884],{},[574,1860,887],{},[574,1862,1863],{},"\u002FSystem",[872,1865,1866,1869,1870,1873,1874,1877,1878,1881,1882,1885],{},[574,1867,1868],{},"\u002FLibrary\u002FDeveloper\u002FCommandLineTools"," and the selected Xcode's ",[574,1871,1872],{},"Contents",", since ",[574,1875,1876],{},"\u002Fusr\u002Fbin\u002Fgit"," is a shim into them, the one file ",[574,1879,1880],{},"\u002FLibrary\u002FPreferences\u002Fcom.apple.dt.Xcode.plist",", the Xcode licence version, and ",[574,1883,1884],{},"MobileDevice.framework",", which Xcode loads until its first launch has run.",[872,1887,1888,675,1891,877,1894,776],{},[574,1889,1890],{},"\u002Fprivate\u002Fetc",[574,1892,1893],{},"\u002Fprivate\u002Fvar\u002Fdb\u002Ftimezone",[574,1895,1896],{},"\u002Fprivate\u002Fvar\u002Fdb\u002Fdyld",[872,1898,1899,675,1902,675,1905,675,1908,675,1911,877,1914,1917],{},[574,1900,1901],{},"\u002Fdev\u002Fnull",[574,1903,1904],{},"\u002Fdev\u002Fzero",[574,1906,1907],{},"\u002Fdev\u002Frandom",[574,1909,1910],{},"\u002Fdev\u002Furandom",[574,1912,1913],{},"\u002Fdev\u002Fdtracehelper",[574,1915,1916],{},"\u002Fdev\u002Ffd",", and no other terminal of yours.",[872,1919,1920,1921,1925],{},"The agent CLI and the command, each ",[579,1922,1923],{"href":805},[574,1924,799],{}," path, the host's CA files and Kraft's own Python.",[565,1927,1928,1929,1931,1932,675,1935,675,1938,675,1941,984,1944,1947,1948,1951,1952,877,1954,1956,1957,776],{},"It reads and writes its worktree, its sandbox home and its ",[574,1930,1678],{},". Nothing else of ",[574,1933,1934],{},"\u002FUsers",[574,1936,1937],{},"\u002FVolumes",[574,1939,1940],{},"\u002FApplications",[574,1942,1943],{},"\u002Fopt",[574,1945,1946],{},"\u002FLibrary"," is readable, though ",[574,1949,1950],{},"stat"," works anywhere. ",[574,1953,1176],{},[574,1955,1179],{}," are ",[579,1958,1960],{"href":1959},"#protected-paths","protected paths",[565,1962,1963,1964,1970],{},"It can signal only processes of its own sandbox, and nothing it starts outlives the session: Kraft ends every process of the session when it exits, including ones that left the process group. A process it cannot end is logged and recorded as a ",[579,1965,1967],{"href":1966},"\u002Freference\u002Fevents#sandbox",[574,1968,1969],{},"sandbox_session_outlived"," event with its pid.",[1972,1973,1974],"style",{},"html pre.shiki code .sb7M8, html code.shiki .sb7M8{--shiki-light:#024C1A;--shiki-default:#024C1A;--shiki-dark:#72F088}html pre.shiki code .s7iLA, html code.shiki .s7iLA{--shiki-light:#0E1116;--shiki-default:#0E1116;--shiki-dark:#F0F3F6}html pre.shiki code .semJd, html code.shiki .semJd{--shiki-light:#032563;--shiki-default:#032563;--shiki-dark:#ADDCFF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sq92B, html code.shiki .sq92B{--shiki-light:#66707B;--shiki-default:#66707B;--shiki-dark:#BDC4CC}",{"title":596,"searchDepth":614,"depth":614,"links":1976},[1977,1978,1979,1982,1983,1984,1985,1986,1987],{"id":740,"depth":614,"text":741},{"id":859,"depth":614,"text":860},{"id":799,"depth":614,"text":799,"children":1980},[1981],{"id":1129,"depth":630,"text":1130},{"id":1205,"depth":614,"text":956},{"id":1294,"depth":614,"text":1295},{"id":1369,"depth":614,"text":1369},{"id":1514,"depth":614,"text":1515},{"id":1554,"depth":614,"text":1555},{"id":1650,"depth":614,"text":587,"children":1988},[1989,1990],{"id":1794,"depth":630,"text":1795},{"id":1842,"depth":630,"text":1843},"Run a repository's workers under the host's own process sandbox, on Linux or macOS, with no image, and what that sandbox mounts, exposes, denies and does not do.","md",null,{},true,{"title":403,"description":1991},"J_64CKbBf7hZdy6RsvjnbMzegcvCtpNyn5s2D6S2JF0",[1999,2001],{"title":399,"path":400,"stem":401,"description":2000,"children":-1},"How a sandboxed worker's repository refs, workspace members and worktree are handled, and what Kraft's own git does and does not do there.",{"title":407,"path":408,"stem":409,"description":2002,"children":-1},"Every KRAFT_* variable Kraft reads or sets, the other variables it reads, and the variables a worker's environment gets.",1792763539410]