Skip to main content

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}