Tenzro Testnet is live. Get testnet TNZO

Visa Trusted Agent Protocol (TAP)

Visa Trusted Agent Protocol (TAP) enables AI agents to make purchases and access resources by signing HTTP requests using RFC 9421 HTTP Message Signatures. TAP provides a three-layer verification system: Agent Recognition (cryptographic identity), Consumer Recognition (human delegation), and Payment Container (settlement details). On Tenzro Network, TAP leverages TDIP for decentralized agent identities and TEE-backed key storage.

Overview

Visa TAP solves the core challenge of agentic commerce: how do merchants verify that an AI agent is authorized to make purchases on behalf of a human consumer? Traditional payment flows assume a human is directly interacting with the merchant, but in an AI-first world, agents act autonomously within delegated boundaries.

TAP introduces three distinct data layers:

  • Agent Recognition — Cryptographic proof of agent identity using RFC 9421 HTTP signatures with Ed25519 or RSA-PSS-SHA256
  • Consumer Recognition — JWT ID token proving the human consumer who delegated authority to the agent, including delegation scope
  • Payment Container — Payment method details (card token, stablecoin address, Tenzro settlement ID) for transaction execution

Architecture

Visa TAP places a CDN Proxy between the agent and merchant to perform verification before the request reaches the merchant's infrastructure:

┌──────────────┐
  AI Agent    
  (Tenzro)    
└──────┬───────┘
        HTTP POST /checkout
        Signature-Input: sig=(...);created=1711374000;nonce="abc123";keyid="did:tenzro:machine:agent123"
        Signature: sig=:SGVsbG8gV29ybGQ=:
        Authorization: Bearer <JWT ID token>
        X-Payment-Container: {"method":"TenzroSettlement","settlement_id":"..."}
       
┌─────────────────────────────────────────┐
  CDN Proxy (7-Stage Verification)      
  1. Header Extraction                  
  2. Key Retrieval (TDIP registry)      
  3. Timestamp Validation (±8 min)      
  4. Replay Prevention (nonce cache)    
  5. Domain Binding (@authority)        
  6. Cryptographic Verification         
  7. Result Construction                
└──────┬──────────────────────────────────┘
        Verified request
        X-Agent-DID: did:tenzro:machine:agent123
        X-Consumer-DID: did:tenzro:human:user456
        X-Verification-Status: verified
       
┌──────────────┐         ┌──────────────────┐
  Merchant    │────────>  Agent Registry  
  Server        Lookup   (TDIP on-chain) 
└──────────────┘         └──────────────────┘

The CDN Proxy sits at the edge, close to the agent, performing all cryptographic verification before forwarding the request to the merchant. This reduces latency and protects the merchant from processing invalid requests.

7-Stage Verification Pipeline

The CDN Proxy implements a rigorous 7-stage pipeline to verify agent requests:

  1. Header Extraction — Parse Signature-Input and Signature headers per RFC 9421. Extract keyid, algorithm, created timestamp, nonce, and covered components.
  2. Key Retrieval — Resolve the agent's public key from the TDIP registry using the keyid (DID format: did:tenzro:machine:*). Verify the agent identity is Active and not Suspended or Revoked.
  3. Timestamp Validation — Verify the created timestamp is within ±8 minutes of the current time. This prevents old signatures from being reused and accounts for clock skew across distributed systems.
  4. Replay Prevention — Check the nonce against an in-memory cache with 8-minute TTL. Reject duplicate nonces to prevent replay attacks where a valid signature is intercepted and resubmitted.
  5. Domain Binding — Verify the @authority component in the signature matches the request's Host header. This prevents signatures from being reused against different merchants (cross-site signature theft).
  6. Cryptographic Verification — Reconstruct the signature base string per RFC 9421 Section 2.5, then verify the signature using the agent's public key with Ed25519 or RSA-PSS-SHA256 algorithm via tenzro_crypto::signatures::verify().
  7. Result Construction — Build a VerificationResult with validity status, agent DID, algorithm used, and any error messages. Inject verification metadata into request headers for downstream processing.

Agent Recognition

Agent Recognition is the cryptographic proof of agent identity. The agent signs the HTTP request with its private key, and the CDN Proxy verifies the signature using the agent's public key from the TDIP registry.

use tenzro_payments::visa_tap::*;
use tenzro_crypto::Ed25519Signer;

#[derive(Debug, Clone)]
pub struct AgentRecognition {
    /// RFC 9421 signature input header
    pub signature_input: String,

    /// RFC 9421 signature header
    pub signature: String,

    /// Agent DID (did:tenzro:machine:*)
    pub key_id: String,

    /// Signing algorithm (Ed25519 or RsaPssSha256)
    pub algorithm: SigningAlgorithm,

    /// Unix timestamp when signature was created
    pub created_at: i64,

    /// Random nonce for replay prevention
    pub nonce: String,
}

// Example: Creating a signed request
async fn create_agent_request(
    agent_did: &str,
    agent_signer: &Ed25519Signer,
) -> Result<http::Request<String>, VisaTapError> {
    let request_body = json!({
        "items": [{"product_id": "prod_123", "quantity": 1}],
        "total_amount": 29.99,
        "currency": "USD"
    }).to_string();

    let request = http::Request::builder()
        .method("POST")
        .uri("https://merchant-xyz.tenzro.network/api/checkout")
        .header("content-type", "application/json")
        .body(request_body)?;

    // Sign the request with RFC 9421
    let signer_config = SignerConfig {
        key_id: agent_did.to_string(),
        algorithm: SigningAlgorithm::Ed25519,
        covered_components: vec![
            "@authority".into(),
            "@path".into(),
            "@method".into(),
            "content-type".into(),
            "content-digest".into(),
        ],
    };

    let signed_request = sign_request(
        request,
        agent_signer,
        signer_config,
    ).await?;

    Ok(signed_request)
}

Consumer Recognition

Consumer Recognition proves that a human consumer has delegated authority to the agent. This is critical for compliance with payment regulations that require proof of consumer consent.

The agent includes a JWT ID token in the Authorization header, issued by the consumer's identity provider (Tenzro Network for TDIP identities). The JWT contains:

  • sub — Consumer's DID (did:tenzro:human:*)
  • agent_did — Agent's DID (did:tenzro:machine:*)
  • delegation_scope_id — Reference to the delegation scope defining spending limits, allowed merchants, time bounds, etc.
  • iat/exp — Token issued/expiration timestamps
use tenzro_payments::visa_tap::*;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConsumerRecognition {
    /// JWT ID token from consumer's identity provider
    pub id_token: String,

    /// Delegation scope ID (from TDIP registry)
    pub delegation_scope_id: String,

    /// Consumer DID (did:tenzro:human:*)
    pub consumer_did: String,

    /// Agent DID (did:tenzro:machine:*)
    pub agent_did: String,
}

// Example: Verifying consumer delegation
async fn verify_consumer_delegation(
    id_token: &str,
    identity_registry: &IdentityRegistry,
) -> Result<ConsumerRecognition, VisaTapError> {
    // Decode JWT (signature verified by identity provider)
    let claims: JwtClaims = decode_jwt(id_token)?;

    // Resolve consumer identity
    let consumer = identity_registry
        .resolve_identity(&claims.sub)
        .await?;

    // Verify agent is in consumer's controlled_machines list
    if !consumer.controlled_machines.contains(&claims.agent_did) {
        return Err(VisaTapError::UnauthorizedAgent);
    }

    // Resolve delegation scope
    let scope = identity_registry
        .get_delegation_scope(&claims.delegation_scope_id)
        .await?;

    // Verify delegation hasn't expired
    if let Some(end_time) = scope.time_bound.end_time {
        if Utc::now().timestamp() > end_time {
            return Err(VisaTapError::DelegationExpired);
        }
    }

    Ok(ConsumerRecognition {
        id_token: id_token.to_string(),
        delegation_scope_id: claims.delegation_scope_id,
        consumer_did: claims.sub,
        agent_did: claims.agent_did,
    })
}

Payment Container

The Payment Container carries payment method details separate from the identity layers. This separation allows the same agent identity to use different payment methods across transactions.

