Skip to content

Latest commit

 

History

History
191 lines (157 loc) · 11.4 KB

architecture.md

File metadata and controls

191 lines (157 loc) · 11.4 KB

Architecture

The problem

Merchants already have HTTP APIs. The gateway adds agent discovery, invocation and payment protocols without moving that logic into every backend or routing funds through a hosted intermediary.

The shape of the answer

┌──────────────────────────────────────────────────────────────────────────────┐
│                                   AI Agent                                   │
└───────────────────────────────────────┬──────────────────────────────────────┘
                                        │  MCP (tools/list, tools/call) · A2A · ACP · HTTP
                                        │  payment proof · Agent-Authorization
┌───────────────────────────────────────▼──────────────────────────────────────┐
│                            Agent Commerce Gateway                            │
│                      (runs in MERCHANT infrastructure)                       │
│                                                                              │
│                  protocol adapters: mcp · http · a2a · acp                   │
│                                      │                                       │
│                                      ▼                                       │
│                              ExecutionPipeline                               │
│                                      │                                       │
│             ┌────────────────────┬───┴─────────────┬────────────────┐        │
│             ▼                    ▼                 ▼                ▼        │
│   AuthorizationProvider   PaymentProvider   BackendExecutor   ReceiptStore   │
│           (ap2)             (x402, mpp)     (bounded HTTP)      (SQLite)     │
└───────────────────────────────────────┬──────────────────────────────────────┘
                                        │
                           ┌────────────▼───────────┐
                           │  Merchant Backend API  │
                           └────────────────────────┘

payment protocol: buyer → merchant, directly. Never through the gateway.

Three properties define the design:

  • Self-hosted. The gateway runs in the merchant's infrastructure. There is no central service operated by this project, and none is planned.

  • Non-custodial. Payment goes from buyer to merchant without passing through the gateway, which needs no buyer or merchant signing key. Local facilitator mode holds a separate gas-paying signer in process; the Security model says where it is allowed.

  • Configuration, not rewriting. A merchant describes an existing endpoint in config.yaml. agent-commerce import openapi can generate draft resource definitions:

    OpenAPI -> importer -> resource definitions -> canonical model -> pipeline
    

    The importer stops at the config boundary. Runtime code does not read the OpenAPI document or use OpenAPI-specific core types, so imported and hand-written resources follow the same path. See OpenAPI import.

Canonical model before protocol adapters

The core defines protocol-neutral commerce operations. It uses stable method names such as x402 and mpp for selection, but imports no MCP, JSON-RPC, x402 SDK, EIP-712 or EVM wire types. Its main concepts are:

Concept Type
a thing an agent can invoke CommerceResource
what it costs Pricing
how to reach the merchant backend BackendHandler
one inbound call CanonicalRequest
what must be paid, and the opaque provider challenge PaymentRequirement
what happened to a payment PaymentResult
proof of delivery CommerceReceipt
everything worth observing CommerceEvent

Adapters and providers own protocol- and rail-specific representations. Provider-native challenges pass through the core as opaque Record<string, unknown> values in PaymentRequirement.challenge.accepts.

New protocols map to the canonical model at their boundary instead of changing the pipeline. See Adapter guide.

The execution pipeline

Every adapter uses this pipeline, so each surface gets the same validation, authorization, payment and receipt behavior.

CanonicalRequest
  │
  ├─ resolve resource ─────────────────────► RESOURCE_NOT_FOUND
  ├─ validate input and the backend
  │  request shape ────────────────────────► INPUT_INVALID / BACKEND_ERROR
  ├─ resolve price
  │
  ├─ free ───────────────────────────────────────────────────┐
  └─ paid                                                    │
       ├─ create requirement                                 │
       ├─ no proof ─────────────► PaymentRequiredOutcome     │
       ├─ verify rejected ────────────► PAYMENT_INVALID      │
       ├─ replayKey missing ──────────► PAYMENT_INVALID      │
       ├─ authorize + reserve ────────► AUTHORIZATION_*      │
       ├─ reserve replayKey ──────────► PAYMENT_REPLAYED     │
       │                          or STORAGE_ERROR           │
       └─ upfront flow: settle ► PAYMENT_SETTLEMENT_FAILED   │
                                                             │
  ┌──────────────────────────────────────────────────────────┘
  ├─ call merchant backend ────────────────► BACKEND_TIMEOUT / BACKEND_ERROR
  ├─ authorization flow (default): settle ─► PAYMENT_SETTLEMENT_FAILED
  ├─ store receipt + events ───────────────► logged on failure, never thrown
  └─ ExecutionOutcome (delivered)

verify never moves money; only settle does. The replay reservation sits deliberately between them: a duplicate authorization is rejected before any funds move. The provider's requirement chooses the flow: x402 settles after a successful backend call by default, and MPP before it. Under authorization, a backend failure settles nothing.

Authorization is optional per resource. When required, its reservation sits between payment verification and settlement. It is consumed when the payment settles, marked when the outcome is unknown, and released when no money moved: a rejected settlement, a failed replay reservation, or a backend failure before settlement. See AP2.

Correlation

The protocol adapter assigns one requestId. The pipeline carries it through errors, events, payment attempts and the receipt, linking the audit records for one invocation.

Event sequence for a successful paid request:

authorization flow (default)
  resource.requested → payment.verified → backend.called → payment.settled
                     → resource.delivered

upfront flow
  resource.requested → payment.verified → payment.settled → backend.called
                     → resource.delivered

The challenge before it is a separate request with its own requestId: resource.requested → payment.required.

A resource requiring authorization adds authorization.verified (or authorization.rejected) between the request and the payment events. The event types are the same whatever the method, so reading the audit trail never requires knowing what AP2 is.

Adapter isolation

An adapter that fails to start is marked unhealthy without stopping other adapters. Readiness and doctor report the failure. Protocol and payment failures remain separate.

Receipts and audit

SQLite stores receipts, events and payment_attempts behind ReceiptStore. A UNIQUE constraint on payment_attempts.replay_key makes replay reservation atomic. The pipeline omits raw payment proofs, and SQLite row mapping redacts recognized secret fields before persistence.

Deterministic by construction

LLMs may call the gateway but are not runtime dependencies. Routing, validation, payment verification, receipts and protocol adaptation are deterministic. CI uses a deterministic buyer and local chain, without a model, public RPC or real funds.

Where to look in the code

Concern Path
canonical model, errors, pipeline src/core
config schema, loader, env substitution src/config
Fastify server, routes, adapter mounting src/gateway
MCP adapter src/protocols/mcp
A2A adapter (experimental) src/protocols/a2a
ACP checkout adapter (experimental) src/protocols/acp
shared bearer check, body reader, URL join src/protocols/http.ts
x402 provider + local/remote facilitator src/payments/x402
MPP provider, settling through an x402 provider src/payments/mpp
AP2 mandate verification src/authorization/ap2
SQLite receipts/events/attempts src/storage/receipts
OpenAPI import (config ingress only) src/openapi
CLI (init, import openapi, validate, doctor, demo, version) src/cli
demo merchant API / buyer / dashboard demo/*
MockUSDC + local chain scripts contracts/, scripts/chain/