Jax Dunfee


Receipts Are the New Reputation: Building Trust Infrastructure for AI Agents
A practical approach to building trust infrastructure for AI agents through cryptographic receipts and public memory
September 30, 2025
ai, blockchain, agents, trust, vhttp

Receipts Are the New Reputation: Building Trust Infrastructure for AI Agents

Inspired by this post on blockchain-based agent trust infrastructure, though I’m by no means an expert and beyond blockchain ctfs and academic papers, this is my first dip into this kind of problem. (aka don’t crucify me nerds)

TL;DR: Give agents price tags and receipts; sign receipts with Ed25519 over canonical JSON (JCS); bind each receipt to a USDC on Polygon PoS payment; cooperation becomes the rational default. Anchoring is an optional extension profile. vHTTP can integrate with x402/AP2 as optional extensions for multi-rail payments.

Disclaimer: This is a personal post written purely for fun. The views expressed are my own and do not reflect those of my employer or any affiliated organizations.

Most discussions about “agent trust” tend to stay pretty theoretical. This post takes a different approach, focusing on practical mechanisms that make cooperation the rational choice for machines that can’t read body language, shake hands, or sue each other.

The Problem: AI agents need to work together, but they can’t trust each other like humans do. When Agent A pays Agent B for data, there’s no guarantee Agent B will deliver what was promised.

My proposed solution: Agents need public memory. Every interaction should leave a small, tamper-evident trace. A receipt becomes the foundation for machine reputation. Not star ratings or subjective assessments, but cryptographic proof of what happened between whom and when.

Here’s how I’d approach this: start with basic signed receipts, batch anchor to a cheap L2, and let anyone audit the trail. I might have gone a bit overboard on the full vHTTP protocol (the name needs some work, I know lol), but you can begin simple and add layers over time.

Table of Contents

  1. The Problem: Why Agent Trust Breaks Down
  2. The Solution: Public Memory Through Receipts
  3. What Makes This Different
  4. From Handshakes to Hash-Verified Receipts
  5. The Real Game: Stag Hunt, Not Betrayal
  6. Meet vHTTP: The Verified HTTP Protocol
  7. Safety & Abuse: Baked In, Not Bolted On
  8. The Trust Gradient: A Compass for “When Blockchain”
  9. Network Effects: Why This Gets Better Over Time
  10. Failure Mode Analysis: Illustrative Scenario
  11. The Agent Society Stack
  12. Builder’s Roadmap (I’ll probably do this soon (don’t quote me on this))
  13. Open Questions & Future Work
  14. Implementation Resources
  15. Conclusion
  16. Glossary
  17. Appendix A: vHTTP Spec
  18. Appendix B: Worked Example (USDC on Polygon PoS)
  19. Appendix C: Extensions (Profiles & Proofs)
  20. Appendix D: Verifier’s Cheat Sheet

The Problem: Why Agent Trust Breaks Down

Imagine your AI agent needs to buy data from another agent. This can fail in predictable ways:

Scenario 1: Your agent pays upfront -> gets garbage data -> no recourse

Scenario 2: The seller demands payment -> your agent refuses -> no transaction

Scenario 3: Both agents “trust” a platform -> platform disappears with funds

This isn’t a technology problem. It’s an incentive alignment problem. Current systems assume either perfect trust or perfect enforcement. Agents get neither.

Hypothetical example: “WeatherOracle” (a hypothetical weather data service) claims to provide real-time precipitation data but consistently reports “no rain” during documented storm events. Prediction Market trading agents (lol) betting on weather outcomes lose money while the oracle pockets fees. No way to prove the oracle manipulated outcomes or used biased data sources.

Hypothetical example: “AlphaAPI” (a hypothetical inference service) suffered botnet farming. Thousands of fake accounts stayed under free-tier limits individually but collectively generated five-figure compute costs overnight. Traditional systems couldn’t detect the pattern across accounts.

The Solution: Public Memory Through Receipts

Think of it like business receipts, but cryptographically signed and publicly anchored. No central authority needed, just ✨vHTTP✨.

The core insight: every interaction should leave a small, verifiable trace that future partners can audit. Not star ratings or subjective assessments, but cryptographic proof of what happened between whom and when.

How vHTTP Solves These Problems

WeatherOracle: With vHTTP, WeatherOracle issues a signed receipt proving it delivered “real-time precipitation data valid from 2025-09-27T16:00:00Z” with a specific content hash. When it consistently reports “no rain” during documented storm events, the receipts provide cryptographic proof of the data delivered versus actual weather conditions, enabling other agents to avoid the unreliable service.

AlphaAPI: AlphaAPI’s receipts include exact resource allocation and usage metrics for each request. When botnet farming occurs across thousands of fake accounts, the pattern becomes visible in the public receipt history through usage analysis, allowing the service to detect and prevent abuse while providing proof of legitimate usage for dispute resolution.

In each case, receipts establish reputation - not subjective ratings, but cryptographic evidence of what actually happened.


What Makes This Different

This is about public monitoring plus credible commitments. Sometimes you need blockchain anchors, sometimes transparency logs, sometimes trusted hardware. The question isn’t “crypto or not?” but rather: what’s the minimal infrastructure that makes cooperation economically rational?

Agents can’t rely on handshakes, legal systems, or social reputation like humans do. When two machines interact, they need mathematical proof-and that means public, tamper-evident records. We start with one public ledger only to verify payment, not to store payloads. Receipts themselves are off-chain, signed JSON; anchoring means taking batches of receipts and publishing cryptographic fingerprints to a blockchain, creating a tamper-proof public record that anyone can verify. Anchoring batched receipts is an extension.

Here’s what I’m building on and what’s new:

Existing Infrastructure:

  • Public Anchoring: Certificate Transparency provides a proven model for public, append-only logs with cryptographic auditability
  • Trusted Execution Environments: Hardware-backed attestation for sensitive computations
  • Agent Payments Protocol (AP2): Google’s framework enabling agents to execute payments across card/bank/crypto rails. AP2 abstracts multiple payment methods, allowing vHTTP to slot in as “one crypto rail profile.”
  • HTTP 402 Payment Required (x402): Cloudflare’s revival of the HTTP 402 status code for payment negotiation, with Coinbase’s implementation. HTTP 402 is reserved in RFC 9110; x402 is a non-standard profile layered on top.

My ✨Innovation✨:

  • Verified HTTP (vHTTP): My proposed web-native protocol that combines x402, AP2, and a new receipt anchoring layer to transform standard HTTP into receipted transactions with cryptographic proof

Minimal first: vHTTP = six receipt fields + three binders (chainId, token, amount). Everything else (AP2, x402, anchoring, TEEs/zkTLS) is an Extension Profile you can add later without changing receipts.

Rather than defaulting to one substrate, I use a Trust Gradient framework with three axes: Pre-trust (existing relationships), Verifiability cost (expense of checking claims), and Neutrality (resistance to operator bias).

Anchoring Policy

While vHTTP keeps anchoring optional for simplicity, the protocol recognizes that shared ledgers serve as the default substrate for truth in open, adversarial contexts. The anchoring policy balances pragmatism with the thesis that agents need credibly neutral memory:

