opencode/packages/opencode/specs/effect/http-api.md
opencode-agent[bot] 3b74077437 chore: generate
2026-04-25 18:47:06 +00:00

11 KiB

HttpApi migration

Plan for replacing instance Hono route implementations with Effect HttpApi while preserving behavior, OpenAPI, and SDK output during the transition.

End State

  • JSON route contracts and handlers live in src/server/routes/instance/httpapi/*.
  • Route modules own their HttpApiGroup, schemas, handlers, and route-level middleware.
  • httpapi/server.ts only composes groups, instance lookup, observability, and the web handler bridge.
  • Hono route implementations are deleted once their HttpApi replacements are default, tested, and represented in the SDK/OpenAPI pipeline.
  • Streaming, SSE, and websocket routes move later through Effect HTTP primitives or another explicit replacement plan; they do not need to fit HttpApi if HttpApi is the wrong abstraction.

Current State

  • OPENCODE_EXPERIMENTAL_HTTPAPI gates the bridge. Default behavior still uses Hono.
  • The bridge mounts selected paths in server/routes/instance/index.ts before legacy Hono routes.
  • Legacy Hono routes remain for default behavior and for hono-openapi SDK generation.
  • HttpApi auth is independent of Hono auth.
  • Authorization is attached in each route module, not centrally wrapped in server.ts.
  • Auth supports Basic auth and the legacy auth_token query parameter through HttpApiSecurity.apiKey.
  • Instance context is provided by httpapi/server.ts using directory, workspace, and x-opencode-directory.
  • Observability.layer is provided in the Effect route layer and deduplicated through the shared memoMap.

Migration Rules

  • Preserve runtime behavior first. Semantic changes, new error behavior, or route shape changes need separate PRs.
  • Migrate one route group, or one coherent subset of a route group, at a time.
  • Reuse existing services. Do not re-architect service logic during HTTP boundary migration.
  • Effect Schema owns route DTOs. Keep .zod only as compatibility for remaining Hono/OpenAPI surfaces.
  • Regenerate the SDK after schema or OpenAPI-affecting changes and verify the diff is expected.
  • Do not delete a Hono route until the SDK/OpenAPI pipeline no longer depends on its Hono describeRoute entry.

Route Slice Checklist

Use this checklist for each small HttpApi migration PR:

  1. Read the legacy Hono route and copy behavior exactly, including default values, headers, operation IDs, response schemas, and status codes.
  2. Put the new HttpApiGroup, route paths, DTO schemas, and handlers in src/server/routes/instance/httpapi/*.
  3. Mount the new paths in src/server/routes/instance/index.ts only inside the OPENCODE_EXPERIMENTAL_HTTPAPI block.
  4. Use InstanceState.context / InstanceState.directory inside HttpApi handlers instead of Instance.directory, Instance.worktree, or Instance.project ALS globals.
  5. Reuse existing services directly. If a service returns plain objects, use Schema.Struct; use Schema.Class only when handlers return actual class instances.
  6. Keep legacy Hono routes and .zod compatibility in place for SDK/OpenAPI generation.
  7. Add tests that hit the Hono-mounted bridge via InstanceRoutes, not only the raw HttpApi web handler, when the route depends on auth or instance context.
  8. Run bun typecheck from packages/opencode, relevant bun run test:ci ... tests from packages/opencode, and ./packages/sdk/js/script/build.ts from the repo root.

Experimental Read Slice Guidance

For the experimental route group, port read-only JSON routes before mutations:

  • Good first batch: GET /console, GET /console/orgs, GET /tool/ids, GET /resource.
  • Consider GET /worktree only if the handler uses InstanceState.context instead of Instance.project.
  • Defer POST /console/switch, worktree create/remove/reset, and GET /session to separate PRs because they mutate state or have broader pagination/session behavior.
  • Preserve response headers such as pagination cursors if a route is ported.
  • If SDK generation changes, explain whether it is a semantic contract change or a generator-equivalent type normalization.

Schema Notes

  • Use Schema.Struct(...).annotate({ identifier }) for named OpenAPI refs when handlers return plain objects.
  • Use Schema.Class only when the handler returns real class instances or the constructor requirement is intentional.
  • Keep nested anonymous shapes as Schema.Struct unless a named SDK type is useful.
  • Avoid parallel hand-written Zod and Effect definitions for the same route boundary.

Phases

1. Stabilize The Bridge

Before porting more routes, cover the bridge behavior that every route depends on.

  • Add tests that hit the Hono-mounted HttpApi bridge, not just HttpApiBuilder.layer directly.
  • Cover auth disabled, Basic auth success, auth_token success, missing credentials, and bad credentials.
  • Cover directory and x-opencode-directory instance selection.
  • Verify generated SDK output remains unchanged for non-SDK work.
  • Fix or remove any implemented-but-unmounted HttpApi groups.

2. Complete The Inventory

Create a route inventory from the actual Hono registrations and classify each route.

Statuses:

  • bridged: served through the HttpApi bridge when the flag is on.
  • implemented: HttpApi group exists but is not mounted through Hono.
  • next: good JSON candidate for near-term porting.
  • later: portable, but needs schema/service cleanup first.
  • special: SSE, websocket, streaming, or UI bridge behavior that likely needs raw Effect HTTP rather than HttpApi.

3. Finish JSON Route Parity

Port remaining JSON routes in small batches.

Good near-term candidates:

  • top-level reads: GET /path, GET /vcs, GET /vcs/diff, GET /command, GET /agent, GET /skill, GET /lsp, GET /formatter
  • simple mutations: POST /instance/dispose
  • experimental JSON reads: console, tool, worktree list, resource list
  • deferred JSON mutations: PATCH /config, project git init, workspace/worktree create/remove/reset, file search, MCP auth flows

Keep large or stateful groups for later:

  • session
  • sync
  • process-level experimental routes

4. Move OpenAPI And SDK Generation

Hono routes cannot be deleted while hono-openapi is the source of SDK generation.

Required before route deletion:

  • Generate the public OpenAPI surface from Effect HttpApi for ported routes.
  • Keep operation IDs, schemas, status codes, and SDK type names stable unless the change is intentional.
  • Compare generated SDK output against dev for every route group deletion.
  • Remove Hono OpenAPI stubs only after Effect OpenAPI is the SDK source for those paths.

5. Make HttpApi Default For JSON Routes

After JSON parity and SDK generation are covered:

  • Flip the bridge default for ported JSON routes.
  • Keep a short-lived fallback flag for the old Hono implementation.
  • Run the same tests against both the default and fallback path during rollout.
  • Stop adding new Hono handlers for JSON routes once the default flips.

6. Delete Hono Route Implementations

Delete Hono routes group-by-group after each group meets the deletion criteria.

Deletion criteria:

  • HttpApi route is mounted by default.
  • Behavior is covered by bridge-level tests.
  • OpenAPI/SDK generation comes from Effect for that path.
  • SDK diff is zero or explicitly accepted.
  • Legacy Hono route is no longer needed as a fallback.

After deleting a group:

  • Remove its Hono route file or dead endpoints.
  • Remove its .route(...) registration from instance/index.ts.
  • Remove duplicate Zod-only route DTOs if Effect Schema now owns the type.
  • Regenerate SDK and verify output.

7. Replace Special Routes

Special routes need explicit designs before Hono can disappear completely.

  • event: SSE
  • pty: websocket
  • tui: UI/control bridge behavior
  • streaming session endpoints

Use raw Effect HTTP routes where HttpApi does not fit. The goal is deleting Hono implementations, not forcing every transport shape through HttpApi.

Current Route Status

Area Status Notes
question bridged GET /question, reply, reject
permission bridged list and reply
provider bridged list, auth, OAuth authorize/callback
config bridged partial reads only; mutation remains Hono
project bridged partial reads only; git-init remains Hono
file bridged partial find text/file/symbol, list/content/status
mcp bridged partial status only
workspace bridged list, get, enter
top-level instance reads bridged path, vcs, command, agent, skill, lsp, formatter
experimental JSON routes bridged partial console reads, tool ids, resource list; worktree and global session list remain later
session later/special large stateful surface plus streaming
sync later process/control side effects
event special SSE
pty special websocket
tui special UI bridge

Next PRs

  1. Produce a generated route inventory from Hono registrations and update Current Route Status with exact paths.
  2. Start the Effect OpenAPI/SDK generation path for already-bridged routes.

Checklist

  • Add first HttpApi JSON route slices.
  • Bridge selected HttpApi routes into Hono behind OPENCODE_EXPERIMENTAL_HTTPAPI.
  • Reuse existing Effect services in handlers.
  • Provide auth, instance lookup, and observability in the Effect route layer.
  • Attach auth middleware in route modules.
  • Support auth_token as a query security scheme.
  • Add bridge-level auth and instance tests.
  • Complete exact Hono route inventory.
  • Resolve implemented-but-unmounted route groups.
  • Port remaining top-level JSON reads.
  • Generate SDK/OpenAPI from Effect routes.
  • Flip ported JSON routes to default-on with fallback.
  • Delete replaced Hono route implementations.
  • Replace SSE/websocket/streaming Hono routes with non-Hono implementations.