Skip to content

feat(cli): pair your phone with a QR code from a running server - #4955

Open
t3dotgg wants to merge 3 commits into
mainfrom
t3code/add-t3-pair-qr-command
Open

feat(cli): pair your phone with a QR code from a running server#4955
t3dotgg wants to merge 3 commits into
mainfrom
t3code/add-t3-pair-qr-command

Conversation

@t3dotgg

@t3dotgg t3dotgg commented Jul 30, 2026

Copy link
Copy Markdown
Member

Getting a phone connected to an already-running server was clunky: you either restarted it under t3 serve to get the startup QR code, or went through T3 Connect. Now it's one command:

npx t3 pair              # QR code for the running server
npx t3 pair --tailscale  # publish over Tailscale Serve HTTPS and pair through the tailnet

t3 pair finds the running server from its persisted runtime state (worktree .t3 first 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 at t3 serve / t3 connect.

Details:

  • The runtime state file now records the dev web URL, so dev servers pair through their single-origin web port instead of the backend origin.
  • --tailscale probes 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.
  • Loopback-only pairing URLs get an explicit "only reachable from this machine" note instead of failing silently on the phone.
  • --ttl, --label, --base-dir, and --tailscale-serve-port for 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 real t3 serve instance 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 .t3 first, then shared home), verifies the recorded PID is alive, and probes /.well-known/t3/environment on 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 devUrl when present (single-origin dev), otherwise the bound host/port. Loopback-only URLs get an explicit reachability note. --tailscale configures 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 devUrl when 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 pair CLI command to mint a pairing token and display a QR code for a running server

  • Adds a new pair subcommand to the t3 CLI 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.
  • Supports optional Tailscale pairing via --tailscale, which configures Tailscale Serve HTTPS and resolves the tailnet URL; includes safe checks to avoid clobbering unrelated Serve mappings.
  • Adds --ttl, --label, and --tailscale-serve-port flags to control token lifetime, label, and Serve port.
  • Extends PersistedServerRuntimeState in serverRuntimeState.ts with an optional devUrl field so the pair command can prefer the dev web URL when present.
  • Adds user documentation in remote-access.md covering npx t3 pair and Tailscale pairing workflows.

Macroscope summarized 36b1016.

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>
@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: a220ed00-9ef1-420c-873c-5a2317dd18e1

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:L 100-499 changed lines (additions + deletions). labels Jul 30, 2026

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Comment thread apps/server/src/cli/pair.ts Outdated
Comment thread apps/server/src/cli/pair.ts
Comment thread apps/server/src/cli/pair.ts
Comment thread apps/server/src/cli/pair.ts Outdated
Comment thread apps/server/src/cli/pair.ts Outdated
Comment thread apps/server/src/cli/pair.ts
@macroscopeapp

macroscopeapp Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

This PR introduces a new t3 pair CLI command with authentication token minting and Tailscale Serve integration (~800 lines of new code). New features of this scope warrant human review. Additionally, there's an unresolved comment about gateway probe logic potentially replacing other services' Tailscale mappings during outages.

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>
@github-actions github-actions Bot added size:XL 500-999 changed lines (additions + deletions). and removed size:L 100-499 changed lines (additions + deletions). labels Jul 30, 2026
Comment thread apps/server/src/cli/pair.ts
Comment thread apps/server/src/cli/pair.ts
…ir its own stale tailscale mapping

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ 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.

Comment thread apps/server/src/cli/pair.ts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XL 500-999 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant