This is a Cargo workspace containing 4 crates under crates/, providing a command-line HTTP client with built-in MPP payment support, wallet identity management, and a release signing tool. The top-level tempo launcher lives in the main tempo repo (tempo/crates/ext/).
Supported Payment Protocols:
- Machine Payments Protocol (MPP) - Open protocol for HTTP-native machine-to-machine payments
The root Cargo.toml is workspace-only (no package). All dependencies are declared as [workspace.dependencies] in the root and consumed via dep.workspace = true in each crate. All crates live under crates/:
Shared library used by tempo-wallet and tempo-request. Contains core logic:
crates/tempo-common/src/lib.rs- Module declarations (analytics, cli, config, error, keys, network, payment, security)crates/tempo-common/src/analytics.rs- Opt-out telemetry (PostHog)crates/tempo-common/src/config.rs- Configuration file handlingcrates/tempo-common/src/error.rs- Error types (ConfigError, TempoError, etc.)crates/tempo-common/src/network.rs- Network definitions (NetworkId), explorer config, RPCcrates/tempo-common/src/security.rs- Security utilities (safe logging, sanitization, redaction)crates/tempo-common/src/cli/- Shared CLI infrastructuremod.rs- Re-exports (parse_cli, GlobalArgs, run_cli, run_main, Verbosity)args.rs- GlobalArgs, parse_clicontext.rs-Contextstruct (Config, NetworkId, Keystore, Analytics, OutputFormat, Verbosity)exit_codes.rs- Process exit codes (ExitCode enum)format.rs- Value formatting helpers (amounts, durations, timestamps)output.rs- OutputFormat, structured output helpersrunner.rs- CLI lifecycle (run_cli, run_main)runtime.rs- Tracing, color mode, error renderingterminal.rs- Terminal output helpers (hyperlinks, field formatting, truncation, sanitization)tracking.rs- Analytics tracking (track_command, track_result)verbosity.rs- Verbosity configuration
crates/tempo-common/src/keys/- Key storage (model, I/O), signer resolution, authorizationmod.rs,model.rs,keystore.rs,io.rs,signer.rs,authorization.rs
crates/tempo-common/src/payment/- Payment error classification and session managementmod.rs- (classify, session)classify.rs- Payment error classification and extractionsession/- Channel persistence and channel management (channel.rs, close.rs, store.rs, tx.rs)
Wallet identity and custody extension, plus session/service management. Source organized by module directories:
crates/tempo-wallet/src/main.rs- CLI entry pointcrates/tempo-wallet/src/args.rs- clap definitions (Cli, Commands, SessionCommands, ServicesCommands)crates/tempo-wallet/src/app.rs- Command dispatch: context building, command routing, analyticscrates/tempo-wallet/src/analytics.rs- Wallet-specific analytics events and payloadscrates/tempo-wallet/src/prompt.rs- Interactive prompt helperscrates/tempo-wallet/src/wallet/- Wallet account types (balances, keys, spending limits) and on-chain queriesmod.rs,types.rs,query.rs,render.rs
crates/tempo-wallet/src/commands/- Command implementations (all take&Contextas first arg)login.rs- Login command (passkey authentication flow)logout.rs- Logout commandwhoami.rs- Whoami commandkeys.rs- Key listing, balance and spending limit queriesfund/- Fund command (browser-based flow)sessions/- Session management (list, close, sync, render)services/- Service directory (client, model, render)sign.rs- Sign MPP payment challengescompletions.rs- Shell completions
crates/tempo-wallet/tests/- Integration tests (black-box CLI testing via assert_cmd)
HTTP client with built-in MPP payment support. Source organized by module directories:
crates/tempo-request/src/main.rs- CLI entry pointcrates/tempo-request/src/args.rs- clap definitions (Cli, QueryArgs)crates/tempo-request/src/app.rs- Command dispatchcrates/tempo-request/src/analytics.rs- Request-specific analytics events and payloadscrates/tempo-request/src/query/- Query flow (request prep, output, challenge parsing, SSE, analytics)mod.rs,analytics.rs,challenge.rs,headers.rs,output.rs,payload.rs,prepare.rs,sse.rs
crates/tempo-request/src/http/- HTTP client and request handlingmod.rs,client.rs,fmt.rs,response.rs
crates/tempo-request/src/payment/- Payment flows (charge + session)mod.rs,charge.rs,router.rssession/- Session-based payment (flow.rs, open.rs, persist.rs, streaming.rs, voucher.rs)
Lightweight release manifest signing tool for authenticating build artifacts.
crates/tempo-sign/src/main.rs- Signing tool source
Packages: tempo-common, tempo-wallet, tempo-request, tempo-sign
make build # Build debug binary
make release # Build optimized release binary
make test # Run all tests (uses mocks, no network required)
make check # Run fmt check, clippy, tests, and doc
make fix # Auto-fix formatting and clippy warnings
make install # Install CLI binaries to ~/.tempo/bin
make uninstall # Uninstall CLI binaries
make run ARGS="<url>" # Run tempo-wallet with argumentsWhen the user explicitly says "ask the oracle" to check a value, run tempo-request against OpenRouter and explicitly tell the user which model was used in the response.
Example:
tempo-request -v -X POST --json '{"model":"openai/gpt-4o-mini","messages":[{"role":"user","content":"what is 1+1"}]}' https://openrouter.mpp.tempo.xyz/v1/chat/completions | jq- ✅ Check:
make check- ZERO issues
When creating pull requests:
- Always include the PR link in your response after creating a PR
- Format as a clickable link:
[#123](https://github.com/tempoxyz/wallet/pull/123) - When creating multiple PRs, provide a summary table with all links
Example summary format:
| PR | Title | Link |
|----|-------|------|
| 1 | feat: add feature X | [#123](https://github.com/tempoxyz/wallet/pull/123) |
| 2 | fix: resolve issue Y | [#124](https://github.com/tempoxyz/wallet/pull/124) |
- Edition: Rust 2021
- Error handling: Prefer typed
TempoErrorboundaries and source-carrying variants (*Source) where a concrete underlying error exists - Async runtime: Tokio (minimal features: macros, rt-multi-thread, signal)
- Serialization: Serde with derive macros
- Group imports: std → external crates → crate modules
- Use
use crate::for internal module imports - Use
use tempo_common::for shared library imports
use std::path::PathBuf;
use clap::Parser;
use tempo_common::config::Config;
use tempo_common::error::TempoError;
fn run() -> Result<(), TempoError> {
Ok(())
}use thiserror::Error;
#[derive(Error, Debug)]
pub enum TempoError {
#[error("failed to parse: {0}")]
ParseError(String),
#[error(transparent)]
Io(#[from] std::io::Error),
}- Each module should have a clear single responsibility
- Use
mod.rsfor modules with submodules - Shared logic goes in
crates/tempo-common/src/ - All commands go in
crates/tempo-wallet/src/commands/
- Integration tests in
tests/use assert_cmd for black-box CLI testing - Use
TestConfigBuilderfor setting up test configurations - Use
test_command(&temp)helper to create properly configured CLI commands
use crate::common::{TestConfigBuilder, test_command};
#[test]
fn test_something() {
let temp_dir = TestConfigBuilder::new().build();
let mut cmd = test_command(&temp_dir);
cmd.arg("--help");
cmd.assert().success();
}- Use derive macros for argument parsing
- Flatten
GlobalArgsfromtempo_common::clifor shared flags - Group related args with
help_heading - Support short aliases (
-v) and long aliases (--verbose)
#[derive(Parser, Debug)]
pub struct Cli {
#[command(flatten)]
pub global: GlobalArgs,
#[command(subcommand)]
pub command: Option<Commands>,
}- Check existing patterns in similar files
- Identify affected tests
- Add shared logic in
crates/tempo-common/src/ - Add CLI flags in the appropriate binary's
src/args.rs - Implement commands in the appropriate binary's
src/commands/ - Add tests: unit tests in source files, integration tests in each crate's
tests/
| Crate | Purpose |
|---|---|
clap |
CLI argument parsing |
alloy |
EVM interactions and signing (minimal features) |
reqwest |
HTTP client |
serde / serde_json / toml |
Serialization |
tokio |
Async runtime (minimal features) |
mpp |
Machine Payments Protocol SDK |
Add to [workspace.dependencies] in the root Cargo.toml, then reference with dep.workspace = true in the crate's Cargo.toml:
# Root Cargo.toml
[workspace.dependencies]
new-crate = "1.0"
# Crate Cargo.toml
[dependencies]
new-crate.workspace = true| Variable | Description |
|---|---|
TEMPO_HOME |
Override data directory (default: ~/.tempo) |
TEMPO_RPC_URL |
Override RPC endpoint |
TEMPO_AUTH_URL |
Override auth server URL |
TEMPO_SERVICES_URL |
Override service directory API URL |
TEMPO_NO_TELEMETRY |
Disable telemetry |
TEMPO_PRIVATE_KEY |
Provide a private key directly for payment (bypasses wallet login and keychain; ephemeral) |
All data lives under $TEMPO_HOME (default: ~/.tempo):
~/.tempo/
├── config.toml # Shared config (RPC overrides, telemetry)
└── wallet/
├── keys.toml # Wallet keys (mode 0600)
└── channels.db # Persisted payment channel state (SQLite)
- Private keys: macOS Keychain (macOS) or inline in
keys.toml(Linux)
struct Config {
tempo_rpc: Option<String>, // Typed RPC override for Tempo mainnet
moderato_rpc: Option<String>, // Typed RPC override for Moderato testnet
rpc: HashMap<String, String>, // General RPC overrides by network id
}Wallet Fields (keys.toml):
wallet_type—"local"or"passkey"wallet_address— On-chain wallet address (the fundable address)chain_id— Chain ID this key is authorized forkey_type— Signature type ("secp256k1","p256", or"webauthn")key_address— Address of the signing keykey— Signing key private key stored inline; file is written with mode 0600key_authorization— RLP-encoded on-chain authorization proof for this keyexpiry— Unix timestamp for key authorization expirylimits— Array of{ currency: "0x...", limit: "..." }
Key Selection: Deterministic: passkey > first key with key > first key (lexicographically). The old active field was removed.
Network Resolution Priority:
TEMPO_RPC_URLenv var (overrides everything)- Typed overrides (
tempo_rpc,moderato_rpc) take precedence - General
[rpc]table as fallback - Default RPC if no override
Built-in Networks: tempo (chain 4217, mainnet), tempo-moderato (chain 42431, testnet)
Built-in Tokens: USDC (mainnet), pathUSD (testnet)