Skip to content

feat(setup): lead the guide's Connect your phone with the Ovis relay QR - #2692

Merged
devswha merged 5 commits into
mainfrom
feat/first-run-relay-qr
Nov 4, 2026
Merged

devswha merged 5 commits into
mainfrom
feat/first-run-relay-qr

Conversation

@devswha

@devswha devswha commented Nov 3, 2026

Copy link
Copy Markdown
Owner

Summary

The setup guide's Connect your phone box now leads with the Ovis relay QR code. Before this change it showed the page's own address QR, or, on the PC's loopback page (the usual case), sent the user to Settings → Phone & devices.

  • Where the relay can lead (the server offers it, the page is not the hosted shell, the browser is not a watching device or a Portal visitor, and nothing blocks the relay), the box shows:
    • relay off: one line on what the relay does, the device-gate note when turning it on closes the open gate (same rule and text as Connect a phone in Settings), and Turn on the Ovis relay as the primary button. The user's tap is the consent.
    • connecting: the relay's status; a start that failed: its reason with Try again.
    • online: the same pairing QR code Settings shows (RelayPairing).
  • Use Tailscale or Wi-Fi instead, under it, shows the previous behaviour: the address QR, or Settings on loopback. Where the relay cannot lead, the whole box behaves as before.
  • Lockout guard: turning the relay on closes the device gate when no token is set (server/relay/service.ts). A browser that got in only because the gate was open (via: "open", a LAN or tailnet address) pairs itself first with the existing pairThisBrowser(), as the address-QR path already does. If that pairing fails, the relay is not turned on. The PC's own loopback (via: "local") is never gated (server/access.ts).
  • The decision logic is in pure functions in src/lib/setupGuideFlow.ts (phoneWay, relayStep), with tests. The security summary comes from App's existing access state.
  • No server route, HTTP/WS contract or Settings dialog change. Two strings now shared with Settings moved from the devices dictionaries to the base dictionaries, as the i18n test requires.
  • DESIGN.md, docs/guide.md and a changelog fragment updated.

Screenshots

Before/after at 1280 and 390 px, light and dark, were taken on an isolated test session with /api/relay* stubbed in the browser, and reviewed by the maintainer before this PR.

Validation

  • bun run check fast (with TMPDIR on disk) at 0738a85: all steps ok, unit 10896 pass / 0 fail / 42 skip.
  • Branched from 389f44e. Not merged with current main: main at 12a7c63 fails bun run typecheck in server/herdr-core/adapter.ts, and this PR does not touch that file.
  • bun test src/lib/setupGuideFlow.test.ts src/lib/i18n.test.ts src/lib/useSetupGuide.hook.test.ts src/lib/pairing.test.ts src/conventions.test.ts scripts/conventions.test.ts scripts/changelog-fold.test.ts: 113 pass.
  • Not run: a real relay connection (the screenshots stub the relay routes), and the via: "open" self-pair-then-relay flow end to end in a browser. That flow was checked against the server code only.

Where the relay is offered, this browser may control it and nothing blocks
it, the first-run guide's Connect your phone box now offers to turn the relay
on and shows its pairing QR code right there. A browser in only because
nothing is paired yet pairs itself first, since starting the relay closes the
open device gate. "Use Tailscale or Wi-Fi instead" keeps the previous
behaviour, which is also all the box shows where the relay cannot lead.
@coderabbitai

coderabbitai Bot commented Nov 3, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 22 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used all 4 included reviews currently available.

Learn how review limits work.

Review configuration:

Run configuration
Commits

Reviewing files that changed from the base of the PR and between 40dcae1 and 2870108.

Files selected for processing: 19
  • DESIGN.md
  • changelog.d/setup-guide-relay-first.md
  • docs/guide.md
  • src/App.tsx
  • src/components/ConnectPhoneDialog.test.tsx
  • src/components/RelayPanel.tsx
  • src/components/SetupGuide.css
  • src/components/SetupGuide.tsx
  • src/lib/i18n.ja.ts
  • src/lib/i18n.ko.ts
  • src/lib/i18n.zh.ts
  • src/lib/i18n/devices.ja.ts
  • src/lib/i18n/devices.ko.ts
  • src/lib/i18n/devices.zh.ts
  • src/lib/i18n/guide.ja.ts
  • src/lib/i18n/guide.ko.ts
  • src/lib/i18n/guide.zh.ts
  • src/lib/setupGuideFlow.test.ts
  • src/lib/setupGuideFlow.ts
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

