Streaming micropayments on Tempo for Hermes Agent.
Open a session, pay per token, close with refund.
hermes skills install Zhekinmaksim/tempo-meter
Every existing Hermes payment skill uses one-shot charges: one API call = one on-chain transaction. For streaming LLM inference billed at $0.000025/token, that means either paying a large lump sum upfront or making 10,000 blockchain transactions for a single response.
Neither is acceptable.
tempo-meter uses MPP sessions — a payment channel that deposits once, signs thousands of off-chain vouchers in microseconds, and closes with automatic refund of unused funds. Two on-chain transactions total, regardless of how many tokens stream through.
TX 1: deposit $1.00 → escrow
voucher × 200 (off-chain, ~50μs each, no RPC calls)
TX 2: close → server gets $0.25, you get $0.75 back
Agent requests a 10,000-token essay from a metered LLM API:
$ python scripts/tempo_meter.py pay \
--url https://api.example.com/inference \
--max-deposit 1.00 \
--close-after
HTTP 200
{"text": "...10,000 tokens..."}
Channel closed: {
"settled": "0.250000",
"refunded": "0.750000",
"vouchers": 200,
"tx_hash": "0x..."
}
One deposit. One close. 200 vouchers signed in process memory, zero chain ops during inference.
Requirements: Python 3.10+, Hermes Agent
hermes skills install Zhekinmaksim/tempo-meter
# Initialize (testnet to start)
python scripts/tempo_meter.py init --network testnet
# Fund testnet wallet
cast rpc tempo_fundAddress $(python scripts/tempo_meter.py status --address-only) \
--rpc-url https://rpc.moderato.tempo.xyz
# Check balance
python scripts/tempo_meter.py balance
# Pay a metered endpoint
python scripts/tempo_meter.py pay --url https://your-mpp-service.example.com/apiPython dependencies (auto-installed):
pympp[tempo]>=0.6.0
web3>=7.0.0
eth-account>=0.13.0
pyyaml>=6.0
httpx>=0.28.0
aiohttp>=3.9.0
~/.tempo-meter/config.yaml:
network: mainnet # mainnet | testnet
rpc_url: ~ # auto-resolved from network
wallet_backend: file # file | veil
wallet_path: ~/.tempo-meter/wallet.key
budget:
daily_usdc_limit: 5.00
per_channel_max_usdc: 1.00
per_recipient_daily_usdc: 2.00
session:
default_deposit_usdc: 0.50
auto_close_after_idle_hours: 24
journal_path: ~/.tempo-meter/journal.jsonlThree independent spend limits enforce before any voucher is signed or channel opened. All limits are checked atomically — no partial spend on refusal.
tempo_meter init [--network mainnet|testnet] [--import-key 0x...]
tempo_meter status [--address-only]
tempo_meter balance
tempo_meter channels list
tempo_meter channels open <recipient> <deposit-usdc>
tempo_meter channels close <channel-id>
tempo_meter pay --url <url> [--max-deposit N] [--close-after]
tempo_meter config
- Agent calls
pay --url ... - Server responds
402 Payment RequiredwithWWW-Authenticate: Payment intent="session" - tempo-meter opens a
TempoStreamChannelescrow (TX 1, ~500ms) - Signs a zero-cumulative voucher, retries the request with
Authorization: Payment <voucher> - Server verifies via
ecrecover— no chain call, ~50μs - For each streaming chunk: increment cumulative, sign new voucher, attach to next request
- On close: submit final voucher to escrow (TX 2) → server settles, agent gets refund
Voucher(bytes32 channelId, uint256 cumulativeAmount)Domain: TempoStreamChannel, version 1, chainId 4217/42431, verifyingContract = escrow address.
Signatures are deterministic (RFC 6979). The server runs ecrecover on each voucher — no RPC, no mempool, no latency.
Three limits checked before every open() and next_voucher():
| Limit | Default | Scope |
|---|---|---|
daily_usdc_limit |
$5.00 | All channels, resets at UTC midnight |
per_channel_max_usdc |
$1.00 | Per channel (caps deposit size) |
per_recipient_daily_usdc |
$2.00 | Per destination address per day |
Budget state is reconstructed from journal.jsonl on every process start — limits survive agent restarts.
Keep your private key encrypted at rest, decrypted only in-process:
hermes skills install Zhekinmaksim/hermes-veil
# Store key in vault:
python ~/.hermes/skills/hermes-veil/scripts/veil.py store --ref tempo-meter-key
# Enable in config:
# wallet_backend: veilWhat tempo-meter defends against:
- Server overspending the agent — bounded by
per_channel_max_usdc - Runaway daily spend — bounded by
daily_usdc_limit - Malicious recipient draining via repeated channels — bounded by
per_recipient_daily_usdc - Stale channels left open after agent crash —
auto_close_after_idle_hours
What it does NOT defend against:
- Server that takes payment but delivers no content (loss bounded by deposit)
- Compromised Hermes process
- User leaving too much USDC in the tempo-meter wallet
Full threat model: references/threat-model.md
hermes-payguard by nativ3ai is excellent for one-shot payments on Base + Circle. Use it for single API calls.
tempo-meter targets a different scenario:
| hermes-payguard | tempo-meter | |
|---|---|---|
| Chain | Base | Tempo |
| Protocol | x402 charge | MPP session |
| Best for | One call = one payment | Streaming / metered billing |
| On-chain per request | 1 TX | 0 TX |
| Unused deposit | No refund | Automatic refund |
The two skills complement each other.
| Mainnet | Testnet (Moderato) | |
|---|---|---|
| Chain ID | 4217 | 42431 |
| RPC | https://rpc.tempo.xyz |
https://rpc.moderato.tempo.xyz |
| Explorer | https://explore.mainnet.tempo.xyz |
https://explore.testnet.tempo.xyz |
| Faucet | — | cast rpc tempo_fundAddress <addr> --rpc-url ... |
| TempoStreamChannel | 0x33b901018174DDabE4841042ab76ba85D4e24f25 |
0xe1c4d3dce17BC111181DdF716F75Bae49e61a336 |
references/sessions-explained.md— why sessions beat one-shotreferences/tempo-setup.md— wallet and RPC setupreferences/mpp-flow.md— full HTTP 402 → session lifecyclereferences/budget-model.md— spending limits designreferences/threat-model.md— security boundariesreferences/vs-payguard.md— detailed comparison
MIT — see LICENSE
Author: Zhekinmaksim
Companion: hermes-veil — encrypted key storage