Files
opencode/services/updates/README.md
T

4.0 KiB

OpenCode Updates

The updates Worker serves all selected artifacts for a channel.

curl 'https://opencode.ai/update/api/latest'
curl 'https://opencode.ai/update/api/latest/cli'
curl 'https://opencode.ai/update/api/latest/cli/npm'

The same Worker also serves the original https://update.opencode.ai hostname without the /update prefix. Admin forms, pagination, and redirects preserve the request's mount.

Minimum releases

Each channel/name/distribution can mark one retained artifact as minimum, independently of its active artifact. Set or clear it from the admin page. Publishing a new active release preserves the minimum marker.

Clients send their running version as ?current=<version> and use the CLI's default User-Agent: opencode/<channel>/<version>/cli. When current is absent, the service parses that User-Agent or the older opencode/<version> format. The caller's channel comes from the default User-Agent, or from a preview version such as 0.0.0-beta-18955 when that channel is absent. If an identified caller channel differs from the requested channel, the service serves the active release without applying the minimum. Historical next and beta count as the same channel. A caller below the minimum receives that exact artifact; callers at or above it receive the active artifact. An unparseable caller version receives the minimum. Requests with neither version source receive the active artifact. All three public API paths apply the same selection and use Cache-Control: no-store because responses can depend on the User-Agent.

Version comparison uses semver, normalizing preview run numbers to numeric prerelease identifiers and historical next versions to beta. The /api/next channel also resolves to beta.

Choose a minimum that older clients can install and that can itself consume the active release. For the CLI package migration, retain a package-aware release published as @opencode-ai/cli as the minimum before activating releases under @opencode/cli.

Both admin mounts must be protected by the same Cloudflare Access self-hosted application:

  • Public hostname/path: update.opencode.ai/admin*
  • Public hostname/path: opencode.ai/update/admin*
  • Policy: allow the OpenCode team identity group

Configure Access before deploying the new Worker route. The Worker has workers_dev and preview URLs disabled; it is exposed through its custom hostname and the /update routes.

Request logging

Every request reaching the Worker emits an unsampled event at request start to the shared production Cloudflare lake stream through the EVENTS Pipelines binding. Events use source: "update", type: "request", an ISO timestamp, and a payload containing the method, path, useragent, ip (from Cloudflare's CF-Connecting-IP header), country, and Cloudflare colo. The path is normalized without /update so both mounts share the existing analytics paths. Query strings, request bodies, cookies, and authorization headers are not included. Response status and duration are not recorded.

Delivery runs in waitUntil without delaying the response. Delivery failures are logged but do not fail requests or retry; this is not lossless audit logging. Requests blocked before reaching the Worker are not recorded.

The stream ID in wrangler.jsonc comes from the lake.stream output of the anomalyco/platform/production Pulumi stack. Update the binding if that stream is replaced. The stream is shared across release channels because the update service has a single public deployment.

Publishing

GitHub Actions publishes artifacts through POST https://opencode.ai/update/api/publish using a short-lived OIDC token with audience https://update.opencode.ai. The original hostname and token audience remain supported. The Worker accepts only tokens signed by GitHub for repository ID 975734319, owner ID 66570915, and .github/workflows/publish.yml on configured publishing refs.

Apply migrations and deploy from this directory:

bun run db:migrate
bun run deploy