This repository contains a Rust implementation of the Transaction Authorization Protocol (TAP), a decentralized protocol for securely authorizing blockchain transactions before they are submitted on-chain. TAP-RS targets payment-related use cases, Travel Rule compliance, and secure transaction coordination.
New in v0.6.0:
- External decision executable support for pluggable authorization workflows
- Poll mode for decision management via MCP tools
tap-clicommand-line interface for full TAP agent operationsdid:webDID document hosting intap-http
TAP-RS is organized as a Rust workspace with multiple crates:
- tap-msg: Core message processing for TAP with integrated DIDComm support
- tap-msg-derive: Procedural derive macro for automatic TAP message trait implementation
- tap-agent: TAP agent functionality and identity management
- tap-caip: Implementation of Chain Agnostic Identifier Standards
- tap-ivms101: Complete IVMS 101.2023 implementation for Travel Rule compliance
- tap-node: TAP node orchestration with per-agent storage isolation, multi-recipient message delivery, and Travel Rule processor
- tap-http: HTTP DIDComm server implementation
- tap-cli: Command-line interface for TAP Agent operations
- tap-wasm: WebAssembly bindings with DIDComm SecretsResolver integration
- tap-ts: TypeScript SDK with full DIDComm v2 support (npm: @taprsvp/agent)
- tap-mcp: Model Context Protocol server for AI/LLM integration
The Transaction Authorization Protocol (TAP) adds a secure authorization layer to blockchain transactions, enabling participants to:
- Verify transaction details before settlement
- Exchange required compliance information privately
- Prevent sending to wrong addresses or incorrect amounts
- Implement multi-party authorization workflows
- Conduct Travel Rule compliance checks off-chain
TAP-RS implements this protocol with a focus on:
- Security: End-to-end encrypted messaging via DIDComm v2
- Interoperability: Support for multiple blockchains through CAIP standards
- Scalability: Per-agent storage isolation with multi-recipient message delivery
- Compliance: Full DIDComm specification adherence for multi-party transactions with IVMS101 Travel Rule support
- Extensibility: Modular design allowing custom integrations
- Cross-Platform: Native support and WebAssembly for browser environments
- Travel Rule: Automatic IVMS101 data generation and attachment for regulatory compliance
This project has successfully implemented all core TAP message types and flows as specified in the TAIPs (Transaction Authorization Protocol Improvement Proposals). The codebase is feature-complete for standard TAP use cases.
- Rust 1.71.0 or later (includes Cargo)
The quickest way to get started is to install the CLI tools. No need to clone the repository.
# Install all CLI tools at once
cargo install tap-cli tap-http tap-agent
# Or install only what you need:
cargo install tap-cli # Full-featured TAP CLI (agents, transactions, customers, DIDs)
cargo install tap-http # HTTP DIDComm server + payment simulator
cargo install tap-agent # Low-level DID/key management + message packing
cargo install tap-mcp # MCP server for AI/LLM integrationgit clone https://github.com/TransactionAuthorizationProtocol/tap-rs.git
cd tap-rs
cargo install --path tap-cli
cargo install --path tap-http
cargo install --path tap-agent
cargo install --path tap-mcptap-cli --help
tap-http --help
tap-agent-cli --help
tap-mcp --help-
tap-cli: Full-featured CLI for TAP Agent operations
# Create an agent (auto-generates a DID) tap-cli agent create --label "my-vasp" # Generate a DID and save it tap-cli did generate --save --label "primary" # Create a transfer tap-cli transaction transfer \ --asset eip155:1/erc20:0xdac17f958d2ee523a2206206994597c13d831ec7 \ --amount 100.0 \ --originator did:key:z6MkOriginator... \ --beneficiary did:key:z6MkBeneficiary... # Authorize a transaction tap-cli action authorize --transaction-id <TX_ID> # Create a payment request tap-cli transaction payment --amount 99.99 --merchant did:key:z6Mk... --currency USD # Request an exchange (TAIP-18) tap-cli transaction exchange --from-assets USD --to-assets eip155:1/erc20:0x... \ --from-amount 1000 --requester did:key:z6Mk... # Manage agents on a transaction (TAIP-5) tap-cli agent-mgmt add-agents --transaction-id <TX_ID> --agents '[...]' # Manage customers and generate IVMS101 data tap-cli customer create --customer-id did:key:z6Mk... --profile '{"@type":"Person","name":"Alice"}' tap-cli customer ivms101 --customer-id did:key:z6Mk...
See the tap-cli README for full documentation.
-
tap-agent-cli: Low-level DID and key management plus DIDComm message packing
# Generate a new did:key with Ed25519 tap-agent-cli generate # List stored keys tap-agent-cli keys list # Pack a plaintext DIDComm message (supports signed, authcrypt, and anoncrypt modes) tap-agent-cli pack --input message.json --output packed.json --mode signed tap-agent-cli pack --input message.json --output packed.json --mode authcrypt --recipient did:key:z6Mk... tap-agent-cli pack --input message.json --output packed.json --mode anoncrypt --recipient did:key:z6Mk... # Unpack a signed or encrypted DIDComm message tap-agent-cli unpack --input packed.json --output unpacked.json # Pack/unpack with specific key selection tap-agent-cli pack --input message.json --output packed.json --mode signed --key did:key:z6Mk... tap-agent-cli unpack --input packed.json --output unpacked.json --key did:key:z6Mk...
-
tap-http: Run a TAP HTTP server for DIDComm messaging
# Start a server with default settings (auto-approve all decisions) tap-http # Start with poll mode (decisions logged to DB for external systems) tap-http --decision-mode poll # Start with external decision executable tap-http --decision-exec /path/to/decision-handler
-
tap-payment-simulator: Test TAP payment flows against a server
# Send a test payment flow to a server tap-payment-simulator --url http://localhost:8000/didcomm --did <server-agent-did>
-
tap-mcp: MCP server for AI/LLM integration (Claude Desktop, etc.)
# Run as MCP server (communicates via stdin/stdout) tap-mcp # With custom settings tap-mcp --tap-root /path/to/data --debug
See individual tool READMEs for detailed usage instructions.
Add any TAP crate to your project:
cargo add tap-msg # Core message types and validation
cargo add tap-agent # Agent identity, key management, message packing
cargo add tap-caip # CAIP-2/10/19 blockchain identifier parsing
cargo add tap-node # Node orchestration, storage, event handling
cargo add tap-ivms101 # IVMS 101.2023 Travel Rule data structuresnpm install @taprsvp/agentSee the tap-ts README for complete documentation.
git clone https://github.com/TransactionAuthorizationProtocol/tap-rs.git
cd tap-rs
cargo build
cargo test- Complete TAP Implementation: Support for all TAP message types (Transfer, Payment, Connect, Escrow, Exchange, Quote, Authorize, Reject, Settle, Cancel, Revert, AddAgents, RemoveAgent, ReplaceAgent, UpdatePolicies, and more)
- DIDComm v2 Integration: Secure, encrypted messaging with authenticated signatures
- Chain Agnostic Identifiers: Implementation of CAIP-2 (ChainID), CAIP-10 (AccountID), and CAIP-19 (AssetID)
- Settlement Address Flexibility: Support for both blockchain (CAIP-10) and traditional payment systems (PayTo URI)
- Multiple DID Methods: Support for did:key, did:web, did:pkh, and more
- Travel Rule Compliance: Full IVMS 101.2023 implementation with automatic data attachment
- Customer Data Management: Automatic extraction and storage of party information from TAP messages
- Enhanced Metadata Support: Schema.org Organization fields for Agents/Parties and Product attributes for invoices
- Command-line Tools: Utilities for DID generation, resolution, and key management
- Modular Agent Architecture: Flexible identity and cryptography primitives
- High-Performance Message Routing: Efficient node implementation for high-throughput environments
- External Decision Support: Pluggable decision modes (auto, poll, exec) for authorization, settlement, and policy decisions
- HTTP and WebSocket Transport: Multiple communication options with robust error handling
- WASM Compatibility: Run in browsers and Node.js via WebAssembly
- TypeScript API: Developer-friendly TypeScript wrapper for web integrations
- Comprehensive Validation: All messages validated against TAP specifications
- Generic Typed Messages: Compile-time type safety with
PlainMessage<Transfer>while maintaining backward compatibility - Derive Macro: Automatic implementation of
TapMessageandMessageContexttraits with#[derive(TapMessage)] - Persistent Storage: SQLite-based storage with automatic migrations providing:
- Transaction tracking for Transfer and Payment messages
- Complete audit trail of all messages for compliance and debugging
- Message delivery tracking with status monitoring, retry counts, and error logging
- Customer profiles with Schema.org JSON-LD format and IVMS101 caching
use tap_msg::message::types::{Transfer, Participant};
use tap_msg::message::tap_message_trait::TapMessageBody;
use tap_caip::AssetId;
use std::collections::HashMap;
use std::str::FromStr;
// Create a Transfer message body
let asset = AssetId::from_str("eip155:1/erc20:0xdac17f958d2ee523a2206206994597c13d831ec7")?;
let originator = Participant {
id: "did:example:sender".to_string(),
role: Some("originator".to_string()),
};
let beneficiary = Participant {
id: "did:example:receiver".to_string(),
role: Some("beneficiary".to_string()),
};
let transfer = Transfer {
asset,
originator,
beneficiary: Some(beneficiary),
amount: "100.0".to_string(),
agents: vec![],
settlement_id: None,
memo: Some("Test transfer".to_string()),
metadata: HashMap::new(),
};
// Create a DIDComm message from the transfer
let message = transfer.to_didcomm_with_route(
Some("did:example:sender"),
["did:example:receiver"].iter().copied()
)?;See the tap-msg README for more detailed examples.
TAP-RS now supports both blockchain and traditional payment settlement addresses:
use tap_msg::settlement_address::{SettlementAddress, PayToUri};
use tap_msg::Payment;
// Traditional payment system addresses using PayTo URI (RFC 8905)
let iban_address = SettlementAddress::from_string(
"payto://iban/DE75512108001245126199".to_string()
)?;
let ach_address = SettlementAddress::from_string(
"payto://ach/122000247/111000025".to_string()
)?;
// Blockchain addresses using CAIP-10
let eth_address = SettlementAddress::from_string(
"eip155:1:0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb".to_string()
)?;
// Payment with fallback settlement addresses
let payment = Payment::builder()
.amount("100.00".to_string())
.currency_code("USD".to_string())
.merchant(Party::new("did:example:merchant"))
.fallback_settlement_addresses(vec![
iban_address, // Primary: traditional bank transfer
eth_address, // Fallback: Ethereum address
])
.build();Agents, Parties, and LineItems now support rich metadata:
use tap_msg::{Agent, LineItem};
// Agent with Organization metadata
let agent = Agent::new("did:example:agent", "PaymentProcessor", "did:example:merchant")
.with_name("Example Payment Services")
.with_url("https://example.com")
.with_email("[email protected]")
.with_telephone("+1-555-0100");
// LineItem with Product attributes
let line_item = LineItem::builder()
.id("item-001".to_string())
.description("Premium Coffee Beans".to_string())
.quantity(2.0)
.unit_price(25.99)
.line_total(51.98)
.name("Colombian Arabica Premium Blend".to_string())
.image("https://example.com/products/coffee.jpg".to_string())
.url("https://example.com/products/coffee".to_string())
.build();TAP-RS now supports generic typed messages for compile-time type safety while maintaining 100% backward compatibility:
use tap_msg::{PlainMessage, Transfer, Participant};
use tap_agent::{Agent, TapAgent};
// Create a strongly-typed message
let transfer = Transfer {
asset: "eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48".parse()?,
originator: Participant::new("did:example:alice"),
beneficiary: Some(Participant::new("did:example:bob")),
amount: "100".to_string(),
// ... other fields
};
// Type-safe message construction
let typed_msg = PlainMessage::new_typed(transfer, "did:example:alice")
.with_recipient("did:example:bob")
.with_thread_id(Some("payment-123".to_string()));
// Send with compile-time type checking
let (packed, results) = agent.send_typed(typed_msg, true).await?;
// Receive with type safety
let received: PlainMessage<Transfer> = agent.receive_typed(&packed).await?;
println!("Amount: {}", received.body.amount);
// Backward compatibility - existing code unchanged
let plain_msg: PlainMessage = serde_json::from_str(json_data)?;
// This is now PlainMessage<Value> due to default type parameterSee GENERIC_PLAINMESSAGE.md for complete documentation.
TAP-RS provides a procedural derive macro that automatically implements TapMessage and MessageContext traits:
use tap_msg::TapMessage;
use tap_msg::message::{Participant, TapMessageBody};
use serde::{Serialize, Deserialize};
#[derive(Debug, Clone, Serialize, Deserialize, TapMessage)]
pub struct CustomMessage {
#[tap(participant)]
pub sender: Participant,
#[tap(participant)]
pub receiver: Option<Participant>,
#[tap(participant_list)]
pub validators: Vec<Participant>,
#[tap(transaction_id)]
pub transaction_id: String,
pub data: String,
}
// The macro automatically provides:
// - thread_id() -> transaction_id
// - get_all_participants() -> extracts all participant DIDs
// - participants() -> returns &Participant references
// - transaction_context() -> creates context with ID and typeSee the tap-msg README for detailed documentation.
use tap_agent::agent::{Agent, DefaultAgent};
use tap_agent::config::AgentConfig;
use tap_agent::crypto::{DefaultMessagePacker, BasicSecretResolver};
use tap_agent::did::DefaultDIDResolver;
use std::sync::Arc;
// Configure the agent
let config = AgentConfig::new("did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK".to_string());
// Set up components
let did_resolver = Arc::new(DefaultDIDResolver::new());
let secret_resolver = Arc::new(BasicSecretResolver::new());
let message_packer = Arc::new(DefaultMessagePacker::new(did_resolver, secret_resolver));
// Create the agent
let agent = DefaultAgent::new(config, message_packer);See the tap-agent README for more detailed examples.
TAP-RS provides comprehensive tools for DID generation and key management:
# Install the tap-agent CLI
cargo install tap-agent
# Generate a did:key with Ed25519 key type
tap-agent-cli generate --method key --key-type ed25519 --output did-document.json --key-output private-key.json
# Generate a did:web for a specific domain
tap-agent-cli generate --method web --domain example.com --output web-did.json
# Look up and resolve a DID to its DID Document
tap-agent-cli lookup did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK
# Look up a DID and save the result to a file
tap-agent-cli lookup did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK --output did-document.json
# Key management operations
tap-agent-cli keys list
tap-agent-cli keys view did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK
tap-agent-cli keys set-default did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doKFor browser and Node.js applications, use the @taprsvp/agent npm package:
npm install @taprsvp/agentimport { TapAgent } from '@taprsvp/agent';
// Create an agent
const agent = await TapAgent.create({ keyType: 'Ed25519' });
console.log('Agent DID:', agent.did);
// Create and send a transfer
const transfer = await agent.createMessage('Transfer', {
amount: '100.00',
asset: 'eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
originator: {
'@id': agent.did,
'@type': 'https://schema.org/Person',
name: 'Alice Smith'
},
beneficiary: {
'@id': recipientDid,
'@type': 'https://schema.org/Person',
name: 'Bob Jones'
},
agents: [] // Add any agents involved in the transaction
});
const packed = await agent.pack(transfer);
// Send packed.message over your transport...The TypeScript SDK provides:
- π Full DIDComm v2 support with Veramo compatibility
- π Lightweight: 3.72KB gzipped + 272KB WASM
- π Multiple key types (Ed25519, P-256, secp256k1)
- π Browser and Node.js support
- π¦ Zero runtime dependencies
See the tap-ts README for complete documentation.
- VASP-to-VASP Transfers: Exchanges and custodians can coordinate transfers with automatic Travel Rule compliance
- Travel Rule Compliance: Automatic IVMS101 data generation and exchange for regulatory requirements
- Self-Custody Verification: Wallets can verify transaction details before settlement
- Multi-Party Authorization: Complex transfers requiring approval from multiple entities
- Cross-Chain Coordination: Consistent messaging across different blockchain networks
- Compliance Automation: Streamline compliance workflows with secure messaging and automatic data extraction
- Customer Data Management: Automatic extraction and storage of party information for KYC/AML
Comprehensive documentation for TAP-RS is available in the docs directory:
- Getting Started - Learn how to set up and start using TAP-RS
- Implementing TAP Flows - Guide to implementing various TAP message flows
- Security Best Practices - Guidelines for securing your implementation
- WASM Integration - Using TAP-RS in browser and Node.js environments
- Travel Rule Implementation - Complete guide to Travel Rule compliance in TAP Node
- Customer Management - Automatic customer data extraction and management
- IVMS101 Data Model - Full IVMS 101.2023 implementation
- Complete Transfer Flow - End-to-end example integrating multiple TAP-RS components
- Travel Rule Flow - Complete example of Travel Rule compliance with IVMS101
cargo build # Build all crates
cargo test # Run all tests
cargo test --package tap-msg # Run tests for a specific crate
cargo bench # Run benchmarks
cargo fmt # Format code
cargo clippy # Lint codeThe tap-agent-cli tool provides commands for packing and unpacking DIDComm messages:
# Install the tap-agent CLI
cargo install tap-agent
# Pack a plaintext message to a signed DIDComm message
tap-agent-cli pack --input message.json --output packed.json --mode signed
# Pack using authenticated encryption (requires recipient DID)
tap-agent-cli pack --input message.json --output packed.json --mode authcrypt --recipient did:key:z6Mk...
# Pack using anonymous encryption (requires recipient DID)
tap-agent-cli pack --input message.json --output packed.json --mode anoncrypt --recipient did:key:z6Mk...
# Use a specific key for packing (otherwise the default key is used)
tap-agent-cli pack --input message.json --output packed.json --mode signed --key did:key:z6Mk...
# Unpack a DIDComm message (works with signed, authcrypt, or anoncrypt messages)
tap-agent-cli unpack --input packed.json --output unpacked.json
# Unpack using a specific key (otherwise all available keys are tried)
tap-agent-cli unpack --input packed.json --output unpacked.json --key did:key:z6Mk...The input message.json should be a plain JSON object following the DIDComm message format:
{
"id": "1234567890",
"type": "https://tap.rsvp/schema/1.0#Transfer",
"from": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
"to": ["did:key:z6MkrJVSYwmQgxBBCnZWuYpKSJ4qWRhWGsc9hhsVf43yirpL"],
"body": {
"asset": "eip155:1/erc20:0xdac17f958d2ee523a2206206994597c13d831ec7",
"originator": {
"@id": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
"role": "originator"
},
"beneficiary": {
"@id": "did:key:z6MkrJVSYwmQgxBBCnZWuYpKSJ4qWRhWGsc9hhsVf43yirpL",
"role": "beneficiary"
},
"amount": "100.0",
"agents": []
}
}The TAP HTTP server provides a DIDComm messaging endpoint for the TAP protocol:
# Install the server
cargo install tap-http
# Run with default settings (creates ephemeral agent)
tap-http
# Run with custom settings
tap-http --host 0.0.0.0 --port 8080 --endpoint /didcomm --logs-dir /var/log/tap
# Use a stored key from tap-agent-cli
tap-http --use-stored-keyYou can test the server using the payment simulator:
# Install the simulator
cargo install tap-http
# Run a test payment flow (using the DID printed when starting the server)
tap-payment-simulator --url http://localhost:8000/didcomm --did did:key:z6Mk...This project is licensed under the MIT License - see the LICENSE-MIT file for details.