import { createRemoteJWKSet, jwtVerify, type JWTPayload } from "jose" import type { Pipeline } from "cloudflare:pipelines" import semver from "semver" import { stringify } from "yaml" interface Env { DB: D1Database EVENTS: Pipeline } type ArtifactRow = { channel: string name: string distribution: string version: string metadata: string active: number minimum: number time_created: number time_updated: number } type Artifact = Omit & { metadata: unknown active: boolean minimum: boolean } type ArtifactInput = Pick & { metadata: unknown } const identifier = /^[a-zA-Z0-9._-]{1,64}$/ const version = /^[a-zA-Z0-9.+_-]{1,128}$/ const select = "SELECT channel, name, distribution, version, metadata, active, minimum, time_created, time_updated FROM artifact" const audience = "https://update.opencode.ai" const githubKeys = createRemoteJWKSet(new URL("https://token.actions.githubusercontent.com/.well-known/jwks")) export default { async fetch(request: Request, env: Env, ctx: Pick): Promise { const url = new URL(request.url) // The legacy hostname only exposes /admin, covered by its existing Access policy. const prefix = url.hostname !== "update.opencode.ai" && (url.pathname === "/update" || url.pathname.startsWith("/update/")) ? "/update" : "" const pathname = url.pathname.slice(prefix.length) || "/" ctx.waitUntil( env.EVENTS.send([ { source: "update", type: "request", timestamp: new Date().toISOString(), payload: { method: request.method, path: pathname, useragent: request.headers.get("user-agent"), ip: request.headers.get("cf-connecting-ip"), cf_country: request.cf?.country, cf_colo: request.cf?.colo, }, }, ]).catch(() => console.error("Failed to send update request event to the data lake")), ) if (pathname === "/") return json({ service: "opencode-update" }) if (pathname === "/admin" && request.method === "GET") return admin(request, env, prefix) if (pathname === "/admin/activate" && request.method === "POST") return markArtifact(request, env, "active", prefix) if (pathname === "/admin/minimum" && request.method === "POST") return markArtifact(request, env, "minimum", prefix) if (pathname === "/admin/rollout" && request.method === "POST") return configureRollout(request, env, prefix) if (pathname === "/api/publish" && request.method === "POST") return publishArtifact(request, env) if (request.method !== "GET") return new Response("Method not allowed", { status: 405 }) const [root, ...path] = pathname.split("/").filter(Boolean) if (root !== "api" || path.length < 1 || path.length > 4 || !path.every(validIdentifier)) { return new Response("Not found", { status: 404 }) } const resolved = resolveChannel(path[0]) const agent = request.headers.get("User-Agent")?.match(/^opencode\/(?:([^/]+)\/([^/]+)\/cli|(.*))$/) const current = url.searchParams.get("current") ?? agent?.[2] ?? agent?.[3] const source = agent?.[1] ?? current?.match(/^v?0\.0\.0-(.+)-\d+(?:\.\d+)?(?:\+.*)?$/)?.[1] const caller = source === undefined || resolveChannel(source) === resolved ? current : undefined const rollout = await env.DB.prepare("SELECT duration_hours FROM channel_rollout WHERE channel = ?") .bind(resolved) .first<{ duration_hours: number }>() const ip = request.headers.get("CF-Connecting-IP") const hash = rollout?.duration_hours && ip ? new DataView(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(`${resolved}:${ip}`))).getUint32( 0, ) : undefined // Missing IPs wait for full rollout. The same IP keeps its position across releases. const cutoff = Date.now() - (rollout?.duration_hours ?? 0) * 3_600_000 * (hash === undefined ? 1 : (hash + 1) / 2 ** 32) if (path.length === 4) { if (path[1] !== "desktop" || !/^latest(?:-mac|-linux(?:-arm64)?)?\.yml$/.test(path[3])) { return new Response("Not found", { status: 404 }) } return artifactDistribution(env.DB, resolved, path[1], path[2], caller, cutoff, path[3]) } if (path.length === 1) return channel(env.DB, resolved, caller, cutoff) if (path.length === 2) return artifactName(env.DB, resolved, path[1], caller, cutoff) return artifactDistribution(env.DB, resolved, path[1], path[2], caller, cutoff) }, } satisfies ExportedHandler async function channel(db: D1Database, channel: string, current: string | undefined, cutoff: number) { const result = await db .prepare(`${select} WHERE channel = ? AND (active = 1 OR minimum = 1) ORDER BY name, distribution`) .bind(channel) .all() const artifacts = await selectArtifacts(db, result.results, current, cutoff) if (!artifacts.length) return json({ error: "Channel not found" }, 404) return updateResponse({ channel, artifacts: artifacts.map(decodeArtifact) }) } async function artifactName( db: D1Database, channel: string, name: string, current: string | undefined, cutoff: number, ) { const result = await db .prepare(`${select} WHERE channel = ? AND name = ? AND (active = 1 OR minimum = 1) ORDER BY distribution`) .bind(channel, name) .all() const artifacts = await selectArtifacts(db, result.results, current, cutoff) if (!artifacts.length) return json({ error: "Artifact not found" }, 404) return updateResponse({ channel, name, artifacts: artifacts.map(decodeArtifact) }) } async function artifactDistribution( db: D1Database, channel: string, name: string, distribution: string, current: string | undefined, cutoff: number, manifest?: string, ) { const result = await db .prepare(`${select} WHERE channel = ? AND name = ? AND distribution = ? AND (active = 1 OR minimum = 1)`) .bind(channel, name, distribution) .all() const artifact = (await selectArtifacts(db, result.results, current, cutoff))[0] if (!artifact) return json({ error: "Artifact not found" }, 404) if (manifest) { const metadata = decodeMetadata(artifact.metadata) const value = isRecord(metadata) && isRecord(metadata.manifests) ? metadata.manifests[manifest.replace(/^latest/, "desktop")] : undefined if (!isRecord(value)) return json({ error: "Manifest not found" }, 404) return new Response(stringify({ ...value, version: artifact.version }), { headers: { "Content-Type": "application/yaml; charset=utf-8", "Cache-Control": "no-store" }, }) } return updateResponse(decodeArtifact(artifact)) } async function selectArtifacts(db: D1Database, rows: ArtifactRow[], current: string | undefined, cutoff: number) { const caller = current === undefined ? undefined : releaseVersion(current) const artifacts = await Promise.all( rows .filter((row) => row.active === 1) .map(async (active) => { const minimum = rows.find( (row) => row.minimum === 1 && row.name === active.name && row.distribution === active.distribution, ) const floor = minimum && releaseVersion(minimum.version) if (current !== undefined && minimum && (!floor || !caller || semver.lt(caller, floor))) return minimum if (active.time_created <= cutoff) return active const previous = await db .prepare( `${select} WHERE channel = ? AND name = ? AND distribution = ? AND time_created < ? AND time_created <= ? ORDER BY time_created DESC, version DESC LIMIT 1`, ) .bind(active.channel, active.name, active.distribution, active.time_created, cutoff) .first() // A rollout must not send an identified client back below its compatibility floor. if (current !== undefined && minimum) { const version = previous && releaseVersion(previous.version) if (!version || !floor || semver.lt(version, floor)) return minimum } return previous }), ) return artifacts.filter((artifact) => artifact !== null) } async function configureRollout(request: Request, env: Env, prefix: string) { const invalid = validMutation(request) if (invalid) return invalid const form = await request.formData() const channel = form.get("channel") const input = form.get("duration_hours") const duration = typeof input === "string" && input.trim() ? Number(input) : NaN if ( !validIdentifier(channel) || channel === "next" || !Number.isFinite(duration) || duration < 0 || !Number.isFinite(duration * 3_600_000) ) { return json({ error: "Channel and a non-negative rollout duration in hours are required" }, 400) } await env.DB.prepare( "INSERT INTO channel_rollout (channel, duration_hours) VALUES (?, ?) ON CONFLICT (channel) DO UPDATE SET duration_hours = excluded.duration_hours", ) .bind(channel, duration) .run() return Response.redirect(new URL(`${prefix}/admin`, request.url), 303) } function releaseVersion(input: string) { // Preview builds use a hyphen before the run number. Normalize it to a numeric // prerelease identifier, and compare historical next builds in the beta channel. return semver.valid( input.replace( /^(v?0\.0\.0)-(next|beta|dev)-(\d+)(?=\.|$)/, (_, core, channel, build) => `${core}-${resolveChannel(channel)}.${build}`, ), ) } async function admin(request: Request, env: Env, prefix: string) { const url = new URL(request.url) const rollouts = await env.DB.prepare( `SELECT channels.channel, COALESCE(channel_rollout.duration_hours, 0) AS duration_hours FROM (SELECT channel FROM artifact UNION SELECT channel FROM channel_rollout) AS channels LEFT JOIN channel_rollout ON channel_rollout.channel = channels.channel WHERE channels.channel != 'next' ORDER BY channels.channel`, ).all<{ channel: string; duration_hours: number }>() const requestedPage = Number.parseInt(url.searchParams.get("page") ?? "1", 10) const page = Number.isSafeInteger(requestedPage) && requestedPage > 0 ? requestedPage : 1 const pageSize = 100 const count = await env.DB.prepare("SELECT COUNT(*) AS total FROM artifact").first<{ total: number }>() const pages = Math.max(1, Math.ceil((count?.total ?? 0) / pageSize)) const currentPage = Math.min(page, pages) const result = await env.DB.prepare(`${select} ORDER BY time_created DESC LIMIT ? OFFSET ?`) .bind(pageSize, (currentPage - 1) * pageSize) .all() const rows = result.results .map( (artifact) => ` ${escape(artifact.channel)} ${escape(artifact.name)} ${escape(artifact.distribution)} ${escape(artifact.version)} ${new Date(artifact.time_created).toISOString()} ${artifact.active ? 'Active' : 'Inactive'} ${artifact.minimum ? 'Minimum' : ""} ${ artifact.active ? "" : `
` }
`, ) .join("") return new Response( ` OpenCode Update

Release control

Artifacts

${escape(request.headers.get("Cf-Access-Authenticated-User-Email") ?? "Cloudflare Access pending")}

Channel rollouts

Hours from publication to full availability. Zero is immediate. Changes apply immediately to existing releases.

${ rollouts.results .map( (rollout) => `
`, ) .join("") || "

Publish a release to configure its channel.

" }

Published builds

Every build received from the trusted publishing workflow, newest first.

${rows || ''}
ChannelNameDistributionVersionCreatedStatus
No builds have been published yet.

Page ${currentPage} of ${pages} · ${count?.total ?? 0} builds

`, { headers: { "Content-Type": "text/html; charset=utf-8", "Cache-Control": "no-store" } }, ) } async function publishArtifact(request: Request, env: Env) { const claims = await verifyGitHub(request) if (claims instanceof Response) return claims const input: unknown = await request.json().catch(() => undefined) const artifact = parseArtifact(isRecord(input) ? input : {}) if (artifact instanceof Response) return artifact if (!channelsForRef(claims.ref).includes(artifact.channel)) return json({ error: "Channel is not allowed" }, 403) if (!isRecord(artifact.metadata)) return json({ error: "Metadata must be an object" }, 400) await activate(env.DB, [ { ...artifact, metadata: { ...artifact.metadata, github: { sha: claims.sha, run_id: claims.run_id, run_attempt: claims.run_attempt, actor: claims.actor, ref: claims.ref, }, }, }, ]) return json({ published: true }) } async function markArtifact(request: Request, env: Env, flag: "active" | "minimum", prefix: string) { const invalid = validMutation(request) if (invalid) return invalid const form = await request.formData() const key = parseKey({ channel: form.get("channel"), name: form.get("name"), distribution: form.get("distribution"), version: form.get("version"), }) if (key instanceof Response) return key const enabled = flag === "minimum" && form.get("enabled") === "0" ? 0 : 1 if (flag === "minimum" && enabled && !releaseVersion(key.version)) { return json({ error: "Minimum must have a comparable release version" }, 400) } const exists = await env.DB.prepare( "SELECT 1 FROM artifact WHERE channel = ? AND name = ? AND distribution = ? AND version = ?", ) .bind(key.channel, key.name, key.distribution, key.version) .first() if (!exists) return json({ error: "Artifact not found" }, 404) await env.DB.batch([ ...(enabled ? [ env.DB.prepare( `UPDATE artifact SET ${flag} = 0 WHERE channel = ? AND name = ? AND distribution = ? AND ${flag} = 1`, ).bind(key.channel, key.name, key.distribution), ] : []), env.DB.prepare( `UPDATE artifact SET ${flag} = ?, time_updated = ? WHERE channel = ? AND name = ? AND distribution = ? AND version = ?`, ).bind(enabled, Date.now(), key.channel, key.name, key.distribution, key.version), ]) return Response.redirect(new URL(`${prefix}/admin`, request.url), 303) } function activate(db: D1Database, artifacts: ArtifactInput[]) { return db.batch( artifacts.flatMap((artifact) => { const time = Date.now() return [ deactivateStatement(db, artifact), db .prepare( `INSERT INTO artifact (channel, name, distribution, version, metadata, active, time_created, time_updated) VALUES (?, ?, ?, ?, ?, 1, ?, ?) ON CONFLICT (channel, name, distribution, version) DO UPDATE SET metadata = excluded.metadata, active = 1, time_updated = excluded.time_updated`, ) .bind( artifact.channel, artifact.name, artifact.distribution, artifact.version, JSON.stringify(artifact.metadata), time, time, ), ] }), ) } function deactivateStatement(db: D1Database, artifact: Pick) { return db .prepare("UPDATE artifact SET active = 0 WHERE channel = ? AND name = ? AND distribution = ? AND active = 1") .bind(artifact.channel, artifact.name, artifact.distribution) } function parseArtifact(input: Record): ArtifactInput | Response { const key = parseKey(input) if (key instanceof Response) return key const metadata = typeof input.metadata === "string" ? parseMetadata(input.metadata) : input.metadata if (metadata === undefined) return json({ error: "Metadata must be valid JSON" }, 400) return { ...key, metadata } } function parseKey(input: Record): Omit | Response { if ( !validIdentifier(input.channel) || input.channel === "next" || !validIdentifier(input.name) || !validIdentifier(input.distribution) || !validVersion(input.version) ) return json({ error: "Invalid artifact" }, 400) return { channel: input.channel, name: input.name, distribution: input.distribution, version: input.version, } } function decodeArtifact(row: ArtifactRow): Artifact { return { ...row, metadata: decodeMetadata(row.metadata), active: row.active === 1, minimum: row.minimum === 1 } } function decodeMetadata(input: string) { return parseMetadata(input) ?? null } function parseMetadata(input: string): unknown | undefined { try { return JSON.parse(input) } catch { return } } async function verifyGitHub(request: Request) { const authorization = request.headers.get("Authorization") if (!authorization?.startsWith("Bearer ")) return json({ error: "Unauthorized" }, 401) const result = await jwtVerify(authorization.slice("Bearer ".length), githubKeys, { issuer: "https://token.actions.githubusercontent.com", audience, }).catch(() => undefined) if (!result || !validGitHubClaims(result.payload)) return json({ error: "Unauthorized" }, 401) return result.payload } type GitHubClaims = JWTPayload & { repository: string repository_id: string repository_owner_id: string workflow_ref: string ref: string sha: string run_id: string run_attempt: string actor: string } export function validGitHubClaims(claims: JWTPayload): claims is GitHubClaims { if (claims.repository !== "anomalyco/opencode") return false if (claims.repository_id !== "975734319") return false if (claims.repository_owner_id !== "66570915") return false if (typeof claims.workflow_ref !== "string" || typeof claims.ref !== "string") return false if (claims.workflow_ref !== `anomalyco/opencode/.github/workflows/publish.yml@${claims.ref}`) return false if (!channelsForRef(claims.ref).length) return false return [claims.sha, claims.run_id, claims.run_attempt, claims.actor].every((value) => typeof value === "string") } export function channelsForRef(ref: string) { if (ref === "refs/heads/dev") return ["dev", "latest"] if (ref === "refs/heads/v2") return ["dev", "latest"] if (ref === "refs/heads/beta") return ["beta"] if (ref === "refs/heads/ci") return ["ci"] if (ref === "refs/heads/fix/npm-native-binary-install") return ["fix/npm-native-binary-install"] const snapshot = ref.match(/^refs\/heads\/(snapshot-[a-zA-Z0-9._-]+)$/)?.[1] return snapshot ? [snapshot] : [] } export function resolveChannel(channel: string) { return channel === "next" ? "beta" : channel } function validMutation(request: Request) { const origin = request.headers.get("Origin") if (origin && origin !== new URL(request.url).origin) return json({ error: "Invalid origin" }, 403) } function validIdentifier(input: unknown): input is string { return typeof input === "string" && identifier.test(input) } function validVersion(input: unknown): input is string { return typeof input === "string" && version.test(input) } function isRecord(input: unknown): input is Record { return typeof input === "object" && input !== null && !Array.isArray(input) } function updateResponse(value: unknown) { return json(value, 200, { "Cache-Control": "no-store" }) } function json(value: unknown, status = 200, headers?: HeadersInit) { return Response.json(value, { status, headers: { "Cache-Control": "no-store", ...headers } }) } function escape(value: string) { return value.replace(/[&<>"']/g, (character) => { if (character === "&") return "&" if (character === "<") return "<" if (character === ">") return ">" if (character === '"') return """ return "'" }) }