feat(server): add --public-url for pairing behind an external proxy - #7
Conversation
- Servers can advertise an HTTP(S) origin (--public-url / T3CODE_PUBLIC_URL) while staying bound to loopback - Startup links, QR codes, `npx t3 pair`, and Settings → Connections prefer the public URL - Auth policy treats a public URL as remote-reachable
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7b31aeaece
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
A server bound to loopback and advertised through --public-url is remote-reachable for auth policy, but cookie naming still looked only at the bind address, so it kept the loopback form containing the internal port. Web mode resolves its port with findAvailablePort, so a restart can land on a different internal port behind an unchanged proxy origin and every paired browser silently appears logged out. Resolve reachability once in isRemoteReachableServer and use it for both the auth descriptor and the session cookie names, so proxied servers get the stable environment-identity name.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9613345112
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
A server behind an external proxy keeps local network exposure off, so renderNetworkAccessRow branched to "Limited to this machine." and dropped NetworkAccessDescription even though visibleDesktopNetworkAdvertisedEndpoints now carries the advertised Public URL endpoint. That contradicted the Authorized clients section, which already treats the same server as remotely reachable, and left no way to expand the endpoint list. Render the description whenever the default advertised endpoint is non-loopback. Tailscale-only and loopback public URLs keep the local-only wording.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a029d9ae53
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
The desktop branch returned the port-scoped cookie name before reachability was considered, so a desktop backend advertised through --public-url kept a name tied to whichever port DesktopApp's upward scan from 3773 happened to pick. A restart onto a different port behind an unchanged proxy origin signed remote browsers out. Proxied desktop backends now use the environment-identity name, which separates local instances just as well as the port did. Ordinary desktop access is untouched, including network-exposed backends, whose cookie names predate --public-url and must not change.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: efe2b8e834
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
withPublicUrlEndpoint labeled every non-loopback --public-url "public", so a proxy on a private address such as http://192.168.1.42:8080 got the "Reachable from anywhere" share hint while only being reachable on the LAN. Classify private IPv4 ranges, IPv6 unique-local and link-local addresses, and mDNS .local names as "lan". They stay QR-shareable, since isQrShareableEndpoint only excludes loopback endpoints.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 616c3a1f4d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
A --public-url on a Tailscale address fell through to "public", so the share hint claimed "Reachable from anywhere" for an endpoint that only resolves inside the tailnet. Classify 100.64.0.0/10 as private-network, matching how tailscaleEndpointProvider already labels the same addresses.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3530914388
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e192909eca
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ac2dbd6e91
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
What Changed
Adds a
--public-urlflag (andT3CODE_PUBLIC_URLenv var) to the server that advertises an external HTTP(S) origin for pairing without changing the bind address. When set, the server:npx t3 paircalls, andserverGetConfig's advertised configThe value is validated as an HTTP(S) origin — no path, query, fragment, or credentials. Documented in
docs/user/remote-access.mdunder a new "External reverse proxy" section.Why
Running T3 Code behind an external reverse proxy previously had no first-class story: pairing links were built from the bind address, so a loopback-bound server behind a proxy produced unreachable pairing URLs and a misleading "only this machine can connect" auth policy. An explicit advertised origin keeps the security posture of a loopback listener while emitting correct, reachable pairing addresses. Scope stays tight: no tunnel is started, and
--tailscalepairing is unchanged.Development servers keep their port-scoped cookie names. A dev server stays reachable at
localhost:<port>, and parallel worktree servers on one machine would otherwise clobber each other's session cookie on that shared hostname.UI Changes
Settings → Connections lists a "Public URL" endpoint, marked as the default and QR-shareable when reachable. The Network access row names that endpoint rather than reading "Limited to this machine.", which is the state a proxied server sits in while local network exposure stays off. The disabled-toggle description now suggests
--public-urlfor proxy setups.Before/after captures use the actual Settings components in Chromium against an isolated development server configured with
T3CODE_PUBLIC_URL=https://t3.example.com. Desktop bridge fixtures supply the local-network exposure state and local/LAN endpoint inventory. The before captures useConnectionsSettings.tsxfrom merge-base665746a; after captures useac2dbd6. Both use the same current backend, authenticated session, viewport, and fixture data. These demonstrate the rendered desktop Settings behavior, not a live external proxy or native Electron/WSL integration test.With local network access off, the before state says “Limited to this machine.” The after state names the public URL and shows authorized clients.
With local network access on and the endpoint list expanded, the after state adds Public URL and selects it as the default.
Checklist
Original implementation worked on by GLM (glm-5.3-flash) via opencode. Review follow-ups worked on by claude-opus-5 via Codex in T3 Code.
Review fixes and screenshot verification completed with GPT-6 via Codex.