Skip to content

🐛 fix(desktop): use Electron net.fetch for remote server requests#13400

Merged
Innei merged 2 commits into
lobehub:canaryfrom
djthread:self-signed-cert-fix
Apr 13, 2026
Merged

🐛 fix(desktop): use Electron net.fetch for remote server requests#13400
Innei merged 2 commits into
lobehub:canaryfrom
djthread:self-signed-cert-fix

Conversation

@djthread

@djthread djthread commented Mar 30, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Switch from Node.js fetch() to Electron's net.fetch() for all HTTP requests from the desktop app to user-configured remote servers
  • Node.js fetch uses a bundled Mozilla CA list and ignores the OS certificate store, so self-hosted instances behind a reverse proxy with a private CA (e.g. Caddy, mkcert, corporate PKI) silently fail every request — TLS errors are swallowed and the user sees only a timeout
  • net.fetch uses Chromium's networking stack, which respects the macOS Keychain, Windows Certificate Store, and system CA bundles on Linux

These changes allowed me to connect (from macos) to my self-hosted lobehub server with a self-signed cert.

I did think the app would pop up and take focus, but it did not. It did log in and appear to work, however. Without these changes, the browser says Authentication Successful but the desktop app just times out.

Affected code paths

File Request
AuthCtr.ts OIDC handoff polling + token exchange
RemoteServerConfigCtr.ts Token refresh
LocalFileCtr.ts Skill package download
BackendProxyProtocolManager.ts Backend API proxy

The network proxy tester (tester.ts) intentionally stays on undici's
fetch because it requires the dispatcher option for proxy agent testing.

Root cause

Self-hosted desktop auth (Connect → enter server URL → browser login) never completes: the handoff record is created server-side but the desktop app's polling requests fail at the TLS handshake because Node.js doesn't trust the host's CA certificate. The error is caught and discarded in a logger.debug() call, so the app silently spins until the 2-minute timeout.

Fixes #9684, fixes #13128

Test plan

  • Self-hosted instance with private CA cert trusted at OS level
  • Desktop app → Connect → enter server URL → browser login → app receives callback
  • Token refresh works after initial auth
  • Skill package install from self-hosted server works
  • Official cloud (lobehub.com) still works (public CA, no change in behavior)

@vercel

vercel Bot commented Mar 30, 2026

Copy link
Copy Markdown

@djthread is attempting to deploy a commit to the LobeHub OSS Team on Vercel.

A member of the Team first needs to authorize it.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We've reviewed this pull request using the Sourcery rules engine

@lobehubbot

Copy link
Copy Markdown
Member

@ONLY-yours @tjx666 - This is a desktop platform fix switching from Node.js fetch() to Electron's net.fetch() for remote server requests (affects auth/OIDC handoff, token refresh, and backend proxy). All changes are in apps/desktop/. Please take a look.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 48c9357707

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread apps/desktop/src/main/utils/net-fetch.ts
@djthread djthread force-pushed the self-signed-cert-fix branch from 48c9357 to d0f5a75 Compare March 30, 2026 19:10
@djthread

Copy link
Copy Markdown
Contributor Author

The tests stub global.fetch but the electron mock has no net object. The last commit makes net-fetch.ts fall back to globalThis.fetch when net is unavailable (tests).

@Innei Innei added the trigger:build-desktop Trigger Desktop build label Apr 10, 2026
@codecov

codecov Bot commented Apr 11, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 66.56%. Comparing base (e0f97c4) to head (44ab3f8).
⚠️ Report is 5 commits behind head on canary.

Additional details and impacted files
@@            Coverage Diff            @@
##           canary   #13400     +/-   ##
=========================================
  Coverage   66.56%   66.56%             
=========================================
  Files        2022     2022             
  Lines      171262   171262             
  Branches    19915    16654   -3261     
=========================================
+ Hits       113996   113998      +2     
+ Misses      57141    57139      -2     
  Partials      125      125             
Flag Coverage Δ
app 58.61% <ø> (+<0.01%) ⬆️
database 92.49% <ø> (ø)
packages/agent-runtime 79.72% <ø> (ø)
packages/context-engine 83.33% <ø> (ø)
packages/conversation-flow 92.36% <ø> (ø)
packages/file-loaders 87.02% <ø> (ø)
packages/memory-user-memory 74.74% <ø> (ø)
packages/model-bank 99.86% <ø> (ø)
packages/model-runtime 84.20% <ø> (ø)
packages/prompts 69.24% <ø> (ø)
packages/python-interpreter 92.90% <ø> (ø)
packages/ssrf-safe-fetch 0.00% <ø> (ø)
packages/utils 90.14% <ø> (ø)
packages/web-crawler 88.66% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

