Skip to content

CLI, Desktop: shim-init-node: add support for PQC TLS#15055

Open
newAM wants to merge 1 commit intolaurent22:devfrom
newAM:dev
Open

CLI, Desktop: shim-init-node: add support for PQC TLS#15055
newAM wants to merge 1 commit intolaurent22:devfrom
newAM:dev

Conversation

@newAM
Copy link
Copy Markdown

@newAM newAM commented Apr 9, 2026

This is a new feature, adding post-quantum resistant TLS key exchange to Joplin.

Reference this forum post for details: https://discourse.joplinapp.org/t/postquantum-safe/49540/1

I tested joplin-desktop on Linux (NixOS) with a webdav sync target behind nginx, using the nginx option ssl_ecdh_curve X25519MLKEM768; to enforce post-quantum key exchange.

I also did a wireshark capture and validated that X25519MLKEM768 is sent on the wire in a TLS ClientHello.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 9, 2026

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 9, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Updated HTTP agent configuration in the Node shim to use shim.httpAgent(url) instead of null when proxying is inactive. Added runtime detection for TLS ECDH curve support (X25519MLKEM768:X25519:P-256:P-384), threading the detected curve into both direct HTTP agents and proxy agent options.

Changes

Cohort / File(s) Summary
TLS configuration and HTTP agent setup
packages/lib/shim-init-node.ts
Added runtime TLS ECDH curve feature detection with fallback to 'auto'. Modified shim.fetch and shim.fetchBlob to use shim.httpAgent(url) instead of null for the agent option when proxying is inactive. Threaded detected ECDH curve value into both direct HTTP/HTTPS agent options and proxy agent (hpagent) configuration.

Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 error, 1 warning)

Check name Status Explanation Resolution
Pr Description Must Follow Guidelines ❌ Error PR description relies on external forum discussion rather than being self-contained, lacks explicit problem/solution/test sections, and includes no new unit tests for the PQC TLS feature. Update PR description with clear Problem, Solution, and Test Plan sections; add unit tests to shim-init-node.test.ts; verify testing on all supported platforms.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: adding support for post-quantum cryptography TLS to the shim-init-node module, which aligns with the changeset's implementation of PQC key exchange detection and configuration.
Description check ✅ Passed The description is directly related to the changeset, providing context about the new PQC TLS feature, referencing relevant discussion, and documenting testing performed on the implementation.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai bot added enhancement Feature requests and code enhancements security labels Apr 9, 2026
@newAM
Copy link
Copy Markdown
Author

newAM commented Apr 9, 2026

I have read the CLA Document and I hereby sign the CLA

github-actions bot added a commit that referenced this pull request Apr 9, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/lib/shim-init-node.ts (1)

704-733: ⚠️ Potential issue | 🟠 Major

Gate the PQC curve list behind runtime support.

ecdhCurve is part of Node/OpenSSL TLS negotiation. Whilst Electron 40.8.3 bundles OpenSSL 3.5.5 which supports X25519MLKEM768, the codebase specifies node: >=18, which may use older OpenSSL versions that do not recognise this hybrid group. If an incompatible runtime is used, new https.Agent(...) will fail and break the plain-HTTP fallback.

Additionally, hardcoding a fixed curve list replaces the adaptive default (auto, which delegates to runtime selection) with a static group list, reducing interoperability for future runtimes.

