In April 2026, the ClawHavoc supply-chain attack shipped 1,400+ malicious skills to ClawHub — disguised as Gmail, Notion, Slack, and GitHub integrations — deploying AMOS and Vidar infostealers. 138 CVEs were disclosed; one (CVE-2026-32922) scored CVSS 9.9. The attack demonstrated that any agent runtime's plugin surface is a live attack vector — including Claude Code.
On April 28, 2026, the FIDO Alliance formed the Agentic Authentication TWG, chaired by CVS Health, Google, and OpenAI. Google contributed AP2 (Agent Payments Protocol) and Mastercard contributed Verifiable Intent as foundational standards. Payment authorization for AI agents became a formal standards problem the same month it became an active attack surface.
The PQSafe Claude Code plugin brings SpendEnvelopes — cryptographically bounded spend authorizations — directly into any Claude Code session. Three slash commands handle the full mandate lifecycle. A payment-reviewer sub-agent audits payment tool calls before they execute. All signatures use ML-DSA-65 (NIST FIPS 204, Level 3) — post-quantum from day one, aligned with the FIDO AP2-PQ profile. The plugin is the fifth distribution surface for PQSafe, shipped May 5, 2026.
Why now
Supply-chain reality, April 2026. ClawHavoc proved that a compromised plugin can run inside a developer session with full tool access. 1,400+ malicious skills reached ClawHub production before detection; several targeted payment-adjacent integrations. A cryptographic spend mandate enforced at the skill layer — verified before any payment tool call executes — is the correct architectural response: the mandate boundary holds even if a co-installed plugin is adversarial.
Standards convergence, April 28, 2026. The FIDO Alliance Agentic Authentication TWG launched with AP2 and Verifiable Intent as foundational contributions. pqsafe-pay aligns with the FIDO AP2-PQ profile: signed mandates with post-quantum algorithms, structured intent fields, and verifiable delegation chains.
Cryptographic deprecation timeline. NIST IR 8547 (initial public draft) proposes deprecation of 112-bit classical algorithms — including ECDSA P-256 — by 2030, with full disallowance by 2035. Payment authorization records produced in 2026 Claude Code sessions may still be within mandatory retention windows when cryptographically-relevant quantum computers arrive. ML-DSA-65 signs from day one; no migration required later.
Three slash commands
The plugin registers three commands under the pqsafe-pay namespace:
- /pqsafe-create Interactively create a SpendEnvelope. Prompts for agent ID, payer ID, max amount, currency, rail, and expiry. Returns a signed envelope with a single-use nonce and an ML-DSA-65 signature over the JCS-canonical payload. The signing key never leaves the PQSafe key service.
- /pqsafe-verify Verify a SpendEnvelope against the PQSafe public key registry and real-time revocation list. Confirms signature integrity, expiry, nonce uniqueness, and that the stated key ID is current. Returns a structured verification result with a canonical status code.
-
/pqsafe-revoke
Revoke an issued SpendEnvelope before expiry. The envelope ID is appended to the real-time revocation list and written to the append-only audit log. Subsequent verify calls will return
REVOKED.
payment-reviewer sub-agent
The plugin ships a payment-reviewer sub-agent that monitors tool calls during a Claude Code session. When a tool call matches a payment pattern — any call to Stripe, Airwallex, Wise, USDC, or x402 endpoints — the reviewer intercepts before execution:
- Checks whether a valid, non-expired SpendEnvelope is in the session context
- Verifies the target recipient is in the envelope's
allowedRecipientslist - Confirms the stated amount is within the envelope's
maxAmount - If any check fails: blocks the tool call, surfaces a clear rejection reason, and logs to the session audit trail
The reviewer runs as a Claude Code sub-agent, meaning it has its own context and can invoke /pqsafe-verify autonomously. It does not require explicit user invocation — it is always on when the plugin is installed.
SpendEnvelope structure
The envelope is a JSON object signed in JCS-canonical form (RFC 8785) before the ML-DSA-65 signature is computed. All fields are included in the signed payload.
{
"id": "01HZ9K4PTXQ7V3M8RG0N5JCWEF",
"agentId": "claude-code-session-a3f9c",
"payerId": "payer_usr_0f3a91bc",
"maxAmount": "250.00",
"currency": "USD",
"rail": "airwallex",
"allowedRecipients": ["vendor.acme.com/billing"],
"expiresAt": "2026-05-05T23:59:00Z",
"nonce": "a3f8c21d9b04e67f1c28d5a0b3e9f412c7d6a8e20b51f3c4d97e6a0b2c8d1e4f",
"signature": "eyJhbGciOiJNTC1EU0EtNjUiLCJraWQiOiJwcXNhZmUta2V5LTIwMjYtMDEifQ...",
"keyId": "pqsafe-key-2026-01"
}
id — ULID (monotonically sortable, 26-character base32). maxAmount — decimal string, never floating-point. nonce — 32-byte hex (256-bit entropy). signature — base64url-encoded ML-DSA-65 signature (3,309 bytes). keyId — identifies the public key in the PQSafe registry.
Install
Path A — plugin directory symlink (fastest):
# Clone the PQSafe repo (or use your existing checkout)
git clone https://github.com/PQSafe/pqsafe.git
# Symlink the plugin into Claude Code's plugin directory
ln -s "$(pwd)/pqsafe/plugins/claude-pqsafe" ~/.claude/plugins/pqsafe-pay
# Restart Claude Code — /pqsafe-create, /pqsafe-verify, /pqsafe-revoke
# are now available in every session
Path B — MCP server (production runtime):
For production deployments or teams that want the payment tools exposed as MCP primitives across multiple Claude instances, add the PQSafe MCP server to your Claude Code config:
// .claude/mcp.json
{
"mcpServers": {
"pqsafe": {
"command": "npx",
"args": ["@pqsafe/mcp-server"],
"env": { "PQSAFE_API_KEY": "${PQSAFE_API_KEY}" }
}
}
}
This exposes create_envelope, verify_envelope, and revoke_envelope as MCP tools usable across any Claude Code session without the plugin directory symlink.
Verifier API
Both install paths call the PQSafe Verifier API for mandate verification and revocation. The API Worker is live at api.pqsafe.xyz (custom domain; fallback Worker URL: pqsafe-api-production.raymond-thu87.workers.dev). Five endpoints:
| Endpoint | Method | Purpose |
|---|---|---|
| /healthz | GET | Health check — returns {"status":"ok"} |
| /v1/mandates/verify | POST | Verify a SpendEnvelope; returns structured result with canonical status code |
| /v1/revoke | POST | Revoke an envelope by ID; appends to revocation list + audit log |
| /v1/revoke/:id | GET | Check revocation status of a specific envelope ID |
| /v1/audit/:id | GET | Retrieve the full audit log entry for a SpendEnvelope |
Supported rails
| Rail | Status | Currency |
|---|---|---|
| airwallex | LIVE sandbox | Multi-currency (real test transfers) |
| wise | LIVE sandbox | 40+ fiat currencies (real test transfers) |
| stripe | mock-ready | USD + 135 others |
| usdc-base | mock-ready | USDC |
| x402 | mock-ready | USDC + ETH |
LIVE sandbox = validated end-to-end with sandbox rails; real test transfers confirmed. Mock-ready = SpendEnvelope creation and verification fully functional; live rail integration in progress.
Security model
- HSM-backed signing keys — ML-DSA-65 private keys are generated and stored in hardware security modules; they never leave the PQSafe key service
- Single-use nonce — each envelope carries a 256-bit random nonce; replay attacks are rejected at the verify layer
- Expiry enforced in signed payload —
expiresAtis part of the signed content; an attacker cannot extend expiry without invalidating the signature - Real-time revocation list —
/pqsafe-revokeappends to a low-latency revocation list checked on every verify call - Append-only audit log — all create, verify, and revoke events are timestamped and written to an immutable audit log at ledger.pqsafe.xyz
- JCS-canonical signing — the payload is serialized in JSON Canonicalization Scheme form (RFC 8785) before signing, eliminating signature ambiguity from key ordering or whitespace variation
- Allowlist enforcement — the
payment-reviewersub-agent checks every recipient against the envelope'sallowedRecipientslist before permitting tool execution
What this does NOT defend against (be honest): high-permission OS-level escalation, untrusted-input prompt injection at the LLM layer, and sandbox escape. Use NemoClaw OpenShell, input filters, and container isolation respectively for those vectors.
Threat model — what this defends against
| Failure mode | Defense |
|---|---|
| Hallucinated recipient | allowedRecipients allowlist enforced by payment-reviewer before tool call |
| Prompt-injected amount | maxAmount signed in payload; signature breaks if changed |
| Replayed mandate | 256-bit nonce + revocation list checked on every verify |
| Compromised co-installed plugin | ML-DSA-65 signature over JCS-canonical bytes; mandate boundary holds regardless of call origin |
| Stale credential reuse | validUntil enforced inside signed payload |
| Audit-log tamper | Append-only hash-chained ledger at ledger.pqsafe.xyz |
ML-DSA-65 parameters
| Parameter | Value |
|---|---|
| Standard | NIST FIPS 204 |
| Security level | NIST Level 3 |
| Public key size | 1,952 bytes |
| Secret key size | 4,032 bytes |
| Signature size | 3,309 bytes |
| Hardness assumption | Module-LWE + Module-SIS |
AP2-PQ test vectors
The PQSafe test vector suite is published at pqsafe.xyz/spec/ap2-pq-test-vectors-v1.json. It contains 6 vectors: 5 positive cases and 1 negative case (TC1) that exposed a real defect in pqcrypto 0.4.0 — the library accepted a tampered ML-DSA-65 message as valid. The fix recommendation is to use @noble/post-quantum for authoritative ML-DSA rejection. Both the defect and the fix are documented in the open-source repository.
Get the plugin
- Plugin source (Apache-2.0) — github.com/PQSafe/pqsafe/tree/main/plugins/claude-pqsafe
- SKILL.md (source of truth) — plugins/claude-pqsafe/skills/pqsafe-pay/SKILL.md
-
MCP server alternative —
npm install @pqsafe/mcp-server— npmjs.com/package/@pqsafe/mcp-server -
Verifier API —
api.pqsafe.xyz
(Cloudflare Workers, 5 endpoints; custom domain pending NS migration — fallback:
pqsafe-api-production.raymond-thu87.workers.dev) - AP2-PQ test vectors — pqsafe.xyz/spec/ap2-pq-test-vectors-v1.json