You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The Desktop app's embedded server (sidecar) is only reachable by the app's own renderer. Companion tools running on the same machine (status-bar widgets, CLI wrappers, IDE extensions, automation scripts) cannot discover or authenticate to it, because the server URL/credentials are generated in memory and never persisted.
Currently the only working "API" for external tools is scraping main.log for the URL:
[info] server ready { url: 'http://127.0.0.1:62057' }
…but the log never contains the password, so discovery always ends in HTTP 401.
Current behavior (v1.18.26, macOS)
out/main/index.js: const password = randomUUID(); — a fresh password per launch, passed to the sidecar via postMessage, never written to disk.
out/main/sidecar.js → prepareSidecarEnv() unconditionally overwrites the environment:
Object.assign(process.env,{OPENCODE_SERVER_USERNAME: "opencode",OPENCODE_SERVER_PASSWORD: password,// always the IPC-provided value
...
})
so externally fixing the password (e.g. launchctl setenv OPENCODE_SERVER_PASSWORD ...) has no effect.
The password is only visible inside processes the server itself spawns (session tool processes). Tools outside that process tree cannot read it: ps -E shows the kernel-initial env, and the runtime-injected value is not there.
Result: every desktop restart rotates port + password, and each external tool has to implement a hack — e.g. running a "bridge" script inside an opencode session that dumps its own env to a well-known file — which only works after a session has started, and silently breaks again on the next restart.
Proposal
After server ready, persist the endpoint once, atomically, with 0600 perms, e.g.:
Rewrite it on every (re)start; remove or invalidate it on clean shutdown.
Loopback-only listener + same-user-readable file puts it in the same trust domain as the existing env-var mechanism used by the CLI (OPENCODE_SERVER_PASSWORD), so no new attack surface.
(Alternative, also fine: let the desktop honor an externally provided OPENCODE_SERVER_PASSWORD instead of always generating one. Then tools can pin credentials via launchctl setenv and discover the port from main.log/lsof as they already do.)
Precedent inside the same codebase
The v2 CLI background-service path already treats credentials as externally retrievable:
Scraping ps -E for OPENCODE_SERVER_PASSWORD — the value is injected at runtime, so it only appears in short-lived session tool processes; not a stable discovery channel.
Persisting the endpoint (or honoring an external password) would let the growing ecosystem of companion tools connect zero-touch, instead of every tool shipping its own rebridge hack.
Environment
OpenCode Desktop 1.18.26, macOS 15 (arm64)
Reproduction: start desktop → curl -u opencode:anything http://127.0.0.1:<sidecar-port>/global/health → 401, with no supported way to learn the real password from outside the app.
Description
The Desktop app's embedded server (sidecar) is only reachable by the app's own renderer. Companion tools running on the same machine (status-bar widgets, CLI wrappers, IDE extensions, automation scripts) cannot discover or authenticate to it, because the server URL/credentials are generated in memory and never persisted.
Currently the only working "API" for external tools is scraping
main.logfor the URL:…but the log never contains the password, so discovery always ends in HTTP 401.
Current behavior (v1.18.26, macOS)
out/main/index.js:const password = randomUUID();— a fresh password per launch, passed to the sidecar viapostMessage, never written to disk.out/main/sidecar.js→prepareSidecarEnv()unconditionally overwrites the environment:so externally fixing the password (e.g.
launchctl setenv OPENCODE_SERVER_PASSWORD ...) has no effect.The password is only visible inside processes the server itself spawns (session tool processes). Tools outside that process tree cannot read it:
ps -Eshows the kernel-initial env, and the runtime-injected value is not there.Result: every desktop restart rotates port + password, and each external tool has to implement a hack — e.g. running a "bridge" script inside an opencode session that dumps its own env to a well-known file — which only works after a session has started, and silently breaks again on the next restart.
Proposal
After
server ready, persist the endpoint once, atomically, with0600perms, e.g.:OPENCODE_SERVER_PASSWORD), so no new attack surface.(Alternative, also fine: let the desktop honor an externally provided
OPENCODE_SERVER_PASSWORDinstead of always generating one. Then tools can pin credentials vialaunchctl setenvand discover the port frommain.log/lsofas they already do.)Precedent inside the same codebase
The v2 CLI background-service path already treats credentials as externally retrievable:
The desktop sidecar is currently the only server mode whose credentials are unreachable from outside the process tree.
Alternatives considered (and why they don't work today)
ps -EforOPENCODE_SERVER_PASSWORD— the value is injected at runtime, so it only appears in short-lived session tool processes; not a stable discovery channel.Persisting the endpoint (or honoring an external password) would let the growing ecosystem of companion tools connect zero-touch, instead of every tool shipping its own rebridge hack.
Environment
curl -u opencode:anything http://127.0.0.1:<sidecar-port>/global/health→ 401, with no supported way to learn the real password from outside the app.