Recommended: detect OpenSSL/Node version support at runtime, only set this curve list when the hybrid group is available, or keep the default for unknown runtimes.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/lib/shim-init-node.ts` around lines 704 - 733, The code
unconditionally sets tlsEcdhCurve = 'X25519MLKEM768:...' and passes it as
ecdhCurve into new http.Agent/new https.Agent and proxyAgentConfig, which can
crash on runtimes whose OpenSSL doesn't support the hybrid group; instead detect
runtime support and only set ecdhCurve when available (or omit it to use the
default 'auto'). Implement a runtime check (e.g. inspect
process.versions.openssl or attempt a safe test creating a TLS context) and
conditionally assign tlsEcdhCurve or leave it undefined, then use that variable
in shim.httpAgent (inside shim.httpAgent_ initialization) and shim.proxyAgent
(proxyAgentConfig) so Agents are created without the unsupported ecdhCurve when
the runtime lacks support.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@packages/lib/shim-init-node.ts`:
- Around line 704-733: The code unconditionally sets tlsEcdhCurve =
'X25519MLKEM768:...' and passes it as ecdhCurve into new http.Agent/new
https.Agent and proxyAgentConfig, which can crash on runtimes whose OpenSSL
doesn't support the hybrid group; instead detect runtime support and only set
ecdhCurve when available (or omit it to use the default 'auto'). Implement a
runtime check (e.g. inspect process.versions.openssl or attempt a safe test
creating a TLS context) and conditionally assign tlsEcdhCurve or leave it
undefined, then use that variable in shim.httpAgent (inside shim.httpAgent_
initialization) and shim.proxyAgent (proxyAgentConfig) so Agents are created
without the unsupported ecdhCurve when the runtime lacks support.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c6e357d4-ecdb-4215-8f67-cb2feebc09df

📥 Commits

Reviewing files that changed from the base of the PR and between 9a180fc and b999b7a.

📒 Files selected for processing (1)
  • packages/lib/shim-init-node.ts

@newAM
Copy link
Copy Markdown
Author

newAM commented Apr 9, 2026