Context Anchoring Level Rationale
Open marketplaces REQUIRED Permissionless environments need shared, tamper-evident history
Bilateral/vendor RECOMMENDED Existing relationships can rely on transparency logs initially
Internal systems OPTIONAL High pre-trust scenarios may use database + audit logs

This policy ensures that when neutrality and history-rewrite resistance are required, anchoring becomes mandatory, honoring the “ledger of truth” ethos while keeping vHTTP lean for trusted contexts.

From Handshakes to Hash-Verified Receipts

Human trust relies on social mechanisms like reading body language, remembering past interactions, and building relationships over time. Machines don’t have access to these channels. When Agent A claims “I delivered service X to Agent B,” that assertion needs to be independently verifiable by Agent C, Agent D, and every future trading partner without requiring slack messages or manual verification.

The solution flips the traditional script: Every interaction emits a signed receipt, a compact, portable proof binding who, what, when, and under which terms. Aggregate enough receipts, and machine reputation emerges that’s mathematical rather than subjective.

Receipts become the new reputation. Public memory becomes the new foundation.

The Real Game: Stag Hunt, Not Betrayal

Most agent interactions aren’t zero-sum “gotcha” scenarios like the Prisoner’s Dilemma. They’re Stag Hunts: coordination games where everyone prefers mutual cooperation (hunting the stag together) but will settle for individual safety (hunting rabbits alone) if they doubt their partner’s commitment.

Sound familiar? This is typically how most real-world cooperation works.

In the classic 2×22 \times 2 Stag Hunt payoff matrix:

Hunt StagHunt RabbitHunt Stag(10,10)(0,7)Hunt Rabbit(7,0)(7,7)\begin{array}{c|cc} & \text{Hunt Stag} & \text{Hunt Rabbit} \\ \hline \text{Hunt Stag} & (10, 10) & (0, 7) \\ \text{Hunt Rabbit} & (7, 0) & (7, 7) \\ \end{array}

Both (Stag, Stag) and (Rabbit, Rabbit) are stable outcomes, but hunting stag together gives everyone better rewards. The tricky part is coordination: getting both parties to trust that the other won’t defect at the last moment.

What transforms Stag Hunt dynamics toward stable cooperation isn’t teaching empathy to GPUs; it’s public monitoring combined with credible commitments. When deviation becomes observable and costly in repeated interactions, cooperation emerges as the default rational strategy.


Meet vHTTP: The Verified HTTP Protocol

With the game theory established, let’s look at the implementation.

vHTTP is the minimal, runnable cut of the protocol: quote -> pay -> redeem -> deliver + signed receipt. No DIDs, no HTTP Message Signatures, no anchoring. One rail (USDC on Polygon PoS). One signature scheme (Ed25519). One canonicalization (JCS). It proves the thesis with 200\sim 200 LoC and interoperable receipts.

We only need a blockchain for the part of the interaction that requires credible neutrality under adversarial conditions: settlement. Everything else (bytes delivered, who signed them) is proven with signatures and can be independently checked off-chain. When neutrality of history matters globally, we add batch anchoring as an extension.

vHTTP: Start Simple

Before diving into the full specification, let me address a critical concern: complexity kills adoption.

Why? Because developers will just use something simpler. (they probably still will lol)

vHTTP keeps the receipt to a small, deterministic set (six receipt fields + three binders). See Appendix A for the minimal receipt fields & verifier checklist.

That’s it. JSON canonicalized with RFC 8785 (JCS), signed with Ed25519, and anchored to modern HTTP integrity/signature RFCs. Just enough to prove who paid whom for what and when.

The full vHTTP spec (Appendix A) adds layers for production use: anchoring, multiple proof modes, dispute resolution. But you can start with vHTTP and add complexity only when you need it.

Hash domain separation. dataHash = base64url(sha256("vhttp:resource:" || content-type || 0x00 || representation-bytes)) where representation-bytes are the selected representation data (identity-decoded when Content-Digest applies, per RFC 9530), ensuring gzip/brotli won’t cause verifier mismatches. This prevents cross-protocol hash confusion and binds content type. All base64url encoding is base64url (no padding).

Stored format: "dataHash": "sha256:<BASE64URL>" - the sha256: prefix is required in receipt JSON fields.

See Appendix A for the full spec and Appendix B for a worked example.

The vHTTP Flow

vHTTP Protocol Flow

1) Quote - Server responds with USDC price and payment details:

{
  "price": "0.10",
  "amount": 100000,
  "asset": "USDC",
  "chainId": 137,
  "token": "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359",
  "expires": "2025-09-28T02:00:00Z",
  "quote_id": "abc123"
}

2) Payment - Client transfers USDC on Polygon PoS:

await usdcContract.transfer(serverAddress, 100000); // 0.10 USDC

3) Redeem - Client presents payment transaction:

{
  "quote_id": "abc123",
  "paymentTx": "0x789def...",
  "idempotencyKey": "idem_2c4c6a4e"
}

Critical Security Note: The server MUST verify that the paymentTx + quote_id combination is unused and that the payment amount exactly matches the still-valid quote. This prevents ambiguous “I paid some amount” attacks when prices change between quote and redeem. The server stores this binding server-side and exposes it via /audit/tx/:hash for third-party verification.

4) Delivery + Receipt - Server returns data with signed receipt:

{
  "data": { 
    "results": [
      {"id": 1, "name": "Item 1"},
      {"id": 2, "name": "Item 2"}
    ],
    "total": 2
  },
  "receipt": {
    "server": "ed25519:Gv8rGm8rYgk5r8b7y2r6Ckb1o8cM1xyM2sJH3J0kH5E",
    "client": "ed25519:Hw9sHn9sZhl6s9c8y3s7Dlc2p9dN2yzN3tKI4K1lI6F",
    "payTo": "0xServerEthAddress",
    "payFrom": "0xClientEthAddress",
    "contentType": "application/json",
    "dataHash": "sha256:abc123def456...",
    "canonicalQuery": "limit=100&order=asc",
    "requestHash": "sha256:def456ghi789...",
    "chainId": 137,
    "token": "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359",
    "amount": 100000,
    "paymentTx": "0x789def...",
    "timestamp": "2025-09-28T01:30:00Z",
    "signature": "ed25519:xyz789abc123..."
  }
}

Verification: Check ERC-20 Transfer event matches payFrom -> payTo for amount on chainId=137, then verify receipt signature.

See Appendix A for the complete vHTTP spec and Appendix B for a worked example.

Extension Profiles (DIDs, HTTP Message Signatures, x402/AP2, anchoring) are covered in Appendix C.

Canonical Query Format (vhttp)

The canonicalQuery field MUST be a normalized RFC-3986 query string with lexicographically sorted keys, percent-encoded once, with no unecessary ? or & characters.

Data Format Specifications

  • Amounts: Integer values in token base units (for USDC: 66 decimals, so amount is in micro-USDC 10610^{-6}). Human-readable price fields use decimal strings.
  • Timestamps: RFC 3339 UTC format
  • Hash algorithms: Use sha256: prefix for receipt-internal digests (base64url, no padding), copy HTTP header values verbatim per RFC 9530
  • Clock skew: Servers MUST tolerate ±120s\pm 120\,\mathrm{s} clock skew on quote.expires/issuedAt

The critical innovation: every call produces a receipt that can be anchored (in batches for efficiency) and independently audited by any third party.

