Skip to content

feat(server): wire Server.listen() through native HttpApi listener (kill-switch)#25595

Closed
kitlangton wants to merge 2 commits into
devfrom
kit/server-listen-native
Closed

feat(server): wire Server.listen() through native HttpApi listener (kill-switch)#25595
kitlangton wants to merge 2 commits into
devfrom
kit/server-listen-native

Conversation

@kitlangton
Copy link
Copy Markdown
Contributor

Summary

  • When the effect-httpapi backend is selected, Server.listen() now delegates to HttpApiListener.listen() (native Bun.serve + inline WS upgrades) instead of routing through the Hono runtime adapter (Hono.fetch + createBunWebSocket).
  • The Hono backend path is unchanged.
  • New env var OPENCODE_HTTPAPI_LEGACY_LISTENER forces the effect-httpapi backend back through the Hono adapter as an escape hatch.

Why now

This is step 6a of the Hono deletion arc. Stacks on top of:

Together those got the native listener to functional parity with the Hono path. This PR finally gives it production traffic — on dev / beta / local install channels where OPENCODE_EXPERIMENTAL_HTTPAPI already defaults on. prod / latest channels still go through the Hono path until the rollout is complete.

Kill switch

OPENCODE_HTTPAPI_LEGACY_LISTENER=1

When set, the effect-httpapi backend goes back through adapter.create(app).runtime.listen() (i.e. Hono is wrapping the HttpApi handler again). Has no effect when the Hono backend is already selected. The flag is documented in flag.ts next to OPENCODE_EXPERIMENTAL_HTTPAPI.

What's NOT in this PR

  • Removing any Hono code. The Hono backend, middlewares, and routes are untouched. That's step 6b.
  • Node adapter for the native listener. Bun-only for now; BunHttpServer.layer is the only path that gives us Bun.serve's path-based WS upgrade hook.
  • Touching the --hono debug flag for Server.openapi() (kept for spec diffability).

Behavior diff for the native path

Concern Hono adapter path Native listener path
HTTP routing Hono → HttpApi via webHandler HttpApi webHandler directly
WS upgrade (PTY) createBunWebSocket Bun.serve#fetchserver.upgrade(...)
WS upgrade (workspace proxy) WorkspaceRouterMiddlewareServerProxy.websocket resolveWorkspaceProxy → inline bridge in httpapi-listener.ts
mDNS publish unchanged unchanged
Server.url module export unchanged unchanged
listen({ port: 0 }) fallback Bun adapter handles start(4096) ?? start(0) (matches existing behavior)
Telemetry opencode.server.backend only adds opencode.server.listener=bun-native

Test plan

  • bun run typecheck clean (13/13)
  • bun run test test/server/ — 172 pass / 0 fail / 2 todo (covers the new httpapi-listener.test.ts workspace-proxy + PTY paths from feat(httpapi-listener): workspace-proxy WS forwarding #25594)
  • Manual: run opencode serve on a dev channel and exercise the SDK end-to-end
  • Manual: set OPENCODE_HTTPAPI_LEGACY_LISTENER=1 and confirm the Hono adapter path comes back (look for opencode.server.listener absent from the structured log)

kitlangton added 2 commits May 3, 2026 09:18
Bridge workspace-proxy WebSocket upgrades inside the Bun.serve listener so
the Hono path's WorkspaceRouterMiddleware → ServerProxy.websocket flow has
a native equivalent. The HttpApi handler still owns HTTP; the listener now
also resolves the workspace target inline (?workspace=… or session lookup),
upgrades the client connection, and bridges it to a remote WebSocket with
queueing, subprotocol forwarding, and close-code propagation.

This unblocks flipping Server.listen() over to the new listener but does
not flip it — the Hono path remains canonical. Bun-only; node:http + ws
adapter is a follow-up (TODO inline).
…ill-switch)

When the effect-httpapi backend is selected, Server.listen() now delegates
to HttpApiListener.listen() — a native Bun.serve listener with inline
WebSocket upgrade handling — instead of routing through the Hono runtime
adapter (Hono.fetch + createBunWebSocket).

The Hono backend path is unchanged, and a kill-switch env var
(OPENCODE_HTTPAPI_LEGACY_LISTENER) forces the effect-httpapi backend back
through the Hono adapter as an escape hatch if the native listener
regresses for a user.

This unblocks the Hono deletion arc by giving the native listener real
production traffic on dev/beta/local channels (where
OPENCODE_EXPERIMENTAL_HTTPAPI defaults on) while leaving prod/latest
channels on the Hono path.
@kitlangton
Copy link
Copy Markdown
Contributor Author

Closing — redundant. The effect-httpapi backend already has full WS + proxy support via HttpApiProxy.websocket + workspace-routing middleware. The real fix is to swap adapter.createFetch for Effect's BunHttpServer.layer in Server.listen()'s effect-httpapi path. Replacement PR coming.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant