Skip to content

feat: add fractalaiReceipts action provider - #1549

Open
johnInarti wants to merge 3 commits into
coinbase:mainfrom
johnInarti:feat/fractalai-receipts-action-provider
Open

johnInarti wants to merge 3 commits into
coinbase:mainfrom
johnInarti:feat/fractalai-receipts-action-provider

Conversation

@johnInarti

Copy link
Copy Markdown

Description

This PR adds a fractalaiReceipts action provider with two actions that give any AgentKit agent signed, offline-verifiable evidence about x402 payments.

verify_x402_receipt verifies an ML-DSA-65 (NIST FIPS 204) signed receipt and reports each level separately, with reason codes:

Level Meaning
integrity well-formed; unsigned fields that repeat signed content match it exactly
authentic the signature verifies over the message rebuilt from the kind's fixed domain
trusted the key is listed in a verified key directory and authorized at the signed time (reserved / revoked never)
settlement the receipt names the transaction / payer the agent observed (only if given)
delivery the signed body digest matches the body the agent received (only if given)

Supported receipts:

  • receipts of the delivery-receipt x402 extension (a draft proposed to coinbase/x402, not merged there yet: signed payload binding the settlement locator, the paid terms and the SHA-256 of the served body), from any issuer. The issuer's key directory is fetched from <issuer>/.well-known/x402-receipt-keys; trust is reported as tls (trust on first use) unless the governance key is pinned in the provider config (pinned);
  • FractalAI notary seals, served proofs and MIDAS alerts. Their directory is checked against a governance key and an epoch-3 checkpoint pinned in the provider (pinned-root): older epochs are refused as rollback, a different epoch-3 root as equivocation.

The kind is chosen by the caller when known (a document of another kind is refused); otherwise it is inferred from shape, never from a field that claims a domain. JSON is parsed strictly (duplicate keys, lone surrogates and trailing data are refused) and strings from receipts are sanitized before they reach the model, in the spirit of #1480.

request_x402_receipt asks the FractalAI notary for an independent seal of an x402 payment that already settled on Base. The notary re-derives the transfer from Base before asking for payment and answers 422 (nothing charged) if it does not match. Otherwise the action pays the 0.005 USDC fee through @x402/fetch with the agent's EVM wallet, using the same signer setup as the existing x402 provider. A payment policy and a pre-payment hook only accept exact USDC on Base to the notary's pinned address, up to maxPaymentUsdc (default 0.005). The returned seal is verified and compared with the request.

What a receipt does not prove

A valid receipt proves that a trusted key signed these bytes at the signed time. It does not prove that the content is true or correct, and unless settlement is checked it does not prove the payment happened (this provider does not query the chain). The FractalAI roots are trust-on-first-use pinned on 2026-10-07. Revoked keys are never trusted because consensus time anchors are not verified here. @noble/post-quantum is not a CMVP / FIPS 140-3 validated module. These limits are listed in the provider README.

Dependency

Adds @noble/post-quantum@^0.4.1 (pure JS, depends only on @noble/hashes, which is already in the tree). AgentKit compiles to CommonJS and later majors of @noble/post-quantum are ESM-only, so 0.4.x is used. Its verify(publicKey, message, signature) argument order is pinned by a known-answer test on a production receipt. The lockfile change is limited to this package.

Authorship

Contributed by FractalAI (FRACTAL AI S.A.S.), which operates the notary and maintains the FractalAI receipt formats and the delivery-receipt extension draft. The code was written with AI assistance (Claude Code).

Tests

