fix(desktop/dev): auto-inject NODE_EXTRA_CA_CERTS for portless TLS trust#646
Merged
jeevanpillay merged 2 commits intomainfrom May 6, 2026
Merged
fix(desktop/dev): auto-inject NODE_EXTRA_CA_CERTS for portless TLS trust#646jeevanpillay merged 2 commits intomainfrom
jeevanpillay merged 2 commits intomainfrom
Conversation
Electron's main process inherits Node's undici fetch as the global
`fetch`, which doesn't honor the macOS keychain. portless mints dev
HTTPS certs from a local root at ~/.portless/ca.pem, so any Node-side
fetch from desktop main against the dev aggregate (https://*.localhost)
fails the TLS handshake with SELF_SIGNED_CERT_IN_CHAIN. Concretely:
the PKCE exchange POST in apps/desktop/src/main/auth-flow.ts
(`/api/desktop/auth/exchange`) silently fails for any contributor
running `pnpm dev:desktop` from a fresh shell, with no diagnostic
beyond `auth_signin_failed{reason:"exchange_failed"}`.
Mirror the injection lightfast-dev's `proxy app-runtime` already does
for the next-dev child: when the resolved LIGHTFAST_APP_ORIGIN points
at a `*.localhost` host AND ~/.portless/ca.pem exists, set
NODE_EXTRA_CA_CERTS on the spawned Electron env. Respect any
pre-existing parent NODE_EXTRA_CA_CERTS so contributors with a custom
trust setup aren't overridden.
A more architecturally correct fix is to switch the main-process fetch
to Electron's net.fetch (Chromium net stack, trusts the OS keychain),
which would obviate this injection entirely. Tracked in
thoughts/shared/research/2026-05-06-desktop-exchange-tls-portless.md.
Verified --print across three cases:
- clean shell → NODE_EXTRA_CA_CERTS=~/.portless/ca.pem injected
- parent override set → parent value respected, not overridden
- production-ish origin → no-op (hostname is not *.localhost)
Standalone TLS reproducer from a strictly scrubbed env confirms the
underlying bug: node fetch against https://lightfast.localhost throws
SELF_SIGNED_CERT_IN_CHAIN without the env var, returns server bytes
with it.
Diagnoses the SELF_SIGNED_CERT_IN_CHAIN failure during PKCE exchange, documents the fix paths (NODE_EXTRA_CA_CERTS injection vs. switching to Electron net.fetch), and pairs with the script-level fix in this branch.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
1 Skipped Deployment
|
Contributor
|
Important Review skippedReview was skipped due to path filters ⛔ Files ignored due to path filters (2)
CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
✨ Simplify code
Comment |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Auto-inject
NODE_EXTRA_CA_CERTS=$HOME/.portless/ca.peminto the Electron child env whenLIGHTFAST_APP_ORIGINresolves to a*.localhosthost and the CA file exists. Mirrors the injectionlightfast-dev proxy app-runtimealready does for the next-dev child.Why
Electron main inherits Node's undici fetch as the global
fetch. Node's bundled CA list does not include portless's local root, so HTTPS calls from desktop main to the dev aggregate (https://*.localhost) fail TLS handshake withSELF_SIGNED_CERT_IN_CHAIN.Concrete failure: the PKCE exchange POST in
apps/desktop/src/main/auth-flow.ts(/api/desktop/auth/exchange) silently fails for any contributor runningpnpm dev:desktopfrom a fresh shell with noNODE_EXTRA_CA_CERTSexported, surfacing only asauth_signin_failed{reason:"exchange_failed"}with no other diagnostic.This was surfaced by PR #627 post-merge live verification (commit
bf1699fa5). Standalone reproducer from a strictly scrubbed env:Behavior
scripts/with-desktop-env.mjs --printresults across three cases:https://lightfast.localhostNODE_EXTRA_CA_CERTS=~/.portless/ca.pemNODE_EXTRA_CA_CERTS=/tmp/x.pemhttps://lightfast.localhost/tmp/x.pem(no override)https://app.lightfast.ai*.localhost)Also no-op when
~/.portless/ca.pemdoes not exist (contributor without portless installed).Future work
The architecturally correct fix is to switch the desktop main-process
fetch(currently only one call site,auth-flow.ts:248) to Electron'snet.fetch, which routes through Chromium's networking and trusts the macOS keychain. That would obviate this env injection entirely. Tracked inthoughts/shared/research/2026-05-06-desktop-exchange-tls-portless.md(added in this PR).Test plan
node scripts/with-desktop-env.mjs --printfrom clean shell → injectsNODE_EXTRA_CA_CERTSalready set in parent → respects parentLIGHTFAST_APP_ORIGIN=https://app.lightfast.ai→ no-opNODE_EXTRA_CA_CERTSexported reachesauth_signed_in(covered by PR 627 verification + this script change)🤖 Generated with Claude Code