Protocol¶
The LedgerFlow protocol defines a warrant format and proof-of-authorization model that extends x402 with agent-level payment authorization. The protocol is intentionally narrow: one warrant format, one proof format, one x402 extension namespace.
Design Philosophy¶
LedgerFlow borrows design lessons from Zanzibar and SpiceDB without adopting their full architecture:
- Small public API — the merchant asks one question: "Is this agent authorized to use this quote?"
- Typed constraints — context-aware checks without a generic expression engine
- Time-bounded authority — expiration is a first-class citizen
- Future extension points — consistency tokens (Zookies/ZedTokens) reserved for v2
LedgerFlow deliberately avoids:
- General relationship graph stores
- Recursive userset traversal
- Schema compilation
- Generic expression evaluation
- Multi-hop object graph traversal on the hot path
The Warrant¶
A warrant is a self-contained, cryptographically signed authorization token.
Structure¶
| Field | Description |
|---|---|
version |
Protocol version, explicit and versioned from day one |
warrant_id |
Unique identifier for the warrant |
issuer |
The entity that issued the warrant (signer identity) |
subject_signer |
The agent's signer identity (verification key) |
payment_subjects |
Settlement identities the agent may use |
audience |
Which merchants the warrant applies to |
not_before_ms |
Validity window start (Unix milliseconds) |
expires_at_ms |
Validity window end (Unix milliseconds) |
delegation |
Whether and how deep this warrant can be sub-delegated |
constraints |
Typed constraint list |
metadata |
Issuer-defined metadata |
signature |
Cryptographic signature over canonical bytes |
Identity Model¶
LedgerFlow separates two identity layers:
Signer Identity — used for cryptographic verification:
pub struct SignerRef {
pub alg: SigningAlgorithm, // Ed25519 or Secp256k1
pub public_key: Vec<u8>,
pub key_id: Option<String>,
}
Payment Subject — used for settlement, opaque to the merchant:
pub struct PaymentSubjectRef {
pub kind: PaymentSubjectKind, // Caip10, FacilitatorAccount, ExchangeAccount, Opaque
pub value: String,
}
Examples: caip10:eip155:8453:0xabc..., binance:uid:12345678, okx:subacct:agent-alpha.
This separation ensures merchants remain rail-agnostic.
Audience Scope¶
Prefer merchant IDs over hostnames when a stable identifier exists.
Delegation Policy¶
Depth is sufficient for v1. No graph-style recursive delegation.
The Proof¶
The proof-of-authorization demonstrates that the agent holds the warrant and is using it for a specific transaction.
Proof Binding¶
The proof must bind to:
- the LedgerFlow challenge ID
- the warrant digest
- the exact selected x402
acceptedobject - the current HTTP request
- the signer identity
- a freshness tuple (timestamp + nonce)
Canonical Request Hash¶
request_hash = SHA256(
UPPER(method) || "\n" ||
LOWER(authority) || "\n" ||
path_and_query_exact || "\n" ||
SHA256(body_bytes)
)
Intentionally simple. Transport headers excluded unless part of x402 payload.
Accepted Hash¶
accepted_hash is the SHA-256 digest of the canonical JSON serialization of the selected x402 accepted object.
This is the critical link that keeps LedgerFlow complementary to x402. The merchant quote is still x402. LedgerFlow only authorizes that quote.
Proof Preimage¶
proof_preimage = CBOR({
domain: "ledgerflow-pop/v1",
challenge_id: ...,
warrant_digest: ...,
accepted_hash: ...,
request_hash: ...,
created_at_ms: ...,
nonce: ...,
signer_key: ...
})
The proof signature is ED25519_SIGN(signer_key, SHA256(proof_preimage)).
Replay Protection¶
created_at_msmust fall within a 60-second verification window- Replay key:
challenge_id + noncewith atomic TTL insert - Storage semantics:
SET key value NX PX ttl_ms(Redis) - Never use non-atomic check-then-set
Idempotent Retries¶
- Same proof + same payment identifier → return cached result
- Same nonce with different request hash or accepted hash → reject as replay
Warrant Transport¶
Transport Modes¶
- Inline — warrant bytes sent on first use
- Digest reference — after merchant has cached the warrant
Rules:
inline_b64present → merchant verifies and caches- Only
digestpresent → merchant looks up cached warrant - Unknown digest → merchant rejects, asks client to resend inline
Cache Key¶
Cache by warrant_digest, never by signer alone.
Precise, portable, easy to implement.
What LedgerFlow Validates¶
The merchant-side verifier checks:
- Warrant signature validity
- Proof signature validity
- Warrant validity window (not_before / expires_at)
- Merchant scope against audience constraint
- Resource scope against resource constraint
- Tool scope against tool constraint
- Asset and amount constraints
- Delegation constraints
- Proof binds to exact selected x402
acceptedobject - Proof binds to current HTTP request
- Proof is not replayed
Protocol Limits¶
| Limit | Value |
|---|---|
| Max delegation depth | 64 |
| Max warrant TTL | 90 days |
| Max warrant size | 8 KB |
| Max constraints per warrant | 32 |
| Proof freshness window | 60 seconds |
| Recommended nonce length | 16 bytes |
See Also¶
- Core Concepts — Detailed concept walkthrough
- Constraints — Full constraint type reference
- x402 Integration — How LedgerFlow extends x402
- Security — Threat model and protection mechanisms