win-ca

Fetches Windows Root CA certificates and wires them into Node.js's HTTPS trust store automatically.

Library
npm
v3.5.1
120stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
38/100Needs Attention
Development Activity0
Maintenance0
Community72
Maturity60
Momentum20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
53/100Fair
Architecture65
Code Quality50
Innovation60
Learning Curve35

win-ca solves a specific, painful problem: on Windows, Node.js ignores the OS’s own certificate store and relies on a small, hardcoded list of root CAs bundled at build time. That means any HTTPS request against an internally-issued, GPO-deployed, or otherwise enterprise-managed certificate fails with UNABLE_TO_VERIFY_LEAF_SIGNATURE, even though the same certificate is fully trusted by the browser and every other OS-aware application. win-ca reads certificates directly out of Windows’ Trusted Root Certification Authorities store and injects them into https.globalAgent.options.ca, so existing Node.js code needs no changes.

Under the hood it offers two fetch strategies: a native N-API addon (crypt32.cpp) that calls the Win32 CryptoAPI directly for speed, and a bundled standalone roots.exe utility invoked via child_process as a fallback for environments (older Node versions, Electron’s renderer/main process boundaries, ASAR-packed apps) where loading a native addon isn’t practical. Three entry points — win-ca, win-ca/fallback, and win-ca/api — expose the same certificate-fetching API but differ in whether they auto-inject into the global agent, force the non-native path, or stay fully manual and let the caller decide what to do with the certificates.

What You Get

  • A drop-in require('win-ca') that populates https.globalAgent.options.ca with the Windows Trusted Root store on load
  • A native N-API addon that reads certificates directly via the Win32 Crypt32 API for fast, in-process enumeration
  • A standalone roots.exe fallback path (invoked via child_process) for Electron, ASAR-packed apps, and older Node runtimes
  • A der2 conversion utility to render fetched certificates as DER, PEM, ASN.1, or node-forge X.509 objects
  • Sync, async (promise-based), and ES6-generator iteration modes over the same certificate stream
  • A companion VS Code extension that imports certificates into the shared Extension Host process

Common Use Cases

  • Node.js services behind a corporate proxy or GPO-managed root CA that need to trust internally-issued certificates without disabling TLS verification
  • Electron desktop apps distributed to Windows users that must trust enterprise-issued certificates without shipping a custom CA bundle
  • VS Code extensions that need to reach properly configured intranet HTTPS endpoints from a locked-down Windows machine
  • Windows-only CLI tools that call internal HTTPS APIs and can’t ask end users to manage CA bundles manually

Under The Hood

Architecture win-ca’s execution flow starts at src/index.ls (compiled to lib/index.ls at pretest time), which exposes a single configurable API function alongside compatibility passthroughs from v2.ls. Calling the API selects an “engine” — either n-api.ls (native Crypt32 addon fetch) or fallback.ls (subprocess-driven roots.exe) — based on Node version, Electron detection, and caller-supplied flags, then wraps the engine’s certificate stream in sync, promise-based async, or ES6-generator adapters depending on the async/generator options passed in. Deduplication (unique.ls), format conversion (der2.ls), optional disk persistence (save.ls), and optional https.globalAgent injection (inject.ls) are each layered on top of the raw engine stream as independent, composable steps rather than baked into the engines themselves, so swapping the native addon for the subprocess fallback changes nothing about how certificates are consumed downstream.

Tech Stack The library is authored in LiveScript (a CoffeeScript-like language compiling to JavaScript) and transpiled to plain JS via lsc in a pretest script before publishing or testing. The native fetch path is a small N-API C++ addon (n-api/crypt32.cpp) built with node-addon-api and linked against Windows’ crypt32 library; the fallback path ships a compiled C executable (n-api/roots.c) invoked as a subprocess. Runtime dependencies are minimal: node-forge for ASN.1/X.509 parsing, is-electron for environment detection, make-dir for filesystem setup, and split for parsing the subprocess’s line-delimited hex output. Tests run under Mocha with LiveScript and choma assertion helpers, and CI historically ran on Travis (Windows image, multiple Node versions) with a GitHub Actions workflow now handling scheduled root-certificate refreshes.

Code Quality Tests live under test/ and cover the DER conversion pipeline, hashing, injection, and both fetch engines using fixture certificates stored under test/pem; choma provides assertion syntax on top of Mocha. Error handling is largely implicit — LiveScript’s terse control flow favors early returns and optional chaining over explicit try/catch, and the native addon path can throw if N-API is unavailable, which the library catches internally at postinstall time but otherwise expects callers to handle. There is no TypeScript, no linter or formatter configuration beyond .editorconfig, and CI configuration is oriented around building and publishing the native binaries rather than gating merges on a broad test matrix.

API Design The public API is a single, densely-parameterized function (format, store, unique, ondata, onend, fallback, async, generator, inject, save) rather than a set of small, named methods, trading discoverability for flexibility — the README’s own framing (“full featured API… the only function with numerous parameters”) acknowledges this is a lot to hold in mind for what is usually a one-line require('win-ca') use case. In exchange, the three entry points (win-ca, win-ca/fallback, win-ca/api) give callers a clear escalation path from zero-config to fully manual, and the sync/async/generator symmetry means the same options object works across very different consumption styles without relearning the API.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search