hydra_sync/lib.rs
1//! # **hydra-sync**
2//!
3//! A lightweight, end-to-end encrypted `(AES-GCM256)` **SPMC broadcast server** over plain `TCP`.
4//!
5//! One producer fans out fixed-size packets to many consumers through per-consumer lock-free ring buffers,
6//! while the server stays a *blind relay* — it never holds a key and never sees plaintext.
7//!
8//! ## Features
9//! - **E2E encryption** — `X25519` key-exchange handshake + `AES-GCM256` per packet; the server relays ciphertext only.
10//! - **Zero-copy relay** — `BytesMut` handles move through cache-padded SPSC rings; no payload copies on the hot path.
11//! - **Fixed-size framing** — server reads/writes exact packet sizes; clients deal only in raw payload bytes, crypto overhead is internal.
12//! - **Cross-language friendly** — dumb, agreed wire protocol; any language can implement a client against it.
13//!
14//! ## Example
15//! ```no_run
16//! use hydra_sync::client::{Consumer, HydraClient, Producer};
17//!
18//! #[tokio::main]
19//! async fn main() {
20//! let (server, addr) = hydra_sync::server::HydraServer::bind_default().await.unwrap();
21//! tokio::spawn(async move { let _ = server.run().await; });
22//!
23//! let session_id = [0xFFu8; 64];
24//! let session_key = [0xAAu8; 32];
25//!
26//! // producer creates & owns the session
27//! let mut producer = HydraClient::<Producer>::connect(addr, session_id, session_key).await.unwrap();
28//!
29//! // consumers join it
30//! let mut consumer = HydraClient::<Consumer>::connect(addr, session_id, session_key).await.unwrap();
31//!
32//! let payload = vec![0u8; producer.get_server_read_write_length() as usize];
33//! producer.broadcast(&payload).await.unwrap(); // fan-out to every consumer
34//!
35//! let packet = consumer.recv().await.unwrap(); // next frame on the ring buffer
36//! }
37//! ```
38//!
39
40// public user modules
41pub mod channel;
42pub mod client;
43pub mod crypto;
44pub mod server;
45
46// private modules
47mod log;
48mod protocol;
49mod session;
50
51pub const BUFFER_SIZE: usize = 2 << 20; // 2 Mb
52pub const READ_WRITE_TIMEOUT: std::time::Duration = std::time::Duration::from_millis(500);
53
54/// Determines what happens when a consumer's channel is full and overflows,
55/// [Issue](https://github.com/ronakgh97/hydra-sync/issues/1).
56///
57/// `THIS was initial experimental design choice all modes will be thought-out & implement in future versions`
58#[derive(Debug, Clone, Copy, PartialEq, Eq)]
59pub enum ChannelOverflowStrategy {
60 /// Producer sleeps until the channel has space.
61 BackPressure,
62 // /// Write overflow to disk temporarily ON DEMAND.
63 // WriteDisk,
64 // /// Write to a write-ahead log (WAL) until connection's over.
65 // WriteAheadLog,
66 /// Dynamically grow/shrink the channel buffer.
67 // ResizableBuffer,
68 /// Disconnect the slowest consumers.
69 DropClient,
70 /// Drop new packets for slow client.
71 DropPacket,
72}
73
74pub(crate) static START_TIME: std::sync::OnceLock<chrono::DateTime<chrono::Local>> =
75 std::sync::OnceLock::new();
76
77/// Get the server uptime in hours
78pub(crate) fn get_uptime_hrs() -> f64 {
79 if let Some(start_time) = START_TIME.get() {
80 let now = chrono::Local::now();
81 let duration = now.signed_duration_since(*start_time);
82 duration.as_seconds_f64() / 3600.0
83 } else {
84 0.0
85 }
86}