Took a stab at auto-detection based on the bot feedback. As far as I can determine there's no way to tell what node uses for curves when it is set to "auto". On my system "auto" is 'X25519:P-256:P-384'

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/lib/shim-init-node.ts`:
- Around line 586-587: The code assigns a single agent to requestOptions.agent
using resolvedProxyUrl/resolveProxyUrl and shim.proxyAgent/shim.httpAgent which
breaks cross-protocol redirects; replace this with an agents map on
requestOptions (requestOptions.agents = { http: <httpAgent>, https: <httpsAgent>
}) and populate each entry using the same proxy logic (use
shim.proxyAgent(url.href, resolvedProxyUrl) and shim.httpAgent(url.href) to
build the appropriate http and https agents) so follow-redirects can pick the
correct agent per protocol instead of reusing requestOptions.agent.
- Around line 531-533: The current assignment of options.agent uses a fixed
agent and breaks cross-protocol redirects; change options.agent to a function
callback that receives the request URL and returns the correct agent
per-redirect: if proxySettings.proxyEnabled and resolvedProxyUrl are set, call
shim.proxyAgent(url, resolvedProxyUrl) for that redirect URL, otherwise call
shim.httpAgent(url); update the code around resolvedProxyUrl, options.agent, and
the shim.fetchWithRetry call so fetchWithRetry receives this agent callback
instead of a single static agent.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d5f88408-0324-4427-91ef-985c29501781

📥 Commits

Reviewing files that changed from the base of the PR and between b999b7a and 48950a8.

📒 Files selected for processing (1)
  • packages/lib/shim-init-node.ts

Comment on lines 531 to 533
const resolvedProxyUrl = resolveProxyUrl(proxySettings.proxyUrl);
options.agent = (resolvedProxyUrl && proxySettings.proxyEnabled) ? shim.proxyAgent(url, resolvedProxyUrl) : null;
options.agent = (resolvedProxyUrl && proxySettings.proxyEnabled) ? shim.proxyAgent(url, resolvedProxyUrl) : shim.httpAgent(url);
return shim.fetchWithRetry(() => {
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.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n packages/lib/shim-init-node.ts | head -750 | tail -250

Repository: laurent22/joplin

Length of output: 10652


🏁 Script executed:

# Check the shim functions and agent creation
rg -A 10 "httpAgent|proxyAgent" packages/lib/shim-init-node.ts

Repository: laurent22/joplin

Length of output: 3126


🏁 Script executed:

# Check package.json for node-fetch version
cat packages/lib/package.json | grep -A 2 -B 2 "node-fetch"

Repository: laurent22/joplin

Length of output: 195


🌐 Web query:

node-fetch agent option callback function support documentation

💡 Result:

node-fetch does document support for an agent “callback” (i.e., a function) in fetch(url, options):

  • In the official node-fetch README, the agent option is documented as either an http(s).Agent instance or a function that returns an Agent. The README further explains that this function is called with the current URL (useful for redirects that switch between http: and https:). [1]
  • The TypeScript typings for node-fetch (DefinitelyTyped, as rendered by jsDocs) define it as:
    agent?: RequestOptions['agent'] | ((parsedUrl: URL) => RequestOptions['agent']); [2]

Docs: see the node-fetch README → API → Options → Custom Agent. [1]

Sources
[1] node-fetch README (GitHub) (github.com)
[2] @types/node-fetch RequestInit typings (jsDocs) (jsdocs.io)

Citations:


Use an agent callback to support cross-protocol redirects.

node-fetch supports passing a function for the agent option, which is called with each redirect URL to allow protocol-specific agent selection. The current fixed agent on Line 532 means an http: URL that redirects to https: keeps the agent selected for the original scheme, causing the request to potentially fail.

Suggested change
-		options.agent = (resolvedProxyUrl && proxySettings.proxyEnabled) ? shim.proxyAgent(url, resolvedProxyUrl) : shim.httpAgent(url);
+		options.agent = currentUrl => {
+			const currentUrlString = currentUrl.href ?? currentUrl.toString();
+			return (resolvedProxyUrl && proxySettings.proxyEnabled)
+				? shim.proxyAgent(currentUrlString, resolvedProxyUrl)
+				: shim.httpAgent(currentUrlString);
+		};
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const resolvedProxyUrl = resolveProxyUrl(proxySettings.proxyUrl);
options.agent = (resolvedProxyUrl && proxySettings.proxyEnabled) ? shim.proxyAgent(url, resolvedProxyUrl) : null;
options.agent = (resolvedProxyUrl && proxySettings.proxyEnabled) ? shim.proxyAgent(url, resolvedProxyUrl) : shim.httpAgent(url);
return shim.fetchWithRetry(() => {
const resolvedProxyUrl = resolveProxyUrl(proxySettings.proxyUrl);
options.agent = currentUrl => {
const currentUrlString = currentUrl.href ?? currentUrl.toString();
return (resolvedProxyUrl && proxySettings.proxyEnabled)
? shim.proxyAgent(currentUrlString, resolvedProxyUrl)
: shim.httpAgent(currentUrlString);
};
return shim.fetchWithRetry(() => {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/lib/shim-init-node.ts` around lines 531 - 533, The current
assignment of options.agent uses a fixed agent and breaks cross-protocol
redirects; change options.agent to a function callback that receives the request
URL and returns the correct agent per-redirect: if proxySettings.proxyEnabled
and resolvedProxyUrl are set, call shim.proxyAgent(url, resolvedProxyUrl) for
that redirect URL, otherwise call shim.httpAgent(url); update the code around
resolvedProxyUrl, options.agent, and the shim.fetchWithRetry call so
fetchWithRetry receives this agent callback instead of a single static agent.

Comment on lines 586 to +587
const resolvedProxyUrl = resolveProxyUrl(proxySettings.proxyUrl);
requestOptions.agent = (resolvedProxyUrl && proxySettings.proxyEnabled) ? shim.proxyAgent(url.href, resolvedProxyUrl) : null;
requestOptions.agent = (resolvedProxyUrl && proxySettings.proxyEnabled) ? shim.proxyAgent(url.href, resolvedProxyUrl) : shim.httpAgent(url.href);
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.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n packages/lib/shim-init-node.ts | sed -n '580,595p'

Repository: laurent22/joplin

Length of output: 907


🏁 Script executed:

rg -A 10 "const resolvedProxyUrl = resolveProxyUrl" packages/lib/shim-init-node.ts

Repository: laurent22/joplin

Length of output: 1324


🏁 Script executed:

rg "fetchBlob" packages/lib/shim-init-node.ts -B 5 -A 15

Repository: laurent22/joplin

Length of output: 1086


🏁 Script executed:

rg "requestOptions" packages/lib/shim-init-node.ts -A 5 -B 2

