pqdoctor
pqdoctor answers one question about a TLS server: is it post-quantum, and if not, why? It sends seven hand-built ClientHellos, reads only the server’s first reply to each, and turns the replies into a verdict, a stable cause code, and the fix for that server’s software.
Download pqdoctor 0.2.1For Windows, macOS (Apple silicon and Intel) and Linux (x86_64 and arm64). Or, with Rust: cargo install pqdoctor (crates.io).
Quickstart
✘ nginx.org:443 (3.147.99.198) Not post-quantum: TLS 1.2 only The server doesn't speak TLS 1.3, so it can't do hybrid ML-KEM. cause: tls12-only server: nginx/1.31.3
Fixes 1. Turn on TLS 1.3 Hybrid post-quantum key exchange only exists in TLS 1.3. This server answered with TLS 1.2. # nginx ssl_protocols TLSv1.2 TLSv1.3; # Apache SSLProtocol -all +TLSv1.2 +TLSv1.3 2. nginx: link against OpenSSL 3.5+ and list the PQ group nginx gets PQ from its TLS library. OpenSSL 3.5 or newer is needed (Ubuntu 25.04+, Debian 13+, or an nginx build linked against 3.5). Check with `nginx -V`. ssl_protocols TLSv1.2 TLSv1.3; ssl_ecdh_curve X25519MLKEM768:X25519:prime256v1;Features
Section titled “Features”| Diagnosis, not just a yes/no | 12 cause codes: old TLS library, hand-set curve list, TLS 1.2 only, Kyber draft only, a middlebox dropping large or split hellos, and more |
| Fixes for your stack | Reads the Server header and prints the config for nginx, Apache, HAProxy, Caddy, Node, Go, IIS, Java, Postfix, Dovecot, Exim or Exchange, plus a Cloudflare Tunnel fallback |
| Handshake-only | Never completes a key-exchange handshake, never sends application data |
| Post-quantum authentication | Reads the certificate chain and checks whether the server serves an ML-DSA certificate |
| Every address | Up to 6 addresses per name, reported per address when they disagree |
| Mail and SSH | STARTTLS for SMTP, IMAP and POP3; SSH key exchange offers |
| Scans | Thousands of hosts, politely: concurrency and rate caps, resume, exclusions, CDN skipping |
| Scriptable | JSON and JSON Lines output, stable codes, meaningful exit status |
| Checked | 59 / 59 against OpenSSL 3.5 on real sites, 30 / 30 in a fix lab of real faulty servers |
Guides
Section titled “Guides”Scanning a listLarge scans, resume, exclusions, skipping CDNs, being a good neighbour.
In CIThe GitHub Action, and exit codes in any pipeline.
Scripting with JSONPowerShell and jq recipes for reports and scans.
Verify by handConfirm any verdict with OpenSSL 3.5 or OpenSSH.
Mail serversSTARTTLS checks and fixes for Postfix, Dovecot, Exim, Exchange.
SSHML-KEM and sntrup761 key exchange, and KexAlgorithms fixes.
Reference
Section titled “Reference”- CLI reference: every command, option, stream and exit status.
- JSON output: the full schema.
- Probes and wire format: exactly what’s sent.
Where else it runs
Section titled “Where else it runs”The same Rust engine is inside Flarebox, the GitHub Action, and pqdoctor on Workers. All of them give the same verdict, cause and fix for the same server.