Repository navigation
Conversation
…ransport stubbed) The connection-management shell from the remote-core plan (P5, part 1): - Connection list persisted by the app itself (connections.json in the data dir): built-in, undeletable "This Mac" plus remote entries. Keys live in the OS keychain (keyring crate), never in the file and never sent back to the UI. - One connector seam (connection::connector): test/connect returns ServerInfo or a ConnectError (unreachable / closed / wrong key / version mismatch / timeout). The remote handshake is a single stub, connector::handshake, that P2 fills in with the Noise handshake; until then it answers "not available yet" after the TCP connect. - ControlClient is retargetable; every clone follows the current connection. In remote mode the local core stops once in-flight requests finish and the watchdog does not restart it; switching back starts it. Never falls back to local on its own. - Launch screen waits at most ~8 s, then the window opens in a "not connected" state (data pages disabled, Settings usable, retry with backoff, edit, switch to this Mac). Version-mismatch page. Mid-session disconnect shows a banner, makes the content read-only, raises one notification and reconciles through the existing core-state/events_dropped path. - Launch guard: two launches in a row that never got ready, or holding Option/Alt at launch, show a small connection picker first. - Sidebar switcher, Settings > Connection section, add/edit dialog with test / save / save and switch, switch confirmation with the "also point these clients at the server" option (clients::retarget_adopted_clients is a TODO hook until adoption moves into lite). - Menubar/tray "Connection" submenu on all platforms. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On macOS the keychain prompts for the login password for this item (again after each update of an unsigned app), which is not worth it for a key that can be copied from the server again. There is now one code path on every platform: connection-keys.json in the data directory, created 0600 from the start (temp file with the mode, then rename), in a directory created private (0700 on unix, protected DACL on Windows; ported from core's private_dir). Kept apart from connections.json so the frequent list writes never touch the secret and the code that reads the list cannot see it. Keys still never go to the webview. The keyring crate and all keychain copy are gone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Sep 24, 2026
fylorn
added a commit
that referenced
this pull request
Sep 24, 2026
…mode UI (#182, includes #181) * Adopt clients, edit MCP and scan client configs in the app instead of core Client adoption, MCP editing, the static scan and the file watcher change files on this machine, so they now run in the desktop app. The tw-adopt and tw-scan crates move here from core (src-tauri/crates/, one workspace with the app; the unused tw-config dependency is dropped). - New Tauri commands replace the control-plane endpoints: list_clients, plan_adopt, adopt_client, plan_restore, restore_client, diagnose_client, prepare_client_key, mcp_targets, plan_mcp, apply_mcp, scan_clients, gateway_base, plus delete_key and rotate_key (the adopted-client check and the key sync need this machine's files). restore_all no longer asks core. - Core is still asked for two things only: the gateway port (Overview) and keys (Keys, ClientKey). The gateway host is a single function so remote mode can swap it; retarget_adopted() repoints every adopted client. - The watcher runs in the app: `local-event` (clients_changed / scan_alert) to the webview, scan alerts into the notice bus; the first scan is a baseline. Core's own ClientsChanged/ScanAlert are ignored. - Types for these commands live in src-tauri/src/wire.rs and generate src/generated/lite-api.ts; names identical to tw-api.ts are imported. - Message codes the app emits itself are listed in src-tauri/msg-codes.txt (generated from source); translations are checked against it plus core's. - The webview endpoint whitelist drops the moved endpoints, and DeleteKey, RotateKey and ClientKey now go only through Rust commands. Works against the pinned core v0.46.0 (its endpoints just go unused). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Connections: switch between the local core and remote cores (remote transport stubbed) The connection-management shell from the remote-core plan (P5, part 1): - Connection list persisted by the app itself (connections.json in the data dir): built-in, undeletable "This Mac" plus remote entries. Keys live in the OS keychain (keyring crate), never in the file and never sent back to the UI. - One connector seam (connection::connector): test/connect returns ServerInfo or a ConnectError (unreachable / closed / wrong key / version mismatch / timeout). The remote handshake is a single stub, connector::handshake, that P2 fills in with the Noise handshake; until then it answers "not available yet" after the TCP connect. - ControlClient is retargetable; every clone follows the current connection. In remote mode the local core stops once in-flight requests finish and the watchdog does not restart it; switching back starts it. Never falls back to local on its own. - Launch screen waits at most ~8 s, then the window opens in a "not connected" state (data pages disabled, Settings usable, retry with backoff, edit, switch to this Mac). Version-mismatch page. Mid-session disconnect shows a banner, makes the content read-only, raises one notification and reconciles through the existing core-state/events_dropped path. - Launch guard: two launches in a row that never got ready, or holding Option/Alt at launch, show a small connection picker first. - Sidebar switcher, Settings > Connection section, add/edit dialog with test / save / save and switch, switch confirmation with the "also point these clients at the server" option (clients::retarget_adopted_clients is a TODO hook until adoption moves into lite). - Menubar/tray "Connection" submenu on all platforms. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Copy the gateway address from the menu bar without asking core for the clients list The menu bar's copy-address action still read gateway_base from the /clients endpoint, which core is dropping. It now uses the same function as the clients and keys pages. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Store remote connection keys on disk, not in the OS keychain On macOS the keychain prompts for the login password for this item (again after each update of an unsigned app), which is not worth it for a key that can be copied from the server again. There is now one code path on every platform: connection-keys.json in the data directory, created 0600 from the start (temp file with the mode, then rename), in a directory created private (0700 on unix, protected DACL on Windows; ported from core's private_dir). Kept apart from connections.json so the frequent list writes never touch the secret and the code that reads the list cannot see it. Keys still never go to the webview. The keyring crate and all keychain copy are gone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Remote mode: say which machine each page changes, trim what only works locally Following mockup ⑧ and the remote-mode table in the plan: - Clients and MCP pages carry a fixed note that they change this Mac's files, pointing clients at the server's gateway; the server's clients are not affected. - Adopted clients still pointing at the local gateway are flagged on their row ("points to the local gateway") and above the table, with a one-click "Point at <server>"; what was and was not repointed is listed per client. The switch-confirm dialog shows the same report when some failed. - Settings split into "the app on this Mac" and "configuration on <server>" (listen, retention, and a note that the remote control listener and key can only be changed on the server). The diagnostics bundle is hidden; About names the connection and the server's core version; uninstall says the server is not affected. Local mode keeps its layout. - ChatGPT sign-in offers only the device code while remote (the browser callback can only reach the machine running core); Z.ai is unchanged. - ${NAME} in upstream headers and the "system proxy" choice say they are the server's environment. - The sidebar and the test result show the gateway clients should use: the host the app dialled plus the server's gateway port, not its bind address. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
Author
|
Landed as part of #182, which contained this branch in full (merged together with the adoption wiring). |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part 1 of P5 in the remote-core plan: the connection-management shell. The remote transport (Noise handshake, key from
listen.control.key) is not released yet, so it sits behind one stub that P2 fills in.What changes
Model (Rust,
src-tauri/src/connection/)store.rs:connections.jsonin the data dir —{ remotes: [{ id, name, host, port, last_connected_at }], last_used, startup: "last" | "local" }. "This Mac" is built in (idlocal), not stored, cannot be deleted. Startup default = last used connection.secrets.rs: control keys inconnection-keys.jsonin the data dir — one code path on every platform, file created 0600 from the start (temp file with the mode, then rename), directory created private (0700 / protected DACL,private_dir.rsported from core). No OS keychain (it prompts for the login password on macOS). Kept apart fromconnections.json; never returned to the webview — the edit dialog shows "saved on this computer" + Replace.connector.rs:test(&Target) -> Result<ServerInfo { core_version, gateway_addr }, ConnectError>withUnreachable / Closed / WrongKey / VersionMismatch { ours, theirs } / Timeout, plus a temporaryNotYetAvailable. Local = existing path. Remote does the TCP connect for real, then callsconnector::handshake— the single P2 seam, currently a stub.mod.rs:Link(current connection +LinkState), remote reconnect loop with backoff, commands, switch logic.ControlClientis now retargetable: all clones ofAppState.controlfollow the current connection; the supervisor and heartbeat keep their own local client.launch.rs: launch guard (launch-attemptsmarker; two launches in a row that never got ready → picker first) and Option/Alt-held detection (macOS, Windows; Linux has only the guard).Behaviour
wait_for_quiet, now shared with the updater) and the watchdog does not restart it; switching back starts it. Never falls back to local on its own;restart_corein remote mode means "retry now".core-statenow describes the active connection (running:0/unlinkedfor remote), so the existing resync paths (useRequests, useLive, events_dropped) handle reconnects. The event bridge follows retargets and waits while a remote is down instead of polling it.UI (mockups ①–⑦)
Left for later
connector::handshake(and deleteConnectError::NotYetAvailable).clients::adopted_pointing_at_localstill asks core's/clients;clients::retarget_adopted_clientsis a TODO hook that currently returns an error (logged, the switch itself still happens).twcore upgradeuntil P6 settles it.Checks
cargo fmt --check,cargo clippy --all-targets -D warnings,cargo test(with twcore v0.46.0),pnpm typecheck,pnpm build,pnpm test. UI verified in the isolated preview harness (mockIPC) for local, remote connected, startup timeout, wrong key, version mismatch, mid-session loss/recovery, the switch flow and the picker.🤖 Generated with Claude Code