Architecture
System Overview
How MPPPal works end-to-end — from an agent's HTTP request to on-chain settlement on Solana.
Stack
| Layer | Technology |
|---|---|
| Payment Protocol | Machine Payments Protocol (MPP) — HTTP 402-based, open standard |
| Blockchain | Solana Mainnet |
| Smart Contract | Anchor (Rust) — Policy Program + Settlement Program |
| Stablecoin | USDC (SPL Token, native Solana mint) |
| API Backend | Node.js / TypeScript, Fastify |
| Database | PostgreSQL — wallets, policies, audit log, session state |
| Queue | BullMQ + Redis — async settlement processing and webhook fan-out |
| Frontend | Next.js + TypeScript — operator dashboard |
| Auth | HMAC-signed API keys + operator OAuth2 |
| Monitoring | Datadog + custom Solana RPC health checks |
| SDK (TypeScript) | @mpppal/sdk |
| SDK (Python) | mpppal |
MPP payment flow (Charge intent)
A single agent request to a paid service:
text
Agent (SDK HTTP client)
│
▼ GET https://api.provider.xyz/data
MPP Service
│ HTTP 402 + WWW-Authenticate: Payment challenge
▼
MPPPal SDK (in-process)
│ 1. Parse payment challenge
│ 2. Check wallet balance and spending policy
│ 3. Sign payment credential
▼
Agent retries with Authorization: Payment header
│
MPP Service
│ HTTP 200 + Payment-Receipt header
▼
MPPPal API (async)
│ 4. Record receipt in PostgreSQL
│ 5. Enqueue settlement job
▼
Settlement Worker (BullMQ)
│ 6. Build Solana SPL transfer instruction
│ 7. Sign with PDA authority (Policy Program enforces limits)
│ 8. Submit to Solana RPC
│ 9. Poll for finality (~400ms)
▼
Solana Mainnet
│ 10. On-chain settlement confirmed
▼
Post-processing
│ 11. Write settled transfer to PostgreSQL audit log
│ 12. Update daily spend counter
│ 13. Fire webhooks (transfer.settled)
▼
Response / Webhook to Operator
Session settlement flow
For high-frequency agents using the Session intent, the on-chain footprint is radically smaller:
text
Agent opens session
│ → On-chain: lock funds in escrow PDA (1 Solana tx)
▼
Agent makes N requests to same provider
│ → Off-chain: micropayment receipts streamed to MPPPal
│ → No on-chain transactions during session
▼
Session closes (timeout or explicit close)
│ → On-chain: batch-settle net amount (1 Solana tx)
│ → On-chain: release unused locked USDC to wallet
▼
N micropayments → 2 Solana transactions total
On-chain program structure
MPPPal deploys two Solana programs, both written in Rust using the Anchor framework:
Policy Program
Stores and enforces spending policy for each agent wallet. Policy rules live in a PDA derived from the wallet's public key. The program validates every transfer instruction against this policy before allowing execution.
text
Policy Program
└── Wallet PDA (one per agent wallet)
├── USDC SPL token account
├── Spending policy state
│ ├── max_single_transfer
│ ├── max_daily_spend
│ ├── allowed_counterparties[]
│ ├── categories[]
│ ├── require_memo
│ ├── escalation_threshold
│ └── paused
└── Transaction nonce (replay protection)
Settlement Program
Handles the movement of USDC between wallets, batch settlement of sessions, and escrow management for session locks.
text
Settlement Program
├── settle_batch(receipts[]) — batch-settle signed micropayment receipts
├── open_session(cap) — lock funds in session escrow PDA
├── close_session(session_id) — settle net amount, release remainder
└── transfer(from, to, amount) — direct wallet-to-wallet transfer
Both programs are open-source. The on-chain programs are publicly verifiable. Anyone can read what they do and confirm the deployed bytecode matches the source. MPPPal's off-chain API layer is trusted for convenience; the on-chain programs are trusted by math.
Database schema (simplified)
accounts
| Column | Type | Notes |
|---|---|---|
account_id | varchar | Primary key. Format: acct_* |
operator_id | uuid | FK to operators table |
name | varchar | Display name |
handle | varchar | Human-readable handle e.g. @research-agent.mpppal |
solana_address | varchar(44) | Base58 PDA address |
usdc_token_account | varchar(44) | Base58 SPL token account |
custody_mode | enum | custodial | non_custodial |
status | enum | active | paused |
created_at | timestamptz |
transfers
| Column | Type | Notes |
|---|---|---|
transfer_id | varchar | Primary key. Format: txn_* |
account_id | varchar | FK to accounts |
to_address | varchar(44) | Recipient SPL address |
amount_usdc | numeric(20,6) | |
memo | varchar(200) | Nullable |
mpp_session_id | varchar | Nullable — set if settled as part of a session |
idempotency_key | varchar(128) | Unique per account |
tx_signature | varchar(88) | Nullable until settled |
status | enum | pending | settled | rejected | failed |
rejection_reason | varchar | Nullable |
settled_at | timestamptz | Nullable |
mpp_sessions
| Column | Type | Notes |
|---|---|---|
session_id | varchar | Primary key. Format: sess_* |
account_id | varchar | FK to accounts |
provider_endpoint | varchar | MPP service URL |
cap_usdc | numeric(20,6) | Pre-authorized spending cap |
running_total_usdc | numeric(20,6) | Off-chain running tally |
status | enum | open | settling | settled |
settlement_tx_signature | varchar(88) | Nullable until settled |
opened_at | timestamptz | |
settled_at | timestamptz | Nullable |