# Architecture

Cortex is a Base-native protocol for agentic commerce. The trust model is straightforward: rich data lives offchain at canonical-hash URIs; commitments and identity live onchain. Agents and merchants verify by re-fetching the document, recomputing `keccak256(canonical-utf8)`, and matching against the onchain hash.

## What ships today

Three onchain primitives carry the buyer's journey end-to-end:

| Contract | Role today |
|----------|------------|
| `CommerceRegistry` | Merchants, services, facilitators, quote commitments, receipts, fulfillment, trust signals, disputes |
| `PolicyModule` | Per-account signed payment policies keyed by `(merchant payout or any merchant, token, facilitator)`. Spend limits, target/function allowlists, guardian + freeze |
| `SettlementAdapter` | Atomic split settlement across merchant, supplier, tax, tip, shipping, and handling lines |

Plus five **registered primitives staged for later phases** — deployed onchain so events index, but not yet load-bearing in any user-facing flow:

| Contract | Status today |
|----------|--------------|
| `AgentRegistry` | Indexed. Registration works from `/onboarding`. Agents identified but reputation/capability matching not yet enforced |
| `PolicyAccount` | Working ERC-4337 smart account with session keys. Optional; users can use any wallet (including Coinbase Smart Wallet) instead |
| `IntentBook` | Intent submission + solver fills work in the demo script (`ops/demo/run.ts`). Not surfaced in the buyer UX yet |
| `SolverRegistry` | Solver registration works. Capability matching during selection is not yet enforced (first-bid-wins today) |
| `AttestorRegistry` + `AttestationRegistry` | Schemas pre-loaded, attestation creation works. No flow yet requires attestations — they're decorative until used in policy or settlement |

This split matters: a reviewer reading the docs should know which surfaces are tested-on-mainnet-ready vs. which are spec-with-deployed-scaffolding-for-later.

## System diagram

```text
Agent owner (any wallet, including Coinbase Smart Wallet)
  | sets policy via PolicyModule.setPaymentPolicy
  v
PolicyModule  (3 today)
  |
  v
Merchant operator
  | registers merchant + services in CommerceRegistry
  | commits quotes with terms hash binding settlement plan
  v
CommerceRegistry  (3 today) ── settles via SettlementAdapter  (3 today)
  | events
  v
Indexer → Postgres → REST API + MCP server + Dashboard

────────────────────────────────────────────────────────────
Staged for later phases (deployed, not yet load-bearing):
  AgentRegistry · PolicyAccount · IntentBook
  SolverRegistry · AttestorRegistry · AttestationRegistry
```

## Trust model

1. **Onchain commits hash, offchain stores body.** Every rich document (merchant profile, service metadata, catalog, quote request, quote response, settlement plan, fulfillment evidence, license document) is stored as canonical JSON and addressed by `keccak256(canonical-utf8)`. Onchain records store the URI/hash when the document is part of a registry commitment. Agents re-fetch and re-hash before acting.
2. **Quote hashes are canonical.** `CommerceRegistry.computeQuoteHash(...)` binds chain id, registry address, all 12 quote fields, and protocol fee terms. `commitQuote` rejects any non-canonical hash. This is the protection against cross-chain, cross-registry, facilitator-substitution, settlement-plan-substitution, and x402-payload-substitution attacks.
3. **Policies guard payments.** `PolicyModule.checkSignedPayment(merchant, token, facilitator, amount)` is the runtime check facilitators call before submitting a payment authorization. The policy can be keyed to the merchant payout address or to the zero-address any-merchant scope; exact merchant policies override the broader scope.
4. **Shipping/PII never goes onchain.** Physical-goods fulfillment uses an ECDH-AES envelope encrypted to the merchant's published fulfillment key. Only `(encryptedPayloadURI, encryptedPayloadHash, encryptionScheme, merchantKeyId)` end up in any public document.
5. **Licenses are canonical use-right records.** `cortex.license.v1` documents make data, API, compute, content, and report rights machine-readable. The license hash is the license id and can be bound into quote terms, receipts, fulfillment evidence, or attestations. The live V1 is document-based; NFT/SBT minting is a later registry layer, not the current source of truth.

## Wallet options

Cortex doesn't require its own smart account. Users connect any wallet through the global `/onboarding` connect flow (powered by wagmi + RainbowKit) and all subsequent signatures route through the connected wallet's signer.

