feat(cli): pair your phone with a QR code from a running server - #4955
feat(cli): pair your phone with a QR code from a running server#4955t3dotgg wants to merge 3 commits into
Conversation
Pairing a phone previously required restarting with t3 serve or going through T3 Connect. t3 pair discovers the already-running server via its persisted runtime state, verifies it answers, mints a one-time pairing token in the server's own store, and prints the pairing URL as a QR code. --tailscale publishes the server over Tailscale Serve HTTPS and pairs through the tailnet URL instead. The runtime state now records the dev web URL so dev servers pair through their single-origin web port. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
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. Comment |
There was a problem hiding this comment.
One finding on the new t3 pair CLI module: PairTailscaleError models four semantically distinct failures behind a single reason discriminator whose only consumer is a switch in the message getter. Everything else in the diff (namespace imports, Effect.fn helpers, layer construction at the CLI boundary, Schema.TaggedErrorClass usage, devUrl persistence) follows the service conventions.
Posted via Macroscope — Effect Service Conventions
ApprovabilityVerdict: Needs human review This PR introduces a new You can customize Macroscope's approvability policy. Learn more. |
- Split PairTailscaleError into per-failure classes per Effect error conventions (same shape as scripts/lib/dev-share.ts). - Distinguish unreachable vs occupied-by-non-T3 HTTPS ports before configuring Tailscale Serve; never overwrite a foreign mapping. - Preserve the dev URL's host when proxying, and reject HTTPS dev URLs that tailscale serve cannot target. - Verify the recorded pid is alive before trusting a runtime state file, so a reused port cannot mint tokens into a dead server's database. - Repoint an existing same-environment mapping at the web port for dev servers instead of blindly reusing it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ir its own stale tailscale mapping Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 36b1016. Configure here.

Getting a phone connected to an already-running server was clunky: you either restarted it under
t3 serveto get the startup QR code, or went through T3 Connect. Now it's one command:t3 pairfinds the running server from its persisted runtime state (worktree.t3first when run inside one, matching dev-runner precedence, then~/.t3), confirms it answers by fetching/.well-known/t3/environment, mints a one-time pairing token directly in that server's store, and prints the pairing URL as a terminal QR code. If nothing is running it says so and points att3 serve/t3 connect.Details:
--tailscaleprobes the HTTPS endpoint first and only configures Tailscale Serve when the port is unused; it refuses to steal a mapping that fronts a different T3 Code server (compared by environment id) or any non-T3 service.--ttl,--label,--base-dir, and--tailscale-serve-portfor the usual overrides.Verified with focused vitest suites (
pair.test.ts,serverRuntimeState.test.ts, plus the adjacent bin/startup suites) and an end-to-end smoke test against a realt3 serveinstance in a throwaway base dir.Built by Claude Fable 5 running in Claude Code.
🤖 Generated with Claude Code
Note
Medium Risk
Touches auth pairing token creation and Tailscale Serve configuration with careful guards, but discovery/minting mistakes could pair the wrong server or clobber serve mappings if probes fail incorrectly.
Overview
Adds
t3 pair, a CLI subcommand that mints a one-time pairing token for an already-running server and prints a terminal QR code—without restarting or using startup-only flows.Discovery walks persisted
server-runtime.json(worktree.t3first, then shared home), verifies the recorded PID is alive, and probes/.well-known/t3/environmenton the recorded origin so stale or hijacked ports are ignored. Tokens are minted into the same datastore the live server uses via a config built from where state was found.Pairing URLs prefer the recorded
devUrlwhen present (single-origin dev), otherwise the bound host/port. Loopback-only URLs get an explicit reachability note.--tailscaleconfigures or reuses Tailscale Serve HTTPS, refuses to overwrite mappings for other environments or non-T3 services, and targets the dev web port or backend as appropriate.Runtime state now optionally records
devUrlwhen the server fronts a dev web server. User docs cover quick pairing and--tailscale.Reviewed by Cursor Bugbot for commit 36b1016. Bugbot is set up for automated code reviews on this repo. Configure here.
Note
Add
t3 pairCLI command to mint a pairing token and display a QR code for a running serverpairsubcommand to thet3CLI that discovers a running T3 server by scanning persisted runtime state, verifies the process is alive, probes the server, mints a one-time pairing token, and prints a QR code with the pairing URL.--tailscale, which configures Tailscale Serve HTTPS and resolves the tailnet URL; includes safe checks to avoid clobbering unrelated Serve mappings.--ttl,--label, and--tailscale-serve-portflags to control token lifetime, label, and Serve port.PersistedServerRuntimeStatein serverRuntimeState.ts with an optionaldevUrlfield so the pair command can prefer the dev web URL when present.npx t3 pairand Tailscale pairing workflows.Macroscope summarized 36b1016.