@daytona/opencode
v0.192.0
Published
OpenCode plugin that automatically runs all sessions in Daytona sandboxes for isolated, reproducible development environments
Downloads
509
Readme
Daytona Sandbox Plugin for OpenCode
This is an OpenCode plugin that automatically runs OpenCode sessions in Daytona sandboxes. Each session has its own remote sandbox which is automatically synced to a local git branch.
Features
- Securely isolate each OpenCode session in a sandbox environment
- Preserves sandbox environments indefinitely until the OpenCode session is deleted
- Generates live preview links when a server starts in the sandbox
- Synchronizes each OpenCode session to a local git branch
Usage
Installation
To add the plugin to a project, edit opencode.json in the project directory:
{
"$schema": "https://opencode.ai/config.json",
"plugin": ["@daytona/opencode"]
}Now that the Daytona plugin is in the plugins list, it will automatically be downloaded when OpenCode starts.
To install the plugin globally, edit ~/.config/opencode/opencode.json.
Environment Configuration
This plugin requires a Daytona account and Daytona API key to create sandboxes.
Set your Daytona API key as an environment variable:
export DAYTONA_API_KEY="your-api-key"Or create a .env file in your project root:
DAYTONA_API_KEY=your-api-keyCreating sandboxes from a snapshot
By default, sandboxes are created from the Daytona default snapshot. To create them from a specific snapshot instead — for example one that pre-installs your project's toolchain and dependencies — set DAYTONA_SNAPSHOT to the snapshot name:
export DAYTONA_SNAPSHOT="my-snapshot"DAYTONA_SNAPSHOT=my-snapshotThe snapshot must already exist and be active in your organization; create one via the Daytona Dashboard or the Daytona CLI. If the name doesn't resolve, sandbox creation fails with an error naming the missing snapshot rather than silently falling back to the default.
Leave DAYTONA_SNAPSHOT unset to keep the default behavior. The plugin still creates /home/daytona/project and syncs your git branch into it.
Pinning the sandbox SSH host key
Git syncing transfers commits between your machine and the sandbox over SSH (ssh.app.daytona.io). By default, host verification follows your normal SSH configuration — on a machine that has never connected before, this means an interactive trust-on-first-use prompt, which blocks noninteractive environments such as CI or supervised agent runs.
To make syncing noninteractive and independently verifiable, point DAYTONA_SSH_KNOWN_HOSTS at a known_hosts file containing the ssh.app.daytona.io host keys:
mkdir -p ~/.config/daytona
ssh-keyscan ssh.app.daytona.io > ~/.config/daytona/known_hosts
export DAYTONA_SSH_KNOWN_HOSTS=~/.config/daytona/known_hostsVerify the collected fingerprints out of band (ssh-keygen -lf ~/.config/daytona/known_hosts) before trusting the file. When DAYTONA_SSH_KNOWN_HOSTS is set, sandbox git transfers use that file as the only host-key database (StrictHostKeyChecking=yes, system-wide known hosts ignored); SSH behavior for every other remote is unaffected. When unset, behavior is unchanged. Paths containing spaces are supported; a literal " in the path is rejected.
Running OpenCode
Before starting OpenCode, ensure that your project is a git repository:
git initNow start OpenCode in your project using the OpenCode command:
opencodeTo check that the plugin is working, type pwd in the chat. You should see a response like /home/daytona/project, and a toast notification that a new sandbox was created.
OpenCode will create new branches using the format opencode/1, opencode/2, etc. To work with these changes, use normal git commands in a separate terminal window. List branches:
git branchCheck out OpenCode's latest changes on your local system:
git checkout [branch]To view live logs from the plugin for debugging, run this command in a separate terminal:
tail -f ~/.local/share/opencode/log/daytona.logHow It Works
File Synchronization
The plugin uses git to synchronize files between the sandbox and your local system. This happens automatically and in the background, keeping your copy of the code up-to-date without exposing your system to the agent.
Sandbox Setup
When a new Daytona sandbox is created:
- The plugin looks for a git repository in the local directory. If none is found, file synchronization will be disabled.
- A parallel repository is created in the sandbox with a single
opencodebranch, mirroring the checked out local branch. - A new
sandboxremote is added to the local repository using an SSH connection to the sandbox. - The
HEADof the local repository is pushed toopencode, and the sandbox repository is reset to match this initial state. - Each sandbox is assigned a unique incrementing branch number (1, 2, 3, etc.) that persists across sessions.
Synchronization
Each time the agent makes changes:
- A new commit is created in the sandbox repository on the
opencodebranch. - The plugin pulls the latest commits from the sandbox remote into a unique local branch named
opencode/1,opencode/2, etc. This keeps both environments in sync while isolating changes from different sandboxes in separate local branches.
The plugin only synchronizes changes from the sandbox to your system. To pass local changes to the agent, commit them to a local branch, and start a new OpenCode session with that branch checked out.
[!CAUTION] When changes are synchronized to local
opencodebranches, any locally made changes will be overwritten.
Sync guarantees
The per-turn sync runs in the background: OpenCode dispatches the session.idle event without waiting for plugin work, so observing that event does not mean the changes have reached your local repository yet. The plugin provides three stronger boundaries:
gitSynctool — commits pending sandbox changes and pulls them into the localopencode/Nbranch, returning only after they are in the local repository. Failures are returned as tool errors. Automation that needs a reliable handoff (for example, a supervisor driving OpenCode through the SDK) should ask the agent to rungitSyncas its final step and check the tool result instead of treatingsession.idleas proof that changes have landed.- Session deletion — before deleting a sandbox, the plugin waits for any in-flight sync and pulls remaining changes from a running sandbox. If unsynced changes cannot be pulled — including when the local repository is no longer accessible — deletion is aborted and the sandbox is preserved. A sandbox that is not running is deleted without being started: anything in it was either synced while it ran or is abandoned by the explicit delete.
- Shutdown — when OpenCode shuts down gracefully, it waits (up to 60 seconds) for the plugin to finish in-flight syncs before exiting.
Session to sandbox mapping
The plugin keeps track of which sandbox belongs to each OpenCode project using local state files. This data is stored in a separate JSON file for each project:
- Default (when
XDG_DATA_HOMEis unset):~/.local/share/opencode/storage/daytona/[projectid].json. - When
XDG_DATA_HOMEis set:$XDG_DATA_HOME/opencode/storage/daytona/[projectid].json.
Each JSON file contains the sandbox metadata for each session in the project, including when the sandbox was created, and when it was last used.
The plugin uses XDG Base Directory specifically to resolve the path to this directory, using the convention set by OpenCode.
Development
This package lives in the daytona/integrations monorepo under packages/opencode-plugin, and is self-contained — its own package.json, lockfile, and dependencies (no workspace tooling).
Setup
git clone https://github.com/daytona/integrations
cd integrations/packages/opencode-plugin
npm installDevelopment and Testing
To modify the plugin, edit the source code files in .opencode/plugin.
To test the OpenCode plugin, create a test project to run OpenCode in:
mkdir ~/myproject
cd myprojectAdd a symlink from the project directory to the plugin source code:
ln -s [ABSOLUTE_PATH_TO_REPO]/packages/opencode-plugin/.opencode .opencodeInitialize git to enable file syncing:
git initStart OpenCode in the test project:
opencodeUse the instructions from Running OpenCode above to check that the plugin is running and view live logs for debugging.
[!NOTE] When developing locally with a symlink, OpenCode loads the TypeScript source directly, so no build step is required.
Building
Build the plugin — tsc compiles .opencode/plugin/**/*.ts to .js + .d.ts in place:
npm run buildThe published package contains the compiled .js/.d.ts; the .ts sources are stripped by .npmignore.
Test the built package
After building, create a test project and add a plugin file to load the built plugin (replace [ABSOLUTE_PATH_TO_REPO] with your clone path, e.g. /Users/you/integrations):
mkdir -p ~/myproject && cd ~/myproject
mkdir -p .opencode/plugins
cat > .opencode/plugins/daytona-local.js << 'EOF'
module.exports = require('[ABSOLUTE_PATH_TO_REPO]/packages/opencode-plugin/.opencode/plugin')
EOFInitialize git to enable file syncing, and start OpenCode:
git init
opencodePublishing
Releases are automated: merging this package's release-please Release PR builds it and publishes the compiled package to npm (public, with provenance) from the repo's release workflow — there is no manual publish step.
Project Structure
packages/opencode-plugin/
├── .opencode/plugin/ # Plugin source (TypeScript)
│ ├── daytona/ # Main Daytona integration
│ └── index.ts # Plugin entry point (compiled to .js in place)
├── .gitignore
├── .npmignore
├── package.json # Package metadata (main/types + build script)
├── tsconfig.json # TypeScript config
└── README.mdLicense
Apache-2.0
