Skip to content

fix: treat same-computer companion reconnect as a hand-off, not a takeover#42

Merged
Anton-Horn merged 1 commit into
mainfrom
fix/companion-same-device-handoff
Jun 4, 2026
Merged

fix: treat same-computer companion reconnect as a hand-off, not a takeover#42
Anton-Horn merged 1 commit into
mainfrom
fix/companion-same-device-handoff

Conversation

@Anton-Horn
Copy link
Copy Markdown
Contributor

Problem

The companion registry identified connections solely by userId, with last-writer-wins. A forgotten or backgrounded zero browser connect reconnecting from the same machine would evict the active session and print:

✗ another computer connected to this account and took over the browser link.

…even though it was the same laptop. Worse, two such clients could ping-pong, each evicting the other. The server had no way to tell "this computer reconnecting" from "a different computer taking over."

Change

  • Each runner now mints a stable per-machine id at ~/.zero/device-id (kept separate from auth config so it survives logout) and sends it as the x-zero-companion-device header.
  • When a new connection displaces one with the same deviceId, the server closes the old one as "superseded" and the runner exits quietly (↪ a newer zero browser connect on this computer took over…) without reconnecting.
  • A genuinely different machine still gets the existing "replaced" takeover warning.
  • Older clients omit the header → always treated as "other" → no behavior change.
  • Also removed the redundant helper text from the device-approval screen; the UI speaks for itself.

Files

  • server/lib/companion/registry.tsregister() takes deviceId, branches same-device vs cross-device on eviction
  • server/lib/http/ws.ts — reads x-zero-companion-device
  • zero/src/sdk/config.tsgetOrCreateDeviceId()
  • zero/src/companion/runner.ts — sends header, handles superseded close reason
  • web/src/pages/DevicePage.tsx — drop explanatory text

Not included (follow-ups)

  • Confirmation/liveness-gated takeover for genuine cross-device connects
  • Token dedup on login (multiple valid 30-day tokens all map to one user)

Testing

tsc --noEmit clean for all touched files; bun build.ts rebuilds the CLI bundle. Not yet exercised against a live two-runner hand-off — recommend a manual end-to-end check of the close-reason flow before merge.

…eover

The companion registry identified connections solely by userId, so a
forgotten 'zero browser connect' reconnecting from the same machine would
evict the active session with the alarming 'another computer took over'
message — and the two could ping-pong.

Give each runner a stable ~/.zero/device-id, sent as x-zero-companion-device.
When a new connection carries the same deviceId as the one it displaces, the
server closes the old one as 'superseded' and the runner exits quietly without
reconnecting. A genuinely different machine still gets the 'replaced' takeover
warning. Older clients omit the header and are unaffected.

Also drop the redundant helper text from the device-approval screen; the UI
speaks for itself.
@Anton-Horn Anton-Horn merged commit ab6fd36 into main Jun 4, 2026
2 checks passed
@Anton-Horn Anton-Horn deleted the fix/companion-same-device-handoff branch June 4, 2026 12:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant