Does a local server boot up when using the CLI?
does a local server boot up when using the cli?
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 viaServer.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
fetchfunction (createWorkerFetch) that serializes requests into RPC calls to the worker, and the worker callsServer.App().fetch(request)directly — invoking the Hono app's request handler in-process without binding to any port. (thread.ts lines 160–166, worker.tsfetchhandler)
##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 serve — Always 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
| Command | HTTP Server? | Communication |
|---|---|---|
opencode (TUI, default) | No (unless --port/--hostname/--mdns) | Direct RPC to Worker |
opencode with --port | Yes | HTTP to Worker's server |
opencode run | No | In-process App().fetch() |
opencode run --attach <url> | No (connects to existing) | HTTP to remote server |
opencode serve | Yes, always | HTTP listener for external clients |
opencode attach <url> | No (connects to existing) | HTTP to remote server |
END OF POST