| Wallet | Status | Notes |
|--------|--------|-------|
| Coinbase Smart Wallet | First-class | Use the Coinbase Wallet connector in the header. All Cortex operations (`registerMerchant`, `setPaymentPolicy`, etc.) sign from the smart wallet directly. No PolicyAccount required. |
| MetaMask / Rabby / any EIP-1193 injected wallet | First-class | Same flow as Coinbase Smart Wallet via the `injected()` connector. |
| Cortex `PolicyAccount` | Optional | An ERC-4337 smart account that bundles session-key support and policy enforcement at the wallet layer. Useful for production agent deployments that want managed session keys, but not required for any onboarding step. |

## Offchain services

| Service | Role |
|---------|------|
| Indexer | Polls Base Sepolia for contract events, writes to Postgres |
| Hosted document API (`/catalogs`, `/quote-requests`, `/quote-responses`, `/fulfillment-payloads`, `/fulfillment-evidence`, `/licenses`) | Stores canonical JSON by hash, returns the public URI |
| REST API (`/merchants`, `/services`, `/agents`, `/accounts/:addr/policies`, `/receipts`, `/disputes`, `/analytics/commerce`, `/licenses/check`, `/x402/normalize`, etc.) | Reads the indexer + serves hosted documents |
| MCP server | Tools for agent runtimes: `lookup_agent`, `list_open_intents`, `get_policy`, `explain_tx`, `preflight_transaction`, `list_licenses`, `check_license`, etc. |
| SDK | TypeScript helpers wrapping the same operations (`ops/sdk-examples/commerce-flow.ts`, `payment-rails.ts` are runnable end-to-end) |
| Dashboard / Onboarding / Purchase simulator | The Next.js web app at `cortex.projectaegis.ai` |

## Payment rails

| Rail | What runs |
|------|-----------|
| Direct transfer | Native ETH or ERC-20 `transfer` to the merchant payout address. Simplest case. |
| Swap | DEX/router call governed by target+selector allowlists and spend limits. |
| Facilitator-mediated | A facilitator submits the payment with a signed authorization; `recordSignedPayment` provides replay protection. |
| x402 | Web-native payment acceptance; the quote binds an `x402PayloadHash` that agents normalize through `/x402/normalize` before signing. |

All four rails execute in `ops/sdk-examples/payment-rails.ts` against the live testnet.

## Indexed tables

| Table | Source | Purpose |
|-------|--------|---------|
| `merchants` | CommerceRegistry events | Merchant discovery + payout context |
| `services` | CommerceRegistry events | Service discovery + capability lookup |
| `facilitators` | CommerceRegistry events | Payment facilitator discovery |
| `quotes` | Quote events | Canonical payment terms + fee instrumentation |
| `commerce_receipts` | Receipt + fulfillment events | Settled commerce + fulfillment hashes |
| `disputes` | Dispute events | Refund / reputation signals |
| `trust_signals` | Trust signal events | Verification, risk, compliance, fulfillment signals |
| `license_documents` | Hosted canonical license documents | License inventory + pre-use allow/deny checks |
| `agents` | Agent registry events | Agent identity + owner lookups |
| `policies` | Policy events | Account spend, allowlist, signed payment policies |
| `solvers`, `attestors`, `attestation_schemas`, `intents`, `solver_bids`, `fills`, `tx_receipts` | Various | Indexed for future phases; not yet driving any user-facing flow |

## Deployment shape

Protocol-first on Base Sepolia today. Mainnet is gated on Sepolia traction (see [protocol-roadmap.md](./protocol-roadmap.md)).

If agentic commerce activity later justifies it, the same primitives can graduate to chain-native modules (predeploys, agent-aware fee markets, sequencer-level intent batching). That's a phase-3 decision driven by usage data, not a default plan.

## License minting status

Licenses are not NFTs in the current deployment. The system deliberately starts with a canonical document model:

- `POST /licenses` canonicalizes and stores a `cortex.license.v1` document.
- The resulting hash is the stable license id.
- `GET /licenses` exposes inventory filters for agents, issuers, merchants, services, assets, and active status.
- `POST /licenses/check` and MCP `check_license` evaluate holder, action, expiry, quote/receipt binding, and common usage constraints.

If onchain minting becomes useful, the clean next step is a separate `LicenseRegistry` that anchors or emits the same `license_hash`. A non-transferable ERC-721/SBT or ERC-1155 wrapper can improve wallet UX, but it should reference the canonical license hash rather than replacing the document/check model.

## Technology

- **Contracts**: Solidity 0.8.24, Foundry, OpenZeppelin
- **Offchain services**: TypeScript, viem, node-postgres
- **API**: Express 4
- **Database**: PostgreSQL 16
- **Web**: Next.js 15 (App Router), wagmi v2 + RainbowKit v2 for wallet connection
- **Local dev**: Anvil, Docker Compose
