Add headless Linux CLI and dashboard#10
Conversation
| if (started) { | ||
| return { | ||
| address: gateway.getAddress(), | ||
| dashboard: gateway.getAddress()?.replace(/\/v1$/, "/dashboard/"), | ||
| }; | ||
| } |
There was a problem hiding this comment.
🟡 Restarting the already-running local service reports its address in the wrong format
When the headless service is asked to start again after it is already running (start() at src/lib/headless-runtime.js:162-167), it returns an object with a different structure than the first start, so any code that reads the gateway address (for example the endpoint field) from the second call gets nothing back.
Impact: A caller relying on the returned address after a repeat start would print or use an undefined endpoint/port instead of the real one.
Return-shape mismatch between first and repeat start
The normal (first) start returns { ...address, endpoint, dashboard } where address is { port, host }, so callers such as src/cli/index.js:135-137 read address.endpoint, address.dashboard, and address.port. The early-return branch when started is already true instead returns { address: gateway.getAddress(), dashboard: ... } — a string under address and no endpoint/port/host keys. Currently every caller (src/cli/index.js:134, scripts/capture-dashboard.js:63, tests) invokes start() exactly once, so this path is not exercised today, but the two shapes are not interchangeable and would break a future repeat caller.
| if (started) { | |
| return { | |
| address: gateway.getAddress(), | |
| dashboard: gateway.getAddress()?.replace(/\/v1$/, "/dashboard/"), | |
| }; | |
| } | |
| if (started) { | |
| const port = gateway.getListeningAddress?.()?.port; | |
| return { | |
| port, | |
| endpoint: gateway.getAddress(), | |
| dashboard: port ? `http://127.0.0.1:${port}/dashboard/` : null, | |
| }; | |
| } |
Was this helpful? React with 👍 or 👎 to provide feedback.
| if (!password || String(password).length < 4) { | ||
| return { ok: false, error: "Password must be at least 4 characters" }; |
There was a problem hiding this comment.
🟨 Weak 4-character minimum for the admin password that now guards the network-reachable dashboard
The shared control plane accepts an admin password as short as 4 characters (src/lib/control-plane.js:200-201, and likewise src/lib/control-plane.js:228). This same password is now the sole credential protecting the new /dashboard/ control plane, which can be exposed beyond loopback when the gateway is bound to 0.0.0.0 (LAN/Tailscale). Combined with only 6 login attempts per minute of throttling in src/lib/dashboard.js:13, a 4-character password provides weak protection for a network-reachable admin surface that exposes providers, OAuth credentials, gateway keys, routes, and activity.
Was this helpful? React with 👍 or 👎 to provide feedback.
Outcome
Adds a Linux/headless distribution path around the existing ReRouted router: interactive terminal onboarding, the shared app control plane at /dashboard/, XDG persistence, process locking, and npm-compatible GitHub Release tarballs while preserving the native macOS shell.
Verification