# OpenCode Update The update Worker serves all selected artifacts for a channel. ```sh 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. ## Desktop update feeds Electron updater manifests are available at `/api//desktop//`: ```sh curl 'https://opencode.ai/update/api/beta/desktop/opencode/latest-mac.yml' ``` Use `latest.yml` for Windows, `latest-mac.yml` for macOS, `latest-linux.yml` for Linux x64, and `latest-linux-arm64.yml` for Linux ARM64. The distribution selects the stored download URLs (`opencode` or `github`). Each manifest includes the selected artifact's version, file URLs, SHA-512 checksums, sizes, and release date. Existing minimum-version selection and `current`/User-Agent handling also apply to these feeds. ## Channel rollouts Set each channel's rollout duration in hours on the admin page. All channels default to `0` (immediate); fractional hours are supported. For example, `6` makes a release available to roughly half of IPs after three hours and all IPs after six hours. Eligibility uses the original publication time (`time_created`) and a SHA-256 hash of the channel and Cloudflare's `CF-Connecting-IP`. Each IP keeps the same rollout position across releases in that channel. Requests without this header wait for the full duration. The retired `next` channel is not configurable; `/api/next` remains a compatibility alias for `/api/beta`, including its rollout settings. Until the active release is eligible, callers receive the newest eligible artifact published before it, for the same name and distribution. This also handles overlapping rollouts. Earlier inactive releases can be fallbacks, including manually deactivated releases; releases newer than the active release cannot. If no eligible artifact exists, it is omitted from listings and individual artifact requests return 404. Minimum releases bypass rollout for clients that need them, and identified clients are not sent a fallback below their configured minimum. Rollout applies to all JSON endpoints and desktop manifests. Responses, including unavailable artifacts, are not cached. Duration changes apply immediately to existing releases. Manual activation uses the original publication time too; set the duration to `0` to make it immediate. Apply the `0004_channel_rollout.sql` migration before deploying. ## 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=` and use the CLI's default `User-Agent: opencode///cli`. When `current` is absent, the service parses that User-Agent or the older `opencode/` 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 retired `/api/next` channel transparently resolves to `beta` for older clients. 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 The publish workflow also registers direct binary downloads with distribution `opencode`, after uploading every file to the public files bucket: ```sh curl 'https://opencode.ai/update/api/dev/cli/opencode' curl 'https://opencode.ai/update/api/dev/cli-node/opencode' curl 'https://opencode.ai/update/api/latest/desktop/opencode' ``` `metadata.files` maps each filename to its direct `https://opencode.ai/files/bin//` URL, SHA-256 checksum, and byte size. Desktop records additionally contain `metadata.manifests`, with the CDN URLs and the original Electron update checksums. Existing `npm`, `github`, and `aur` distributions are published independently. 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: ```sh bun run db:migrate bun run deploy ```