OpenCode

Open-source multi-provider CLI coding agent with external launch verification and Keychain-backed secret injection.

Last updated

Cloud API Go

1. Installation

Prerequisites

  • macOS 13+ or Linux (kernel 5.13+)
  • Go 1.21+ (or install pre-built binary)
  • API key for at least one provider (Anthropic, OpenAI, Google, xAI, Groq)

Install OpenCode

brew install opencode

Install the preferred stack

brew tap nvk/tap
brew install nvk/tap/agent-bondage
brew install nvk/tap/envchain-xtra
brew install nono

Verify

opencode --version
bondage --help
nono --version
envchain --version

2. nono Profile

OpenCode stores config in ~/.opencode.json (written with 0644 permissions — world-readable). In the preferred setup, bondage verifies the OpenCode target first, envchain-xtra injects the needed provider keys, and nono limits what the process can touch beyond the project directory.

What the sandbox allows

ResourceAccessWhy
Current working directoryRead + WriteProject files
~/.opencode.jsonReadConfig (no secrets if using envchain)
API endpointsNetworkProvider API calls
/dev/tty, /dev/nullRead + WriteTerminal I/O
/dev/urandomReadCrypto randomness
Config gotcha: OpenCode's docs mention {env:VARIABLE_NAME} interpolation in config files. This does not work. The source code treats apiKey as a literal string — it sends the text {env:ANTHROPIC_API_KEY} to the API as the bearer token. Omit the providers section entirely and let OpenCode read keys from env vars via os.Getenv().

3. envchain-xtra

OpenCode supports multiple providers. Store each API key in the same envchain-xtra namespace so the chosen provider credentials are injected into one process tree without living in plaintext config files.

Store your API keys

# Store all provider keys under one namespace
envchain --set opencode ANTHROPIC_API_KEY
envchain --set opencode OPENAI_API_KEY

Provider environment variable names

ProviderEnv Var
AnthropicANTHROPIC_API_KEY
OpenAIOPENAI_API_KEY
Google / GeminiGOOGLE_GENERATIVE_AI_API_KEY
xAI / GrokXAI_API_KEY
GroqGROQ_API_KEY
Name mismatch: Google's key is GOOGLE_GENERATIVE_AI_API_KEY, not GEMINI_API_KEY. Using the wrong name silently fails.

Minimal config file (no secrets)

# ~/.opencode.json — safe to commit to dotfiles
{
  "provider": "anthropic",
  "model": "claude-sonnet-4-6"
}

4. bondage Wrapper

Add this thin wrapper to your shell config:

opencode() {
  bondage exec opencode ~/.config/bondage/bondage.conf -- "$@"
}

Sample stack snippets

Assuming your shared [global] block already exists in ~/.config/bondage/bondage.conf, this is a script-backed OpenCode shape to adapt:

# ~/.config/bondage/bondage.conf
[profile "opencode"]
namespace = opencode
use_envchain = true
use_nono = true
nono_profile = opencode
touch_policy = prompt
target_kind = script
target = /absolute/path/to/opencode/bin/opencode
target_fp = sha256:replace-me
interpreter = /absolute/path/to/node
interpreter_fp = sha256:replace-me
package_root = /absolute/path/to/opencode
package_tree_fp = sha256:replace-me
nono_allow_cwd = true
nono_allow_file = /dev/tty
nono_allow_file = /dev/null
nono_read_file = /dev/urandom
{
  "extends": "opencode",
  "meta": {
    "name": "opencode",
    "description": "OpenCode with project-only access and envchain-backed provider keys"
  },
  "policy": {
    "add_deny_access": ["/Volumes"]
  },
  "workdir": {
    "access": "readwrite"
  }
}

How it works

The sequence is:

  1. The shell wrapper passes only the profile name and your arguments
  2. bondage verifies the exact OpenCode target
  3. envchain-xtra resolves Keychain lookups before the sandbox applies
  4. nono applies Seatbelt/Landlock restrictions
  5. OpenCode reads provider keys from env vars — no plaintext file access needed

Reload your shell:

source ~/.zshrc

5. Verification

Test the full chain

bondage verify opencode ~/.config/bondage/bondage.conf
bondage chain opencode ~/.config/bondage/bondage.conf -- --help
opencode --help

Confirm no plaintext keys

# Should return nothing
grep -r "sk-ant\|sk-proj" ~/.opencode* 2>/dev/null

Troubleshooting

SymptomCauseFix
"Invalid authentication credentials" Config file has literal {env:...} string Remove the providers section from config
Wrong provider used Config overrides env var defaults Keep config minimal — just provider and model
"Operation not permitted" nono blocking a path Check nono why for denied access
Shell name → bondage → envchain-xtra → nono → OpenCode Convenience Launch policy Secret release Kernel sandbox Actual agent