Skip to main content

tap_node/storage/
mod.rs

1//! Storage module for persisting TAP messages and transactions
2//!
3//! This module provides comprehensive persistent storage capabilities for the TAP Node,
4//! maintaining both business transaction records and a complete message audit trail
5//! in a SQLite database for compliance, debugging, and operational purposes.
6//!
7//! # Architecture
8//!
9//! The storage system uses a dual-table design:
10//! - **transactions**: Stores Transfer and Payment messages for business logic
11//! - **messages**: Stores all messages (incoming/outgoing) for audit trail
12//!
13//! # Features
14//!
15//! - **Automatic Schema Migration**: Database schema is created and migrated automatically
16//! - **Connection Pooling**: Uses sqlx's built-in async connection pool
17//! - **Async API**: Native async operations without blocking threads
18//! - **WASM Compatibility**: Storage is automatically disabled in WASM builds
19//! - **Idempotent Operations**: Duplicate messages are silently ignored
20//! - **Direction Tracking**: Messages are tagged as incoming or outgoing
21//! - **Thread Tracking**: Full support for DIDComm thread and parent thread IDs
22//!
23//! # Usage
24//!
25//! Storage is automatically initialized when creating a TapNode with the storage feature enabled:
26//!
27//! ```no_run
28//! use tap_node::{NodeConfig, TapNode};
29//! use tap_node::storage::MessageDirection;
30//! use std::path::PathBuf;
31//!
32//! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
33//! let config = NodeConfig {
34//!     #[cfg(feature = "storage")]
35//!     storage_path: Some(PathBuf::from("./my-database.db")),
36//!     ..Default::default()
37//! };
38//!
39//! let node = TapNode::new(config);
40//!
41//! // Access storage functionality
42//! if let Some(storage) = node.storage() {
43//!     // Query transactions
44//!     let txs = storage.list_transactions(10, 0).await?;
45//!     
46//!     // Query message audit trail
47//!     let messages = storage.list_messages(20, 0, Some(MessageDirection::Incoming)).await?;
48//! }
49//! # Ok(())
50//! # }
51//! ```
52//!
53//! # Environment Variables
54//!
55//! - `TAP_NODE_DB_PATH`: Override the default database path
56//!
57//! # Automatic Message Logging
58//!
59//! The TapNode automatically logs all messages:
60//! - Incoming messages are logged when `receive_message()` is called
61//! - Outgoing messages are logged when `send_message()` is called
62//! - Transfer and Payment messages are additionally stored in the transactions table
63
64#[cfg(feature = "storage")]
65pub mod agent_storage_manager;
66#[cfg(feature = "storage")]
67pub mod db;
68#[cfg(feature = "storage")]
69pub mod error;
70#[cfg(feature = "storage")]
71pub mod models;
72
73#[cfg(feature = "storage")]
74pub use agent_storage_manager::AgentStorageManager;
75#[cfg(feature = "storage")]
76pub use db::Storage;
77#[cfg(feature = "storage")]
78pub use error::StorageError;
79#[cfg(feature = "storage")]
80pub use models::{
81    Customer, CustomerIdentifier, CustomerRelationship, DecisionLogEntry, DecisionStatus,
82    DecisionType, Delivery, DeliveryStatus, DeliveryType, IdentifierType, Message,
83    MessageDirection, Received, ReceivedStatus, SchemaType, SourceType, Transaction,
84    TransactionStatus, TransactionType,
85};
86
87#[cfg(not(feature = "storage"))]
88pub use mock::*;
89
90#[cfg(not(feature = "storage"))]
91mod mock {
92    use serde::{Deserialize, Serialize};
93    use tap_msg::didcomm::PlainMessage;
94
95    #[derive(Debug, Clone)]
96    pub struct Storage;
97
98    #[derive(Debug, thiserror::Error)]
99    #[error("Storage is not available in this build")]
100    pub struct StorageError;
101
102    #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
103    pub enum MessageDirection {
104        Incoming,
105        Outgoing,
106    }
107
108    impl Storage {
109        pub async fn new(_path: Option<std::path::PathBuf>) -> Result<Self, StorageError> {
110            Ok(Storage)
111        }
112
113        pub async fn insert_transaction(
114            &self,
115            _message: &PlainMessage,
116        ) -> Result<(), StorageError> {
117            Ok(())
118        }
119
120        pub async fn log_message(
121            &self,
122            _message: &PlainMessage,
123            _direction: MessageDirection,
124        ) -> Result<(), StorageError> {
125            Ok(())
126        }
127    }
128}