The Redeem Call

vHTTP redeem body (minimal) - MUST include quote_id, paymentTx, idempotencyKey:

{
  "quote_id": "abc123",
  "paymentTx": "0x789def...",
  "idempotencyKey": "idem_2c4c6a4e"
}

This ensures idempotency and prevents replay attacks. Extension profiles (x402/AP2) add quote binding and request verification.

Safety & Abuse: Baked In, Not Bolted On

Replay Protection: Short-lived quote IDs and idempotency keys on redemption endpoints prevent replay attacks.

Micropayment DoS: Prepaid ticket systems or per-ticket deposits with automatic throttling for underpayments eliminate free-tier abuse at scale. For vHTTP, your simplest guard is an upfront deposit or per-client prepaid balance; receipts debit against it, avoiding on-chain thrash while keeping the same verification story.

Profile B - Deposit/Prepaid Balance: For high-frequency scenarios, implement thresholded prepaid balances where receipts debit against client deposits rather than requiring per-transaction on-chain payments. This prevents on-chain thrash while maintaining the same receipt semantics and verification story. Example: Client deposits $10 USDC, server issues receipts debiting $0.01 per call until balance exhausted.

Settlement Risk: Finality windows for crypto rails and escrow/refund mechanisms for traditional payment methods (via AP2) manage counterparty risk.

Privacy: Anchor commitments (hashes/pointers) rather than raw payloads. Default proof = re-execution. Upgrade path = TEE attestation (remote-attested) for private inputs. Optional selective-disclosure via zkTLS/TLSNotary.

TLSNotary = proof-of-TLS transcript (selective disclosure). zkTLS = zero-knowledge proof that a TLS session occurred/produced output (research & early libs). TEE = remote attestation for execution.

Data Minimization: Receipts serve as public memory, not PII storage. Never include user PII in receipts-only cryptographic references (hashes, timestamps, keys, payment refs). Defer content quality proofs to proof pointers (re-exec, TEE attestations) instead of embedding details.

Think Certificate Transparency: anchor the commitment, not the content.

Censorship & Reorgs: Batch anchors to fast Layer 2 networks with optional multi-anchor redundancy; publish explicit anchor SLAs (“Anchored 5minutes\leq 5\,\text{minutes}”) similar to Certificate Transparency’s Maximum Merge Delay (MMD) concept from RFC 9162.

The fundamental principle: treat receipts like money and abuse like DDoS.

The Trust Gradient: A Compass for “When Blockchain”

Rather than blindly copying what others do, the Trust Gradient uses three axes to determine what infrastructure you actually need:

Pre-trust (P): Do you already have shared administration, contracts, or legal recourse? Verifiability cost (V): How expensive is it to independently check claims?
Neutrality (N): How resistant do you need to be to single-operator bias or history rewriting?

Trust Gradient 3D Space

This creates a decision space where you pick the minimal infrastructure that actually solves your trust problem. Here’s how it works in practice:

Your agent calling your own API? High pre-trust, cheap verification -> database + audit log. Maybe anchor the daily root if external auditors matter.

Agent buying data from a vendor API? This is where vHTTP shines. You need receipts to prove you got what you paid for, but you don’t need to put every transaction on mainnet. Batch anchor to a cheap L2.

Agent marketplace where anyone can join? Zero pre-trust, maximum neutrality needed -> escalate from transparency logs to public chains when neutrality and history-rewrite resistance have to be shared, permissionlessly.

High-frequency agent trading? Some things are too fast for consensus. Price the counterparty risk, settle disputes later.

Notice that “blockchain” emerges at the intersection of lowest pre-trust and highest neutrality requirements, not as a universal solution. Use transparency logs when a single operator (plus auditors) suffices; use a public chain when independent parties need shared, permissionless finality and rewrite-resistance. The gradient helps you avoid both “blockchain everything” and “blockchain never” extremes.

Anti-patterns (please don’t)

  • Everything on-chain -> batch commitments; keep payloads off-chain.
  • One global reputation score -> let consumers compute from receipts.
  • Unlimited free tiers -> deposits/prepaid tickets or you get farmed.
  • “Trust me” logs -> externalize or anchor; assume rewrite risk.
  • Single proof mode -> support re-exec, TEE, zkTLS; choose per threat model.

Network Effects: Why This Gets Better Over Time

The value proposition compounds because new connections become verifiable, not just numerous. Unlike traditional networks where more users just means more noise, receipted networks get smarter as they grow.

Traditional network effect models range from linear (Sarnoff’s Law: value n\propto n) to exponential (Reed’s Law: value 2n\propto 2^n), but evidence suggests reality follows Odlyzko’s more conservative nlognn \log n pattern. Empirically, network value often tracks nlognn \log n rather than n2n^2 or 2n2^n. What matters isn’t the exact formula. It’s that verified connections create compounding value.

Network Value Growth Laws

Here’s the simple math: Cooperation becomes rational when the benefits outweigh the costs plus the reputation damage from cheating.

If you want the actual formula (because some of you like math):

Cooperation wins if:

Rcv+δΔrepSR - c_v + \delta \Delta_{\text{rep}} \geq S

and

qPLqP \geq L

Translation: Make cheating expensive, verification cheap, and getting caught realllly awkward at future agent work conferences.

Translation for the rest of us:

  • RR = reward from cooperating, SS = safe fallback option
  • cvc_v = cost to validate claims, δΔrep\delta \Delta_{\text{rep}} = future reputation boost
  • qPqP = (detection probability ×\times penalty), LL = loss from cheating

In plain English:

  • Cheaper validation -> easier to check if someone’s trustworthy
  • Public detection -> harder to hide bad behavior
  • Real penalties -> reputation damage actually hurts future business

As more agents join and create receipts, due diligence costs decrease and fraud detection improves for everyone in the network.

Failure Mode Analysis: Illustrative Scenario

Lets go back to the beginning example scenario where “AlphaAPI” an inference service, suffered botnet farming. Thousands of sockpuppet accounts stayed beneath free-tier limits individually but collectively generated five-figure compute costs overnight. Traditional rate limiting and KYC failed because:

  • Rate limiting: Easily bypassed with distributed IPs and rotating user agents
  • KYC: Fake identities and stolen credentials circumvented identity verification
  • Pattern detection: Individual accounts looked legitimate; only aggregate analysis revealed the attack

Let’s not pretend that these haven’t been bypassed before.

Why vHTTP solves this better:

  • Economic disincentive: $0.01 deposit per call makes 250k call bot run costs $2,500 upfront-flipping attacker ROI negative
  • Cross-account correlation: Public receipts reveal patterns across accounts (same payment source, timing, behavior) that individual account analysis misses
  • Reputation binding: AgentCards with Verifiable Credentials make mass identity creation expensive and traceable
  • Third-party monitoring: Public receipt logs enable external services to detect and blacklist coordinated attacks
  • Attestation signals: Legitimate clients can signal reliability through verified attestations, negotiating better terms while attackers pay full price

The core principle: accountability cannot be faked at scale when every interaction leaves a cryptographic trace that can be correlated across accounts.

The Agent Society Stack

Stage 1 - Payments: Get basic quote-and-pay working across different payment methods (cards, banks, crypto) using AP2 and x402

Stage 2 - Identity & Discovery: Let agents advertise what they do and find each other using portable AgentCards

Stage 3 - Receipts & Proofs: Add cryptographic proof that interactions actually happened as claimed

Stage 4 - Reputation: Build reputation scores from the receipt history, letting each consumer decide what matters

Stage 5 - Coordination Markets: Enable complex multi-agent coordination through escrows, bounties, and auctions

The architecture keeps on-chain components minimal while maintaining proof portability across different trust substrates.

Builder’s Roadmap (I’ll probably do this soon (don’t quote me on this))

MVP - Basic Receipts

  • Simple signed receipts with content hashes and timestamps
  • Daily batch anchoring to Arbitrum (cheap, fast)
  • Public /audit endpoint for receipt verification
  • Basic CLI tool for generating and verifying receipts
  • Focus: Prove the core concept works with real data

V1 - Payment Integration

  • Integrate with one payment rail
  • Add quote/redemption flow with proper quote ID handling
  • Basic DID-based identity (start with simple key pairs)
  • Receipt compression and selective anchoring based on value
  • Focus: Make it actually usable for real agent transactions

V2 - Full Protocol

  • Complete vHTTP specification with all the bells and whistles
  • Multiple proof modes (re-execution, TEE, zkTLS)
  • AgentCard discovery and basic reputation scoring
  • Cross-chain anchoring and receipt portability
  • Focus: Production-ready system that scales

The hard part is getting agents to actually use it and proving the economics work in practice.

When NOT to Use Blockchain

Same organization, same admin: Use database + audit log; optionally anchor roots for external assurance

Ultra-low latency requirements (HFT-style): Courts + contracts, not confirmations. Price the risk; settle later.

Sensitive data: Prefer TEE/zkTLS flows with on-chain commitments, not payloads

Any scenario where the admin has both motive and means to bias the log: This is precisely when you escalate to public anchors.

Mathematical Foundation

For those who want the formal treatment, cooperation becomes rational in repeated interactions when:

Benefit of cooperatingvalidation cost+future reputation liftsafe fallback\text{Benefit of cooperating} - \text{validation cost} + \text{future reputation lift} \geq \text{safe fallback}

AND

(detection probability×penalty)loss from deviation(\text{detection probability} \times \text{penalty}) \geq \text{loss from deviation}

What this means in practice: vHTTP gives you the knobs to tune these variables. We make validation cheaper (standardized receipts), detection more likely (public anchoring), and penalties real (reputation damage that affects future business).

The cool part is that these aren’t abstract game theory concepts, they’re parameters you can tune.

We deliberately bound identity to Ed25519 public keys in the core protocol (DIDs are an optional extension) and bound payments to native USDC on Polygon PoS (chainId=137, published token address), eliminating two of the biggest sources of early-stage ambiguity (identity resolution and bridged assets).

Economics: Making Trust Profitable

Fee Structure: 0.1%–1% of transaction value, decreasing with volume. This funds L2 anchoring and infrastructure while remaining competitive with traditional payment systems (2%–3%).

Economic Incentives: Deposits prevent griefing, reputation damage makes cheating expensive, and public receipts enable community-driven fraud detection.

Cost Model (example): Batch 10,00010{,}000 receipts per anchor. A single Arbitrum L2 root publish costs gas_used ×\times gas_price; with typical L2 prices, this yields sub-cent per $10$k receipts; per-receipt anchoring cost $0.001\ll \$0.001 (order-of-magnitude).

Concrete numbers: Assume one Arbitrum L2 anchor tx ≈ $0.05–$0.30 depending on congestion. Batch 10,000 receipts -> one tx: per-receipt anchor ≈ $0.000005–$0.00003 ($0.0005–$0.003 cents). Even if fees spike to $0.50/tx, you’re still at $0.00005/receipt ($0.005 cents). Order-of-magnitude 10510^{-5}10310^{-3} USD per receipt at current L2 fees (fees fluctuate with network congestion). Publish your actual gas figures in /audit.

Open Questions & Future Work

While the core vHTTP design addresses the fundamental trust problem, several areas need deeper exploration:

Privacy at Scale: While TEE and zkTLS provide privacy for individual interactions, some agent workflows involve sensitive data where even hashed commitments could leak information. Advanced techniques like homomorphic encryption or secure multi-party computation may be needed for truly private agent-to-agent commerce.

Cross-Chain Interoperability: The current design focuses on single-chain anchoring, but agent networks will likely span multiple chains. We need mechanisms for cross-chain receipt verification and reputation portability-perhaps through notary schemes or decentralized oracles.

Dispute Resolution: What happens when receipts conflict or one party claims non-delivery? A decentralized arbitration process or smart contract-based escrow could provide transparent resolution pathways without requiring human intervention.

Economic Sustainability: Who pays for L2 anchoring costs? How do we prevent griefing through expensive anchoring of meaningless receipts? The fee structure needs to align transaction value with anchoring costs while maintaining system integrity.

Scalability & Lifecycle: How do we handle agents generating millions of receipts? Do receipts expire? What happens during key rotation? These operational concerns will determine whether vHTTP scales beyond proof-of-concept.

Developer Experience: The protocol is comprehensive, but I may have gone a bit overboard. It has a learning curve. We need better tooling, documentation, and perhaps simplified APIs to encourage adoption among existing agent developers.

Implementation Resources

The foundational protocols are actively developed:

  • Google AP2: Agent payment framework with dozens of partners including Mastercard, PayPal, and Coinbase
  • Cloudflare x402: HTTP-native payment negotiation with deferred settlement schemes
  • ERC-8004 (proposed): Trustless agent identity, reputation, and validation standard - actively discussed in the ecosystem
  • A2A Protocol: Agent-to-agent communication with Google backing
  • Model Context Protocol: Anthropic’s tool integration standard

Certificate Transparency provides a proven model for public, append-only logs with cryptographic auditability. Trusted Execution Environments offer hardware-backed attestation for sensitive computations.

Conclusion

If your agent infrastructure relies on unpriced access, lacks receipts, and operates without public memory, it will eventually face exploitation. Not due to malicious intent, but because the incentive structure invites it.

Public memory serves as the foundation. Receipts become reputation. Everything else is engineering.

The transition from human-mediated to machine-mediated commerce requires new trust primitives. vHTTP, building on AP2, x402, and ERC-8004, provides a verifiable path forward. One that hopefully makes cooperation not just possible, but profitable.

What’s Next

This is mostly just a brain dump of ideas I’ve been thinking about. If any of it resonates with you, feel free to run with it.

Honestly? I’m probably going to start building this way sooner than my roadmap suggests. The pieces are all there: AP2, x402, decent crypto primitives. Someone just needs to actually wire them together.

If you’re working on anything related to agent infrastructure, blockchain trust systems, or just think this approach is interesting (or completely wrong), I’d love to hear from you. The core insight: that agents need public memory through receipts, is worth exploring even if the full implementation is complex.

Hit me up on GitHub, Twitter, or LinkedIn. Always down to chat about this stuff.


For developers interested in implementation, Cloudflare’s x402 blog and Coinbase’s developer docs provide working code samples and integration guides.

Glossary

