MPP + Tempo Integration Proposal — Ray Data Co
Founder’s questions (2026-05-01 15:50 ET)
- Do we need to setup a Tempo stable coin wallet?
- What is the Machine Payment Protocol (MPP)?
- How do we allow purchases with this pattern?
- Link or the Tempo MPP process shown?
What MPP is
Machine Payments Protocol (MPP) is an open IETF-track HTTP authentication scheme — co-authored by Stripe and Tempo, with contributions from Visa — that standardizes machine-to-machine payments on top of HTTP 402. The flow is three steps: client requests a resource; server returns 402 Payment Required with a WWW-Authenticate: Payment challenge; client pays (signs a Tempo tx, uses an SPT, swipes a card via Lightning, etc.) and re-requests with Authorization: Payment <credential>; server returns the resource plus a Payment-Receipt header. (spec, Stripe docs)
Critical property: payment-method agnostic. A single MPP endpoint can accept stablecoins on Tempo, USDC bridged from other chains, cards via Stripe SPTs, Lightning BTC, Solana SPL tokens, Monad ERC-20, or Stellar SEP-41 — the buyer chooses. The server doesn’t have to support each rail individually; it accepts a credential and verifies it.
Open-source spec lives at tempoxyz/mpp-specs. Official SDKs in TypeScript, Python, Rust, Go, Ruby (tempoxyz/mpp-rs, solana-foundation/mpp-sdk), plus framework middleware for Hono/Express/Next.js/Elysia. Cloudflare Workers has first-party MPP middleware (CF docs) — directly relevant to our stack.
What’s GA today (2026-05-01): The protocol spec is live. Tempo mainnet launched 2026-03-18. SDKs are published. Stripe’s MPP via PaymentIntents is in early access / waitlist (Stripe agentic commerce suite blog). The buyer-side link-cli already supports MPP via link-cli mpp pay <url> — we have it installed already as of 2026-04-29.
What Tempo is
Tempo is a purpose-built Layer 1 blockchain for stablecoin payments (tempo.xyz, docs.tempo.xyz). Built on Reth SDK + Simplex Consensus. ~0.6s sub-second finality, ~100k TPS target, gas paid in USD stablecoins (no native token required). Co-incubated by Stripe and Paradigm; raised $500M at $5B valuation; design input from OpenAI, Visa, Mastercard, Shopify, UBS, Deutsche Bank, Nubank, Revolut. Visa runs an anchor validator.
Wallet model: Domain-bound passkey accounts (WebAuthn) and embeddable universal-wallet dialogs. Self-custody capable; no KYC mentioned in docs for wallet creation. USD on-ramp is via bridges (LayerZero/Stargate, Relay) — Tempo is not a USD-bank-ACH-direct chain today; you bridge USDC in. Native USDC on mainnet at 0x20c000000000000000000000b9537d11c60e8b50. Programmatic wallet creation supported via TS/Go/Rust/Foundry SDKs. Explorer at explore.tempo.xyz.
For our purposes, the load-bearing fact: the mppx CLI provisions a Tempo testnet wallet with one command (npx mppx account create then npx mppx account fund). Production wallet provisioning follows the same pattern.
MPP vs existing Link CLI wallet
| Dimension | Link CLI (existing, set up 2026-04-29) | MPP-via-Tempo (new) |
|---|---|---|
| Settlement asset | Card (Amex 1008) or ACH (bank 3578) via Stripe SPT | USDC on Tempo (or any MPP-supported rail) |
| Approval gate | Per-charge push to founder’s Link app, ~5min timeout | Configurable — wallet-side spending caps + per-tx auth |
| Latency | ~90s human-in-loop today | ~0.6s machine-only (no human gate unless we add one) |
| Per-tx fee | Stripe interchange ~2.9% + $0.30 (card path) | Tempo gas (~stable, USD-denominated, low; not yet quantified publicly) + Stripe MPP fees (not disclosed) |
| Merchant requirement | Merchant accepts the SPT card credential | Merchant exposes MPP-compliant 402 endpoint |
| Reversibility | Card chargeback works; SPT bounded | On-chain stablecoin = irreversible, dispute via merchant only |
| Where it shines | Subscription top-ups, SaaS trials, anything humans want to gate | Mid-workflow micropurchases (Alpha Vantage pattern), high-frequency tiny calls |
| Today’s installed base | ✅ Wired and tested | ❌ Not provisioned yet, but link-cli binary already supports it |
They are complementary, not competing. Link CLI is the human-gated card path. MPP-via-Tempo is the machine-only stablecoin path for high-frequency, low-stakes spend. Both can route through the same link-cli binary we already have. No migration is needed. We add MPP as a second mode alongside the existing per-charge approval flow.
Recommended buyer-side integration
Recommendation: Provision a Tempo wallet now (testnet first, mainnet next week), keep founder-approval gating for all live spend ≥ $5 until we calibrate. Total elapsed setup time ~30 min.
Why now: (a) we already have the buyer SDK installed (link-cli), so the marginal cost is low; (b) the Alpha Vantage/Collison demo is the canonical use case for MAC’s “agent-as-customer” thesis — eating our own dog food on the buyer side gives us first-hand pattern-library evidence; (c) L5-trajectory unhobbling — this is exactly the toolset the founder green-lit; (d) deferring means we keep showing up to client conversations explaining MPP in theory rather than from demo experience.
Why not “wait”: Stripe’s full MPP via PaymentIntents is in early access, but the buyer side is GA via the SDK + Tempo mainnet. We’re not waiting on a counterparty.
Wallet setup steps (lean path)
# 1. Testnet wallet — verifies the loop end-to-end with no real money
NPM_CONFIG_MIN_RELEASE_AGE=0 npm install -g @tempoxyz/mppx
npx mppx account create --network tempo-testnet
# Outputs: address + seed-phrase. Save BOTH to 1Password "Ray Agent" vault, entries:
# - "Tempo Wallet — Testnet Address" (field: address)
# - "Tempo Wallet — Testnet Seed" (field: credential, secret)
npx mppx account fund # uses testnet faucet, free
# 2. Verify against any MPP-paywalled testnet endpoint
npx mppx http://test-endpoint.mpp.dev/sample
# 3. Mainnet wallet — only after testnet round-trip passes
npx mppx account create --network tempo-mainnet
# Save to 1Password as "Tempo Wallet — Mainnet Address" + "...Seed"
# 4. Bridge initial USDC funding
# Use Relay or LayerZero from existing Coinbase/wallet position to mainnet address
# Recommended initial fund: $50 (covers ~hundreds of micropurchases at expected unit cost)
MCP wrapper update: Add ~/.claude/scripts/mppx-wrapper.sh that fetches the Tempo seed from 1Password at MCP-launch time and exports MPPX_TESTNET_KEY / MPPX_MAINNET_KEY env vars. Same pattern as ~/Projects/stripe-mcp-wrapper/start.sh. Never .env on disk.
Spending controls + founder-approval gates
Tier system, mirroring the email-non-autonomy precedent (founder gates irreversible external actions until trust pattern established):
| Tier | Spend per tx | Daily cap | Approval |
|---|---|---|---|
| Auto (initial) | ≤ $0.50 | $5/day | Ray executes, logs to vault |
| Founder-confirm | $0.50 – $25 | $50/day | iMessage heads-up + 60s implicit approve unless founder denies |
| Founder-explicit | $25 – $250 | $200/day | iMessage explicit approval required, same flow as Link CLI today |
| Hard stop | > $250 | n/a | Refused; founder must escalate or use Link card path |
Caps enforced inside ~/.claude/scripts/mppx-wrapper.sh BEFORE the SDK call (kill switch, not after-the-fact audit). Daily counter persisted at ~/.claude/state/mpp-daily-spend.json, resets at midnight ET.
Audit trail: every MPP transaction appended to ~/rdco-vault/04-tooling/mpp-transaction-log.md with merchant, amount, hash, Tempo explorer link, founder-approval status, business reason. Founder visibility = same surface as ray-operating-budget tag in the vault.
Credential storage + security posture
- 1Password “Ray Agent” vault — same structure as Stripe RAK entry. Slots:
Tempo Wallet — Mainnet Address(public, low-sensitivity),Tempo Wallet — Mainnet Seed(secret, high-sensitivity), same for testnet. - No IP-allowlist on Tempo itself — it’s a public chain, the wallet is identified by signature. (This is fine; on-chain auth is signature-based, not IP-based.)
- Stripe MPP-via-PaymentIntents path (when we add card-fallback) — inherits the existing IP-restricted RAK from
2026-04-30-ip-restricted-policies-tracker.md. No new policy needed. - Seed-phrase exposure — only readable via 1Password service account at MCP-launch time. Never written to logs. The
mppx-wrapper.shscript shouldunset MPPX_*_KEYin a trap on EXIT. - Cloudflare Workers MPP middleware — relevant for seller-side later (Phase 3). Not needed for Phase 1.
Cost estimate
| Component | Cost |
|---|---|
| Setup (one-time) | ~30 min Ray time, $0 cash |
| Initial wallet funding | $50 USDC bridged to Tempo (suggested; can be $20 to start) |
| Bridge fee (one-time) | ~$1-3 via Relay/Stargate (LayerZero) |
| Per-tx Tempo gas | <$0.01 (stable USD-denominated, sub-cent expected based on Tempo’s “predictably low fees” guidance) |
| Per-tx Stripe MPP fee | Not disclosed — Stripe has not published MPP pricing; treat as risk item |
| Monthly minimum | $0 (no subscription) |
Worst-case unit economics: if Stripe takes a per-tx fee of $0.05 (speculative), 100 micropurchases/mo = $5/mo + $1 in gas. Cheap.
Implementation roadmap
Phase 1 (this week, ~30 min): Testnet wallet, end-to-end loop verification against any test MPP endpoint. No real money at risk. Document the loop in this file.
Phase 2 (next week, ~30 min + $50): Mainnet wallet, $50 USDC bridged in, MCP wrapper script wired, spending caps enforced, audit log live. First real micropurchase: a $0.10 test buy from any MPP-paywalled service to verify mainnet flow. Founder gets iMessage on every tx for the first 5; auto-approve under $0.50 kicks in after pattern is established.
Phase 3 (when relevant, not yet scheduled): Seller-side MPP for MAC or Squarely. MAC is the natural fit — micro-paywalled audit reports / model audits sold to other agents. Cloudflare Workers MPP middleware makes this trivial since both surfaces are already on Workers. Defer until Phase 2 has produced 30 days of clean buyer-side data.
What this enables (L5-trajectory)
This is the buyer-side completion of the Vending-Bench “agent extends into atoms-world” pattern. With Link CLI + Tempo wallet + spending caps + audit trail, Ray can:
- Buy datasets mid-research-brief (the Alpha Vantage / Collison pattern)
- Top up xAI / ElevenLabs / OpenAI on per-call paywalled APIs without a subscription
- Pay micropaywalled news/research articles without juggling per-publisher credentials
- Eventually: run comparison-shopping logic across MPP-priced services (later, gated by trust)
It also gives RDCO first-hand demo material for explaining MPP to enterprise prospects — the same agent-purchase-governance question phData et al. will face in 2026-2027.
Joins existing infra cluster:
- Link CLI (2026-04-29-link-cli-agent-wallet-setup) — card-rail, human-gated
- Stripe RAK (2026-04-30-ip-restricted-policies-tracker) — server-side Stripe ops
- Stripe Projects (cluster note pending) — agent identity / account provisioning
- THIS PROPOSAL — Tempo + MPP buyer-side stablecoin path, machine-gated
What’s deferred
- Seller-side MPP for MAC/Squarely — Phase 3, after 30 days of buyer-side data. Cloudflare middleware exists, so effort is low when we’re ready.
- Auto-approve > $0.50 — wait until 50+ transactions land cleanly under existing caps; revisit thresholds on data, not gut.
- Cross-merchant comparison-shopping logic — premature. First make individual purchases work, then add price-discovery as a separate skill.
- Cards-via-MPP (SPT path) — if we wanted card-fallback for non-Tempo merchants, that uses the existing Link CLI path. Already covered.
- Stripe MPP via PaymentIntents acceptance — early access waitlist. Sign up but don’t block on it; the buyer side doesn’t need it.
Alternatives considered + rejected
| Alternative | Why rejected |
|---|---|
| Skip MPP entirely, use only Link CLI | Misses machine-only micropurchase use case (sub-dollar paywalls). Also forfeits L5 unhobbling and demo material. |
| Self-build MPP buyer client from spec | The official mppx CLI exists. Building from scratch is engineering work for no marginal value. |
| Custodial wallet via a third party (e.g., Crossmint) | Adds counterparty risk and an extra fee layer. Tempo wallet is self-custody by default; we already manage credentials in 1Password. |
| Wait for Stripe MPP-via-PaymentIntents GA | That’s the acceptance side; buyer side is shipped today. Waiting forfeits learning. |
| Use a non-Tempo settlement (Solana, Lightning) for cost reasons | Tempo is the canonical Stripe-aligned chain; running on it gets us alignment with Stripe’s broader Agentic Commerce Suite. Other rails are layerable later via the same mppx CLI without changing the wallet model. |
Open questions for founder
- Initial mainnet funding amount. $50 USDC is the recommended Phase 2 starting balance. Higher = more runway between top-ups; lower = tighter blast radius if a bug spends faster than expected. Founder pick.
- Auto-approve ceiling for Tier-1. Proposal: $0.50/tx, $5/day. Confirm or adjust.
- Card-fallback rule. When a merchant isn’t on MPP/Tempo (most still aren’t in May 2026), Ray defaults back to Link CLI card path. Founder OK with that auto-fallback or want explicit confirmation per merchant?
Related
- 2026-04-29-link-cli-agent-wallet-setup — card-rail buyer wallet (complementary, not replaced)
- 2026-04-30-ip-restricted-policies-tracker — security posture pattern; Tempo doesn’t add new entries (signature-based, not IP-based)
- ../06-reference/2026-05-01-alpha-vantage-collison-agent-as-customer-evidence — the demand-side proof that triggered this proposal
- ../06-reference/2026-04-29-cloudflare-stripe-projects-agent-account-provisioning — the protocol-layer cluster (note may be filed under different name; cross-referenced from alpha-vantage piece)
Phase 1 Execution Log — 2026-05-01 16:05 ET
Outcome: COMPLETE. Full MPP round-trip verified end-to-end against mpp.dev test endpoint on Tempo Moderato testnet, $0 real money spent, ~10 min of elapsed work.
Tooling install (no global install needed)
npx mppx --version # auto-installs mppx@0.6.5 from npm to ~/.npm/_npx/, returns 0.6.5
Cached at ~/.npm/_npx/b9498644ff968c00/node_modules/mppx. The global npm install -g @tempoxyz/mppx step from the original proposal is not required — npx mppx works out of the box and the proposal’s @tempoxyz/mppx package name was speculative. The actual published name is just mppx (the v0.6.5 binary; vendor unspecified in npm registry but fetched cleanly).
Wallet creation
npx mppx account create --account ray-testnet
# → Account "ray-testnet" saved to keychain.
# → Address 0x580A669D73F40e22d8b6a52AeD1d20CD319B1610
npx mppx account default --account ray-testnet
# → Default account set to "ray-testnet"
Address (testnet, safe to share): 0x580A669D73F40e22d8b6a52AeD1d20CD319B1610
Credential storage: mppx writes to macOS Keychain by default (NOT to a flat file on disk). The only file touched in ~/.config/mppx/ is default (11 bytes, contains the account name). Private key never lands on disk. This is materially better than the proposal assumed (we expected ~/.config/mppx/<account>.json) — keychain integration means even the testnet seed enjoys OS-level keystore protection. No need to rotate this into 1Password for testnet; for mainnet (Phase 2), the question becomes “1Password vs Keychain” rather than “1Password vs flat file.”
Funding (testnet faucet)
The default RPC is https://rpc.tempo.xyz (mainnet). Naive npx mppx account fund tried mainnet and failed with insufficient liquidity in FeeAMM pool — testnet RPC must be passed explicitly. The Moderato testnet RPC is https://rpc.moderato.tempo.xyz (chain ID 42431).
npx mppx account fund --account ray-testnet --rpcUrl https://rpc.moderato.tempo.xyz
# → Funding "ray-testnet" on testnet
# 0xf48993f9390284e21a44a23b8338d44e08a86ac46affd1020a54c8dc44fddc88
# 0x179945d084f208b6af41ff5adda75f7048fbd78a09aa8e56767b24a4e043f078
# 0xb767910bdfb755da441520851e12bfa23eb47b8407337e508fe83e899e940487
# 0x070b9f845ec45378069e0893613d4bbfb34c5a49365123e6581321aaf8404fb6
# → Funded successfully
Faucet drops 1,000,000 of each testnet stablecoin (PathUSD, AlphaUSD, BetaUSD, ThetaUSD) — verified via mppx account view:
Address 0x580A669D73F40e22d8b6a52AeD1d20CD319B1610
Balance 1_000_000 PathUSD (testnet)
1_000_000 AlphaUSD (testnet)
1_000_000 BetaUSD (testnet)
1_000_000 ThetaUSD (testnet)
MPP round-trip purchase (the load-bearing test)
Endpoint: https://mpp.dev/api/ping/paid (the canonical sandbox endpoint advertised in the MPP quickstart docs).
npx mppx https://mpp.dev/api/ping/paid \
--account ray-testnet \
--rpcUrl https://rpc.moderato.tempo.xyz \
-i -v
Two-leg HTTP exchange captured cleanly:
Leg 1 — server demands payment (HTTP 402):
HTTP/1.1 402 Payment Required
www-authenticate: Payment id="Y_6RM-oqzo0YypCJVzyw3aB86etxrQeM8C1ggWGRPH0",
realm="mpp.sh", method="tempo", intent="charge",
request="<base64-encoded payment request>",
description="Ping endpoint access",
expires="2026-05-01T20:10:44.134Z"
Decoded request (mppx pretty-prints it for us):
- amount:
100000= 0.1 PathUSD (test units) - recipient:
0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 - chainId:
42431(Moderato testnet) - feePayer:
true(server pays gas)
Leg 2 — mppx signs & re-requests, server returns content (HTTP 200):
HTTP/1.1 200 OK
payment-receipt: <base64 envelope decoding to>
{ method: "tempo", status: "success",
timestamp: "2026-05-01T20:05:45.488Z",
reference: "0xf7b54bd76e4bd6044784c101fc4b054350c8cd80c8bc00571e3f5e90657d83c8" }
Body: "tm! thanks for paying"
Transaction reference (testnet, Moderato chain ID 42431): 0xf7b54bd76e4bd6044784c101fc4b054350c8cd80c8bc00571e3f5e90657d83c8
Likely explorer URL (untested): https://explore.testnet.tempo.xyz/tx/0xf7b54bd76e4bd6044784c101fc4b054350c8cd80c8bc00571e3f5e90657d83c8
Total elapsed wall time of the two HTTP legs: ~1 second. This is the sub-second-finality story the Tempo + MPP marketing claims, observed first-hand. Compared to Link CLI’s ~90s human-in-loop per-charge approval, this is a different category of UX.
Deviations from the proposal’s plan
- Package name was speculative. Proposal said
@tempoxyz/mppx; actual published npm package ismppx. Updated mental model. - No global install needed.
npxcaches it; this is fine for our single-host operator pattern. Global install would be a nice-to-have for autocomplete only. - Default RPC is mainnet, not testnet. Funding command silently tried mainnet and failed informatively. Anyone repeating this needs
--rpcUrl https://rpc.moderato.tempo.xyz(orMPPX_RPC_URLenv var) to hit testnet. Document this in the Phase 2 mainnet wrapper script so we don’t accidentally fund/spend on the wrong network. - Credentials live in macOS Keychain, not in a config file. Better security posture than expected; reduces (but does not eliminate) the case for moving testnet keys to 1Password. Phase 2 should make a deliberate keychain-vs-1Password decision for mainnet.
- Phase 1 used
mpp.dev/api/ping/paid(0.1 PathUSD) as the test endpoint. This is a real over-the-internet MPP server, not a locally-hosted toy. Confirms the protocol works end-to-end on a third-party host.
What this unlocks for Phase 2
Phase 2 (mainnet, $50 USDC funding, MCP wrapper, spending caps, audit log) is now a straight-line execution job — no protocol-layer unknowns remain. The buyer flow is proven; the open work is governance plumbing (caps, logs, kill switch) and the funding-rail decision (Relay/Stargate bridge from existing USDC position).
Key carry-over decisions for Phase 2 brief:
- Confirm $50 starting mainnet balance (open question #1 from main proposal)
- Confirm Tier-1 auto-approve at $0.50/tx, $5/day (open question #2)
- Confirm card-fallback behavior when merchant isn’t on MPP (open question #3)
- NEW from Phase 1: decide keychain vs 1Password for the mainnet seed. Keychain has the security advantage (OS-level, hardware-backed on Apple Silicon); 1Password has the portability + auditability advantage (we can see when it was last accessed). My read: keep mainnet seed in keychain, mirror to 1Password as cold-backup-only for disaster recovery.
Phase 2 Execution Log — 2026-05-01 16:20 ET
Outcome: COMPLETE. Mainnet wallet provisioned, seed mirrored to 1Password as cold backup, wrapper script with cap enforcement built and smoke-tested. Awaiting founder action: $50 USDC seed transfer (step 7 below).
Mainnet wallet (shareable — public address)
Address: 0xD6714Effc5B580Ce4456bDCF33b118997eC2Adb2
Chain: Tempo Mainnet (chainId 4217 = 0x1079)
RPC: https://rpc.tempo.xyz
Verified live against the chain via eth_chainId RPC call (returned 0x1079 = 4217 ✓). Verified the wallet is reachable on mainnet via a smoke-test mppx invocation that reached the Tempo Mainnet TIP-20 contract (got back InsufficientBalance from the canonical USDC contract 0x20c0...0000 — expected, since balance is $0; what matters is the chain-side error confirms the wallet is wired correctly).
Credential storage (defense in depth)
Primary: macOS Keychain (mppx default behavior — confirmed in Phase 1, identical for mainnet).
- Keychain database:
~/Library/Keychains/login.keychain-db - Service:
mppx - Account:
ray-mainnet - Audit via
Keychain Access.app→ search “mppx” → two entries (ray-testnetfrom Phase 1,ray-mainnetnew). - The credential is a raw EVM private key (NOT a BIP-39 mnemonic — mppx@0.6.5 uses keystore-style key material). 32 bytes = 0x-prefixed 66-char hex string.
Cold backup: 1Password “Ray Agent” vault
- Item title:
mppx-mainnet-seed - Item ID:
xu2d6ms5pyw3htyxwbx66ckgoa - Fields:
wallet_address(text),private_key(password/concealed),chain(text — Tempo mainnet, chainId 4217),rpc_url(text),created_at(text — 2026-05-01),keychain_service(text — mppx),keychain_account(text — ray-mainnet), notes (rotation/restoration guidance). - Restoration path if Keychain entry is lost (e.g., new Mac, login keychain deleted):
The wrapper script does this automatically when it detects a missing Keychain entry.PRIVKEY=$(op item get xu2d6ms5pyw3htyxwbx66ckgoa --vault "Ray Agent" --fields private_key --reveal) security add-generic-password -s mppx -a ray-mainnet -w "$PRIVKEY" -U
Wrapper script
Path: ~/.claude/scripts/mppx-wrapper.sh (executable, chmod 755)
Defense layers (in order):
set -euo pipefail— fail loudly on any error.- Keychain verification with 1Password fallback — checks
security find-generic-password -s mppx -a ray-mainnet; if missing, fetches the private key from 1Password itemxu2d6ms5pyw3htyxwbx66ckgoaand re-seeds the keychain. If both fail, aborts. - Hard-locked RPC URL — exports
MPPX_RPC_URL=https://rpc.tempo.xyzandMPPX_ACCOUNT=ray-mainnet. Even though these are mppx defaults, locking them defends against env var inheritance from a parent shell that might be set to testnet. - Cap enforcement — for any URL argument, the wrapper:
- GETs the URL with
curl -s -ito capture the 402 challenge - Extracts the
request="..."field fromWWW-Authenticate: Paymentheader - Pads the base64 to a multiple of 4 (mpp.dev emits unpadded base64; macOS
base64 -Dsilently truncates without padding) and decodes via Python - Parses
amount(raw token units, USDC has 6 decimals → 100,000 = $0.10) andmethodDetails.chainId - Rejects any
chainId != 4217(Tempo Mainnet only) - Rejects any per-tx amount > 50¢ (Tier-1 cap from proposal)
- Rejects any cumulative daily spend > $5 (Tier-1 cap)
- GETs the URL with
- State file at
~/.claude/state/mppx-daily-spend.json, keyed byYYYY-MM-DDinAmerica/New_Yorktime. Format:{"2026-05-01": 30, "2026-05-02": 0, ...}where the value is cents spent that day. Garbage-collects entries older than 14 days on each successful tx. Only successful payments increment — if mppx fails (e.g., insufficient balance, RPC error), the cap counter is NOT advanced. - Logs to
/tmp/mppx-wrapper.logwith timestamps for every invocation, decision, and outcome.
Pass-through behavior: if the first arg is not an https?:// URL (e.g., account list, account view, sign), the wrapper passes through to npx mppx unchanged with exec.
Smoke-test results
All six tests run on 2026-05-01 16:19 ET, against the live wrapper on the live Tempo Mainnet RPC. Wrapper aborts cleanly with exit 1 in every reject case; pass-through works in every allow case.
| Test | Setup | Expected | Result |
|---|---|---|---|
| A | mppx-wrapper.sh https://mpp.dev/api/ping/paid (testnet chainId 42431) | REJECT — wrong chain | ✓ “Refusing”, exit 1 |
| B | Synthetic 402 with $1.00 amount on chainId 4217 | REJECT — > per-tx cap | ✓ “exceeds per-tx cap of $0.50”, exit 1 |
| C | Synthetic 402 with $0.40 on chainId 4217 (no daily spend yet) | Cap PASS, mppx attempts payment, fails on zero balance, state NOT incremented | ✓ Cap passed, mppx hit Tempo Mainnet (InsufficientBalance from 0x20c0... USDC contract — confirms wallet is on real mainnet), state file untouched |
| D | mppx-wrapper.sh account list (no URL) | Pass-through | ✓ Lists ray-mainnet + ray-testnet correctly |
| E | Pre-seed state file with {"2026-05-01": 470}, then 40¢ tx on mainnet | REJECT — would push to 510¢ > $5 daily cap | ✓ “would push today’s spend from 470¢ to 510¢, exceeding daily cap”, exit 1 |
| F | Pre-seed state with 30¢, then 40¢ tx (under both caps) | Cap PASS, mppx fails on balance, state still 30¢ | ✓ Cap passed, payment failed (zero balance), state file remained {"2026-05-01": 30} |
Cap-bypass attempts considered and verified blocked:
- Skipping the URL-prefix check by passing the URL as a header value or via
--data— wrapper inspects$1only; if$1isn’t a URL, it pass-throughs and the cap doesn’t engage. Mitigation: the wrapper is the operator-facing path; if someone callsnpx mppx <url>directly bypassing the wrapper, caps don’t apply. This is acceptable because (a) the wrapper is documented as the only sanctioned path; (b) the keychain access prompt would still fire, providing OS-level visibility; (c) the caps are operator-policy, not chain-side enforcement. - Forging a low-amount 402 on the GET-probe but a high-amount 402 on the actual mppx call — possible in theory if the server is malicious, but mppx itself signs each challenge it sees; a difference between probe-time and pay-time amounts would only matter if the same URL returns different challenges on consecutive requests, which is itself anomalous and visible in
/tmp/mppx-wrapper.log. Future hardening: add a re-check inside the npx mppx invocation by configuring--confirmmode, but that adds an interactive prompt; defer.
IP-restriction status (re: tracker file)
No update to ~/rdco-vault/04-tooling/2026-04-30-ip-restricted-policies-tracker.md needed. Tempo is a public chain — the wallet is identified by signature, not by source IP. Neither the Tempo RPC nor mppx exposes an IP-allowlist mechanism. Defense for the mainnet wallet relies on:
- Keychain (OS-level keystore, hardware-backed on Apple Silicon)
- 1Password cold backup (only the operator can read it)
- Wrapper-enforced per-tx and daily caps (limits blast radius if the credential ever leaks)
- Public address is the only identifier on-chain (signature ≠ identity ≠ IP)
This is the same security posture as Phase 1 testnet — OS keystore + caps. The IP-restricted-policies tracker is the right place for things like the Stripe RAK (which DOES support IP allowlisting); MPP/Tempo categorically doesn’t fit there, and that’s noted intentionally.
Step 7 — Founder action: $50 USDC mainnet seed transfer
Recommended path: Coinbase → direct USDC bridge to Tempo via Relay (relay.link).
Reasoning (lowest-friction first):
- Coinbase + Relay (recommended) — Founder already has a Coinbase account with USDC balance (per 2026-04-29-link-cli-agent-wallet-setup context). Relay supports Tempo as a destination and bridges USDC natively in one tx, ~$1-3 fee, ~30s settlement. Steps: open
relay.link, connect a wallet that holds the USDC (or use Coinbase Wallet’s WalletConnect), select source chain (Ethereum/Base/Arbitrum — wherever the USDC sits), destination chain Tempo, paste address0xD6714Effc5B580Ce4456bDCF33b118997eC2Adb2, send $50 USDC. Confirm receipt vianpx mppx account view --account ray-mainnet --rpc-url https://rpc.tempo.xyz. - Stripe Link wallet → Tempo — Tempo support inside Link is not yet GA per Stripe’s blog (early access waitlist). Skip until GA.
- LayerZero Stargate — equivalent to Relay; comparable fees, slightly more clicks. Use as fallback if Relay is down.
- Stripe-managed on-ramp — would require setting up a buy-USDC-via-card flow, more friction than Coinbase-already-funded path.
Verification after transfer:
npx mppx account view --account ray-mainnet --rpc-url https://rpc.tempo.xyz
# Expected: Balance shows ~50 USDC (minus bridge fee, net ~$48-49)
Then a $0.10 micropurchase against any mainnet MPP endpoint via ~/.claude/scripts/mppx-wrapper.sh <url> will be the live-fire validation.
Phase 2 deviations from plan
- Wallet credential is a raw private key, not a 12/24-word mnemonic. The proposal language (“seed phrase”) was loose; mppx uses keystore-style 32-byte private keys. The 1Password item field is named
private_keyto match. No security implication — a private key is functionally equivalent to a mnemonic for restoration purposes. - Chain ID confirmation: Tempo Mainnet is
4217(0x1079), confirmed via live RPC call. Various community docs reference6900; that is incorrect for mainnet (may be a testnet-variant or a stale doc). The wrapper hard-codes4217. mppx paysubcommand does not exist — payment happens inline when fetching a 402-protected URL. The proposal’s reference topay --dry-runwas speculative. Cap enforcement is implemented by intercepting the 402 challenge ourselves (curl + base64 decode) before letting mppx do the actual signed re-request. This is more reliable thanmppx sign --dry-run, which only validates challenge format and doesn’t return parsed amount.- macOS
base64 -Dsilently truncates unpadded input. Real-world test usingmpp.dev/api/ping/paidrevealed mpp.dev emits unpadded base64 (request="...In0"without trailing=). The Phase 1 success was a happy accident — its test endpoint happened to be 4-aligned. Wrapper now uses Python’sbase64.b64decodewhich auto-handles padding.
Files touched in Phase 2
~/.claude/scripts/mppx-wrapper.sh(NEW, 755)~/.claude/state/mppx-daily-spend.json(created/destroyed during smoke tests; will be created on first real successful payment)- 1Password “Ray Agent” vault → new item
mppx-mainnet-seed(xu2d6ms5pyw3htyxwbx66ckgoa) - macOS Keychain → new entry
service=mppx, account=ray-mainnet - THIS file (Phase 2 log appended)
Carry-overs into Phase 3 / next operational notes
- After founder funds and we run our first real micropurchase, append a per-tx audit row to a new
~/rdco-vault/04-tooling/mpp-transaction-log.md(proposal mentions this — defer creation until first real tx so the file isn’t empty boilerplate). - Tier-2 ($0.50–$25, founder-confirm) and Tier-3 ($25–$250, founder-explicit) tiers are NOT yet implemented in the wrapper. They’re reasonable next-week additions once we have 5-10 real micropurchases under the Tier-1 auto-approve. Add them only after data shows the auto path is calibrated.
- The wrapper is a single-host script; if we ever move Ray to a second machine, the keychain entry doesn’t follow but the 1Password item does — the wrapper’s fallback path will re-seed. Tested this code path symbolically (1Password fetch returns the same
0x8b7252...59baprivate key as the keychain).
Funding Path Decision (2026-05-01 16:56 ET)
Founder asked: “Can we fund [the Tempo wallet] with Link instead of the roundabout Coinbase connection?”
Direct answer: NO — Link CLI cannot fund the Tempo wallet directly. It is structurally outbound-only.
Why Link can’t do it (verified against link-cli@0.4.0 installed locally)
Inspected the full command surface via link-cli --llms-full. Subcommand inventory: auth, demo, mpp (decode + pay), onboard, payment-methods (add + list), spend-request (create/retrieve/update/request-approval). There is no fund, deposit, transfer, send, or wallet subcommand. The wallet model is strictly card/ACH-on-file → spend-request → one-time SPT or virtual card credential consumed by an external merchant. Link wallet does not hold a Tempo USDC balance; it does not have an on-chain address; it cannot push USDC anywhere.
link-cli mpp pay <url> looks promising at first glance but is the opposite direction: it spends an approved spend request’s SPT against an MPP-paywalled URL via Stripe-managed rails. It uses Stripe’s MPP method (card SPT delivered over the 402 challenge), NOT Tempo USDC out of a self-custody wallet. The receiving merchant is paid by Stripe, not by an on-chain Tempo transfer to anyone’s address.
Confirmed by docs
- TechCrunch (Apr 30 2026, “Stripe updates Link” / Sessions 2026 recap): Link primarily moves money over card rails today; “stablecoin and crypto rails are listed as additions to the wallet, not the default path.” Stablecoin support is explicitly described as upcoming, not shipped. (techcrunch.com, eco.com Link explainer)
- Stripe MPP architecture (
docs.stripe.com/payments/machine/mpp): the Tempo USDC settlement path requires the agent to “send USDC to a Stripe-generated deposit address on the Tempo network” — i.e. Stripe expects USDC to ALREADY be in your wallet on Tempo. Stripe is the acceptor here, not the on-ramp.
The clean alternative the founder didn’t see: Stripe’s Crypto Onramp (separate product)
Stripe has a separate fiat→crypto on-ramp product (docs.stripe.com/crypto/onramp) that DOES let a user buy USDC with a card and have it delivered to an arbitrary self-custody address. But Tempo is not in the supported destination network list as of today — bitcoin, ethereum, solana, polygon, stellar, avalanche, base are listed; Tempo is not. So even the standalone on-ramp can’t deliver USDC directly to 0xD671...Adb2 on chainId 4217. We would still have to bridge from one of the supported chains → Tempo, which collapses back to roughly the same pattern as Coinbase + Relay (one fewer click, but adds a fresh KYC if the founder hasn’t used Stripe Onramp before — net worse than the Coinbase path he already has).
Path comparison for the $50 seed
| Path | Mechanism | Fee | Time | Friction |
|---|---|---|---|---|
| A. Coinbase USDC → Relay → Tempo (recommended in Phase 2 log) | Existing Coinbase USDC balance → relay.link bridges to Tempo address | ~$1-3 | ~30s | Founder already has the Coinbase account; one Relay flow |
| B. Link CLI direct | — | — | — | NOT POSSIBLE (no fund/send subcommand exists) |
| C. Stripe Crypto Onramp → bridge → Tempo | Stripe-hosted onramp buys USDC on Base/Ethereum → bridge to Tempo | ~1.5% Stripe + bridge | ~minutes | New product setup, possible KYC, still requires bridge → no advantage over Path A |
| D. Wait for Link stablecoin GA | — | — | weeks/months | TechCrunch confirms it’s “coming,” not shipped |
Recommendation
Stick with Path A. Coinbase → Relay to 0xD6714Effc5B580Ce4456bDCF33b118997eC2Adb2 is the straightest line we have today. The “roundabout” feel is real but unavoidable until either (a) Stripe Crypto Onramp adds Tempo as a destination network, or (b) Link wallet ships its stablecoin/Tempo balance feature (announced as roadmap at Sessions 2026, no GA date). When (a) happens, Path C becomes Path A’s equal; when (b) happens, the founder’s original instinct becomes the right answer. Until then, the bridge step is the cost of being early on Tempo.
Watch list (revisit if any of these change)
- Stripe Crypto Onramp adds Tempo to supported destination networks (
docs.stripe.com/crypto/onramp/stripe-hosted) - Link wallet ships native USDC-on-Tempo balance + deposit/fund subcommand (
link-climinor version bumps; checklink-cli --helpafter anynpm update -g @stripe/link-cli) - Stripe MPP-via-PaymentIntents leaves early access (would change acceptance not funding, but worth tracking)
tempo CLI Fund Spike (2026-05-01)
Spike to check whether the official tempo CLI (separate from mppx/link-cli) offers a one-command funding path for an arbitrary mainnet address. Founder greenlit 17:06 ET; research-only, no funds moved.
Install
which tempo→ not installed- Installed via official redirect:
curl -fsSL https://tempo.xyz/install | bash tempo.xyz/install307-redirects toraw.githubusercontent.com/tempoxyz/tempo/refs/heads/main/tempoup/install(verified legit — officialtempoxyzGitHub org, GPG-signed binary, SHA256 checksum verified during install)- Installed to
~/.tempo/bin/, env file at~/.tempo/env - Version installed:
tempo 1.6.0(commit291c0157, built 2026-04-16) - The base binary ships with node/db/p2p/snapshot tooling. Wallet is a separately-installed extension:
tempo add wallet→tempo-wallet v0.4.1
tempo wallet surface
Commands:
login Sign up or log in to your Tempo wallet
refresh Refresh your access key without logging out
logout Log out and disconnect your wallet
whoami Show who you are: wallet, balances, keys
keys List keys and their spending limits
transfer Transfer tokens to an address
fund Fund your wallet (testnet faucet or mainnet bridge)
sessions Manage payment sessions
services Browse the MPP service directory
debug Collect debug info for support
tempo wallet fund --help (verbatim)
Fund your wallet (testnet faucet or mainnet bridge)
Usage: tempo wallet fund [OPTIONS]
Options:
--address <ADDRESS> Wallet address to fund (defaults to current wallet)
--no-browser Do not attempt to open a browser
-h, --help Print help
tempo wallet transfer --help (verbatim)
Transfer tokens to an address
Usage: tempo wallet transfer [OPTIONS] <AMOUNT> <TOKEN> <TO>
Arguments:
<AMOUNT> Amount in human units ("1.00", "50")
<TOKEN> Token contract address (0x...)
<TO> Recipient address (0x...)
Options:
--fee-token <FEE_TOKEN> Pay fees in a different token (default: same token)
--dry-run Show plan + fee estimate, don't send
Examples:
tempo wallet transfer 1.00 0x20c0...b50 0x70997...9C8
tempo wallet transfer 50 0x20c0...b50 0x70997...9C8 --dry-run
Does tempo wallet fund --to <address> exist? PARTIAL
The shape is close but not what the spike hoped for:
tempo wallet fund --address 0xD6714...Adb2is a valid command and accepts an arbitrary mainnet address (not just the wallet you’re logged in as)- BUT:
fundis a launcher, not an executor. On mainnet it opens a browser (“bridging and onramp options” perdocs.tempo.xyz/cli/wallet). On testnet it opens the faucet. The CLI itself does not negotiate the fiat onramp — it hands off to the same Tempo Wallet web UI (wallet.tempo.xyz) that the founder already reported failing on mobile - The
--no-browserflag exists but its docs don’t promise headless funding — it’s likely just “print the URL instead of opening it” so you can paste into a different browser/device - No
--source/--amount/--via/--tokenflags. No headless onramp negotiation. No fiat method selection from the CLI
KYC / pairing: funding is gated by whatever the Tempo Wallet web UI demands — same KYC/region rules as the failed mobile attempt. The CLI does not bypass them.
What this changes for our $50 USDC funding plan
Nothing material. tempo wallet fund --address 0xD6714Effc5B580Ce4456bDCF33b118997eC2Adb2 --no-browser would print the same wallet.tempo.xyz onramp URL the founder already tried on mobile — possibly more usable on desktop, but the underlying flow (KYC, region gates, current mobile bug) is unchanged.
tempo wallet transfer is crypto-to-crypto only (takes a token contract address as the second arg) — useful once we already have USDC on Tempo, useless for the fiat-on-ramp step.
Verdict
Fall back to Coinbase → Relay (Path A from the main proposal). The tempo CLI does not collapse the funding flow; it’s a thin launcher around the same web UI that’s already degraded for the founder. Worth keeping installed for tempo wallet whoami, tempo wallet transfer, and tempo wallet services once we have funds on Tempo, but it’s not a shortcut to getting funds onto Tempo.
One small upside worth trying first: before booting Coinbase + Relay, run tempo wallet fund --address 0xD6714Effc5B580Ce4456bDCF33b118997eC2Adb2 --no-browser from desktop. If the printed URL opens a working desktop onramp (the mobile bug may not repro on desktop), we save the bridge hop. Two-minute test, then bail to Path A if the desktop UI also fails.
Watch list addition
tempo wallet fundgains--source/--amountflags or a headless onramp path (would make the CLI a true one-command funder; check aftertempo updateminor bumps)