npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

check-pqc

v0.2.8

Published

Check if a host is post-quantum (PQC) ready — TLS 1.3 + ML-KEM hybrid key exchange. CLI wrapper over checkpqc.app.

Readme

Is your TLS post-quantum ready? A command-line tool that tells you whether any host on the public internet (or your own intranet) negotiates a quantum-resistant key exchange — and what to do about it if it doesn't.

npm npm scoped ghcr license

npx check-pqc google.com
  ● HYBRID ENABLED  — Server negotiated a hybrid PQC + classical group. Recommended state.

    Target:      google.com:443
    Verdict:     HYBRID_ENABLED

    Hybrid attempt    ✓ TLSv1.3 · X25519MLKEM768 · TLS_AES_256_GCM_SHA384 (62ms)
    Classical attempt ✓ TLSv1.3 · X25519 · TLS_AES_256_GCM_SHA384 (58ms)

    Full report: https://checkpqc.app/?host=google.com

Why should I care?

Right now, attackers are recording encrypted TLS traffic so they can decrypt it later when sufficiently large quantum computers exist. This is called "Harvest Now, Decrypt Later" (HNDL).

The defense is a hybrid TLS 1.3 key exchange that combines classical ECDH (X25519) with ML-KEM-768 (formerly Kyber-768, NIST FIPS 203). Even if a quantum computer breaks the classical half a decade from now, the ML-KEM half still protects the session.

check-pqc tells you, in one command, whether a server is using that hybrid handshake — so you can verify your own services and audit your vendors.


Quick start (60 seconds)

You need one of these:

  • Node.js 18 or newer (check with node --version), OR
  • Docker (any modern Docker Desktop or daemon)

Then:

# Option A: no install, run once
npx check-pqc google.com

# Option B: install permanently (short alias or org-branded — same package)
npm install -g check-pqc
# or
npm install -g @aegyrix/check-pqc
check-pqc google.com

# Option C: Docker (no Node needed)
docker run --rm ghcr.io/aegyrix/check-pqc:latest google.com

That's it. If the output says HYBRID ENABLED or PQC ENABLED, the host is post-quantum-ready. Anything else means it isn't — see Fixing a host that fails below.


Getting started

You just installed check-pqc. Here's the 3-minute tour of what to do next.

1. Confirm your local setup

check-pqc --version          # tool itself
check-pqc --check-offline    # local OpenSSL + ML-KEM capability

--check-offline tells you whether your machine has an OpenSSL build new enough to do PQC handshakes locally (used by --offline mode). If it says PQC-capable, you're set. If not, online probes still work — see Airgap / SCIF mode for fixes.

2. Audit a host you care about

check-pqc <your-domain>          # e.g. checkpqc.app, your company's site
check-pqc <your-domain> --json   # for scripts / dashboards

Read the verdict in the output table below. The exit code tells your shell whether the host is OK (0) or needs work (non-zero).

3. Decide what to look for

| If you see... | What it means | Your move | |---|---|---| | PQC_ENABLED / HYBRID_ENABLED | Server picked a PQ key exchange. Recommended. | ✅ Nothing — re-check after stack upgrades. | | AVAILABLE_NOT_ACTIVE | Server can speak PQC but didn't pick it. | Bump PQC groups to the front of ssl_conf_command Groups. | | CLIENT_ONLY | Your client supports PQC; the server doesn't. | Server upgrade — see Fix. | | SERVER_ONLY | Server supports PQC but classical-only clients can't connect. | Add classical fallback (X25519:secp384r1) to your group list. | | NOT_READY | Neither side speaks PQC. | Upgrade OpenSSL to 3.5+ and reconfigure. | | UNKNOWN | Network error / timeout / handshake aborted. | Re-run; check the target is reachable on port 443. |

4. Wire it into something useful

Most people install check-pqc and then forget about it. To get value, pick one of these:

  • CI gate on every deploy — see CI/CD example.
  • Nightly cron that emails you on regression — see Scheduled audit.
  • Library import in a custom dashboard — see Library import.
  • Slack/Teams hook — pipe --json through jq and post to a webhook.

If you only do one thing, do the CI gate. PQC posture is the kind of thing that silently regresses on a TLS library upgrade or an LB swap; a CI check catches it the same hour it happens.

5. Bookmark these

| Resource | URL | |---|---| | Web checker | checkpqc.app | | Status badge | https://checkpqc.app/badge/<host> | | API docs | api.checkpqc.app/docs | | Source / issues | github.com/aegyrix/checkpqc.app | | Security report | [email protected] (PGP key on /.well-known/security.txt) |


