跳到主要内容
    ↑↓ 选择↵ 打开esc 关闭
    中文English
    isanchez31
    opencode-sandbox

    OpenCode plugin that sandboxes agent commands using @anthropic-ai/sandbox-runtime (seatbelt on macOS, bubblewrap on Linux)

    GitHub 星标

    48

    近 30 天 +3

    月装机量

    1,355

    近 7 天 557

    综合评分SCORE

    56.5

    生态多维模型

    最近提交

    2 天前

    2026-08-17

    快速安装与配置

    opencode.json

    写入当前项目的 opencode.json,只对这个仓库生效。

    opencode.json

    {
      "$schema": "https://opencode.ai/config.json",
      "plugin": ["opencode-sandbox@0.5.1"]
    }

    opencode 启动时会通过内嵌运行时自动加载 npm 依赖并缓存至本地目录,无需手动在全局环境执行安装。

    CI npm version License: MIT

    opencode-sandbox

    An OpenCode plugin that sandboxes agent-executed commands using @anthropic-ai/sandbox-runtime.

    Every bash tool invocation is wrapped with OS-level filesystem and network restrictions — no containers, no VMs, just native OS sandboxing primitives.

    Platform Mechanism
    macOS sandbox-exec (Seatbelt profiles)
    Linux bubblewrap (namespace isolation)
    Windows Not currently supported by OpenCode's command-string hook (commands pass through)

    Install

    // opencode.json
    {
      "plugin": ["opencode-sandbox"]
    }
    

    The plugin is automatically installed from npm when OpenCode starts.

    Linux prerequisites

    1. Install bubblewrap:

    # Debian/Ubuntu
    sudo apt install bubblewrap
    
    # Fedora
    sudo dnf install bubblewrap
    
    # Arch
    sudo pacman -S bubblewrap
    

    2. Ubuntu 24.04+ (AppArmor fix):

    Ubuntu 24.04 and later restrict unprivileged user namespaces via AppArmor, which prevents bubblewrap from working. You need to enable the bwrap-userns-restrict AppArmor profile:

    # Install the AppArmor profiles package
    sudo apt install apparmor-profiles
    
    # Create the symlink to enable the profile
    sudo ln -s /etc/apparmor.d/bwrap-userns-restrict /etc/apparmor.d/force-complain/bwrap-userns-restrict
    
    # Load the profile
    sudo apparmor_parser -r /etc/apparmor.d/bwrap-userns-restrict
    

    You can verify bwrap works:

    bwrap --ro-bind / / --dev /dev --proc /proc -- echo "sandbox works"
    

    Without this fix, bwrap will fail with loopback: Failed RTM_NEWADDR: Operation not permitted or setting up uid map: Permission denied.

    What it does

    When the agent runs a bash command, the sandbox enforces three layers of protection:

    Filesystem write protection

    Commands can only write to the project directory and /tmp. Writing anywhere else returns "Read-only file system":

    $ touch ~/some-file
    touch: cannot touch '/home/user/some-file': Read-only file system
    
    $ echo "data" > /etc/config
    /usr/bin/bash: line 1: /etc/config: Read-only file system
    

    Sensitive file read protection

    Access to credential directories is blocked:

    $ cat ~/.ssh/id_rsa
    cat: /home/user/.ssh/id_rsa: Permission denied
    

    Network allowlist

    Only approved domains are reachable. All other traffic is blocked via a local proxy:

    $ curl https://evil.com
    Connection blocked by network allowlist
    
    $ curl https://registry.npmjs.org
    (works — npmjs.org is in the default allowlist)
    

    Default restrictions

    Filesystem (deny-read):

    • ~/.ssh, ~/.gnupg
    • ~/.aws/credentials, ~/.config/gcloud
    • ~/.npmrc, ~/.env

    Filesystem (allow-read):

    • Empty by default

    Filesystem (allow-write):

    • Project directory
    • Git worktree (validated — unsafe paths like / are rejected)
    • /tmp

    Network (allow-only):

    • registry.npmjs.org, *.npmjs.org
    • registry.yarnpkg.com
    • pypi.org, crates.io
    • github.com, *.github.com
    • gitlab.com, *.gitlab.com
    • api.openai.com, api.anthropic.com
    • *.googleapis.com

    Everything else is blocked by default.

    Configuration

    Config files are stored outside the project directory (in ~/.config/opencode-sandbox/) so that sandboxed commands cannot modify them. This prevents indirect prompt injection from weakening the sandbox by overwriting the config.

    Config file locations

    The plugin searches for configuration in this order (first match wins):

    1. Environment variable OPENCODE_SANDBOX_CONFIG (JSON string)
    2. Per-project config ~/.config/opencode-sandbox/projects/<project-name>.json
    3. Global config ~/.config/opencode-sandbox/config.json
    4. Built-in defaults

    The <project-name> is the basename of the project directory (e.g., my-app for /home/user/projects/my-app).

    If XDG_CONFIG_HOME is set, it is used instead of ~/.config.

    Example: Global config

    // ~/.config/opencode-sandbox/config.json
    {
      "filesystem": {
        "denyRead": ["~/.ssh", "~/.aws/credentials"],
        "allowRead": ["~/.ssh/id_ed25519.pub"],
        "allowWrite": [".", "/tmp", "/var/data"],
        "denyWrite": [".env.production"]
      },
      "network": {
        "allowedDomains": [
          "registry.npmjs.org",
          "github.com",
          "*.github.com",
          "api.openai.com",
          "api.anthropic.com",
          "my-internal-api.company.com"
        ],
        "deniedDomains": ["malicious.example.com"]
      }
    }
    

    Path precedence

    Path precedence is inherited from @anthropic-ai/sandbox-runtime:

    • Read: allowRead takes precedence over denyRead
    • Write: denyWrite takes precedence over allowWrite

    Example: allow git commit signing with SSH public key

    If your Git workflow needs to read a public key (for example ~/.ssh/id_ed25519.pub) while keeping ~/.ssh blocked by default, re-allow only that file:

    // ~/.config/opencode-sandbox/config.json
    {
      "filesystem": {
        "denyRead": [
          "~/.ssh",
          "~/.gnupg",
          "~/.aws/credentials",
          "~/.azure",
          "~/.config/gcloud",
          "~/.config/gh",
          "~/.kube",
          "~/.docker/config.json",
          "~/.npmrc",
          "~/.netrc",
          "~/.env"
        ],
        "allowRead": ["~/.ssh/id_ed25519.pub"]
      }
    }
    

    Example: Per-project config

    // ~/.config/opencode-sandbox/projects/my-app.json
    {
      "network": {
        "allowedDomains": ["my-internal-api.company.com"]
      }
    }
    

    Environment variable

    OPENCODE_SANDBOX_CONFIG='{"filesystem":{"denyRead":["~/.ssh"]},"network":{"allowedDomains":["github.com"]}}' opencode
    

    Example allowing only the SSH public key to be read:

    OPENCODE_SANDBOX_CONFIG='{"filesystem":{"denyRead":["~/.ssh","~/.gnupg","~/.aws/credentials","~/.azure","~/.config/gcloud","~/.config/gh","~/.kube","~/.docker/config.json","~/.npmrc","~/.netrc","~/.env"],"allowRead":["~/.ssh/id_ed25519.pub"]}}' opencode
    

    Disable

    OPENCODE_DISABLE_SANDBOX=1 opencode
    

    Or in any config file:

    {
      "disabled": true
    }
    

    How it works

    The plugin uses two OpenCode hooks:

    1. tool.execute.before — Intercepts bash commands and wraps them with SandboxManager.wrapWithSandbox() before execution
    2. tool.execute.after — Restores the original command in the UI (hides the bwrap wrapper)
    Agent → bash tool → [plugin wraps command] → sandboxed execution → [plugin restores UI] → Agent
    

    The AI model interprets sandbox errors (like "Read-only file system" or "Connection blocked") directly from command output — no additional annotation layer needed.

    Sandbox initialization is deferred until the first bash command, so the plugin does not interfere with OpenCode startup. Plugin diagnostics are sent through OpenCode's structured logger instead of being printed into the TUI. Sandbox violations are correlated with each individual tool call, including concurrent or repeated commands.

    Windows status

    @anthropic-ai/sandbox-runtime supports Windows through an argv-and-environment API, while OpenCode currently exposes this plugin's bash hook as a command string. Until those interfaces can be connected safely, this plugin leaves Windows commands unsandboxed rather than claiming protection it cannot enforce.

    Fail-open design

    If anything goes wrong (sandbox init fails, wrapping fails, platform unsupported), commands run normally without sandbox. The plugin never breaks your workflow.

    Contributing

    See CONTRIBUTING.md for development setup, architecture, and guidelines.

    Related