# CBDDC
**Peer-to-Peer Data Synchronization Middleware for .NET** [![.NET Version](https://img.shields.io/badge/.NET-8.0%20%7C%2010.0-purple)](https://dotnet.microsoft.com/) ## Status ![Version](https://img.shields.io/badge/version-1.0.0-blue.svg) ![Build](https://img.shields.io/badge/build-passing-brightgreen.svg) CBDDC is not a database - it's a **sync layer** that plugs into your existing data store and enables automatic peer-to-peer replication across nodes in a mesh network. [Architecture](#architecture) | [Quick Start](#quick-start) | [Integration Guide](#integrating-with-your-database) | [Documentation](#documentation)
--- ## Table of Contents - [Overview](#overview) - [Ownership and Support](#ownership-and-support) - [Architecture](#architecture) - [Key Features](#key-features) - [Installation](#installation) - [Prerequisites](#prerequisites) - [Configuration and Secrets](#configuration-and-secrets) - [Build, Test, and Quality Gates](#build-test-and-quality-gates) - [Deployment and Rollback](#deployment-and-rollback) - [Operations and Incident Response](#operations-and-incident-response) - [Security and Compliance Posture](#security-and-compliance-posture) - [Troubleshooting](#troubleshooting) - [Change Governance](#change-governance) - [Quick Start](#quick-start) - [Integrating with Your Database](#integrating-with-your-database) - [Cloud Deployment](#cloud-deployment) - [Production Features](#production-features) - [Use Cases](#use-cases) - [Documentation](#documentation) - [Contributing](#contributing) --- ## Overview **CBDDC** is a lightweight, embeddable **data synchronization middleware** for .NET. It observes changes in your database via **Change Data Capture (CDC)**, records them in an append-only **Oplog**, and replicates them across nodes connected via a **P2P mesh network**. Your application continues to read and write to its database as usual. CBDDC works in the background. > **[LAN] Designed for Local Area Networks (LAN)** > Built for trusted environments: offices, retail stores, edge deployments. Cross-platform (Windows, Linux, macOS). > **[Cloud] Cloud Ready** > ASP.NET Core hosting for controlled, server-side deployments. --- ## Ownership and Support - **Owning team**: CBDDC Core Maintainers - **Issue reporting**: [GitHub Issues](https://github.com/CBDDC/ZB.MOM.WW.CBDDC.Net/issues) - **Usage/support questions**: [GitHub Discussions](https://github.com/CBDDC/ZB.MOM.WW.CBDDC.Net/discussions) - **Incident escalation for active deployments**: Follow your local on-call process first, then attach CBDDC diagnostics from `docs/runbook.md` --- ## Architecture ``` +---------------------------------------------------+ | Your Application | | db.Users.InsertAsync(user) | | db.Users.Find(u => u.Age > 18) | +---------------------------------------------------+ | uses your DbContext directly +---------------------------------------------------+ | Your Database (BLite) | | +---------------------------------------------+ | | | Users | Orders | Products | ... | | +---------------------------------------------+ | | | CDC (Change Data Capture) | | | | | +---------------------------------------------+ | | | CBDDC Sync Engine | | | | - Oplog (append-only hash-chained journal)| | | | - Vector Clock (causal ordering) | | | | - Conflict Resolution (LWW / Custom Merge)| | | +---------------------------------------------+ | +---------------------------------------------------+ | P2P Network (TCP + UDP Discovery) +---------------------------------------------------+ | Other Nodes (same setup) | | Node-A <-----> Node-B <-----> Node-C | +---------------------------------------------------+ ``` ### Core Concepts | Concept | Description | |---------|-------------| | **Oplog** | Append-only journal of changes, hash-chained per node for integrity | | **Vector Clock** | Tracks causal ordering - knows who has what across the mesh | | **CDC** | Change Data Capture - watches your registered collections for local writes | | **Document Store** | Your bridge class - maps between your entities and the sync engine | | **Conflict Resolution** | Pluggable strategy (Last-Write-Wins or custom recursive merge) | | **VectorClockService** | Shared singleton keeping the Vector Clock in sync between CDC and OplogStore | ### Sync Flow ``` Local Write -> CDC Trigger -> OplogEntry Created -> VectorClock Updated | v SyncOrchestrator (gossip every 2s) | +---------+----------+ | | Push changes Pull changes to peers from peers | | v v Remote node Apply to local applies via OplogStore + ApplyBatchAsync DocumentStore ``` --- ## Key Features ### [Select] Selective Collection Sync Only collections registered via `WatchCollection()` are tracked. Your database can have hundreds of tables - only the ones you opt-in participate in replication. ### [Gossip] Interest-Aware Gossip Nodes advertise which collections they sync. The orchestrator prioritizes peers sharing common interests, reducing unnecessary traffic. ### [Offline] Offline First - Read/write operations work offline - they're direct database operations - Automatic sync when peers reconnect - Oplog-based gap recovery and snapshot fallback ### [Secure] Secure Networking - Noise Protocol handshake with ECDH key exchange - AES-256 encryption for data in transit - HMAC authentication - Brotli compression for bandwidth efficiency ### [Conflict] Conflict Resolution - **Last Write Wins (LWW)** - default, HLC timestamp-based - **Recursive Merge** - deep JSON merge for concurrent edits - **Custom** - implement `IConflictResolver` for your business logic ### [Cloud] Cloud Infrastructure - ASP.NET Core hosting (single-cluster mode) - BLite embedded persistence - shared-token authentication --- ## Installation ### Packages | Package | Purpose | |---------|---------| | `ZB.MOM.WW.CBDDC.Core` | Interfaces, models, conflict resolution (.NET Standard 2.0+) | | `ZB.MOM.WW.CBDDC.Persistence` | BLite persistence provider, OplogStore, VectorClockService (.NET 10+) | | `CBDDC.Network` | TCP sync, UDP discovery, Protobuf protocol (.NET Standard 2.0+) | ```bash # BLite (embedded document DB) dotnet add package ZB.MOM.WW.CBDDC.Core dotnet add package ZB.MOM.WW.CBDDC.Persistence dotnet add package CBDDC.Network ``` --- ## Prerequisites Before onboarding a new node, verify: - .NET SDK/runtime required by selected packages (recommended: .NET 8+) - Network access for configured TCP/UDP sync ports - Persistent storage path with write permissions for database and backups - Access to deployment secrets (cluster `AuthToken`, connection strings, environment-specific config) - Ability to run health checks (`/health` in hosted mode) --- ## Configuration and Secrets CBDDC runtime behavior should be configured from environment-specific settings, not hardcoded values. - Required runtime settings: - `NodeId` - `TcpPort` (and `UdpPort` when discovery is enabled) - `AuthToken` (secret; do not commit) - Persistence connection/database path - Secret handling guidance: - Keep secrets in a secret manager or protected environment variables - Rotate tokens during maintenance windows and validate mesh convergence after rotation - Avoid exposing tokens in logs, screenshots, or troubleshooting output See `docs/security.md` and `docs/production-hardening.md` for detailed control guidance. --- ## Build, Test, and Quality Gates Run these checks before merge or release: ```bash dotnet restore dotnet build dotnet test ``` Recommended release gates: - Unit/integration tests pass - Health endpoint reports healthy in staging after deployment - No unresolved high-severity issues in deployment or runbook checklists --- ## Deployment and Rollback Canonical deployment procedures are documented in: - `docs/deployment.md` (promotion flow, validation gates, rollback path) - `docs/deployment-lan.md` (LAN deployment details) - `docs/deployment-modes.md` (hosted mode behavior) - `docs/production-hardening.md` (operational hardening controls) --- ## Operations and Incident Response Use `docs/runbook.md` as the primary operational runbook. It includes: - Alert triage sequence - Escalation path and severity mapping - Recovery and verification steps - Post-incident follow-up expectations For peer lifecycle incidents, use `docs/peer-deprecation-removal-runbook.md`. --- ## Security and Compliance Posture - Primary protections: authenticated peer communication and encrypted transport options in CBDDC networking components - Sensitive data handling: classify data by environment and apply least privilege on runtime access - Operational controls: hardening, key rotation, and monitoring requirements are documented in `docs/security.md` and `docs/access.md` --- ## Troubleshooting Common issue patterns and step-by-step remediation are documented in `docs/troubleshooting.md`. High-priority troubleshooting topics: - Peer unreachable or lagging confirmation state - Persistence faults and backup restore flow - Authentication mismatch or configuration drift --- ## Change Governance - Use short-lived branches and pull requests for every change - Require review before merge to protected branches - Run build/test/health validation before release - Record release-impacting changes in `CHANGELOG.md` and link related deployment notes --- ## Quick Start ### 1. Define Your Database Context ```csharp public class MyDbContext : CBDDCDocumentDbContext { public DocumentCollection Customers { get; private set; } public DocumentCollection Orders { get; private set; } public MyDbContext(string dbPath) : base(dbPath) { } } ``` ### 2. Create Your Document Store (the Sync Bridge) This is where you tell CBDDC **which collections to sync** and **how to map** between your entities and the sync engine: ```csharp public class MyDocumentStore : BLiteDocumentStore { public MyDocumentStore( MyDbContext context, IPeerNodeConfigurationProvider configProvider, IVectorClockService vectorClockService, ILogger? logger = null) : base(context, configProvider, vectorClockService, logger: logger) { // Register collections for CDC - only these will be synced WatchCollection("Customers", context.Customers, c => c.Id); WatchCollection("Orders", context.Orders, o => o.Id); } // Map incoming sync data back to your entities protected override async Task ApplyContentToEntityAsync( string collection, string key, JsonElement content, CancellationToken ct) { switch (collection) { case "Customers": var customer = content.Deserialize()!; customer.Id = key; var existing = _context.Customers .Find(c => c.Id == key).FirstOrDefault(); if (existing != null) _context.Customers.Update(customer); else _context.Customers.Insert(customer); break; case "Orders": var order = content.Deserialize()!; order.Id = key; var existingOrder = _context.Orders .Find(o => o.Id == key).FirstOrDefault(); if (existingOrder != null) _context.Orders.Update(order); else _context.Orders.Insert(order); break; } await _context.SaveChangesAsync(ct); } protected override Task GetEntityAsJsonAsync( string collection, string key, CancellationToken ct) { object? entity = collection switch { "Customers" => _context.Customers.Find(c => c.Id == key).FirstOrDefault(), "Orders" => _context.Orders.Find(o => o.Id == key).FirstOrDefault(), _ => null }; return Task.FromResult(entity != null ? (JsonElement?)JsonSerializer.SerializeToElement(entity) : null); } protected override async Task RemoveEntityAsync( string collection, string key, CancellationToken ct) { switch (collection) { case "Customers": _context.Customers.Delete(key); break; case "Orders": _context.Orders.Delete(key); break; } await _context.SaveChangesAsync(ct); } protected override Task> GetAllEntitiesAsJsonAsync(string collection, CancellationToken ct) { IEnumerable<(string, JsonElement)> result = collection switch { "Customers" => _context.Customers.FindAll() .Select(c => (c.Id, JsonSerializer.SerializeToElement(c))), "Orders" => _context.Orders.FindAll() .Select(o => (o.Id, JsonSerializer.SerializeToElement(o))), _ => Enumerable.Empty<(string, JsonElement)>() }; return Task.FromResult(result); } } ``` ### 3. Wire It Up ```csharp var builder = Host.CreateApplicationBuilder(); // Configure the node builder.Services.AddSingleton( new StaticPeerNodeConfigurationProvider(new PeerNodeConfiguration { NodeId = "node-1", TcpPort = 8580, AuthToken = "my-cluster-secret" })); // Register CBDDC services builder.Services .AddCBDDCCore() .AddCBDDCBLite( sp => new MyDbContext("mydata.blite")) .AddCBDDCNetwork(); await builder.Build().RunAsync(); ``` ### 4. Use Your Database Normally ```csharp public class MyService { private readonly MyDbContext _db; public MyService(MyDbContext db) => _db = db; public async Task CreateCustomer(string name) { // Write directly - CBDDC handles sync automatically await _db.Customers.InsertAsync( new Customer { Id = Guid.NewGuid().ToString(), Name = name }); await _db.SaveChangesAsync(); // Changes are automatically: // 1. Detected via CDC // 2. Recorded in the Oplog with HLC timestamp + hash chain // 3. Pushed to connected peers via gossip // 4. Applied on remote nodes via conflict resolution } public async Task> GetYoungCustomers() { // Read directly from your DB - no CBDDC API return _db.Customers.Find(c => c.Age < 30).ToList(); } } ``` --- ## Integrating with Your Database If you have an **existing database** and want to add P2P sync: ### Step 1 - Wrap your context Create a `DbContext` extending `CBDDCDocumentDbContext`. This can wrap your existing collections/tables. ```csharp public class MyExistingDbContext : CBDDCDocumentDbContext { // Your existing collections public DocumentCollection Products { get; private set; } public DocumentCollection Inventory { get; private set; } public MyExistingDbContext(string dbPath) : base(dbPath) { } } ``` ### Step 2 - Create a DocumentStore Extend `BLiteDocumentStore`. This is the **bridge** between your data model and the sync engine. ```csharp public class MyDocumentStore : BLiteDocumentStore { public MyDocumentStore(MyExistingDbContext ctx, IPeerNodeConfigurationProvider cfg, IVectorClockService vc, ILogger? log = null) : base(ctx, cfg, vc, logger: log) { // Continue to next step... } // Implement abstract methods (see below)... } ``` ### Step 3 - Register only what you need Call `WatchCollection()` in the constructor for each collection you want to replicate. Everything else is ignored by the sync engine. ```csharp public MyDocumentStore(...) : base(ctx, cfg, vc, logger: log) { // Only these 2 collections will be synced across the mesh WatchCollection("Products", ctx.Products, p => p.Id); WatchCollection("Inventory", ctx.Inventory, i => i.Id); // All other collections in your DB are local-only } ``` ### Step 4 - Implement the mapping methods CBDDC stores data as `JsonElement`. You provide four mapping methods: | Method | Purpose | |--------|---------| | `ApplyContentToEntityAsync` | Write incoming sync data to your entities | | `GetEntityAsJsonAsync` | Read your entities for outbound sync | | `RemoveEntityAsync` | Handle remote deletes | | `GetAllEntitiesAsJsonAsync` | Provide full collection for snapshot sync | ```csharp protected override async Task ApplyContentToEntityAsync( string collection, string key, JsonElement content, CancellationToken ct) { switch (collection) { case "Products": var product = content.Deserialize()!; product.Id = key; var existing = _context.Products.Find(p => p.Id == key).FirstOrDefault(); if (existing != null) _context.Products.Update(product); else _context.Products.Insert(product); break; case "Inventory": var inv = content.Deserialize()!; inv.Id = key; var existingInv = _context.Inventory.Find(i => i.Id == key).FirstOrDefault(); if (existingInv != null) _context.Inventory.Update(inv); else _context.Inventory.Insert(inv); break; } await _context.SaveChangesAsync(ct); } protected override Task GetEntityAsJsonAsync( string collection, string key, CancellationToken ct) { object? entity = collection switch { "Products" => _context.Products.Find(p => p.Id == key).FirstOrDefault(), "Inventory" => _context.Inventory.Find(i => i.Id == key).FirstOrDefault(), _ => null }; return Task.FromResult(entity != null ? (JsonElement?)JsonSerializer.SerializeToElement(entity) : null); } protected override async Task RemoveEntityAsync( string collection, string key, CancellationToken ct) { switch (collection) { case "Products": _context.Products.Delete(key); break; case "Inventory": _context.Inventory.Delete(key); break; } await _context.SaveChangesAsync(ct); } protected override Task> GetAllEntitiesAsJsonAsync(string collection, CancellationToken ct) { IEnumerable<(string, JsonElement)> result = collection switch { "Products" => _context.Products.FindAll() .Select(p => (p.Id, JsonSerializer.SerializeToElement(p))), "Inventory" => _context.Inventory.FindAll() .Select(i => (i.Id, JsonSerializer.SerializeToElement(i))), _ => Enumerable.Empty<(string, JsonElement)>() }; return Task.FromResult(result); } // Optional: Batch operations for better performance protected override async Task ApplyContentToEntitiesBatchAsync( IEnumerable<(string Collection, string Key, JsonElement Content)> documents, CancellationToken ct) { foreach (var (collection, key, content) in documents) { // Call the single-item method (you can optimize this further) await ApplyContentToEntityAsync(collection, key, content, ct); } } ``` **Your existing CRUD code stays unchanged.** CBDDC plugs in alongside it. ### What Happens Under the Hood ``` Your Code: db.Users.InsertAsync(user) | v BLite: SaveChangesAsync() | | CDC fires (WatchCollection observer) DocumentStore: CreateOplogEntryAsync() | +-> OplogEntry written (hash-chained, HLC timestamped) +-> VectorClockService.Update() -> sync sees it immediately | v SyncOrchestrator (background, every 2s) +-> Compare VectorClocks with peers +-> Push local changes (interest-filtered) +-> Pull remote changes -> ApplyBatchAsync | v Remote DocumentStore: ApplyContentToEntityAsync() | v Remote Database: Updated! ``` --- ## Cloud Deployment CBDDC supports ASP.NET Core hosting with BLite persistence for cloud deployments. ### Example: ASP.NET Core with BLite ```csharp var builder = WebApplication.CreateBuilder(args); builder.Services.AddCBDDCCore() .AddCBDDCBLite(sp => new MyDbContext("cbddc.blite")) .AddCBDDCNetwork(); builder.Services.AddCBDDCHostingSingleCluster(options => { options.TcpPort = 5001; }); var app = builder.Build(); app.MapHealthChecks("/health"); await app.RunAsync(); ``` --- ## Production Features ### Configuration ```json { "CBDDC": { "KnownPeers": [ { "NodeId": "gateway-1", "Address": "192.168.1.10:5000", "Type": "StaticRemote" } ], "RetentionHours": 24, "SyncIntervalSeconds": 2 }, "Logging": { "LogLevel": { "Default": "Information", "CBDDC": "Warning" } } } ``` ### Health Monitoring ```csharp var healthCheck = new CBDDCHealthCheck(store, syncTracker); var status = await healthCheck.CheckAsync(); Console.WriteLine($"Database: {status.DatabaseHealthy}"); Console.WriteLine($"Network: {status.NetworkHealthy}"); Console.WriteLine($"Peers: {status.ConnectedPeers}"); ``` ### Resilience - **Exponential Backoff**: Automatic retry for unreachable peers - **Offline Queue**: Buffer local changes when network is down - **Snapshot Recovery**: Fast catch-up after long disconnects - **Hash Chain Validation**: Detect and recover from oplog gaps ### Performance - **VectorClock Cache**: In-memory tracking of node states - **Brotli Compression**: 70-80% bandwidth reduction - **Batch Operations**: Group changes for efficient network transfer - **Interest Filtering**: Only sync collections both peers care about ### Security - **Noise Protocol Handshake**: XX pattern with ECDH key exchange - **AES-256 Encryption**: Protect data in transit - **Auth Tokens**: Shared secret validation - **LAN Isolation**: Designed for trusted network environments --- ## Use Cases ### Ideal For - **Retail POS Systems** - Terminals syncing inventory and sales across a store - **Office Applications** - Shared task lists, calendars, CRM data on LAN - **Edge Computing** - Distributed sensors and controllers at a facility - **Offline-First Apps** - Work without internet, sync when connected - **Multi-Site Replication** - Keep regional databases in sync (over VPN) - **Existing Database Modernization** - Add P2P sync without rewriting your app ### Not Designed For - **Public internet without HTTPS/VPN** (P2P mesh mode, use ASP.NET Core mode instead) - **Sub-millisecond consistency requirements** (eventual consistency model, typical convergence < 5s) - **Unstructured data** (designed for document collections with keys) - **Append-only event logs** (oplog pruning after 24h retention) --- ## Documentation ### Getting Started - **[Sample Application](samples/ZB.MOM.WW.CBDDC.Sample.Console/)** - Complete two-node sync example with interactive CLI - **[Quick Start Guide](#quick-start)** - 5-minute setup - **[Integration Guide](#integrating-with-your-database)** - Add sync to existing DB ### Concepts - **[Architecture & Concepts](docs/architecture.md)** - HLC, Gossip, Vector Clocks, Hash Chains - **[Conflict Resolution](docs/conflict-resolution.md)** - LWW vs Recursive Merge - **[Oplog & CDC Design](docs/database-sync-manager-design.md)** - How change tracking works ### Deployment - **[Production Guide](docs/production-hardening.md)** - Configuration, monitoring, best practices - **[Deployment Runbook](docs/deployment.md)** - Promotion flow, validation, rollback - **[Deployment Modes](docs/deployment-modes.md)** - Single-cluster deployment strategy - **[Operations Runbook](docs/runbook.md)** - Incident triage and recovery ### API - **[API Reference](docs/api-reference.md)** - Complete API documentation - **[Persistence Providers](docs/persistence-providers.md)** - BLite, custom --- ## Examples See [`samples/ZB.MOM.WW.CBDDC.Sample.Console/`](samples/ZB.MOM.WW.CBDDC.Sample.Console/) for a complete working example with: - **Two-node sync** simulation on different ports - **Interactive CLI** for testing operations - **Conflict resolution demo** (switchable LWW/Merge) - **User and TodoList** entities with full CRUD - **Health monitoring** and cache inspection - **Automatic peer discovery** via UDP Run two instances: ```bash # Terminal 1 cd samples/ZB.MOM.WW.CBDDC.Sample.Console dotnet run -- node-1 8580 # Terminal 2 dotnet run -- node-2 8581 # Create a user on node-1 with command "n" # Watch it appear on node-2 automatically! ``` --- ## Roadmap - [x] Core P2P mesh networking (v0.1.0) - [x] Secure networking (ECDH + AES-256) (v0.6.0) - [x] Conflict resolution strategies (LWW, Recursive Merge) (v0.6.0) - [x] Hash-chain sync with gap recovery (v0.7.0) - [x] Brotli compression (v0.7.0) - [x] Persistence snapshots (v0.8.6) - [x] ASP.NET Core hosting (v0.8.0) - [x] **VectorClockService refactor** (v1.0.0) - [x] **CDC-aware sync** (v1.0.0) - [ ] Query optimization & advanced indexing - [ ] Admin UI / monitoring dashboard - [ ] Mobile support (Xamarin/MAUI) --- ## Contributing We welcome contributions! CBDDC is open-source and we'd love your help. ### How to Contribute 1. **Fork the repository** 2. **Create a feature branch** (`git checkout -b feature/amazing-feature`) 3. **Make your changes** with clear commit messages 4. **Add tests** for new functionality 5. **Ensure all tests pass** (`dotnet test`) 6. **Submit a Pull Request** ### Development Setup ```bash # Clone the repository git clone https://github.com/CBDDC/ZB.MOM.WW.CBDDC.Net.git cd CBDDC.Net # Restore dependencies dotnet restore # Build dotnet build # Run all tests (69 tests) dotnet test # Run sample cd samples/ZB.MOM.WW.CBDDC.Sample.Console dotnet run ``` ### Areas We Need Help - **[Bug] Bug Reports** - Found an issue? Let us know! - **[Docs] Documentation** - Improve guides and examples - **[Feature] Features** - Implement items from the roadmap - **[Test] Testing** - Add integration and performance tests - **[Sample] Samples** - Build example applications ### Code of Conduct Be respectful, inclusive, and constructive. We're all here to learn and build great software together.