1. Install

check-pqc runs on macOS, Linux, and Windows. CI smoke-tests every release on all three. Pick the install path that fits your environment.

macOS

# Easiest — uses Apple's bundled npm if you've ever installed Node
npm install -g check-pqc

# If you don't have Node yet:
brew install node
npm install -g check-pqc

# Verify
check-pqc --version    # → check-pqc v0.2.6

Linux (any distro)

# Debian / Ubuntu
sudo apt-get update && sudo apt-get install -y nodejs npm
sudo npm install -g check-pqc

# Fedora / RHEL / Rocky
sudo dnf install -y nodejs npm
sudo npm install -g check-pqc

# Alpine
apk add --no-cache nodejs npm
npm install -g check-pqc

# Arch
sudo pacman -S nodejs npm
sudo npm install -g check-pqc

# Verify
check-pqc --version

Windows

# PowerShell — install Node first if you don't have it
winget install OpenJS.NodeJS.LTS

# Open a NEW PowerShell window so PATH refreshes, then:
npm install -g check-pqc

# Verify
check-pqc --version

You can also use WSL2 (Linux instructions above) or Docker Desktop (see below). All three paths work identically.

Docker (any OS, no Node required)

The image is multi-architecture (linux/amd64 + linux/arm64) and ships with OpenSSL 3.5+ built-in so airgap mode "just works" without installing anything else.

# Pull the latest image
docker pull ghcr.io/aegyrix/check-pqc:latest

# Run it
docker run --rm ghcr.io/aegyrix/check-pqc:latest google.com

# Pin to a specific version
docker run --rm ghcr.io/aegyrix/check-pqc:0.2.6 google.com

# Use the airgap-optimized image (same image, different tag)
docker run --rm ghcr.io/aegyrix/check-pqc:offline google.com

From a tarball (airgapped / vendored)

When you can't reach npmjs.com directly:

# On a machine WITH internet:
npm pack check-pqc                          # → check-pqc-0.2.6.tgz
# transfer the .tgz to the airgapped machine, then:
npm install -g ./check-pqc-0.2.6.tgz

Verifying the curl-pipe installer (optional)

If you'd rather not pipe curl | sh, every published installer has a matching .sha256 sidecar at the same URL. The two files are written together by the same deploy script, so a tampered installer will not match its sidecar.

# macOS / Linux
curl -fsSL https://checkpqc.com/install.sh        -o install.sh
curl -fsSL https://checkpqc.com/install.sh.sha256 -o install.sh.sha256
shasum -a 256 -c install.sh.sha256          # → install.sh: OK
sh install.sh
# Windows
$ErrorActionPreference = 'Stop'
Invoke-WebRequest https://checkpqc.com/install.ps1        -OutFile install.ps1
Invoke-WebRequest https://checkpqc.com/install.ps1.sha256 -OutFile install.ps1.sha256
$expected = (Get-Content install.ps1.sha256).Split(' ')[0]
$actual   = (Get-FileHash -Algorithm SHA256 install.ps1).Hash.ToLower()
if ($expected -ne $actual) { throw 'install.ps1 sha mismatch' }
.\install.ps1

This protects against an attacker who can serve a different install.sh than its sidecar (e.g. cache poisoning). It does not protect against a full origin compromise that rewrites both files in lock-step — for that, pin to a specific package version: npm install -g [email protected].


2. Check (run a probe)

The basic command is:

check-pqc <hostname>

check-pqc connects to the target and performs two independent TLS 1.3 handshakes:

  1. A hybrid attempt that offers X25519MLKEM768 first.
  2. A classical attempt that only offers classical ECDH groups.

The combination of the two outcomes determines the verdict.

Examples

# Default port 443
check-pqc example.com

# Custom port
check-pqc mail.example.com:993
check-pqc example.com --port 8443

# Machine-readable JSON
check-pqc example.com --json

# No ANSI colors (e.g. when piping to a file)
check-pqc example.com --no-color > report.txt

# Show the local PQC capability and exit
check-pqc --check-offline

# Run the probe with no network call (airgap / SCIFs)
check-pqc internal.corp --offline

Reading the output

| Verdict | What it means | Exit code | |---|---|---| | PQC_ENABLED | Pure post-quantum group negotiated. Best possible state. | 0 | | HYBRID_ENABLED | Hybrid PQC + classical group negotiated. Recommended. | 0 | | AVAILABLE_NOT_ACTIVE | Server can speak PQC but did not select it for you. Check group ordering. | 2 | | CLIENT_ONLY | Your client supports PQC; the server does not. Server upgrade needed. | 2 | | SERVER_ONLY | Server supports PQC; a classical-only client could not negotiate. | 2 | | NOT_READY | Neither side can negotiate PQC. | 1 | | UNKNOWN | Network error, timeout, or inconclusive result. | 3 |

