Bug
installCertificateAuthority in src/certificate-authority.ts persists the root CA cert and key to their canonical paths before attempting to add the CA to the OS trust store. If addToTrustStores fails or is declined (e.g. the user clicks "No" on the Windows Security Warning), the on-disk state remains, and certificateFor in src/index.ts then skips re-installation on every subsequent run because its guard is !existsSync(rootCAKeyPath). The browser stays broken with NET::ERR_CERT_AUTHORITY_INVALID, and devcert never re-prompts.
Current flow
In src/certificate-authority.ts:
generateKey(rootKeyPath); // tmp key
openssl(['req', '-new', '-x509', /*...*/, '-out', rootCACertPath, /*...*/]); // ← PERSISTS rootCACertPath
await saveCertificateAuthorityCredentials(rootKeyPath); // ← PERSISTS rootCAKeyPath
await currentPlatform.addToTrustStores(rootCACertPath, options); // ← may fail/be declined silently
In src/platforms/win32.ts addToTrustStores:
try {
run('certutil', ['-addstore', '-user', 'root', certificatePath]);
} catch (e) {
e.output.map((buffer) => { if (buffer) console.log(buffer.toString()); });
// error swallowed; no rethrow
}
In src/index.ts certificateFor:
if (!existsSync(rootCAKeyPath)) {
await installCertificateAuthority(options); // never reached again once the file exists
}
Reproduction (Windows)
- Fresh machine, no prior devcert state.
- Call
devcert.certificateFor('localcdn.example.com') from a Node process.
- Windows shows the Security Warning dialog ("You are about to install a certificate from a certification authority claiming to represent: devcert ..."). Click No.
- devcert's win32
addToTrustStores swallows the certutil error and installCertificateAuthority returns successfully. Files at %LOCALAPPDATA%\devcert\certificate-authority\{certificate.cert,private-key.key} exist on disk.
- Open
https://localcdn.example.com:<port>/ in the browser → NET::ERR_CERT_AUTHORITY_INVALID.
- Re-run
devcert.certificateFor(...). devcert sees rootCAKeyPath exists, skips installCertificateAuthority entirely, never re-prompts. Browser remains broken.
The same shape applies on macOS/Linux if their sudo security add-trusted-cert / sudo update-ca-certificates call fails after the canonical files have already been written — those platforms do propagate errors, but the next run still skips install because the files are on disk.
Proposed fix
Generate the cert+key into temp paths, install into the trust store first, and persist to canonical paths only after the trust install succeeds. Also propagate certutil failures from win32.addToTrustStores (keeping the Firefox open-failure tolerance — most users don't have Firefox).
PR: #117 ← will update with the new PR number after opening
Bug
installCertificateAuthorityinsrc/certificate-authority.tspersists the root CA cert and key to their canonical paths before attempting to add the CA to the OS trust store. IfaddToTrustStoresfails or is declined (e.g. the user clicks "No" on the Windows Security Warning), the on-disk state remains, andcertificateForinsrc/index.tsthen skips re-installation on every subsequent run because its guard is!existsSync(rootCAKeyPath). The browser stays broken withNET::ERR_CERT_AUTHORITY_INVALID, and devcert never re-prompts.Current flow
In
src/certificate-authority.ts:In
src/platforms/win32.ts addToTrustStores:In
src/index.ts certificateFor:Reproduction (Windows)
devcert.certificateFor('localcdn.example.com')from a Node process.addToTrustStoresswallows the certutil error andinstallCertificateAuthorityreturns successfully. Files at%LOCALAPPDATA%\devcert\certificate-authority\{certificate.cert,private-key.key}exist on disk.https://localcdn.example.com:<port>/in the browser →NET::ERR_CERT_AUTHORITY_INVALID.devcert.certificateFor(...). devcert seesrootCAKeyPathexists, skipsinstallCertificateAuthorityentirely, never re-prompts. Browser remains broken.The same shape applies on macOS/Linux if their
sudo security add-trusted-cert/sudo update-ca-certificatescall fails after the canonical files have already been written — those platforms do propagate errors, but the next run still skips install because the files are on disk.Proposed fix
Generate the cert+key into temp paths, install into the trust store first, and persist to canonical paths only after the trust install succeeds. Also propagate certutil failures from
win32.addToTrustStores(keeping the Firefox open-failure tolerance — most users don't have Firefox).PR: #117 ← will update with the new PR number after opening