Add an Autostart Wails service wrapping app.Autostart and a toggle in
the General settings tab. The OS login-item registration is the single
source of truth (nothing mirrored to the preferences file). Affects the
graphical UI only, not the daemon. The toggle hides itself on platforms
where autostart is unsupported.
Missed in the previous commit. The StatusContext is the only frontend
consumer of the renamed service (the modules/main/.../peers/Peers.tsx
React component is a different identifier — unchanged).
When the tray "Extend now" notification action and the about-to-expire
dialog both start a flow for the same deadline, the daemon was running
two independent IdP polls and the older one surfaced an InvalidArgument
toast as soon as the second RequestExtend overwrote the pending flow.
Follow the WaitSSOLogin pattern: at the top of WaitExtendAuthSession
cancel the previous wait (the SetWaitCancel/CancelWait pair on
PendingFlow already existed but was unused), then register the new
wait's cancel. Preempted callers exit with codes.Canceled; the
authsession service translates that into ExtendResult{Preempted: true}
so the tray and the React dialog can stay silent on the losing flow
instead of showing a false-failure toast / error dialog.
pnpm 11 blocks dependency build scripts by default and exits non-zero
when any are skipped, which made task build fail at install:frontend:deps.
esbuild's postinstall is required to fetch the platform-specific binary.
Exit nodes are mutually exclusive, but the RouteSelector stores routes with
default-on semantics, so every available exit node reported as selected at once.
Reconcile exit-node selection on each network map (and on runtime selection):
keep at most one selected — the user's persisted pick, else whatever management
marks for auto-apply (SkipAutoApply=false), else none. Never auto-activate an
exit node the map doesn't request; it stays off until the user picks it.
The server deselects sibling exit nodes when the user activates one (leaving
non-exit routes untouched), and the tray/React exit-node toggle now appends so
activating an exit node no longer wipes network-route selections.
Make the tray Exit Node submenu selectable (mutually exclusive, sourced from
ListNetworks by NetID) instead of read-only.
Add networksRevision to the status snapshot, bumped by the route manager on
network-map and selection changes, so the tray and the React NetworksContext
re-fetch ListNetworks via the push stream instead of polling. The peer-status
route list only carries chosen routes, so a candidate exit node appearing or
disappearing would otherwise never reach the UI.
- Relocate the session-expiry row from below the status item to below the
profile email so active profile, email, and session deadline form one block.
- Rename the label to "Expires in {remaining}" (en/hu/de).
- Capture the Connect/Disconnect separator via lastMenuItem and hide it when
both action rows are hidden (daemon unavailable), avoiding two adjacent
separators with nothing between them.
The main-window toggle stayed visually stuck on "Connecting" when the
user clicked Connect in the UI and then clicked Disconnect in the
tray (or the daemon was otherwise cancelled mid-Connecting).
Repro: open the main window, click the toggle to Connect, then while
the daemon is still in Connecting click Disconnect in the tray menu.
The tray and daemon agree the session is Idle, but the React toggle
keeps painting "Connecting" until the next manual interaction.
Root cause is in ConnectionStatusSwitch.tsx. The component holds an
`action` latch ("connect" | "logging-in" | "disconnect" | null) so the
toggle can show an optimistic transitional state while the daemon
catches up. The connState memo treats `action === "connect"` plus any
non-Connected daemon state as Connecting:
if ((action === "connect" || action === "logging-in") &&
daemonState !== "Connected") {
return ConnectionState.Connecting;
}
The effect that releases the latch only cleared it on `Connected` or
`DaemonUnavailable`. There was no branch for "the connect flow was
cancelled externally and the daemon is back at Idle", so the latch
remained set forever and the optimistic Connecting state never
collapsed.
Fix: add a `sawConnectingRef` that flips to true the first time the
daemon reports Connecting during an active "connect" action, and
resets when `action` returns to null. When `action === "connect"` and
the daemon flips from a state we'd observed as Connecting back to
Idle, clear the latch so connState falls through to Disconnected.
Other paths are untouched:
- Successful connect still clears on Connected.
- NeedsLogin still hands off to driveLogin.
- DaemonUnavailable still clears via the `unreachable` branch.
- The `"logging-in"` action is intentionally not handled here; Login's
internal Down flaps the daemon through Idle and driveLogin's
.finally remains the sole clearer for that latch.
- The `"disconnect"` action's Idle/Disconnected/unreachable clear is
unchanged.
The daemon returns gRPC errors whose message is a wrapped mgm + JWT
stack (e.g. "invalid jwt token, err: token could not be parsed: ...").
Showing that in a native dialog is unreadable. Connection now maps the
substrings it recognises to a ClientError{code, short, long} so the UI
can render a localised summary plus a Details: block carrying the raw
daemon text. formatErrorMessage on the TS side reads the structured
payload from Wails' Error.cause (or the JSON-stringified Error.message)
and falls back to plain Error.message for callers not yet migrated.
Also bumps Wails to v3.0.0-alpha.95.
Adds an end-to-end SSO session-extension feature: the management server
publishes per-peer session deadlines on every Login/Sync, a new
ExtendAuthSession RPC refreshes the deadline using a fresh JWT without
tearing down the tunnel, and the daemon tracks the deadline locally so
the UI can fire a T-10min warning toast with an interactive "Extend now"
action.