AP2 (Agent Payments Protocol): Extension profile for payment negotiation/abstraction; not used in vHTTP. Google’s framework enabling agents to execute payments across different payment systems (cards, banks, crypto).

ERC-8004 (proposed): Ethereum standard for trustless agent identity, reputation, and validation

TEE (Trusted Execution Environment): Extension profile for privacy-sensitive agent interactions. Hardware-backed secure computing environment that provides attestation for sensitive computations.

vHTTP (Verified HTTP): A minimal, receipt-first HTTP pattern (vHTTP) with Ed25519 + JCS receipts and a single payment rail (USDC on Polygon PoS). x402/AP2/anchoring are optional Extension Profiles.

x402 (HTTP 402 Payment Required): Extension profile for payment negotiation; not used in vHTTP. HTTP 402 is reserved in RFC 9110; x402 is a non-standard revival by Cloudflare/Coinbase.

zkTLS: Extension profile for privacy-sensitive agent interactions. Zero-knowledge proofs applied to TLS connections, allowing validation of data without revealing the full content

Appendix A: vHTTP Spec

Status: MVP / Heavy WIP

Scope: Paid HTTP delivery with verifiable receipts using Ed25519 + JCS and a single payment rail (USDC on Polygon PoS).

A. Roles & Flow

Client requests a paid resource. Server quotes price. Client pays USDC on Polygon PoS (chainId=137) and calls /redeem with paymentTx. Server delivers the resource and a signed receipt binding who/what/when to that on-chain payment.

Sequence: GET /resource -> 402 with quote -> off-chain USDC transfer -> POST /redeem -> 200 with {data, receipt}.

B. Canonicalization & Signatures

  • Canonical JSON: RFC 8785 JCS for the exact bytes covered by the signature. No ad-hoc “sorted JSON.”
  • Signature: Ed25519 (RFC 8032 semantics; any standard library). Message = JCS of the receipt object without the signature field.
  • Binary encoding: All receipt-internal binary fields use base64url without padding (RFC 4648 §5). Do not apply this to HTTP header fields. Receipt-internal digests use sha256:<base64url> format; HTTP header echoes (like contentDigest) copy the header value verbatim per RFC 9530 (e.g., Content-Digest: sha-256=:BASE64=:).

C. Minimal Objects

1) Quote (JSON)

{
  "price": "0.10",                 // human-readable
  "amount": 100000,                // base units (USDC has 6 decimals)
  "asset": "USDC",
  "chainId": 137,
  "token": "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359",
  "expires": "2025-09-28T02:00:00Z",
  "quote_id": "abc123"
}
  • Use native USDC on Polygon PoS (not USDC.e). Address shown is Polygon’s published native USDC.
  • Deploy-time verification: Implementers should verify the current canonical token address with official sources (Circle/Polygonscan) at deploy time-the exact address can change in docs over time and you don’t need to hard-code it in the spec. The commonly cited address 0x3c499c542cef5e3811e1192ce70d8cc03d5c3359 serves as a reference, but always verify against official sources.

2) Redeem (JSON)

{
  "quote_id": "abc123",
  "paymentTx": "0x789def...",
  "idempotencyKey": "idem_2c4c6a4e"
}

3) Delivery (JSON)

{
  "data": { "...": "application-specific payload" },
  "receipt": {
    "server": "ed25519:BASE64URLPUB",    // signing identity
    "client": "ed25519:BASE64URLPUB",    // optional: client identity (mutual auth in extensions)
    "payTo": "0xServerEthAddress",       // ERC-20 receiver
    "payFrom": "0xClientEthAddress",     // ERC-20 sender
    "contentType": "application/json",
    "dataHash": "sha256:BASE64URL",      // over identity-decoded bytes
    "contentDigest": "sha-256=:BASE64=:", // from Content-Digest header (RFC 9530) - verbatim copy
    "canonicalQuery": "limit=100&order=asc",  // RFC-3986 normalized query
    "requestHash": "sha256:BASE64URL",   // sha256(method|path|canonicalQuery|accept)
    "quoteHash": "sha256:BASE64URL",     // JCS of the quote object for cryptographic binding
    "serverOrigin": "https://api.example.com", // server origin to prevent cross-origin replay
    "serverPubKeyURL": "https://api.example.com/.well-known/vhttp/keys.json", // optional: key discovery URL
    "hostHeader": "api.example.com", // optional: Host header used on wire
    "chainId": 137,
    "token": "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359",
    "amount": 100000,
    "paymentTx": "0x789def...",
    "timestamp": "2025-09-28T01:30:00Z",
    "anchorRoot": "sha256:BASE64URL",    // optional: Merkle batch root
    "anchorRef": "eip155:137:0x...",     // optional: anchor transaction/URI
    "signature": "BASE64URL"             // ed25519 over JCS(receipt minus signature)
  }
}

Why these fields?

  • contentType + requestHash prevent variant/encoding swaps at delivery.
  • chainId/token/amount make the on-chain check unambiguous (disambiguate native USDC vs USDC.e and ensure correct chain).
  • anchorRoot + anchorRef provide optional linkage to batch anchoring without requiring crypto costs in vHTTP.

Key & Address Binding

vHTTP uses distinct namespaces for different purposes:

  • Ed25519 keys for signing receipts (cryptographic identity)
  • EVM addresses for payment settlement (on-chain accounts)

These must be bound in server metadata to prevent confusion. Servers MUST publish verification keys at /.well-known/vhttp/keys.json:

{
  "version": "vhttp-keys-v1",
  "server": "ed25519:Gv8rGm8rYgk5r8b7y2r6Ckb1o8cM1xyM2sJH3J0kH5E",
  "acceptedPayTo": ["0xServerEthAddress1", "0xServerEthAddress2"],
  "validFrom": "2025-09-27T00:00:00Z",
  "validUntil": "2025-12-31T23:59:59Z",
  "rotationPolicy": {
    "graceWindowDays": 30,
    "oldKeysValidUntil": "2026-01-30T23:59:59Z"
  },
  "evmBindingSig": "0x...",  // EIP-191 or EIP-712 signature from payTo address over Ed25519 pubkey
  "bindingType": "eip-191"   // or "eip-712"
}

Key Rotation & Multi-Key Policies:

  • Grace windows: Old keys remain valid for 30\geq 30 days after rotation to ensure past receipts remain verifiable
  • Multi-key servers: For per-service keys, include multiple server entries with distinct acceptedPayTo arrays
  • Strong binding: The Ed25519 key and accepted payTo addresses must be cryptographically bound (e.g., via server attestation or key derivation)
  • HTTP Message Signatures: This pairs well with later HTTP Message Signatures on headers for CDN/proxy traversal

This mapping ensures that receipt verification uses the correct Ed25519 key while payment verification uses the correct EVM address, preventing namespace confusion.

