Visitar URL original
Opt-in Claude Code / opencode hook that keeps shell commands off the laptop when a VM is pinned · Issue #95 · modem-dev/boxery · GitHub
Skip to content

Opt-in Claude Code / opencode hook that keeps shell commands off the laptop when a VM is pinned #95

Open

Description

@benvinegar

Problem

With MCP, "project work goes to the VM" is only guidance: the server's instructions and core.guidance (mcp/boxery-mcp.mjs:266). Claude Code, Codex and opencode keep their own built-in shell tool, and models often fall back to it. You find out afterwards that npm install or a test run happened on the laptop. That is the outcome boxery exists to prevent.

ARCHITECTURE.md ("Design decisions") explains why boxery doesn't override built-ins: the old pi extension that replaced bash/read/write/edit conflicted with other extensions. Claude Code's PreToolUse hooks and opencode's tool.execute.before don't have that problem. They sit in front of the built-in tool without replacing it, and they're opt-in user config.

Proposal

boxery hook install claude-code|opencode [--mode guard|redirect], plus boxery hook uninstall …. It edits the agent's settings idempotently and leaves other hooks alone. The hook is active only when a VM is pinned for the session (BOXERY_VM, or a pin file the MCP server writes when a VM is selected); with nothing pinned it does nothing.

  • guard (default). Deny the built-in Bash call with a reason the model sees: "this session is pinned to VM demo; run it with vm_bash (or vm_jobs for long-running commands)". An allowlist (git status, git diff, ls, … configurable, empty by default) stays local. This makes the guidance actually enforced, and it can't cause the problem below.

  • redirect. Rewrite the command to boxery run NAME --cwd <mapped dir> -- bash -c '<original>' via updatedInput. The original command travels as one argument, so it can't run on the host. If the hook can't rewrite a call safely, it denies it rather than letting it run locally.

    The catch: Read/Write/Edit still act on laptop files, so the commands run against whatever is in the VM's copy of the project. Redirect is only correct together with a sync step (boxery cp, or the sync idea from boxery sync: push a local checkout's state into the VM #9). The docs must say so, and the hook should warn when the VM's tree is behind.

Codex has no comparable hook today; document it as guidance-only.

Prior art

CubeSandbox ships both variants as examples:

  • Claude Code PreToolUse hook: rewrites every Bash call with updatedInput, quotes the original command as a single shlex argument, denies when it can't rewrite safely, keeps one sandbox per session_id, and persists cd/export between calls.
  • opencode plugin: the same through tool.execute.before, plus a per-session lock.

Their README states the motivation directly: an integration that relies on the agent choosing a sandbox tool still lets a plain shell call land on the host.

Their redirect mode has the same split between local files and remote commands. They work around it with a read-only host mount, which is only possible when the agent runs on the sandbox host. That is why guard is the safer default for boxery.

Acceptance criteria

  • boxery hook install claude-code is idempotent, keeps existing hooks, and uninstall removes only boxery's entry.
  • With a pinned VM, built-in Bash is denied with a message naming vm_bash; without a pinned VM, the hook allows every call.
  • Redirect mode: a command containing quotes, ;, $(…) and newlines runs only in the VM. Unit tests feed the hook script recorded PreToolUse payloads.
  • docs/mcp.md documents both modes and the file-sync caveat.

This issue was generated by Claude Code using Claude Opus 5.5 (CubeSandbox comparison, 2026-11-01, at 2b2520b).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: mcpThe MCP server (mcp/boxery-mcp.mjs)enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions