Skip to main content

nodedb_crdt/
lib.rs

1// SPDX-License-Identifier: BUSL-1.1
2
3//! # nodedb-crdt
4//!
5//! CRDT engine with SQL constraint validation for NodeDB.
6//!
7//! ## The CRDT / SQL Paradox
8//!
9//! CRDTs are AP (Available + Partition-tolerant): agents compute optimistic
10//! deltas offline and sync later. SQL constraints are CP (Consistent +
11//! Partition-tolerant): UNIQUE indexes, foreign keys, etc. must hold globally.
12//!
13//! This crate bridges the gap:
14//!
15//! 1. **Optimistic local writes** — agents apply deltas to their local `LoroDoc`
16//!    without constraint checks (AP behavior for availability).
17//! 2. **Constraint validation at commit** — when deltas sync to the leader,
18//!    constraints are validated against the committed state.
19//! 3. **Dead-letter queue** — rejected deltas are routed to a DLQ with
20//!    compensation hints so the application can recover gracefully.
21//! 4. **Pre-validation** — optional fast-reject against the leader's state
22//!    before the full Raft round-trip, reducing wasted consensus bandwidth.
23
24/// Authentication context threaded through CRDT validation.
25///
26/// Carries the identity of who submitted the delta so the DLQ and deferred
27/// queue can attribute entries to the correct user/tenant.
28///
29/// ## Replay protection
30///
31/// `device_id` and `seq_no` close the replay vulnerability: a captured
32/// delta cannot be resubmitted because `seq_no` must be strictly greater
33/// than `last_seen[(user_id, device_id)]` on the server. The HMAC input
34/// binds the seq_no and device_id so they cannot be altered after signing.
35///
36/// ## Required fields
37///
38/// All fields, including `device_id` and `seq_no`, must be present in the
39/// serialized form. Missing fields are a hard decode error.
40#[derive(Debug, Clone, Copy, Default, serde::Serialize, serde::Deserialize)]
41pub struct CrdtAuthContext {
42    /// Authenticated user_id (0 = unauthenticated).
43    pub user_id: u64,
44    /// Tenant this operation belongs to.
45    pub tenant_id: u64,
46    /// Unix timestamp (milliseconds) when this auth session expires.
47    /// 0 = no expiry (trust mode).
48    /// Agents accumulating deltas offline must re-authenticate before
49    /// syncing if their auth context has expired.
50    pub auth_expires_at: u64,
51    /// HMAC signature over delta bytes (optional delta signing).
52    /// All-zeros = unsigned. When non-zero, the validator verifies this
53    /// before accepting, and also enforces replay protection.
54    pub delta_signature: [u8; 32],
55    /// Stable per-device identifier assigned by the server on first bind.
56    pub device_id: u64,
57    /// Monotonically increasing per-device sequence number.
58    /// Must be strictly greater than last_seen[(user_id, device_id)] on the server.
59    pub seq_no: u64,
60}
61
62pub mod constraint;
63pub mod constraint_checks;
64pub mod dead_letter;
65pub mod deferred;
66pub mod error;
67pub mod list_ops;
68pub mod policy;
69pub mod pre_validate;
70pub mod signing;
71pub mod state;
72pub mod validator;
73
74pub use constraint::{Constraint, ConstraintKind, ConstraintSet};
75pub use dead_letter::{CompensationHint, DeadLetterQueue};
76pub use deferred::DeferredQueue;
77pub use error::{CrdtError, Result};
78pub use policy::{
79    CollectionPolicy, ConflictPolicy, PolicyRegistry, PolicyResolution, ResolvedAction,
80};
81pub use signing::{DeltaSigner, DeviceRegistry};
82pub use state::CrdtState;
83pub use validator::{ValidationOutcome, Validator};