The exit code makes it easy to gate CI/CD pipelines:

check-pqc api.example.com || exit 1

3. Test (verify your install works)

After installing, run these to make sure everything is wired up:

# 1. Print version
check-pqc --version
# Expected: check-pqc v0.2.6

# 2. Check local PQC engine
check-pqc --check-offline
# Expected on a PQC-capable host:
#   ● PQC-capable
#     openssl:      /opt/homebrew/opt/openssl@3/bin/openssl
#     hybrid group: X25519MLKEM768
# Exit code: 0  (capable)  or  3  (not capable — still usable, see below)

# 3. Probe a known-good public PQC host
check-pqc google.com
# Expected: HYBRID_ENABLED, exit code 0

# 4. Probe a known-not-yet-PQC host (most banks, most legacy hosts)
check-pqc www.irs.gov
# Expected: NOT_READY or AVAILABLE_NOT_ACTIVE, non-zero exit code

# 5. JSON parses cleanly
check-pqc google.com --json | jq .verdict
# Expected: "HYBRID_ENABLED"

If all five pass, your install is good.

Continuous Integration example

# .github/workflows/pqc-audit.yml
- name: Audit our public TLS endpoints
  run: |
    npx check-pqc api.example.com
    npx check-pqc www.example.com
    npx check-pqc auth.example.com

4. Fix (apply a remedy)

If check-pqc reports a non-PQC_ENABLED / HYBRID_ENABLED verdict, here's what to do based on the verdict.

Fixing a host that fails

NOT_READY or CLIENT_ONLY

The server doesn't speak PQC. Upgrade or reconfigure it:

nginx (Linux)

You need OpenSSL 3.5+ (native ML-KEM) or OpenSSL 3.2+ with the oqsprovider loaded.

# /etc/nginx/sites-available/your-site.conf
ssl_protocols TLSv1.3 TLSv1.2;
ssl_conf_command Groups X25519MLKEM768:SecP256r1MLKEM768:X25519:secp384r1:prime256v1;
ssl_ecdh_curve X25519:secp384r1:prime256v1;
ssl_prefer_server_ciphers off;

Reload, then re-run check-pqc.

Apache 2.4.62+
SSLOpenSSLConfCmd Groups X25519MLKEM768:SecP256r1MLKEM768:X25519:secp384r1:prime256v1
SSLProtocol -all +TLSv1.2 +TLSv1.3
Caddy 2.8+
{
  servers {
    protocols h1 h2 h3
  }
}
example.com {
  tls {
    curves x25519mlkem768 x25519 secp384r1
  }
}
Cloudflare / Fastly / AWS CloudFront / Azure Front Door

Most large CDNs already enable PQC by default in 2026. If you front your origin with one of these and check-pqc still reports NOT_READY, check your custom TLS profile settings — some require explicit opt-in.

AVAILABLE_NOT_ACTIVE

The server can speak PQC but didn't pick it. Almost always a group ordering issue: list the hybrid groups first in your TLS config (see nginx example above — X25519MLKEM768 before X25519).

SERVER_ONLY

The server supports PQC; only your client is classical-only. This verdict isn't a problem with the host — it's telling you your local openssl can't negotiate hybrid. Install OpenSSL 3.5+ (see Fixing the local PQC engine).

UNKNOWN

Network or DNS issue. Try:

# Force IPv4
check-pqc example.com --json | jq .

# Try a different port
check-pqc example.com:8443

# Sanity-check connectivity
curl -v https://example.com 2>&1 | head -20

Fixing the local PQC engine

check-pqc --offline and --check-offline need a system OpenSSL 3.5+ that exposes the X25519MLKEM768 group. Here's how to get one on each platform:

| Platform | Command | Result | |---|---|---| | macOS | brew install openssl@3 | OpenSSL 3.5+ in /opt/homebrew/opt/openssl@3/bin/openssl | | Linux — Alpine edge | apk add openssl | OpenSSL 3.5.x ✓ | | Linux — Debian/Ubuntu | distro openssl is 3.0.x (too old). Use Docker (ghcr.io/aegyrix/check-pqc:offline) or compile OpenSSL 3.5 from source | | | Linux — Fedora 41+ | sudo dnf install openssl | 3.5+ ✓ | | Linux — RHEL 9 / Rocky 9 | enable EPEL + install oqs-provider, OR use Docker | | | Linux — Arch / openSUSE Tumbleweed | sudo pacman -S openssl / sudo zypper in openssl-3 | rolling, 3.5+ ✓ | | Windows | winget install ShiningLight.OpenSSL.Light or scoop install openssl | 3.5+ ✓ | | Anything else | docker run --rm ghcr.io/aegyrix/check-pqc:offline ... | Bundled OpenSSL 3.5.6 ✓ |

