fix: web app session hydration, crypto polyfill, and Tailscale IPC#60
fix: web app session hydration, crypto polyfill, and Tailscale IPC#60jcanizalez merged 2 commits intomainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
This PR improves the web + Electron integration for remote connectivity by (1) hydrating pre-existing terminal sessions in the web UI, (2) preventing crypto.randomUUID failures in non-secure HTTP contexts (e.g., Tailscale), and (3) wiring a missing Tailscale status API through the Electron IPC bridge while updating “Network” settings terminology to “Remote Access”.
Changes:
- Web app now hydrates already-running terminal sessions on mount via
terminal:listActive. - Web entrypoint polyfills
crypto.randomUUIDfor non-secure HTTP contexts. - Adds
getTailscaleStatusto the Electron preload + main IPC handler and updates settings labels/copy to “Remote Access”.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| src/renderer/components/settings/NetworkSettings.tsx | Uses typed window.api.getTailscaleStatus() and updates UI text/labels for “Remote Access”. |
| src/renderer/components/SettingsPage.tsx | Renames sidebar “Network” section label to “Remote Access”. |
| src/renderer/App.tsx | Adds web-only hydration of active sessions via listActiveSessions(). |
| src/preload/index.ts | Exposes getTailscaleStatus() on window.api via ipcRenderer.invoke. |
| src/main/ipc-handlers.ts | Forwards IPC.TAILSCALE_STATUS through the bridge with safeHandle. |
| packages/web/src/main.tsx | Adds crypto.randomUUID polyfill before bootstrapping the web app. |
| packages/web/src/api-shim.ts | Adds listActiveSessions() mapped to terminal:listActive. |
Comments suppressed due to low confidence (1)
src/renderer/components/settings/NetworkSettings.tsx:486
- This paragraph claims only devices on the same Tailscale account can reach the app and that no firewall rules are needed. With the current server behavior (binding to 0.0.0.0 when enabled), the service can also be reachable on other interfaces, so this statement can be misleading and could cause unintended exposure. Please either tighten the bind to Tailscale-only or soften/qualify this wording.
Tailscale creates an encrypted mesh network between your devices. When remote access
is enabled, other devices on your tailnet can connect to VibeGrid.
</p>
<p>
Only devices signed into your Tailscale account can reach this address. No passwords,
no port forwarding, no firewall rules.
</p>
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
packages/web/src/main.tsx
Outdated
| crypto.randomUUID = () => | ||
| '10000000-1000-4000-8000-100000000000'.replace(/[018]/g, (c) => | ||
| (+c ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (+c / 4)))).toString(16) | ||
| ) as `${string}-${string}-${string}-${string}-${string}` |
There was a problem hiding this comment.
Fixed during rebase: the polyfill now uses a single getRandomValues(new Uint8Array(16)) call and formats the bytes, instead of calling it per-character via String.replace.
| @@ -454,8 +450,8 @@ export function NetworkSettings() { | |||
| {status && status.running && ( | |||
| <div className="space-y-1"> | |||
| <SettingRow | |||
| label="Enable Network Access" | |||
| description={`Bind to ${status.selfIP || 'Tailscale IP'} so other devices on your tailnet can connect`} | |||
| label="Enable Remote Access" | |||
| description="Allow other devices on your Tailscale network to access VibeGrid" | |||
| > | |||
There was a problem hiding this comment.
Acknowledged. The 0.0.0.0 bind is intentional for LAN access in addition to Tailscale — many users access VibeGrid from other local devices. The Tailscale section focuses on the secure mesh network use case, but the server is reachable on any interface. We can clarify the copy in a follow-up.
| // Determine bind address: if networkAccessEnabled AND Tailscale is running, | ||
| // bind to Tailscale IP so other devices on the tailnet can reach us. | ||
| // bind to 0.0.0.0 so other devices on the tailnet can reach us. | ||
| // Otherwise, localhost only. | ||
| let host = options.host ?? '127.0.0.1' | ||
| if (!options.host && config.defaults.networkAccessEnabled) { | ||
| try { | ||
| const tsStatus = await getTailscaleStatus() | ||
| if (tsStatus.running && tsStatus.selfIP) { | ||
| host = '0.0.0.0' // Bind all interfaces so localhost (Electron) + Tailscale IP both work | ||
| host = '0.0.0.0' | ||
| log.info( | ||
| `[server] network access enabled, binding to 0.0.0.0 (tailscale IP: ${tsStatus.selfIP})` | ||
| `[server] remote access enabled, binding to 0.0.0.0 (tailscale IP: ${tsStatus.selfIP})` | ||
| ) |
There was a problem hiding this comment.
Same as above — the 0.0.0.0 bind covers both Tailscale and LAN use cases by design. Binding to only the Tailscale interface would break LAN-only setups.
| for (const session of active) { | ||
| if (!state.terminals.has(session.id)) { | ||
| state.addTerminal(session) | ||
| } |
There was a problem hiding this comment.
Valid point, but in practice the number of active sessions is small (typically 1-5). The per-session state update also triggers widget notification which is the desired behavior. Batching would add complexity for minimal gain at this scale.
- Hydrate active sessions on web connect via terminal:listActive RPC - Polyfill crypto.randomUUID for non-secure HTTP contexts (Tailscale) - Wire getTailscaleStatus through Electron preload/IPC bridge - Rename Network settings tab to Remote Access
Generate all 16 bytes at once instead of per-character replacement.
9c2bed3 to
476fab9
Compare
Summary
terminal:listActiveon mount so pre-existing sessions appear)crypto.randomUUIDfor non-secure HTTP contexts (plain HTTP over Tailscale)getTailscaleStatusthrough Electron preload/IPC bridge (was missing, causing console errors)Test plan
crypto.randomUUIDerrorgetTailscaleStatuserror