Components Coverage Δ
Store 65.70% <ø> (ø)
Services 52.19% <ø> (ø)
Server 66.31% <ø> (+<0.01%) ⬆️
Libs 52.83% <ø> (ø)
Utils 91.07% <ø> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@Innei

Innei commented Apr 12, 2026

Copy link
Copy Markdown
Member

@djthread Test failed, plz take a look

@djthread djthread force-pushed the self-signed-cert-fix branch from d0f5a75 to 44ab3f8 Compare April 12, 2026 06:34
@djthread

djthread commented Apr 12, 2026

Copy link
Copy Markdown
Contributor Author

Thanks, @Innei !

Two categories of test breakage patched in this commit:

E2E selectors (e2e/src/steps/page/): The "New Page" button in the Pages sidebar is rendered by @lobehub/ui's ActionIcon, which outputs <div role="button"> rather than a native <button>. The old selectors (button:has(svg.lucide-square-pen), input[placeholder*="Search"]) were both dead — the first matched no element, and the search bar is a click-handler NavItem, not an <input>. Fixed to [role="button"]:has(svg.lucide-square-pen).

Unit test mocks (apps/desktop/.../LocalFileCtr.test.ts, RemoteServerConfigCtr.test.ts): The net.fetch change routes HTTP calls through Electron's networking stack instead of globalThis.fetch. The tests were patching global.fetch / using vi.stubGlobal, which no longer intercepted anything. Fixed by hoisting the fetch mock and pointing vi.mock('@/utils/net-fetch', ...) at it directly.

@Innei

Innei commented Apr 12, 2026

Copy link
Copy Markdown
Member

E2E selectors: This is a common error that often occurs; retrying might resolve it. Here, there is no need for any changes; you just need to fix the unit test you are involved with. Thx.

Thanks, @Innei !

Two categories of test breakage patched in this commit:

E2E selectors (e2e/src/steps/page/): The "New Page" button in the Pages sidebar is rendered by @lobehub/ui's ActionIcon, which outputs <div role="button"> rather than a native <button>. The old selectors (button:has(svg.lucide-square-pen), input[placeholder*="Search"]) were both dead — the first matched no element, and the search bar is a click-handler NavItem, not an <input>. Fixed to [role="button"]:has(svg.lucide-square-pen).

Unit test mocks (apps/desktop/.../LocalFileCtr.test.ts, RemoteServerConfigCtr.test.ts): The net.fetch change routes HTTP calls through Electron's networking stack instead of globalThis.fetch. The tests were patching global.fetch / using vi.stubGlobal, which no longer intercepted anything. Fixed by hoisting the fetch mock and pointing vi.mock('@/utils/net-fetch', ...) at it directly.

Both LocalFileCtr and RemoteServerConfigCtr tests were patching
global.fetch / stubGlobal, which no longer intercepts calls now that
the controllers route through Electron's net.fetch via @/utils/net-fetch.
Hoist the fetch mock and point vi.mock('@/utils/net-fetch') at it directly.
@djthread djthread force-pushed the self-signed-cert-fix branch from 44ab3f8 to 2492c24 Compare April 12, 2026 18:38
@djthread

Copy link
Copy Markdown
Contributor Author

@Innei Ty again. Done.

@Innei

Innei commented Apr 13, 2026

Copy link
Copy Markdown
Member

@codex Will this modification cause issues with exception handling for requests?

@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create an environment for this repo.

@Innei Innei merged commit b857ae6 into lobehub:canary Apr 13, 2026
2 of 3 checks passed
@lobehubbot

Copy link
Copy Markdown
Member

❤️ Great PR @djthread ❤️

The growth of project is inseparable from user feedback and contribution, thanks for your contribution! If you are interesting with the lobehub developer community, please join our discord and then dm @arvinxx or @canisminor1990. They will invite you to our private developer channel. We are talking about the lobe-chat development or sharing ai newsletter around the world.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

trigger:build-desktop Trigger Desktop build

Projects

None yet

3 participants