Problem
Currently opencode serve only supports HTTP Basic Auth via OPENCODE_SERVER_PASSWORD. This creates friction in common deployment scenarios:
-
Reverse proxy / API gateway integration — Most ingress controllers (nginx, Caddy, Traefik, Cloudflare Tunnel) and API gateways use Bearer tokens for upstream authentication. Operators must add a translation layer to convert Bearer → Basic.
-
Programmatic access — External tools (CI pipelines, scripts, desktop apps) typically pass Authorization: Bearer <token> rather than encoding username:password in base64. The current Basic Auth requirement forces each client to know the username convention ("opencode").
-
Credential leakage surface — Basic Auth embeds the password in every request header as reversible base64, whereas a Bearer token is opaque and easier to rotate/revoke without changing the underlying secret.
Related issues that reflect current auth pain points:
Current Implementation
From packages/opencode/src/server/middleware.ts:
export const AuthMiddleware: MiddlewareHandler = (c, next) => {
if (c.req.method === "OPTIONS") return next()
const password = Flag.OPENCODE_SERVER_PASSWORD
if (!password) return next()
const username = Flag.OPENCODE_SERVER_USERNAME ?? "opencode"
if (c.req.query("auth_token"))
c.req.raw.headers.set("authorization", `Basic ${c.req.query("auth_token")}`)
return basicAuth({ username, password })(c, next)
}
The Authorization Effect layer (server.ts#L69-L83) only handles the basic scheme — no bearer branch exists.
Proposal
Add a new env var OPENCODE_SERVER_TOKEN that enables Bearer token auth:
# Option A: Bearer token only
OPENCODE_SERVER_TOKEN=my-secret-token
# Option B: Both schemes simultaneously (backward compatible)
OPENCODE_SERVER_PASSWORD=legacy-pass
OPENCODE_SERVER_TOKEN=my-secret-token
Expected behavior
| Header |
Result |
Authorization: Bearer <OPENCODE_SERVER_TOKEN> |
✅ Authenticated |
Authorization: Basic base64(user:OPENCODE_SERVER_PASSWORD) |
✅ Authenticated (existing) |
?auth_token=<OPENCODE_SERVER_TOKEN> |
✅ Authenticated (query param) |
| No header, no token set |
✅ Unsecured (existing) |
| Wrong token/password |
❌ 401 |
Suggested middleware change
export const AuthMiddleware: MiddlewareHandler = (c, next) => {
if (c.req.method === "OPTIONS") return next()
const password = Flag.OPENCODE_SERVER_PASSWORD
const token = Flag.OPENCODE_SERVER_TOKEN
// No auth configured — pass through
if (!password && !token) return next()
const authHeader = c.req.header("authorization") ?? ""
// Bearer token check
if (token) {
if (authHeader === `Bearer ${token}`) return next()
if (c.req.query("auth_token") === token) return next()
}
// Fall back to Basic Auth
if (password) {
if (c.req.query("auth_token"))
c.req.raw.headers.set("authorization", `Basic ${c.req.query("auth_token")}`)
const username = Flag.OPENCODE_SERVER_USERNAME ?? "opencode"
return basicAuth({ username, password })(c, next)
}
return c.text("Unauthorized", 401)
}
Changes needed
- Add
OPENCODE_SERVER_TOKEN to flag.ts
- Update
AuthMiddleware in middleware.ts to handle Bearer scheme
- Add
bearer branch to the Effect-based Authorization layer in server.ts
- Update
opencode attach --password to also accept --token flag
- Update the
web command warning to mention both env vars
Alternatives Considered
- Reuse
OPENCODE_SERVER_PASSWORD as Bearer token — Conflates two auth schemes; clients can't know which header format to use.
- Only support Bearer, drop Basic — Breaking change for existing deployments.
- Reverse proxy workaround — Works but adds operational complexity for a common use case.
Problem
Currently
opencode serveonly supports HTTP Basic Auth viaOPENCODE_SERVER_PASSWORD. This creates friction in common deployment scenarios:Reverse proxy / API gateway integration — Most ingress controllers (nginx, Caddy, Traefik, Cloudflare Tunnel) and API gateways use Bearer tokens for upstream authentication. Operators must add a translation layer to convert Bearer → Basic.
Programmatic access — External tools (CI pipelines, scripts, desktop apps) typically pass
Authorization: Bearer <token>rather than encodingusername:passwordin base64. The current Basic Auth requirement forces each client to know the username convention ("opencode").Credential leakage surface — Basic Auth embeds the password in every request header as reversible base64, whereas a Bearer token is opaque and easier to rotate/revoke without changing the underlying secret.
Related issues that reflect current auth pain points:
opencode attachhardcodes username "opencode", ignoringOPENCODE_SERVER_USERNAMECurrent Implementation
From
packages/opencode/src/server/middleware.ts:The
AuthorizationEffect layer (server.ts#L69-L83) only handles thebasicscheme — nobearerbranch exists.Proposal
Add a new env var
OPENCODE_SERVER_TOKENthat enables Bearer token auth:Expected behavior
Authorization: Bearer <OPENCODE_SERVER_TOKEN>Authorization: Basic base64(user:OPENCODE_SERVER_PASSWORD)?auth_token=<OPENCODE_SERVER_TOKEN>Suggested middleware change
Changes needed
OPENCODE_SERVER_TOKENtoflag.tsAuthMiddlewareinmiddleware.tsto handle Bearer schemebearerbranch to the Effect-basedAuthorizationlayer inserver.tsopencode attach --passwordto also accept--tokenflagwebcommand warning to mention both env varsAlternatives Considered
OPENCODE_SERVER_PASSWORDas Bearer token — Conflates two auth schemes; clients can't know which header format to use.