Skip to content

fix: web app session hydration, crypto polyfill, and Tailscale IPC#60

Merged
jcanizalez merged 2 commits intomainfrom
fix/web-app-improvements
Mar 19, 2026
Merged

fix: web app session hydration, crypto polyfill, and Tailscale IPC#60
jcanizalez merged 2 commits intomainfrom
fix/web-app-improvements

Conversation

@jcanizalez
Copy link
Copy Markdown
Owner

Summary

  • Hydrate already-running sessions when the web app connects (calls terminal:listActive on mount so pre-existing sessions appear)
  • Polyfill crypto.randomUUID for non-secure HTTP contexts (plain HTTP over Tailscale)
  • Wire getTailscaleStatus through Electron preload/IPC bridge (was missing, causing console errors)
  • Rename "Network" settings tab to "Remote Access" with updated labels and descriptions

Test plan

  • Open sessions in desktop app, then load web app — sessions should appear
  • Access web app over Tailscale HTTP — no crypto.randomUUID error
  • Open Remote Access settings in desktop app — no getTailscaleStatus error
  • Verify settings tab shows "Remote Access" label

Copilot AI review requested due to automatic review settings March 18, 2026 01:40
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.randomUUID for non-secure HTTP contexts.
  • Adds getTailscaleStatus to 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.

Comment on lines +3 to +6
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}`
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines 421 to 455
@@ -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"
>
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines 115 to 126
// 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})`
)
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +172 to +175
for (const session of active) {
if (!state.terminals.has(session.id)) {
state.addTerminal(session)
}
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.
@jcanizalez jcanizalez force-pushed the fix/web-app-improvements branch from 9c2bed3 to 476fab9 Compare March 19, 2026 19:14
@jcanizalez jcanizalez merged commit 67204f6 into main Mar 19, 2026
1 check passed
@jcanizalez jcanizalez deleted the fix/web-app-improvements branch March 19, 2026 20:52
This was referenced Mar 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants