feat(desktop): serve packaged renderer locally - #8101
Conversation
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Effect service conventions review of the packaged renderer protocol changes. Two findings in apps/desktop/src/electron/ElectronProtocol.ts around dependency acquisition and runtime boundaries; the rest of the diff (service tag/interface additions, error classes, DesktopEnvironment field, DesktopApp wiring) follows the conventions.
Posted via Macroscope — Effect Service Conventions
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit ae5a743. Configure here.
ApprovabilityVerdict: Not approved Macroscope's review found this PR not approvable — Production desktop startup now relies on a new Electron local protocol that resolves and serves packaged renderer files, including SPA fallback, filesystem containment, and CSP behavior. This cross-cutting runtime and security-boundary change merits human review. Not approved because:
Adjust the Minimum Blocking Severity for this repo — including turning it Off — in Settings. You can add or adjust custom eligibility rules. Learn more. |
| showConnectingSplash, | ||
| handleBackendConfigured: Effect.gen(function* () { | ||
| yield* Ref.set(backendReadyRef, true); | ||
| yield* createMainIfBackendReady; |
There was a problem hiding this comment.
🟡 Medium window/DesktopWindow.ts:864
handleBackendConfigured and handleBackendReady can concurrently create two main BrowserWindows, leaving one orphaned when the later setMain overwrites it. Both paths call the non-atomic createMainIfBackendReady, which checks for an existing window before createWindow and setMain; serialize this creation or otherwise make it single-flight.
🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/desktop/src/window/DesktopWindow.ts around line 864:
`handleBackendConfigured` and `handleBackendReady` can concurrently create two main `BrowserWindow`s, leaving one orphaned when the later `setMain` overwrites it. Both paths call the non-atomic `createMainIfBackendReady`, which checks for an existing window before `createWindow` and `setMain`; serialize this creation or otherwise make it single-flight.
There was a problem hiding this comment.
Removed in 57a5d07. Only the existing handleBackendReady path creates the window, so this PR no longer adds a competing creation path.
There was a problem hiding this comment.
Sorry, I'm unable to act on this request because you do not have permissions within this repository.

What Changed
Packaged desktop builds now serve the bundled renderer directly through
t3code://app/instead of proxying those assets through the local backend HTTP server.Development builds continue proxying
t3code-dev://app/to Vite. The packaged protocol handler includes path containment checks, SPA fallback, and the existing content security policy.Backend readiness and window creation are unchanged.
Why
Packaged renderer assets do not need to come from the backend HTTP server. Serving them from the desktop bundle removes that unnecessary dependency while preserving the existing startup and readiness flow.
The renderer already receives explicit backend HTTP and WebSocket endpoints through desktop IPC.
Validation
vp test run apps/desktop/src/electron/ElectronProtocol.test.ts apps/desktop/src/app/DesktopEnvironment.test.ts(14 tests passed)vp run --filter @t3tools/desktop typecheckChecklist
Prepared with GPT-5.6 Codex through T3 Code.
Note
Medium Risk
Touches Electron custom-protocol handling and static file serving, including path containment and CSP. Incorrect sanitization could leak files or weaken renderer isolation.
Overview
Packaged desktop builds now load the UI from local files on
t3code://app/instead of proxying through the backend HTTP server. Dev still proxiest3code-dev://app/to Vite.Adds
registerDesktopFileProtocol, which serves assets fromrendererRootPath(serverRoot/apps/server/dist/client) with SPA fallback toindex.html. Requests are limited to hostapp, GET/HEAD, and paths contained under the renderer root. The existing CSP is still applied.ElectronProtocol.layernow depends onNodeServicesforFileSystem/Path.Reviewed by Cursor Bugbot for commit 57a5d07. Bugbot is set up for automated code reviews on this repo. Configure here.
Note
Serve packaged renderer from local disk in production desktop builds
DesktopAppnow registers a file protocol that serves renderer assets fromenvironment.rendererRootPathinstead of proxying to the backend origin; development behavior is unchanged.rendererRootPathtoDesktopEnvironment.Service, computed aspath.join(serverRoot, "apps/server/dist/client").registerDesktopFileProtocol,resolveRendererFilePath, andserveRendererFiletoElectronProtocol, serving static files with SPA fallback toindex.html, CSP headers, host/method enforcement, and path-traversal rejection.NodeServices.layerintoElectronProtocol.layerso file protocol handlers have FileSystem/Path access.resolveRendererFilePathin ElectronProtocol.ts rejects malformed or traversal paths by returningnull; verify the SPA fallback and content-type handling cover all expected renderer asset types.Macroscope summarized 57a5d07.