use tenzro_payments::visa_tap::*;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PaymentContainer {
    /// Payment method variant
    pub method: PaymentMethod,

    /// Currency code (USD, EUR, TNZO)
    pub currency: String,

    /// Optional merchant category code
    pub mcc: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum PaymentMethod {
    /// Tenzro Network settlement
    TenzroSettlement {
        settlement_id: String,
        escrow_address: String,
    },

    /// Tokenized card (Visa token service)
    CardToken {
        token: String,
        token_requestor_id: String,
        cryptogram: String,
    },

    /// Stablecoin transfer (USDC, USDT)
    StablecoinTransfer {
        token_address: String,
        chain_id: u64,
        transfer_signature: String,
    },
}

// Example: Creating a payment container
fn create_payment_container(
    settlement_id: String,
) -> PaymentContainer {
    PaymentContainer {
        method: PaymentMethod::TenzroSettlement {
            settlement_id: settlement_id.clone(),
            escrow_address: format!("0x{}", hex::encode(settlement_id.as_bytes())),
        },
        currency: "TNZO".to_string(),
        mcc: Some("5734".to_string()), // Computer software stores
    }
}

Tenzro Integration

Tenzro Network provides a fully decentralized implementation of Visa TAP, replacing Visa's centralized agent registry with TDIP on-chain identities:

  • Agent Registry — TDIP IdentityRegistry on Tenzro Ledger replaces centralized Visa registry. All agent DIDs are on-chain and verifiable by anyone.
  • Key Storage — Agent private keys stored in TEE enclaves (Intel TDX, AMD SEV-SNP, AWS Nitro) or MPC wallets, never exposed to application code.
  • Settlement — Native settlement via Tenzro Ledger using TNZO token, eliminating card network fees for on-chain merchants.
  • Delegation Scopes — Fine-grained spending limits, merchant allowlists, time bounds, and protocol restrictions enforced on-chain via TDIP delegation scopes.
use tenzro_payments::visa_tap::*;
use tenzro_payments::PaymentProtocol;

// Initialize Visa TAP server with Tenzro components
let visa_tap_server = VisaTapServer::new(
    identity_registry.clone(),
    settlement_engine.clone(),
    nonce_cache.clone(),
);

// Handle incoming agent request
async fn handle_agent_checkout(
    request: http::Request<String>,
    visa_tap_server: &VisaTapServer,
) -> Result<CheckoutResponse, VisaTapError> {
    // Verify RFC 9421 signature (7-stage pipeline)
    let verification_result = visa_tap_server
        .verify_agent_signature(&request)
        .await?;

    if !verification_result.valid {
        return Err(VisaTapError::InvalidSignature);
    }

    // Extract consumer JWT
    let auth_header = request.headers()
        .get("authorization")
        .ok_or(VisaTapError::MissingConsumerToken)?;

    let id_token = auth_header
        .to_str()?
        .strip_prefix("Bearer ")
        .ok_or(VisaTapError::InvalidAuthHeader)?;

    // Verify consumer delegation
    let consumer_recognition = visa_tap_server
        .verify_consumer_delegation(id_token)
        .await?;

    // Extract payment container
    let payment_header = request.headers()
        .get("x-payment-container")
        .ok_or(VisaTapError::MissingPaymentContainer)?;

    let payment_container: PaymentContainer =
        serde_json::from_str(payment_header.to_str()?)?;

    // Execute settlement
    let settlement_result = visa_tap_server
        .execute_settlement(
            &verification_result.agent_did,
            &consumer_recognition.consumer_did,
            &payment_container,
            29.99, // amount
        )
        .await?;

    Ok(CheckoutResponse {
        status: "confirmed".to_string(),
        transaction_id: settlement_result.transaction_id,
        agent_did: verification_result.agent_did,
        consumer_did: consumer_recognition.consumer_did,
    })
}

MCP Usage

AI agents can create Visa TAP payment challenges programmatically using Tenzro's Model Context Protocol (MCP) server:

# Create a Visa TAP payment challenge
curl -X POST https://mcp.tenzro.network/mcp \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "tools/call",
    "params": {
      "name": "create_payment_challenge",
      "arguments": {
        "protocol": "visa-tap",
        "amount": "29.99",
        "currency": "USD",
        "merchant_id": "merchant_123",
        "resource_url": "https://merchant-xyz.tenzro.network/api/checkout",
        "requires_consumer_jwt": true,
        "allowed_payment_methods": ["TenzroSettlement", "CardToken"]
      }
    },
    "id": 1
  }'

# Response
{
  "jsonrpc": "2.0",
  "result": {
    "content": [{
      "type": "text",
      "text": "{\"challenge_id\":\"ch_abc123\",\"protocol\":\"visa-tap\",\"amount\":\"29.99\",\"currency\":\"USD\",\"merchant_id\":\"merchant_123\",\"resource_url\":\"https://merchant-xyz.tenzro.network/api/checkout\",\"requires_consumer_jwt\":true,\"allowed_payment_methods\":[\"TenzroSettlement\",\"CardToken\"],\"expires_at\":1711377600}"
    }]
  },
  "id": 1
}

Security Considerations

  1. Timestamp Validation Window — The ±8 minute window balances security and usability. Narrower windows increase replay protection but cause issues with clock skew; wider windows increase risk.
  2. Nonce Replay Protection — Each request must have a unique nonce. The 8-minute TTL prevents unbounded memory growth while covering the timestamp validation window.
  3. Domain Binding Prevents Cross-Site Attacks — The @authority component ensures a signature for merchant A cannot be reused against merchant B, even if the agent is authorized for both.
  4. Content-Digest Integrity — The content-digest header (SHA-256 hash of request body) prevents tampering with payment amounts or item quantities after signature creation.
  5. TEE Key Storage — Agent private keys are stored in hardware-backed Trusted Execution Environments, preventing extraction even if the agent's host system is compromised.

Next Steps