feat(box): support explicit host backend (#2498)

This commit is contained in:
huanghuoguoguo
2026-09-02 21:22:14 +08:00
committed by GitHub
parent 601c6975ea
commit 7b7d3f04e8
9 changed files with 36 additions and 12 deletions
+2 -1
View File
@@ -1349,7 +1349,8 @@
"local-agent",
"tools",
"e2b",
"nsjail"
"nsjail",
"host"
],
"automation": "",
"setup_automation": [],
+7 -1
View File
@@ -63,7 +63,7 @@ Key settings:
| `api.global_api_key` | **Global API key** for the HTTP API + MCP server. Non-empty = accepted with no login/DB record; no `lbk_` prefix required. Empty = disabled. Plaintext — trusted/internal only, serve over HTTPS. |
| `plugin.runtime_ws_url` | Standalone plugin runtime WS URL (e.g. `ws://langbot_plugin_runtime:5400/control/ws`) |
| `box.enabled` | Master switch for the Box sandbox runtime |
| `box.backend` | `local` (Docker/nsjail autopick) / `docker` / `nsjail` / `e2b`; env override `BOX__BACKEND` |
| `box.backend` | `local` (Docker/nsjail autopick) / `docker` / `nsjail` / `e2b` / explicit unsafe `host`; env override `BOX__BACKEND` |
| `box.runtime.endpoint` | External Box runtime URL (e.g. `ws://127.0.0.1:5410`); empty = local auto-managed |
Many keys have `ENV__SUBKEY` overrides (e.g. `BOX__BACKEND`, `BOX__ENABLED`).
@@ -75,6 +75,10 @@ Many keys have `ENV__SUBKEY` overrides (e.g. `BOX__BACKEND`, `BOX__ENABLED`).
with `--standalone-runtime`.
- Box has a parallel `--standalone-box` flag; the Docker box host is
`langbot_box:5410`.
- `box.backend: host` runs commands directly as the Box Runtime system user.
It is never auto-selected, provides no sandbox isolation, and is only for
trusted local development. A WebSocket-controlled host backend requires
`LANGBOT_BOX_CONTROL_TOKEN`; local stdio control is allowed.
## Global API key — enabling for agents/automation
@@ -93,5 +97,7 @@ login session. See `langbot-mcp-ops` for using it, and `docs/API_KEY_AUTH.md`.
- "No supported sandbox backend (Docker / nsjail / E2B)" with Docker running
usually means the user isn't in the `docker` group →
`sudo usermod -aG docker <user>` and restart in a new shell.
- Do not use `box.backend: host` as a production fallback. It cannot enforce
image, filesystem, network, PID, CPU, memory, or storage isolation.
- Box root host/container path mismatch breaks sandbox container creation.
- Don't commit a non-empty `api.global_api_key` to version control.
@@ -13,6 +13,7 @@ tags:
- tools
- e2b
- nsjail
- host
skills:
- langbot-env-setup
- langbot-testing
@@ -23,7 +24,7 @@ env:
- LANGBOT_LOCAL_AGENT_PIPELINE_NAME
preconditions:
- "LANGBOT_LOCAL_AGENT_PIPELINE_URL or LANGBOT_LOCAL_AGENT_PIPELINE_NAME points to the local-agent pipeline under test."
- "LangBot is started with the sandbox backend intended for this run, such as e2b or nsjail."
- "LangBot is started with the Box backend intended for this run, such as e2b, nsjail, or explicit host development mode."
- "The selected model route supports tool/function calling strongly enough to invoke sandbox tools."
steps:
- "Start LangBot with the target sandbox backend and confirm the Box status UI or LANGBOT_BACKEND_URL /api/v1/box/status reports the expected backend."
@@ -33,7 +34,7 @@ steps:
checks:
- "UI: Debug Chat final assistant response contains E2E_OK:<skill-name>."
- "Logs: The model called exec, register_skill, activate, then exec again from the activated skill path."
- "Logs: The selected backend name is the expected one, such as e2b or nsjail."
- "Logs: The selected backend name is the expected one, such as e2b, nsjail, or host."
- "Skill store: The registered package and activated writeback match references/sandbox-skill-authoring.md."
- "Box status: recent_error_count is 0 after the run."
evidence_required:
@@ -4,7 +4,7 @@
Verify that Local Agent can use sandbox tools to create, register, activate, and use a LangBot skill package through the same path a user would exercise in Debug Chat.
This flow applies to Docker, nsjail, and E2B backends. API calls are useful diagnostics, but the primary pass/fail signal is the model-driven Debug Chat tool sequence.
This flow applies to Docker, nsjail, E2B, and the explicit host development backend. Host runs commands directly as the Box Runtime user and must never be treated as sandbox-isolation coverage. API calls are useful diagnostics, but the primary pass/fail signal is the model-driven Debug Chat tool sequence.
## Preconditions
@@ -13,6 +13,7 @@ This flow applies to Docker, nsjail, and E2B backends. API calls are useful diag
- `BOX_BACKEND=e2b` when validating E2B.
- `BOX_BACKEND=nsjail` when validating nsjail.
- `BOX_BACKEND=local` or `docker` when validating local container fallback.
- `BOX_BACKEND=host` only when validating explicit, trusted local direct execution.
3. Confirm `/api/v1/box/status` reports `available: true` and the expected backend name.
4. Confirm Debug Chat uses a model with function-calling ability.
5. Confirm backend logs say native sandbox tools are available.
@@ -71,7 +72,7 @@ Backend logs should show:
- `register_skill`
- `activate`
- a second `exec` whose workdir is `/workspace/.skills/<skill-name>`
- `backend=e2b`, `backend=nsjail`, or the expected local backend
- `backend=e2b`, `backend=nsjail`, `backend=host`, or the expected local backend
After the run, verify the skill store through the UI or API:
@@ -125,6 +126,8 @@ For E2B raw HTTP diagnostics, include a valid template id such as `base`; a miss
- Session metadata should keep LangBot logical paths such as `/workspace`; storing provider-internal paths can make later requests look incompatible.
- nsjail versions differ. Some expose only `--disable_clone_new*` flags and use `--bindmount` instead of `--rw_bind`.
- On WSL, cgroup v2 may exist but not be writable. The backend should warn and fall back to rlimits rather than fail the sandbox.
- The host backend does not honor sandbox image, network, rootfs, process, or
resource isolation. Use a disposable workspace and low-privilege account.
- If `ALL_PROXY` uses a SOCKS URL and `socksio` is not installed, some Python HTTP clients can fail during startup. Prefer consistent HTTP proxy variables unless SOCKS support is installed.
## Related Troubleshooting
@@ -3,7 +3,7 @@ title: "Native sandbox tools are unavailable even though a backend is configured
date: 2026-05-18
symptoms:
- "Backend logs show Native sandbox tools (exec/read/write/edit/glob/grep) are NOT available."
- "The Box runtime later reports that E2B, nsjail, or Docker is configured."
- "The Box runtime later reports that E2B, nsjail, Docker, or explicit host mode is configured."
- "Debug Chat does not expose exec, register_skill, or activate as usable tools."
patterns:
- "Native sandbox tools ... are NOT available"
@@ -19,6 +19,7 @@ fix_steps:
- "Ensure the Box runtime reselects a backend when get_backend_info is called and the cached backend is empty."
- "For E2B, verify the key without printing it and confirm any required template setting."
- "For nsjail, run nsjail --help and confirm the binary is on PATH for the LangBot process."
- "For trusted local development only, explicitly set box.backend=host; never use host as a production sandbox fallback."
verification: "Run sandbox-skill-authoring-e2e. Logs should show Native sandbox tools are available and /api/v1/box/status should report available=true with the expected backend."
related_cases:
- sandbox-skill-authoring-e2e
+10 -3
View File
@@ -455,7 +455,9 @@ class BoxService:
async def _require_validated_workspace_sandbox(self, execution_context: ExecutionContext) -> None:
if not self._available:
raise BoxError('Box runtime is not available. Install and start Docker to use sandbox features.')
raise BoxError(
'Box runtime is not available. Configure an available Box backend before using Box features.'
)
if self._cloud_managed:
if self._admission is None:
raise BoxAdmissionError('Cloud Box sandbox admission is unavailable')
@@ -565,7 +567,9 @@ class BoxService:
skip_host_mount_validation: bool = False,
) -> dict:
if not self._available:
raise BoxError('Box runtime is not available. Install and start Docker to use sandbox features.')
raise BoxError(
'Box runtime is not available. Configure an available Box backend before using Box features.'
)
execution_context = await self._validated_execution_context(self._query_execution_context(query))
spec_payload = self._managed_policy_payload(execution_context, spec_payload)
await self._require_validated_workspace_sandbox(execution_context)
@@ -2142,5 +2146,8 @@ class BoxService:
if backend_name:
payload['connector_error'] = f'Configured sandbox backend "{backend_name}" is unavailable'
else:
payload['connector_error'] = 'No supported sandbox backend (Docker / nsjail / E2B) is available'
payload['connector_error'] = (
'No supported sandbox backend (Docker / nsjail / E2B) is available. '
'Trusted local development may explicitly select the unsafe host backend.'
)
return payload
@@ -222,6 +222,7 @@ class NativeToolLoader(loader.ToolLoader):
self.ap.logger.warning(
'Native sandbox tools (exec/read/write/edit/glob/grep) are NOT available. '
'No sandbox backend (Docker/nsjail/E2B) is ready. '
'Trusted local development may explicitly select box.backend=host. '
'The LLM will not have access to code execution or file operation tools.'
)
@@ -42,7 +42,8 @@ class SkillToolLoader(loader.ToolLoader):
else:
self.ap.logger.info(
'Skill tools (activate/register_skill) are NOT available. '
'No sandbox backend (Docker/nsjail/E2B) is ready.'
'No sandbox backend (Docker/nsjail/E2B) is ready. '
'Trusted local development may explicitly select box.backend=host.'
)
async def _check_sandbox_available(self) -> bool:
+4 -1
View File
@@ -331,7 +331,10 @@ box:
# skill tool, skill add/edit, and stdio-mode MCP servers. Skills can still
# be listed read-only and http/sse MCP servers continue to work.
enabled: true
backend: 'local' # 'local' (Docker/nsjail), 'docker', 'nsjail', or 'e2b'. Can be written via BOX__BACKEND.
# 'host' runs commands directly as the Box Runtime user without sandbox
# isolation. It is never auto-selected and is only for trusted local
# development. Can be written via BOX__BACKEND.
backend: 'local' # 'local' (Docker/nsjail), 'docker', 'nsjail', 'e2b', or explicit unsafe 'host'.
runtime:
# LANGBOT_BOX_CONTROL_TOKEN is optional for OSS external WebSocket
# runtimes. To protect an exposed endpoint, set the same strong secret