pqcheck 0.7.5 → 0.7.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -2
- package/bin/pqcheck.js +25 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -12,6 +12,12 @@ The same scanner that powers [quantapact.com](https://quantapact.com), the brows
|
|
|
12
12
|
|
|
13
13
|
---
|
|
14
14
|
|
|
15
|
+
## What's new in 0.7.6
|
|
16
|
+
|
|
17
|
+
User-Agent now consistently tags the subcommand on every request (`pqcheck-cli/0.7.6 (scan)`, `(lock)`, `(deps)`, `(history)`, `(watch)`). Lets the server aggregate adoption by subcommand. No new data collected — the subcommand token rides inside the User-Agent header that has always been logged anonymously. See [privacy](https://quantapact.com/privacy) and [CHANGELOG.md](./CHANGELOG.md).
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
15
21
|
## What it does
|
|
16
22
|
|
|
17
23
|
`pqcheck` scans any HTTPS domain and computes its **Decryption Blast Radius score** — the first continuous metric for harvest-now-decrypt-later (HNDL) risk. Every other TLS scanner answers "is post-quantum cryptography enabled?" with yes/no. `pqcheck` answers the question that actually matters: *if an adversary harvests this traffic today and decrypts it in 2035, how much past + future data unlocks?*
|
|
@@ -179,7 +185,7 @@ curl -s "https://www.quantapact.com/api/scan?domain=stripe.com" | jq '.grade, .s
|
|
|
179
185
|
|
|
180
186
|
Full API reference at [quantapact.com/api](https://quantapact.com/api).
|
|
181
187
|
|
|
182
|
-
**Rate
|
|
188
|
+
**Rate limits:** 300 scans per hour per IP, 20 `--fresh` (force-refresh) scans per hour per IP. No API key required. Returns HTTP 429 if exceeded — back off and retry, or [let us know via the feedback form](https://quantapact.com/feedback) if you need higher limits (we're prioritizing the API tier based on real demand).
|
|
183
189
|
|
|
184
190
|
## Methodology
|
|
185
191
|
|
|
@@ -233,6 +239,8 @@ MIT. © 2026 Quantapact.
|
|
|
233
239
|
|
|
234
240
|
---
|
|
235
241
|
|
|
236
|
-
**Source:** [github.com/quantapact/pqcheck](https://github.com/quantapact/pqcheck)
|
|
242
|
+
**Source:** [github.com/quantapact/pqcheck](https://github.com/quantapact/pqcheck)
|
|
243
|
+
|
|
244
|
+
**Changelog:** [CHANGELOG.md](./CHANGELOG.md) for version-by-version release notes.
|
|
237
245
|
|
|
238
246
|
**Issues / feedback:** [quantapact.com/feedback](https://quantapact.com/feedback) or open an issue on the repo.
|
package/bin/pqcheck.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
// =============================================================================
|
|
8
8
|
|
|
9
9
|
const API_BASE = process.env.PQCHECK_API_BASE || "https://quantapact.com";
|
|
10
|
-
const VERSION = "0.7.
|
|
10
|
+
const VERSION = "0.7.7";
|
|
11
11
|
|
|
12
12
|
const ANSI = {
|
|
13
13
|
reset: "\x1b[0m",
|
|
@@ -107,28 +107,45 @@ async function main() {
|
|
|
107
107
|
return; // runWatch handles its own exit; in practice it runs until killed.
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
+
// --fresh: bypass server cache, force a fresh scan. Useful when verifying
|
|
111
|
+
// a cert/key change you just deployed. Subject to a 20/hr per-IP cap on
|
|
112
|
+
// the server side.
|
|
113
|
+
const fresh = args.includes("--fresh") || args.includes("--force");
|
|
114
|
+
|
|
110
115
|
// One-shot scan(s)
|
|
111
116
|
let worstExit = 0;
|
|
112
117
|
for (const domain of domains) {
|
|
113
|
-
const exit = await runOneScan({ domain, format, quiet, threshold, webhookUrl, multi: domains.length > 1 });
|
|
118
|
+
const exit = await runOneScan({ domain, format, quiet, threshold, webhookUrl, multi: domains.length > 1, fresh });
|
|
114
119
|
if (exit > worstExit) worstExit = exit;
|
|
115
120
|
}
|
|
116
121
|
process.exit(worstExit);
|
|
117
122
|
}
|
|
118
123
|
|
|
119
|
-
async function runOneScan({ domain, format, quiet, threshold, webhookUrl, multi }) {
|
|
120
|
-
if (!quiet && format === "text") process.stderr.write(color("dim", `Scanning ${domain} ...`));
|
|
124
|
+
async function runOneScan({ domain, format, quiet, threshold, webhookUrl, multi, fresh }) {
|
|
125
|
+
if (!quiet && format === "text") process.stderr.write(color("dim", `Scanning ${domain}${fresh ? " (forcing fresh)" : ""} ...`));
|
|
121
126
|
let report;
|
|
122
127
|
try {
|
|
123
|
-
|
|
128
|
+
// --fresh appends ?force=1 to bypass the smart-cache. Use when verifying
|
|
129
|
+
// a cert/key change you just deployed — otherwise scans hit the 1h SWR
|
|
130
|
+
// cache and return up-to-1h-old data. Subject to a 20/hr per-IP cap on
|
|
131
|
+
// the server side; if exceeded, the server silently downgrades to a
|
|
132
|
+
// cached scan and returns that instead of erroring.
|
|
133
|
+
const qs = fresh ? `?domain=${encodeURIComponent(domain)}&force=1` : `?domain=${encodeURIComponent(domain)}`;
|
|
134
|
+
const resp = await fetch(`${API_BASE}/api/scan${qs}`, {
|
|
124
135
|
method: "GET",
|
|
125
|
-
headers: { accept: "application/json", "user-agent": `pqcheck-cli/${VERSION}` },
|
|
136
|
+
headers: { accept: "application/json", "user-agent": `pqcheck-cli/${VERSION} (scan)` },
|
|
126
137
|
});
|
|
127
138
|
if (!quiet && format === "text") process.stderr.write("\r\x1b[K");
|
|
128
139
|
if (!resp.ok) {
|
|
129
140
|
const errBody = await safeJSON(resp);
|
|
130
141
|
console.error(color("red", `error scanning ${domain}: ${resp.status} ${errBody?.error || resp.statusText}`));
|
|
131
142
|
if (errBody?.detail) console.error(color("dim", errBody.detail));
|
|
143
|
+
// Surface the 429 upsell hint if present — tells users how to ask for
|
|
144
|
+
// higher limits via the feedback form. Same demand signal we capture
|
|
145
|
+
// on the homepage.
|
|
146
|
+
if (resp.status === 429 && errBody?.need_more?.feedback_url) {
|
|
147
|
+
console.error(color("dim", `${errBody.need_more.message} → ${errBody.need_more.feedback_url}`));
|
|
148
|
+
}
|
|
132
149
|
return 1;
|
|
133
150
|
}
|
|
134
151
|
report = await resp.json();
|
|
@@ -215,7 +232,7 @@ async function runWatch({ domains, format, quiet, threshold, webhookUrl, interva
|
|
|
215
232
|
try {
|
|
216
233
|
const resp = await fetch(`${API_BASE}/api/scan?domain=${encodeURIComponent(domain)}`, {
|
|
217
234
|
method: "GET",
|
|
218
|
-
headers: { accept: "application/json", "user-agent": `pqcheck-cli/${VERSION}` },
|
|
235
|
+
headers: { accept: "application/json", "user-agent": `pqcheck-cli/${VERSION} (watch)` },
|
|
219
236
|
});
|
|
220
237
|
if (!resp.ok) continue;
|
|
221
238
|
const report = await resp.json();
|
|
@@ -547,6 +564,7 @@ ${color("bold", "Common flags:")}
|
|
|
547
564
|
-q, --quiet Print only the numeric score
|
|
548
565
|
--watch [seconds] Poll every N seconds (default 300) and report changes
|
|
549
566
|
--webhook <url> POST scan results to a URL (one-shot or each watch tick)
|
|
567
|
+
--fresh Bypass server cache, force a fresh scan (subject to 20/hr per-IP cap)
|
|
550
568
|
|
|
551
569
|
${color("bold", "Subcommand-specific:")}
|
|
552
570
|
pqcheck deps:
|