Skip to content

Check signed receipts on x402 Doctor and presign-guard answers (0.9.0) - #17

Merged
Fizzl13 merged 1 commit into
mainfrom
claude/x402-agents-solana-payments-nceg9b
Sep 27, 2026
Merged

Fizzl13 merged 1 commit into
mainfrom
claude/x402-agents-solana-payments-nceg9b

Conversation

@Fizzl13

@Fizzl13 Fizzl13 commented Sep 27, 2026

Copy link
Copy Markdown
Owner

x402 Doctor and presign-guard now sign every paid answer: EIP-191 over canonical JSON, with the request inside the signed body. The actions that decide "pay" or "sign" now check it, so an agent never acts on a verdict that isn't provably from the service, for exactly its request.

What

  • FIZZL_PREFLIGHT_X402, FIZZL_DIAGNOSE_X402 and FIZZL_PRESIGN_CHECK verify the receipt:
    • signed by the service's pinned signer: Doctor 0xAaE6…277d, presign-guard 0xf084…1EaE;
    • its input_sha256 matches the request that was sent: the query for Doctor, the JSON body for presign-guard.
  • A valid answer ends with "Signed by x402 Doctor ✓ (receipt …)".
  • A changed, unsigned or foreign answer, or one for another request, is not used: the action fails with "Do not pay on it" or "Do not sign until it can be checked".
  • src/receipt.ts: canonical JSON, input hash, verifyReceipt (viem, already a dependency) and SIGNERS.
  • The services attach the signed request (route and input) to each result.
  • New settings: FIZZL_VERIFY_RECEIPTS (require by default, or off), FIZZL_DOCTOR_SIGNERS and FIZZL_PRESIGN_SIGNERS.
  • README "Signed verdicts" section; version 0.9.0.

Tests

  • The fixtures now sign like the live services, so every existing Doctor and presign test runs through the check.
  • New cases: a flipped verdict, a missing receipt, a foreign signer and the off switch.
  • The pinned signers are checked.
  • Real production receipts from both services verify, and fail when changed or checked against the other service's key.
  • npm test: 40/40.

After merge

Publish 0.9.0 (the Publish to npm workflow).

🤖 Generated with Claude Code

https://claude.ai/code/session_01TsD5haPKxeWjYmErHzvU48


Generated by Claude Code

Both services now sign every paid answer (EIP-191 over canonical JSON, request
inside the signed body). The preflight, diagnose and presign actions verify it
before the agent uses a verdict: signed by the service's pinned signer and
bound to exactly the request sent. A valid answer ends with "Signed by ... ✓";
a changed, unsigned or foreign answer, or one for another request, is not used
("Do not pay on it" / "Do not sign until it can be checked").

- src/receipt.ts: canonical JSON, input hash, verifyReceipt (viem), SIGNERS.
- Services attach the signed route and input to each result.
- Settings: FIZZL_VERIFY_RECEIPTS (require|off), FIZZL_DOCTOR_SIGNERS,
  FIZZL_PRESIGN_SIGNERS.
- Tests: fixtures sign like the live services; tampered, unsigned and foreign
  answers are refused; the off switch; pinned signers; real production
  receipts from both services verify.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TsD5haPKxeWjYmErHzvU48
@Fizzl13
Fizzl13 merged commit 6aa5a75 into main Sep 27, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants