banner
BGIcon

Overview

Most browser traffic to large edge networks already uses hybrid post-quantum key exchange, but the origin servers behind them mostly do not. pqdoctor is a Rust command-line tool that answers the question every operator asks next: is this server post-quantum, and if not, why? It sends hand-built TLS ClientHellos, reads only the server's first reply, and reports whether the host negotiates X25519MLKEM768. When it does not, it names the cause — an old TLS library, pinned curves, a Kyber-draft-only stack, TLS 1.2 only, or a middlebox that drops large hellos — and prints the fix for that server.

Version 0.3.0 is published on crates.io, through Homebrew, Scoop and cargo-binstall, and as downloads for Windows, macOS (universal, 11+) and Linux (x64 and arm64) at pqdoctor.com. Around the CLI it has grown into a small product family: a GitHub Action on the Marketplace, a fleet dashboard for organizations, a WebAssembly build on Cloudflare Workers, and the post-quantum features of Flarebox, a desktop app for Cloudflare.

Technologies

  • - Rust (CLI + library crate)
  • - TLS 1.3 wire format, hand-built ClientHellos
  • - ML-KEM / X25519MLKEM768, ML-DSA
  • - SSH key-exchange negotiation
  • - WebAssembly (C ABI, JSON in/out)
  • - zig + cargo cross-compilation (musl, universal macOS)
  • - Astro + Starlight (docs site)
  • - Cloudflare Workers + D1 (fleet dashboard)
  • - GitHub Actions (Marketplace action, SARIF, attested release builds)
A real run against github.com: not post-quantum, the cause, and the fixes
The same check against cloudflare.com, which prefers X25519MLKEM768
pqdoctor.com, the documentation site
Results of the Tranco top-10k study, published on pqdoctor.com
pqdoctor fleet: post-quantum readiness across an organization, by team
The pqdoctor GitHub Action, live on the GitHub Marketplace
BG
Icon
  • Year

    2026

  • Type

    CLI, Action + Fleet

  • Role

    Solo Developer

  • Status

    0.3.0 on crates.io

Highlights

Diagnoses why a server is not post-quantum and prints the exact fix for nginx, Apache, HAProxy, Caddy, Node and Go, plus an openssl s_client command to verify it.

Powers the origin post-quantum study: of 4,625 non-CDN origins in the Tranco top 10k that answered, 28.1% negotiated post-quantum key exchange, and --fingerprint sorts the rest into library, provider and config causes.

Shipped 0.3.0 on crates.io, Homebrew and Scoop, with binaries built from the published crate by a public GitHub workflow with build attestations, and a GitHub Action live on the Marketplace.

BG
Icon
  • Probes

    7 key-exchange hellos

  • Protocols

    TLS · STARTTLS · SSH

Technical Implementation

Seven probes, one reply each: pqdoctor builds its ClientHellos byte by byte — classical TLS 1.3, ML-KEM only, PQ preference, the old Kyber draft, split and same-size padded hellos, and NIST curves only. It reads the first server message and hangs up. The key-exchange probes use no cryptography and never complete a handshake, so a check is fast and leaves nothing behind on the server.

Cause, not just a verdict: comparing the answers across probes separates the failure modes — no-mlkem, tls12-only, pinned-nist, large-hello-dropped, pq-group-rejected, kyber and no-tls. Each cause maps to a concrete fix, with a Cloudflare Tunnel fallback for servers that cannot be upgraded.

Authentication readiness (ML-DSA): a separate check completes one real X25519 exchange through the TLS 1.3 key schedule just far enough to decrypt the server's Certificate and CertificateVerify, without ever sending Finished. It lists the chain and signature scheme and reports whether the server serves ML-DSA (codepoints 0x0904–0x0906), refuses cleanly, ignores the offer, or breaks on it.

Beyond HTTPS: every resolved address is checked (IPv4 and IPv6), mail servers are reached over STARTTLS for SMTP, IMAP and POP3, and pqdoctor ssh reads an SSH server's key-exchange offer without logging in. On github.com and gitlab.com its result matches OpenSSH 10's own negotiation output.

Built for automation: JSON output and exit codes for CI, and the same library compiled to WebAssembly behind a small C ABI so the engine can run inside a Cloudflare Worker with results identical to the native CLI.

Cross-platform releases: static musl builds for Linux x64 and arm64 and a universal macOS 11+ binary are cross-compiled from one machine using zig linker wrappers, after the default toolchain produced macOS-15-only binaries. Every download ships with SHA-256 checksums.

BG
Icon
  • Study

    5,141 origins

  • Releases

    0.1 → 0.2 → 0.3

Accuracy, Study & Releases

A tool that tells operators what to change has to be right. pqdoctor is checked against a local fix lab of 30 deliberately misconfigured servers (30/30 diagnosed correctly), cross-checked against OpenSSL on real hosts (91 of 93 agreeing), and its Linux build reproduces the Windows scan of 60 sites with the only two differences traced to the servers themselves.

The origin study took the Tranco top 10k, removed hosts served by the large CDNs, and scanned the remaining 5,141 origins with a contact address in the user agent and an opt-out list. The leading causes were a TLS library without ML-KEM, TLS 1.2 only and pinned NIST curves, which is why the tool's output centres on the fix rather than the verdict. The full write-up, including a second pass down to rank 1,000,000, is its own research entry.

0.2 added QUIC and DTLS probes, STARTTLS for FTP, XMPP, LDAP, PostgreSQL, MySQL and RDP, CSV/HTML/SARIF/JUnit output, a CycloneDX CBOM, check history with diff, and a fix command. 0.3 goes one level below the verdict: --fingerprint tells a config problem from an old library or a provider product, plan picks which origins to fix first for the most post-quantum traffic, fix --apply backs up, writes, tests, reloads, re-checks and rolls back on failure, and sidecar puts cloudflared or Caddy in front of a server that cannot be upgraded. Default options still behave like 0.1.0, so existing scripts and CI jobs do not change.

For organizations: pqdoctor inventory builds a host list from certificate transparency and DNS zones, the GitHub Action checks it on a schedule and fails only on regressions, and results push to pqdoctor fleet, a dashboard on Cloudflare Workers and D1 that shows readiness by team, what each remaining host needs, and the trend over time.