afpay
Cryptocurrency micropayment tool designed for AI agents. Cashu, Lightning, Solana, EVM, Bitcoin on-chain — single binary, unified CLI, with multi-tier spend limits (per-wallet, per-network, global).
Pure Rust, zero C dependencies.
Architecture
All network wallets managed in one process via the Provider trait:
afpay <command>
├── cashu provider
├── ln provider
├── sol provider
├── evm provider
└── btc provider
For remote operation, two server modes are available:
# RPC mode — gRPC + AES-256-GCM PSK (process-to-process)
afpay CLI / pipe
│ gRPC (AES-256-GCM PSK)
└──→ afpay --mode rpc (VPS)
# REST mode — HTTP + Bearer token (any HTTP client)
curl / scripts / any language
│ HTTP POST /v1/afpay
└──→ afpay --mode rest (VPS / Docker)
Both server modes enforce spend limits independently.
See Architecture for advanced multi-server deployment patterns.
Supported Networks
| Network | Unit | Token Support | Feature |
|---|---|---|---|
| Cashu | sats | — | cashu |
| Lightning | sats | — | ln-phoenixd (default) / ln-lnbits / ln-nwc |
| Solana | lamports | USDC, USDT (SPL) | sol |
| EVM chain | gwei | USDC, USDT (ERC-20) | evm |
| Bitcoin | sats | — | btc-esplora / btc-core / btc-electrum |
All features enabled by default. Selective compilation:
Storage Backends
| Backend | Feature | Use Case |
|---|---|---|
| redb | redb (default) |
Embedded key-value, single-process, zero config |
| PostgreSQL | postgres (default) |
Multi-process, concurrent access, server deployments |
Both compiled by default. Select via config.toml:
= "redb" # default — embedded (no setup needed)
= "postgres" # PostgreSQL
= "postgres://user:pass@localhost/afpay"
PostgreSQL uses pg_advisory_xact_lock for spend limit concurrency safety. When storage_backend = "postgres", all data — wallet metadata (including seed secrets), CDK proofs, transaction history, and spend accounting — is stored in PostgreSQL.
Install
&&
Binary: afpay
Usage
Default mode is CLI — one command per invocation:
# Local (wallet on this machine)
# Remote (forward to rpc daemon)
Other modes: --mode interactive (REPL), --mode pipe (JSONL stdin/stdout), --mode rpc (gRPC daemon), --mode rest (HTTP REST API). See Manual for details.
Quick Start
Cashu
# Setup
# Deposit (Lightning → Cashu)
# → returns invoice + quote_id; pay the invoice, then claim:
# Send P2P cashu token
# → returns cashu token string
# Or filter by mint URL (picks first wallet with sufficient balance):
# Receive cashu token (auto-matches wallet by mint URL in token)
# Send to Lightning invoice
Lightning
# Setup (choose one backend)
# Receive — BOLT11 invoice (one-time, amount-specific)
# Receive — BOLT12 offer (persistent, reusable — phoenixd only)
# Send — pay BOLT11 invoice
# Send — pay BOLT12 offer (phoenixd only, --amount required)
Solana
# Setup
# Native SOL
# SPL token (USDC — built-in)
# Custom SPL token (register first, then use by symbol)
EVM Chain
# Setup
# Native ETH
# ERC-20 token (USDC — built-in)
# Optional: match afpay-encoded on-chain memo while waiting (amount is still required)
# Optional: increase per-poll history scan window when waiting (default 500, clamp 1..5000)
# Custom ERC-20 token (register first, then use by symbol)
Bitcoin On-Chain
# Setup — Esplora backend (default)
# Setup — Bitcoin Core RPC backend
# Setup — Electrum backend
# Receive address
# Optional: wait for incoming funds (exact amount match)
# Optional: increase per-poll history scan window when waiting (default 500, clamp 1..5000)
# Send (amount in satoshis)
# Restore from mnemonic
# Custom Esplora endpoint
receive --wait for EVM/BTC emits on-chain transaction IDs in history_status.transaction_id, so they can be re-queried with history status --transaction-id ....
BTC backend options are validated at wallet creation time:
--btc-backend core-rpcrequires--btc-core-url--btc-backend electrumrequires--btc-electrum-url- if provided,
--btc-esplora-urlmust be non-empty - the selected backend feature must be compiled in (
btc-esplora/btc-core/btc-electrum)
When restoring from --mnemonic-secret, afpay runs one full chain scan and persists scan progress before returning.
Cross-Network
Token Support
USDC and USDT are built-in for sol and evm. Other tokens (DAI, WBTC, BONK, WIF, JUP, etc.) can be registered per-wallet via wallet config token-add.
Balance queries automatically show all known tokens:
Built-in tokens: EVM — USDC/USDT on Base (8453), Arbitrum (42161), Ethereum (1). SOL — USDC/USDT on mainnet-beta, USDC on devnet. Raw contract/mint addresses can also be passed to --token.
Spend Limits
Multi-tier spend limits — all rules checked before every send, any breach rejects the transaction:
Design Constraints
| Constraint | Approach |
|---|---|
| No unwrap/expect/panic | #![deny(...)] global lint |
| Key security | All secret fields use _secret suffix, agent-first-data auto-redacts |
| Spend limits non-bypassable | RPC daemon enforces limits server-side; agent cannot modify daemon config |
| Single-point failure isolation | Each network can run in its own VPS/container independently |
| Consistent output | All modes use the same Output types |
| RPC security | gRPC + PSK AES-256-GCM payload encryption, zero certificate management |
| Dual storage backend | redb (embedded) or PostgreSQL, selected via config |
| Pure Rust zero C deps | CDK, Alloy, BDK, Solana component crates, redb, sqlx, aes-gcm — all pure Rust |
| REST API (Docker-friendly) | HTTP POST /v1/afpay with Bearer auth, no client needed |
| Depends on agent-first-data | Output formatting, _secret redaction, OutputFormat enum |
Containers
Container assets now live under container/:
container/docker/— canonical Docker/Podman image, compose stack, and supervisor configcontainer/apple-container/— ApplecontainerCLI workflow for macOS that reuses the same Dockerfile
Single-container deployment with supervisord (afpay + optional phoenixd + optional bitcoind). Works with both Docker and Podman — all commands are interchangeable (docker ↔ podman, docker compose ↔ podman compose):
# REST mode (default) — curl-accessible
# macOS + Apple Container CLI workflow
# RPC mode
AFPAY_MODE=rpc AFPAY_PORT=9400
AFPAY_MODE=rpc AFPAY_PORT=9400
# Optional local bitcoind (pruned mainnet)
ENABLE_BITCOIND=true INSTALL_BITCOIND=true
ENABLE_BITCOIND=true
# Backup / restore container data
CONTAINER_RUNTIME=docker
# Podman without compose — build and run directly
AFPAY_MODE selects rest or rpc. Secrets auto-generated on first run and persisted to volumes. bitcoind is disabled by default; when enabled it runs pruned mainnet with BTC_PRUNE_MB=550. See container/README.md for backup and restore scripts, container/apple-container/README.md for the Apple Container CLI flow, and Architecture for the full variable reference.
Testing
Docs
- Quick Start — 30-second walkthrough for each network
- Manual — Full command reference, all run modes, protocol details
- Architecture — Deployment patterns, RPC protocol, Provider design
- Testing — Unit and integration tests
License
MIT