| Crates.io | tap-http |
| lib.rs | tap-http |
| version | 0.4.0 |
| created_at | 2025-05-12 08:35:35.172266+00 |
| updated_at | 2025-06-17 07:36:07.763404+00 |
| description | HTTP server for the Transaction Authorization Protocol (TAP) |
| homepage | |
| repository | https://github.com/TransactionAuthorizationProtocol/tap-rs |
| max_upload_size | |
| id | 1670215 |
| size | 248,808 |
HTTP DIDComm server implementation for the Transaction Authorization Protocol (TAP), providing secure message exchange via standard HTTP endpoints.
use tap_http::{TapHttpConfig, TapHttpServer};
use tap_node::{NodeConfig, TapNode};
use tap_agent::DefaultAgent;
use std::time::Duration;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create a TAP Agent - either load from stored keys or create ephemeral
let agent = DefaultAgent::from_stored_or_ephemeral(None, true);
println!("Server using agent with DID: {}", agent.get_agent_did());
// Create a TAP Node configuration with storage
let mut node_config = NodeConfig::default();
node_config.storage_path = Some("tap-http.db".into());
// Create a TAP Node for message processing
let mut node = TapNode::new(node_config);
node.init_storage().await?;
node.register_agent(Arc::new(agent)).await?;
// Configure the HTTP server with custom settings
let config = TapHttpConfig {
host: "0.0.0.0".to_string(), // Listen on all interfaces
port: 8080, // Custom port
didcomm_endpoint: "/api/didcomm".to_string(), // Custom endpoint path
request_timeout_secs: 60, // 60-second timeout for requests
..TapHttpConfig::default()
};
// Create and start the server
let mut server = TapHttpServer::new(config, node);
server.start().await?;
// Wait for shutdown signal
tokio::signal::ctrl_c().await?;
// Gracefully stop the server
server.stop().await?;
Ok(())
}
The main endpoint for receiving DIDComm messages. The endpoint path is configurable (default is /didcomm):
POST /didcomm HTTP/1.1
Host: example.com
Content-Type: application/didcomm-encrypted+json
eyJhbGciOiJFQ0RILUVTK0EyNTZLVyIsImFwdiI6InpRbFpBQ0pZVFpnZUNidFhvd0xkX18zdWNmQmstLW0za2NXekQyQ0kiLCJlbmMiOiJBMjU2R0NNIiwiZXBrIjp7ImNydiI6IlAtMjU2Iiwia3R5IjoiRUMiLCJ4IjoiZ1RxS2ZaQk45bXpLNHZhX1l2TXQ2c0VkNEw0X1Q3aS1PVmtvMGFaVHUwZyIsInkiOiJQOHdyeFFDYmFZckdPdTRXWGM0R05WdWkyLWVpbEhYNUNHZXo5dk9FX2ZrIn0sInByb3RlY3RlZCI6ImV5SmtjeUk2ZXlKQmJXRjZiMjVCZEhSeWFXSmhkR1ZVWVdkZmMxOGdUVVZCTUZVMVRFMVlXRkF5UmtkRWFEVmFkejA5SW4xOSIsInR5cCI6ImFwcGxpY2F0aW9uL2RpZGNvbW0tZW5jcnlwdGVkK2pzb24ifQ...
When unpacked, the above message would contain a TAP protocol message like:
{
"id": "1234567890",
"type": "https://tap.rsvp/schema/1.0#transfer",
"body": {
"amount": "100.00",
"asset": "eip155:1/erc20:0x6b175474e89094c44da98b954eedeac495271d0f",
"transaction_id": "tx-123456"
},
"from": "did:example:sender",
"to": ["did:example:recipient"],
"created_time": 1620000000
}
Health check endpoint for monitoring system availability:
GET /health HTTP/1.1
Host: example.com
Response:
HTTP/1.1 200 OK
Content-Type: application/json
{
"status": "ok",
"version": "0.1.0"
}
For successfully processed messages:
HTTP/1.1 200 OK
Content-Type: application/json
{
"status": "success",
"message": "Message received and processed"
}
For validation and other errors:
HTTP/1.1 400 Bad Request
Content-Type: application/json
{
"status": "error",
"error": {
"type": "validation_error",
"message": "Unsupported message type: https://didcomm.org/basicmessage/2.0/message, expected TAP protocol message"
}
}
The server performs several validation steps on incoming messages:
Message Unpacking:
DID Verification:
Protocol Validation:
TAP Node Processing:
The server can be configured with the following options in TapHttpConfig:
pub struct TapHttpConfig {
/// The host address to bind to.
pub host: String,
/// The port to bind to.
pub port: u16,
/// The endpoint path for receiving DIDComm messages.
pub didcomm_endpoint: String,
/// Optional rate limiting configuration.
pub rate_limit: Option<RateLimitConfig>,
/// Optional TLS configuration.
pub tls: Option<TlsConfig>,
/// Default timeout for outbound HTTP requests in seconds.
pub request_timeout_secs: u64,
/// Event logger configuration (for tracking server events).
pub event_logger: Option<EventLoggerConfig>,
/// CORS configuration for cross-origin requests.
pub cors: Option<CorsConfig>,
}
Enable HTTPS with TLS certificates:
let config = TapHttpConfig {
// ...other settings
tls: Some(TlsConfig {
cert_path: "/path/to/cert.pem".to_string(),
key_path: "/path/to/key.pem".to_string(),
}),
// ...
};
Configure event logging to track server activity:
use tap_http::event::{EventLoggerConfig, LogDestination};
let config = TapHttpConfig {
// ...other settings
event_logger: Some(EventLoggerConfig {
destination: LogDestination::File {
path: "./logs/tap-http.log".to_string(), // Default location
max_size: Some(10 * 1024 * 1024), // 10 MB
rotate: true, // Enable rotation
},
structured: true, // Use JSON format
log_level: log::Level::Info,
include_payloads: false, // Don't log sensitive message payloads
}),
// ...
};
The event logging system captures:
Custom event subscribers can also be implemented:
use std::sync::Arc;
use async_trait::async_trait;
use tap_http::event::{EventSubscriber, HttpEvent};
struct CustomEventHandler;
#[async_trait]
impl EventSubscriber for CustomEventHandler {
async fn handle_event(&self, event: HttpEvent) {
// Custom handling of events
println!("Event: {:?}", event);
}
}
// After creating the server
let custom_handler = Arc::new(CustomEventHandler);
server.event_bus().subscribe(custom_handler);
Configure rate limiting to prevent abuse:
let config = TapHttpConfig {
// ...other settings
rate_limit: Some(RateLimitConfig {
max_requests: 100, // Maximum requests per window
window_secs: 60, // Time window in seconds
}),
// ...
};
The package includes an HTTP client for sending DIDComm messages to other endpoints:
use tap_http::DIDCommClient;
use tap_agent::{Agent, DefaultAgent};
use tap_msg::message::Transfer;
// Create a TAP Agent
let (agent, agent_did) = DefaultAgent::new_ephemeral()?;
// Create client with custom timeout
let client = DIDCommClient::new(std::time::Duration::from_secs(30));
// Create a message
let transfer = Transfer {
// Message fields...
transaction_id: uuid::Uuid::new_v4().to_string(),
};
// Pack a message using the agent
let recipient_did = "did:web:example.com";
let (packed_message, _) = agent.send_message(&transfer, vec![recipient_did], false).await?;
// Send the packed message to a recipient's endpoint
let response = client.deliver_message(
"https://recipient.example.com/didcomm",
&packed_message
).await?;
// Process the response
println!("Delivery status: {}", response.status());
You can also use the built-in delivery functionality of the TAP Agent:
// The third parameter (true) enables automatic delivery
let (_, delivery_results) = agent.send_message(&transfer, vec![recipient_did], true).await?;
// Check delivery results
for result in delivery_results {
if let Some(status) = result.status {
println!("Delivery status: {}", status);
}
}
The server uses a comprehensive error handling system with appropriate HTTP status codes:
400 Bad Request: Format and validation errors401 Unauthorized: Authentication errors429 Too Many Requests: Rate limiting500 Internal Server Error: Server-side errors503 Service Unavailable: Configuration errorsThe tap-http package includes binary executables that can be run from the command line:
The TAP HTTP server can be installed in several ways:
# From crates.io (recommended for most users)
cargo install tap-http
# From the repository (if you have it cloned)
cargo install --path tap-rs/tap-http
# Run the HTTP server with default settings (creates ephemeral agent)
tap-http
# Run with custom options
tap-http --host 0.0.0.0 --port 8080 --endpoint /api/didcomm
# Run with stored key (uses default from ~/.tap/keys.json)
tap-http --use-stored-key
# Run with a specific stored key by its DID
tap-http --use-stored-key --agent-did did:key:z6Mk...
# Run with custom logging options
tap-http --logs-dir /var/log/tap --structured-logs
After installation, you can use the tap-http command to run a TAP HTTP server:
USAGE:
tap-http [OPTIONS]
OPTIONS:
-h, --host <HOST> Host to bind to [default: 127.0.0.1]
-p, --port <PORT> Port to listen on [default: 8000]
-e, --endpoint <ENDPOINT> Path for the DIDComm endpoint [default: /didcomm]
-t, --timeout <SECONDS> Request timeout in seconds [default: 30]
--use-stored-key Use a key from the local key store (~/.tap/keys.json)
--agent-did <DID> Specific DID to use from key store (when --use-stored-key is set)
--generate-key Generate a new key and save it to the key store
--key-type <TYPE> Key type for generation [default: ed25519] [possible values: ed25519, p256, secp256k1]
--logs-dir <DIR> Directory for event logs [default: ./logs]
--structured-logs Use structured JSON logging [default: true]
--db-path <PATH> Path to the database file [default: tap-http.db]
--rate-limit <RATE> Rate limit in requests per minute [default: 60]
--tls-cert <PATH> Path to TLS certificate file
--tls-key <PATH> Path to TLS private key file
-v, --verbose Enable verbose logging
--help Print help information
--version Print version information
You can also configure the server using environment variables:
# Server configuration
export TAP_HTTP_HOST=0.0.0.0
export TAP_HTTP_PORT=8080
export TAP_HTTP_DIDCOMM_ENDPOINT=/api/didcomm
export TAP_HTTP_TIMEOUT=60
# Agent configuration
export TAP_USE_STORED_KEY=true
export TAP_AGENT_DID=did:key:z6Mk...
export TAP_GENERATE_KEY=false
export TAP_KEY_TYPE=ed25519
# Logging configuration
export TAP_LOGS_DIR=/var/log/tap
export TAP_STRUCTURED_LOGS=true
export TAP_LOG_LEVEL=info
# Storage configuration
export TAP_NODE_DB_PATH=/var/lib/tap/tap-http.db
# Security configuration
export TAP_RATE_LIMIT=100
export TAP_TLS_CERT=/path/to/cert.pem
export TAP_TLS_KEY=/path/to/key.pem
# Run the server (will use environment variables)
tap-http
The package also includes a payment flow simulator that can be used to test the TAP HTTP server:
# Run the payment simulator
tap-payment-simulator --url http://localhost:8000/didcomm --did did:key:z6Mk...
# Run with custom amount and currency
tap-payment-simulator --url http://localhost:8000/didcomm --did did:key:z6Mk... --amount 500 --currency EUR
The payment simulator is installed together with the tap-http package. You can use it to test your TAP HTTP server:
USAGE:
tap-payment-simulator --url <server-url> --did <server-agent-did> [OPTIONS]
REQUIRED ARGUMENTS:
--url <URL> URL of the TAP HTTP server's DIDComm endpoint
--did <DID> DID of the server's agent
OPTIONS:
--amount <AMOUNT> Amount to transfer [default: 100.00]
--currency <CURRENCY> Currency code [default: USD]
-v, --verbose Enable verbose logging
--help Print help information
--version Print version information
Check the examples directory for complete usage examples:
http_message_flow.rs: Basic HTTP message flowwebsocket_message_flow.rs: WebSocket message flow exampleevent_logger_demo.rs: Demonstration of event logging configurationTo run the examples:
# Run the HTTP message flow example
cargo run --example http_message_flow
# Run the WebSocket message flow example (with websocket feature)
cargo run --example websocket_message_flow --features websocket
# Run the event logger demo
cargo run --example event_logger_demo
Using the tap-payment-simulator tool, you can easily test a complete TAP payment flow:
Install the HTTP server and payment simulator (if not already installed):
cargo install tap-http
This installs both tap-http and tap-payment-simulator binaries.
Start the tap-http server with an ephemeral agent:
tap-http --verbose
The server will display the generated DID on startup:
TAP HTTP Server started with agent DID: did:key:z6Mk...
In another terminal, run the payment simulator to send messages to the server:
tap-payment-simulator --url http://localhost:8000/didcomm --did did:key:z6Mk...
The payment simulator will also display its agent DID:
Payment simulator using agent DID: did:key:z6Mk...
The simulator will:
Check the server logs to see the received messages and how they were processed:
tail -f ./logs/tap-http.log
This simulates a complete payment flow between two agents, demonstrating how the TAP protocol works in practice.
The TAP HTTP server leverages all the key features of the TAP Agent:
The server can use any of the TAP Agent's key management approaches:
~/.tap/keys.json) - shared with tap-agent-cliTo generate and manage keys for use with tap-http, you can use the tap-agent-cli tool:
# Install the tap-agent CLI
cargo install tap-agent
# Generate and save a key for later use with tap-http
tap-agent-cli generate --save
# View your stored keys
tap-agent-cli keys list
# Then use a stored key with tap-http
tap-http --use-stored-key
# Use a specific stored key
tap-http --use-stored-key --agent-did did:key:z6Mk...
The server uses the TAP Agent's DID resolution capabilities:
did:key and did:web by defaultAll security modes from the TAP Agent are supported:
The server acts as a service endpoint for incoming messages:
The TAP HTTP server includes built-in SQLite storage using async SQLx for:
By default, the server creates a database file at tap-http.db in the current directory. You can customize this location:
# Via command line
tap-http --db-path /var/lib/tap/tap-http.db
# Via environment variable
export TAP_NODE_DB_PATH=/var/lib/tap/tap-http.db
tap-http
The storage system maintains two tables:
messages - Complete audit trail of all messages:
transactions - Business logic for Transfer and Payment messages:
You can query the database directly using SQLite tools:
# View recent messages
sqlite3 tap-http.db "SELECT message_id, message_type, direction, created_at FROM messages ORDER BY created_at DESC LIMIT 10;"
# Count messages by type
sqlite3 tap-http.db "SELECT message_type, COUNT(*) FROM messages GROUP BY message_type;"
# View pending transactions
sqlite3 tap-http.db "SELECT * FROM transactions WHERE status = 'pending';"
The TAP HTTP server is designed for performance:
For high-volume deployments, consider: