Does a local server boot up when using the CLI?

HEAD
950df3dci: temporarily disable assigning of issues to rekram1-node (#14486)
This post might have stale content, as HEAD is 569 commits ahead.

does a local server boot up when using the cli?

Avatar of anthropic/claude-opus-4.6
Claude Opus 4.6
commented

Now I have a complete picture. Here's the answer:


Yes — but it depends on which CLI command you use and how you configure it. There are three distinct modes:

##1. TUI (default $0 command) — Conditionally starts a server

When you run opencode (the default TUI command), defined in src/cli/cmd/tui/thread.ts, it spawns a Worker thread (src/cli/cmd/tui/worker.ts) and then decides:

  • If you explicitly pass --port, --hostname, or --mdns (or the config resolves to non-default values), a real HTTP server is started via Server.listen() inside the worker. The TUI then communicates with it over HTTP. (thread.ts lines 144–159)

  • Otherwise (the default), no HTTP server is booted. Instead, the TUI communicates with the worker via direct RPC over Bun's Worker messaging. It creates a custom fetch function (createWorkerFetch) that serializes requests into RPC calls to the worker, and the worker calls Server.App().fetch(request) directly — invoking the Hono app's request handler in-process without binding to any port. (thread.ts lines 160–166, worker.ts fetch handler)

##2. opencode run (headless/non-interactive) — No server

The run command (src/cli/cmd/run.ts) does not start a server at all (unless you use --attach). It calls Server.App().fetch(request) directly in-process via a custom fetch function:

// run.ts lines 616–622
await bootstrap(process.cwd(), async () => {
  const fetchFn = (async (input, init) => {
    const request = new Request(input, init)
    return Server.App().fetch(request)
  }) as typeof globalThis.fetch
  const sdk = createOpencodeClient({ baseUrl: "http://opencode.internal", fetch: fetchFn })
  await execute(sdk)
})

The "http://opencode.internal" URL is a dummy — no actual network listener is created.

##3. opencode serveAlways starts a server

The explicit serve command (src/cli/cmd/serve.ts) always boots a real HTTP server via Server.listen(), binding to a port (defaulting to 4096 if available, otherwise a random port). This is the headless server mode designed for external clients to connect to.

##How Server.listen() works

Defined in src/server/server.ts lines 576–623, it calls Bun.serve() to start an HTTP server with the Hono app. If port is 0, it tries 4096 first, then falls back to a random port. It also optionally publishes via mDNS for network discovery.

###Summary

CommandHTTP Server?Communication
opencode (TUI, default)No (unless --port/--hostname/--mdns)Direct RPC to Worker
opencode with --portYesHTTP to Worker's server
opencode runNoIn-process App().fetch()
opencode run --attach <url>No (connects to existing)HTTP to remote server
opencode serveYes, alwaysHTTP listener for external clients
opencode attach <url>No (connects to existing)HTTP to remote server

END OF POST

Does a local server boot up when using the CLI? — anomalyco/opencode