macOS: explicit `tls.getCACertificates("system")` call adds 5+ seconds to every CLI invocation
- Ngôn ngữ chính
- Shell
- Star
- 11.2k
- Fork
- 1.9k
- Merge trung bình
- 14 giờ 16 phút
- Pull request đã merge (30 ngày)
- 6
Mô tả
### Describe the bug
GitHub Copilot CLI's CA loader calls `tls.getCACertificates("system")` on every invocation in addition to `"bundled"` and `"extra"`. On macOS, that call iterates every keychain cert and invokes `SecTrustEvaluateWithError` synchronously via XPC to `trustd` — which (because of a Node bug, see References) uses a revocation-enabled trust policy and triggers OCSP/CRL fetches per cert. On machines with any network-flow filter (corporate NetworkExtension, ZeroTier, etc.), each fetch pays a per-flow cost and totals **5-10 seconds of latency on every CLI invocation** — even sub-commands like `--version` that don't make HTTPS requests inherit the cost because the call happens during SEA startup.
The `"system"` arg is also functionally redundant with `"bundled"` for `*.github.com` / `*.githubcopilot.com` endpoints. See related #869 (which reports a separate failure caused by the duplicate-certs concatenation from this same code).
### Affected version
GitHub Copilot CLI 1.0.48 (also reproduced on 1.0.39). macOS 26.4.1, Apple Silicon. Node 24.15.0 inside the SEA.
### Steps to reproduce the behavior
```bash
$ time copilot --allow-all-tools -p "reply with exactly: ack"
ack
real 0m12.43s
user 0m1.98s
sys 0m0.17s
```
Wall-clock vs user CPU mismatch confirms it's I/O wait, not CPU work. `sample(1)` on the grandchild SEA process during the wait:
```
node::crypto::GetSystemCACertificates
node::crypto::ReadMacOSKeychainCertificates
node::crypto::IsCertificateTrustedForPolicy
node::crypto::IsCertificateTrustValid
SecTrustEvaluateWithError
SecTrustEvaluateIfNecessary
securityd_send_sync_and_do ← synchronous XPC to trustd
xpc_connection_send_message_with_reply_sync
mach_msg2_trap
```
Isolated reproduction with standalone Node 24 on the same machine:
```bash
$ node -e 'const t0=process.hrtime.bigint();
require("tls").getCACertificates("system");
console.log(`${(Number(process.hrtime.bigint()-t0)/1e6).toFixed(0)}ms`)'
5287ms
$ node -e 'const t0=process.hrtime.bigint();
require("tls").getCACertificates("bundled");
console.log(`${(Number(process.hrtime.bigint()-t0)/1e6).toFixed(0)}ms`)'
0.1ms
```
HTTPS request to `api.github.com`:
- default (bundled): **53ms**
- `node --use-bundled-ca …`: **103ms**
- `node --use-system-ca …`: **5093ms** — 96× slower
### Expected behavior
CLI startup should not pay multi-second latency for a CA loader that produces certs equivalent to the bundled Mozilla store.
### Root cause in copilot-cli source
Decoded from `app.js` (npm-loader fallback; the SEA's bundled JS contains the same logic):
```js
function buildCAList() {
const envCerts = ["NODE_EXTRA_CA_CERTS","SSL_CERT_FILE","CURL_CA_BUNDLE"].flatMap(readEnvCAFile);
return typeof tls.getCACertificates === "function"
? [...envCerts,
...tls.getCACertificates(), // default (bundled in Node 24+)
...tls.getCACertificates("system"), // ← THIS LINE
...tls.getCACertificates("bundled"), // bundled (already implied above)
...tls.getCACertificates("extra")]
: [...envCerts, ...tls.rootCertificates];
}
```
### Proposed fix
Remove the `tls.getCACertificates("system")` line. One-line patch. If retaining the option for users with private CAs is desired, gate it behind a `COPILOT_USE_SYSTEM_CA=1` env var defaulting OFF. The current always-on cost is borne by every macOS user even when they don't need it. This also resolves the duplicate-certs issue tracked at #869.
### Why this user sees the upper end
My login keychain had 1589 certs (mostly auto-imported S/MIME contact certs from Mail.app over 20 years). After pruning 1478 long-expired certs, the call dropped from ~10s to ~5s — the floor on this machine, set by ~270 still-valid certs each costing ~20 ms in `SecTrustEvaluateWithError`. ZeroTier's `feth1960` virtual interface and `zerotier-one` daemon further amplify per-flow cost for the trustd OCSP traffic.
Users without a flow filter and a smaller keychain see a smaller version of this same tax (1-2s baseline, per anthropics/claude-code#53660's measurements).
### Related
- **anthropics/claude-code#53660** — same Node-side root cause analyzed in detail (revocation-enabled trust policy in `SecTrustEvaluateWithError`). Their workaround was to ship a `CLAUDE_CODE_CERT_STORE=bundled` env var users can set.
- **#1250** — Windows variant of the same call: throws "X509 to PEM conversion" instead of slow execution. Different symptom, same offending line.
- **#869** — duplicate certs from this same `bundled + system` concatenation cause `Failed to list available models` for some users.
- **nodejs/node#58990** — Node tracking issue for custom CA support; needs a sub-issue for the revocation-policy perf bug specifically, which I'm filing separately.
Hướng dẫn đóng góp
Hướng nghiên cứu
Bắt đầu trong hàm buildCAList của app.js đã được giải mã và kiểm tra các lệnh gọi tls.getCACertificates, đặc biệt là lệnh gọi đến kho hệ thống. Xóa tra cứu hệ thống luôn bật, sau đó xác minh rằng việc khởi động CLI không còn gây ra độ trễ vài giây trên macOS và rằng hành vi CA được đóng gói vẫn khả dụng.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Đánh giá
- Công nghệ
- macos, node.js
- Lĩnh vực
- cli, operating-systems, performance
- Loại issue
- Lỗi
- Độ khó
- 1/5
- Thời gian dự kiến
- Dưới một giờ
- Mức độ hoạt động
- Ít trao đổi
- Độ rõ ràng
- Đặc tả rõ ràng
- Mức phù hợp với người mới
- 55/100