Try Live
Add Docs
Rankings
Pricing
Docs
Install
Theme
Install
Docs
Pricing
More...
More...
Try Live
Rankings
Enterprise
Create API Key
Add Docs
LedgerFlow
https://github.com/longcipher/ledgerflow
Admin
LedgerFlow is a chain-agnostic x402 v2 facilitator that enables resource servers to adopt x402
...
Tokens:
10,384
Snippets:
61
Trust Score:
7.5
Update:
3 weeks ago
Context
Skills
Chat
Benchmark
55.3
Suggestions
Latest
Show doc for...
Code
Info
Show Results
Context Summary (auto-generated)
Raw
Copy
Link
# LedgerFlow LedgerFlow is the missing authorization layer for x402 AI payments. It provides a secure framework for humans to issue scoped, time-limited payment warrants to AI agents, which can then make purchases from merchants while maintaining cryptographic proof of authorization. The system keeps x402 as the merchant and agent wire protocol while adding LedgerFlow authorization through x402 extensions and routing verified payments to settlement rails through a Facilitator layer. The core architecture consists of three main crates: `ledgerflow-core` for warrant, proof, and constraint verification logic; `ledgerflow-x402` for x402 challenge and payload extensions with merchant verification and replay protection; and `ledgerflow-facilitator` for payment-subject resolution and routing to EVM, exchange, custodial, or traditional gateway rails. The system supports multiple settlement rails including on-chain (EVM), centralized exchanges, custodial accounts, and traditional payment gateways. ## Core Authorization Types ### SigningKeyPair - Ed25519 Key Generation and Signing The `SigningKeyPair` type provides Ed25519 cryptographic key pair generation and signing capabilities for warrant issuance and proof creation. It supports both random key generation and deterministic key creation from secret bytes. ```rust use ledgerflow_core::SigningKeyPair; use rand_core::OsRng; // Generate a new random Ed25519 key pair let issuer_keys = SigningKeyPair::generate(&mut OsRng); // Create from deterministic secret bytes (32 bytes required) let secret: [u8; 32] = *b"issuer-secret-key-32-bytes-long!"; let issuer_keys = SigningKeyPair::from_bytes(&secret); // Get public key bytes for sharing let public_key: [u8; 32] = issuer_keys.public_key_bytes(); // Create a SignerRef for use in warrants let signer_ref = issuer_keys.signer_ref(); // SignerRef { alg: Ed25519, public_key: [...], key_id: None } // Sign arbitrary data let message = b"data to sign"; let signature = issuer_keys.sign(message); // SignatureEnvelope { alg: Ed25519, value: [...] } ``` ### Warrant - Payment Authorization Structure The `Warrant` type represents a signed, scoped, time-limited authorization from a human issuer to an AI agent. It defines constraints on merchants, resources, tools, payment amounts, and settlement rails. ```rust use ledgerflow_core::{ AmountLimit, AssetRef, AudienceScope, Constraint, DelegationPolicy, MerchantConstraint, PaymentConstraint, PaymentRail, PaymentSubjectKind, PaymentSubjectRef, ResourceConstraint, SigningKeyPair, SponsorshipConstraint, ToolConstraint, Warrant, WarrantMetadata, WARRANT_VERSION_V1, }; let issuer = SigningKeyPair::from_bytes(&[1u8; 32]); let agent = SigningKeyPair::from_bytes(&[2u8; 32]); // Create a payment subject (CAIP-10 blockchain address) let payment_subject = PaymentSubjectRef::new( PaymentSubjectKind::Caip10, "caip10:eip155:8453:0xabc123" ); // Build and sign a warrant let warrant = Warrant { version: WARRANT_VERSION_V1, warrant_id: "warrant-1".to_string(), issuer: issuer.signer_ref(), subject_signer: agent.signer_ref(), payment_subjects: vec![payment_subject], audience: AudienceScope::MerchantIds(vec!["merchant-a".to_string()]), not_before_ms: 1_000, expires_at_ms: 10_000, delegation: DelegationPolicy { can_delegate: true, max_depth: 1 }, constraints: vec![ Constraint::Merchant(MerchantConstraint { merchant_ids: vec!["merchant-a".to_string()], host_suffixes: vec![], }), Constraint::Resource(ResourceConstraint { http_methods: vec!["POST".to_string()], path_prefixes: vec!["/pay".to_string()], }), Constraint::Tool(ToolConstraint { tool_names: vec!["web-search".to_string()], model_providers: vec![], action_labels: vec![], }), Constraint::Payment(PaymentConstraint { max_per_request: AmountLimit { amount: 200 }, period_limit: None, allowed_assets: vec![AssetRef::new("USDC", Some("base".to_string()))], allowed_rails: vec![PaymentRail::Onchain], allowed_schemes: vec!["exact".to_string()], payee_ids: vec!["merchant-a".to_string()], }), Constraint::Sponsorship(SponsorshipConstraint { allow_sponsored_execution: false, sponsor_ids: vec![], }), ], metadata: WarrantMetadata::default(), signature: issuer.sign(b"placeholder"), // placeholder, will be replaced }.sign_with(&issuer); // Get the warrant digest for caching/reference let digest = warrant.digest(); // "sha256:abc123..." // Encode to CBOR for transport let cbor_bytes = warrant.encode_cbor().expect("encode"); let decoded = Warrant::decode_cbor(&cbor_bytes).expect("decode"); ``` ### Proof - Authorization Proof Creation The `Proof` type binds a warrant to a specific merchant challenge and request/quote pair. The agent creates and signs proofs to demonstrate authorization. ```rust use ledgerflow_core::{Proof, SigningKeyPair, sha256_prefixed}; let agent_keys = SigningKeyPair::from_bytes(&[2u8; 32]); // Create canonical hashes for binding let request_hash = sha256_prefixed("GET\nmerchant.example\n/search?q=test\nsha256:body"); let accepted_hash = sha256_prefixed("exact:USDC:200:merchant-a"); // Create a signed proof let proof = Proof::new_signed( "challenge-1", // challenge_id from merchant "sha256:warrant-digest", // digest of the warrant being used accepted_hash, // hash of accepted quote request_hash, // hash of HTTP request 2_000, // created_at_ms timestamp "unique-nonce-1", // unique nonce for replay protection &agent_keys, // agent's signing keys ); // Access proof fields println!("Challenge: {}", proof.challenge_id); println!("Nonce: {}", proof.nonce); println!("Request Hash: {}", proof.request_hash); ``` ### verify_authorization - Authorization Verification The `verify_authorization` function validates a warrant and proof against the merchant's request context, checking signatures, constraints, expiration, and binding hashes. ```rust use ledgerflow_core::{ AuthorizationContext, PaymentRail, PaymentSubjectKind, PaymentSubjectRef, Proof, SigningKeyPair, Warrant, sha256_prefixed, verify_authorization, DEFAULT_PROOF_FRESHNESS_MS, }; // Build the authorization context from the merchant's perspective let context = AuthorizationContext { merchant_id: "merchant-a".to_string(), merchant_host: "merchant-a.example".to_string(), tool_name: "web-search".to_string(), model_provider: String::new(), action_label: String::new(), http_method: "GET".to_string(), path_and_query: "/search?q=ledgerflow".to_string(), selected_quote_amount: 200, asset: "USDC".to_string(), scheme: "exact".to_string(), payee_id: "merchant-a".to_string(), rail: PaymentRail::Onchain, challenge_id: "challenge-1".to_string(), request_hash: sha256_prefixed("GET\nmerchant-a.example\n/search?q=ledgerflow\nsha256:body"), accepted_hash: sha256_prefixed("exact:USDC:200:merchant-a"), now_ms: 2_000, freshness_window_ms: DEFAULT_PROOF_FRESHNESS_MS, presented_delegation_depth: 1, payment_subject: PaymentSubjectRef::new( PaymentSubjectKind::Caip10, "caip10:eip155:8453:0xabc123" ), }; // Verify authorization (warrant and proof must be valid) match verify_authorization(&warrant, &proof, &context) { Ok(verified) => { println!("Authorized merchant: {}", verified.merchant_id); println!("Tool: {}", verified.tool_name); println!("Amount: {} {}", verified.amount, verified.asset); println!("Rail: {:?}", verified.rail); } Err(e) => { eprintln!("Authorization failed: {}", e); // Possible errors: InvalidWarrantSignature, WarrantExpired, // MerchantNotAllowed, PaymentAmountExceeded, etc. } } ``` ## x402 Integration ### merchant_payment_required - Creating 402 Responses The `merchant_payment_required` function creates a standard x402 `402 Payment Required` response with a LedgerFlow challenge extension. ```rust use ledgerflow_x402::{AcceptedQuote, merchant_payment_required}; // Create accepted quote options let quotes = vec![ AcceptedQuote::exact("USDC", 200, "merchant-a", Some("base".to_string())), AcceptedQuote::exact("ETH", 50, "merchant-a", Some("ethereum".to_string())), ]; // Generate 402 response with LedgerFlow challenge let response = merchant_payment_required( "challenge-uuid-12345", // unique challenge ID "merchant-a", // merchant identifier "/api/search", // requested resource quotes, // accepted payment options 60_000, // proof freshness window (ms) ); // Response structure assert_eq!(response.status_code, 402); assert!(response.headers.iter().any(|(k, _)| k == "x-payment-required")); let challenge = response.ledgerflow.expect("LedgerFlow challenge"); assert_eq!(challenge.version, "lfx402/v1"); assert_eq!(challenge.challenge_id, "challenge-uuid-12345"); assert_eq!(challenge.merchant_id, "merchant-a"); assert_eq!(challenge.proof_freshness_ms, 60_000); ``` ### build_payment_payload - Creating Payment Payloads The `build_payment_payload` function creates an x402 payment payload that echoes the selected quote and adds LedgerFlow authorization data. ```rust use ledgerflow_x402::{ AcceptedQuote, HttpRequest, PaymentPayloadSeed, WarrantTransport, build_payment_payload, merchant_payment_required, }; use ledgerflow_core::{PaymentSubjectKind, PaymentSubjectRef, SigningKeyPair}; let agent_keys = SigningKeyPair::from_bytes(&[2u8; 32]); // Create the HTTP request context let request = HttpRequest::new( "POST", "merchant-a.example", "/pay", br#"{"item":"premium-search"}"#.to_vec(), ); // Get the challenge from merchant's 402 response let accepted = AcceptedQuote::exact("USDC", 200, "merchant-a", Some("base".to_string())); let challenge = merchant_payment_required( "challenge-1", "merchant-a", "/pay", vec![accepted.clone()], 60_000 ).ledgerflow.expect("challenge"); // Build payment payload with inline warrant let payload = build_payment_payload( &challenge, &request, accepted, WarrantTransport::inline(warrant), // or WarrantTransport::digest_ref("sha256:...") PaymentPayloadSeed { payment_subject: PaymentSubjectRef::new( PaymentSubjectKind::Caip10, "caip10:eip155:8453:0xabc123" ), signer: agent_keys, created_at_ms: 2_000, nonce: "unique-nonce-1".to_string(), payment_identifier: Some("payment-id-123".to_string()), }, ); // Access payload fields println!("Selected quote: {} {}", payload.accepted.amount, payload.accepted.asset); println!("Payment ID: {:?}", payload.payment_identifier); let extension = payload.ledgerflow.expect("extension"); println!("Challenge ID: {}", extension.challenge_id); println!("Warrant digest: {}", extension.warrant.digest); ``` ### MerchantVerifier - Server-Side Payment Verification The `MerchantVerifier` type provides merchant-side verification that composes x402 payloads with LedgerFlow authorization checks, including replay protection and warrant caching. ```rust use ledgerflow_x402::{ AcceptedQuote, HttpRequest, InMemoryWarrantRepository, MerchantVerifier, PaymentPayloadSeed, WarrantTransport, build_payment_payload, merchant_payment_required, InMemoryReplayStore, }; use ledgerflow_core::{PaymentSubjectKind, PaymentSubjectRef, SigningKeyPair}; // Create verifier with replay store and warrant cache let mut verifier = MerchantVerifier::new( InMemoryReplayStore::default(), InMemoryWarrantRepository::default(), ); // Merchant generates challenge let accepted = AcceptedQuote::exact("USDC", 200, "merchant-a", Some("base".to_string())); let challenge = merchant_payment_required( "challenge-1", "merchant-a", "/pay", vec![accepted.clone()], 60_000 ).ledgerflow.expect("challenge"); // Agent sends payment request let request = HttpRequest::new("POST", "merchant-a.example", "/pay", br#"{"ok":true}"#.to_vec()); // Verify the payment match verifier.verify_payment( &challenge, &request, &payload, // PaymentPayload from agent "web-search", // tool name 2_000, // current timestamp (ms) 1, // delegation depth ) { Ok(outcome) => { println!("Authorization valid!"); println!("Merchant: {}", outcome.authorization.merchant_id); println!("Amount: {}", outcome.authorization.amount); println!("Settlement reused: {}", outcome.settlement_reused); } Err(e) => { eprintln!("Verification failed: {}", e); // Possible errors: MissingLedgerFlowExtension, ChallengeMismatch, // ReplayDetected, UnknownWarrantDigest, etc. } } ``` ### ReplayStore - Replay Protection The `ReplayStore` trait and `InMemoryReplayStore` implementation provide nonce-based replay protection and payment-identifier idempotency. ```rust use ledgerflow_x402::{InMemoryReplayStore, ReplayStore, ReplayFingerprint}; // Create store with custom TTL (5 minutes default) let mut store = InMemoryReplayStore::with_ttl(300_000); // Claim a nonce for replay protection let fingerprint = ReplayFingerprint { challenge_id: "challenge-1".to_string(), nonce: "unique-nonce-1".to_string(), request_hash: "sha256:request".to_string(), accepted_hash: "sha256:accepted".to_string(), }; match store.claim_nonce(fingerprint.clone(), 2_000) { Ok(()) => println!("Nonce claimed successfully"), Err(conflict) => println!("Replay detected: {:?}", conflict.existing), } // Second attempt with same nonce fails match store.claim_nonce(fingerprint, 2_100) { Ok(()) => unreachable!(), Err(_) => println!("Replay correctly detected"), } ``` ## Payment Routing ### Facilitator - Settlement Rail Routing The `Facilitator` type routes verified authorizations to the appropriate settlement rail (EVM, Exchange, Custodial, or Gateway) based on the payment subject. ```rust use ledgerflow_facilitator::{Facilitator, RailKind, RouteDecision}; use ledgerflow_core::{ PaymentRail, PaymentSubjectKind, PaymentSubjectRef, SignerRef, SigningAlgorithm, VerifiedAuthorization, }; // Create default facilitator with all rail adapters let facilitator = Facilitator::default(); // Create a verified authorization (from MerchantVerifier) let authorization = VerifiedAuthorization { merchant_id: "merchant-a".to_string(), tool_name: "web-search".to_string(), payment_subject: PaymentSubjectRef::new( PaymentSubjectKind::Caip10, "caip10:eip155:8453:0xabc123" // Base chain address ), payer: SignerRef::new(SigningAlgorithm::Ed25519, "agent-key"), warrant_digest: "sha256:warrant".to_string(), accepted_hash: "sha256:accepted".to_string(), request_hash: "sha256:request".to_string(), amount: 200, asset: "USDC".to_string(), scheme: "exact".to_string(), payee_id: "merchant-a".to_string(), rail: PaymentRail::Onchain, }; // Route to appropriate settlement rail match facilitator.route(&authorization) { Ok(decision) => { println!("Rail: {:?}", decision.rail); println!("Subject: {}", decision.subject_value); println!("Merchant flow preserved: {}", decision.merchant_flow_preserved); if let Some(quote) = decision.quote { println!("Estimated fee: {}", quote.estimated_fee); } } Err(e) => eprintln!("Routing failed: {}", e), } // Different subject kinds route to different rails: // - Caip10 -> RailKind::Evm // - ExchangeAccount -> RailKind::Exchange // - Opaque("custodial:...") -> RailKind::Custodial // - Opaque("gateway:...") -> RailKind::Gateway ``` ## CLI Tool ### ledgerflow-cli - Development Fixtures The CLI tool generates deterministic sample fixtures for local development and testing. ```bash # Generate a sample warrant fixture cargo run -p ledgerflow-cli -- sample-warrant # Output: # warrant_id=warrant-1 # merchant_id=merchant-a # tool_name=web-search # amount=200 # payment_subject=caip10:eip155:8453:0xabc123 # digest=sha256:... # Generate a sample x402 payment payload cargo run -p ledgerflow-cli -- sample-payment # Output: # challenge_id=challenge-1 # payment_identifier=payment-1 # accepted_amount=200 # warrant_digest=sha256:... # request_hash=sha256:... # accepted_hash=sha256:... # payment_subject=caip10:eip155:8453:0xabc123 ``` ## CBOR Serialization ### Encoding and Decoding Extensions Both challenge and authorization extensions support CBOR encoding for wire transport. ```rust use ledgerflow_x402::{LedgerFlowChallenge, LedgerFlowAuthorizationExtension}; // Encode challenge to CBOR let challenge = LedgerFlowChallenge { version: "lfx402/v1".to_string(), challenge_id: "challenge-1".to_string(), merchant_id: "merchant-a".to_string(), resource: "/api/search".to_string(), proof_freshness_ms: 60_000, required_subject_kinds: vec!["signer".to_string(), "payment_subject".to_string()], }; let encoded = challenge.encode_cbor().expect("encode"); let decoded = LedgerFlowChallenge::decode_cbor(&encoded).expect("decode"); assert_eq!(challenge, decoded); // Authorization extensions encode the same way let extension: LedgerFlowAuthorizationExtension = /* from payment payload */; let encoded = extension.encode_cbor().expect("encode"); let decoded = LedgerFlowAuthorizationExtension::decode_cbor(&encoded).expect("decode"); ``` ## Summary LedgerFlow provides a complete authorization framework for AI agents making payments through the x402 protocol. The typical integration flow involves: (1) humans issuing scoped warrants to AI agents with specific merchant, resource, tool, and payment constraints; (2) merchants generating 402 Payment Required responses with LedgerFlow challenges; (3) agents creating signed proofs binding warrants to specific requests and quotes; (4) merchants verifying authorization with replay protection; and (5) the Facilitator routing verified payments to appropriate settlement rails. The system supports multiple settlement rails including EVM chains (Base, Ethereum), centralized exchanges, custodial accounts, and traditional payment gateways. Key security features include Ed25519 cryptographic signatures, time-limited warrants with freshness windows, nonce-based replay protection, payment-identifier idempotency, and constraint verification covering merchants, resources, tools, payment amounts, assets, and rails. The modular architecture allows merchants to remain x402-only while receiving LedgerFlow authorization data through protocol extensions.