D. Server Requirements (vHTTP)

  1. Quotes: generate unique quote_id MUST contain 128\geq 128 bits of unpredictable entropy (e.g., base64url of 1616 random bytes), set expires (RFC 3339 UTC), compute amount in base units (USDC: 66 decimals). Quote ID TTL 10\leq 10 min.
  2. Payment check: on POST /redeem, ensure paymentTx is final using policy-based finality. Default: minFinalitySecs = 900 on Polygon PoS; implementers should tune via runtime policy. Return FINALITY_PENDING with retryAfter seconds when transaction is visible but not final. Servers MUST reject paymentTx if multiple matching Transfer logs exist in the transaction and none matches all of (token, from, to, amount). PoS chains can experience temporary finality delays; configure appropriate thresholds based on your risk tolerance and network conditions.
  3. Quote binding verification: Verify that paymentTx + quote_id combination is unused and payment amount exactly matches the still-valid quote. Store this binding server-side and expose via /audit/tx/:hash for third-party verification. This prevents ambiguous payment attacks when prices change between quote and redeem.
  4. Idempotency: store idempotencyKey (opaque, 128\geq 128 bits) with retention window 24h\geq 24\,\mathrm{h}; same parameters -> same receipt; different parameters -> HTTP 409.
  5. Receipt signing: JCS-canonicalize the receipt without signature, then Ed25519-sign and attach signature. (see JCS)
  6. Data hashing: compute dataHash over the identity-decoded representation bytes; include the delivered contentType. For future profiles, you can adopt Repr-Digest/Content-Digest headers; vHTTP keeps this in-body. (RFC 9530)
  7. Key discovery: Servers MUST publish verification keys at /.well-known/vhttp/keys.json and an EIP-191 or EIP-712 signature from each payTo address over the server’s Ed25519 pubkey (evmBindingSig). Verifiers MUST validate this signature for binding. Retain old keys for N\geq N days so past receipts remain verifiable.
  8. Public audit: Expose /audit/tx/:hash endpoint that returns the server’s view of a payment/receipt link for third-party verification.

E. Verifier Checklist (vHTTP)

Given {data, receipt} and server_pubkey:

  • Recompute dataHash (sha-256 over delivered bytes); compare to receipt.
  • If Content-Digest header is present, compare it to contentDigest in receipt and to a recomputed digest of the identity representation; mismatch -> RECEIPT_MALFORMED. Note: When a Content-Encoding is present, compute dataHash over the identity representation (post-decode), aligning with Repr-Digest semantics.
  • Recompute requestHash = sha256(method\npath\ncanonicalQuery\naccept) used for this call; compare. Canonical format: UPPER(METHOD) "\n" CANON_PATH "\n" CANON_QUERY "\n" CANON_ACCEPT where:
    • UPPER(METHOD): Uppercased ASCII (e.g., “GET”)
    • CANON_PATH: Percent-decoded once, dot-segments removed, only unreserved bytes decoded per RFC 3986 (e.g., “/resource”)
    • CANON_QUERY: RFC-3986 normalized with lexicographically sorted keys, spaces encoded as %20, percent-encoded once, no superfluous ? or & characters
    • CANON_ACCEPT: Trimmed, lowercased MIME tokens, collapsed OWS (e.g., “application/json”)
  • JCS-canonicalize the receipt without signature; verify Ed25519 signature. (see JCS)
  • Fetch paymentTx on chainId 137, parse logs: find ERC-20 Transfer where address == token, topic0 == keccak("Transfer(address,address,uint256)"), topic1 == payFrom, topic2 == payTo, data == amount. Compare topics[0] as 32-byte values to avoid type mismatches. Reject if multiple candidate Transfer logs exist and none matches all of (token, from, to, amount). Tx must have status == 1. (ERC-20)
  • EVM↔Ed25519 binding verification: Verifiers MUST validate evmBindingSig (EIP-191 or EIP-712) proving that a listed payTo has signed the server Ed25519 key. This prevents namespace confusion between payment addresses and receipt signing keys.
  • Enforce finality policy: confirmations N\geq N or block_age T\geq T seconds (tunable due to PoS finality events). Example: confirmations >= 20 or block_age >= 900s on Polygon PoS; operators should tune per incident advisories. PoS chains can experience temporary finality delays; configure appropriate thresholds based on your risk tolerance and network conditions.

Pass -> cryptographic evidence the quoted request was paid on the right rail and these exact bytes were delivered by this server key.

Note: The canonical ERC-20 Transfer event has topic0 = keccak256("Transfer(address,address,uint256)"); topic1 is from, topic2 is to, data is the uint256 amount. Parse on chainId=137 and require receipt.status == 1. (ERC-20)

Client identity semantics: In vHTTP, the client field is optional and serves as a hint for mutual authentication. For full mutual non-repudiation, clients should sign the redeem request body and include clientSig in the receipt. This moves mutual authentication to Extension Profiles (DIDs/HTTP Message Signatures) to keep vHTTP minimal. Note: Without client signing, servers can attribute any payFrom to any client key, so mutual auth requires explicit client signatures.


Appendix B: Worked Example (USDC on Polygon PoS)

Status: Example / Non-normative
All IDs, hashes, signatures, and keys below are illustrative.
JCS = RFC 8785 canonical JSON, base64url (no padding).

0) Setup (Keys, Chain, Token)

  • Server key (Ed25519): Gv8rGm8rYgk5r8b7y2r6Ckb1o8cM1xyM2sJH3J0kH5E (base64url no padding)
  • Client key (Ed25519): Hw9sHn9sZhl6s9c8y3s7Dlc2p9dN2yzN3tKI4K1lI6F (base64url no padding)
  • Chain: Polygon PoS (chainId: 137)
  • Token: Native USDC on Polygon PoS (0x3c499c542cef5e3811e1192ce70d8cc03d5c3359)
  • Hash alg: sha-256
  • Sig alg: ed25519

1) Quote

Client -> Server GET /resource?limit=100&order=asc

Server -> Client 402 Payment Required

{
  "price": "0.10",
  "amount": 100000,
  "asset": "USDC",
  "chainId": 137,
  "token": "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359",
  "expires": "2025-09-28T02:00:00Z",
  "quote_id": "abc123"
}

2) Payment (USDC Transfer on Polygon PoS)

Client executes USDC transfer on-chain:

// USDC Transfer transaction
const tx = await usdcContract.transfer(
  "0xServerAddress",  // server's address
  100_000n,           // amount in base units (0.10 USDC) - BigInt
  // gasLimit omitted - let ethers estimate
);

Expected Transfer event log:

{
  "address": "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359",
  "topics": [
    "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", // Transfer(address,address,uint256)
    "0x000000000000000000000000ClientAddress",
    "0x000000000000000000000000ServerAddress"
  ],
  "data": "0x00000000000000000000000000000000000000000000000000000000000186a0", // 100000 in hex
  "transactionHash": "0x789def...",
  "status": 1
}

Note: Polygon PoS has experienced temporary finality delays (10–15m); vHTTP recommends minConfirmations=N or minFinalitySecs=T before redemption.


3) Redeem

Client -> Server POST /redeem

{
  "quote_id": "abc123",
  "paymentTx": "0x789def...",
  "idempotencyKey": "idem_2c4c6a4e"
}

4) Delivery + Receipt

Server -> Client 200 OK

