{"_id":"@aegyrix/check-pqc","_rev":"13-2c3b2642b64146d3e2b90e6829463a18","name":"@aegyrix/check-pqc","dist-tags":{"latest":"0.2.8"},"versions":{"0.2.3":{"name":"@aegyrix/check-pqc","version":"0.2.3","keywords":["pqc","post-quantum","tls","tls13","ml-kem","kyber","hybrid","x25519mlkem768","security","compliance"],"license":"MIT","_id":"@aegyrix/check-pqc@0.2.3","maintainers":[{"name":"harrycaskey","email":"harrycaskey@gmail.com"}],"homepage":"https://checkpqc.com","bugs":{"url":"https://github.com/aegyrix/checkpqc.app/issues"},"bin":{"check-pqc":"dist/index.js"},"dist":{"shasum":"6e04160f8d00fce92ae183b195f9bb55238a15dd","tarball":"https://registry.npmjs.org/@aegyrix/check-pqc/-/check-pqc-0.2.3.tgz","fileCount":9,"integrity":"sha512-JJnnKDHeifBRU0RJvnA+pVaJfNjhvFiHgFjt0OT+J3sQWVQ+d6w2Vlh5QZzhdxChSVVdcjnsFAGOyX85IG3u/A==","signatures":[{"sig":"MEUCIQCqmq5oB3ZCzDitXf+nwqqp0LA1CmjOipTrfi/lgSVjWgIgdzFgi62XfTywanYoJY20IvPmRYs2nJvLzLajZtlE/R0=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":48702},"main":"./dist/lib.js","type":"module","types":"./dist/lib.d.ts","engines":{"node":">=18.18"},"exports":{".":{"types":"./dist/lib.d.ts","import":"./dist/lib.js"},"./offline":{"types":"./dist/offline.d.ts","import":"./dist/offline.js"},"./package.json":"./package.json"},"gitHead":"beeaf306cc1cbc16c6b7fdd322be03c795189dbe","scripts":{"dev":"tsx src/index.ts","build":"tsc -p tsconfig.json && chmod +x dist/index.js","prepack":"pnpm run build"},"_npmUser":{"name":"harrycaskey","email":"harrycaskey@gmail.com"},"repository":{"url":"git+https://github.com/aegyrix/checkpqc.app.git","type":"git","directory":"packages/cli"},"_npmVersion":"11.12.1","description":"Check if a host is post-quantum (PQC) ready — TLS 1.3 + ML-KEM hybrid key exchange. CLI wrapper over checkpqc.app.","directories":{},"_nodeVersion":"25.9.0","_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.0","typescript":"^5.6.0"},"_npmOperationalInternal":{"tmp":"tmp/check-pqc_0.2.3_1777394876355_0.2869739577721504","host":"s3://npm-registry-packages-npm-production"}},"0.2.4":{"name":"@aegyrix/check-pqc","version":"0.2.4","keywords":["pqc","post-quantum","tls","tls13","ml-kem","kyber","hybrid","x25519mlkem768","security","compliance"],"license":"MIT","_id":"@aegyrix/check-pqc@0.2.4","maintainers":[{"name":"harrycaskey","email":"harrycaskey@gmail.com"}],"homepage":"https://checkpqc.com","bugs":{"url":"https://github.com/aegyrix/checkpqc.app/issues"},"bin":{"check-pqc":"dist/index.js"},"dist":{"shasum":"db5dea9d10699a048bd1994563d092a8ff928067","tarball":"https://registry.npmjs.org/@aegyrix/check-pqc/-/check-pqc-0.2.4.tgz","fileCount":9,"integrity":"sha512-8jIibxF8CqF1aAuanmaeEc1NQuTM+mwO0PKN7mmFDJTVWtQYgIzWOmXuJrr1ZdBkdzfoegOUIsylWLgWAwy2ig==","signatures":[{"sig":"MEYCIQCFXawZr5zv6vKW7Saf9/EYYo+IlIsLDgVUfkbqRt1DkAIhAOdipvw0iSip/3kf/cxwVF9fYKYT9QfE3kjSH/+Sud7G","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":48708},"main":"./dist/lib.js","type":"module","types":"./dist/lib.d.ts","engines":{"node":">=18.18"},"exports":{".":{"types":"./dist/lib.d.ts","import":"./dist/lib.js"},"./offline":{"types":"./dist/offline.d.ts","import":"./dist/offline.js"},"./package.json":"./package.json"},"gitHead":"3dae7ce5cc58b3f63ad739bacc7b159f146eef49","scripts":{"dev":"tsx src/index.ts","build":"tsc -p tsconfig.json && chmod +x dist/index.js","prepack":"pnpm run build"},"_npmUser":{"name":"harrycaskey","email":"harrycaskey@gmail.com"},"repository":{"url":"git+https://github.com/aegyrix/checkpqc.app.git","type":"git","directory":"packages/cli"},"_npmVersion":"10.9.7","description":"Check if a host is post-quantum (PQC) ready — TLS 1.3 + ML-KEM hybrid key exchange. CLI wrapper over checkpqc.app.","directories":{},"_nodeVersion":"22.22.2","_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.0","typescript":"^5.6.0"},"_npmOperationalInternal":{"tmp":"tmp/check-pqc_0.2.4_1777395182601_0.44885192447759614","host":"s3://npm-registry-packages-npm-production"}},"0.2.5":{"name":"@aegyrix/check-pqc","version":"0.2.5","keywords":["pqc","post-quantum","tls","tls13","ml-kem","kyber","hybrid","x25519mlkem768","security","compliance"],"license":"MIT","_id":"@aegyrix/check-pqc@0.2.5","maintainers":[{"name":"harrycaskey","email":"harrycaskey@gmail.com"}],"homepage":"https://checkpqc.com","bugs":{"url":"https://github.com/aegyrix/checkpqc.app/issues"},"bin":{"check-pqc":"dist/index.js"},"dist":{"shasum":"e58faf0f596792001ba16f6d7c85765c1caf3261","tarball":"https://registry.npmjs.org/@aegyrix/check-pqc/-/check-pqc-0.2.5.tgz","fileCount":9,"integrity":"sha512-EQgn+Qt5f+Ch0R0I6yUqIQtrWQYBAQRoW28dHS0Oo3Ut4cx52hRdLjX2qiLAppiOUjUiEbZDv7+2ur1EafmcCQ==","signatures":[{"sig":"MEUCIAu6ZpCiFyWkUWU1d6H7CRqlP2z1CqeLPbV0yDGFc4t/AiEAm6OA6RPm4UReZQqXTHeHjnh+VU4VO8q+WEZm7Ryrr14=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":49202},"main":"./dist/lib.js","type":"module","types":"./dist/lib.d.ts","engines":{"node":">=18.18"},"exports":{".":{"types":"./dist/lib.d.ts","import":"./dist/lib.js"},"./offline":{"types":"./dist/offline.d.ts","import":"./dist/offline.js"},"./package.json":"./package.json"},"gitHead":"5a48b8099d712b77ad4e6a047a15fa6102e7ac43","scripts":{"dev":"tsx src/index.ts","build":"tsc -p tsconfig.json && chmod +x dist/index.js","prepack":"pnpm run build"},"_npmUser":{"name":"harrycaskey","email":"harrycaskey@gmail.com"},"repository":{"url":"git+https://github.com/aegyrix/checkpqc.app.git","type":"git","directory":"packages/cli"},"_npmVersion":"10.9.7","description":"Check if a host is post-quantum (PQC) ready — TLS 1.3 + ML-KEM hybrid key exchange. CLI wrapper over checkpqc.app.","directories":{},"_nodeVersion":"22.22.2","_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.0","typescript":"^5.6.0"},"_npmOperationalInternal":{"tmp":"tmp/check-pqc_0.2.5_1777398149678_0.7884003495004979","host":"s3://npm-registry-packages-npm-production"}},"0.2.6":{"name":"@aegyrix/check-pqc","version":"0.2.6","keywords":["pqc","post-quantum","tls","tls13","ml-kem","kyber","hybrid","x25519mlkem768","security","compliance"],"license":"MIT","_id":"@aegyrix/check-pqc@0.2.6","maintainers":[{"name":"harrycaskey","email":"harrycaskey@gmail.com"}],"homepage":"https://checkpqc.com","bugs":{"url":"https://github.com/aegyrix/checkpqc.app/issues"},"bin":{"check-pqc":"dist/index.js"},"dist":{"shasum":"200909eabd5bda237892413deb9945c4eef40979","tarball":"https://registry.npmjs.org/@aegyrix/check-pqc/-/check-pqc-0.2.6.tgz","fileCount":9,"integrity":"sha512-6HaH3bsDKEG2I3Yq2M6kQjDLP3+bF4PPbJ0kQwm+zPAE8DpuPVGGj5MN1IdYbqqrqVRi6PpEiePoLfaF639IuQ==","signatures":[{"sig":"MEUCIQDUmrVIGzXoS3bPN65DbEsrz4h8NnSXyHc0l+UCId/+ZwIgTUPX4IzRaULg3DJEpWmDSuvZDniTuthmj/Y1czzXqgw=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@aegyrix%2fcheck-pqc@0.2.6","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":49202},"main":"./dist/lib.js","type":"module","types":"./dist/lib.d.ts","engines":{"node":">=18.18"},"exports":{".":{"types":"./dist/lib.d.ts","import":"./dist/lib.js"},"./offline":{"types":"./dist/offline.d.ts","import":"./dist/offline.js"},"./package.json":"./package.json"},"gitHead":"8ef66dabfe5ccc2b755f95c8b8443df55db5b836","scripts":{"dev":"tsx src/index.ts","build":"tsc -p tsconfig.json && chmod +x dist/index.js","prepack":"pnpm run build"},"_npmUser":{"name":"harrycaskey","email":"harrycaskey@gmail.com"},"repository":{"url":"git+https://github.com/aegyrix/checkpqc.app.git","type":"git","directory":"packages/cli"},"_npmVersion":"10.9.7","description":"Check if a host is post-quantum (PQC) ready — TLS 1.3 + ML-KEM hybrid key exchange. CLI wrapper over checkpqc.app.","directories":{},"_nodeVersion":"22.22.2","_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.0","typescript":"^5.6.0"},"_npmOperationalInternal":{"tmp":"tmp/check-pqc_0.2.6_1777402202178_0.721934179799339","host":"s3://npm-registry-packages-npm-production"}},"0.2.7":{"name":"@aegyrix/check-pqc","version":"0.2.7","keywords":["pqc","post-quantum","tls","tls13","ml-kem","kyber","hybrid","x25519mlkem768","security","compliance"],"license":"MIT","_id":"@aegyrix/check-pqc@0.2.7","maintainers":[{"name":"harrycaskey","email":"harrycaskey@gmail.com"}],"homepage":"https://checkpqc.com","bugs":{"url":"https://github.com/aegyrix/checkpqc.app/issues"},"bin":{"check-pqc":"dist/index.js"},"dist":{"shasum":"c205fef94e5ca04a43ee3d8dd6acb81479f0a415","tarball":"https://registry.npmjs.org/@aegyrix/check-pqc/-/check-pqc-0.2.7.tgz","fileCount":9,"integrity":"sha512-kyNgeePcKE5yyu1/faD2/T90UJYT3ukmFA/C3xifDVFnrA3tAV9bIClpahx5NPGFfqLNquJZiKpbJ4HIf8bidw==","signatures":[{"sig":"MEUCIQD+AUzZ3cRZ21IauaLpfnK1Hq+q1l554Vpw96ovlRsvrwIgVqyKVKliNy+0wv6NugwwtrUvleV7tVopS1jEahLM3X0=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@aegyrix%2fcheck-pqc@0.2.7","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":65109},"main":"./dist/lib.js","type":"module","types":"./dist/lib.d.ts","engines":{"node":">=18.18"},"exports":{".":{"types":"./dist/lib.d.ts","import":"./dist/lib.js"},"./offline":{"types":"./dist/offline.d.ts","import":"./dist/offline.js"},"./package.json":"./package.json"},"gitHead":"331dd7f84509dd1af4e19ba44201fb5557384abc","scripts":{"dev":"tsx src/index.ts","build":"tsc -p tsconfig.json && chmod +x dist/index.js","prepack":"pnpm run build"},"_npmUser":{"name":"harrycaskey","email":"harrycaskey@gmail.com"},"repository":{"url":"git+https://github.com/aegyrix/checkpqc.app.git","type":"git","directory":"packages/cli"},"_npmVersion":"10.9.7","description":"Check if a host is post-quantum (PQC) ready — TLS 1.3 + ML-KEM hybrid key exchange. CLI wrapper over checkpqc.app.","directories":{},"_nodeVersion":"22.22.2","_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.0","typescript":"^5.6.0"},"_npmOperationalInternal":{"tmp":"tmp/check-pqc_0.2.7_1777409630294_0.2800309930166538","host":"s3://npm-registry-packages-npm-production"}},"0.2.8":{"name":"@aegyrix/check-pqc","version":"0.2.8","description":"Check if a host is post-quantum (PQC) ready — TLS 1.3 + ML-KEM hybrid key exchange. CLI wrapper over checkpqc.app.","license":"MIT","homepage":"https://checkpqc.com","repository":{"type":"git","url":"git+https://github.com/aegyrix/checkpqc.app.git","directory":"packages/cli"},"bugs":{"url":"https://github.com/aegyrix/checkpqc.app/issues"},"keywords":["pqc","post-quantum","tls","tls13","ml-kem","kyber","hybrid","x25519mlkem768","security","compliance"],"type":"module","bin":{"check-pqc":"dist/index.js"},"main":"./dist/lib.js","types":"./dist/lib.d.ts","exports":{".":{"types":"./dist/lib.d.ts","import":"./dist/lib.js"},"./offline":{"types":"./dist/offline.d.ts","import":"./dist/offline.js"},"./package.json":"./package.json"},"scripts":{"build":"tsc -p tsconfig.json && chmod +x dist/index.js","dev":"tsx src/index.ts","typecheck":"tsc --noEmit","test":"node --test --import tsx src/**/*.test.ts","prepack":"pnpm run build"},"engines":{"node":">=18.18"},"devDependencies":{"tsx":"^4.19.0","typescript":"^5.6.0"},"_id":"@aegyrix/check-pqc@0.2.8","gitHead":"df0bf37d36a9e920ef78caae9c28baddf3cde10a","_nodeVersion":"22.22.2","_npmVersion":"10.9.7","dist":{"integrity":"sha512-a7ykxfYB/54fIu4utedmULJSiSU+11WKBdwFuMPCaeQ60zcvwerwisYM2GxGzFcR888Qoy3AWOKfXd52G1pLjQ==","shasum":"f8946fc04c62c960973e81e46a0981124a087bc6","tarball":"https://registry.npmjs.org/@aegyrix/check-pqc/-/check-pqc-0.2.8.tgz","fileCount":9,"unpackedSize":66583,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@aegyrix%2fcheck-pqc@0.2.8","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIDpFnNf0rOvWey0KYPqrASCMAOwjBd5Ff1kbHQ6/JjyaAiA8Qdv8fmjZEKQ5ImsDowRGgmzX0j7e2SiKdl/WJFUecA=="}]},"_npmUser":{"name":"harrycaskey","email":"harrycaskey@gmail.com"},"directories":{},"maintainers":[{"name":"harrycaskey","email":"harrycaskey@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/check-pqc_0.2.8_1777479895780_0.8667151026100615"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-28T16:47:56.266Z","modified":"2026-04-29T16:24:56.205Z","0.2.3":"2026-04-28T16:47:56.486Z","0.2.4":"2026-04-28T16:53:02.731Z","0.2.5":"2026-04-28T17:42:29.794Z","0.2.6":"2026-04-28T18:50:02.347Z","0.2.7":"2026-04-28T20:53:50.449Z","0.2.8":"2026-04-29T16:24:55.927Z"},"bugs":{"url":"https://github.com/aegyrix/checkpqc.app/issues"},"license":"MIT","homepage":"https://checkpqc.com","keywords":["pqc","post-quantum","tls","tls13","ml-kem","kyber","hybrid","x25519mlkem768","security","compliance"],"repository":{"type":"git","url":"git+https://github.com/aegyrix/checkpqc.app.git","directory":"packages/cli"},"description":"Check if a host is post-quantum (PQC) ready — TLS 1.3 + ML-KEM hybrid key exchange. CLI wrapper over checkpqc.app.","maintainers":[{"name":"harrycaskey","email":"harrycaskey@gmail.com"}],"readme":"<!-- markdownlint-disable MD012 MD033 MD040 MD041 MD060 -->\n\n<p align=\"center\">\n  <img src=\"https://raw.githubusercontent.com/aegyrix/checkpqc.app/main/assets/logos/mark.svg\" alt=\"CheckPQC\" width=\"120\" height=\"120\" />\n</p>\n\n<h1 align=\"center\">check-pqc</h1>\n\n<p align=\"center\">\n  <a href=\"https://www.npmjs.com/package/checkpqc\"><img src=\"https://img.shields.io/npm/v/checkpqc.svg?color=7c3aed&labelColor=1e1b4b\" alt=\"npm\" /></a>\n  <a href=\"https://checkpqc.com\"><img src=\"https://img.shields.io/badge/web-checkpqc.com-22d3ee?labelColor=1e1b4b\" alt=\"checkpqc.com\" /></a>\n</p>\n\n> **Is your TLS post-quantum ready?** A command-line tool that tells you whether\n> any host on the public internet (or your own intranet) negotiates a\n> quantum-resistant key exchange — and what to do about it if it doesn't.\n\n[![npm](https://img.shields.io/npm/v/check-pqc.svg)](https://www.npmjs.com/package/check-pqc)\n[![npm scoped](https://img.shields.io/npm/v/@aegyrix/check-pqc.svg?label=%40aegyrix%2Fcheck-pqc)](https://www.npmjs.com/package/@aegyrix/check-pqc)\n[![ghcr](https://img.shields.io/badge/ghcr.io-aegyrix%2Fcheck--pqc-blue?logo=github)](https://github.com/aegyrix/checkpqc.app/pkgs/container/check-pqc)\n[![license](https://img.shields.io/npm/l/check-pqc.svg)](https://www.npmjs.com/package/check-pqc)\n\n```bash\nnpx check-pqc google.com\n```\n\n```\n  ● HYBRID ENABLED  — Server negotiated a hybrid PQC + classical group. Recommended state.\n\n    Target:      google.com:443\n    Verdict:     HYBRID_ENABLED\n\n    Hybrid attempt    ✓ TLSv1.3 · X25519MLKEM768 · TLS_AES_256_GCM_SHA384 (62ms)\n    Classical attempt ✓ TLSv1.3 · X25519 · TLS_AES_256_GCM_SHA384 (58ms)\n\n    Full report: https://checkpqc.app/?host=google.com\n```\n\n---\n\n## Why should I care?\n\nRight now, attackers are recording encrypted TLS traffic so they can decrypt\nit later when sufficiently large quantum computers exist. This is called\n**\"Harvest Now, Decrypt Later\" (HNDL)**.\n\nThe defense is a **hybrid TLS 1.3 key exchange** that combines classical\nECDH (`X25519`) with **ML-KEM-768** (formerly Kyber-768, NIST FIPS 203).\nEven if a quantum computer breaks the classical half a decade from now, the\nML-KEM half still protects the session.\n\n`check-pqc` tells you, in one command, whether a server is using that\nhybrid handshake — so you can verify your own services and audit your\nvendors.\n\n---\n\n## Quick start (60 seconds)\n\nYou need one of these:\n\n- **Node.js 18 or newer** (check with `node --version`), OR\n- **Docker** (any modern Docker Desktop or daemon)\n\nThen:\n\n```bash\n# Option A: no install, run once\nnpx check-pqc google.com\n\n# Option B: install permanently (short alias or org-branded — same package)\nnpm install -g check-pqc\n# or\nnpm install -g @aegyrix/check-pqc\ncheck-pqc google.com\n\n# Option C: Docker (no Node needed)\ndocker run --rm ghcr.io/aegyrix/check-pqc:latest google.com\n```\n\nThat's it. If the output says `HYBRID ENABLED` or `PQC ENABLED`, the host\nis post-quantum-ready. Anything else means it isn't — see [Fixing a host\nthat fails](#fixing-a-host-that-fails) below.\n\n---\n\n## Getting started\n\nYou just installed `check-pqc`. Here's the 3-minute tour of what to do\nnext.\n\n### 1. Confirm your local setup\n\n```bash\ncheck-pqc --version          # tool itself\ncheck-pqc --check-offline    # local OpenSSL + ML-KEM capability\n```\n\n`--check-offline` tells you whether your machine has an OpenSSL build\nnew enough to do PQC handshakes locally (used by `--offline` mode).\nIf it says **PQC-capable**, you're set. If not, online probes still\nwork — see [Airgap / SCIF mode](#airgap--scif-mode) for fixes.\n\n### 2. Audit a host you care about\n\n```bash\ncheck-pqc <your-domain>          # e.g. checkpqc.app, your company's site\ncheck-pqc <your-domain> --json   # for scripts / dashboards\n```\n\nRead the verdict in the output table below. The exit code tells your\nshell whether the host is OK (`0`) or needs work (non-zero).\n\n### 3. Decide what to look for\n\n| If you see... | What it means | Your move |\n|---|---|---|\n| `PQC_ENABLED` / `HYBRID_ENABLED` | Server picked a PQ key exchange. Recommended. | ✅ Nothing — re-check after stack upgrades. |\n| `AVAILABLE_NOT_ACTIVE` | Server **can** speak PQC but didn't pick it. | Bump PQC groups to the front of `ssl_conf_command Groups`. |\n| `CLIENT_ONLY` | Your client supports PQC; the **server** doesn't. | Server upgrade — see [Fix](#4-fix-apply-a-remedy). |\n| `SERVER_ONLY` | Server supports PQC but classical-only clients can't connect. | Add classical fallback (`X25519:secp384r1`) to your group list. |\n| `NOT_READY` | Neither side speaks PQC. | Upgrade OpenSSL to 3.5+ and reconfigure. |\n| `UNKNOWN` | Network error / timeout / handshake aborted. | Re-run; check the target is reachable on port 443. |\n\n### 4. Wire it into something useful\n\nMost people install `check-pqc` and then forget about it. To get value,\npick **one** of these:\n\n- **CI gate** on every deploy — see [CI/CD example](#continuous-integration-example).\n- **Nightly cron** that emails you on regression — see [Scheduled audit](#scheduled-audit-cron--task-scheduler).\n- **Library import** in a custom dashboard — see [Library import](#library-import-node--typescript).\n- **Slack/Teams hook** — pipe `--json` through `jq` and post to a webhook.\n\nIf you only do one thing, do the CI gate. PQC posture is the kind of\nthing that silently regresses on a TLS library upgrade or an LB swap;\na CI check catches it the same hour it happens.\n\n### 5. Bookmark these\n\n| Resource | URL |\n|---|---|\n| Web checker | [checkpqc.app](https://checkpqc.app) |\n| Status badge | [https://checkpqc.app/badge/&lt;host&gt;](https://checkpqc.app/badge) |\n| API docs | [api.checkpqc.app/docs](https://api.checkpqc.app/docs) |\n| Source / issues | [github.com/aegyrix/checkpqc.app](https://github.com/aegyrix/checkpqc.app) |\n| Security report | [security@checkpqc.com](mailto:security@checkpqc.com) (PGP key on `/.well-known/security.txt`) |\n\n---\n\n## 1. Install\n\n`check-pqc` runs on **macOS, Linux, and Windows**. CI smoke-tests every\nrelease on all three. Pick the install path that fits your environment.\n\n### macOS\n\n```bash\n# Easiest — uses Apple's bundled npm if you've ever installed Node\nnpm install -g check-pqc\n\n# If you don't have Node yet:\nbrew install node\nnpm install -g check-pqc\n\n# Verify\ncheck-pqc --version    # → check-pqc v0.2.6\n```\n\n### Linux (any distro)\n\n```bash\n# Debian / Ubuntu\nsudo apt-get update && sudo apt-get install -y nodejs npm\nsudo npm install -g check-pqc\n\n# Fedora / RHEL / Rocky\nsudo dnf install -y nodejs npm\nsudo npm install -g check-pqc\n\n# Alpine\napk add --no-cache nodejs npm\nnpm install -g check-pqc\n\n# Arch\nsudo pacman -S nodejs npm\nsudo npm install -g check-pqc\n\n# Verify\ncheck-pqc --version\n```\n\n### Windows\n\n```powershell\n# PowerShell — install Node first if you don't have it\nwinget install OpenJS.NodeJS.LTS\n\n# Open a NEW PowerShell window so PATH refreshes, then:\nnpm install -g check-pqc\n\n# Verify\ncheck-pqc --version\n```\n\nYou can also use **WSL2** (Linux instructions above) or **Docker Desktop**\n(see below). All three paths work identically.\n\n### Docker (any OS, no Node required)\n\nThe image is multi-architecture (`linux/amd64` + `linux/arm64`) and\nships with **OpenSSL 3.5+ built-in** so airgap mode \"just works\" without\ninstalling anything else.\n\n```bash\n# Pull the latest image\ndocker pull ghcr.io/aegyrix/check-pqc:latest\n\n# Run it\ndocker run --rm ghcr.io/aegyrix/check-pqc:latest google.com\n\n# Pin to a specific version\ndocker run --rm ghcr.io/aegyrix/check-pqc:0.2.6 google.com\n\n# Use the airgap-optimized image (same image, different tag)\ndocker run --rm ghcr.io/aegyrix/check-pqc:offline google.com\n```\n\n### From a tarball (airgapped / vendored)\n\nWhen you can't reach npmjs.com directly:\n\n```bash\n# On a machine WITH internet:\nnpm pack check-pqc                          # → check-pqc-0.2.6.tgz\n# transfer the .tgz to the airgapped machine, then:\nnpm install -g ./check-pqc-0.2.6.tgz\n```\n\n### Verifying the curl-pipe installer (optional)\n\nIf you'd rather not pipe `curl | sh`, every published installer has a\nmatching `.sha256` sidecar at the same URL. The two files are written\ntogether by the same deploy script, so a tampered installer will not\nmatch its sidecar.\n\n```bash\n# macOS / Linux\ncurl -fsSL https://checkpqc.com/install.sh        -o install.sh\ncurl -fsSL https://checkpqc.com/install.sh.sha256 -o install.sh.sha256\nshasum -a 256 -c install.sh.sha256          # → install.sh: OK\nsh install.sh\n```\n\n```powershell\n# Windows\n$ErrorActionPreference = 'Stop'\nInvoke-WebRequest https://checkpqc.com/install.ps1        -OutFile install.ps1\nInvoke-WebRequest https://checkpqc.com/install.ps1.sha256 -OutFile install.ps1.sha256\n$expected = (Get-Content install.ps1.sha256).Split(' ')[0]\n$actual   = (Get-FileHash -Algorithm SHA256 install.ps1).Hash.ToLower()\nif ($expected -ne $actual) { throw 'install.ps1 sha mismatch' }\n.\\install.ps1\n```\n\nThis protects against an attacker who can serve a different `install.sh`\nthan its sidecar (e.g. cache poisoning). It does **not** protect against\na full origin compromise that rewrites both files in lock-step — for\nthat, pin to a specific package version: `npm install -g check-pqc@0.2.8`.\n\n---\n\n## 2. Check (run a probe)\n\nThe basic command is:\n\n```bash\ncheck-pqc <hostname>\n```\n\n`check-pqc` connects to the target and performs **two independent TLS 1.3\nhandshakes**:\n\n1. A **hybrid attempt** that offers `X25519MLKEM768` first.\n2. A **classical attempt** that only offers classical ECDH groups.\n\nThe combination of the two outcomes determines the verdict.\n\n### Examples\n\n```bash\n# Default port 443\ncheck-pqc example.com\n\n# Custom port\ncheck-pqc mail.example.com:993\ncheck-pqc example.com --port 8443\n\n# Machine-readable JSON\ncheck-pqc example.com --json\n\n# No ANSI colors (e.g. when piping to a file)\ncheck-pqc example.com --no-color > report.txt\n\n# Show the local PQC capability and exit\ncheck-pqc --check-offline\n\n# Run the probe with no network call (airgap / SCIFs)\ncheck-pqc internal.corp --offline\n```\n\n### Reading the output\n\n| Verdict | What it means | Exit code |\n|---|---|---|\n| `PQC_ENABLED` | Pure post-quantum group negotiated. Best possible state. | `0` |\n| `HYBRID_ENABLED` | Hybrid PQC + classical group negotiated. **Recommended.** | `0` |\n| `AVAILABLE_NOT_ACTIVE` | Server *can* speak PQC but did not select it for you. Check group ordering. | `2` |\n| `CLIENT_ONLY` | Your client supports PQC; the server does not. **Server upgrade needed.** | `2` |\n| `SERVER_ONLY` | Server supports PQC; a classical-only client could not negotiate. | `2` |\n| `NOT_READY` | Neither side can negotiate PQC. | `1` |\n| `UNKNOWN` | Network error, timeout, or inconclusive result. | `3` |\n\nThe exit code makes it easy to gate CI/CD pipelines:\n\n```bash\ncheck-pqc api.example.com || exit 1\n```\n\n---\n\n## 3. Test (verify your install works)\n\nAfter installing, run these to make sure everything is wired up:\n\n```bash\n# 1. Print version\ncheck-pqc --version\n# Expected: check-pqc v0.2.6\n\n# 2. Check local PQC engine\ncheck-pqc --check-offline\n# Expected on a PQC-capable host:\n#   ● PQC-capable\n#     openssl:      /opt/homebrew/opt/openssl@3/bin/openssl\n#     hybrid group: X25519MLKEM768\n# Exit code: 0  (capable)  or  3  (not capable — still usable, see below)\n\n# 3. Probe a known-good public PQC host\ncheck-pqc google.com\n# Expected: HYBRID_ENABLED, exit code 0\n\n# 4. Probe a known-not-yet-PQC host (most banks, most legacy hosts)\ncheck-pqc www.irs.gov\n# Expected: NOT_READY or AVAILABLE_NOT_ACTIVE, non-zero exit code\n\n# 5. JSON parses cleanly\ncheck-pqc google.com --json | jq .verdict\n# Expected: \"HYBRID_ENABLED\"\n```\n\nIf all five pass, your install is good.\n\n### Continuous Integration example\n\n```yaml\n# .github/workflows/pqc-audit.yml\n- name: Audit our public TLS endpoints\n  run: |\n    npx check-pqc api.example.com\n    npx check-pqc www.example.com\n    npx check-pqc auth.example.com\n```\n\n---\n\n## 4. Fix (apply a remedy)\n\nIf `check-pqc` reports a non-`PQC_ENABLED` / `HYBRID_ENABLED` verdict,\nhere's what to do based on the verdict.\n\n### Fixing a host that fails\n\n#### `NOT_READY` or `CLIENT_ONLY`\n\nThe **server** doesn't speak PQC. Upgrade or reconfigure it:\n\n##### nginx (Linux)\n\nYou need **OpenSSL 3.5+** (native ML-KEM) **or** OpenSSL 3.2+ with the\n[`oqsprovider`](https://github.com/open-quantum-safe/oqs-provider) loaded.\n\n```nginx\n# /etc/nginx/sites-available/your-site.conf\nssl_protocols TLSv1.3 TLSv1.2;\nssl_conf_command Groups X25519MLKEM768:SecP256r1MLKEM768:X25519:secp384r1:prime256v1;\nssl_ecdh_curve X25519:secp384r1:prime256v1;\nssl_prefer_server_ciphers off;\n```\n\nReload, then re-run `check-pqc`.\n\n##### Apache 2.4.62+\n\n```apache\nSSLOpenSSLConfCmd Groups X25519MLKEM768:SecP256r1MLKEM768:X25519:secp384r1:prime256v1\nSSLProtocol -all +TLSv1.2 +TLSv1.3\n```\n\n##### Caddy 2.8+\n\n```caddy\n{\n  servers {\n    protocols h1 h2 h3\n  }\n}\nexample.com {\n  tls {\n    curves x25519mlkem768 x25519 secp384r1\n  }\n}\n```\n\n##### Cloudflare / Fastly / AWS CloudFront / Azure Front Door\n\nMost large CDNs already enable PQC by default in 2026. If you front your\norigin with one of these and `check-pqc` still reports `NOT_READY`, check\nyour custom TLS profile settings — some require explicit opt-in.\n\n#### `AVAILABLE_NOT_ACTIVE`\n\nThe server *can* speak PQC but didn't pick it. Almost always a **group\nordering** issue: list the hybrid groups *first* in your TLS config (see\nnginx example above — `X25519MLKEM768` before `X25519`).\n\n#### `SERVER_ONLY`\n\nThe server supports PQC; only your **client** is classical-only. This\nverdict isn't a problem with the host — it's telling you your local\nopenssl can't negotiate hybrid. Install OpenSSL 3.5+ (see [Fixing the\nlocal PQC engine](#fixing-the-local-pqc-engine)).\n\n#### `UNKNOWN`\n\nNetwork or DNS issue. Try:\n\n```bash\n# Force IPv4\ncheck-pqc example.com --json | jq .\n\n# Try a different port\ncheck-pqc example.com:8443\n\n# Sanity-check connectivity\ncurl -v https://example.com 2>&1 | head -20\n```\n\n### Fixing the local PQC engine\n\n`check-pqc --offline` and `--check-offline` need a **system OpenSSL 3.5+**\nthat exposes the `X25519MLKEM768` group. Here's how to get one on each\nplatform:\n\n| Platform | Command | Result |\n|---|---|---|\n| **macOS** | `brew install openssl@3` | OpenSSL 3.5+ in `/opt/homebrew/opt/openssl@3/bin/openssl` |\n| **Linux — Alpine edge** | `apk add openssl` | OpenSSL 3.5.x ✓ |\n| **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 | |\n| **Linux — Fedora 41+** | `sudo dnf install openssl` | 3.5+ ✓ |\n| **Linux — RHEL 9 / Rocky 9** | enable EPEL + install `oqs-provider`, OR use Docker | |\n| **Linux — Arch / openSUSE Tumbleweed** | `sudo pacman -S openssl` / `sudo zypper in openssl-3` | rolling, 3.5+ ✓ |\n| **Windows** | `winget install ShiningLight.OpenSSL.Light` or `scoop install openssl` | 3.5+ ✓ |\n| **Anything else** | `docker run --rm ghcr.io/aegyrix/check-pqc:offline ...` | Bundled OpenSSL 3.5.6 ✓ |\n\nAfter installing, verify:\n\n```bash\nopenssl version                 # → OpenSSL 3.5.x or newer\nopenssl list -tls-groups | grep -i mlkem    # should print X25519MLKEM768\n\ncheck-pqc --check-offline       # → ● PQC-capable\n```\n\nIf your distro's openssl is stuck at 3.0.x and you can't upgrade, the\n**Docker image is your easy out** — it ships OpenSSL 3.5.6 inside and\nruns identically on every OS.\n\n---\n\n## Library import (Node / TypeScript)\n\n`check-pqc` is also a TypeScript library if you want to embed the probe\nin your own tool:\n\n```ts\nimport { offlineProbe, detectOpensslCapability } from 'check-pqc';\n\nconst cap = detectOpensslCapability();\nif (!cap.available) {\n  throw new Error('Host has no PQC-capable OpenSSL — install 3.5+');\n}\n\nconst result = await offlineProbe('internal.corp', 443);\nif (result.verdict !== 'HYBRID_ENABLED' && result.verdict !== 'PQC_ENABLED') {\n  console.error('Not PQC-ready:', result.verdict);\n  process.exit(1);\n}\n```\n\nOr import only the offline subpath to keep the bundle tiny:\n\n```ts\nimport { offlineProbe } from 'check-pqc/offline';\n```\n\n---\n\n## Airgap / SCIF mode\n\n`check-pqc --offline` runs the entire twin-probe **locally**, with **zero\noutbound API calls**. It uses Node's built-in `tls` module for the\nclassical attempt and shells out to your system OpenSSL for the hybrid\nattempt.\n\n```bash\ncheck-pqc --check-offline                  # confirm local engine status\ncheck-pqc --offline internal.corp:443      # probe a host with no API call\ncheck-pqc --offline target --json          # machine-readable\n```\n\n### Three ways to get the tool into an airgapped environment\n\n| Method | Command | Best for |\n|---|---|---|\n| **npm tarball** | `npm pack check-pqc` online → transfer the `.tgz` → `npm install -g ./check-pqc-0.2.6.tgz` | Any OS with Node 18+ |\n| **Vendored** | clone the repo + `pnpm pack` → ship the `.tgz` | Strict supply-chain controls |\n| **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 |\n\nThen on the airgapped host:\n\n```bash\ndocker load -i cli.tar\ndocker run --rm ghcr.io/aegyrix/check-pqc:offline internal.corp --offline\n```\n\n---\n\n## All command-line options\n\n```text\ncheck-pqc <hostname[:port]> [options]\n\nOptions\n  --port, -p <n>      Port (default: 443, or :PORT in hostname)\n  --json              Output raw JSON\n  --no-color          Disable ANSI colors\n  --offline           Probe locally with no API call (airgap / SCIFs)\n  --check-offline     Print local PQC capability and exit (no probe)\n  --api <url>         Override the online API endpoint\n  --help, -h          Show help\n  --version, -v       Show version\n```\n\n---\n\n## Operate (run it day-to-day)\n\n### Scheduled audit (cron + Task Scheduler)\n\n#### Linux / macOS — cron\n\n`/etc/cron.d/checkpqc-audit` (root) or `crontab -e` (user):\n\n```cron\n# Probe a list of hosts every morning at 06:30 local time.\n# On regression (non-zero exit), mail the user via cron's MAILTO.\nMAILTO=ops@example.com\n30 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\"\n35 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\"\n```\n\nOr — simpler, no jq — just rely on the exit code:\n\n```cron\n30 6 * * * /usr/local/bin/check-pqc api.example.com >/dev/null || \\\n           echo \"PQC regression on api.example.com\" | \\\n           mail -s \"[checkpqc] regression\" ops@example.com\n```\n\n#### Windows — Task Scheduler (PowerShell)\n\n```powershell\n$action = New-ScheduledTaskAction -Execute \"powershell.exe\" `\n  -Argument '-NoProfile -Command \"& check-pqc api.example.com; if ($LASTEXITCODE -ne 0) { Send-MailMessage -To ops@example.com -Subject ''[checkpqc] regression'' -SmtpServer smtp.example.com }\"'\n$trigger = New-ScheduledTaskTrigger -Daily -At 6:30am\nRegister-ScheduledTask -TaskName \"CheckPQC daily audit\" -Action $action -Trigger $trigger -User SYSTEM\n```\n\n### Sending results to a Slack / Teams webhook\n\n```bash\nRESULT=$(check-pqc api.example.com --json)\nVERDICT=$(echo \"$RESULT\" | jq -r .verdict)\nif [[ \"$VERDICT\" != \"PQC_ENABLED\" && \"$VERDICT\" != \"HYBRID_ENABLED\" ]]; then\n  curl -X POST -H 'Content-Type: application/json' \\\n    -d \"{\\\"text\\\":\\\":warning: PQC regression on api.example.com — verdict: $VERDICT\\\"}\" \\\n    \"$SLACK_WEBHOOK_URL\"\nfi\n```\n\n### Updating the CLI\n\n```bash\n# npm install\nnpm update -g check-pqc            # or @aegyrix/check-pqc\n\n# Pin to a specific version (recommended for CI)\nnpm install -g check-pqc@0.2.8\n\n# Docker\ndocker pull ghcr.io/aegyrix/check-pqc:latest\n\n# PowerShell module\nUpdate-Module CheckPQC\n\n# winget\nwinget upgrade aegyrix.check-pqc\n```\n\n### Telemetry / data sent\n\nThe CLI sends **hostname + port only**, and **only in online mode**. No\nrequest bodies, no headers, no credentials. `--offline` makes zero\noutbound calls. The API drops requester IPs after 7 days. Full details:\n[checkpqc.com/privacy](https://checkpqc.com/privacy).\n\n### Logs and where to find them\n\n`check-pqc` itself does not write a log file — output goes to stdout.\nIf you want a persistent record, redirect:\n\n```bash\ncheck-pqc api.example.com --json >> /var/log/checkpqc/api.log\n```\n\nThe **API** keeps a 7-day rotating audit log on the server (operator\nside, not visible to clients). If you self-host the API, see\n`/var/log/checkpqc/contact.log` and the systemd journal:\n\n```bash\njournalctl -u checkpqc-api -f\n```\n\n### Troubleshooting\n\n| Symptom | Likely cause | Fix |\n|---|---|---|\n| `command not found: check-pqc` | npm prefix not on `$PATH` | `export PATH=\"$(npm config get prefix)/bin:$PATH\"` in your shell rc |\n| `UNKNOWN — handshake aborted` | Target firewalled, blocking your IP, or down | Try from another network; retry; check target on port 443 |\n| `--check-offline` says \"not capable\" | Local OpenSSL too old | `brew upgrade openssl@3` (macOS) or use `docker run ghcr.io/aegyrix/check-pqc:offline` |\n| All hosts return `UNKNOWN` | Outbound TLS to api.checkpqc.app blocked | Use `--offline` mode (requires OpenSSL 3.5+) or run via Docker |\n| `EACCES` during `npm install -g` | npm prefix not user-writable | `sudo npm install -g`, or use a Node version manager (nvm/fnm/asdf) |\n| Slow / hanging probe | DNS resolution slow | The CLI has a 15s overall timeout; increase upstream DNS-cache TTL |\n\nFor anything not in the table, file an issue:\n[github.com/aegyrix/checkpqc.app/issues](https://github.com/aegyrix/checkpqc.app/issues).\n\n---\n\n## Uninstall\n\nThe CLI installs **only one global npm package** (or one container image,\nor one PowerShell module). Removal is one command. No dotfiles, no\nlaunch agents, no daemons, no registry edits — `check-pqc` is fully\nephemeral; every probe is a fresh subprocess.\n\n### npm install\n\n```bash\n# Whichever name you installed under\nnpm uninstall -g check-pqc\nnpm uninstall -g @aegyrix/check-pqc\n\n# Verify\ncommand -v check-pqc          # should print nothing\n```\n\n### Homebrew (if you used `brew install`)\n\n```bash\nbrew uninstall check-pqc\n```\n\n### Docker\n\n```bash\n# Just remove the image — there is no persistent container\ndocker rmi ghcr.io/aegyrix/check-pqc:latest\ndocker rmi ghcr.io/aegyrix/check-pqc:offline\ndocker rmi ghcr.io/aegyrix/check-pqc:0.2.6\n```\n\n### Windows — winget\n\n```powershell\nwinget uninstall aegyrix.check-pqc\n```\n\n### Windows / cross-platform — PowerShell module\n\n```powershell\nUninstall-Module CheckPQC -AllVersions\n```\n\n### What gets left behind?\n\n**Nothing the CLI created itself.** It doesn't write to `~/.checkpqc`,\n`~/.config`, or anywhere else. The only artifacts are whatever output\n**you** redirected (e.g. `> report.txt`, `>> /var/log/checkpqc/audit.log`),\nplus any cron entries or scheduled tasks you wrote.\n\nIf you want a paranoid sweep:\n\n```bash\n# macOS / Linux\nwhich check-pqc                                     # confirm gone\nls -la \"$(npm config get prefix)/bin/check-pqc\" 2>/dev/null   # confirm gone\ncrontab -l 2>/dev/null | grep -i check-pqc          # check for cron entries\n```\n\n```powershell\n# Windows\nGet-Command check-pqc -ErrorAction SilentlyContinue\nGet-ScheduledTask | Where-Object { $_.Actions.Execute -match 'check-pqc' }\n```\n\n---\n\n## Privacy\n\n- The CLI sends **only the hostname and port** to `api.checkpqc.app` in\n  online mode — never request bodies, never headers.\n- Request IPs are dropped from logs after a sliding 7-day window.\n- `--offline` mode makes **zero** outbound API calls.\n- See [checkpqc.com/privacy](https://checkpqc.com/privacy).\n\n## License\n\nMIT — © Aegyrix LLC\n\n","readmeFilename":"README.md"}