Agent Commerce
February 10, 2026 ยท View on GitHub
Setting up buyer and seller agents for autonomous Lightning payments.
Lightning Agent Tools enables a payment pattern where agents buy and sell
resources from each other over the Lightning Network without any pre-arranged
billing relationship. A buyer agent uses lnget to fetch paid content. A
seller agent uses aperture to gate access behind Lightning invoices. Both run
their own lnd nodes for payment infrastructure. The three components compose
into a self-contained commerce loop.
This document walks through setting up both sides and running them together.
The Commerce Loop
When a buyer agent fetches a resource from a seller, the following exchange happens automatically:
sequenceDiagram
participant BA as Buyer Agent
participant BL as Buyer lnd
participant LN as Lightning Network
participant SL as Seller lnd
participant AP as Aperture
participant BE as Backend Service
BA->>AP: GET /api/data (via lnget)
AP->>SL: Generate invoice (100 sats)
SL-->>AP: BOLT11 invoice
AP-->>BA: 402 + L402 challenge
BA->>BL: Pay invoice (100 sats)
BL->>LN: Route payment
LN->>SL: Deliver payment
SL-->>LN: Preimage
LN-->>BL: Preimage
BL-->>BA: Payment settled
BA->>AP: GET /api/data + L402 token
AP->>AP: Validate token
AP->>BE: Proxy request
BE-->>AP: Response data
AP-->>BA: 200 OK + data
From the buyer agent's perspective, this is a single command:
lnget --max-cost 100 -q https://seller-host:8081/api/data | jq .
From the seller's perspective, aperture handles everything: invoice generation, challenge issuance, token validation, and request proxying. The backend service just serves HTTP requests.
Buyer Agent Setup
A buyer agent needs two components: an lnd node for payments and lnget for
HTTP requests with automatic L402 handling.
Install
# Pulls Docker images by default; add --source to build from source.
skills/lnd/scripts/install.sh
skills/lnget/scripts/install.sh
Create and Start the Node
All commands below auto-detect Docker and launch containers. Pass --native to
any script to use a locally built binary instead.
# Create a wallet (standalone mode for testing, watch-only for production)
skills/lnd/scripts/create-wallet.sh --mode standalone
# Start lnd (launches a litd Docker container)
skills/lnd/scripts/start-lnd.sh
# Verify it's running
skills/lnd/scripts/lncli.sh getinfo
For production deployments, use watch-only mode with a remote signer. See Security.
Fund the Wallet
The node needs on-chain bitcoin to open payment channels.
# Generate a funding address
skills/lnd/scripts/lncli.sh newaddress p2tr
# Send BTC to this address from an exchange or another wallet
# Check balance (wait for confirmations)
skills/lnd/scripts/lncli.sh walletbalance
Open a Channel
Lightning payments travel through channels. The buyer needs at least one channel with outbound capacity to reach the seller (or a path to the seller through the network).
# Connect to a well-connected node
skills/lnd/scripts/lncli.sh connect <pubkey>@<host>:9735
# Open a channel (amount in satoshis)
skills/lnd/scripts/lncli.sh openchannel --node_key=<pubkey> --local_amt=1000000
# Wait for the funding transaction to confirm (typically 3-6 blocks)
skills/lnd/scripts/lncli.sh pendingchannels
skills/lnd/scripts/lncli.sh listchannels
Configure lnget
# Initialize config (auto-detects local lnd)
lnget config init
# Verify backend connection
lnget ln status
Fetch Paid Resources
# Fetch with a spending cap
lnget --max-cost 500 https://api.example.com/paid-data.json
# Preview cost without paying
lnget --no-pay --json https://api.example.com/paid-data.json | jq '.invoice_amount_sat'
# Pipe to other tools
lnget -q https://api.example.com/market-data.json | jq '.price'
Seller Agent Setup
A seller agent needs three components: an lnd node for invoice generation,
aperture as the L402 reverse proxy, and a backend HTTP service to serve the
actual content.
Install
skills/lnd/scripts/install.sh
skills/aperture/scripts/install.sh
Create and Start the Node
skills/lnd/scripts/create-wallet.sh --mode standalone
skills/lnd/scripts/start-lnd.sh # Docker container by default
skills/lnd/scripts/lncli.sh getinfo
The seller's lnd node generates invoices for aperture. It needs inbound channel capacity to receive payments. Other nodes must have channels open to the seller, not just from the seller.
Start a Backend Service
Aperture proxies authenticated requests to a backend HTTP service. For testing, a simple file server works:
mkdir -p /tmp/api-data
echo '{"market_data": {"btc_usd": 104250, "timestamp": "2025-02-09T12:00:00Z"}}' \
> /tmp/api-data/data.json
cd /tmp/api-data && python3 -m http.server 8080 &
In production, the backend is whatever service you want to monetize: a REST API, a data feed, an inference endpoint.
Configure and Start Aperture
# Generate aperture config connected to local lnd
skills/aperture/scripts/setup.sh --insecure --port 8081
# Start the L402 proxy
skills/aperture/scripts/start.sh
The --insecure flag disables TLS (suitable for development). For production,
configure TLS with Let's Encrypt (--autocert) or your own certificates.
For regtest testing, a Docker Compose file at
skills/aperture/templates/docker-compose-aperture.yml runs aperture alongside
a busybox backend on the same Docker network as the litd regtest stack.
Aperture's config (~/.aperture/aperture.yaml) defines which paths are
protected and how much they cost:
services:
- name: "market-data"
hostregexp: ".*"
pathregexp: "^/api/.*$"
address: "127.0.0.1:8080"
protocol: http
price: 100 # satoshis per request
authwhitelistpaths:
- "^/health$" # free health check endpoint
Each request to a path matching ^/api/.*$ costs 100 satoshis. Requests to
/health pass through without payment. The address field points to the
backend service.
Verify
# Should return 402 with L402 challenge
lnget -k --no-pay https://localhost:8081/api/data.json
Running Both Sides
With both agents running, the buyer fetches data from the seller:
# Buyer fetches paid data
lnget --max-cost 100 -q https://seller-host:8081/api/data.json | jq .
Verify the payment went through:
# On the buyer
lnget tokens list # shows cached token
skills/lnd/scripts/lncli.sh channelbalance # outbound decreased
# On the seller
skills/lnd/scripts/lncli.sh listinvoices # shows settled invoice
skills/lnd/scripts/lncli.sh channelbalance # inbound converted to local
Subsequent requests from the buyer to the same domain reuse the cached token without additional payment (until the token expires).
Deployment Diagram
A production deployment separates the signing infrastructure from the agent-facing components:
graph TB
subgraph Buyer["Buyer Agent"]
B_Agent["Agent Process"]
B_lnget["lnget"]
B_lnd["lnd (watch-only)"]
B_Signer["lnd signer<br/>(separate machine)"]
B_Agent --> B_lnget
B_lnget --> B_lnd
B_lnd <-->|"gRPC :10012"| B_Signer
end
subgraph Seller["Seller Agent"]
S_Agent["Agent Process"]
S_Aperture["aperture :8081"]
S_Backend["Backend :8080"]
S_lnd["lnd (watch-only)"]
S_Signer["lnd signer<br/>(separate machine)"]
S_Agent --> S_Aperture
S_Aperture --> S_Backend
S_Aperture --> S_lnd
S_lnd <-->|"gRPC :10012"| S_Signer
end
LN["Lightning Network"]
B_lnd <-->|":9735"| LN
S_lnd <-->|":9735"| LN
Cost Management
Agents operating autonomously need spending guardrails at multiple levels:
Per-request limits. Set --max-cost on every lnget call. If the seller's
price exceeds the limit, lnget refuses to pay and exits with code 2:
lnget --max-cost 500 https://api.example.com/data
Cost preview. Before committing to a purchase, preview what the server is charging:
lnget --no-pay --json https://api.example.com/data | jq '.invoice_amount_sat'
Spending tracking. Monitor cumulative spending through the token list:
lnget tokens list --json | jq '[.[] | .amount_paid_sat] | add'
Wallet balance monitoring. Check channel balance to ensure the agent has sufficient capacity:
skills/lnd/scripts/lncli.sh channelbalance
skills/lnd/scripts/lncli.sh walletbalance
Node-level controls. Use a pay-only macaroon (via macaroon-bakery) for
the buyer agent. This restricts lnget to payment and invoice-decoding operations
only. The agent cannot open channels, change configuration, or access any
other node functionality:
skills/macaroon-bakery/scripts/bake.sh --role pay-only
For the seller agent, use an invoice-only macaroon for aperture. It only needs
to create and look up invoices.
Production Considerations
Remote signer. Both buyer and seller nodes should run in watch-only mode with a remote signer in production. See Security.
Inbound liquidity. The seller's lnd node needs inbound channel capacity to receive payments. This means other nodes must open channels to the seller. Options include requesting inbound channels from LSPs (Lightning Service Providers), or using circular rebalancing to shift local capacity to the remote side.
Channel sizing. Size channels based on expected payment volume. A 1,000,000 sat channel can handle many 100-sat L402 payments before rebalancing is needed. Monitor channel balances and open new channels proactively.
TLS for aperture. Use --autocert with a domain name for automatic Let's
Encrypt TLS, or provide your own certificate. Never run --insecure on
public-facing endpoints.
Stopping everything.
skills/aperture/scripts/stop.sh
skills/lnd/scripts/stop-lnd.sh
skills/lnd/scripts/stop-lnd.sh --clean # also remove Docker volumes