Files
opencode/AGENTS.md
T
OpenCode Cabillot c2c0a81547
PR Checks / hadolint (pull_request) Successful in 12s
PR Checks / build-test (pull_request) Successful in 3m14s
feat: migrate to OpenCode 2
OpenCode 2 publishes its CLI as @opencode/cli (opencode-ai stops at
1.19.x), so install that package and pin it to 2.0.32.

Also fix what the v2 migration invalidates:
- renovate.json: track @opencode/cli instead of opencode-ai, otherwise
  the pin is never bumped.
- opencode-attach: `opencode attach` no longer exists in v2. Point the
  client at the server with `--server <url>` instead (appended after a
  subcommand when one is given); auth still comes from
  OPENCODE_SERVER_PASSWORD / OPENCODE_PASSWORD.
- README/AGENTS: v2 package name, entrypoint, serve defaults, API
  surface (/global/health is gone, everything lives under authenticated
  /api/*), and removal of OPENCODE_SERVER_USERNAME (the username is
  always `opencode` in v2).

The entrypoint stays ["opencode"] on purpose: existing deployments pass
`serve ...` at runtime, and OpenCode 2 is invoked the same way.
2026-10-17 19:28:14 +00:00

4.9 KiB

AGENTS.md

Project overview

This repository builds and publishes a Docker image for OpenCode, the open source AI coding agent. The image ships OpenCode 2, runs it in headless server mode (opencode serve), and is automatically rebuilt and pushed to Docker Hub (jcabillot/opencode) every night by a Gitea Actions pipeline.

Repository structure

.
├── Dockerfile                            # Image definition
├── .gitea/workflows/docker-build.yaml    # CI/CD pipeline (Gitea Actions)
├── renovate.json                         # Dependency update automation (Renovatebot)
├── opencode-attach                       # Helper script for attaching to a running server
└── README.md                             # Usage documentation

Dependency management

  • Always pin versions in the Dockerfile npm install command (e.g. @opencode/cli@2.0.32 n2-soul@9.0.9). Never leave packages unpinned.
  • Update renovate customManagers when adding, removing, or renaming a dependency tracked in the Dockerfile. Each pinned package must have a corresponding customManager entry in renovate.json with a regex matchStrings pattern that captures the version. If a dependency is added without a renovate entry, Renovatebot will not open automated PRs for it.
  • apt packages (apt-get install lines in Dockerfile) and COPY --from image references are not currently tracked by Renovate. Pinning these manually is acceptable for now but adding renovate managers for them is encouraged.

Dockerfile conventions

  • Base image: node:26-trixie — Debian-based Node.js image (not Alpine) so that downstream images can still use apt-get and Node-based MCP servers keep working.
  • Install: npm i -g @opencode/cli@<version> n2-soul@<version> — installs OpenCode 2 and Soul globally, both pinned. @opencode/cli replaced opencode-ai as the published package in OpenCode 2; --allow-scripts=@opencode/cli is required for its postinstall to select the native binary.
  • Version check: RUN opencode --version after install to validate the build and record the installed version in build logs.
  • Dedicated user: a non-root opencode user and group are created with groupadd/useradd (UID/GID 1000). All runtime steps run as this user.
  • Cluster tooling: kubectl is copied from the official registry.k8s.io/kubectl image (multi-stage COPY).
  • Entrypoint: ["opencode"] — arguments are passed at runtime; serve starts the headless server. Bare opencode opens the TUI, so the server is always invoked explicitly.

OpenCode 2 notes

  • The npm package is @opencode/cli (bins opencode and opencode2); opencode-ai stops at 1.19.x and must not come back.
  • opencode attach no longer exists: point a client at a server with opencode --server <url> (after the subcommand when one is given). opencode-attach wraps this.
  • The server always authenticates: username is fixed to opencode, password comes from OPENCODE_SERVER_PASSWORD or OPENCODE_PASSWORD, and a random one is generated and logged when neither is set.
  • The v1 REST API was replaced (/global/health is gone, every route now lives under /api/* and requires auth). Any client calling the v1 API — e.g. perso/opencode-dispatch — must be migrated separately.

CI/CD conventions (Gitea Actions)

  • Pipeline triggers on push to main, PRs targeting main, and a nightly cron (cron: '0 0 * * *').
  • Image is pushed to Docker Hub only on non-PR events (main branch pushes and cron runs).
  • Docker Hub credentials are stored in Gitea Actions secrets (DOCKERHUB_USERNAME, DOCKERHUB_TOKEN).
  • The image is published as jcabillot/opencode:latest with digest and branch tags.

Useful commands

# Build the image locally
docker build -t opencode .

# Run the server on all interfaces
docker run -it -p 4096:4096 opencode serve --hostname 0.0.0.0

# Run with a project mounted
docker run -it -p 4096:4096 \
  -v $(pwd):/home/opencode/project \
  opencode serve --hostname 0.0.0.0

# Protect with a password
docker run -it -p 4096:4096 \
  -e OPENCODE_SERVER_PASSWORD=secret \
  opencode serve --hostname 0.0.0.0

opencode serve options

Flag Default Description
--port 4096 Port to listen on
--hostname 127.0.0.1 Hostname to bind
--cors [] Additional allowed browser origins (repeatable)
--service false Run as the shared background service
--stdio false Serve over stdio for editor integrations

Environment variables

Variable Description
OPENCODE_SERVER_PASSWORD Server password (HTTP basic auth, username opencode)
OPENCODE_PASSWORD Same, takes precedence over OPENCODE_SERVER_PASSWORD

OPENCODE_SERVER_USERNAME existed in OpenCode 1 and is no longer read: the username is always opencode.