Use this checklist before merging or publishing changes that affect money handling, x402 protocol behavior, signing, settlement, deployment, or package release artifacts.
Run these from the repository root:
pnpm install --frozen-lockfile
pnpm typecheck
pnpm test -- --runInBand
pnpm audit --prod --audit-level=moderate
pnpm security:payments
pnpm security:releaseIf the Semgrep CLI is installed, also run the optional AST-aware payment rules:
pnpm security:semgrepFor MCP package releases, also run:
pnpm --filter @cardano402/mcp-server test
pnpm --filter @cardano402/mcp-server buildFor core package releases, also run:
pnpm --filter @cardano402/core test
pnpm --filter @cardano402/core buildThe root package prepublishOnly gate must continue to run typecheck, full
serial tests, payment invariants, release-readiness invariants, and build before
any publish attempt.
Protected branches should require these checks before merge:
- CI / Lint & Type Check
- CI / Test
- CI / Build
- CI / Docker Build
- CI / Security Audit
- CodeQL
- Dependency Review
- Gitleaks
- OSV-Scanner
- Scorecard supply-chain security
- Zizmor
The Protocol Monitor should be treated as an operational gate for the live
service. It is manual-only until Cloudflare machine-route skip rules are
applied; once those rules are live, re-enable the schedule and require it to
pass against https://cardano402.com with the approved minimum confirmation
threshold.
Before release, verify:
paymentRequirementsare derived from server-owned configuration, not from client input.- Paid routes are schema-validated before discovery publication, so
/.well-known/x402.json, agent-card, ai-agent, and MCP server-card manifests cannot advertise malformed payment terms or unsafe endpoint paths. Payment-Requiredquotes and payment gates are schema-validated before serving requests, so misconfigured routes fail closed instead of emitting malformed payment terms for agents to sign.- Asset identifiers are validated as either
lovelaceor lowercasepolicyId.assetNameHexbefore discovery, quote, verification, settlement, or signer paths consume them. X-Payment-Response/PAYMENT-RESPONSEmetadata is schema-checked before headers are emitted; malformed transaction hashes or networks from a facilitator fail closed instead of being surfaced to agents.- Core facilitator-client settle parsing rejects malformed transaction hashes on successful settlement responses while preserving empty transaction fields for failure responses.
X-PAYMENTandPayment-Signaturerequest headers remain accepted where expected.- CORS preflight methods remain limited to the public GET/POST API surface.
- Payment headers are decoded through the shared strict codec, have a bounded maximum length, and malformed or oversized values fail before facilitator verification.
- MCP clients strictly decode untrusted
Payment-Requiredresponse headers and reject malformed or oversized values before signing. - MCP clients reject malformed catalog payment terms before tool registration: non-decimal or over-uint64 amounts, malformed CAIP-2 networks, recipient addresses with whitespace/control characters, and TTL windows above 3600s.
- MCP clients bound and schema-check
X-Payment-Response/PAYMENT-RESPONSEheaders before surfacing paid-response metadata to agents. - MCP HTTP transport bearer tokens are at least 32 characters and are compared in constant time when configured.
/verifyand/settlestrictly decode rawpaymentHeaderrequest bodies and bound their size before verification or settlement work starts./verifyand/settlerejectpaymentRequirements.maxTimeoutSecondsvalues above the configured verification maximum before verification or settlement work starts./supportedschema-checks discovery output before publishing it, including exact-scheme kinds, CAIP/family signer keys, and printable signer addresses.- Transaction CBOR payloads are strictly base64-decoded and size-bounded before CBOR parsing or settlement submission.
- CORS still allows
X-PAYMENT. - Malformed
paymentRequirements.payTovalues return structured verification failures rather than public 500s. /statustransaction hashes are validated as 64-character lowercase hex before any chain-provider lookup./files/:cidvalidates content identifiers before backend lookup, so malformed or oversized values do not reach filesystem/IPFS storage backends./uploadenforces its intended multipart file limit explicitly instead of inheriting the smaller global JSON body limit or storing oversized files.- Settlement dedup records fail closed and structured when Redis contains corrupt JSON or malformed transaction hashes.
- Public
paymentRequirements.amountvalues are bounded before route-levelBigIntconversion. /healthexposes non-secretpolicy.confirmationandpolicy.signer.pnpm monitor:protocol -- --base-url https://cardano402.com --min-confirmations 6 --jsonpasses after Cloudflare machine-route rules are applied.facilitator_payment_results_totalis scraped and alerting on invalid request spikes, nonce lookup failures, settlement failures, and status drift.- Production
metrics.bearerTokenvalues are at least 32 characters, metrics Authorization headers are parsed strictly, and comparisons are constant-time. - The optional Semgrep payment rules are run for high-risk PRs or replicated in Semgrep Cloud / a paid code-review workflow.
- Mainnet deployments do not use inline signing material unless the explicit unsafe override is documented and approved.
- Mainnet
local-filesigning requires the explicitCARDANO402_ALLOW_MAINNET_LOCAL_FILE_SIGNER=truehot-wallet acknowledgement; high-value Mainnet deployments use the remote or hardware-backed signer plan instead of this override. - Production deployments with the live demo enabled use
demo.seedPhraseFilefor a separate Preview/Preprod demo wallet; inline demo seed material is rejected in production. - Public quota- or transaction-consuming routes, including
/demo/runand/demo/status, use the tighter sensitive route rate limit instead of only the global limiter. /demo/statusreportsconfigured: falseandready: falsewhen demo wallet configuration is absent.- Backup and restore scripts reject loose restic credential-file permissions and restore snapshots only into private directories.
- Docker build context excludes local
secrets/signing material and runtimedata/uploads. - Docker build stages activate the pinned
pnpm@10.8.1package-manager version declared inpackage.json. - CI and protocol-monitor workflows pin
pnpm/action-setupto the samepnpm@10.8.1toolchain used by local and Docker builds. - Dependabot covers npm, GitHub Actions, and Docker ecosystems so dependency, workflow, and image updates are surfaced for review.
- Dependabot version updates use cooldown windows for npm, GitHub Actions, and Docker so fresh releases have time to settle before routine update PRs; security updates are still created immediately by GitHub.
- Root, core, and MCP package manifests keep explicit npm
filesallowlists so source, scripts, test fixtures, local config, generated data, and secrets are not packed accidentally. - Scaffold adapter packages remain
private: trueuntil they are implemented and separately release-reviewed. - Production Compose fails fast without
REDIS_PASSWORD, binds production ports to loopback, and drops unnecessary container privileges. - Production Redis uses AOF and
maxmemory-policy noevictionso settlement dedup keys are not silently evicted under memory pressure. - Production config sets
server.trustProxyto a numeric trusted-proxy hop count only for the documented loopback reverse-proxy deployment so rate limits/logs use real client IPs without trusting arbitrary forwarded chains. - High-value Mainnet deployments have an implementation plan for the remote or hardware-backed policy signer boundary.
Release notes for money-handling changes should include:
- Security impact.
- Operator action required.
- Any new environment variables or config fields.
- Any changed Mainnet safety behavior.
- Verification commands run and their results.