pqcheck 0.16.10 → 0.16.13
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 +100 -68
- package/bin/cipherwake-statusline.js +40 -1
- package/bin/pqcheck.js +202 -11
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,24 +1,50 @@
|
|
|
1
1
|
# pqcheck
|
|
2
2
|
|
|
3
|
-
> **
|
|
3
|
+
> **Type `npx pqcheck stripe.com`** (or any HTTPS domain) to scan its public posture in seconds — Decryption Blast Radius grade (0–10), letter (A–F), and findings ranked by severity. No signup, no API key, per-IP rate limited.
|
|
4
|
+
>
|
|
5
|
+
> **Or, for your own deploys:** wire `npx pqcheck deploy-check yourdomain.com --ai` into Claude Code / Cursor / Copilot. Your AI coder parses `ship_decision=pass|review|block` and decides whether to announce the deploy, ask you, or stop.
|
|
4
6
|
|
|
5
7
|
[](https://www.npmjs.com/package/pqcheck)
|
|
6
8
|
[](https://www.npmjs.com/package/pqcheck)
|
|
7
9
|
[](./LICENSE)
|
|
8
10
|
|
|
11
|
+
> **Latest: v0.16.12** — ⚠️ **Existing GitHub Action users:** one-line fix required in `.github/workflows/cipherwake.yml` (`uses: cipherwakelabs/pqcheck@v3` → `cipherwakelabs/pqcheck/action@v3`). The old ref was broken since v0.15 and silently failed every CI run; today's end-to-end test caught it. Re-running `pqcheck onboard` also regenerates the workflow correctly. Plus: README rewritten to advertise both `pqcheck <domain>` and `pqcheck deploy-check --ai` equally, rate limits corrected. [Full changelog →](./CHANGELOG.md)
|
|
12
|
+
|
|
13
|
+
## Two ways to use it
|
|
14
|
+
|
|
15
|
+
### 1. Scan any domain — no signup, no API key, no per-account quota
|
|
16
|
+
|
|
9
17
|
```bash
|
|
10
|
-
npx pqcheck
|
|
18
|
+
npx pqcheck stripe.com
|
|
11
19
|
```
|
|
12
20
|
|
|
13
21
|
```
|
|
14
|
-
◆ Cipherwake ·
|
|
22
|
+
◆ Cipherwake · stripe.com DBR 2.3 B · 1 finding
|
|
15
23
|
|
|
16
24
|
Top finding:
|
|
17
|
-
[
|
|
25
|
+
[MEDIUM] Intermediate cert uses RSA — quantum-vulnerable chain link
|
|
26
|
+
|
|
27
|
+
Full report: https://cipherwake.io/r/stripe.com
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Anonymous. Per-IP rate limited (120 scans/hour) for cost protection — that's it. Use it to spot-check a vendor before signing, audit a competitor's HTTPS posture, or just satisfy "I wonder how `<domain>` grades."
|
|
31
|
+
|
|
32
|
+
### 2. Gate your own deploys with your AI coder
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
npx pqcheck deploy-check yourdomain.com --ai
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
◆ Cipherwake · yourdomain.com ⚠ REVIEW · 2 changes since last scan · HIGH
|
|
40
|
+
|
|
41
|
+
Surface changes:
|
|
42
|
+
+ New third-party script: widget.intercom.io
|
|
43
|
+
~ Strict-Transport-Security weakened: max-age=31536000 → max-age=3600
|
|
18
44
|
|
|
19
45
|
CIPHERWAKE_AI_GUARD_RESULT
|
|
20
46
|
ship_decision=review
|
|
21
|
-
top_issue=
|
|
47
|
+
top_issue=vendor.new_third_party_script
|
|
22
48
|
END_CIPHERWAKE_AI_GUARD_RESULT
|
|
23
49
|
```
|
|
24
50
|
|
|
@@ -28,70 +54,108 @@ The last block — `CIPHERWAKE_AI_GUARD_RESULT` — is what your AI coding agent
|
|
|
28
54
|
- **review** — stop and ask you what to do (your HTTPS posture drifted vs. last scan)
|
|
29
55
|
- **block** — refuse to announce until you investigate (something critical changed)
|
|
30
56
|
|
|
31
|
-
Zero install. Works in any terminal with Node 18+.
|
|
57
|
+
Zero install. Works in any terminal with Node 18+. Both modes are free — no signup, no API key required for first use of either.
|
|
32
58
|
|
|
33
59
|
## What pqcheck actually checks
|
|
34
60
|
|
|
35
|
-
|
|
61
|
+
### Bare scan (`pqcheck <domain>`) — current-state posture grade
|
|
36
62
|
|
|
37
|
-
|
|
38
|
-
- a **letter grade** (A–F)
|
|
39
|
-
- a **findings list** ranked by severity — TLS cipher suites, certificate quality, security headers (CSP / HSTS / etc.), third-party scripts loaded by the page, key reuse across subdomains, and more
|
|
63
|
+
Scans any public HTTPS surface and produces a **DBR score** (0–10), a **letter grade** (A–F), and a **findings list** ranked by severity. Same scanner that powers [cipherwake.io](https://cipherwake.io), the browser extension, and the GitHub Action — what it checks:
|
|
40
64
|
|
|
41
|
-
|
|
65
|
+
- **TLS posture** — ciphersuite class, hybrid PQC key agreement (`X25519MLKEM768`), forward secrecy
|
|
66
|
+
- **Certificate chain** — issuer, intermediate quality, key reuse across rotations (★ unique to pqcheck), wildcard / subdomain blast radius
|
|
67
|
+
- **Security headers** — HSTS (preload + max-age), CSP, X-Frame-Options, Referrer-Policy, Permissions-Policy, COOP, CORP
|
|
68
|
+
- **Email security** — SPF, DMARC, DKIM (~30 selectors probed including Resend/Mailgun/SES), BIMI
|
|
69
|
+
- **Supply chain** — every third-party script loaded by the page, graded for quantum risk
|
|
70
|
+
- **Subdomain takeover** — fingerprint scan against AWS S3, GitHub Pages, Heroku, Shopify, Fastly, etc.
|
|
71
|
+
|
|
72
|
+
### Deploy gate (`pqcheck deploy-check --ai`) — drift since your last scan
|
|
73
|
+
|
|
74
|
+
Compares your site's public HTTPS surface *now* against your last scan and surfaces what changed:
|
|
75
|
+
|
|
76
|
+
- **New third-party scripts** loading on the page that weren't there before (the Polyfill.io-class supply-chain risk)
|
|
77
|
+
- **Header regressions** — CSP weakened, HSTS shortened or removed, X-Frame-Options dropped, permissive tokens (`'unsafe-inline'`, `*`) introduced
|
|
78
|
+
- **Certificate / SPKI changes** — unexpected rotations, key reuse across renewals, intermediate-chain changes
|
|
79
|
+
- **TLS posture changes** — ciphersuite shifts, hybrid PQC key-agreement added or removed
|
|
80
|
+
- **Vendor surface changes** — new email senders (SPF/DMARC), new CDN origins, new analytics endpoints
|
|
81
|
+
- **Subdomain takeover exposure** — new dangling CNAMEs
|
|
82
|
+
|
|
83
|
+
The gate routes each change through the same DBR severity model above to decide `pass` / `review` / `block`. Cosmetic drift passes; high-severity drift (new script from an unknown origin, header regression that breaks defense-in-depth, cert SPKI change without a rotation event) triggers `review`; critical drift (expired cert served, takeover-vulnerable subdomain, malicious vendor injected) triggers `block`. DBR's full severity rubric: [/methodology/decryption-blast-radius](https://cipherwake.io/methodology/decryption-blast-radius).
|
|
84
|
+
|
|
85
|
+
On **first use** with no prior scan to diff against, `deploy-check` falls back to the bare scan's absolute-posture grading to give you a baseline. Every subsequent run is drift-relative to the previous scan.
|
|
42
86
|
|
|
43
87
|
---
|
|
44
88
|
|
|
45
89
|
## Commands at a glance
|
|
46
90
|
|
|
91
|
+
Grouped by intent: **deploy gate** (the flagship wedge) → **drift comparison** (the engine the gate runs on) → **AI setup / install** → **workflow scaffolds** → **committable artifacts** → **posture grade + tracking** → **diagnostic**. Every CLI command is listed here exactly once; flags / output formats / exit codes are in [Flags, formats & exit codes](#flags-formats--exit-codes) further down.
|
|
92
|
+
|
|
47
93
|
| Command | What it gives you |
|
|
48
94
|
|---|---|
|
|
49
|
-
|
|
|
50
|
-
| `npx pqcheck deploy-check <domain> --ai` | The flagship
|
|
95
|
+
| **Deploy gate — the flagship wedge** | |
|
|
96
|
+
| `npx pqcheck deploy-check <domain> --ai` | **The flagship.** Scans the domain, compares against the previous scan (first run sets the baseline), and emits a `ship_decision=pass\|review\|block` field your AI coding agent parses to decide whether to announce the deploy, ask you, or stop. Works anonymously — no signup needed. |
|
|
97
|
+
| **`npx pqcheck guard --domain <D> -- <cmd>`** | **Deploy guard wrapper.** Wraps any deploy command. Runs `deploy-check` first; conditionally runs `<cmd>` based on `ship_decision`. Modes: `--gate-mode balanced` (default) / `advisory` / `strict`. ONE command instead of two — the strongest single artifact for AI-coder workflows because the AI never has to remember to chain check + deploy. |
|
|
98
|
+
| **`--ai` flag** (any of the above) | **AI Coder Mode.** Three-layer output (banner / body / structured `CIPHERWAKE_AI_GUARD_RESULT` block) tuned for Claude Code / Cursor / Aider / Zed. Includes a `ship_decision=pass\|review\|block` field your AI coworker parses to decide whether to announce the deploy, ask you, or revert. See [/methodology/ai-coder-mode](https://cipherwake.io/methodology/ai-coder-mode). |
|
|
99
|
+
| **Drift comparison — what the gate runs on** | |
|
|
51
100
|
| `npx pqcheck trust-diff <domain>` | Compare today's HTTPS surface against a saved baseline (last week / last month / a saved CI baseline). For CI gates and release checklists. |
|
|
52
|
-
| `npx pqcheck preview-diff --preview <URL> --production <URL>` | Compare a Vercel/Netlify preview deployment URL to production. Surfaces new third-party scripts, header regressions, and DBR score drops *inside the PR*, before merge.
|
|
53
|
-
|
|
|
101
|
+
| `npx pqcheck preview-diff --preview <URL> --production <URL>` | Compare a Vercel/Netlify preview deployment URL to production. Surfaces new third-party scripts, header regressions, and DBR score drops *inside the PR*, before merge. Renders per-signal N vs N+1 status on every run (scripts, headers, cert SPKI, TLS, …) so you can see *every* check fired, not just "did something change." Add `--verbose` for the full side-by-side table. |
|
|
102
|
+
| **AI setup / install** | |
|
|
103
|
+
| **`npx pqcheck setup --auto --domain <D>`** | **One-command full setup for every AI coder.** Installs (idempotently): GitHub Action workflow, AI Coder Protocol across all detected rules files (Claude / Cursor / Copilot / Aider / Windsurf / Continue / Cline / AGENTS.md) using fenced markers (`<!-- CIPHERWAKE_AI_CODER_PROTOCOL_START/END -->`), git pre-push hook, Claude Code statusLine + 2 hooks (PostToolUse Bash + **UserPromptSubmit**), per-repo `.cipherwake/last-status.json` for Cursor / Copilot / Continue to read as context. Skip flags available. Backups taken before any `~/.claude/settings.json` write. Audit trail at `~/.config/cipherwake/install-prefs.json`; install manifest at `~/.config/cipherwake/install-manifest.json`. |
|
|
104
|
+
| **`npx pqcheck setup --plan --domain <D>`** | **Dry-run mode.** Prints every file change `--auto` would make (target paths + operation type: create / append-markered / deep-merge / backup-first) without writing anything. Run this first when you're not sure what `--auto` will touch. |
|
|
105
|
+
| **`npx pqcheck protocol install`** | **Opt-in installer** for the AI Coder Protocol — appends the pre-deploy verification rule to your `CLAUDE.md` / `.cursorrules` / `.aider.conf.yml` with explicit consent (Rule 17). One upfront question (auto / manual / no). Never silent writes. |
|
|
106
|
+
| **`UserPromptSubmit` hook** | **Claude sees `ship_decision` before responding to every prompt.** When `pqcheck setup --auto` runs, it wires `cipherwake-prompt-hook` as a Claude Code UserPromptSubmit hook. On every user prompt, the hook injects `additionalContext` with the current scan's `ship_decision` IF it's `review`/`block` and the state is <24h old. Silent when state is missing, stale, or `pass`. Different timing from the PostToolUse chat-hook: this fires *before* Claude thinks (proactive), the chat-hook fires *after* a Bash command (reactive). |
|
|
107
|
+
| **Per-repo state file** `.cipherwake/last-status.json` | **Cursor / Copilot / Continue read this for workspace context.** Every `pqcheck` scan writes the same payload as the per-user file. Created by `setup --auto`; auto-added to `.gitignore` (per-developer state, not committable). Gives AI agents inside VS Code-family editors a repo-local artifact they pick up automatically when reading workspace files. |
|
|
108
|
+
| **Workflow scaffolds** | |
|
|
54
109
|
| `npx pqcheck onboard <domain>` | One command: scan → scaffold the GitHub Action → capture a vendor lockfile → set a baseline → commit + push. Zero copy-paste from docs. |
|
|
55
|
-
|
|
|
56
|
-
|
|
|
57
|
-
|
|
|
58
|
-
|
|
|
59
|
-
|
|
|
60
|
-
|
|
|
61
|
-
|
|
|
62
|
-
|
|
|
110
|
+
| `npx pqcheck init` | Interactive scaffold for `.github/workflows/cipherwake.yml`. Use when you want manual control instead of `onboard`'s all-in-one flow. |
|
|
111
|
+
| `npx pqcheck release-checklist [domain]` | Pre-release trust checklist (markdown, offline). Paste into release notes. |
|
|
112
|
+
| `npx pqcheck vendors export/check/sync <domain>` | Vendor lockfile (`cipherwake.vendors.json`) + CI gate that exits non-zero when a new third-party origin appears. Like `package-lock.json` for vendor scripts. |
|
|
113
|
+
| **Committable artifacts (SBOM-style)** | |
|
|
114
|
+
| `npx pqcheck lock <domain>` | Generate `cipherwake.lock` (QXM committable manifest) + human-readable `cipherwake-report.md`. SBOM-style artifact for quantum exposure — commit both, diff in PRs. |
|
|
115
|
+
| `npx pqcheck diff <old.lock> <new.lock>` | Compare two QXM lockfiles; exit 2 on regression. For CI PR-comment diffs. |
|
|
116
|
+
| `npx pqcheck deps <domain>` | Scan all third-party origins on the page (supply-chain HNDL grading). `--lock` writes `cipherwake-deps.lock` + `.md`; `--allowlist`, `--baseline`, `--fail-on-new` for CI vendor gates. |
|
|
117
|
+
| `npx pqcheck cert <file.pem>` | Analyze a local PEM/CRT cert file offline (no network). |
|
|
118
|
+
| **Posture grade + tracking** | |
|
|
119
|
+
| `npx pqcheck <domain>` | **Posture grade (no diff baseline).** Returns DBR score (0–10), letter grade (A–F), and a list of findings ranked by severity. Use when you want a one-shot health check without setting up a baseline — for first-time audits, ad-hoc spot checks, or grading a domain you don't own. For ongoing deploys, use `deploy-check` instead. |
|
|
120
|
+
| `npx pqcheck history <domain>` | 90-day score history (sparkline + samples). `--days <N>` to change window. |
|
|
121
|
+
| `npx pqcheck changes <domain>` | Summarize public attack-surface changes in last 14 days. |
|
|
122
|
+
| `npx pqcheck watch <domain>` | Add domain to your watched list on the server (needs `CIPHERWAKE_API_KEY`). Distinct from the `--watch <secs>` flag, which is local polling. |
|
|
123
|
+
| **Diagnostic** | |
|
|
124
|
+
| **`npx pqcheck debug-network`** | **Connectivity diagnostic.** Probes cipherwake.io API, homepage, crt.sh upstream, and the direct Vercel URL (bypassing Cloudflare). Reports HTTP status + timing per hop. Use when "scan hung" / "command not found" / corporate proxy issues come up — surfaces the actual broken hop with an actionable cause list. |
|
|
63
125
|
|
|
64
126
|
Free tier covers all of the above within 100 Trust Diff calls/month per repo via OIDC. **Founder Pro** ($19.99/mo, locked while subscription active) raises that to 5,000 calls/month + unlocks custom thresholds, vendor lockfile, CI fail rules, and 5 watched domains. Single-domain scans (`npx pqcheck <domain>`) are anonymous + rate-limited per IP — no account or key needed. `npx pqcheck deploy-check <domain> --ai` also works fully anonymously for first-deploy gating.
|
|
65
127
|
|
|
66
128
|
### AI Coder Mode in 30 seconds
|
|
67
129
|
|
|
68
130
|
```bash
|
|
69
|
-
npx pqcheck cipherwake.io --ai
|
|
131
|
+
npx pqcheck deploy-check cipherwake.io --ai
|
|
70
132
|
```
|
|
71
133
|
|
|
72
134
|
Output:
|
|
73
135
|
|
|
74
136
|
```
|
|
75
|
-
◆ Cipherwake · cipherwake.io ⚠ REVIEW ·
|
|
137
|
+
◆ Cipherwake · cipherwake.io ⚠ REVIEW · 1 change since last scan · HIGH
|
|
76
138
|
|
|
77
|
-
|
|
78
|
-
|
|
139
|
+
Surface changes:
|
|
140
|
+
+ New third-party script: widget.intercom.io
|
|
141
|
+
Loaded from an origin not present in your last scan.
|
|
79
142
|
|
|
80
143
|
Why it matters:
|
|
81
|
-
|
|
82
|
-
|
|
144
|
+
New third-party scripts execute in full page context. A script that
|
|
145
|
+
appeared without an intentional code change = supply-chain risk vector
|
|
146
|
+
(Polyfill.io-class). Confirm it was added on purpose.
|
|
83
147
|
|
|
84
148
|
Recommended next action:
|
|
85
|
-
Review
|
|
149
|
+
Review the change above and decide if it was intentional.
|
|
86
150
|
View full report: https://cipherwake.io/r/cipherwake.io
|
|
87
|
-
Re-scan
|
|
151
|
+
Re-scan after fix: npx pqcheck deploy-check cipherwake.io --ai
|
|
88
152
|
|
|
89
153
|
CIPHERWAKE_AI_GUARD_RESULT
|
|
90
154
|
status=review
|
|
91
155
|
domain=cipherwake.io
|
|
92
156
|
ship_decision=review
|
|
93
157
|
max_severity=high
|
|
94
|
-
top_issue=
|
|
158
|
+
top_issue=vendor.new_third_party_script
|
|
95
159
|
advisory_only=true
|
|
96
160
|
END_CIPHERWAKE_AI_GUARD_RESULT
|
|
97
161
|
```
|
|
@@ -215,15 +279,7 @@ npx pqcheck vendors sync mycompany.com # Founder Pro — pull dashboard all
|
|
|
215
279
|
|
|
216
280
|
`pqcheck deps` also surfaces a one-line site-wide **CSP verdict** above the supply-chain table (`✗ No CSP enforcement` / `⚠ CSP is permissive` / `✓ Strict CSP enforced`) and friendly vendor labels (`New Relic · errors` / `Cloudflare · cdn` / `Adobe Fonts · fonts`) instead of raw hostnames. Same data shape ships on `/r/<domain>` and in the browser extension.
|
|
217
281
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
```bash
|
|
221
|
-
npx pqcheck init # interactive scaffold for .github/workflows/cipherwake.yml
|
|
222
|
-
npx pqcheck deploy-check # pre-deploy gate (Trust Diff alias, last-scan baseline)
|
|
223
|
-
npx pqcheck release-checklist # markdown trust checklist for release notes (offline)
|
|
224
|
-
```
|
|
225
|
-
|
|
226
|
-
The GitHub Action posts a **sticky PR comment** with results when `comment-on-pr: true` is set on `pull_request` events. Comment auto-edits on subsequent pushes — no spam.
|
|
282
|
+
> **GitHub Action note:** when scaffolded via `pqcheck onboard` or `pqcheck init`, the Action posts a **sticky PR comment** with results when `comment-on-pr: true` is set on `pull_request` events. The comment auto-edits on subsequent pushes — no spam.
|
|
227
283
|
|
|
228
284
|
---
|
|
229
285
|
|
|
@@ -243,33 +299,9 @@ Plus a full ASM check suite for credibility:
|
|
|
243
299
|
- **HTTP header security** — HSTS (with preload + max-age), CSP, X-Frame-Options, Referrer-Policy, Permissions-Policy, COOP, CORP
|
|
244
300
|
- **Subdomain takeover detection** — fingerprint-based scan against AWS S3, GitHub Pages, Heroku, Shopify, Fastly, etc.
|
|
245
301
|
|
|
246
|
-
##
|
|
302
|
+
## Flags, formats & exit codes
|
|
247
303
|
|
|
248
|
-
|
|
249
|
-
npx pqcheck <domain> Scan + print human-readable report
|
|
250
|
-
npx pqcheck lock <domain> Generate cipherwake.lock (QXM) committable manifest
|
|
251
|
-
npx pqcheck deps <domain> Scan all third-party origins on the page (supply-chain HNDL)
|
|
252
|
-
npx pqcheck diff <old.lock> <new.lock> Compare two QXM lockfiles; exit 2 on regression
|
|
253
|
-
npx pqcheck history <domain> Show 90-day score history (sparkline + samples)
|
|
254
|
-
npx pqcheck changes <domain> Summarize public attack-surface changes in last 14 days
|
|
255
|
-
npx pqcheck cert <file.pem> Analyze a local PEM/CRT cert file (offline, no network)
|
|
256
|
-
npx pqcheck trust-diff <domain> Trust Diff vs configured baseline; CI gate (Free: 100/repo/mo)
|
|
257
|
-
npx pqcheck preview-diff --preview U --production U Preview-URL vs production-URL diff; new scripts + header regressions + score drops (NEW in 0.14.0)
|
|
258
|
-
npx pqcheck deploy-check <domain> Pre-deploy gate (Trust Diff alias with last-scan baseline)
|
|
259
|
-
npx pqcheck onboard <domain> One-command setup wizard (scan + init + vendors + checklist)
|
|
260
|
-
npx pqcheck init Interactive scaffold for .github/workflows/cipherwake.yml
|
|
261
|
-
npx pqcheck release-checklist [domain] Pre-release trust checklist (markdown, offline)
|
|
262
|
-
npx pqcheck vendors export <domain> Write cipherwake.vendors.json from observed third-party scripts
|
|
263
|
-
npx pqcheck vendors check <domain> CI gate; exit 4 on new origins not in lockfile
|
|
264
|
-
npx pqcheck vendors sync <domain> Pull dashboard allowlist into lockfile (Founder Pro, needs API key)
|
|
265
|
-
npx pqcheck watch <domain> Add domain to your watched list (needs CIPHERWAKE_API_KEY)
|
|
266
|
-
npx pqcheck guard --domain <D> -- <cmd> AI Coder Mode (0.15.0) — wrap any deploy command with a Trust Diff gate
|
|
267
|
-
npx pqcheck deploy-check <D> --ai AI Coder Mode (0.15.0) — frictionless first-deploy, anonymous, emits CIPHERWAKE_AI_GUARD_RESULT block
|
|
268
|
-
npx pqcheck setup --auto --domain <D> AI Coder Mode (0.15.0) — one-command install across CLAUDE.md/.cursorrules/.github + git pre-push hook + statusline
|
|
269
|
-
npx pqcheck setup --plan --domain <D> AI Coder Mode (0.15.0) — dry-run: print every file change before --auto writes anything
|
|
270
|
-
npx pqcheck protocol install --auto AI Coder Mode (0.15.0) — append AI Coder Protocol to detected rules files (idempotent, fenced markers)
|
|
271
|
-
npx pqcheck debug-network Probe upstream connectivity (cipherwake.io / crt.sh / Vercel) — for "scan hung" diagnosis
|
|
272
|
-
```
|
|
304
|
+
Every CLI command is documented in [Commands at a glance](#commands-at-a-glance) above. What follows is reference material for usage patterns, flags, output formats, and exit codes shared across commands.
|
|
273
305
|
|
|
274
306
|
### Multi-domain
|
|
275
307
|
|
|
@@ -422,7 +454,7 @@ curl -s "https://www.cipherwake.io/api/scan?domain=stripe.com" | jq '.grade, .sc
|
|
|
422
454
|
|
|
423
455
|
Full API reference at [cipherwake.io/api](https://cipherwake.io/api).
|
|
424
456
|
|
|
425
|
-
**Rate limits:**
|
|
457
|
+
**Rate limits:** 120 scans per hour per IP for anonymous CLI use, 20 `--fresh` (force-refresh) scans per hour per IP. **Authenticated paths bypass this:** GitHub Actions OIDC (Free = 100 calls/month per repo) and API key (Founder Pro = 5,000/month per account) each have their own per-account / per-repo quota with no per-IP cap. No API key required for the anonymous path. Returns HTTP 429 if exceeded — back off and retry, or [let us know via the feedback form](https://cipherwake.io/feedback) if you need higher limits.
|
|
426
458
|
|
|
427
459
|
## Methodology
|
|
428
460
|
|
|
@@ -109,6 +109,10 @@ const {
|
|
|
109
109
|
domain, score, grade, ship_decision, written_at, max_severity, unreachable,
|
|
110
110
|
// v0.16.4 — preview-diff-specific fields for the 4-line render
|
|
111
111
|
kind, delta_count, diff_no_change, sector_ranking, verified_signal_categories, last_changed,
|
|
112
|
+
// v0.16.11 — top finding ID so REVIEW/BLOCK lines say WHAT'S WRONG
|
|
113
|
+
// inline. Without this, the customer sees "⚠ REVIEW" with no cause and
|
|
114
|
+
// has to run a separate `pqcheck deploy-check --ai` to find out why.
|
|
115
|
+
top_issue,
|
|
112
116
|
} = state;
|
|
113
117
|
const age = ageHours(written_at);
|
|
114
118
|
|
|
@@ -179,6 +183,41 @@ if (!isUnreachable && stabilitySource) {
|
|
|
179
183
|
else stabilitySegment = ` · stable ${days}d`;
|
|
180
184
|
}
|
|
181
185
|
|
|
186
|
+
// v0.16.11 — terse human label for the top_issue finding ID. Only rendered
|
|
187
|
+
// when ship_decision is review or block (the cases where the customer needs
|
|
188
|
+
// to know WHY without running another command). Mapping covers the
|
|
189
|
+
// finding IDs from lib/findingRegistry.ts that drive verdicts; for unknown
|
|
190
|
+
// IDs, derive a fallback by taking the last dotted segment and replacing
|
|
191
|
+
// underscores with spaces. "as few words as possible" — typical render is
|
|
192
|
+
// 2-4 words; never more than ~30 chars.
|
|
193
|
+
const TOP_ISSUE_LABELS = {
|
|
194
|
+
"chain.weakest_link.intermediate": "weak intermediate cert",
|
|
195
|
+
"chain.weakest_link.root": "weak root cert",
|
|
196
|
+
"tls.ecdhe_only_quantum_vulnerable": "quantum-vulnerable kex",
|
|
197
|
+
"tls.pqc_test_inconclusive_scanner_limit": "PQC test inconclusive",
|
|
198
|
+
"tls.rsa_kex_only": "RSA kex only",
|
|
199
|
+
"tls.rsa_kex_fallback": "RSA fallback",
|
|
200
|
+
"tls.rsa_kex_accepted_legacy": "RSA kex accepted",
|
|
201
|
+
"tls.version_obsolete": "obsolete TLS",
|
|
202
|
+
"tls.domain_unreachable": "unreachable",
|
|
203
|
+
"email.spf.missing": "no SPF",
|
|
204
|
+
"email.dmarc.missing": "no DMARC",
|
|
205
|
+
"email.dkim.missing": "no DKIM",
|
|
206
|
+
};
|
|
207
|
+
function topIssueLabel(id) {
|
|
208
|
+
if (!id || id === "none") return null;
|
|
209
|
+
if (TOP_ISSUE_LABELS[id]) return TOP_ISSUE_LABELS[id];
|
|
210
|
+
// Fallback: last dotted segment, underscores → spaces, cap 30 chars.
|
|
211
|
+
const tail = String(id).split(".").pop() || id;
|
|
212
|
+
return tail.replace(/_/g, " ").slice(0, 30);
|
|
213
|
+
}
|
|
214
|
+
const issueSegment = (!isUnreachable && (ship_decision === "review" || ship_decision === "block"))
|
|
215
|
+
? (() => {
|
|
216
|
+
const label = topIssueLabel(top_issue);
|
|
217
|
+
return label ? ` · ${label}` : "";
|
|
218
|
+
})()
|
|
219
|
+
: "";
|
|
220
|
+
|
|
182
221
|
process.stdout.write(
|
|
183
222
|
c(cdec, "◆") +
|
|
184
223
|
" " +
|
|
@@ -188,6 +227,6 @@ process.stdout.write(
|
|
|
188
227
|
" " +
|
|
189
228
|
c(C.bold, displayDomain) +
|
|
190
229
|
" " +
|
|
191
|
-
c(cdec, `${symbol} ${labelWord}`) +
|
|
230
|
+
c(cdec, `${symbol} ${labelWord}${issueSegment}`) +
|
|
192
231
|
c(C.dim, `${dbrSegment}${stabilitySegment} · ${formatAge(written_at)}`)
|
|
193
232
|
);
|
package/bin/pqcheck.js
CHANGED
|
@@ -24,9 +24,9 @@
|
|
|
24
24
|
})();
|
|
25
25
|
|
|
26
26
|
const API_BASE = process.env.PQCHECK_API_BASE || "https://cipherwake.io";
|
|
27
|
-
const VERSION = "0.16.
|
|
27
|
+
const VERSION = "0.16.13";
|
|
28
28
|
|
|
29
|
-
// API-key support — paid
|
|
29
|
+
// API-key support — paid tier (Founder Pro $19.99/mo launch pricing, locked while sub active) gets
|
|
30
30
|
// per-account monthly quotas instead of the per-IP rate limit. Set via:
|
|
31
31
|
// export CIPHERWAKE_API_KEY=qpk_<32-hex>
|
|
32
32
|
// Anonymous CLI use still works (no env var → falls back to IP rate limit).
|
|
@@ -95,6 +95,16 @@ async function main() {
|
|
|
95
95
|
process.exit(0);
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
+
// v0.16.13: opportunistic update-check banner. Reads cached registry
|
|
99
|
+
// result from disk and prints a one-line stderr banner if a newer
|
|
100
|
+
// version is published; refreshes the cache in the background for next
|
|
101
|
+
// time. Never blocks the command. See maybeShowVersionBanner() for the
|
|
102
|
+
// full rationale (TL;DR: prevents the npx-cached-stale-version trap
|
|
103
|
+
// that caused 0.7.0 to silently run for months on existing users).
|
|
104
|
+
// Awaited so the banner appears BEFORE the command output for visibility,
|
|
105
|
+
// but it only reads a tiny file (no network on hot path).
|
|
106
|
+
await maybeShowVersionBanner(args);
|
|
107
|
+
|
|
98
108
|
// Subcommand dispatch.
|
|
99
109
|
if (args[0] === "lock") {
|
|
100
110
|
return runLockCommand(args.slice(1));
|
|
@@ -759,6 +769,169 @@ function formatAiFooterBlock(fields) {
|
|
|
759
769
|
return color("dim", lines.join("\n"));
|
|
760
770
|
}
|
|
761
771
|
|
|
772
|
+
// v0.16.13 — fail-loud AI guard for fetch/quota/server errors during
|
|
773
|
+
// deploy-check. Without this, a network blip during `pqcheck deploy-check
|
|
774
|
+
// --ai` would print a red error to stderr and exit 3 — but the AI Coder
|
|
775
|
+
// Protocol relies on the CIPHERWAKE_AI_GUARD_RESULT block. Missing block =
|
|
776
|
+
// AI agent assumes "no signal" and may continue shipping. The fix: in AI
|
|
777
|
+
// mode, always emit a block with ship_decision=review and a top_issue code
|
|
778
|
+
// the agent can route on. Behaviour in non-AI text mode is unchanged.
|
|
779
|
+
function emitAiGuardReviewAndExit(args, errorDetail) {
|
|
780
|
+
if (parseAiMode(args)) {
|
|
781
|
+
const positional = args.filter((a) => !a.startsWith("-"));
|
|
782
|
+
const domain = positional[0] || "";
|
|
783
|
+
const baseline = parseFlag(args, "--baseline") || "last-scan";
|
|
784
|
+
try {
|
|
785
|
+
console.log("");
|
|
786
|
+
console.log(formatBrandHeader(domain));
|
|
787
|
+
console.log("");
|
|
788
|
+
console.log(color("yellow", " ⚠ REVIEW — Cipherwake deploy-check could not complete"));
|
|
789
|
+
console.log(color("dim", ` ${errorDetail.message}`));
|
|
790
|
+
console.log(color("dim", " Treating as REVIEW per fail-safe policy. Do NOT announce the deploy until you manually verify or rerun the check successfully."));
|
|
791
|
+
console.log(formatAiFooterBlock({
|
|
792
|
+
status: "review",
|
|
793
|
+
domain,
|
|
794
|
+
kind: "trust-diff",
|
|
795
|
+
baseline,
|
|
796
|
+
verdict: "review",
|
|
797
|
+
delta_count: 0,
|
|
798
|
+
max_severity: "unknown",
|
|
799
|
+
ship_decision: "review",
|
|
800
|
+
top_issue: errorDetail.code,
|
|
801
|
+
top_issue_title: errorDetail.message,
|
|
802
|
+
dbr: "",
|
|
803
|
+
grade: "",
|
|
804
|
+
quota_used: "",
|
|
805
|
+
quota_limit: "",
|
|
806
|
+
scanned_at: new Date().toISOString(),
|
|
807
|
+
advisory_only: "true",
|
|
808
|
+
error: errorDetail.code,
|
|
809
|
+
}));
|
|
810
|
+
console.log("");
|
|
811
|
+
// Persist the review state so the IDE statusbar reflects the failure
|
|
812
|
+
// (otherwise the bar stays on the previous successful scan).
|
|
813
|
+
writeLastScanFile({
|
|
814
|
+
domain,
|
|
815
|
+
kind: "trust-diff",
|
|
816
|
+
score: null,
|
|
817
|
+
grade: null,
|
|
818
|
+
max_severity: "unknown",
|
|
819
|
+
ship_decision: "review",
|
|
820
|
+
baseline,
|
|
821
|
+
delta_count: 0,
|
|
822
|
+
top_issue: errorDetail.code,
|
|
823
|
+
error: errorDetail.code,
|
|
824
|
+
}).catch(() => { /* best-effort */ });
|
|
825
|
+
} catch {
|
|
826
|
+
// even error path must not throw — fall through to exit
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
process.exit(errorDetail.exitCode ?? 3);
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
// v0.16.13 — opportunistic version check. Reads a small cache file on cold
|
|
833
|
+
// start; if a newer version is in cache AND we haven't already banner'd
|
|
834
|
+
// today, prints a banner to stderr. Separately, if cache is >24h old, fires
|
|
835
|
+
// off a background fetch (non-blocking) whose result lands for the NEXT
|
|
836
|
+
// invocation. Zero network on hot path. Skipped in machine-readable formats
|
|
837
|
+
// (JSON/SARIF/github) so it never pollutes scripted output. The banner
|
|
838
|
+
// helps users who installed via `npx pqcheck` months ago and have a stale
|
|
839
|
+
// version cached in ~/.npm/_npx/ that they don't know is stale.
|
|
840
|
+
const VERSION_CHECK_TTL_MS = 24 * 60 * 60 * 1000;
|
|
841
|
+
const VERSION_REGISTRY_URL = "https://registry.npmjs.org/pqcheck";
|
|
842
|
+
|
|
843
|
+
async function maybeShowVersionBanner(args) {
|
|
844
|
+
// Skip in machine-parsed output formats.
|
|
845
|
+
const format = parseFlag(args, "--format");
|
|
846
|
+
if (format === "json" || format === "sarif" || format === "github") return;
|
|
847
|
+
// Skip if explicitly disabled (env opt-out).
|
|
848
|
+
if (process.env.PQCHECK_NO_UPDATE_CHECK === "1") return;
|
|
849
|
+
|
|
850
|
+
try {
|
|
851
|
+
const os = await import("node:os");
|
|
852
|
+
const path = await import("node:path");
|
|
853
|
+
const fs = await import("node:fs/promises");
|
|
854
|
+
|
|
855
|
+
const cacheDir = path.join(os.homedir(), ".config", "cipherwake");
|
|
856
|
+
const cachePath = path.join(cacheDir, "version-check.json");
|
|
857
|
+
|
|
858
|
+
let cached = null;
|
|
859
|
+
try {
|
|
860
|
+
cached = JSON.parse(await fs.readFile(cachePath, "utf8"));
|
|
861
|
+
} catch {
|
|
862
|
+
// first run or unreadable — fall through
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
const now = Date.now();
|
|
866
|
+
const stale = !cached || !cached.checked_at || (now - cached.checked_at) > VERSION_CHECK_TTL_MS;
|
|
867
|
+
|
|
868
|
+
// 1. If cache has a known-newer version, print banner now (synchronous,
|
|
869
|
+
// cheap, no network).
|
|
870
|
+
if (cached && cached.latest && isNewerVersion(cached.latest, VERSION)) {
|
|
871
|
+
const lastShownAt = cached.last_shown_at || 0;
|
|
872
|
+
const showThrottleMs = 6 * 60 * 60 * 1000; // at most once per 6h
|
|
873
|
+
if (now - lastShownAt > showThrottleMs) {
|
|
874
|
+
process.stderr.write(
|
|
875
|
+
color("yellow", `\n ⬆ pqcheck ${cached.latest} is available `) +
|
|
876
|
+
color("dim", `(you have ${VERSION}) — run: `) +
|
|
877
|
+
color("bold", "npm i -g pqcheck@latest") +
|
|
878
|
+
color("dim", " (or: rm -rf ~/.npm/_npx && npx pqcheck@latest ...)\n\n")
|
|
879
|
+
);
|
|
880
|
+
// Persist that we just shown the banner so we don't spam.
|
|
881
|
+
await fs.mkdir(cacheDir, { recursive: true });
|
|
882
|
+
await fs.writeFile(cachePath, JSON.stringify({ ...cached, last_shown_at: now }, null, 2));
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
// 2. If cache is stale, fire-and-forget a registry fetch so the NEXT
|
|
887
|
+
// cold start has fresh data. Detached from the await chain so it
|
|
888
|
+
// never delays exit.
|
|
889
|
+
if (stale) {
|
|
890
|
+
void refreshVersionCacheInBackground(cachePath);
|
|
891
|
+
}
|
|
892
|
+
} catch {
|
|
893
|
+
// best-effort — never break the CLI for an update check
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
async function refreshVersionCacheInBackground(cachePath) {
|
|
898
|
+
try {
|
|
899
|
+
const controller = new AbortController();
|
|
900
|
+
const timeout = setTimeout(() => controller.abort(), 2500);
|
|
901
|
+
const resp = await fetch(VERSION_REGISTRY_URL, {
|
|
902
|
+
headers: { "Accept": "application/json", "User-Agent": `pqcheck-cli/${VERSION}` },
|
|
903
|
+
signal: controller.signal,
|
|
904
|
+
});
|
|
905
|
+
clearTimeout(timeout);
|
|
906
|
+
if (!resp.ok) return;
|
|
907
|
+
const body = await resp.json();
|
|
908
|
+
const latest = body?.["dist-tags"]?.latest;
|
|
909
|
+
if (!latest) return;
|
|
910
|
+
const fs = await import("node:fs/promises");
|
|
911
|
+
const path = await import("node:path");
|
|
912
|
+
await fs.mkdir(path.dirname(cachePath), { recursive: true });
|
|
913
|
+
// Preserve last_shown_at so we don't reset the throttle on every refresh.
|
|
914
|
+
let prior = {};
|
|
915
|
+
try { prior = JSON.parse(await fs.readFile(cachePath, "utf8")); } catch { /* none */ }
|
|
916
|
+
await fs.writeFile(cachePath, JSON.stringify({
|
|
917
|
+
...prior,
|
|
918
|
+
latest,
|
|
919
|
+
checked_at: Date.now(),
|
|
920
|
+
}, null, 2));
|
|
921
|
+
} catch {
|
|
922
|
+
// best-effort
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
function isNewerVersion(remote, local) {
|
|
927
|
+
const parse = (v) => String(v).split(".").map((n) => parseInt(n, 10) || 0);
|
|
928
|
+
const [a, b, c] = parse(remote);
|
|
929
|
+
const [x, y, z] = parse(local);
|
|
930
|
+
if (a !== x) return a > x;
|
|
931
|
+
if (b !== y) return b > y;
|
|
932
|
+
return c > z;
|
|
933
|
+
}
|
|
934
|
+
|
|
762
935
|
// v0.16.0 — high-yield output formatters.
|
|
763
936
|
//
|
|
764
937
|
// Design (per user direction 2026-05-22): every scan should deliver
|
|
@@ -1291,7 +1464,7 @@ function printMarkdown(r, multi) {
|
|
|
1291
1464
|
// not the old "watch free / weekly digest"). PR-comment context plants
|
|
1292
1465
|
// team-invitation idea for the eventual Growth tier upgrade.
|
|
1293
1466
|
lines.push(`📌 **Monitor ${r.domain} continuously?** ${API_BASE}/watch/${encodeURIComponent(r.domain)}`);
|
|
1294
|
-
lines.push(`Cipherwake
|
|
1467
|
+
lines.push(`Cipherwake Founder Pro $19.99/mo (launch pricing, locked while subscription active) · 5 watched domains · daily scans · full CI Trust Gate · approved-vendor allowlist · webhook delivery (Slack incoming-webhook URLs work).`);
|
|
1295
1468
|
if (multi) lines.push("\n---\n");
|
|
1296
1469
|
console.log(lines.join("\n"));
|
|
1297
1470
|
}
|
|
@@ -1421,7 +1594,7 @@ function printReport(r) {
|
|
|
1421
1594
|
// 2026-05-14: copy must match current locked revenue plan ($29 Starter,
|
|
1422
1595
|
// not the old "free 1-domain weekly digest").
|
|
1423
1596
|
console.log(color("violet", ` 📌 Monitor ${r.domain} daily: ${API_BASE}/watch/${encodeURIComponent(r.domain)}`));
|
|
1424
|
-
console.log(color("dim", ` Cipherwake
|
|
1597
|
+
console.log(color("dim", ` Cipherwake Founder Pro $19.99/mo · 5 watched domains · email alerts · launch pricing locked while subscription active · cancel anytime`));
|
|
1425
1598
|
console.log("");
|
|
1426
1599
|
console.log(color("dim", ` → Full report: ${API_BASE}/?check=${encodeURIComponent(r.domain)}`));
|
|
1427
1600
|
console.log(color("dim", ` → Share this: ${API_BASE}/r/${encodeURIComponent(r.domain)}`));
|
|
@@ -3040,19 +3213,33 @@ async function runTrustDiffCommand(args) {
|
|
|
3040
3213
|
});
|
|
3041
3214
|
} catch (err) {
|
|
3042
3215
|
console.error(color("red", `error: network failure calling /api/trust-diff: ${err.message}`));
|
|
3043
|
-
|
|
3216
|
+
// v0.16.13: in AI mode, emit a ship_decision=review block so the
|
|
3217
|
+
// calling agent doesn't silently treat a fetch failure as "no signal".
|
|
3218
|
+
return emitAiGuardReviewAndExit(args, {
|
|
3219
|
+
code: "deploy_check_fetch_failed",
|
|
3220
|
+
message: `Network failure: ${err?.message || "fetch failed"}`,
|
|
3221
|
+
exitCode: 3,
|
|
3222
|
+
});
|
|
3044
3223
|
}
|
|
3045
3224
|
|
|
3046
3225
|
if (resp.status === 401 || resp.status === 403) {
|
|
3047
3226
|
await handleAuthError(resp);
|
|
3048
|
-
|
|
3227
|
+
return emitAiGuardReviewAndExit(args, {
|
|
3228
|
+
code: "deploy_check_auth_failed",
|
|
3229
|
+
message: `Authorization failed (${resp.status}). Check CIPHERWAKE_API_KEY or hit /account#api-keys.`,
|
|
3230
|
+
exitCode: 3,
|
|
3231
|
+
});
|
|
3049
3232
|
}
|
|
3050
3233
|
if (resp.status === 429) {
|
|
3051
3234
|
const body = await safeJSON(resp);
|
|
3052
3235
|
console.error(color("red", "error: Trust Diff API quota exceeded"));
|
|
3053
3236
|
if (body?.message) console.error(color("dim", body.message));
|
|
3054
3237
|
console.error(color("dim", "Higher quota via free API key (no card): https://cipherwake.io/account#api-keys"));
|
|
3055
|
-
|
|
3238
|
+
return emitAiGuardReviewAndExit(args, {
|
|
3239
|
+
code: "deploy_check_quota_exceeded",
|
|
3240
|
+
message: body?.message || "Trust Diff monthly quota exceeded — upgrade or wait for monthly reset.",
|
|
3241
|
+
exitCode: 3,
|
|
3242
|
+
});
|
|
3056
3243
|
}
|
|
3057
3244
|
// R74-confirm friction fix #2 (GPT 2026-05-22): 404 on first-deploy is
|
|
3058
3245
|
// expected — the domain has never been scanned, so there's no baseline to
|
|
@@ -3069,7 +3256,11 @@ async function runTrustDiffCommand(args) {
|
|
|
3069
3256
|
console.error(color("red", `error: /api/trust-diff returned ${resp.status}`));
|
|
3070
3257
|
if (body?.message) console.error(color("dim", body.message));
|
|
3071
3258
|
if (body?.hint) console.error(color("dim", body.hint));
|
|
3072
|
-
|
|
3259
|
+
return emitAiGuardReviewAndExit(args, {
|
|
3260
|
+
code: "deploy_check_server_error",
|
|
3261
|
+
message: body?.message || `Cipherwake server returned ${resp.status}.`,
|
|
3262
|
+
exitCode: 3,
|
|
3263
|
+
});
|
|
3073
3264
|
}
|
|
3074
3265
|
|
|
3075
3266
|
const result = await resp.json();
|
|
@@ -4232,7 +4423,7 @@ function renderReleaseChecklist(domain, opts = {}) {
|
|
|
4232
4423
|
// `pqcheck init` — interactive workflow scaffold (habit-loop #4, locked 2026-05-16)
|
|
4233
4424
|
// =============================================================================
|
|
4234
4425
|
// Writes a ready-to-commit .github/workflows/cipherwake.yml that calls
|
|
4235
|
-
// cipherwakelabs/pqcheck@v3 in trust-diff mode. Zero copy-paste docs friction.
|
|
4426
|
+
// cipherwakelabs/pqcheck/action@v3 in trust-diff mode. Zero copy-paste docs friction.
|
|
4236
4427
|
//
|
|
4237
4428
|
// Flags:
|
|
4238
4429
|
// --domain <d> Skip the domain prompt
|
|
@@ -4409,7 +4600,7 @@ jobs:
|
|
|
4409
4600
|
runs-on: ubuntu-latest
|
|
4410
4601
|
steps:
|
|
4411
4602
|
- name: Run Cipherwake Trust Diff
|
|
4412
|
-
uses: cipherwakelabs/pqcheck@v3
|
|
4603
|
+
uses: cipherwakelabs/pqcheck/action@v3
|
|
4413
4604
|
with:
|
|
4414
4605
|
mode: trust-diff
|
|
4415
4606
|
domain: ${domain}
|
|
@@ -4417,7 +4608,7 @@ jobs:
|
|
|
4417
4608
|
fail-on: ${failOn}
|
|
4418
4609
|
# No env/secrets needed for Free tier — the action uses the
|
|
4419
4610
|
# workflow's id-token: write permission to fetch a GitHub-signed
|
|
4420
|
-
# OIDC token and meters per repo (
|
|
4611
|
+
# OIDC token and meters per repo (100 calls/mo, no setup).
|
|
4421
4612
|
# If you want higher limits, link this repo to a paid Cipherwake
|
|
4422
4613
|
# account at https://cipherwake.io/account → Linked repos.
|
|
4423
4614
|
`;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pqcheck",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.13",
|
|
4
4
|
"description": "Deploy gate for AI-coded web apps. `pqcheck deploy-check --ai` returns ship_decision=pass|review|block for Claude Code / Cursor / Copilot / Aider to gate deploys before they ship. Anonymous, no signup, free for first use.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai-coder",
|