Unit tests only (src/action-providers/fractalaiReceipts/fractalaiReceiptsActionProvider.test.ts, 36 tests). No network access, no payment:

  • positive vector: a real FractalAI production receipt (MIDAS alert fe62b072…, signed by the active key 86c139c960bb274c) verified against the published epoch-3 directory (fixtures copied from https://fractalai.net.co/.well-known/x402-receipt-keys);
  • negative vectors: altered signed digit, unsigned fields that disagree with signed content, duplicate JSON keys, ambiguous kinds, reserved routes, a directory signed by another governance key, a tampered directory, unlisted / reserved / revoked / out-of-window keys, rollback, equivocation, chain gaps, non-append-only epochs;
  • delivery-receipt: TLS vs pinned trust, settlement and body mismatches, altered payload, issuer authorization;
  • request_x402_receipt with @x402/fetch mocked: exact seal_body, the payment policy and hook, notary refusal, invalid-signature and unlisted-key seals, and the success path with test trust roots.
cd typescript/agentkit
pnpm test      # Test Suites: 63 passed, 63 total; Tests: 939 passed, 939 total
pnpm lint      # 0 problems
pnpm format:check
pnpm check     # tsc --noEmit

Not yet done: a manual run with an example chatbot on Base mainnet (it would spend 0.005 USDC per notary call). We can add a transcript before review if maintainers want one.

Checklist

  • Added documentation to all relevant README.md files
  • Added a changelog entry

🤖 Generated with Claude Code

johnInarti and others added 3 commits October 9, 2026 17:48
Agents that pay for x402 resources get a settlement transaction but no
verifiable record of what the paid service committed to. This provider adds
two actions so any AgentKit agent can check and obtain signed receipts:

- verify_x402_receipt: verifies post-quantum (ML-DSA-65, FIPS 204) receipts
  and reports integrity / authentic / trusted / settlement / delivery as
  separate levels with reason codes. It supports receipts of the proposed
  x402 `delivery-receipt` extension from any issuer (issuer key directory
  fetched from /.well-known/x402-receipt-keys, trust basis reported as
  "tls" unless a governance key is pinned in the config) and FractalAI's
  notary seals, served proofs and MIDAS alerts, which are checked against
  a governance key and an epoch-3 directory checkpoint pinned in the
  provider (rollback and equivocation are refused). The signed message is
  always rebuilt from the kind's fixed domain; unsigned copies of signed
  content must match exactly; keys are checked for use, status and
  validity window at the signed time (reserved and revoked keys never
  authorize). JSON input is parsed strictly (duplicate keys, lone
  surrogates, trailing data refused) and untrusted strings are sanitized
  before they reach the model.
- request_x402_receipt: asks the FractalAI notary for an independent seal
  of an x402 payment that already settled on Base, pays its 0.005 USDC fee
  through @x402/fetch with the agent's EVM wallet (a payment policy only
  accepts USDC on Base to the notary's pinned address, up to the
  configured cap), then verifies the returned seal and compares it with
  the request.

@noble/post-quantum is pinned to ^0.4.1 because AgentKit compiles to
CommonJS and later majors are ESM-only; its verify argument order
(publicKey, message, signature) is pinned by a known-answer test on a
production receipt.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The provider makes trust decisions, so the tests pin them against real
artefacts instead of only self-generated ones:

- fixtures are FractalAI's published epoch-3 key directory, its pinned
  trust roots and a production MIDAS alert receipt (fe62b072...) signed
  by the active key 86c139c960bb274c; a known-answer test fixes the
  ML-DSA-65 argument order of @noble/post-quantum 0.4.x;
- negative vectors: a signed digit changed in the canonical (signature
  fails even with consistent unsigned copies), unsigned facts or ids that
  disagree with signed content, duplicate JSON keys, ambiguous kinds,
  reserved routes, self-attested seals, a directory signed by another
  governance key, a tampered production directory, unlisted, reserved,
  revoked and out-of-window keys, rollback, equivocation, chain gaps and
  non-append-only epochs;
- delivery-receipt: TLS vs pinned trust, settlement and body mismatches,
  altered payload, issuer authorization;
- request_x402_receipt with fetch and payment fully mocked: exact
  seal_body, the payment policy and pre-payment hook, notary refusal,
  bad-signature and unlisted-key seals, and the trusted path with test
  trust roots swapped in an isolated module registry.

No test touches the network or moves funds.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Adds the provider README (actions, verification levels, trust bases,
configuration) with an explicit scope-and-limits section, so users know
what a valid receipt does and does not prove: it shows that a trusted key
signed the bytes at the signed time, not that the content is true; the
FractalAI roots are trust-on-first-use pinned on 2026-10-07; revoked keys
are never trusted because time anchors are not verified here;
delivery-receipt is a proposed x402 extension; @noble/post-quantum is not
a CMVP-validated module. Also lists the provider in the package README
and adds the patch changeset required by CONTRIBUTING-TYPESCRIPT.md.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@cb-heimdall

Copy link
Copy Markdown

🟡 Heimdall Review Status

Requirement Status More Info
Reviews 🟡 0/1
Denominator calculation
Show calculation
1 if user is bot 0
1 if user is external 0
2 if repo is sensitive 0
From .codeflow.yml 1
Additional review requirements
Show calculation
Max 0
0
From CODEOWNERS 0
Global minimum 0
Max 1
1
1 if commit is unverified 0
Sum 1

@github-actions github-actions Bot added documentation Improvements or additions to documentation action provider New action provider typescript labels Oct 9, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

action provider New action provider documentation Improvements or additions to documentation typescript

Development

Successfully merging this pull request may close these issues.

2 participants