# Oh-My-OpenCode Setup
Brief overview of my sandboxed opencode setup with oh-my-opencode and reusing claude auth to get around repeated 429s and annoying flakiness.
I run each instance of this on a zellij session on a server which I connect to over SSH (and forward my gpg agent over for commit signing).
If I get a token issue in opencode I just quickly create a new shell, type claude and switch back to the original. TTL is ~24 hours and usually dies around 9am, so I don't have any reason to script this atm.
Full workflow involves zed remote sessions where I can quickly review and inspect changes when needed. I have one master zed session running locally with a terminal tab for each workstream (what can I say, I like the zed builtin terminal). This is open in my notes directory so I can easily switch between notes, todos and terminals and is where I spend the bulk of my time.
I have other zed sessions open in remote folders for some workstreams where I want to easily view files.
I also have a python http server set up and tunnel some ports via SSH so I can view web apps locally.
A lot of what I'm doing right now is simulation and research where this works really nicely.
## How the Claude Token Gets Reused
The chain is:
1. **Claude Code** authenticates via OAuth and stores credentials at `~/.claude/.credentials.json`:
```
{
"claudeAiOauth": {
"accessToken": "sk-ant-oat01-...",
"refreshToken": "sk-ant-ort01-...",
"expiresAt": 1774389809706,
"scopes": \[
"user:file\_upload",
"user:inference",
"user:mcp\_servers",
"user:profile",
"user:sessions:claude\_code"
\],
"subscriptionType": "team",
"rateLimitTier": "default\_claude\_max\_20x"
}
}
```
2. **OpenCode** loads the plugin `opencode-claude-auth@latest` (declared in `opencode.json`). This plugin reads the Claude OAuth tokens from that credentials file and uses them to authenticate Anthropic API calls — so OpenCode piggybacks on your existing Claude Code subscription without a separate API key.
3. Similarly, `opencode-antigravity-auth@latest` handles Google/Gemini auth via a separate credential flow stored in `~/.config/opencode/antigravity-accounts.json` (Google OAuth refresh token).
**In short**: You log into Claude Code once → `opencode-claude-auth` reads those tokens → OpenCode gets Anthropic API access for free (on your subscription). Same pattern for Gemini via Antigravity.
---
## OpenCode Config
## `~/.config/opencode/opencode.json` — declares plugins and providers:
```
{
"$schema": "https://opencode.ai/config.json",
"plugin": \[
"opencode-claude-auth@latest",
"oh-my-opencode@latest",
"opencode-antigravity-auth@latest"
\],
"provider": {
"anthropic": {
"options": {
"baseURL": "https://api.anthropic.com/v1"
}
}
}
}
```
The `plugin` array is the key — these three plugins are resolved and loaded at runtime by OpenCode itself (not via npm). The only npm dependency is the plugin SDK:
`~/.config/opencode/package.json`:
```
{
"dependencies": {
"@opencode-ai/plugin": "1.2.27"
}
}
```
Run `bun install` in `~/.config/opencode/` after creating this.
## Oh-My-OpenCode Config
`~/.config/opencode/oh-my-opencode.json` — the multi-agent orchestration brain:
```
{
"$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-opencode/dev/assets/oh-my-opencode.schema.json",
"agents": {
"sisyphus": { "model": "anthropic/claude-opus-4-6", "variant": "max" },
"hephaestus": { "model": "openai/gpt-5.3-codex", "variant": "medium" },
"oracle": { "model": "openai/gpt-5.2", "variant": "high" },
"explore": { "model": "anthropic/claude-haiku-4-5" },
"multimodal-looker": { "model": "google/gemini-3-flash-preview", "variant": "medium" },
"prometheus": { "model": "anthropic/claude-opus-4-6", "variant": "max" },
"metis": { "model": "anthropic/claude-opus-4-6", "variant": "max" },
"momus": { "model": "openai/gpt-5.2", "variant": "medium" },
"atlas": { "model": "anthropic/claude-sonnet-4-6" },
"sisyphus-junior": { "model": "anthropic/claude-sonnet-4-6" },
"librarian": { "model": "anthropic/claude-sonnet-4-6" }
},
"categories": {
"visual-engineering": { "model": "google/gemini-3.1-pro-preview", "variant": "high" },
"ultrabrain": { "model": "openai/gpt-5.3-codex", "variant": "xhigh" },
"deep": { "model": "openai/gpt-5.3-codex", "variant": "medium" },
"artistry": { "model": "google/gemini-3.1-pro-preview", "variant": "high" },
"quick": { "model": "anthropic/claude-haiku-4-5" },
"unspecified-low": { "model": "anthropic/claude-sonnet-4-6" },
"unspecified-high": { "model": "anthropic/claude-opus-4-6", "variant": "max" },
"writing": { "model": "google/gemini-3-flash-preview" }
}
}
```
### What this does
## **`agents`** — maps named agent roles to specific models. When the system prompt says "consult Oracle" or "fire explore agent", it dispatches to the model defined here. The primary agent (`sisyphus`) runs on Opus with max thinking. Cheap grunt work (`explore`) runs on Haiku. Consultation (`oracle`) runs on GPT-5.2.
**`categories`** — when `task(category="quick", ...)` is called, it spawns a `sisyphus-junior` agent using the model defined for that category. This lets you route different task types to the best-fit model: visual work → Gemini Pro, hard logic → GPT-5.3 Codex, trivial edits → Haiku.
**`variant`** — controls thinking budget. `max` = extended thinking, `high`/`medium`/`low` = progressively less thinking time, absent = no extended thinking.
## TL;DR Setup Steps
### 1. Install and authenticate Claude Code normally (gets you ~/.claude/.credentials.json)
claude login
### 2. Install OpenCode (https://opencode.ai)
### 3. Create ~/.config/opencode/opencode.json (see above)
### 4. Create ~/.config/opencode/package.json (see above)
### 5. Create ~/.config/opencode/oh-my-opencode.json (see above)
### 6. Install plugin SDK
cd ~/.config/opencode && bun install
### 7. Launch OpenCode — it picks up the plugins, reads Claude's OAuth tokens,
### and you're running the full multi-agent stack on your Claude subscription
opencode
# Sandboxing this
I sandbox each instance with a simple docker container and mount anything I need within it (Github read only PAT, SSH agent, GPG agent)
```fish
function opencode -d "Run OpenCode in a sandboxed Docker container"
set -l image_name "opencode-sandbox"
set -l workspace (pwd)
set -l opencode_args
for arg in $argv
if test "$arg" = --rebuild
echo "Rebuilding sandbox image..."
docker rmi $image_name 2>/dev/null
else
set -a opencode_args $arg
end
end
set workspace (realpath "$workspace")
# Build UID-matched image from sandbox template (one-time)
if not docker image inspect $image_name >/dev/null 2>&1
echo "Building sandboxed OpenCode image (one-time)..."
set -l host_uid (id -u)
set -l host_gid (id -g)
printf '%s\n' \
"FROM docker/sandbox-templates:opencode" \
"USER root" \
"RUN usermod -u $host_uid agent && groupmod -g $host_gid agent && chown -R $host_uid:$host_gid /home/agent" \
"RUN mkdir -p /home/agent/.gnupg && chown $host_uid:$host_gid /home/agent/.gnupg && chmod 700 /home/agent/.gnupg" \
"USER agent" \
| docker build -t $image_name -
or return 1
end
# Ensure host dirs exist (Docker creates missing mount sources as root-owned)
mkdir -p "$HOME/.config/opencode" "$HOME/.local/share/opencode" \
"$HOME/.local/state/opencode" "$HOME/.cache/opencode"
# Core mounts: config, data, state, cache, workspace
set -l docker_args \
-it --rm \
-v "$HOME/.config/opencode:/home/agent/.config/opencode" \
-v "$HOME/.local/share/opencode:/home/agent/.local/share/opencode" \
-v "$HOME/.local/state/opencode:/home/agent/.local/state/opencode" \
-v "$HOME/.cache/opencode:/home/agent/.cache/opencode" \
-v "$workspace:/home/agent/workspace"
# Git config (read-only)
if test -f "$HOME/.gitconfig"
set -a docker_args -v "$HOME/.gitconfig:/home/agent/.gitconfig:ro"
end
# Forward GitHub token (read-only API access)
if test -n "$GH_TOKEN"
set -a docker_args -e "GH_TOKEN=$GH_TOKEN"
end
# Claude auth/config files
if test -f "$HOME/.claude.json"
set -a docker_args -v "$HOME/.claude.json:/home/agent/.claude.json"
end
if test -d "$HOME/.claude"
set -a docker_args -v "$HOME/.claude:/home/agent/.claude"
end
# Forward SSH agent
if test -n "$SSH_AUTH_SOCK"
set -a docker_args \
-v "$SSH_AUTH_SOCK:/tmp/ssh-agent.sock" \
-e "SSH_AUTH_SOCK=/tmp/ssh-agent.sock"
end
# Forward GPG agent for commit signing (YubiKey)
set -l gpg_sock "$HOME/.gnupg/S.gpg-agent"
if test -S "$gpg_sock"
set -a docker_args \
-v "$HOME/.gnupg:/home/agent/.gnupg"
end
docker run $docker_args --entrypoint opencode $image_name $opencode_args
end
```
## SSH setup
Add the following to your `~/.ssh/config` for any SSH host where your opencode sessions are running to get yubikey signing and being able to view any web apps running on the remote (below port 56789)
```
...
ControlMaster auto
ControlPath ~/.ssh/control:%C
ControlPersist 600
LocalForward 56789 localhost:56789
...
RemoteForward /home/donal/.gnupg/S.gpg-agent /Users/donal/.gnupg/S.gpg-agent.extra
```