Repository: laurent22/joplin

Length of output: 1675


🏁 Script executed:

rg "http\.request\|https\.request" packages/lib/shim-init-node.ts -A 3 -B 2

Repository: laurent22/joplin

Length of output: 42


🌐 Web query:

follow-redirects library agents vs agent parameter options API

💡 Result:

In follow-redirects you can use either the standard Node.js agent option or the library-specific agents option:

  • agent (standard http/https option): a single agent instance used for the request (works fine if you know you’ll stay on one protocol). follow-redirects supports the normal http/https request options in addition to its own. [1]

  • agents (follow-redirects-specific): lets you specify the agent per protocol because HTTP and HTTPS use different agent classes, and redirects can switch protocols (e.g., http → https). Shape is:

    agents: { http?: http.Agent, https?: https.Agent }

    [1][2]

Example:

const { http, https } = require('follow-redirects');

https.request({
  url: 'http://example.com',
  agents: {
    http: new (require('http')).Agent({ keepAlive: true }),
    https: new (require('https')).Agent({ keepAlive: true }),
  },
});

Sources: [1] follow-redirects README options docs, [2] Type definitions for agents shape.


🏁 Script executed:

rg "shim\.httpAgent\s*=|shim\.proxyAgent\s*=" packages/lib/shim-init-node.ts -A 5 -B 1

Repository: laurent22/joplin

Length of output: 421


🏁 Script executed:

rg "httpAgent|proxyAgent" packages/lib/shim-init-node.ts | head -20

Repository: laurent22/joplin

Length of output: 805


🏁 Script executed:

rg "shim\.httpAgent\s*=" packages/lib/shim-init-node.ts -A 15

Repository: laurent22/joplin

Length of output: 443


🏁 Script executed:

rg "shim\.proxyAgent\s*=" packages/lib/shim-init-node.ts -A 25

Repository: laurent22/joplin

Length of output: 1003


Use agents for follow-redirects downloads.

On line 587, a single agent is assigned to requestOptions.agent based on the initial URL scheme. However, follow-redirects requires an agents map to handle cross-protocol redirects correctly—when an HTTP download redirects to HTTPS (or vice versa), the current code reuses the wrong agent type. The library expects:

requestOptions.agents = { http: httpAgent, https: httpsAgent }

This ensures each protocol gets its appropriate agent instance during the redirect chain.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/lib/shim-init-node.ts` around lines 586 - 587, The code assigns a
single agent to requestOptions.agent using resolvedProxyUrl/resolveProxyUrl and
shim.proxyAgent/shim.httpAgent which breaks cross-protocol redirects; replace
this with an agents map on requestOptions (requestOptions.agents = { http:
<httpAgent>, https: <httpsAgent> }) and populate each entry using the same proxy
logic (use shim.proxyAgent(url.href, resolvedProxyUrl) and
shim.httpAgent(url.href) to build the appropriate http and https agents) so
follow-redirects can pick the correct agent per protocol instead of reusing
requestOptions.agent.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
packages/lib/shim-init-node.ts (2)

716-730: ecdhCurve only applies to HTTPS agent.

The ecdhCurve option is a TLS setting and has no effect on http.Agent. While this is harmless (the option is silently ignored), for clarity consider applying it only to the HTTPS agent.

Suggested fix
 shim.httpAgent = url => {
   if (!shim.httpAgent_) {
-    const AgentSettings = {
+    const baseSettings = {
       keepAlive: true,
       maxSockets: 1,
       keepAliveMsecs: 5000,
-      ecdhCurve: tlsEcdhCurve,
     };
     shim.httpAgent_ = {
-      http: new http.Agent(AgentSettings),
-      https: new https.Agent(AgentSettings),
+      http: new http.Agent(baseSettings),
+      https: new https.Agent({ ...baseSettings, ecdhCurve: tlsEcdhCurve }),
     };
   }
   return url.startsWith('https') ? shim.httpAgent_.https : shim.httpAgent_.http;
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/lib/shim-init-node.ts` around lines 716 - 730, The current
shim.httpAgent creates a single AgentSettings object including ecdhCurve and
uses it for both http and https agents; move ecdhCurve into the HTTPS-only
options so the http.Agent is created without TLS-specific settings. Concretely,
keep common options (keepAlive, maxSockets, keepAliveMsecs) in a shared object
(used to construct shim.httpAgent_.http), then create a separate httpsOptions
that spreads the common options and adds ecdhCurve, and use that to construct
shim.httpAgent_.https in the shim.httpAgent factory.

