Skip to content

Is there a way to sandbox the agent ? #2242

Closed as duplicate
Closed as duplicate

Description

@edmBernard

Hi,

Is there a way to restrict terminal command from a agent to access/edit file outside the current directory ?
I see that gemini-cli or codex-cli use seatbelt on macOS.
I don't see any equivalent for copencode.

Thanks,

Activity

  1. github-actions commented on Aug 25, 2025

    @github-actions
    Contributor

    This issue might be a duplicate of existing issues. Please check:

    • [FEATURE] add exec tool as more secure alternative to bash tool #2206: This is a comprehensive feature request for adding sandboxing capabilities to OpenCode, addressing the same core concern about restricting agent access to files and system commands outside the current directory. It discusses security risks with the current bash tool and proposes solutions including sandboxing mechanisms similar to what you're asking about.

    Feel free to ignore if none of these address your specific case.

  2. rekram1-node commented on Aug 25, 2025

    @rekram1-node
    Collaborator

    yeah we need better sandboxing, we try to restrict to cwd but agent can use bash to get around it

  3. TyceHerrman commented on Oct 25, 2025

    @TyceHerrman
  4. rekram1-node commented on Oct 25, 2025

    @rekram1-node
    Collaborator

    Oo thats a good callout we will need to look into that, I wonder how easy it is to hookup

  5. gimbo commented on Oct 25, 2025

    @gimbo

    I currently mitigate this by running opencode inside a docker container, with bind mounts of just the folders I want it to access (or which it needs); can also restrict network activity too if desired this way. Its a bit fiddly and of course container escape is possible, but better than nothing.

  6. dwt commented on Dec 2, 2025

    @dwt

    Docker containers are a good start, but when coding on Mac, you loose native tools, and docker (by itself) does not sandbox network requests, which means secret exfiltration is not mitigated at all. I very much second the attention to the anthropics sandbox-runtime.

  7. robbash commented on Dec 2, 2025

    @robbash

    I started off with docker as well, but as mentioned there's a lot of obstacles, incl. the auto-copy on selection in OpenCode being a big problem.

    I've spent some time on the macOs sandbox-exec, which is the underlying tool anthropic's srt is using. The issue I had with srt is that I would need to build a deny list for reading permissions instead of an allowlist, and therefore used the macOs tool straight away.

    https://gist.github.com/robbash/84aaa7c4133535b59cbaf0c1761031a4

    I don't have a lot of experience with the tool though, and there was a lot of trial and error involved. Happy about feedback.

  8. redtux commented on Dec 3, 2025

    @redtux

    Thanks for sharing! For those here not using macos and ready to test this (had no time to do so yet), here is what the bot says:

    To create a similar sandbox on Debian GNU/Linux & Co. without Docker/Podman, you can use bubblewrap (bwrap), which is Linux's equivalent to macOS's sandbox-exec. Bubblewrap provides lightweight containerization with namespace isolation.

    Installation

    sudo apt install bubblewrap

    Sandbox Script

    Here's a bubblewrap-based sandbox configuration equivalent to your macOS setup:

    #!/bin/bash
    # Save as ~/opencode-sandbox.sh and chmod +x it
    
    # Define your workspace directories
    WORKSPACES=(
        "$HOME/Development/docker"
        "$HOME/Development/dotnet"
        "$HOME/Development/node"
    )
    
    # Build workspace bind mounts
    WORKSPACE_BINDS=()
    for ws in "${WORKSPACES[@]}"; do
        if [ -d "$ws" ]; then
            WORKSPACE_BINDS+=(--bind "$ws" "$ws")
        fi
    done
    
    # Execute OpenCode in sandbox
    bwrap \
        --unshare-all \
        --share-net \
        --die-with-parent \
        --new-session \
        \
        `# Basic filesystem structure` \
        --ro-bind /usr /usr \
        --ro-bind /lib /lib \
        --ro-bind /lib64 /lib64 \
        --ro-bind /bin /bin \
        --ro-bind /sbin /sbin \
        --symlink usr/lib /lib \
        --symlink usr/lib64 /lib64 \
        --symlink usr/bin /bin \
        --symlink usr/sbin /sbin \
        --proc /proc \
        --dev /dev \
        --tmpfs /tmp \
        --tmpfs /run \
        \
        `# Minimal /etc files needed` \
        --ro-bind /etc/resolv.conf /etc/resolv.conf \
        --ro-bind /etc/hosts /etc/hosts \
        --ro-bind /etc/nsswitch.conf /etc/nsswitch.conf \
        --ro-bind /etc/passwd /etc/passwd \
        --ro-bind /etc/group /etc/group \
        --ro-bind /etc/localtime /etc/localtime \
        \
        `# SSL certificates for API access` \
        --ro-bind /etc/ssl /etc/ssl \
        --ro-bind-try /etc/ca-certificates /etc/ca-certificates \
        \
        `# Home directory structure` \
        --dir "$HOME" \
        --setenv HOME "$HOME" \
        --chdir "$HOME" \
        \
        `# OpenCode config and cache directories (read-write)` \
        --bind-try "$HOME/.cache/opencode" "$HOME/.cache/opencode" \
        --bind-try "$HOME/.local/share/opencode" "$HOME/.local/share/opencode" \
        --bind-try "$HOME/.local/state/opencode" "$HOME/.local/state/opencode" \
        --bind-try "$HOME/.config/opencode" "$HOME/.config/opencode" \
        \
        `# Workspace directories (read-write)` \
        "${WORKSPACE_BINDS[@]}" \
        \
        `# Terminal and environment` \
        --setenv TMPDIR /tmp \
        --setenv PATH /usr/local/bin:/usr/bin:/bin \
        \
        `# Run OpenCode` \
        opencode "$@"

    Usage

    # Make executable
    chmod +x ~/opencode-sandbox.sh
    
    # Create config directories if they don't exist
    mkdir -p ~/.cache/opencode ~/.local/share/opencode \
             ~/.local/state/opencode ~/.config/opencode
    
    # Run OpenCode in sandbox
    ~/opencode-sandbox.sh

    Key Differences from macOS Version

    1. Network isolation: Uses --share-net to allow network access (similar to allow network* in macOS)
    2. Filesystem: Read-only binds for system directories, read-write for workspaces
    3. No full HOME access: Only specific OpenCode directories are accessible
    4. Process isolation: --unshare-all creates new namespaces (PID, mount, IPC, UTS, cgroup)

    Alternative: More Restrictive Network Control

    If you want to limit network access (e.g., only API calls), you could combine bubblewrap with iptables or nftables rules, though this requires root privileges and is more complex.

    Debugging

    To troubleshoot, run with verbose output:

    strace -f ~/opencode-sandbox.sh 2>&1 | grep -i "denied\|error"

    This setup provides isolation comparable to Claude Code CLI's sandbox while remaining lightweight and avoiding full container solutions.

  9. dwt commented on Dec 6, 2025

    @dwt

    Bubblewrap is the other backend that sandbox-runtime is using. For sure you can deal with all those options, but I think it makes a lot of sense to use something like sandbox-runtime to keep that much simpler to use. Plus, you get the network sandboxing with a man in the middle proxy for which you can also specify rules.

  10. llucax commented on Dec 11, 2025

    @llucax

    I ended up writing this simple tool: https://github.com/frequenz-floss/contai

    It is still super simple, and far from user-friendly or finished, but it fulfills my needs for some sense of security while running AI agents, so maybe somebody else also find it useful.

  11. mdlmarkham commented on Jan 12, 2026

    @mdlmarkham

    I'm using https://coder.com/ on ProxMox - it works OK, but it's a little of a hassle to get going.

  12. dwt commented on Jan 13, 2026

    @dwt

    I recently found https://github.com/Use-Tusk/fence which looks like a very nice implementation to build a basic sandbox on.

  13. aemonge commented on Jan 13, 2026

    @aemonge
  14. reneleonhardt commented on Jan 20, 2026

    @reneleonhardt

    There's an easy way to improve security on macOS by sandboxing agents with shell scripts.
    Can SandVault be integrated into OpenCode?
    https://github.com/webcoyote/sandvault

    Security Model

    The sandvault user:

    • Cannot access your home directory
    • Runs with standard user privileges
    • Cannot modify system files
    • Has its own isolated home directory
  15. 88 remaining items

  16. Wuodan commented on Oct 23, 2026

    @Wuodan

    30. ~/.ssh and friends unreadable

    There's a trade off for these: ~/.shh and ~/.gnupg are often needed so the agent can use git like the user (commit, pull, sign commits) depending on your git config.

    • Plain Docker doesn't limit outbound traffic by default

    True. But it can be configured. And imho that configuration is pretty individual and and not so easy to put in user friendly UIs.
    My dockerized setup with aicage let's one pass arguments to the docker run process to configure networking.

  17. balabommablock-cpu commented on Oct 24, 2026

    @balabommablock-cpu

    @Wuodan fair point on git. One middle ground: keep the key files unreadable, but pass the agent sockets through (SSH_AUTH_SOCK, and the gpg-agent socket for signing). The agent can still commit, pull and sign while it runs, but it can't read the key itself, so nothing can copy it out.

    And agreed that network rules are personal. That's probably why a built-in version would start with files only and leave the network side to wrappers like yours.

  18. opencodejr commented on Oct 29, 2026

    @opencodejr
    Contributor

    Thanks for raising this, @edmBernard!

    To restate the request: you'd like OpenCode to run the agent's terminal commands in an OS-level sandbox (like seatbelt on macOS in Codex or Gemini CLI), so they can't read or change files outside the current project. Maintainers will consider it.

    OpenCode doesn't have a built-in OS sandbox for shell commands today. What exists now:

    • The external_directory permission (deny / ask / allow per path) controls OpenCode's own file tools, but it is not enforced at the OS level, so commands run through bash can get around it.
    • In v2, plugins can rewrite a shell command before it runs (the shell create.before hook), so a plugin can wrap commands in sandbox-exec (macOS) or bwrap (Linux). Community plugins such as opencode-sandbox and opencode-seatbelt already take this approach.
    • Many people in this thread run OpenCode itself inside bwrap, sandbox-exec or a container. Several working scripts are posted in the comments above.

    Related:

  19. zcutlip commented on Oct 29, 2026

    @zcutlip

    @opencodjr, no the request is for all of opencode and its subprocesses to run in a sandbox, not just the shell tool. For example opencode's reads, writes, and edits should be confined to what the sandbox allows. This guards against a misbehaving or prompt-injected agent compromising or exfiltrating sensitive data. It also guards against misconfigured permissions or vulnerabilities in opencode itself, such that the permission system could be overridden or disabled.

  20. matglas commented on Oct 30, 2026

    @matglas

    Nono has been working for me on opencode since inception but with v2 and the split it only works with --standalone flag.

    It would be great if the design of nono as an architecture would fit the architecture of opencode. With it I feel safe running auto approve.

    And I 100% support the last comment on suprocess isolation from start to finish by @zcutlip

  21. Hamza-Abili commented on Nov 9, 2026

    @Hamza-Abili

    For anyone landing here: there is a community plugin that does this on both OpenCode v1 and v2, since core still has no sandbox. I maintain it, so disclosure up front.

    opencode-sandbox — https://github.com/Hamza-Abili/opencode-sandbox · npm @hamzaabili/opencode-sandbox

    • V2 registers ctx.shell.hook("create.before") and replaces only the shell executable with a generated wrapper: the command, cwd, timeout, permission rules and session history keep their original values. V1 uses the legacy tool.execute.before path, which is lower fidelity and documented as such.
    • bubblewrap on Linux, Seatbelt (sandbox-exec) on macOS. The workspace is writable, the system read-only, $HOME is synthetic, ambient environment/secrets are cleared, and the network is denied by default.
    • Fail-closed: if the sandbox cannot be created (missing bwrap, bad profile, broken wrapper), commands are refused instead of silently running unsandboxed. It probes the real wrapper at setup, not just the binary.
    • Zero runtime dependencies, no proxy, no helper daemon. Deliberate scope: the shell service only. File tools, MCP, LSP and webfetch are not sandboxed, and network is deny/allow with no domain allowlist.

    There are integration tests on real runners for both engines (bwrap on Ubuntu, Seatbelt on macOS), so the macOS path is not only unit-tested. If someone here tries it and finds a boundary gap, I would rather hear it in the repo than have the comment stand as a claim.

  22. redtux commented on Nov 11, 2026

    @redtux

    As many people here pointed out already, nono is working fine with both V1 and V2 - and it is not just a one-person project and trusted by a big community. You will have to adopt the permissions to your needs (as with any such tool), but I have been using it for a while now, and my experience is quite positive. If stronger isolation and access restrictions are needed (e.g., by using a dedicated web proxy that only allows some whitelisted URLs or domains to be accessed), then OpenShell is your friend. Both tools also support other harnesses like Pi or OpenClaw.

  23. Hamza-Abili commented on Nov 11, 2026

    @Hamza-Abili

    Thanks @redtux — no argument that nono and OpenShell are solid, and nono's community is a real advantage. One clarification for anyone comparing, because they sit at a different layer.

    nono/OpenShell sandbox the whole agent, so they cover more than my plugin (file tools, MCP, webfetch). The tradeoff is the launch model: nono's V2 docs require --standalone for full containment, and note that it sets OPENCODE_DISABLE_PROJECT_CONFIG=1, disabling project opencode.json, AGENTS.md and project plugins; without it, the client is contained but the background server that runs tools is not guaranteed to be. OpenShell runs the agent in its own container with a gateway/proxy — a different operating model.

    opencode-sandbox stays a server plugin: no wrapper, no relaunch, no proxy. The shell is OS-sandboxed (bwrap/Seatbelt), and 0.2.0 adds an in-process tool guard for the file tools and webfetch: workspace/readablePaths/writablePaths confinement, mount precedence, symlink-aware path checks, a webfetch hostname allowlist, and a pre-execution check so write/edit/patch cannot read a target before the policy sees it. It is a policy layer, not an OS boundary, and the README says so plainly. V1 gets a best-effort guard through the legacy tool hook; V2 is the full path.

    So: whole-harness + network/credential policy → nono/OpenShell. Zero-friction shell confinement plus an in-process file/webfetch policy in a normal OpenCode install → this plugin. They're complementary, not substitutes. Happy to be corrected on any specific claim.

    0.2.0: https://github.com/Hamza-Abili/opencode-sandbox/releases/tag/v0.2.0

  24. redtux commented on Nov 11, 2026

    @redtux

    I use nono with OpenCode V2 in server mode with no issues. I guess using standalone is just a recommendation (due to better isolation/granular control), but I had no issues so far...

  25. Hamza-Abili commented on Nov 11, 2026

    @Hamza-Abili

    Thanks @redtux — fair distinction, and I should have been clearer. --standalone isn't required for nono to work with V2; it's what nono's docs call for when you need full containment. Their warning is that V2 normally connects the client to a background server that owns tool execution, plugins and model requests; without --standalone, nono constrains the client but cannot guarantee that an existing external server is inside the sandbox.

    So if you start the opencode serve process under nono too (or otherwise keep tool execution inside the same sandbox), your setup is fine and my caveat doesn't apply. If the server is already running outside nono and the wrapped client connects to it, the sandbox covers the client but not the server that actually runs the tools. That's the case the docs are warning about — not a claim that nono doesn't work.

    Out of curiosity: are you launching the server under nono as well, or connecting to one started outside?

  26. redtux commented on Nov 11, 2026

    @redtux

    Exactly, I have a proper nono profile that allows access to certain external files or directories to be working. Normally nono tells you what's needed, or it tells the agent to run "nono why" in a sandbox. The server is also sandboxed, and MCP is working fine for me (both remote and stdio). For sure it depends on each user's particular setup - e.g. accessing non-root Podman containers isn't always trivial. Allowing the user socket is no issue, but the PID will change after each restart, so you might need a workaround here. Or I had an issue with the codebase-memory-mcp tool as it always wants to access / and /home, but I am using it with supergateway now (similar to mcp-proxy) and access it with type remote instead of stdin from OpenCode. This is working fine, and I have more control over what it can read. I was not arguing against any other solutions or tools btw, just confirming the good experience that other users reported when using nono locally.

  27. zcutlip commented on Nov 12, 2026

    @zcutlip

    I can't recommend nono (from @lukehinds) enough. I use it daily, and I use it with Claude Code at work (since I can't use OpenCode there).

    The more I use nono, the more I feel like sandboxing probably shouldn't be a feature of the agent itself (/me ducks). Everyone's config is different. Plugins access different resources, etc.; people use different SSH agents (system agent, 1Password, etc.). There's no way for OpenCode to anticipate a million configuration permutations and provide a sandboxing feature that can accommodate them all and still provide meaningful confinement.

    The feature OpenCode actually needs is the ability to be properly sandboxed. That is to say, it is designed with the expectation of running in a sandbox, and it degrades gracefully when it encounters sandbox enforcement-related failures. Maybe even a way to tell the agent process, "you are running confined; adjust your behavior accordingly."

    Many years ago I worked for a company that maintained the official SELinux Reference Policy for the US DoD. Every Linux product that we shipped could easily be confined with SELinux because we designed it with confinement in mind. The hard part was confining a process that had never heard of SELinux. E.g., it'd open a file read/write that it only needed to read, then fell on its face when it encountered the denial.

    I think the ask should be this: OpenCode should anticipate being sandboxed and behave gracefully when it is. It should even provide guidance and documentation about how to run OpenCode in an arbitrary confined environment and still get the most out of its feature set.

    Re: nono and project configs. OPENCODE_DISABLE_PROJECT_CONFIG is no longer set by the nono opencode profile. The profile initially set it because OpenCode V2 would perform a recursive filesystem walk looking for OpenCode config files (and AGENTS.md and other files, I believe; see #52759). When it encountered an access denial, the process blew up. That environment variable prevented the recursive walk but also prevented locating and loading the project-local config. (See above about the agent being designed to be sandboxed.) This OpenCode bug is now fixed, and the nono opencode profile no longer depends on this workaround. I warn you, though, that early V2 releases will blow up because of this.

    The following nono run command shows that this variable no longer gets set:

    nono run --profile nolabs-ai/opencode --allow-cwd -- sh -c 'env | grep OPENCODE_DISABLE_PROJECT_CONFIG || echo "OPENCODE_DISABLE_PROJECT_CONFIG not set"'
    
    [output abbreviated]
    
    OPENCODE_DISABLE_PROJECT_CONFIG not set

    Re: --standalone. The recommendation exists because OpenCode kicks off a server daemon if one doesn't already exist. All instances of OpenCode (CLI, TUI, desktop app) thereafter will opportunistically communicate with that daemon process. If the OpenCode daemon is not sandboxed, even if your CLI/TUI process is sandboxed, then for all intents and purposes, you are running unconfined. This is because the server daemon is the process that takes all actions: file reads/writes, tool calls (including, especially, the bash tool), etc. The --standalone flag says, "don't opportunistically communicate with an existing OpenCode daemon; instead start up a fresh one and communicate only with that one."

    I suspect you could get by with something resembling nono run <profile> -- sh -c 'opencode service stop && opencode', which would ensure the OpenCode service daemon starts fresh, inheriting the sandbox. The risk, though, is that you run OpenCode some other way, such as the desktop app, and it connects to a sandboxed OpenCode daemon expecting to do unsandboxed things and hilarity ensues (I haven't experimented with this but it sounds interesting).

  28. Hamza-Abili commented on Nov 12, 2026

    @Hamza-Abili

    Thanks @zcutlip — that's a genuinely useful correction, and you're right. I took the OPENCODE_DISABLE_PROJECT_CONFIG caveat from nono's published OpenCode docs; if the current profile no longer sets it, my comparison was stale. I've corrected the README in a docs-only 0.2.1 and removed the claim that nono disables project config/AGENTS.md/plugins.

    The --standalone point is the one I should have led with, and your daemon explanation is clearer than mine: it's not about nono "requiring" a flag to work, it's that the daemon owns tool execution, so wrapping only the client doesn't contain the process doing the work. Your opencode service stop && opencode shape is a sensible way to get a fresh in-sandbox daemon; the desktop-app cross-connection risk is exactly why a first-class "you are confined" contract would help.

    I also agree with the broader design point: OpenCode should be expected to run under an arbitrary sandbox and degrade gracefully on denials. A plugin like mine can cover the shell/file-tool surface from inside, but it's a stopgap, not a substitute for whole-process confinement.

    0.2.1 (docs only): https://github.com/Hamza-Abili/opencode-sandbox/releases/tag/v0.2.1

  29. matglas commented on Nov 12, 2026

    @matglas

    @neriousy would it be possible to include that last part about expecting to run under a sandbox be included in the other "duplicate" ticket? @opencodejr can you help?

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

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions