Integrating Docker Sandbox with OpenCode and Venice

A coding agent running inside a sandbox

In a previous article I concluded that the most crypto aligned tech stack for a coding agent was OpenCode as the harness, GLM 5.2 as the LLM, Venice as the inference provider and Docker Sandbox for isolation. The goal of this tutorial is to show you how to integrate all these technologies together.

Installation

The first layer of the stack is Docker Sandbox. You can install it on MacOS using Homebrew. If you use Windows or Linux you can look at the docs for the installation instructions on those operating systems.

Before you can install a package from a source not yet registered in your system, you have to add it to your list of trusted sources. This is a new security feature added to Homebrew 6.

$ brew trust docker/tap
>>>
Trusted tap: docker/tap

Now that the source is trusted you can install sbx, the CLI of Docker Sandbox.

$ brew install docker/tap/sbx
>>>
...
sbx was successfully installed!

In order to use sbx you need to be authenticated to Docker.com so make sure to create an account if you don’t have one already before attempting to run login from the terminal.

$ sbx login
>>>
...
Signed in as <your-username>.

After login you’ll be asked you to setup your global network policy. This policy defines the level of access your coding agents will have to the internet.

     1. Open         — All network traffic allowed, no restrictions.
  ❯  2. Balanced     — Default deny, with common dev sites allowed.
     3. Locked Down  — All network traffic blocked unless you allow it.

Select “Balanced” as it’s a secure starting point that can be expanded later as needed.

Finally, create a folder for our sample project called myapp.

$ mkdir ~/myapp

Managing Sandboxes

To create a sandbox you can use the create command specifying the coding agent you’d like to use and and the path of your project folder.

$ sbx create opencode ~/myapp
>>>
Created sandbox 'opencode-myapp'

This command will create a sandbox using a micro VM with OpenCode installed and mount the project folder.

The create command will give your sandboxes a default name that is a combination of the chosen coding agent and the project folder like opencode-myapp. To create a sandbox with a custom name you can use the flag --name.

$ sbx create opencode --name mysbx ~/myapp
>>>
Created sandbox 'mysbx'

To list of all the created sandboxes and their status you can use the ls command.

$ sbx ls
>>>
SANDBOX          AGENT      STATUS    PORTS   WORKSPACE
mysbx            opencode   running           /Users/david/myapp
opencode-myapp   opencode   running           /Users/david/myapp

Docker Sandbox comes with a TUI to manage all sandboxes in one place. To launch the TUI you simply need to run sbx without any option or flag.

$ sbx

This command will launch an interactive interface directly on the terminal.

Docker Sandbox TUI

For this tutorial I’ll stick to the CLI instead of the TUI as it’s easier to follow along on a written guide but the TUI is a better option when managing multiple sandboxes.

Having two different sandboxes pointing at the same workspace and using the same agent is kind of pointless so we can get rid of the first one with the rm command.

$ sbx rm opencode-myapp
>>>
...
Sandbox 'opencode-myapp' removed

At this point we are left only with one sandbox that we named mysbx running in the background which isn’t very useful. To attach our terminal to the sandbox we use the run command passing the name of the sandbox.

$ sbx run --name mysbx

This will attach the terminal to the mysbx sandbox and launch the agent we defined for it, in this case OpenCode.

OpenCode welcome interface

Connecting Venice

In order to use OpenCode with an LLM we need to connect it with an inference provider, in this case Venice. You’ll need to head over to Venice.ai to register, pre-fund your account with some money for inference and generate and API key.

Normally you’d run the command /connect inside OpenCode, select Venice from the list of providers and paste the API key but that wouldn’t work here. First, we don’t want the agent to have a direct access to the API key to prevent exfiltration. Second, the “Balanced” network policy we setup at the beginning doesn’t include api.venice.ai in its list of allowed domains.

Network Policy

You can check which domains are part of the network policy by running the command:

$ sbx policy ls network

It will return a long list of domains that would include sites like Github, Gitlab and AWS but not Venice. To add Venice to the list of allowed domains for all sandboxes you can run the command:

$ sbx policy allow network "venice.ai, **.venice.ai"
>>>
Rule added to policy local (scope: global): <id> (venice.ai)
Rule added to policy local (scope: global): <id> (**.venice.ai)

We needed to add both the top level domain (venice.ai) and all subdomains because Docker does an exact match on the proxy. The most important subdomain is api.venice.ai but we are using a wildcard to also allow the agent to search the documentation at docs.venice.ai.

You can verify that the domains were added to the allowed list by using grep.

$ sbx policy ls network | grep venice
>>>
local all <id> network allow venice.ai
local all <id> network allow **.venice.ai

Custom Secrets

Now that our sandbox is allowed to reach out to Venice for inference, we need to add the API key to Docker Sandbox’s proxy so it’s replaced on the fly when the agent makes a request to api.venice.ai using a fake API key or placeholder. This would prevent the coding agent from ever having access to the real API key eliminating the risk of exfiltration.

Docker sandbox replacing on the fly the API key of the request

To add Venice’s API key to the proxy mechanism of Docker sandbox you’ll run the following command:

$ sbx secret set-custom -g 
    --host api.venice.ai   
    --env VENICE_API_KEY  
    --value <real-api-key>
>>>
Saved custom secret placeholder "sbx-cs-wdSgnbQhZyS5ii85" for target "api.venice.ai" env "VENICE_API_KEY" in scope "(global)"
...

This command stores the API key on the Keychain (because I’m on MacOS), creates an environment variable on all sandboxes called VENICE_API_KEY that resolves to a randomly generated value (the placeholder) and adds an interception rule for the proxy to replace credentials on the fly when reaching out to api.venice.ai.

To see all the secrets you’ve created so far you can run the following command:

$ sbx secret ls
>>>
CUSTOM SECRETS
SCOPE      TARGETS         ENV              PLACEHOLDER               SECRET
(global)   api.venice.ai   VENICE_API_KEY   sbx-cs-wdSgnbQhZyS5ii85   VENICE******...******T8Gc

Configuring OpenCode

We are now ready to go back to mysbx sandbox to configure OpenCode to use Venice as the inference provider.

$ sbx run --name mysbx

Inside OpenCode we now execute the /connect command, select Venice AI as the inference provider and provide the placeholder value we got from secret for the Venice API key, not the real value. If the setup was done correctly you should see next the list of LLMs supported by Venice along with the thinking effort.

Gif showing how to connect OpenCode to Venice

To avoid doing this setup every time you create a new sandbox you can create a configuration file for OpenCode at the root of your project called opencode.json and allow the agent to pick up the placeholder value from the environment variable VENICE_API_KEY automatically.

opencode.json

{
  "$schema": "https://opencode.ai/config.json",
  "model": "venice/zai-org-glm-5-2",
  "agent": {
    "build": { "variant": "high" },
    "plan": { "variant": "high" },
    "general": { "variant": "medium" },
    "explore": { "variant": "low" }
  },
  "provider": {
    "venice": {
      "options": {
        "apiKey": "{env:VENICE_API_KEY}"
      }
    }
  }
}

We are explicitly defining the thinking effort for each of the 4 agents it support to strike a balance between performance and cost based on the importance of each agent.

Surprisingly, the domain opencode.ai is not part of the network allow list of Docker Sandbox and it’s likely the agent will need it to verify the schema of opencode.json and its docs. Let’s add it to the whitelist as a good practice.

$ sbx policy allow network "opencode.ai, **.opencode.ai"
>>>
Rule added to policy local (scope: global): <id> (opencode.ai)
Rule added to policy local (scope: global): <id> (**.opencode.ai)

Conclusion

At this point you are now ready to start working on your project with a coding agent in way that protects your laptop and the project secrets from your own AI going rogue.

The setup shown here can be further improved by creating a sandbox in clone mode instead of edit mode as it’s the default. In clone mode the coding agent can’t directly modify the files of your project, instead it would append itself as a new remote to your git repository that you can pull and merge from once the changes are ready. Clone mode pairs well with git worktrees to allow for a multi coding agent setup.

Cheat Sheet

// Create a sandbox without running it
sbx create opencode --name <sandbox-name> <project-path>

// Run an already created sandbox
sbx run --name <sandbox-name>

// Shortcut to create and run a sandbox on active directory
sbx run opencode --name <sandbox-name>

// Delete a sandbox
sbx rm <sandbox-name>

// Add a url to the allow list
sbx policy allow network <url>

// Add an API key as a secret
sbx secret set-custom -g 
    --host <url>   
    --env <env-name>  
    --value <api-key>

So, what do you think?

This site uses Akismet to reduce spam. Learn how your comment data is processed.