An opencode plugin that executes shell commands on a remote machine through
sshd instead of (or in addition to) the local shell. No agent, daemon, or
helper binary is installed on the remote: sshd is the only server-side
requirement.
Architecture and design decisions: docs/design-docs.md.
opencode plugin opencode-remote-worker -g # global installKeep the plugin declared in exactly one scope. A global instance plus the
projects map is the supported setup; declaring it in both global and
project config registers two colliding instances.
Host aliases and credentials come from your existing SSH setup (agent, keys,
~/.ssh/config). known_hosts entries must be unhashed (plain) in v1;
before first use, seed the entry with
ssh-keyscan build.example.com >> ~/.ssh/known_hosts.
| Option | Meaning |
|---|---|
hosts | Map of host name to { host?, user?, port?, shell?, remoteCwd?, timeout?, keepalive?, identityFile? } |
default | Host used when no project matches and no host arg is given |
remoteCwd | Default remote working directory for hosts that do not set one |
keepalive | SSH keepalive interval in seconds (maps to ServerAliveInterval semantics) |
requireProjectMatch | true denies remote execution outside listed projects |
projects | Map of local directory (longest-prefix) to { host, remoteCwd? } |
overrideBash | true registers a tool named bash so all shell execution goes remote |
Host resolution order: tool host arg, then project longest-prefix match on
the session directory, then default. Working directory: tool cwd arg,
then project remoteCwd, then host remoteCwd, then the remote home.
The plugin registers the remote_bash tool with args { command, host?, cwd?, env? }.
Remote execution is independently allow/deniable under the remote_bash
permission id:
{ "permission": { "remote_bash": { "allow": ["*"], "deny": ["rm -rf *"] } } }In the TUI, open the panel via ctrl+p (commands) -> "Link this directory to a
remote host": link the current directory to a host (probed through the plugin's
own transport first) or unlink it. Changes apply on the next opencode start.
The TUI hides output of custom tools by default. To see remote_bash results,
toggle "Show generic tool output" in the session menu. The overrideBash mode
is not affected and always shows output.
bun install
bun test# unit tests
bun typecheckEnvironment variables:
| Variable | Purpose |
|---|---|
OPENCODE_RW_SSH_DIR | Overrides ~/.ssh for config/known_hosts lookups (tests use a throwaway dir) |
OPENCODE_RW_TEST_SSH_HOST | Enables the integration tests against a real sshd (host must be in known_hosts) |
OPENCODE_RW_TEST_SSH_USER / OPENCODE_RW_TEST_SSH_PORT | Integration test credentials overrides |
Integration test example with a throwaway ssh dir:
OPENCODE_RW_TEST_SSH_HOST=build.example.com \
OPENCODE_RW_SSH_DIR=/tmp/ssh-fixture \
bun test test/ssh.integration.test.ts- Commands run as the remote SSH user with that user's environment; there is no sandbox on the worker in v1. Prefer a dedicated low-privilege SSH user.
- Host keys are verified against
known_hosts(plain entries only in v1). - The permission gate is the same trust surface as local
bash.
{ "plugin": [ ["opencode-remote-worker", { "hosts": { "worker1": { "host": "build.example.com", "user": "builder", "port": 22, "shell": "bash", "timeout": 120 } }, "default": "worker1", "remoteCwd": "/var/builds", "keepalive": 10, "requireProjectMatch": false, "projects": { "/home/me/code/heavy-builds": { "host": "worker1", "remoteCwd": "/srv/builds/heavy-builds" } } }] ] }