Skip to content

Commit 9aee240

Browse files
authored
Merge pull request #77 from aaditagrawal/sync/upstream-2026-05-04
sync: merge upstream/main (38 commits, multi-provider SPI)
2 parents a63852e + 2b9845d commit 9aee240

516 files changed

Lines changed: 60112 additions & 27322 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.docs/remote-architecture.md

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ Examples:
9393

9494
A known environment may or may not know the target `environmentId` before first successful connect.
9595

96+
In the hosted web app, known environments are browser-local. A hosted pairing URL can create the saved entry, but it does not give the hosted app a server-side control plane or a copy of the session state.
97+
9698
### AccessEndpoint
9799

98100
An `AccessEndpoint` is one concrete way to reach a known environment.
@@ -108,6 +110,67 @@ A single environment may have many endpoints:
108110

109111
The environment stays the same. Only the access path changes.
110112

113+
### AdvertisedEndpoint
114+
115+
An `AdvertisedEndpoint` is a server or desktop-authored candidate endpoint for an environment. It is how the backend tells the client which URLs may be useful for pairing and reconnecting.
116+
117+
`AdvertisedEndpoint` is deliberately narrower than the full access model:
118+
119+
- it describes a concrete HTTP and WebSocket base URL pair
120+
- it can mark the endpoint as default, available, or unavailable
121+
- it includes reachability hints such as loopback, LAN, private, public, or tunnel
122+
- it includes compatibility hints such as whether the endpoint can be used from the hosted HTTPS app
123+
124+
Clients should treat advertised endpoints as hints, not as proof that a route works from the current device. The final connection attempt still decides whether the endpoint is reachable.
125+
126+
The UI presents one default advertised endpoint in the network-access summary and keeps the rest behind an expandable advanced list. The default controls pairing QR codes and primary copy actions. Users can override it, but that override is a UI preference, not backend configuration.
127+
128+
Persist the override by stable endpoint kind rather than raw URL whenever possible. For example, a LAN endpoint should be stored as the desktop LAN endpoint preference, not as `192.168.x.y`, because the address can change when the user switches networks. Provider endpoints should use provider-specific stable keys such as Tailscale IP or Tailscale MagicDNS HTTPS. Custom endpoints may fall back to their concrete identity.
129+
130+
When no user default is saved, endpoint selection should prefer:
131+
132+
1. endpoints compatible with the hosted HTTPS app
133+
2. explicitly default endpoints
134+
3. non-loopback endpoints
135+
4. loopback endpoints only for same-machine clients
136+
137+
This keeps endpoint discovery centralized without making any one provider, such as Tailscale or a future tunnel service, part of the core environment model.
138+
139+
### Endpoint providers
140+
141+
Endpoint providers are add-ons that contribute advertised endpoints for the current environment.
142+
143+
The provider boundary is intentionally outside the core environment model:
144+
145+
- core owns `ExecutionEnvironment`, saved environments, pairing, and connection lifecycle
146+
- providers discover or synthesize endpoints
147+
- providers return normalized `AdvertisedEndpoint` records
148+
- the UI and pairing logic select from those records without knowing provider-specific commands
149+
150+
The first provider is Tailscale. It can discover Tailnet IP and MagicDNS addresses from the local machine and publish them as additional endpoint candidates. Future providers, such as a hosted tunnel service, should plug into the same shape rather than adding a separate remote environment path.
151+
152+
Provider-specific confidence should remain a hint. A Tailscale endpoint still needs a successful browser or desktop connection before the client treats it as connected.
153+
154+
### Hosted pairing request
155+
156+
A hosted pairing request is a bootstrap URL for the static web app, not a transport.
157+
158+
Example:
159+
160+
```text
161+
https://app.t3.codes/pair?host=https://backend.example.com:3773#token=PAIRCODE
162+
```
163+
164+
The hosted app reads the `host` parameter and pairing token, exchanges the token directly with that backend, then saves the resulting environment record in browser local storage.
165+
166+
Important constraints:
167+
168+
- the hosted app does not proxy HTTP or WebSocket traffic
169+
- the backend must still be reachable directly from the browser
170+
- HTTPS pages can only connect to HTTPS/WSS backends
171+
- HTTP LAN endpoints should keep using direct desktop or CLI pairing URLs
172+
- the token belongs in the URL hash so it is not sent to the hosted app origin
173+
111174
### RepositoryIdentity
112175