{
  "data": { 
    "results": [
      {"id": 1, "name": "Item 1"},
      {"id": 2, "name": "Item 2"}
    ],
    "total": 2
  },
  "receipt": {
    "server": "ed25519:Gv8rGm8rYgk5r8b7y2r6Ckb1o8cM1xyM2sJH3J0kH5E",
    "client": "ed25519:Hw9sHn9sZhl6s9c8y3s7Dlc2p9dN2yzN3tKI4K1lI6F",
    "payTo": "0xServerEthAddress",
    "payFrom": "0xClientEthAddress",
    "contentType": "application/json",
    "dataHash": "sha256:abc123def456...",
    "canonicalQuery": "limit=100&order=asc",
    "requestHash": "sha256:def456ghi789...",
    "chainId": 137,
    "token": "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359",
    "amount": 100000,
    "paymentTx": "0x789def...",
    "timestamp": "2025-09-28T01:30:00Z",
    "signature": "ed25519:xyz789abc123..."
  }
}

5) Verifier Checklist (vHTTP)

Given {data, receipt} and server_pubkey:

  1. Recompute dataHash (sha-256 over delivered bytes); compare to receipt.
  2. Recompute requestHash = sha256(“GET\n/resource\nlimit=100&order=asc\napplication/json”); compare. Note: canonicalQuery field contains the RFC-3986 normalized query string.
  3. JCS-canonicalize the receipt without signature; verify Ed25519 signature. (see JCS)
  4. Fetch paymentTx on chainId 137, parse logs: find ERC-20 Transfer where:
    • address == "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359"
    • topic0 == keccak("Transfer(address,address,uint256)")
    • topic1 == payFrom_address
    • topic2 == payTo_address
    • data == 100000
    • status == 1
  5. EVM↔Ed25519 binding verification: Verifiers MUST validate evmBindingSig (EIP-191 or EIP-712) proving that a listed payTo has signed the server Ed25519 key. This prevents namespace confusion between payment addresses and receipt signing keys.
  6. Enforce finality policy: confirmations >= N or block_age >= T seconds. Configure appropriate thresholds based on your risk tolerance and network conditions. (finality delays)

Pass -> cryptographic evidence the quoted request was paid on the right rail and these exact bytes were delivered by this server key.


6) Minimal Verifier (Pseudo-Code)

from rfc8785 import canonicalize_json  # JCS
from crypto import ed25519_verify, sha256
from web3 import Web3

def verify_vhttp(data, receipt, server_pubkey):
    # 1) Data hash verification
    prefix = b"vhttp:resource:"
    content_type = receipt["contentType"].encode('utf-8')
    separator = b"\x00"
    data_bytes = json.dumps(data).encode('utf-8')
    hash_input = prefix + content_type + separator + data_bytes
    assert base64url(sha256(hash_input)) == receipt["dataHash"].split(":")[1]
    
    # 2) Request hash verification (canonical format)
    method = "GET"
    path = "/resource"  # percent-decoded once
    canonical_query = receipt["canonicalQuery"]  # RFC-3986 normalized from receipt
    accept = "application/json"  # lowercased, spaces collapsed
    request_string = f"{method}\n{path}\n{canonical_query}\n{accept}"
    assert base64url(sha256(request_string.encode())) == receipt["requestHash"].split(":")[1]
    
    # 3) Receipt signature verification
    receipt_without_sig = {k: v for k, v in receipt.items() if k != "signature"}
    receipt_canonical = canonicalize_json(receipt_without_sig)
    signature = receipt["signature"].split(":")[1]
    assert ed25519_verify(server_pubkey, receipt_canonical, signature)
    
    # 4) On-chain payment verification
    w3 = Web3(Web3.HTTPProvider(os.getenv("POLYGON_RPC_URL", "https://polygon-rpc.com")))
    tx = w3.eth.get_transaction_receipt(receipt["paymentTx"])
    
    # Find ERC-20 Transfer event
    transfer_topic = w3.keccak(text="Transfer(address,address,uint256)")
    pay_from = receipt["payFrom"].lower().replace("0x", "").zfill(64)
    pay_to = receipt["payTo"].lower().replace("0x", "").zfill(64)
    
    for log in tx.logs:
        if (log.address.lower() == receipt["token"].lower() and 
            log.topics[0] == transfer_topic and
            log.topics[1].hex() == f"0x{pay_from}" and
            log.topics[2].hex() == f"0x{pay_to}" and
            int(log.data, 16) == receipt["amount"]):
            break
    else:
        raise Exception("Transfer event not found")
    
    # 5) Finality check
    current_block = w3.eth.block_number
    assert current_block - tx.blockNumber >= MIN_CONFIRMATIONS

    return True

7) Failure Cases

  • QUOTE_ID_MISMATCH
{ "code": 1001, "error": "QUOTE_ID_MISMATCH", "hint": "Unknown/expired/reused quote ID." }
  • UNDERPAID
{ "code": 1002, "error": "UNDERPAID", "hint": "Amount/asset does not satisfy quote." }
  • FINALITY_PENDING
{ "code": 1003, "error": "FINALITY_PENDING", "hint": "Payment visible but not final (Polygon PoS has seen 10-15m delays)." }
  • RECEIPT_MALFORMED
{ "code": 1004, "error": "RECEIPT_MALFORMED", "hint": "Bad signature or schema." }
  • IDEMPOTENCY_CONFLICT
{ "code": 1006, "error": "IDEMPOTENCY_CONFLICT", "hint": "Same key, different parameters." }

8) Interop Notes

  • Canonicalization: Always apply JCS before hashing/signing receipts.
  • USDC on Polygon PoS: Use native USDC address 0x3c499c542cef5e3811e1192ce70d8cc03d5c3359 (not USDC.e). (Polygon PoS)
  • Finality: Configure minConfirmations or minFinalitySecs due to Polygon PoS finality delays.
  • ERC-20 Events: Use standard Transfer(address,address,uint256) event for payment verification. (ERC-20)

Appendix C: Extensions (Profiles & Proofs)

The following extensions can be added to vHTTP without changing the core receipt format. These are Extension Profiles that layer additional functionality on top of the minimal protocol.

HTTP Message Signatures

Purpose: Header integrity when interacting with proxies/CDNs.

Implementation: Use RFC 9421 HTTP Message Signatures over selected headers (date, content-digest, x-quote-id) for additional response integrity beyond the receipt signature.

Profile A: Sign date content-digest content-type x-quote-id to survive CDNs/re-proxies, with references to RFC 9530 and RFC 9421.

Status: Out of vHTTP scope; recommended for production deployments.

Digest Fields (Content/Repr-Digest)

Purpose: On-wire integrity verification.

Implementation: Adopt RFC 9530 Content-Digest and Repr-Digest headers for standardized payload integrity checking.

Status: Easy to add later without changing receipt layout; vHTTP keeps integrity checking in-body for simplicity.

x402/AP2 Profiles

Purpose: Multi-rail payment abstraction.

Implementation: Map paymentRef semantics to different payment rails (cards, ACH, stablecoins) while maintaining receipt compatibility.

Field Mapping:

vHTTP Field x402 Location AP2 Location Notes
quoteId X-Quote-Id header quoteId in request body Unique quote identifier
quote_id X-Quote-Id header quote_id in request body Replay protection
paymentRef X-Payment-Ref header paymentRef in request body Payment reference across rails

Note: HTTP 402 is reserved by RFC 9110; x402 is a non-standard revival by Cloudflare/Coinbase.

Status: Extension profile for when you need payment rail abstraction.

Anchoring

Purpose: Batch receipts and anchor roots to public logs/L2 for tamper-evidence.