…er ways reachable

Review follow-up: a relay QR answers another relay QR's code by saying its own expired instead of
minting a new offer (the guide's and Settings' QRs could replace each other's without end); the
guide's box offers Use Tailscale or Wi-Fi instead while it waits for the relay's first answer; and
that link is off while the relay starts, whose gate closing would spend the other way's code.
@devswha

devswha commented Nov 3, 2026

Copy link
Copy Markdown
Owner Author

Review round (Codex adversarial at the pre-push merge efb8e0a7, same change as 0738a85; all findings source-traced, none run in a browser). No HIGH. Fixed in 7506c94:

  • MEDIUM, two relay QRs replacing each other: with the guide's relay QR up and Settings → Connect a phone open, each RelayPairing answered the other's announced code by minting a new offer, without end. A relay QR now answers another relay QR's code by showing its own as expired (New code stays), and still re-mints for a Devices group code as before.
  • MEDIUM, no fallback while the relay's status never arrives (non-404 failures): the box now shows Use Tailscale or Wi-Fi instead while it waits.
  • MEDIUM, switching away mid-start: that link is disabled while the relay starts, since the start closes the gate and spends the other way's pairing code.

LOW, left as is: after a phone pairs through the guide's relay QR, the QR stays up until it expires or New code (Settings' relay QR behaves the same).

Validation at 7506c94: bun run check fast (TMPDIR on disk) all ok, unit 10896 pass / 0 fail / 42 skip.

…re shows

Adds a test that mounts two relay QR panels and expects two offers and one expired code (it times
out on the earlier announcement handling), and shows a failed retry's own error even while the
relay still reports the earlier start failure.
…two relay QRs on one code

Review follow-up: the guide's relay QR watches the device list as its address QR does and gives way
to Paired and New code once a phone used its single-use code; and a relay QR that hears another
relay QR's code keeps showing it when it is the same code, or asks for the offer again without
announcing it (the server's offer reuses its pending code), instead of calling it expired.
@devswha

devswha commented Nov 3, 2026

Copy link
Copy Markdown
Owner Author

omo review at 7506c94 (BLOCK) reproduced two more; fixed in 0ffe92f, with 65c8b95 adding the two-panel test:

  • MEDIUM, used relay QR stays up: the guide's relay QR now watches the device list like its address QR and gives way to "Paired: ." and New code once a new device is listed (the code is single-use).
  • LOW, Settings marks the guide's valid QR expired: an announcement from another relay QR is ignored when it carries the code on screen; otherwise the panel re-asks for the offer without announcing it (the server's offer() reuses its pending code), so it cannot loop. The two-panel test now expects both QR codes up, no "expired", and at most 3 offer requests; it fails on 7506c94 and times out on 0738a85.
  • LOW (65c8b95): a retry's own failure shows even while the relay still reports the earlier start failure.

Left as is (LOW, unreproduced): a relay error that never clears has no reset button in the guide; Use Tailscale or Wi-Fi instead stays available, and Settings → Connect a phone has the reset. The box polls relay status while open, as Settings does.

Validation at 0ffe92f: bun run check fast (TMPDIR on disk) all ok, unit 10897 pass / 0 fail / 42 skip.

@devswha

devswha commented Nov 4, 2026

Copy link
Copy Markdown
Owner Author

Merge note: owner approved the first-run relay QR screens from screenshots on 2026-11-04. CodeRabbit was rate-limited for this head; merged on the omo review (CLEAR at the head), the Codex Terra review (findings fixed with tests) and green required checks.

@devswha
devswha merged commit 53cd5a9 into main Nov 4, 2026
12 checks passed
@devswha
devswha deleted the feat/first-run-relay-qr branch November 4, 2026 01:50
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