113176
`RepositoryIdentity` remains a best-effort logical repo grouping mechanism across environments.
@@ -151,6 +214,8 @@ Benefits:
151214
- no client-specific process management required
152215
- best fit for hosted or self-managed remote T3 deployments
153216

217+
Browser security rules are part of this access method. A hosted HTTPS web client can connect to `wss://` backends, but it cannot connect to plain `ws://` or `http://` LAN backends because that would be mixed content.
218+
154219
### 2. Tunneled WebSocket access
155220

156221
Examples:
@@ -170,6 +235,8 @@ This is especially useful when:
170235
- mobile must reach a desktop-hosted environment
171236
- a machine should be reachable without exposing raw LAN or public ports
172237

238+
Tailscale-backed access sits here architecturally even though the current implementation is endpoint discovery rather than a T3-managed tunnel. It contributes private-network endpoints and lets the existing HTTP/WebSocket client path do the actual connection.
239+
173240
### 3. Desktop-managed SSH access
174241

175242
SSH is an access and launch helper, not a separate environment type.
@@ -185,6 +252,8 @@ After that, the renderer should still connect using an ordinary WebSocket URL ag
185252

186253
This keeps the renderer transport model consistent with every other access method.
187254

255+
The desktop main process owns the SSH bridge because it can spawn local SSH processes, manage askpass prompts, write temporary launch scripts, and clean up forwards. The renderer receives a saved environment record and connects through the forwarded URL; it should not need SSH-specific RPC paths for normal environment traffic.
256+
188257
## Launch methods
189258

190259
Launch methods answer a different question:
@@ -227,6 +296,15 @@ The recommended T3 flow is:
227296
4. Desktop establishes local port forwarding.
228297
5. Renderer connects to the forwarded WebSocket endpoint as a normal environment.
229298

299+
The saved environment should remember that it was created by desktop SSH launch only for reconnect and lifecycle UX. That metadata should not change the server protocol or the environment identity model.
300+
301+
Failure handling should be explicit:
302+
303+
- SSH authentication failure should surface before any environment is saved
304+
- remote launch failure should include remote logs or the launcher command output when available
305+
- forwarded-port failure should leave the saved environment disconnected rather than falling back to an unrelated endpoint
306+
- reconnect should attempt to restore the SSH bridge before reconnecting the normal WebSocket client
307+
230308
### 3. Client-managed local publish
231309

232310
This is the inverse of remote launch: a local T3 server is already running, and the client publishes it through a tunnel.
@@ -267,6 +345,8 @@ T3 already supports a WebSocket auth token on the server. That should become a f
267345

268346
For publicly reachable environments, authenticated access should be treated as required.
269347

348+
Hosted pairing should be treated as a client-side convenience only. The hosted app must not receive pairing tokens through query parameters, must not store pairing state server-side, and must not imply that an HTTP backend is safe or reachable from an HTTPS browser context.
349+
270350
## Relationship to Zed
271351

272352
Zed is a useful reference implementation for managed remote launch and reconnect behavior.

.github/VOUCHED.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,6 @@ github:Yash-Singh1
3030
github:eggfriedrice24
3131
github:Ymit24
3232
github:shivamhwp
33+
github:jappyjan
34+
github:justsomelegs
35+
github:UtkarshUsername

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,5 @@ apps/web/src/components/__screenshots__
2222
__screenshots__/
2323
.tanstack
2424
squashfs-root/
25+
.vercel
26+
.claude/worktrees/

0 commit comments

Comments
 (0)