After installing, verify:

openssl version                 # → OpenSSL 3.5.x or newer
openssl list -tls-groups | grep -i mlkem    # should print X25519MLKEM768

check-pqc --check-offline       # → ● PQC-capable

If your distro's openssl is stuck at 3.0.x and you can't upgrade, the Docker image is your easy out — it ships OpenSSL 3.5.6 inside and runs identically on every OS.


Library import (Node / TypeScript)

check-pqc is also a TypeScript library if you want to embed the probe in your own tool:

import { offlineProbe, detectOpensslCapability } from 'check-pqc';

const cap = detectOpensslCapability();
if (!cap.available) {
  throw new Error('Host has no PQC-capable OpenSSL — install 3.5+');
}

const result = await offlineProbe('internal.corp', 443);
if (result.verdict !== 'HYBRID_ENABLED' && result.verdict !== 'PQC_ENABLED') {
  console.error('Not PQC-ready:', result.verdict);
  process.exit(1);
}

Or import only the offline subpath to keep the bundle tiny:

import { offlineProbe } from 'check-pqc/offline';

Airgap / SCIF mode

check-pqc --offline runs the entire twin-probe locally, with zero outbound API calls. It uses Node's built-in tls module for the classical attempt and shells out to your system OpenSSL for the hybrid attempt.

check-pqc --check-offline                  # confirm local engine status
check-pqc --offline internal.corp:443      # probe a host with no API call
check-pqc --offline target --json          # machine-readable

Three ways to get the tool into an airgapped environment

| Method | Command | Best for | |---|---|---| | npm tarball | npm pack check-pqc online → transfer the .tgznpm install -g ./check-pqc-0.2.6.tgz | Any OS with Node 18+ | | Vendored | clone the repo + pnpm pack → ship the .tgz | Strict supply-chain controls | | Docker | docker pull ghcr.io/aegyrix/check-pqc:offline && docker save -o cli.tar ghcr.io/aegyrix/check-pqc:offline → transfer | Hosts with Docker but no Node |

Then on the airgapped host:

docker load -i cli.tar
docker run --rm ghcr.io/aegyrix/check-pqc:offline internal.corp --offline

All command-line options

check-pqc <hostname[:port]> [options]

Options
  --port, -p <n>      Port (default: 443, or :PORT in hostname)
  --json              Output raw JSON
  --no-color          Disable ANSI colors
  --offline           Probe locally with no API call (airgap / SCIFs)
  --check-offline     Print local PQC capability and exit (no probe)
  --api <url>         Override the online API endpoint
  --help, -h          Show help
  --version, -v       Show version

Operate (run it day-to-day)

Scheduled audit (cron + Task Scheduler)

Linux / macOS — cron

/etc/cron.d/checkpqc-audit (root) or crontab -e (user):

# Probe a list of hosts every morning at 06:30 local time.
# On regression (non-zero exit), mail the user via cron's MAILTO.
[email protected]
30 6 * * * checkpqc /usr/local/bin/check-pqc api.example.com  --json | tee -a /var/log/checkpqc/api.log    | jq -e '.verdict | test("^(PQC|HYBRID)_ENABLED$")' >/dev/null || echo "API regressed"
35 6 * * * checkpqc /usr/local/bin/check-pqc www.example.com  --json | tee -a /var/log/checkpqc/www.log    | jq -e '.verdict | test("^(PQC|HYBRID)_ENABLED$")' >/dev/null || echo "WWW regressed"

Or — simpler, no jq — just rely on the exit code:

30 6 * * * /usr/local/bin/check-pqc api.example.com >/dev/null || \
           echo "PQC regression on api.example.com" | \
           mail -s "[checkpqc] regression" [email protected]

Windows — Task Scheduler (PowerShell)

