Development Documentation (main branch) - For stable release docs, see docs.rs/eidetica

eidetica/sync/
protocol.rs

1//! Protocol definitions for sync communication.
2//!
3//! This module defines transport-agnostic message types that can be
4//! used across different network transports (HTTP, Iroh, Bluetooth, etc.).
5
6use serde::{Deserialize, Serialize};
7
8use super::peer_types::Address;
9use crate::{
10    auth::{Permission, crypto::PublicKey},
11    entry::{Entry, ID},
12};
13
14/// Handshake request sent when establishing a peer connection.
15#[allow(clippy::large_enum_variant)]
16#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
17pub struct HandshakeRequest {
18    // FIXME: device_id and public_key are functionally identical
19    /// Unique device identifier
20    pub device_id: PublicKey,
21    /// Ed25519 public key of the sender
22    pub public_key: PublicKey,
23    /// Optional human-readable display name
24    pub display_name: Option<String>,
25    /// Protocol version number
26    pub protocol_version: u32,
27    /// Random challenge bytes for signature verification
28    pub challenge: Vec<u8>,
29    /// Addresses where this peer can be reached for sync
30    pub listen_addresses: Vec<Address>,
31}
32
33/// Information about a tree available for sync
34#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
35pub struct TreeInfo {
36    /// The root ID of the tree
37    pub tree_id: ID,
38    /// Optional human-readable name for the tree
39    pub name: Option<String>,
40    /// Number of entries in the tree
41    pub entry_count: usize,
42    /// Unix timestamp of last modification
43    pub last_modified: u64,
44}
45
46/// Handshake response sent in reply to a handshake request.
47#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
48pub struct HandshakeResponse {
49    // FIXME: device_id and public_key are functionally identical
50    /// Unique device identifier
51    pub device_id: PublicKey,
52    /// Ed25519 public key of the responder
53    pub public_key: PublicKey,
54    /// Optional human-readable display name
55    pub display_name: Option<String>,
56    /// Protocol version number
57    pub protocol_version: u32,
58    /// Signed challenge from the request
59    pub challenge_response: Vec<u8>,
60    /// New challenge for mutual authentication
61    pub new_challenge: Vec<u8>,
62    /// Trees available for synchronization
63    pub available_trees: Vec<TreeInfo>,
64}
65
66/// Unified sync request for both bootstrap and incremental sync
67#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
68pub struct SyncTreeRequest {
69    /// Database ID to sync
70    pub tree_id: ID,
71    /// Our current tips (empty vec signals bootstrap needed)
72    pub our_tips: Vec<ID>,
73    /// Device public key of the requesting peer (used for automatic tree/peer relationship tracking)
74    pub peer_pubkey: Option<PublicKey>,
75    // Note: requesting_key is unverified but this is safe - see handler.rs
76    // handle_bootstrap_request() for detailed explanation.
77    /// Authentication key requesting access (for bootstrap)
78    pub requesting_key: Option<PublicKey>,
79    /// Key name/identifier for the requesting key
80    pub requesting_key_name: Option<String>,
81    /// Desired permission level for bootstrap
82    pub requested_permission: Option<Permission>,
83}
84
85/// Bootstrap response containing complete tree state
86#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
87pub struct BootstrapResponse {
88    /// Database ID being bootstrapped
89    pub tree_id: ID,
90    /// The root entry of the tree
91    pub root_entry: Entry,
92    /// All entries in the tree (excluding root)
93    pub all_entries: Vec<Entry>,
94    /// Whether the requesting key was approved and added
95    pub key_approved: bool,
96    /// The permission level granted (if approved)
97    pub granted_permission: Option<Permission>,
98}
99
100/// Incremental sync response for existing trees
101#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
102pub struct IncrementalResponse {
103    /// Database ID being synced
104    pub tree_id: ID,
105    /// Peer's current tips
106    pub their_tips: Vec<ID>,
107    /// Entries missing from our tree
108    pub missing_entries: Vec<Entry>,
109}
110
111/// Request messages that can be sent to a sync peer.
112#[allow(clippy::large_enum_variant)]
113#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
114pub enum SyncRequest {
115    /// Initial handshake request
116    Handshake(HandshakeRequest),
117    /// Unified tree sync request (handles both bootstrap and incremental)
118    SyncTree(SyncTreeRequest),
119    /// Send entries for synchronization (backward compatibility)
120    SendEntries(Vec<Entry>),
121}
122
123/// Response messages returned from a sync peer.
124#[allow(clippy::large_enum_variant)]
125#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
126pub enum SyncResponse {
127    /// Handshake response
128    Handshake(HandshakeResponse),
129    /// Full database bootstrap for new peers
130    Bootstrap(BootstrapResponse),
131    /// Incremental sync for existing peers
132    Incremental(IncrementalResponse),
133    /// Bootstrap request pending manual approval
134    BootstrapPending {
135        /// Unique identifier for the pending request
136        request_id: String,
137        /// Human-readable message about the pending status
138        message: String,
139    },
140    /// Acknowledgment that entries were received successfully
141    Ack,
142    /// Number of entries received (for multiple entries)
143    Count(usize),
144    /// Error response
145    Error(String),
146}
147
148/// Current protocol version - 0 indicates unstable
149pub const PROTOCOL_VERSION: u32 = 0;
150
151/// Context information about the incoming request.
152///
153/// This struct captures metadata about the connection that initiated
154/// the request, allowing the handler to know where the request came from.
155#[derive(Debug, Clone, Default)]
156pub struct RequestContext {
157    /// The remote address from which this request originated.
158    /// Extracted from the transport layer's connection metadata.
159    pub remote_address: Option<Address>,
160    /// The public key of the peer making this request.
161    /// Set after successful handshake to identify the authenticated peer.
162    pub peer_pubkey: Option<PublicKey>,
163}