wikis / T3 Code / wiki / concepts / remote-access.md view as markdown report a mistake
Definition
Remote access is the ability to connect to a T3 Code server from a different device — a phone, tablet, or separate desktop — rather than only from the machine running the server. T3 Code has a single runtime boundary: a client talks to a T3 server over HTTP and WebSocket, and the server alone owns orchestration, providers, terminals, git, and filesystem operations. Remoteness is expressed entirely at the connection layer; the runtime itself is never split.
How It Works
The model
Client (desktop / mobile / web)
known environments, connection supervisor
| resolves one access endpoint
Access method
direct ws/wss, relay tunnel, Tailscale serve, desktop-managed ssh
| connects to one T3 server
Execution environment = one T3 server
identity, providers, projects/threads, terminals, git, filesystem
One running T3 server instance (an ExecutionEnvironment) owns provider availability/auth, model
availability, projects/threads, terminal processes, filesystem access, git operations, and server
settings. It is identified by a stable environmentId persisted at
<stateDir>/environment-id and generated on first start.
Ways to enable network access
- Desktop app — Settings → Connections → toggle Network access on (restarts the app,
binds the backend to all network interfaces). The settings panel surfaces a default reachable
endpoint plus alternatives (loopback, LAN, private-network, HTTPS); Create Link generates a
shareable pairing link. If Tailscale is detected, Tailnet endpoints (a
100.x.y.zIP, MagicDNS name, or an HTTPS MagicDNS endpoint via Tailscale Serve) are added to the list; the HTTPS MagicDNS endpoint must be opted into via Enable Tailscale HTTPS. - Headless server (CLI) —
npx t3 serve --host "$(tailscale ip -4)"starts the server without a GUI and prints a connection string, a pairing token, a pairing URL, and a QR code. Add--tailscale-serveto advertise an HTTPS Tailnet endpoint (default port 443, or--tailscale-serve-portfor another port). - Desktop-managed SSH launch — Settings → Connections → Remote Environments → Add
environment → SSH launch flow. The desktop app probes the host, starts or reuses a remote T3
server, opens a local port forward, and saves the environment. The remote host still owns the
actual server, projects, files, git state, terminals, and provider sessions; the renderer just
connects to a forwarded local endpoint. The remote host needs a Node.js version satisfying
^22.16 || ^23.11 || >=24.10reachable from a non-interactive shell (T3 Code tries Volta, asdf, mise, fnm, nodenv, nvm ifnodeisn't directly onPATH).
Quick pairing
For an already-running server: npx t3 pair mints a fresh one-time pairing token and prints it as
a scannable QR code, without restarting anything. If the server is loopback-only, use
npx t3 pair --tailscale to publish it via Tailscale Serve HTTPS first. Flags include
--tailscale-serve-port, --ttl (token lifetime), and --base-dir.
Connection targets and endpoints
Four target tags form the real access taxonomy (connection/model.ts): PrimaryConnectionTarget
(platform-managed local server — desktop backend or CLI-served web app), BearerConnectionTarget
(any manually paired direct HTTP/WebSocket endpoint), RelayConnectionTarget (managed T3 Connect
relay tunnels — see t3 connect), and SshConnectionTarget (desktop-managed SSH
environments). Tailscale is not a separate target kind — a Tailscale URL is paired through the
ordinary bearer path; Tailscale is an endpoint provider/transport, not a distinct runtime concept.
An AdvertisedEndpoint is a server- or desktop-authored candidate endpoint (HTTP+WS URL pair,
default/available marker, reachability hint, HTTPS-app compatibility hint); clients treat these as
hints, not proof a route works — the connection attempt itself decides.
How pairing works
The remote device needs no long-lived secret up front: t3 serve issues a one-time owner pairing
token, the remote device exchanges it with the server, and the server creates an authenticated
session for that device. After pairing, access is session-based.
Hosted web app pairing
https://app.t3.codes can save a remote backend from a URL like
https://app.t3.codes/pair?host=https://backend.example.com:3773#token=PAIRCODE. This only works
when the backend is reachable from the browser over HTTPS/WSS — browsers block an HTTPS page from
connecting to an insecure http:///ws:// LAN backend. The hosted app does not proxy traffic; the
browser connects directly to the backend URL in the link. Hosted pairing is a client-side
convenience only: the hosted app never gets a server-side control plane or copy of session state,
and keeps the token in the URL hash so it is never sent to the hosted app's own server.
Managing access afterward
t3 auth manages access after initial pairing: issuing additional pairing credentials, inspecting
active sessions, and revoking old pairing links or sessions.
For the underlying relay/pairing mechanism behind RelayConnectionTarget, see
t3 connect.
Key Parameters
- Recommended setup: a trusted private network / tailnet, for a stable address, transport security, and less exposure than the open internet.
- Three network-access enablement paths: desktop app toggle, headless
t3 serve, or desktop-managed SSH launch. - Four connection target kinds: Primary, Bearer, Relay, SSH.
- WebSocket auth: a dedicated short-lived ticket (not a query-string token), obtained via
POST /api/auth/websocket-ticketand appended aswsTicket; tickets default to a five-minute TTL (DEFAULT_WEBSOCKET_TOKEN_TTL). t3 pairflags:--tailscale,--tailscale-serve-port,--ttl,--base-dir.
When To Use
Use remote access whenever you want to direct an agent from a device other than the one running
the T3 Code server — controlling a desktop-hosted environment from the mobile app, from
app.t3.codes, or reaching a headless server on another machine over SSH or a tailnet.
Risks & Pitfalls
- A loopback-bound server's pairing URL is not reachable from another device; pair over Tailscale or enable network access first.
- Hosted pairing links (
app.t3.codes) do not work with plain HTTP LAN URLs due to browser mixed-content rules — use the direct pairing URL from the desktop app or CLI instead. - Pairing URLs and tokens should be treated like passwords: anyone holding a valid credential can
create a session until it expires or is revoked (via
t3 auth). - SSH launch can fail with
node: command not foundor a version-range error if a version manager only initializes from an interactive shell profile — it must be configured to work withsh -lc "command -v node && node --version"non-interactively. - Binding
--hostbroadly (rather than to a trusted private address) increases exposure unnecessarily.
Related Concepts
Sources
- raw/github_doc-docs-user-remote-access-md.md
- raw/github_doc-docs-internals-remote-md.md