Implementation: Merkle-batch receipts and publish anchor roots to public transparency logs or L2 networks. Expose /anchor/{batchId} with Merkle proofs.

SLO: “Anchored 5minutes\leq 5\,\text{minutes}” MMD-style similar to Certificate Transparency’s Maximum Merge Delay concept.

Status: Extension for when you need public tamper-evidence beyond individual receipt signatures.

TEEs / zkTLS

Purpose: Privacy and provenance proofs for sensitive computations.

Implementation:

  • TEE: Remote attestation for execution in trusted hardware
  • zkTLS: Zero-knowledge proofs of TLS session occurrence/output (experimental)

Status: Extension profiles for privacy-sensitive agent interactions.

Example: Full vHTTP with Extensions (Non-normative)

AgentCard Discovery:

{
  "version": "agentcard-v1",
  "did": "did:web:example.com/agent-42",
  "name": "DataAnalysis Agent",
  "capabilities": ["csv-process", "stat-analyze"],
  "acceptedRails": ["x402:USDC-ETH", "x402:USDC-SOL", "ap2:CARD", "ap2:ACH"],
  "proofModes": ["zkTLS", "tee", "re-exec"],
  "updatedAt": "2025-09-27T16:09:00Z",
  "attestations": {
    "count": 1247,
    "latestAnchor": "eip155:42161:0xabc123...",
    "reputationRefs": ["ipfs://QmAttest..."]
  }
}

HTTP Message Signatures (Extension):

Date: Sat, 27 Sep 2025 16:10:20 GMT
Content-Digest: sha-256=:F9a4...: (per RFC 9530)
X-Quote-Id: q_7f1c9d
Signature-Input: sig1=("date" "content-digest" "content-type" "x-quote-id");keyid="did:web:example.com#key-1";alg="ed25519";created=1758999020
Signature: :c2lnbmF0dXJlLWJhc2U2NHVybDo...:

x402/AP2 Redeem Body (Extension):

{
  "paymentRef": "pay_0x9f00c3",
  "quoteId": "q_7f1c9d",
  "quote_id": "8f4b3b2c-07c1-4a5d-a62e-2e2cd1f5d77f",
  "idempotencyKey": "idem_2c4c6a4e",
  "clientDid": "did:web:client.ai#key-1",
  "canonicalRequest": {
    "method": "GET",
    "path": "/resource",
    "query": "limit=100&order=asc",
    "contentDigest": "sha-256=:...:"
  }
}

Standards Used in vHTTP

  • JSON Canonicalization Scheme (JCS) - deterministic, hashable JSON.
  • ERC-20 events - Transfer log is the payment truth. (ERC-20)
  • HTTP Semantics (402 reserved) - context for later x402 profile. (RFC 9110)
  • Digest Fields / HTTP Message Signatures - for future profiles. (RFC 9530)

Appendix D: Verifier’s Cheat Sheet

Quick Reference for vHTTP Receipt Verification

Inputs Required

  • {data, receipt} - Response payload and signed receipt
  • server_pubkey - Ed25519 public key from /.well-known/vhttp/keys.json
  • paymentTx - Transaction hash to verify on-chain

5-Step Verification Process

Step Check Expected Output Failure Code
1 dataHash matches sha256(data_bytes) Hash comparison passes RECEIPT_MALFORMED
2 requestHash matches sha256(method\npath\ncanonicalQuery\naccept) Request hash matches RECEIPT_MALFORMED
3 Ed25519 signature over JCS(receipt - signature) Signature verification passes RECEIPT_MALFORMED
4 ERC-20 Transfer event on chainId=137 Transfer event found with correct params UNDERPAID
5 Finality policy (confirmations N\geq N or block_age T\geq T) Transaction is final FINALITY_PENDING

Canonical Request Format

UPPER(METHOD)
CANON_PATH
CANON_QUERY
CANON_ACCEPT

Where:

  • UPPER(METHOD): Uppercased ASCII (e.g., “GET”)
  • CANON_PATH: Percent-decoded once, dot-segments removed, only unreserved bytes decoded per RFC 3986 (e.g., “/resource”)
  • CANON_QUERY: RFC-3986 normalized with lexicographically sorted keys, spaces encoded as %20, percent-encoded once, no superfluous ? or & characters (e.g., “limit=100&order=asc”)
  • CANON_ACCEPT: Trimmed, lowercased MIME tokens, collapsed OWS (e.g., “application/json”)

Test Vector Suite

Input:

  • Method: “GET”
  • Path: “/resource”
  • Query: “limit=100&order=asc”
  • Accept: “application/json”

Expected Canonical String:

GET
/resource
limit=100&order=asc
application/json

Expected SHA-256 Hash (base64url): sha256:abc123def456... (implementation-specific)

Accept Header Normalization Test Vector

Input: Accept: application/json, text/plain;q=0.8, */*;q=0.1 Expected canonical: application/json,text/plain;q=0.8,*/*;q=0.1 Notes: Trimmed, lowercased MIME tokens, collapsed OWS, no space after commas

Note: The Accept header canonicalization is an implementation convention for receipts, not a change to HTTP semantics. See HTTP Semantics (RFC 9110) for the authoritative HTTP specification.

Canonical Query Algorithm:

  1. Encode spaces as %20 (not +)
  2. Sort by key, then by value for duplicate keys
  3. For multi-valued keys, repeat the key (don’t use , joins)
  4. Percent-encode only characters outside ALPHA / DIGIT / “-” / “.” / “_” / “~”
  5. No superfluous ? or & characters

Test Vectors:

Pathological ordering:

  • Input: ?z=1&a=2&a=1&z=2
  • Output: a=1&a=2&z=1&z=2

Duplicate keys:

  • Input: ?tag=value1&tag=value2&other=test
  • Output: other=test&tag=value1&tag=value2

Note: Independent implementations must produce identical canonical strings and hashes. Publish your test vectors to ensure interoperability.

ERC-20 Transfer Event Check

// On chainId=137, find Transfer event where:
address == "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359"  // Native USDC
topic0 == keccak256("Transfer(address,address,uint256)")
topic1 == payFrom_address
topic2 == payTo_address
data == amount
status == 1

Success Criteria

All 5 checks pass -> Cryptographic evidence that:

  1. The quoted request was paid on the correct rail
  2. These exact bytes were delivered by this server key
  3. The interaction occurred as claimed

Common Failure Modes

  • Hash mismatch: Data was modified after receipt signing
  • Signature invalid: Receipt was tampered with or wrong key used
  • Transfer not found: Payment never occurred or wrong chain/token
  • Not final: Transaction visible but not yet final (Polygon PoS delays)

Ship-Ready Lint Checklist

  • Request canonicalization produces identical bytes across two independent libs (publish vectors)
  • /.well-known/vhttp/keys.json served over HTTPS; includes evmBindingSig (EIP-191/712)
  • Content-Digest present on responses; matches contentDigest in receipt; recomputation matches
  • Finality policy numbers set per chain (Polygon PoS default 900s\geq 900\,\mathrm{s})
  • Verifier rejects if multiple Transfer logs and none matches all tuple fields
  • Quote ID is 128\geq 128-bit random; idempotency retention 24h\geq 24\,\mathrm{h}; 409 on conflict
  • All alg:value fields use unambiguous, unpadded base64url