704-714: Minor typo in comment.

Line 706 has "Not supported on by all" which should be "Not supported by all".

The runtime detection approach for PQC TLS support is well-designed—it gracefully falls back to 'auto' when the curve isn't available on older Node.js/OpenSSL versions.

Suggested fix
 // X25519MLKEM768 is a post-quantum cryptography key exchange, details:
 // https://developers.cloudflare.com/ssl/post-quantum-cryptography/
-// Not supported on by all SSL stacks and versions, detect support at runtime.
+// Not supported by all SSL stacks and versions, detect support at runtime.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/lib/shim-init-node.ts` around lines 704 - 714, Typo in the comment
above the TLS PQC curve detection: change "Not supported on by all SSL stacks
and versions, detect support at runtime." to "Not supported by all SSL stacks
and versions, detect support at runtime." in the block that references
tls.createSecureContext and the tlsEcdhCurve variable so the comment reads
correctly while leaving the runtime detection logic (tls.createSecureContext and
tlsEcdhCurve assignment/fallback) unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/lib/shim-init-node.ts`:
- Around line 716-730: The current shim.httpAgent creates a single AgentSettings
object including ecdhCurve and uses it for both http and https agents; move
ecdhCurve into the HTTPS-only options so the http.Agent is created without
TLS-specific settings. Concretely, keep common options (keepAlive, maxSockets,
keepAliveMsecs) in a shared object (used to construct shim.httpAgent_.http),
then create a separate httpsOptions that spreads the common options and adds
ecdhCurve, and use that to construct shim.httpAgent_.https in the shim.httpAgent
factory.
- Around line 704-714: Typo in the comment above the TLS PQC curve detection:
change "Not supported on by all SSL stacks and versions, detect support at
runtime." to "Not supported by all SSL stacks and versions, detect support at
runtime." in the block that references tls.createSecureContext and the
tlsEcdhCurve variable so the comment reads correctly while leaving the runtime
detection logic (tls.createSecureContext and tlsEcdhCurve assignment/fallback)
unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 06c8a790-215e-43cb-b1f5-f19ff35adfa8

📥 Commits

Reviewing files that changed from the base of the PR and between 48950a8 and 5cfca1b.

📒 Files selected for processing (1)
  • packages/lib/shim-init-node.ts

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
packages/lib/shim-init-node.ts (2)

716-730: Separate ecdhCurve configuration for https.Agent only.

The ecdhCurve option is a TLS-specific setting that only applies to https.Agent. Passing it to http.Agent is semantically incorrect since plain HTTP connections don't use TLS. Although Node.js silently ignores unknown options (preventing runtime errors), this configuration is misleading.

Suggested refactor
 shim.httpAgent = url => {
   if (!shim.httpAgent_) {
-    const AgentSettings = {
+    const baseSettings = {
       keepAlive: true,
       maxSockets: 1,
       keepAliveMsecs: 5000,
-      ecdhCurve: tlsEcdhCurve,
     };
     shim.httpAgent_ = {
-      http: new http.Agent(AgentSettings),
-      https: new https.Agent(AgentSettings),
+      http: new http.Agent(baseSettings),
+      https: new https.Agent({ ...baseSettings, ecdhCurve: tlsEcdhCurve }),
     };
   }
   return url.startsWith('https') ? shim.httpAgent_.https : shim.httpAgent_.http;
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/lib/shim-init-node.ts` around lines 716 - 730, The AgentSettings
currently includes the TLS-only option tlsEcdhCurve and is passed to both
http.Agent and https.Agent via shim.httpAgent_ in shim.httpAgent; remove
ecdhCurve from the settings used for the plain http.Agent and only include
ecdhCurve (tlsEcdhCurve) when constructing the https.Agent. Concretely, build a
base AgentSettings for both agents (keepAlive, maxSockets, keepAliveMsecs) and a
separate httpsAgentSettings that spreads base and adds ecdhCurve, then
instantiate shim.httpAgent_.http with new http.Agent(base) and
shim.httpAgent_.https with new https.Agent(httpsAgentSettings) so tlsEcdhCurve
is not passed to http.Agent.

