Repository navigation
feat(desktop): dynamic free-port selection (v0.5.0) - #259
Merged
Merged
Conversation
…'t block boot The desktop app bound fixed ports (control plane 8787, frontend 8788, d1-shim 9001) and failed to start if any was occupied — e.g. a stray `wrangler dev` or another app on 8787. Now the app picks the first free port at/after each default before binding (explicit CONTROLPLANE_PORT / FRONTEND_PORT / D1_SHIM_ADDR overrides are still honored verbatim). Wiring for a dynamically-chosen control-plane port: - workerd control plane: `--external-addr d1-shim=<addr>` so it reaches a dynamic shim port (the capnp hardcodes 127.0.0.1:9001). - Sandbox VM reverse bridge: the guest side stays baked at 8787, but the HOST target of `--reverse-port-forward` now follows the real control-plane port (new VMConfig.controlplane_host_port) so guest→host callbacks still land. - Frontend bakes NEXT_PUBLIC_API_URL=:8787 at build time and can't learn a runtime port, so the trusted loading screen hands it over via ?cp=<port> on the redirect (mirrors the surface-token handoff). config/env.ts reads ?cp=, strictly gated to a loopback baked base, caches it, and strips it from the URL. - New get_ports Tauri command reports the bound ports; the loading screen fetches it to probe the right URLs and build the redirect. Scope: covers the workerd services (control plane + web interface + d1-shim), which is what "failing to start" referred to. The sandbox VM's own host port (8080) stays fixed for now. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014t8Ukp4NPWtqJ55X261wMZ
…hed cp port Two review findings on the dynamic-ports change: P1 — CLI ignored dynamic ports. orcabot.rs hardcoded 8787/8080/8788 for health checks, API calls, WebSockets and status, so after a dynamic-port GUI boot "Switch to CLI" hit the wrong process (or nothing). The backend now records the bound ports in <data>/ports; the CLI reads them fresh on each call (not cached, so `up`'s spawn-then-poll converges once the file appears) and falls back to the defaults when the file is absent (older backend / not started). Also fixes the terminal-WS Origin header, which was a hardcoded :8788 that ALLOWED_ORIGINS would reject under a dynamic frontend port. P2 — a cached non-default control-plane port could survive into a default boot. The loading screen omitted cp= when the port was the default 8787, so after a prior CONTROLPLANE_PORT=8790 run a default relaunch (same :8788 origin) left the frontend reading the stale cached 8790 and never reaching the real CP on 8787. The loading screen now ALWAYS passes the current cp= so env.ts overwrites the cache every boot. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014t8Ukp4NPWtqJ55X261wMZ
Completes the free-port story: if 8080 is busy on the host, the app now picks a
free host port for the sandbox's host→guest forward. The GUEST side stays fixed
at 8080 (new vm::SANDBOX_GUEST_PORT) because config.env isn't delivered to the
guest — it boots from image defaults — so only the host TCP listen can move, same
constraint as the control-plane reverse bridge.
- Host→guest forward is now `{host_port}:8080` (native VZ --port-forward, macOS
QEMU fallback hostfwd, and Linux QEMU hostfwd).
- main.rs picks a free SANDBOX_PORT (avoiding cp/fe/d1) and points SANDBOX_URL at
it so the control plane reaches the sandbox; the port is recorded in the ports
file, so the CLI (/debug/exec, status) and the loading screen follow it.
- PORT env handed to the VM config is the guest bind (8080), not the host listen.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014t8Ukp4NPWtqJ55X261wMZ
…_DATA_HOME Two more review findings on dynamic ports: P1 — a busy default control-plane port still blocked CLI startup. `up` and bare `orcabot` treated any healthy :8787 /health as "stack already running", so a foreign listener (another Orcabot's `wrangler dev`, an unrelated server) made the CLI attach to it and never launch our own backend — the exact conflict dynamic ports is meant to survive. Detection now requires a live `orcabot-desktop` process (pid file / pgrep) AND a healthy control plane (new `stack_running()`). Also hardened the post-spawn readiness poll: it clears any stale ports file before launch and only trusts health once OUR backend has rewritten it, so a foreign listener on the default port isn't mistaken for our control plane mid-boot. P2 — Linux port discovery ignored XDG_DATA_HOME. The CLI hardcoded ~/.local/share/com.orcabot.desktop while the backend writes via Tauri's app_data_dir(), which follows an absolute $XDG_DATA_HOME. On a custom data dir the CLI never found <data>/ports and fell back to defaults. data_dir() now mirrors Tauri's resolution (honors an absolute XDG_DATA_HOME on Linux; macOS unchanged). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014t8Ukp4NPWtqJ55X261wMZ
stack_running() required both a live backend PID and a healthy control plane, so a backend that existed but was still staging binaries / starting workerd counted as "absent" — and `orcabot up` (or bare `orcabot`) would launch a SECOND orcabot-desktop. The two would pick overlapping ports, clobber the shared ports file, and race their service children. Separate the two states: cmd_up now guards on app_pid() (backend existence) first. If a backend process is present it attaches and waits for readiness (extracted wait_for_ready) instead of spawning; only a genuine absence of any backend leads to a launch. run_tui claims ownership (tear-down-on-quit) only when it actually started the backend, not when it merely waited for one already coming up. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014t8Ukp4NPWtqJ55X261wMZ
…hen-spawn race The app_pid() check and the spawn in cmd_up weren't atomic, so two simultaneous first-launches could both pass the check and spawn rival backends. Wrap the critical section (check + spawn + pid-file write) in a best-effort exclusive flock on <data>/up.lock, released before the long readiness poll. A concurrent `up` now blocks briefly, then finds the backend the winner started and attaches to it. Best-effort: if locking is unavailable we proceed as before (no regression). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014t8Ukp4NPWtqJ55X261wMZ
…ore wait Two follow-ups on the CLI launch path: P2 — concurrent bare `orcabot`s both claimed ownership. run_tui computed `started_it` from app_pid() OUTSIDE cmd_up's launch lock, so two simultaneous callers both saw no PID and both set we_own=true; closing either TUI then tore the backend down under the other. cmd_up's core is now ensure_stack_up() -> (exit, spawned), which decides `spawned` INSIDE the lock. run_tui claims ownership from that authoritative flag (the pre-check now only drives the cosmetic message). P3 — the attach path held the launch lock across the up-to-150s readiness wait (`return wait_for_ready(...)` evaluated before the guard dropped), blocking other CLI starts. It now explicitly drops the lock before waiting, matching the spawn path and the "short critical section" contract. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014t8Ukp4NPWtqJ55X261wMZ
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds dynamic port allocation