Commit f02ee99
authored
feat(desktop,cli): support self-hosted desktop installs (#7136)
* feat(desktop,cli): support self-hosted desktop installs
The desktop shell was already origin-agnostic at runtime — navigation, CSP,
cookie partition, and the update feed all derive from the configured origin,
and every deployment already serves /api/desktop/update/download and its
updater manifest. The one thing missing was a way to change that origin:
ConfigStore.setOrigin had no IPC channel, menu item, or UI behind it, so a
self-hoster installing the signed build was stuck on the baked default.
Adds the native server picker (Sim → Server…, plus a "Change server" button on
the offline page, since a shell pointed at an unreachable origin lands there
with nothing else to click). Its IPC family is gated to bundled file: senders:
the surface that repoints the shell must keep working when the current server
cannot be reached, and must never be drivable by a page that server serves.
A confirmed change relaunches rather than swapping in place — the origin keys
the cookie partition, update feed, encrypted per-origin task state, and every
live browser view and PTY.
Adds `sim-setup desktop`, which resolves the installer from the operator's own
deployment, checks that the update feed resolves too, and prints the server URL
to paste in. Documents the whole path under self-hosting, including the
build-your-own escape hatch for organizations with their own Developer ID.
* refactor(desktop,cli): review pass on self-hosted desktop support
Two real defects found while auditing the change for hardcoded assumptions.
`lastRoute` is a single global setting that carries a workspace id, so it
survived an origin change and opened /workspace/<old-id> on the new server.
resolveStartRoute cannot rescue that — it discards a route only on a confirmed
403, and a fresh partition draws a 401. Cleared on change, via a named list
that is now the documented home for deployment-scoped settings; the agent
browser's jar and its known-sites metadata are deliberately kept, together,
since changing deployments does not imply the account changed.
The offline page's "Check status" sent self-hosters to status.sim.ai, which
reports on Sim's deployments and is always green for theirs. Withheld for a
non-sim.ai origin, as is the same link in the Help menu, through one
isSimCloudOrigin predicate. Hiding it needed `button[hidden]{display:none}`:
the page's own `button{display:inline-flex}` is an author rule and outranks the
UA `[hidden]`, so the attribute alone left it rendering. The e2e offline test
now asserts the whole path, which covers the `server:` local-page IPC gate.
Review cleanups: setOrigin no longer rewrites settings when handed the origin
it already stores; the picker window installs a permission handler and pre-
paints its background like every other window, and its page is theme-aware so
that background is not a flash; the CLI reuses httpHealth and the
cross-platform openBrowser instead of reimplementing both, skips Compose/Helm
discovery when --url makes it dead, and folds two parallel switches into one
exhaustive one. Value-flag parsing is now one helper instead of a third copy.
* fix(desktop,cli): scope deployment capabilities to their origin
Changing the server left two device-global stores in place that grant the
INCOMING deployment authority the user only handed the outgoing one: local
filesystem grants (directories its agent may read, plus security-scoped
bookmarks) and the agent browser's cookie jar (live third-party sessions its
agent may drive). Sign-out clears exactly this pair; an origin change is the
same boundary, so it now clears it too — awaited before the relaunch, since a
quit racing an async clear could leave either behind. browserKnownSites goes
with the jar it describes, so Sim is never left believing in sign-ins the
profile no longer has.
The CLI printed the redirect's filename straight to the terminal. It is read
out of a Location the deployment chose, so percent-encoded ANSI or OSC survives
decodeURIComponent as real control bytes and could forge CLI output; control
characters are stripped and the name is bounded before it reaches the spinner.
resolveDeploymentUrl took the first source naming an app URL. A machine with
both a local checkout and a real deployment would be probed, printed, and
opened at whichever enumerated first, silently — so disagreeing sources are now
an error naming each candidate and asking for --url, the way
resolveFeatureSetupDestination already refuses ambiguity.
* fix(desktop,cli): fail closed on origin change, widen terminal sanitizer
The capability teardown could partially fail and still let the shell move.
Sequential awaits meant a filesystem-grant rejection skipped the browser-profile
clear entirely, and the new origin was already persisted by then, so the
incoming deployment inherited whatever survived — and startup restores it.
Now the two stores clear independently via allSettled and report which ones
survived, and the whole teardown runs BEFORE anything is written. A store that
cannot be emptied refuses the change outright and names what it could not
clear. Nothing is persisted at that point, so refusing leaves the shell exactly
where it was rather than half-applying. Validation moved up front for the same
reason: a typo now costs no teardown.
The terminal sanitizer matched only C0/DEL/C1 by range, so percent-encoded bidi
overrides and isolates survived decodeURIComponent and could still reorder what
the reader sees without emitting one control byte. Matched by Unicode class
instead — Cc covers the cursor controls, Cf covers the bidi ones.
Configuration discovery compared raw strings, so a trailing slash, a default
port, a host-case difference, or an ignored path read as two different servers
and demanded a --url override to settle an ambiguity that did not exist. Now
compared on the parsed origin, which is what the command ends up using.
* fix(cli): sanitize the installer name by Unicode group, not by escape list
U+2028 and U+2029 are Zl/Zp, so the Cc/Cf filter let them through and a
deployment-controlled redirect filename could still forge a status line.
Enumerating what to strip had cost a patch per class found — C0 and C1, then
the bidi overrides, now the line separators — so this keeps whole groups
instead. `C` removes every control, format, surrogate, private-use, and
unassigned code point, covering ESC and OSC, the bidi overrides and isolates,
zero-width characters, and the BOM; `Z` removes every space, line, and
paragraph separator. Separators become a plain space rather than vanishing so
a name is not run together at the seam, and runs are collapsed so the result
cannot be padded to push text off the line.
The regression table now names each class that reached the terminal in an
earlier round, so a future bypass says which one came back.
* fix(desktop): serialize server changes and report partial teardown honestly
Two problems in the same transaction.
The picker re-enabled Connect whenever the field changed, including while a
request was in flight, so typing and pressing Enter could start a second change
that interleaved its teardown and its write with the first — the later write,
not a single transition, deciding the next server. The transaction is now
serialized in the main process, the way the sign-out coordinator guards its
own teardown, since the IPC boundary is reachable regardless of what the page
does; the page keeps its button disabled for the whole request so it never asks
for something it will only be refused.
The stores clear independently, so one can succeed while the other fails. There
is nothing to roll back to — a revoked cookie jar and deleted security-scoped
bookmarks cannot be un-deleted — and moving anyway would hand the incoming
deployment whatever survived. So the change is still refused, but the message
no longer names only the failed store as though nothing else had happened: it
says some local access may already have been cleared, and that retrying
finishes the job. Clearing an already-empty store succeeds, so a retry is safe.1 parent 40ca081 commit f02ee99
25 files changed
Lines changed: 1910 additions & 59 deletions
File tree
- apps
- desktop
- e2e
- src
- main
- preload
- static
- docs/content/docs/en/platform/self-hosting
- sim/lib/copilot/generated
- packages
- desktop-bridge
- src
- sim-setup
- src
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
113 | 113 | | |
114 | 114 | | |
115 | 115 | | |
116 | | - | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
117 | 125 | | |
118 | 126 | | |
119 | 127 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
9 | 9 | | |
10 | 10 | | |
11 | 11 | | |
| 12 | + | |
12 | 13 | | |
13 | 14 | | |
14 | 15 | | |
| |||
145 | 146 | | |
146 | 147 | | |
147 | 148 | | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
148 | 169 | | |
149 | 170 | | |
150 | 171 | | |
| |||
223 | 244 | | |
224 | 245 | | |
225 | 246 | | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
226 | 266 | | |
227 | 267 | | |
228 | 268 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
179 | 179 | | |
180 | 180 | | |
181 | 181 | | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
182 | 198 | | |
183 | 199 | | |
184 | 200 | | |
| |||
339 | 355 | | |
340 | 356 | | |
341 | 357 | | |
| 358 | + | |
| 359 | + | |
| 360 | + | |
| 361 | + | |
| 362 | + | |
| 363 | + | |
342 | 364 | | |
343 | 365 | | |
344 | 366 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
51 | 51 | | |
52 | 52 | | |
53 | 53 | | |
| 54 | + | |
54 | 55 | | |
55 | 56 | | |
56 | 57 | | |
| |||
78 | 79 | | |
79 | 80 | | |
80 | 81 | | |
| 82 | + | |
81 | 83 | | |
82 | 84 | | |
83 | 85 | | |
| |||
473 | 475 | | |
474 | 476 | | |
475 | 477 | | |
| 478 | + | |
| 479 | + | |
| 480 | + | |
| 481 | + | |
| 482 | + | |
| 483 | + | |
| 484 | + | |
| 485 | + | |
| 486 | + | |
| 487 | + | |
| 488 | + | |
| 489 | + | |
| 490 | + | |
| 491 | + | |
| 492 | + | |
| 493 | + | |
| 494 | + | |
| 495 | + | |
| 496 | + | |
| 497 | + | |
| 498 | + | |
| 499 | + | |
| 500 | + | |
| 501 | + | |
| 502 | + | |
| 503 | + | |
| 504 | + | |
| 505 | + | |
| 506 | + | |
476 | 507 | | |
477 | 508 | | |
478 | 509 | | |
| |||
659 | 690 | | |
660 | 691 | | |
661 | 692 | | |
| 693 | + | |
| 694 | + | |
| 695 | + | |
| 696 | + | |
| 697 | + | |
662 | 698 | | |
663 | 699 | | |
664 | 700 | | |
665 | 701 | | |
666 | 702 | | |
667 | 703 | | |
668 | 704 | | |
| 705 | + | |
669 | 706 | | |
670 | 707 | | |
671 | 708 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
316 | 316 | | |
317 | 317 | | |
318 | 318 | | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
319 | 324 | | |
320 | 325 | | |
321 | 326 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
9 | 9 | | |
10 | 10 | | |
11 | 11 | | |
| 12 | + | |
| 13 | + | |
12 | 14 | | |
13 | 15 | | |
14 | 16 | | |
| |||
301 | 303 | | |
302 | 304 | | |
303 | 305 | | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
| 309 | + | |
| 310 | + | |
304 | 311 | | |
305 | 312 | | |
306 | 313 | | |
| |||
1737 | 1744 | | |
1738 | 1745 | | |
1739 | 1746 | | |
| 1747 | + | |
| 1748 | + | |
| 1749 | + | |
| 1750 | + | |
| 1751 | + | |
| 1752 | + | |
| 1753 | + | |
| 1754 | + | |
| 1755 | + | |
| 1756 | + | |
| 1757 | + | |
| 1758 | + | |
| 1759 | + | |
| 1760 | + | |
| 1761 | + | |
| 1762 | + | |
| 1763 | + | |
| 1764 | + | |
| 1765 | + | |
| 1766 | + | |
| 1767 | + | |
| 1768 | + | |
| 1769 | + | |
| 1770 | + | |
| 1771 | + | |
1740 | 1772 | | |
1741 | 1773 | | |
1742 | 1774 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
6 | 6 | | |
7 | 7 | | |
8 | 8 | | |
9 | | - | |
| 9 | + | |
10 | 10 | | |
11 | 11 | | |
12 | 12 | | |
13 | | - | |
| 13 | + | |
14 | 14 | | |
15 | 15 | | |
16 | 16 | | |
17 | 17 | | |
18 | 18 | | |
19 | 19 | | |
20 | 20 | | |
| 21 | + | |
21 | 22 | | |
22 | 23 | | |
23 | 24 | | |
| |||
51 | 52 | | |
52 | 53 | | |
53 | 54 | | |
| 55 | + | |
54 | 56 | | |
55 | 57 | | |
56 | 58 | | |
| |||
105 | 107 | | |
106 | 108 | | |
107 | 109 | | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
108 | 117 | | |
109 | 118 | | |
110 | 119 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | | - | |
| 3 | + | |
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
| |||
15 | 15 | | |
16 | 16 | | |
17 | 17 | | |
| 18 | + | |
| 19 | + | |
18 | 20 | | |
19 | 21 | | |
20 | 22 | | |
| |||
155 | 157 | | |
156 | 158 | | |
157 | 159 | | |
| 160 | + | |
158 | 161 | | |
159 | 162 | | |
160 | 163 | | |
| |||
250 | 253 | | |
251 | 254 | | |
252 | 255 | | |
253 | | - | |
254 | | - | |
255 | | - | |
256 | | - | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
257 | 266 | | |
258 | 267 | | |
259 | 268 | | |
| |||
0 commit comments