732-752: Consider conditionally applying ecdhCurve only to HttpsProxyAgent.

The ecdhCurve option is only meaningful when establishing a TLS connection to the target server. When HttpProxyAgent is used (for HTTP server URLs), this option will not be utilised, so it can be omitted from that configuration to improve clarity.

For consistency, separate the configuration objects:

Suggested change
 shim.proxyAgent = (serverUrl: string, proxyUrl: string) => {
-  const proxyAgentConfig = {
+  const baseProxyConfig = {
     keepAlive: true,
     maxSockets: proxySettings.maxConcurrentConnections,
     keepAliveMsecs: 5000,
     proxy: proxyUrl,
     timeout: proxySettings.proxyTimeout * 1000,
-    ecdhCurve: tlsEcdhCurve,
   };
+  const httpsProxyConfig = { ...baseProxyConfig, ecdhCurve: tlsEcdhCurve };

   // Based on https://github.com/delvedor/hpagent#usage
   if (!isUrlHttps(proxyUrl) && !isUrlHttps(serverUrl)) {
-    return new HttpProxyAgent(proxyAgentConfig);
+    return new HttpProxyAgent(baseProxyConfig);
   } else if (isUrlHttps(proxyUrl) && !isUrlHttps(serverUrl)) {
-    return new HttpProxyAgent(proxyAgentConfig);
+    return new HttpProxyAgent(baseProxyConfig);
   } else if (!isUrlHttps(proxyUrl) && isUrlHttps(serverUrl)) {
-    return new HttpsProxyAgent(proxyAgentConfig);
+    return new HttpsProxyAgent(httpsProxyConfig);
   } else {
-    return new HttpsProxyAgent(proxyAgentConfig);
+    return new HttpsProxyAgent(httpsProxyConfig);
   }
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/lib/shim-init-node.ts` around lines 732 - 752, The proxyAgent
implementation in shim.proxyAgent currently always includes ecdhCurve
(tlsEcdhCurve) in proxyAgentConfig even when returning an HttpProxyAgent; update
shim.proxyAgent to build two config objects (or clone and adjust): one config
for HttpProxyAgent (keepAlive, maxSockets from
proxySettings.maxConcurrentConnections, keepAliveMsecs, proxy, timeout) and a
separate config for HttpsProxyAgent that includes the same fields plus
ecdhCurve: tlsEcdhCurve; then return HttpProxyAgent with the HTTP config when
serverUrl is HTTP and/or proxyUrl is HTTP and return HttpsProxyAgent with the
HTTPS config when serverUrl is HTTPS (or proxy requires TLS).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/lib/shim-init-node.ts`:
- Around line 716-730: The AgentSettings currently includes the TLS-only option
tlsEcdhCurve and is passed to both http.Agent and https.Agent via
shim.httpAgent_ in shim.httpAgent; remove ecdhCurve from the settings used for
the plain http.Agent and only include ecdhCurve (tlsEcdhCurve) when constructing
the https.Agent. Concretely, build a base AgentSettings for both agents
(keepAlive, maxSockets, keepAliveMsecs) and a separate httpsAgentSettings that
spreads base and adds ecdhCurve, then instantiate shim.httpAgent_.http with new
http.Agent(base) and shim.httpAgent_.https with new
https.Agent(httpsAgentSettings) so tlsEcdhCurve is not passed to http.Agent.
- Around line 732-752: The proxyAgent implementation in shim.proxyAgent
currently always includes ecdhCurve (tlsEcdhCurve) in proxyAgentConfig even when
returning an HttpProxyAgent; update shim.proxyAgent to build two config objects
(or clone and adjust): one config for HttpProxyAgent (keepAlive, maxSockets from
proxySettings.maxConcurrentConnections, keepAliveMsecs, proxy, timeout) and a
separate config for HttpsProxyAgent that includes the same fields plus
ecdhCurve: tlsEcdhCurve; then return HttpProxyAgent with the HTTP config when
serverUrl is HTTP and/or proxyUrl is HTTP and return HttpsProxyAgent with the
HTTPS config when serverUrl is HTTPS (or proxy requires TLS).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 871bd771-16dd-47d9-b9c9-f98823299a95

📥 Commits

Reviewing files that changed from the base of the PR and between 5cfca1b and c55c764.

📒 Files selected for processing (2)
  • packages/lib/shim-init-node.ts
  • packages/tools/cspell/dictionary2.txt
✅ Files skipped from review due to trivial changes (1)
  • packages/tools/cspell/dictionary2.txt

@mrjo118
Copy link
Copy Markdown
Contributor

mrjo118 commented Apr 10, 2026

@newAM Thank you for your submission. Please could you elaborate on the testing which you have done for this code change in the PR description?

In particular, I'm wondering whether testing has actually been done on one of the mobile apps (eg. Android), or if you've assumed it would work because it worked on the Linux app

@newAM
Copy link
Copy Markdown
Author

newAM commented Apr 10, 2026

Done! I did not test Android, only the joplin-desktop electron application on Linux.

@mrjo118
Copy link
Copy Markdown
Contributor

mrjo118 commented Apr 10, 2026

Done! I did not test Android, only the joplin-desktop electron application on Linux.

If you don't want to go through all the process of building the mobile app, I can build an apk with your change for you (using the Joplin dev signing certificate). Would you be willing to test if the PQC TLS works on Android on a spare device / Android emulator if I provide the apk for you?

It doesn't matter so much if it doesn't actually work on Android, but I think it would be good to update the platforms which the fix addresses in the PR title accordingly if not, otherwise it is going to go in the changelog for all platforms and could be misleading.

@newAM newAM changed the title All: shim-init-node: add support for PQC TLS CLI, Desktop: shim-init-node: add support for PQC TLS Apr 10, 2026
@newAM
Copy link
Copy Markdown
Author

newAM commented Apr 10, 2026

Took some wrangling, but I built it on Android, I could not get it working. The Android app was sending x25519, secp256r1, secp384r1.

I tried some changes to get this working for Android, but the APIs and call stack to the underlying system TLS libraries requires a lot of background knowledge that I do not yet have.

I updated the PR title to correctly reflect the platforms this works on. Mobile is going to take more scrutiny.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/lib/shim-init-node.ts (1)

732-752: ⚠️ Potential issue | 🔴 Critical

Remove ecdhCurve from proxyAgentConfig or verify TLS configuration reaches the target server.

The hpagent library (v1.2.0) does not support passing ecdhCurve through to the underlying tls.connect() call for the target server. The library's proxyConnectOptions is limited to proxy tunnel TLS setup, not target server TLS configuration. Including ecdhCurve in proxyAgentConfig will not configure PQC curves for HTTPS target servers and should be removed unless hpagent gains this capability.

Additionally, ecdhCurve is irrelevant for HttpProxyAgent (HTTP targets do not use TLS).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/lib/shim-init-node.ts` around lines 732 - 752, The proxyAgent
function currently injects tlsEcdhCurve into proxyAgentConfig which hpagent (and
HttpProxyAgent) doesn't forward to target-server TLS; remove the ecdhCurve
property from proxyAgentConfig and ensure you only set TLS-specific options when
constructing an HttpsProxyAgent if hpagent adds support — otherwise omit
ecdhCurve entirely; update shim.proxyAgent so HttpProxyAgent never receives
ecdhCurve and HttpsProxyAgent only receives options hpagent actually forwards
(relying on proxySettings and tlsEcdhCurve only if you verify hpagent exposes
target TLS options).
🧹 Nitpick comments (1)
packages/lib/shim-init-node.ts (1)

718-727: ecdhCurve only applies to HTTPS connections.

The ecdhCurve option is a TLS setting and only meaningful for https.Agent. Applying it to http.Agent (Line 725) has no effect since HTTP doesn't use TLS. While Node.js silently ignores unknown options, separating the configurations would be cleaner.

Suggested change
 	shim.httpAgent = url => {
 		if (!shim.httpAgent_) {
-			const AgentSettings = {
+			const baseSettings = {
 				keepAlive: true,
 				maxSockets: 1,
 				keepAliveMsecs: 5000,
-				ecdhCurve: tlsEcdhCurve,
 			};
 			shim.httpAgent_ = {
-				http: new http.Agent(AgentSettings),
-				https: new https.Agent(AgentSettings),
+				http: new http.Agent(baseSettings),
+				https: new https.Agent({ ...baseSettings, ecdhCurve: tlsEcdhCurve }),
 			};
 		}
 		return url.startsWith('https') ? shim.httpAgent_.https : shim.httpAgent_.http;
 	};
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/lib/shim-init-node.ts` around lines 718 - 727, The AgentSettings
object is passing the TLS-specific tlsEcdhCurve into both http.Agent and
https.Agent via shim.httpAgent_, but ecdhCurve only applies to TLS/HTTPS; update
the initialization so http.Agent receives a settings object without ecdhCurve
and https.Agent receives one that includes ecdhCurve (e.g., split into
commonSettings and tlsSettings or create AgentSettingsHttp and
AgentSettingsHttps), keeping the other properties (keepAlive, maxSockets,
keepAliveMsecs) unchanged and use tlsEcdhCurve only when constructing
https.Agent.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@packages/lib/shim-init-node.ts`:
- Around line 732-752: The proxyAgent function currently injects tlsEcdhCurve
into proxyAgentConfig which hpagent (and HttpProxyAgent) doesn't forward to
target-server TLS; remove the ecdhCurve property from proxyAgentConfig and
ensure you only set TLS-specific options when constructing an HttpsProxyAgent if
hpagent adds support — otherwise omit ecdhCurve entirely; update shim.proxyAgent
so HttpProxyAgent never receives ecdhCurve and HttpsProxyAgent only receives
options hpagent actually forwards (relying on proxySettings and tlsEcdhCurve
only if you verify hpagent exposes target TLS options).

---

Nitpick comments:
In `@packages/lib/shim-init-node.ts`:
- Around line 718-727: The AgentSettings object is passing the TLS-specific
tlsEcdhCurve into both http.Agent and https.Agent via shim.httpAgent_, but
ecdhCurve only applies to TLS/HTTPS; update the initialization so http.Agent
receives a settings object without ecdhCurve and https.Agent receives one that
includes ecdhCurve (e.g., split into commonSettings and tlsSettings or create
AgentSettingsHttp and AgentSettingsHttps), keeping the other properties
(keepAlive, maxSockets, keepAliveMsecs) unchanged and use tlsEcdhCurve only when
constructing https.Agent.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 35a62a19-ead4-4b7c-8876-5abeaa9a7fa5

📥 Commits

Reviewing files that changed from the base of the PR and between c55c764 and 5e3e70a.

📒 Files selected for processing (1)
  • packages/lib/shim-init-node.ts

Comment thread packages/lib/shim-init-node.ts Outdated
@laurent22
Copy link
Copy Markdown
Owner

Please let me know when you feel this is ready to merge. I guess we could add the change as part of 3.7 pre-release to get additional feedback

@laurent22 laurent22 added the v3.7 label Apr 14, 2026
@newAM
Copy link
Copy Markdown
Author

newAM commented Apr 15, 2026

Please let me know when you feel this is ready to merge. I guess we could add the change as part of 3.7 pre-release to get additional feedback

I think it is good to go, everything works as expected when viewed from wireshark.
I'm not that experienced with typescript, but the code changes are small, and I addressed your feedback (hopefully in the right way).

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

Labels

enhancement Feature requests and code enhancements security v3.7

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants