Provider-based remote workspace plugin and Go stub for using OpenCode against Linux hosts that cannot run OpenCode directly.
opencode-remote-ssh lets OpenCode work against remote Linux machines over SSH.
The system has two parts:
- A local OpenCode plugin that resolves configured hosts, manages SSH bootstrap, and exposes a remote workspace target.
- A small Go stub installed on the remote host that serves an OpenCode-compatible API over an SSH tunnel.
There are two ways to load the plugin in your local OpenCode config:
- Package-based plugin registration
- Path-based local development registration
The public, automated path in this repository is package-based registration.
The helper CLI scripts/opencode-remote-cli.sh only manages package-based plugin config entries.
If you are loading the plugin from a local filesystem path during development, edit your config manually.
- Provider-based host management
- SSH bootstrap for the remote stub
- Plugin-managed SSH tunneling
- Permission-first path access
- Persistent
alwaysapprovals per workspace - Self-contained remote stub binary
- Local: OpenCode, Node.js for the plugin, Go 1.21+ to build the stub
- Remote: Linux, SSH access, POSIX shell
Recommended public configuration uses the package name:
{
"plugin": [
[
"opencode-remote-provider",
{
"providers": {
"default": {
"strategy": "first_available",
"hosts": [
{
"name": "prod-web-1",
"ssh": {
"host": "203.0.113.10",
"user": "ops",
"port": 22,
"identityFile": "~/.ssh/id_ed25519"
},
"labels": ["linux", "production"]
}
]
}
}
}
]
]
}The CLI manages package-based plugin config entries in ~/.config/opencode/opencode.json.
./scripts/opencode-remote-cli.sh add default prod-web-1 ops 22 ~/.ssh/id_ed25519
./scripts/opencode-remote-cli.sh listUse the setup script to install and start the remote stub on the host:
./scripts/setup-host.sh 203.0.113.10 ops 22 ~/.ssh/id_ed25519What this does:
- Verifies SSH connectivity
- Creates remote directories under
~/.opencode-remote/ - Uploads the stub binary
- Writes a new auth token
- Starts the stub on the remote host
What it does not do by itself:
- It does not permanently attach OpenCode to the remote host.
- The runtime connection is established later by the plugin when a workspace is created.
When creating a workspace, specify the ssh-provider type and provider/host in extra:
{
"type": "ssh-provider",
"extra": {
"provider": "default",
"host": "prod-web-1"
}
}The plugin will:
- Resolve the configured host
- Ensure the remote stub is installed and running
- Establish a local SSH tunnel
- Verify remote health
- Return the remote workspace target to OpenCode
./scripts/opencode-remote-cli.sh init
./scripts/opencode-remote-cli.sh add <provider> <host> <user> [port] [identity-file]
./scripts/opencode-remote-cli.sh remove <provider> <host>
./scripts/opencode-remote-cli.sh list
./scripts/opencode-remote-cli.sh setup <host> <user> [port] [identity-file]Notes:
- The CLI creates timestamped backups of
~/.config/opencode/opencode.jsonbefore modifying it. - The CLI only supports package-based plugin entries.
- If your config uses a local filesystem path to load the plugin, manage that config block manually.
If you are developing this repository locally, you may point OpenCode at the plugin source directory directly.
Example:
{
"plugin": [
[
"/absolute/path/to/opencode-remote/plugin",
{
"providers": {
"default": {
"hosts": []
}
}
}
]
]
}In that mode:
- You are using a path-based local dev setup.
- The CLI in this repository will not mutate that plugin config automatically.
- Edit the config manually.
Each host may now define optional aliases, and workspace creation can target either the canonical name or any configured alias. The plugin still resolves the real network destination from ssh.host at runtime.
Example:
{
"providers": {
"default": {
"hosts": [
{
"name": "protagmanager",
"aliases": ["protag", "tagmgr"],
"ssh": {
"host": "159.203.115.52",
"user": "root",
"port": 22
},
"labels": ["linux", "remote"]
}
]
}
}
}Any of these workspace targets now resolve to the same configured host:
{
"type": "ssh-provider",
"extra": {
"provider": "default",
"host": "protagmanager"
}
}{
"type": "ssh-provider",
"extra": {
"provider": "default",
"host": "protag"
}
}Path access is denied by default.
When a shell operation attempts to access an unapproved path:
- The stub creates a permission request.
- The request must be approved explicitly.
alwaysapprovals persist for that workspace.
The plugin does not auto-approve these permissions.
On the remote host, files are installed under ~/.opencode-remote/:
~/.opencode-remote/
├── bin/
│ └── opencode-remote-stub
├── run/
│ └── stub.token
├── log/
│ └── stub.log
└── state/
├── workspaces/
├── sessions/
└── approvals/
If you want to verify the stub manually after setup:
ssh -N -L 39300:127.0.0.1:39217 user@host
TOKEN=$(ssh user@host 'cat ~/.opencode-remote/run/stub.token')
curl -H "Authorization: Bearer $TOKEN" http://127.0.0.1:39300/global/health- Older hosts such as CentOS/RHEL 7 era systems may require a statically built stub binary.
- The current build/test flow for this repository uses
CGO_ENABLED=0when targeting those older hosts. - Some older hosts do not keep a backgrounded process alive reliably with a plain remote
nohup ... &launch. setup-host.shnow starts the stub in its own session from the Python launcher so older systems are less likely to kill it when the bootstrap SSH process exits.- Password-only SSH hosts are supported for bootstrap:
- if
sshpassis installed locally, the script can install the generated key automatically - otherwise, the script prints exact manual
authorized_keyssetup steps
- if
Build the plugin:
cd plugin
npm install
npm run buildBuild the stub:
cd stub
CGO_ENABLED=0 go build -o bin/opencode-remote-stub ./cmd