From 7b7d3f04e8c8647cefc8aad85ac91e6e8d9a06bb Mon Sep 17 00:00:00 2001 From: huanghuoguoguo <1051233107@qq.com> Date: Wed, 2 Sep 2026 21:22:14 +0800 Subject: [PATCH] feat(box): support explicit host backend (#2498) --- skills/skills.index.json | 3 ++- skills/skills/langbot-deploy/SKILL.md | 8 +++++++- .../cases/sandbox-skill-authoring-e2e.yaml | 5 +++-- .../references/sandbox-skill-authoring.md | 7 +++++-- .../sandbox-native-tools-unavailable.yaml | 3 ++- src/langbot/pkg/box/service.py | 13 ++++++++++--- src/langbot/pkg/provider/tools/loaders/native.py | 1 + .../pkg/provider/tools/loaders/skill_authoring.py | 3 ++- src/langbot/templates/config.yaml | 5 ++++- 9 files changed, 36 insertions(+), 12 deletions(-) diff --git a/skills/skills.index.json b/skills/skills.index.json index 640996adc..b109490b3 100644 --- a/skills/skills.index.json +++ b/skills/skills.index.json @@ -1349,7 +1349,8 @@ "local-agent", "tools", "e2b", - "nsjail" + "nsjail", + "host" ], "automation": "", "setup_automation": [], diff --git a/skills/skills/langbot-deploy/SKILL.md b/skills/skills/langbot-deploy/SKILL.md index e03182e01..b26ef13a0 100644 --- a/skills/skills/langbot-deploy/SKILL.md +++ b/skills/skills/langbot-deploy/SKILL.md @@ -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 ` 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. diff --git a/skills/skills/langbot-testing/cases/sandbox-skill-authoring-e2e.yaml b/skills/skills/langbot-testing/cases/sandbox-skill-authoring-e2e.yaml index 91bb97fd3..608266c4e 100644 --- a/skills/skills/langbot-testing/cases/sandbox-skill-authoring-e2e.yaml +++ b/skills/skills/langbot-testing/cases/sandbox-skill-authoring-e2e.yaml @@ -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:." - "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: diff --git a/skills/skills/langbot-testing/references/sandbox-skill-authoring.md b/skills/skills/langbot-testing/references/sandbox-skill-authoring.md index db9b82647..1d4b9a02f 100644 --- a/skills/skills/langbot-testing/references/sandbox-skill-authoring.md +++ b/skills/skills/langbot-testing/references/sandbox-skill-authoring.md @@ -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/` -- `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 diff --git a/skills/skills/langbot-testing/troubleshooting/sandbox-native-tools-unavailable.yaml b/skills/skills/langbot-testing/troubleshooting/sandbox-native-tools-unavailable.yaml index 6c6106036..e5da2dbcb 100644 --- a/skills/skills/langbot-testing/troubleshooting/sandbox-native-tools-unavailable.yaml +++ b/skills/skills/langbot-testing/troubleshooting/sandbox-native-tools-unavailable.yaml @@ -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 diff --git a/src/langbot/pkg/box/service.py b/src/langbot/pkg/box/service.py index c8554cc52..bf626090b 100644 --- a/src/langbot/pkg/box/service.py +++ b/src/langbot/pkg/box/service.py @@ -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 diff --git a/src/langbot/pkg/provider/tools/loaders/native.py b/src/langbot/pkg/provider/tools/loaders/native.py index d2dfdc969..e51195678 100644 --- a/src/langbot/pkg/provider/tools/loaders/native.py +++ b/src/langbot/pkg/provider/tools/loaders/native.py @@ -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.' ) diff --git a/src/langbot/pkg/provider/tools/loaders/skill_authoring.py b/src/langbot/pkg/provider/tools/loaders/skill_authoring.py index 01e297842..5be0dc3e1 100644 --- a/src/langbot/pkg/provider/tools/loaders/skill_authoring.py +++ b/src/langbot/pkg/provider/tools/loaders/skill_authoring.py @@ -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: diff --git a/src/langbot/templates/config.yaml b/src/langbot/templates/config.yaml index ab98d7af8..6b3c716fa 100644 --- a/src/langbot/templates/config.yaml +++ b/src/langbot/templates/config.yaml @@ -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