$action = New-ScheduledTaskAction -Execute "powershell.exe" `
  -Argument '-NoProfile -Command "& check-pqc api.example.com; if ($LASTEXITCODE -ne 0) { Send-MailMessage -To [email protected] -Subject ''[checkpqc] regression'' -SmtpServer smtp.example.com }"'
$trigger = New-ScheduledTaskTrigger -Daily -At 6:30am
Register-ScheduledTask -TaskName "CheckPQC daily audit" -Action $action -Trigger $trigger -User SYSTEM

Sending results to a Slack / Teams webhook

RESULT=$(check-pqc api.example.com --json)
VERDICT=$(echo "$RESULT" | jq -r .verdict)
if [[ "$VERDICT" != "PQC_ENABLED" && "$VERDICT" != "HYBRID_ENABLED" ]]; then
  curl -X POST -H 'Content-Type: application/json' \
    -d "{\"text\":\":warning: PQC regression on api.example.com — verdict: $VERDICT\"}" \
    "$SLACK_WEBHOOK_URL"
fi

Updating the CLI

# npm install
npm update -g check-pqc            # or @aegyrix/check-pqc

# Pin to a specific version (recommended for CI)
npm install -g [email protected]

# Docker
docker pull ghcr.io/aegyrix/check-pqc:latest

# PowerShell module
Update-Module CheckPQC

# winget
winget upgrade aegyrix.check-pqc

Telemetry / data sent

The CLI sends hostname + port only, and only in online mode. No request bodies, no headers, no credentials. --offline makes zero outbound calls. The API drops requester IPs after 7 days. Full details: checkpqc.com/privacy.

Logs and where to find them

check-pqc itself does not write a log file — output goes to stdout. If you want a persistent record, redirect:

check-pqc api.example.com --json >> /var/log/checkpqc/api.log

The API keeps a 7-day rotating audit log on the server (operator side, not visible to clients). If you self-host the API, see /var/log/checkpqc/contact.log and the systemd journal:

journalctl -u checkpqc-api -f

Troubleshooting

| Symptom | Likely cause | Fix | |---|---|---| | command not found: check-pqc | npm prefix not on $PATH | export PATH="$(npm config get prefix)/bin:$PATH" in your shell rc | | UNKNOWN — handshake aborted | Target firewalled, blocking your IP, or down | Try from another network; retry; check target on port 443 | | --check-offline says "not capable" | Local OpenSSL too old | brew upgrade openssl@3 (macOS) or use docker run ghcr.io/aegyrix/check-pqc:offline | | All hosts return UNKNOWN | Outbound TLS to api.checkpqc.app blocked | Use --offline mode (requires OpenSSL 3.5+) or run via Docker | | EACCES during npm install -g | npm prefix not user-writable | sudo npm install -g, or use a Node version manager (nvm/fnm/asdf) | | Slow / hanging probe | DNS resolution slow | The CLI has a 15s overall timeout; increase upstream DNS-cache TTL |

For anything not in the table, file an issue: github.com/aegyrix/checkpqc.app/issues.


Uninstall

The CLI installs only one global npm package (or one container image, or one PowerShell module). Removal is one command. No dotfiles, no launch agents, no daemons, no registry edits — check-pqc is fully ephemeral; every probe is a fresh subprocess.

npm install

# Whichever name you installed under
npm uninstall -g check-pqc
npm uninstall -g @aegyrix/check-pqc

# Verify
command -v check-pqc          # should print nothing

Homebrew (if you used brew install)

brew uninstall check-pqc

Docker

# Just remove the image — there is no persistent container
docker rmi ghcr.io/aegyrix/check-pqc:latest
docker rmi ghcr.io/aegyrix/check-pqc:offline
docker rmi ghcr.io/aegyrix/check-pqc:0.2.6

Windows — winget

winget uninstall aegyrix.check-pqc

Windows / cross-platform — PowerShell module

Uninstall-Module CheckPQC -AllVersions

What gets left behind?

Nothing the CLI created itself. It doesn't write to ~/.checkpqc, ~/.config, or anywhere else. The only artifacts are whatever output you redirected (e.g. > report.txt, >> /var/log/checkpqc/audit.log), plus any cron entries or scheduled tasks you wrote.

If you want a paranoid sweep:

# macOS / Linux
which check-pqc                                     # confirm gone
ls -la "$(npm config get prefix)/bin/check-pqc" 2>/dev/null   # confirm gone
crontab -l 2>/dev/null | grep -i check-pqc          # check for cron entries
# Windows
Get-Command check-pqc -ErrorAction SilentlyContinue
Get-ScheduledTask | Where-Object { $_.Actions.Execute -match 'check-pqc' }

Privacy

  • The CLI sends only the hostname and port to api.checkpqc.app in online mode — never request bodies, never headers.
  • Request IPs are dropped from logs after a sliding 7-day window.
  • --offline mode makes zero outbound API calls.
  • See checkpqc.com/privacy.

License

MIT — © Aegyrix LLC