Allow local browser origins such as 127.0.0.1, localhost, and ::1 to use the desktop server without H5 token flow, while keeping LAN and public origins behind H5 access rules. Also make Vite SPA healthcheck fallback to the default loopback backend and document scoped verification expectations.
Tested: bun test src/server/__tests__/h5-access-policy.test.ts
Tested: bun test src/server/__tests__/h5-access-auth.test.ts
Tested: bun test src/server/middleware/cors.test.ts
Tested: cd desktop && bun run test -- src/lib/desktopRuntime.test.ts --run
Tested: git diff --check
Not-tested: bun run verify and coverage were intentionally skipped for this scoped local-dev fix.
Scope-risk: moderate
Complete the Electron replacement boundary before merging by removing the renderer-side Tauri host fallback, tightening H5/browser access so only desktop navigation is tokenless, and moving desktop release publication to a tag-driven GitHub Actions matrix with a single final publish job.
Constraint: H5/browser capability access must not gain tokenless access through localhost or retired Tauri origins
Constraint: Desktop release artifacts must be built by GitHub Actions from version tags, not treated as local build outputs
Rejected: Keep localhost browser origins trusted for convenience | local browser contexts can access loopback services and must use the H5 token path
Rejected: Publish from each matrix job | partial releases can be created before all platforms finish
Confidence: high
Scope-risk: broad
Directive: Do not reintroduce Tauri origins or localhost browser origins into the trusted desktop origin set without a reviewed security design
Tested: bun test src/server/__tests__/h5-access-policy.test.ts src/server/__tests__/h5-access-auth.test.ts src/server/__tests__/diagnostics-service.test.ts src/server/middleware/cors.test.ts
Tested: bun test scripts/pr/release-workflow.test.ts scripts/release-update-metadata.test.ts
Tested: bun run check:desktop
Tested: bun run check:native
Tested: git diff --check
Not-tested: bun run check:server is blocked by expired quarantine entries server:cron-scheduler, server:providers-real, server:tasks, server:e2e:business-flow, server:e2e:full-flow
Electron's sidecar runs outside app.asar, so H5 static files must be available as normal unpacked files. Point the sidecar at the unpacked renderer dist and keep a server fallback for stale app.asar-style paths.
Constraint: Packaged Bun sidecars cannot read app.asar paths with ordinary fs stat calls.
Rejected: Serve H5 from app.asar directly | the external sidecar is not Electron and does not get asar filesystem support.
Confidence: high
Scope-risk: narrow
Directive: Keep package-smoke checking app.asar.unpacked/dist/index.html before changing asarUnpack or H5 dist paths.
Tested: bun test src/server/__tests__/h5-access-auth.test.ts src/server/__tests__/h5-access-policy.test.ts
Tested: bun test desktop/electron/services/sidecarManager.test.ts scripts/quality-gate/package-smoke/index.test.ts
Tested: bun run check:server
Tested: cd desktop && bun run check:electron
Tested: git diff --check
Tested: SKIP_INSTALL=1 SIGN_BUILD=0 MAC_TARGETS=dmg desktop/scripts/build-macos-arm64.sh
Tested: packaged sidecar curl /?serverUrl=...&h5Token=... returned HTTP 200
Not-tested: Gatekeeper notarization for the local ad-hoc DMG
Introduce the Electron desktop shell alongside the existing React renderer and local Bun server boundary. The migration keeps the DesktopHost contract explicit across Tauri, Electron, and browser runtimes while adding Electron main/preload services for dialogs, shell, notifications, updates, tray/window lifecycle, terminal, preview WebContentsView, app mode, and release/package validation.
The commit also carries the latest local main desktop command updates, including agent slash entries and hidden-by-default markdown thinking details, so the packaged Electron build matches the current main UX surface.
Constraint: React renderer, local Bun server, REST/WebSocket, and sidecar boundaries must remain reusable during the migration
Constraint: macOS dev packages are ad-hoc signed and cannot prove Developer ID notarization or Gatekeeper release launch
Rejected: Browser-only smoke validation | it cannot exercise native dialogs, keychain prompts, notification behavior, or packaged app startup
Confidence: medium
Scope-risk: broad
Directive: Do not remove Tauri host support until signed Electron release artifacts pass native OS smoke on macOS, Windows, and Linux
Tested: bun run check:desktop
Tested: cd desktop && bun run check:electron
Tested: CSC_IDENTITY_AUTO_DISCOVERY=false bun run electron📦dir
Tested: bun run test:package-smoke --platform macos --package-kind dir --artifacts-dir desktop/build-artifacts/electron
Tested: Computer Use read packaged Electron app window at desktop/build-artifacts/electron/mac-arm64/Claude Code Haha.app
Not-tested: Developer ID signed/notarized Gatekeeper launch
Not-tested: Real OS notification click-to-session action
Not-tested: Windows and Linux packaged app smoke on real hosts
The H5 browser shell can load while deeper Settings tabs still exercise separate API routes. This adds MCP, plugin, and Agents endpoints to the H5 policy and integration matrices so the QR-token path is protected against page-level regressions.\n\nConstraint: H5 must expose existing desktop settings surfaces only after local opt-in and a valid H5 token.\nRejected: Rely on manual browser clicks alone | they missed the stopped-port failure mode and would not guard future regressions.\nConfidence: high\nScope-risk: narrow\nDirective: Add new Settings API surfaces to this H5 matrix when exposing them in the browser UI.\nTested: bun test src/server/__tests__/h5-access-policy.test.ts src/server/__tests__/h5-access-auth.test.ts\nTested: bun run check:server
The desktop sidecar can bind on LAN addresses for phone access, so the H5 settings switch must be an authorization boundary for remote capability routes, not only a token-mode toggle. Remote browser API, proxy, websocket, and SDK routes now fail closed while H5 is disabled; local desktop, Tauri, WebUI, adapter, and internal SDK paths remain tokenless. When H5 is enabled, remote API, proxy, and websocket requests must use the H5 token carried by the QR link, and the server API key cannot substitute for that H5 token.
Constraint: Desktop sidecar binds 0.0.0.0 while reporting loopback to local UI.
Constraint: Client-controlled Host and Origin headers cannot prove a local request; the boundary uses Bun requestIP instead.
Constraint: Static H5 shell and /health must still load so browser bootstrap can show a recovery flow.
Rejected: Trust loopback Host headers | LAN clients can spoof Host and Origin.
Rejected: Use ANTHROPIC_API_KEY as a remote H5 credential | it is not the phone pairing token and would weaken the QR-token boundary.
Confidence: high
Scope-risk: moderate
Directive: Do not make h5Enabled=false an open remote state for /api, /proxy, /ws, or /sdk routes.
Tested: bun test src/server/__tests__/h5-access-policy.test.ts src/server/__tests__/h5-access-auth.test.ts src/server/middleware/cors.test.ts
Tested: bun run check:server
Remote browser access now needs the H5 token once LAN mode is enabled, but changing that mode remains a local desktop control-plane operation even when deployment auth is explicitly enabled. CORS also no longer treats a non-local same-origin alias as trusted unless it is the configured H5 public origin or allowlisted origin.
Constraint: H5 exposes desktop control surfaces on the LAN and must not let remote browsers enable their own access.
Constraint: Desktop WebView, localhost WebUI, SDK websocket, and IM adapter loopback calls must remain tokenless.
Rejected: Allow authenticated remote control-plane writes under SERVER_AUTH_REQUIRED | violates the local opt-in boundary for H5 exposure.
Confidence: high
Scope-risk: moderate
Directive: Do not relax /api/h5-access mutating endpoints for browser origins without adding a reviewed desktop-only trust signal.
Tested: bun test src/server/__tests__/h5-access-auth.test.ts
Tested: bun test src/server/__tests__/h5-access-policy.test.ts src/server/middleware/cors.test.ts
Tested: bun run check:server
Wire request-scoped H5 auth and CORS decisions through the server so LAN browser API, proxy, and websocket access require the H5 token while loopback desktop, Tauri WebView, local adapter, SDK, and static shell paths keep their existing bootstrap behavior.
Constraint: Task 2 scope is limited to server auth/CORS wiring and the four requested files
Rejected: Global auth toggle for all requests | would incorrectly block loopback desktop and bootstrap asset flows
Directive: Keep static H5 shell and assets tokenless unless the browser-side bootstrap model changes
Confidence: high
Scope-risk: narrow
Tested: bun test src/server/__tests__/h5-access-policy.test.ts src/server/middleware/cors.test.ts
Tested: bun test src/server/__tests__/h5-access-policy.test.ts src/server/__tests__/h5-access-auth.test.ts src/server/middleware/cors.test.ts | h5-access-auth blocked by missing zod dependency in src/server/api/haha-oauth.ts
Tested: LSP diagnostics clean for src/server/index.ts, src/server/middleware/cors.ts, src/server/__tests__/h5-access-auth.test.ts, src/server/middleware/cors.test.ts
Not-tested: h5-access-auth runtime assertions because this checkout is missing zod for haha-oauth imports
The H5 token gate still caused chat startup and runtime failures after the desktop startup hotfix, so the default server path now stays open for browser and LAN access. Explicit auth remains available through SERVER_AUTH_REQUIRED=1 or --auth-required for deployments that intentionally need it.
Constraint: Current H5 token state is blocking active users from normal chat usage
Rejected: Keep automatic remote-host auth with broader client-side token handling | still leaves existing upgraded clients vulnerable to stale or missing token state
Confidence: high
Scope-risk: moderate
Directive: Do not re-enable default H5 token auth without a migration and end-to-end browser chat verification
Tested: bun test src/server/__tests__/h5-access-auth.test.ts src/server/middleware/cors.test.ts
Tested: cd desktop && bun run test src/lib/desktopRuntime.test.ts src/__tests__/generalSettings.test.tsx
Tested: bun run check:server
Tested: bun run check:desktop
The H5 LAN release made the desktop sidecar bind broadly and require auth for non-localhost hosts. The Tauri WebView still talks to the loopback control URL without an H5 bearer token, so startup requests were rejected before the app could load.
Constraint: Desktop WebView control requests must stay tokenless for the local sidecar.
Rejected: Reuse H5 token bootstrap for Tauri | would add remote-access state to the native app startup path.
Confidence: high
Scope-risk: narrow
Directive: Tauri WebView bypass is limited to loopback control hosts; do not extend it to LAN or public hosts without re-reviewing H5 auth boundaries.
Tested: bun test src/server/__tests__/h5-access-auth.test.ts
Tested: bun run check:server
Packaged desktop builds started the sidecar on loopback only, so phones on the same WiFi could not connect to the H5 URL. The server now binds to LAN interfaces while keeping the desktop control URL on loopback, serves the bundled H5 frontend, and reports a LAN public URL when H5 access is enabled.
Constraint: Packaged Tauri resources place ../dist under Contents/Resources/_up_/dist.
Rejected: Bind the desktop control URL to 0.0.0.0 | desktop clients should keep using loopback for local control.
Confidence: high
Scope-risk: moderate
Directive: Keep packaged H5 resource lookup aligned with Tauri resource mapping when changing bundle resources.
Tested: bun run check:server
Tested: cd desktop/src-tauri && cargo test --lib
Tested: cd desktop && bun run test -- desktopRuntime.test.ts generalSettings.test.tsx
Tested: SKIP_INSTALL=1 ./desktop/scripts/build-macos-arm64.sh
Tested: Packaged claude-sidecar smoke served H5 HTML while listening on *:39876
Not-tested: Physical phone browser test on the local WiFi network
H5 browser use needs to keep local desktop WebUI testing open while giving remote phones a usable chat-first surface. This change scopes browser auth by origin and target host, hides non-chat desktop chrome on H5 mobile, compacts the empty-session composer, and unifies mobile selectors behind a full-width bottom sheet with an explicit close action.
Constraint: Local browser development may bypass H5 auth only for loopback or private LAN server hosts; public or remote hosts still require an H5 token.
Constraint: Mobile H5 scope is chat-first; settings and scheduled-task surfaces stay hidden instead of fully redesigned.
Rejected: Reuse desktop popovers on mobile | hover/anchored dropdown behavior leaves key actions hard to dismiss and cramped on narrow screens.
Rejected: Let any localhost origin bypass remote auth | it would let arbitrary local pages access public H5 servers without a token.
Confidence: medium
Scope-risk: moderate
Directive: Keep future H5 selector popups on MobileBottomSheet unless a selector needs a reviewed custom mobile interaction.
Tested: cd desktop && bun run lint
Tested: cd desktop && bun run test -- src/pages/EmptySession.test.tsx src/components/chat/ChatInput.test.tsx src/lib/desktopRuntime.test.ts src/components/shared/RepositoryLaunchControls.test.tsx src/components/controls/PermissionModeSelector.test.tsx src/components/controls/ModelSelector.test.tsx src/components/shared/DirectoryPicker.test.tsx src/__tests__/pages.test.tsx
Tested: bun test src/server/__tests__/h5-access-auth.test.ts src/server/middleware/cors.test.ts
Tested: Chrome mobile viewport smoke for context, model, permission, project, branch, and worktree bottom sheets with no horizontal overflow.
Not-tested: Full bun run verify quality gate.
Remote browser access needs an explicit path that preserves localhost desktop behavior while allowing H5 tokens for REST and client websocket upgrades. This keeps the existing synchronous Anthropic auth helper intact, adds async request validation for H5 tokens, and centralizes origin handling so preflight, API, proxy, websocket, and health responses behave consistently.
Constraint: Existing validateAuth(req) must stay synchronous for current tests and callers
Constraint: Default localhost and Tauri desktop behavior must remain unchanged
Rejected: Replacing validateAuth with an async-only API | would break existing sync tests and call sites
Rejected: Allowing wildcard or fallback remote origins | violates the H5 explicit-origin security model
Directive: Restore ProviderService serverPort in server integration tests that call startServer with custom ports to avoid leaking global proxy runtime state
Confidence: high
Scope-risk: moderate
Tested: bun test src/server/__tests__/h5-access-auth.test.ts --timeout 30000
Tested: bun test src/server/__tests__/h5-access-service.test.ts src/server/__tests__/h5-access-api.test.ts src/server/__tests__/settings.test.ts --timeout 30000
Tested: bun test src/server/__tests__/h5-access-auth.test.ts src/server/__tests__/conversation-service.test.ts src/server/__tests__/cron-scheduler-launcher.test.ts --timeout 30000
Tested: bun test src/server/__tests__/e2e/full-flow.test.ts --timeout 30000
Not-tested: Full non-live server gate end-to-end; an attempted broader run was interrupted after uncovering and then fixing a test-side ProviderService port leak