Initial import of the CBDDC codebase with docs and tests. Add a .NET-focused gitignore to keep generated artifacts out of source control.
Some checks failed
CI / verify (push) Has been cancelled

This commit is contained in:
Joseph Doherty
2026-02-20 13:03:21 -05:00
commit 08bfc17218
218 changed files with 33910 additions and 0 deletions

73
docs/README.md Executable file
View File

@@ -0,0 +1,73 @@
# CBDDC Documentation
This folder contains the official documentation for CBDDC, published as GitHub Pages.
## Documentation Structure (v0.9.0)
### Getting Started
- **[Getting Started](getting-started.md)** - Installation, setup, and first steps with CBDDC
### Core Documentation
- **[Architecture](architecture.md)** - Hybrid Logical Clocks, Gossip Protocol, mesh networking
- **[API Reference](api-reference.md)** - Complete API documentation with examples
- **[Querying](querying.md)** - Data querying patterns and LINQ support
### Persistence & Storage
- **[Persistence Providers](persistence-providers.md)** - SQLite, EF Core, PostgreSQL comparison
- **[Deployment Modes](deployment-modes.md)** - Single-cluster deployment strategy
### Networking & Security
- **[Security](security.md)** - Encryption, authentication, secure networking
- **[Conflict Resolution](conflict-resolution.md)** - LWW and Recursive Merge strategies
- **[Network Telemetry](network-telemetry.md)** - Monitoring and diagnostics
- **[Dynamic Reconfiguration](dynamic-reconfiguration.md)** - Runtime configuration changes
- **[Remote Peer Configuration](remote-peer-configuration.md)** - Managing remote peers and tracking lifecycle
- **[Upgrade: Peer-Confirmed Pruning](upgrade-peer-confirmed-pruning.md)** - Rollout notes and adoption checklist
### Deployment & Operations
- **[Deployment (LAN)](deployment-lan.md)** - Platform-specific deployment guide
- **[Production Hardening](production-hardening.md)** - Configuration, monitoring, best practices
- **[Peer Deprecation & Removal Runbook](peer-deprecation-removal-runbook.md)** - Operational workflow for de-tracking and removal
## Building the Documentation
This documentation uses Jekyll with the Cayman theme and is automatically published via GitHub Pages.
### Local Development
```bash
# Install Jekyll (requires Ruby)
gem install bundler jekyll
# Serve documentation locally
cd docs
jekyll serve
# Open http://localhost:4000
```
### Site Configuration
- **_config.yml** - Jekyll configuration and site metadata
- **_layouts/default.html** - Main page layout with navigation and styling
- **_includes/nav.html** - Top navigation bar
- **_data/navigation.yml** - Sidebar navigation structure per version
## Version History
The documentation supports multiple versions:
- **v0.9** (current) - Latest stable release
- **v0.8** - ASP.NET Core hosting, EF Core, PostgreSQL support
- **v0.7** - Brotli compression, Protocol v4
- **v0.6** - Secure networking, conflict resolution strategies
## Contributing to Documentation
1. **Fix typos or improve clarity** - Submit PRs directly
2. **Add new guides** - Create a new .md file and update navigation.yml
3. **Test locally** - Run Jekyll locally to preview changes before submitting
## Links
- [Main Repository](https://github.com/CBDDC/ZB.MOM.WW.CBDDC.Net)
- [NuGet Packages](https://www.nuget.org/packages?q=CBDDC)

5
docs/_config.yml Executable file
View File

@@ -0,0 +1,5 @@
title: "CBDDC Documentation"
description: A decentralized, offline-first peer-to-peer database.
theme: jekyll-theme-cayman
plugins:
- jekyll-seo-tag

123
docs/_data/navigation.yml Executable file
View File

@@ -0,0 +1,123 @@
v0.9:
- title: Getting Started
url: /getting-started.html
- title: Architecture
url: /architecture.html
- title: Persistence Providers
url: /persistence-providers.html
- title: Deployment Modes
url: /deployment-modes.html
- title: Deployment (LAN)
url: /deployment-lan.html
- title: Security
url: /security.html
- title: Conflict Resolution
url: /conflict-resolution.html
- title: Querying
url: /querying.html
- title: Network Telemetry
url: /network-telemetry.html
- title: Dynamic Reconfiguration
url: /dynamic-reconfiguration.html
- title: Remote Peer Configuration
url: /remote-peer-configuration.html
- title: Upgrade: Peer-Confirmed Pruning
url: /upgrade-peer-confirmed-pruning.html
- title: Peer Deprecation & Removal Runbook
url: /peer-deprecation-removal-runbook.html
- title: Production Hardening
url: /production-hardening.html
- title: API Reference
url: /api-reference.html
v0.8:
- title: Getting Started
url: /v0.8/getting-started.html
- title: Architecture
url: /v0.8/architecture.html
- title: Persistence Providers
url: /v0.8/persistence-providers.html
- title: Deployment Modes
url: /v0.8/deployment-modes.html
- title: Security
url: /v0.8/security.html
- title: Conflict Resolution
url: /v0.8/conflict-resolution.html
- title: Querying
url: /v0.8/querying.html
- title: Production Hardening
url: /v0.8/production-hardening.html
- title: Deployment (LAN)
url: /v0.8/deployment-lan.html
- title: API Reference
url: /v0.8/api-reference.html
v0.7:
- title: Getting Started
url: /v0.7/getting-started.html
- title: Architecture
url: /v0.7/architecture.html
- title: Security
url: /v0.7/security.html
- title: Conflict Resolution
url: /v0.7/conflict-resolution.html
- title: Querying
url: /v0.7/querying.html
- title: Production Hardening
url: /v0.7/production-hardening.html
- title: Deployment (LAN)
url: /v0.7/deployment-lan.html
- title: API Reference
url: /v0.7/api-reference.html
v0.6:
- title: Getting Started
url: /v0.6/getting-started.html
- title: Architecture
url: /v0.6/architecture.html
- title: Security
url: /v0.6/security.html
- title: Conflict Resolution
url: /v0.6/conflict-resolution.html
- title: Deployment (LAN)
url: /v0.6/deployment-lan.html
- title: API Reference
url: /v0.6/api-reference.html
- title: Production Hardening
url: /v0.6/production-hardening.html
v0.5:
- title: Getting Started
url: /v0.5/getting-started.html
- title: Architecture
url: /v0.5/architecture.html
- title: Deployment (LAN)
url: /v0.5/deployment-lan.html
- title: API Reference
url: /v0.5/api-reference.html
- title: Production Hardening
url: /v0.5/production-hardening.html
v0.4:
- title: Getting Started
url: /v0.4/getting-started.html
- title: Architecture
url: /v0.4/architecture.html
- title: Deployment (LAN)
url: /v0.4/deployment-lan.html
- title: API Reference
url: /v0.4/api-reference.html
- title: Production Hardening
url: /v0.4/production-hardening.html
v0.2:
- title: Getting Started
url: /v0.2/getting-started.html
- title: Architecture
url: /v0.2/architecture.html
- title: Deployment (LAN)
url: /v0.2/deployment-lan.html
- title: API Reference
url: /v0.2/api-reference.html
- title: Production Hardening
url: /v0.2/production-hardening.html

5
docs/_includes/nav.html Executable file
View File

@@ -0,0 +1,5 @@
<nav class="site-nav">
<a href="{{ '/' | relative_url }}">Home</a>
<a href="{{ '/getting-started.html' | relative_url }}">Docs (v0.9)</a>
<a href="https://github.com/CBDDC/ZB.MOM.WW.CBDDC.Net" target="_blank">GitHub</a>
</nav>

390
docs/_layouts/default.html Executable file
View File

@@ -0,0 +1,390 @@
<!DOCTYPE html>
<html lang="{{ site.lang | default: " en-US" }}">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="{{ site.description }}">
<link rel="stylesheet" href="{{ '/assets/css/style.css?v=' | append: site.github.build_revision | relative_url }}">
<title>{{ page.title | default: site.title }}</title>
<style>
/* Modern Color Palette */
:root {
--primary-color: #159957;
--primary-dark: #0e7042;
--primary-light: #1eb96a;
--secondary-color: #155799;
--background: #ffffff;
--background-alt: #f8f9fa;
--text-primary: #24292e;
--text-secondary: #586069;
--border-color: #e1e4e8;
--shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24);
--shadow-hover: 0 3px 6px rgba(0,0,0,0.15), 0 2px 4px rgba(0,0,0,0.12);
}
/* Base Styles */
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
color: var(--text-primary);
line-height: 1.6;
margin: 0;
padding: 0;
}
/* Top Navigation */
.site-nav {
background: linear-gradient(135deg, var(--primary-color), var(--primary-light));
padding: 1rem 0;
text-align: center;
box-shadow: var(--shadow);
position: sticky;
top: 0;
z-index: 1000;
}
.site-nav a {
color: rgba(255, 255, 255, 0.9);
margin: 0 1.5rem;
text-decoration: none;
font-weight: 600;
font-size: 0.95rem;
letter-spacing: 0.5px;
transition: all 0.3s ease;
padding: 0.5rem 1rem;
border-radius: 4px;
}
.site-nav a:hover,
.site-nav a.active {
color: white;
background: rgba(255, 255, 255, 0.15);
transform: translateY(-1px);
}
/* Header Section */
.page-header {
background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
color: white;
text-align: center;
padding: 3rem 1rem;
}
.project-name {
margin: 0 0 0.5rem 0;
font-size: 2.5rem;
font-weight: 700;
letter-spacing: -0.5px;
}
.project-tagline {
margin: 0 0 1.5rem 0;
font-size: 1.2rem;
font-weight: 400;
opacity: 0.9;
}
.btn {
display: inline-block;
margin: 0.5rem;
padding: 0.75rem 2rem;
background: rgba(255, 255, 255, 0.2);
color: white;
border: 2px solid rgba(255, 255, 255, 0.8);
border-radius: 6px;
text-decoration: none;
font-weight: 600;
transition: all 0.3s ease;
font-size: 0.95rem;
}
.btn:hover {
background: white;
color: var(--primary-color);
transform: translateY(-2px);
box-shadow: var(--shadow-hover);
}
/* Layout Grid */
.page-content {
display: flex;
flex-direction: column;
max-width: 1200px;
margin: 2rem auto;
padding: 0 1rem;
gap: 2rem;
}
@media (min-width: 64em) {
.page-content {
flex-direction: row;
align-items: flex-start;
padding: 0 2rem;
}
}
/* Sidebar */
.sidebar {
width: 100%;
background: var(--background-alt);
padding: 1.5rem;
border-radius: 8px;
border: 1px solid var(--border-color);
box-shadow: var(--shadow);
}
@media (min-width: 64em) {
.sidebar {
width: 280px;
flex-shrink: 0;
position: sticky;
top: calc(4rem + 1rem);
max-height: calc(100vh - 6rem);
overflow-y: auto;
}
}
.sidebar h3 {
margin: 0 0 1rem 0;
font-size: 1.1rem;
color: var(--text-primary);
font-weight: 700;
padding-bottom: 0.75rem;
border-bottom: 2px solid var(--primary-color);
}
.sidebar ul {
list-style: none;
padding: 0;
margin: 0;
}
.sidebar li {
margin: 0;
}
.sidebar a {
color: var(--text-secondary);
text-decoration: none;
display: block;
padding: 0.5rem 0.75rem;
border-radius: 4px;
transition: all 0.2s ease;
font-size: 0.95rem;
}
.sidebar a:hover {
color: var(--primary-color);
background: rgba(21, 153, 87, 0.08);
padding-left: 1rem;
}
/* Main Content */
.main-content-wrapper {
flex: 1;
min-width: 0;
}
.main-content {
background: white;
padding: 2rem;
border-radius: 8px;
box-shadow: var(--shadow);
}
.main-content h1 {
color: var(--text-primary);
font-size: 2rem;
margin-top: 0;
margin-bottom: 1.5rem;
padding-bottom: 0.75rem;
border-bottom: 3px solid var(--primary-color);
}
.main-content h2 {
color: var(--text-primary);
font-size: 1.5rem;
margin-top: 2rem;
margin-bottom: 1rem;
padding-bottom: 0.5rem;
border-bottom: 2px solid var(--border-color);
}
.main-content h3 {
color: var(--text-primary);
font-size: 1.25rem;
margin-top: 1.5rem;
margin-bottom: 0.75rem;
}
.main-content code {
background: var(--background-alt);
padding: 0.2rem 0.4rem;
border-radius: 3px;
font-size: 0.9em;
color: var(--secondary-color);
}
.main-content pre {
background: var(--background-alt);
padding: 1rem;
border-radius: 6px;
overflow-x: auto;
border: 1px solid var(--border-color);
}
.main-content pre code {
background: none;
padding: 0;
color: var(--text-primary);
}
.main-content a {
color: var(--primary-color);
text-decoration: underline;
text-decoration-color: rgba(21, 153, 87, 0.3);
text-decoration-thickness: 1px;
text-underline-offset: 2px;
transition: all 0.2s ease;
}
.main-content a:hover {
color: var(--primary-dark);
text-decoration-color: var(--primary-dark);
text-decoration-thickness: 2px;
}
.main-content table {
width: 100%;
border-collapse: collapse;
margin: 1rem 0;
}
.main-content th,
.main-content td {
padding: 0.75rem;
border: 1px solid var(--border-color);
text-align: left;
}
.main-content th {
background: var(--background-alt);
font-weight: 600;
}
.main-content blockquote {
margin: 1rem 0;
padding: 1rem;
border-left: 4px solid var(--primary-color);
background: var(--background-alt);
border-radius: 0 4px 4px 0;
}
/* Footer */
.site-footer {
text-align: center;
padding: 2rem;
border-top: 1px solid var(--border-color);
margin-top: 3rem;
background: var(--background-alt);
color: var(--text-secondary);
font-size: 0.9rem;
}
.site-footer a {
color: var(--primary-color);
text-decoration: none;
}
.site-footer a:hover {
text-decoration: underline;
}
/* Mobile Responsiveness */
@media (max-width: 64em) {
.project-name {
font-size: 2rem;
}
.page-header {
padding: 2rem 1rem;
}
.main-content {
padding: 1.5rem;
}
.sidebar {
margin-bottom: 1.5rem;
}
}
/* Scrollbar Styling */
.sidebar::-webkit-scrollbar {
width: 8px;
}
.sidebar::-webkit-scrollbar-track {
background: var(--background-alt);
}
.sidebar::-webkit-scrollbar-thumb {
background: var(--border-color);
border-radius: 4px;
}
.sidebar::-webkit-scrollbar-thumb:hover {
background: var(--text-secondary);
}
</style>
</head>
<body>
{% include nav.html %}
<section class="page-header">
<h1 class="project-name">{{ site.title }}</h1>
<h2 class="project-tagline">{{ site.description }}</h2>
<a href="https://github.com/CBDDC/ZB.MOM.WW.CBDDC.Net" class="btn">View on GitHub</a>
<a href="https://github.com/CBDDC/ZB.MOM.WW.CBDDC.Net/releases" class="btn">Download</a>
</section>
<div class="page-content">
<!-- Sidebar Logic -->
{% assign version = page.url | split: '/' %}
{% assign current_version = version[1] %}
<!-- If current_version is empty or doesn't start with 'v', treat as v0.9 (root level) -->
{% if current_version == '' or current_version contains '.html' or current_version contains '.md' %}
{% assign current_version = 'v0.9' %}
{% endif %}
{% if current_version == 'v0.9' or current_version == 'v0.8' or current_version == 'v0.7' or current_version == 'v0.6' or current_version == 'v0.5' or current_version ==
'v0.4' or current_version ==
'v0.2' %}
<nav class="sidebar">
<h3>Docs {{ current_version }}</h3>
<ul>
{% for item in site.data.navigation[current_version] %}
<li><a href="{{ item.url | relative_url }}">{{ item.title }}</a></li>
{% endfor %}
</ul>
</nav>
{% endif %}
<div class="main-content-wrapper">
<section class="main-content">
{{ content }}
</section>
</div>
</div>
<footer class="site-footer">
<span class="site-footer-owner"><a href="https://cbddc.com">CBDDC</a> is maintained by <a
href="https://github.com/mrdevrobot">MrDevRobot</a>.</span>
<span class="site-footer-credits">This page was generated by <a href="https://pages.github.com">GitHub
Pages</a>.</span>
</footer>
</body>
</html>

142
docs/api-reference.md Executable file
View File

@@ -0,0 +1,142 @@
# API Reference
## PeerDatabase
The entry point for interacting with the database.
```csharp
public class PeerDatabase : IPeerDatabase
{
public PeerDatabase(IPeerStore store, string nodeId = "local");
// Non-generic collection access
public IPeerCollection Collection(string name);
// Generic collection access (type-safe)
public IPeerCollection<T> Collection<T>(string? customName = null);
}
```
### Collection Naming Convention
When using `Collection<T>()`, the collection name defaults to `typeof(T).Name.ToLowerInvariant()`. You can change this globally using `CBDDCMapper`.
```csharp
// Uses default collection name "user"
var users = db.Collection<User>();
// Custom name override
var users = db.Collection<User>("custom_users");
```
## IPeerCollection<T>
Represents a collection of documents (like a Table or Container).
```csharp
public interface IPeerCollection<T> : IPeerCollection
{
// Single Operations
Task Put(string key, T document, CancellationToken cancellationToken = default);
Task<T> Get(string key, CancellationToken cancellationToken = default);
Task Delete(string key, CancellationToken cancellationToken = default);
// Batch Operations
Task PutMany(IEnumerable<T> documents, CancellationToken cancellationToken = default);
Task DeleteMany(IEnumerable<string> keys, CancellationToken cancellationToken = default);
// Queries
Task<IEnumerable<T>> Find(Expression<Func<T, bool>> predicate, CancellationToken cancellationToken = default);
Task<int> Count(Expression<Func<T, bool>>? predicate = null, CancellationToken cancellationToken = default);
}
```
### Batch Operations
For bulk inserts or deletes, use `PutMany` and `DeleteMany` for better performance and atomicity.
```csharp
var users = new List<User> { ... };
await db.Collection<User>().PutMany(users);
```
### Count
Get the total number of documents matching a filter efficiently (database-side).
```csharp
int total = await db.Collection<User>().Count();
int active = await db.Collection<User>().Count(u => u.IsActive);
```
## Global Configuration (CBDDCMapper)
You can configure entity mappings globally instead of using attributes.
```csharp
CBDDCMapper.Global.Entity<Product>()
.Collection("products_v2")
.Index(p => p.Price)
.Index(p => p.Category);
```
## Primary Keys and Indexes
### Attributes
```csharp
using ZB.MOM.WW.CBDDC.Core.Metadata;
public class User
{
[PrimaryKey(AutoGenerate = true)]
public string Id { get; set; } = "";
[Indexed]
public int Age { get; set; }
}
```
### Indexed Fields
Indexes improve query performance (e.g., `Find(u => u.Age > 18)`).
You can define them via `[Indexed]` attribute or `CBDDCMapper`.
> **Note**: CBDDC automatically creates SQLite indexes for these properties.
## Exceptions
CBDDC throws specific exceptions for better error handling:
- `CBDDCException` (Base class)
- `DocumentNotFoundException`
- `CBDDCConcurrencyException`
- `TimeoutException`
- `NetworkException`
- `PersistenceException`
```csharp
try
{
await db.Collection<User>().Get("invalid-id");
}
catch (DocumentNotFoundException ex)
{
Console.WriteLine($"Document {ex.Key} not found in {ex.Collection}");
}
```
## Querying
Supported LINQ operators:
- Equality: `==`, `!=`
- Comparison: `>`, `<`, `>=`, `<=`
- Boolean: `&&`, `||`
- String: `Contains`, `StartsWith`, `EndsWith` (Mapped to SQL `LIKE`)
- Collections: `Contains` (IN clause)
```csharp
// Find users in list of IDs
var ids = new[] { "1", "2" };
await col.Find(u => ids.Contains(u.Id));
```

70
docs/architecture.md Executable file
View File

@@ -0,0 +1,70 @@
# Architecture & Concepts
## Design Philosophy
CBDDC is designed for **Local Area Networks (LAN)** and **Local-First** scenarios.
It does not rely on a central master server. Every node is equal (Peer-to-Peer).
**Target Deployment**: Trusted LAN environments (offices, homes, private networks)
**Cross-Platform**: Windows, Linux, macOS (.NET 8.0+, with .NET 6.0 and .NET Standard 2.0 support)
### HLC (Hybrid Logical Clock)
To resolve conflicts without a central authority, we use **Hybrid Logical Clocks**.
This allows us to determine the "happened-before" relationship between events even if system clocks are slightly skewed.
In case of concurrent edits, the "Last Write Wins" (LWW) policy based on HLC is applied.
## Synchronization
### Anti-Entropy
When two nodes connect, they exchange their latest HLC timestamps.
- If Node A is ahead of Node B, Node B "pulls" the missing operations from Node A.
- If Node B is ahead, Node A "pushes" its operations.
### Gossip Protocol
Nodes discover each other via UDP Broadcast (LAN) and then form random TCP connections to gossip updates.
This ensures that updates propagate exponentially through the network (Epidemic Algorithm).
### Snapshots & Fast Recovery
To optimize reconnection, each node maintains a **Snapshot** of the last known state (Hash & Timestamp) for every peer.
- When re-connecting, nodes compare their snapshot state.
- If the chain hash matches, they only exchange the delta.
- This avoids re-processing the entire operation history and ensures efficient gap recovery.
### Peer-Confirmed Oplog Pruning
CBDDC maintenance pruning now uses a two-cutoff model:
- **Retention cutoff**: derived from `OplogRetentionHours` (time-based policy).
- **Confirmation cutoff**: the oldest confirmation watermark across all active tracked peers and relevant source nodes.
The effective prune cutoff is:
- `min(retention cutoff, confirmation cutoff)` when confirmation data is complete.
- retention cutoff only when confirmation tracking is not configured or when there are no active tracked peers.
- **no prune** for that maintenance cycle when any active tracked peer is missing confirmation for any relevant source stream.
Key semantics:
- Relevant source streams are taken from the local non-default vector clock entries.
- A peer only gates pruning while it is active in peer tracking.
- Removing peer tracking excludes that peer from prune gating.
- Maintenance logs include explicit skip reasons when pruning is blocked (for example,
missing confirmation for a tracked peer/source pair).
## Security Disclaimer
::: warning FOR LAN USE ONLY
**CBDDC is designed for trusted Local Area Networks.**
:::
- **P2P Mesh Mode**: Designed for **LAN/VPN** use. Uses raw TCP and UDP broadcast. Not safe for public internet by default.
- **ASP.NET Core Server**: Designed for controlled server deployments. Supports standard HTTPS and hosted sync endpoints.
- **Transport**: Data is transmitted via raw TCP. There is **NO Encryption (TLS/SSL)** by default.
- **Authentication**: A basic "Shared Key" mechanism is implemented. Nodes must share the same `AuthToken` to sync.
- **Authorization**: Once authenticated, a node has full read/write access to all collections.
**Recommendation**:
- Use only within trusted private networks (LAN, VPN, or localhost)
- For internet deployment, implement TLS, proper authentication, and firewall rules
- Consider the production hardening features for resilience on LAN
**Cross-Platform Support**: Runs on Windows, Linux, and macOS with .NET 8.0+ (also supports .NET 6.0 and .NET Standard 2.0).

251
docs/conflict-resolution.md Executable file
View File

@@ -0,0 +1,251 @@
# Conflict Resolution
**CBDDC v0.6.0** introduces **pluggable conflict resolution strategies** to handle concurrent updates to the same document across different nodes.
## Overview
When two nodes modify the same document offline and later sync, a conflict occurs. CBDDC provides two built-in strategies:
1. **Last Write Wins (LWW)** - Simple, timestamp-based resolution
2. **Recursive Merge** - Intelligent JSON merging with array handling
## Conflict Resolution Strategies
### Last Write Wins (LWW)
**How it works:**
- Each document has a Hybrid Logical Clock (HLC) timestamp
- During sync, the document with the **highest timestamp wins**
- Conflicts are resolved automatically with no merge attempt
**Pros:**
- ✅ Simple and predictable
- ✅ Fast (no merge computation)
- ✅ No data corruption or invalid states
**Cons:**
- ❌ Data loss - one change is discarded entirely
- ❌ Not suitable for collaborative editing
**Use Cases:**
- Configuration data with infrequent updates
- Reference data (product catalogs, price lists)
- Single-user scenarios with backup sync
#### Example
```csharp
// Both nodes start with same document
{ "id": "doc-1", "name": "Alice", "age": 25 }
// Node A updates (timestamp: 100)
{ "id": "doc-1", "name": "Alice", "age": 26 }
// Node B updates (timestamp: 105)
{ "id": "doc-1", "name": "Alicia", "age": 25 }
// After sync: Node B wins (higher timestamp)
{ "id": "doc-1", "name": "Alicia", "age": 25 }
// Node A's age change is LOST
```
---
### Recursive Merge
**How it works:**
- Performs deep JSON merge of conflicting documents
- Uses the **highest timestamp** for each individual field
- Arrays with `id` or `_id` fields are merged by identity
- Arrays without IDs are concatenated and deduplicated
**Pros:**
- ✅ Preserves changes from both nodes
- ✅ Suitable for collaborative scenarios
- ✅ Intelligent array handling
**Cons:**
- ❌ More complex logic
- ❌ Slightly slower (~5-10ms overhead)
- ❌ May produce unexpected results with complex nested structures
**Use Cases:**
- TodoLists, shopping carts (demonstrated in samples)
- Collaborative documents
- Complex objects with independent fields
- Data where every change matters
#### Example: Field-Level Merge
```csharp
// Both nodes start with same document
{ "id": "doc-1", "name": "Alice", "age": 25 }
// Node A updates (timestamp: 100)
{ "id": "doc-1", "name": "Alice", "age": 26 }
// Node B updates (timestamp: 105)
{ "id": "doc-1", "name": "Alicia", "age": 25 }
// After Recursive Merge:
{ "id": "doc-1", "name": "Alicia", "age": 26 }
// Uses latest timestamp for each field independently
```
#### Example: Array Merging with IDs
```csharp
// Initial TodoList
{
"id": "list-1",
"name": "Shopping",
"items": [
{ "id": "1", "task": "Buy milk", "completed": false },
{ "id": "2", "task": "Buy bread", "completed": false }
]
}
// Node A: Completes milk, adds eggs
{
"items": [
{ "id": "1", "task": "Buy milk", "completed": true },
{ "id": "2", "task": "Buy bread", "completed": false },
{ "id": "3", "task": "Buy eggs", "completed": false }
]
}
// Node B: Completes bread, adds cheese
{
"items": [
{ "id": "1", "task": "Buy milk", "completed": false },
{ "id": "2", "task": "Buy bread", "completed": true },
{ "id": "4", "task": "Buy cheese", "completed": false }
]
}
// After Recursive Merge: ALL changes preserved!
{
"items": [
{ "id": "1", "task": "Buy milk", "completed": true }, // A's completion
{ "id": "2", "task": "Buy bread", "completed": true }, // B's completion
{ "id": "3", "task": "Buy eggs", "completed": false }, // A's addition
{ "id": "4", "task": "Buy cheese", "completed": false } // B's addition
]
}
```
## Configuration
### Select Strategy at Startup
```csharp
using ZB.MOM.WW.CBDDC.Core.Sync;
// Last Write Wins (default)
services.AddSingleton<IConflictResolver, LastWriteWinsConflictResolver>();
// OR Recursive Merge
services.AddSingleton<IConflictResolver, RecursiveNodeMergeConflictResolver>();
```
### Console Sample
```bash
# Use Recursive Merge
dotnet run --merge
# Use Last Write Wins (default)
dotnet run
```
### UI Clients
Resolver selection can be exposed in your UI:
1. Choose "Recursive Merge" or "Last Write Wins"
2. Save configuration
3. Restart the application if required by your host
## Interactive Demo
A UI demo can expose a **"Run Conflict Demo"** action that:
1. Creates a TodoList with 2 items
2. Simulates concurrent edits from two "nodes"
3. Shows the merged result
4. Compares LWW vs Recursive Merge behavior
**Try it yourself** to see the difference!
## Best Practices
### Use LWW When:
- Only one user/node typically writes
- Data is reference/configuration
- Simplicity is more important than preserving every change
- Performance is critical
### Use Recursive Merge When:
- Multiple users collaborate
- Every change is valuable (e.g., TodoItems, cart items)
- Data has independent fields that can conflict
- Arrays have `id` fields for identity
### Avoid Conflicts Entirely:
- Use **different collections** for different data types
- Implement **optimistic locking** with version fields
- Design data models to minimize overlapping writes
## Custom Resolvers
You can implement `IConflictResolver` for custom logic:
```csharp
public class CustomResolver : IConflictResolver
{
public ValueTask<string> ResolveConflict(
string localJson,
string remoteJson,
long localTimestamp,
long remoteTimestamp)
{
// Your custom merge logic here
return new ValueTask<string>(result);
}
}
```
Register your resolver:
```csharp
services.AddSingleton<IConflictResolver, CustomResolver>();
```
## Performance
Benchmark results (1000 conflict resolutions):
| Strategy | Avg Time | Throughput |
|----------|----------|------------|
| Last Write Wins | 0.05ms | 20,000 ops/sec |
| Recursive Merge | 0.15ms | 6,600 ops/sec |
**Recommendation**: Performance difference is negligible for most applications. Choose based on data preservation needs.
## FAQ
**Q: Can I switch resolvers at runtime?**
A: No. The resolver is injected at startup. Changing requires application restart.
**Q: What happens if I change resolvers after data exists?**
A: Existing data is unaffected. Only future conflicts use the new strategy.
**Q: Can different nodes use different resolvers?**
A: Technically yes, but **not recommended**. All nodes should use the same strategy for consistency.
**Q: Does this handle schema changes?**
A: No. Conflict resolution assumes both documents have compatible schemas.
---
**See Also:**
- [Getting Started](getting-started.html)
- [Architecture](architecture.html)
- [Security](security.html)

File diff suppressed because it is too large Load Diff

146
docs/deployment-lan.md Executable file
View File

@@ -0,0 +1,146 @@
# CBDDC - Deployment Guide for LAN
## Target Environment
CBDDC is specifically designed for **Local Area Networks (LAN)** in trusted environments:
**Ideal Use Cases**:
- Office networks (employee workstations, kiosks)
- Home automation systems
- Retail point-of-sale systems (POS)
- Edge computing deployments
- Private industrial networks
- Development/testing environments
**NOT Recommended**:
- Public internet deployment (without significant security enhancements)
- Multi-tenant SaaS applications
- Untrusted network environments
## Cross-Platform Support
CBDDC runs on all major operating systems:
| Platform | Support | Notes |
|----------|---------|-------|
| **Windows** | ✅ Full | Windows 10+, Server 2019+ |
| **Linux** | ✅ Full | Ubuntu, Debian, RHEL, Alpine |
| **macOS** | ✅ Full | macOS 11+ (Big Sur and later) |
**Requirements**: .NET 8.0+ Runtime (also compatible with .NET 6.0 and .NET Standard 2.0)
## LAN Deployment Checklist
### Network Configuration
- [ ] **Firewall Rules**: Open TCP port (default: 5000) and UDP port (default: 6000)
- [ ] **Broadcast Domain**: Ensure nodes are in the same subnet for UDP discovery
- [ ] **Network Stability**: LAN should have reasonable stability (WiFi or wired)
- [ ] **Bandwidth**: Adequate for sync operations (typically low, < 1 Mbps)
### Security Configuration
- [ ] **Cluster Key**: Configure unique cluster authentication key
- [ ] **Network Isolation**: Use VLANs or network segmentation
- [ ] **Access Control**: Limit network access to authorized devices
- [ ] **Monitoring**: Set up logging and health checks
### Application Configuration
```json
{
"CBDDC": {
"Network": {
"TcpPort": 5000,
"UdpPort": 6000,
"LocalhostOnly": false
},
"Persistence": {
"DatabasePath": "/var/lib/cbddc/data.db",
"EnableWalMode": true,
"EnableAutoBackup": true,
"BackupPath": "/var/lib/cbddc/backups"
}
}
}
```
### Platform-Specific Considerations
#### Windows
- Use Windows Services for background operation
- Configure Windows Firewall rules
- Consider SQLite file locking on network shares
#### Linux
- Use systemd for service management
- Set appropriate file permissions
- Consider SELinux/AppArmor policies
#### macOS
- Use launchd for background services
- Configure macOS firewall
- Handle macOS sleep/wake for laptops
## Example: Office Network Deployment
### Scenario
10 workstations in an office need to sync product catalog data.
### Setup
1. **Network**: All on 192.168.1.0/24 subnet
2. **Nodes**: Each workstation runs CBDDC
3. **Discovery**: UDP broadcast for automatic peer discovery
4. **Sync**: TCP for data synchronization
5. **Storage**: Local SQLite database per workstation
### Benefits
- **No Internet Required**: Works during internet outages
- **Low Latency**: Local network = fast reads/writes
- **Resilient**: No single point of failure
- **Offline Capable**: Each workstation works independently
## Troubleshooting
### Nodes Not Discovering Each Other
- Check firewall rules for UDP port
- Verify nodes are on same broadcast domain
- Check cluster key matches on all nodes
### Slow Synchronization
- Check network bandwidth
- Verify no packet loss
- Review batch size configuration
### Database Corruption
- Verify WAL mode is enabled
- Check disk space
- Review backup/restore procedures
## Security Best Practices for LAN
1. **Network Segmentation**: Isolate CBDDC network from public networks
2. **Cluster Authentication**: Use strong cluster keys
3. **Access Control**: Limit which devices can join the network
4. **Monitoring**: Log all sync operations
5. **Regular Backups**: Automated backup to separate storage
6. **Update Policy**: Keep .NET runtime updated
## NOT Recommended for Internet
CBDDC **should NOT** be deployed on public internet without:
- TLS/SSL encryption for TCP connections
- Proper authentication beyond cluster key
- Network firewalls and security groups
- DDoS protection
- Rate limiting
- Intrusion detection
For internet deployment, consider traditional client-server databases instead.
## Support
For LAN deployment questions, see:
- [Production Hardening Guide](production-hardening.md)
- [API Reference](api-reference.md)
- [Architecture Documentation](architecture.md)

82
docs/deployment-modes.md Executable file
View File

@@ -0,0 +1,82 @@
# CBDDC Deployment Mode
ZB.MOM.WW.CBDDC.Hosting supports a single deployment mode.
## Single-Cluster Mode
**Best for:**
- Dedicated database servers
- Simple deployments
- Development/testing environments
- Small-to-medium applications
### Architecture
```
┌─────────────────────┐
│ ASP.NET Server │
│ │
│ ┌───────────────┐ │
│ │ CBDDC │ │
│ │ (1 cluster) │ │
│ └───────────────┘ │
│ │ │
│ [TCP:5001] │
└─────────────────────┘
```
### Configuration
```csharp
builder.Services.AddCBDDCHostingSingleCluster(options =>
{
options.NodeId = "server-01";
options.TcpPort = 5001;
});
```
### Characteristics
- One database per server instance
- Simple configuration
- Easy to scale horizontally (multiple servers = multiple databases)
- No port collision issues
- Recommended for most use cases
## Server Behavior
Servers use respond-only operation:
- Accept incoming TCP sync connections
- Respond to sync requests
- Serve data to clients
- Maintain persistence
- Do not initiate outbound sync
- Do not perform UDP discovery
- Do not connect to other servers automatically
## Persistence Layer Compatibility
Single-cluster mode works with all persistence providers:
| Provider | Support | Notes |
|----------|---------|-------|
| SQLite (Direct) | Yes | One DB file per cluster |
| EF Core | Yes | Flexible DB options |
| PostgreSQL | Yes | Best for production |
## Scaling Strategy
### Horizontal Scaling
```
Load Balancer
├─> Server 1 (Cluster A) [PostgreSQL A]
├─> Server 2 (Cluster B) [PostgreSQL B]
└─> Server 3 (Cluster C) [PostgreSQL C]
```
## Recommendation
Use single-cluster mode for ASP.NET hosting. It is simpler, robust, and straightforward to scale.

66
docs/dynamic-reconfiguration.md Executable file
View File

@@ -0,0 +1,66 @@
---
layout: default
title: Dynamic Reconfiguration
nav_order: 8
---
# Dynamic Reconfiguration & Leader Election
CBDDC v0.8.0 introduces support for dynamic reconfiguration, allowing nodes to change their role, listening ports, and database identity without a full process restart. This is essential for containerized environments and long-running services.
## Configuration Provider
The core of this feature is the `IPeerNodeConfigurationProvider` interface (available in .Net, Kotlin, and NodeJs).
```typescript
// NodeJs Example
interface IPeerNodeConfigurationProvider {
getConfiguration(): Promise<PeerNodeConfiguration>;
onConfigurationChanged(callback: (config: PeerNodeConfiguration) => void): void;
}
```
Implementations can watch a file (e.g., `config.json`), a remote configuration server, or environment variables. When the configuration changes, the provider fires an event.
### Usage
Services like `TcpSyncServer` subscribe to these events.
- **Port Change**: The server stops the listener and restarts on the new port.
- **NodeId Change**: If the NodeID changes, it typically implies a change of identity. The persistence layer (SQLite) is bound to a specific NodeID (internal HLC history). Thus, a NodeID change requires closing the existing DB and opening/creating a new one matching the new ID.
## Known Peers (Static Remote)
You can now persist a list of "Known Peers" (Static Remote nodes) that survive restarts. This is useful for:
- Configuring a fixed set of Cloud Gateways.
- Connecting to specific peers outside the local broadcast domain.
### API
```typescript
// Add a static peer
await peerStore.saveRemotePeer({
nodeId: "gateway-1",
address: "192.168.1.50:25000",
type: PeerType.StaticRemote,
...
});
// Retrieve
const peers = await peerStore.getRemotePeers();
```
## Leader Election (Bully Algorithm)
For Cloud Synchronization, usually one node in a LAN cluster acts as the "Gateway" to the cloud to avoid redundant traffic. CBDDC implements the **Bully Algorithm** for automatic leader election.
- **Rule**: Among all discovered peers (LAN or Static) that are eligible, the node with the **lexicographically smallest NodeID** declares itself the Leader.
- **Mechanism**: Each node periodically checks the list of active peers. If it finds no other peer with a smaller ID than itself, it assumes leadership.
- **Event**: The `LeaderElectionService` emits a `LeadershipChanged` event (boolean `isLeader`).
- **Action**: The `SyncOrchestrator` uses this flag to decide whether to push/pull from Cloud Remotes.
### NodeJs Logic
```typescript
const amILeader = !activePeers.some(p => p.nodeId < myNodeId);
```
This simple consensus mechanism works well for small-to-medium clusters on a low-latency LAN.

188
docs/getting-started.md Executable file
View File

@@ -0,0 +1,188 @@
# Getting Started (v0.9.0)
## Installation
CBDDC is available as a set of NuGet packages for .NET 8.0, .NET 6.0, and .NET Standard 2.0.
```bash
dotnet add package ZB.MOM.WW.CBDDC.Core
dotnet add package CBDDC.Network
dotnet add package ZB.MOM.WW.CBDDC.Persistence.Sqlite
```
### Cloud & Enterprise Packages
For ASP.NET Core hosting and enterprise database support:
```bash
# ASP.NET Core hosting
dotnet add package ZB.MOM.WW.CBDDC.Hosting
# Entity Framework Core (SQL Server, MySQL, SQLite)
dotnet add package ZB.MOM.WW.CBDDC.Persistence.EntityFramework
# PostgreSQL with JSONB optimization
dotnet add package ZB.MOM.WW.CBDDC.Persistence.PostgreSQL
```
## Requirements
- **.NET 8.0+ Runtime** (recommended) or .NET 6.0+
- **SQLite** (included via Microsoft.Data.Sqlite)
- **PostgreSQL 12+** (optional, for PostgreSQL persistence)
- **SQL Server 2016+** (optional, for SQL Server persistence)
## Basic Usage
### 1. Initialize the Store
Use `SqlitePeerStore` for persistence. Supported on Windows, Linux, and macOS.
```csharp
using ZB.MOM.WW.CBDDC.Core;
using ZB.MOM.WW.CBDDC.Core.Sync;
using ZB.MOM.WW.CBDDC.Persistence.Sqlite;
using CBDDC.Network.Security;
// Choose conflict resolver (v0.6.0+)
var resolver = new RecursiveNodeMergeConflictResolver(); // OR LastWriteWinsConflictResolver()
var store = new SqlitePeerStore("Data Source=my-node.db", logger, resolver);
// Automatically creates tables on first run
```
### 2. Configure Networking (with Optional Security)
Use `AddCBDDCNetwork` extension method to register services.
```csharp
var services = new ServiceCollection();
string myNodeId = "node-1";
int port = 5001;
string authToken = "my-secret-cluster-key";
services.AddSingleton<IPeerStore>(store);
// Optional: Enable encryption (v0.6.0+)
services.AddSingleton<IPeerHandshakeService, SecureHandshakeService>();
services.AddCBDDCNetwork(myNodeId, port, authToken);
```
### 3. Start the Node
```csharp
var provider = services.BuildServiceProvider();
var node = provider.GetRequiredService<CBDDCNode>();
node.Start();
```
### 4. CRUD Operations
Interact with data using `PeerDatabase`.
```csharp
var db = new PeerDatabase(store, "my-node-id"); // Node ID used for HLC clock
await db.InitializeAsync();
var users = db.Collection("users");
// Put
await users.Put("user-1", new { Name = "Alice", Age = 30 });
// Get
var user = await users.Get<User>("user-1");
// Query
var results = await users.Find<User>(u => u.Age > 20);
```
## ASP.NET Core Deployment (v0.8.0+)
### Single Cluster Mode (Recommended)
Perfect for production deployments with dedicated database servers:
```csharp
// Program.cs
var builder = WebApplication.CreateBuilder(args);
// Use PostgreSQL for production
builder.Services.AddCBDDCPostgreSql(
builder.Configuration.GetConnectionString("CBDDC"));
// Configure single cluster
builder.Services.AddCBDDCHostingSingleCluster(options =>
{
options.NodeId = "server-01";
options.TcpPort = 5001;
});
var app = builder.Build();
app.MapHealthChecks("/health");
app.Run();
```
See [Deployment Modes](deployment-modes.md) for deployment details.
## What's New in v0.9.0
### 🚀 Production Enhancements
- **Improved ASP.NET Core Sample**: Enhanced error handling and better examples
- **EF Core Stability**: Fixed runtime issues for all persistence providers
- **Sync Refinements**: More reliable synchronization across hosted deployments
### 📸 Snapshots (v0.8.6)
- **Fast Reconnection**: Peers resume sync from the last known state
- **Optimized Recovery**: Prevents re-processing of already applied operations
- **Automatic Management**: Snapshot metadata tracked per peer
See [CHANGELOG](https://github.com/CBDDC/ZB.MOM.WW.CBDDC.Net/blob/main/CHANGELOG.md) for complete version history.
## What's New in v0.8.0
### ☁️ Cloud Infrastructure
- **ASP.NET Core Hosting**: Single-cluster deployment mode
- **Multi-Database Support**: SQL Server, PostgreSQL, MySQL, SQLite via EF Core
- **PostgreSQL Optimization**: JSONB storage with GIN indexes
- **Shared-token Authentication**: Secure controlled deployments
- **Health Checks**: Production monitoring and observability
[Learn more about Cloud Deployment →](deployment-modes.md)
## What's New in v0.7.0
### 📦 Efficient Networking
- **Brotli Compression**: Data is automatically compressed, significantly reducing bandwidth usage
- **Protocol v4**: Enhanced framing and security negotiation
## What's New in v0.6.0
### 🔐 Secure Networking
Protect your data in transit with:
- **ECDH** key exchange
- **AES-256-CBC** encryption
- **HMAC-SHA256** authentication
[Learn more about Security →](security.md)
### 🔀 Advanced Conflict Resolution
Choose your strategy:
- **Last Write Wins** - Simple, fast, timestamp-based
- **Recursive Merge** - Intelligent JSON merging with array ID detection
[Learn more about Conflict Resolution →](conflict-resolution.md)
### 🎯 Multi-Target Framework Support
- `netstandard2.0` - Maximum compatibility
- `net6.0` - Modern features
- `net8.0` - Latest performance optimizations
## Next Steps
- [Architecture Overview](architecture.html) - Understand HLC, Gossip Protocol, and mesh networking
- [Persistence Providers](persistence-providers.html) - Choose the right database for your deployment
- [Deployment Modes](deployment-modes.html) - Single-cluster deployment strategy
- [Security Configuration](security.html) - Encryption and authentication
- [Conflict Resolution Strategies](conflict-resolution.html) - LWW vs Recursive Merge
- [Production Hardening](production-hardening.html) - Best practices and monitoring
- [API Reference](api-reference.html) - Complete API documentation

57
docs/index.md Executable file
View File

@@ -0,0 +1,57 @@
---
layout: default
---
# CBDDC Documentation
Welcome to the CBDDC documentation for **version 0.9.0**.
## What's New in v0.9.0
Version 0.9.0 brings enhanced ASP.NET Core support, improved EF Core runtime stability, and refined synchronization and persistence layers.
- **ASP.NET Core Enhancements**: Improved sample application with better error handling
- **EF Core Stability**: Fixed runtime issues and improved persistence layer reliability
- **Sync & Persistence**: Enhanced stability across all persistence providers
- **Production Ready**: All features tested and stable for production use
See the [CHANGELOG](https://github.com/CBDDC/ZB.MOM.WW.CBDDC.Net/blob/main/CHANGELOG.md) for complete details.
## Documentation
### Getting Started
* [**Getting Started**](getting-started.html) - Installation, basic setup, and your first CBDDC application
### Core Concepts
* [Architecture](architecture.html) - Understanding HLC, Gossip Protocol, and P2P mesh networking
* [API Reference](api-reference.html) - Complete API documentation and examples
* [Querying](querying.html) - Data querying patterns and LINQ support
### Persistence & Storage
* [Persistence Providers](persistence-providers.html) - SQLite, EF Core, PostgreSQL comparison and configuration
* [Deployment Modes](deployment-modes.html) - Single-cluster deployment strategy
### Networking & Security
* [Security](security.html) - Encryption, authentication, and secure networking
* [Conflict Resolution](conflict-resolution.html) - LWW and Recursive Merge strategies
* [Network Telemetry](network-telemetry.html) - Monitoring and diagnostics
* [Dynamic Reconfiguration](dynamic-reconfiguration.html) - Runtime configuration and leader election
* [Remote Peer Configuration](remote-peer-configuration.html) - Managing remote peers and tracking lifecycle
* [Upgrade: Peer-Confirmed Pruning](upgrade-peer-confirmed-pruning.html) - Adoption and rollout notes
### Deployment & Operations
* [Deployment (LAN)](deployment-lan.html) - Platform-specific deployment instructions
* [Production Hardening](production-hardening.html) - Configuration, monitoring, and best practices
* [Peer Deprecation & Removal Runbook](peer-deprecation-removal-runbook.html) - Operational deprecation/removal workflow
## Previous Versions
- [v0.8.x Documentation](v0.8/getting-started.html)
- [v0.7.x Documentation](v0.7/getting-started.html)
- [v0.6.x Documentation](v0.6/getting-started.html)
## Links
* [GitHub Repository](https://github.com/CBDDC/ZB.MOM.WW.CBDDC.Net)
* [NuGet Packages](https://www.nuget.org/packages?q=CBDDC)
* [Changelog](https://github.com/CBDDC/ZB.MOM.WW.CBDDC.Net/blob/main/CHANGELOG.md)

71
docs/network-telemetry.md Executable file
View File

@@ -0,0 +1,71 @@
---
layout: default
title: Network Telemetry
nav_order: 9
---
# Network Telemetry
Starting from v0.7.2, CBDDC includes a built-in telemetry system to monitor network performance, compression efficiency, and encryption overhead. This system is designed to have near-zero impact on runtime performance while providing valuable insights into peer-to-peer communication.
## Collected Metrics
The system collects the following metrics:
| Metric Type | Description | Unit |
| :--- | :--- | :--- |
| **CompressionRatio** | Ratio of compressed size to original size. Lower is better (e.g., 0.4 means 60% reduction). | Ratio (0.0 - 1.0) |
| **EncryptionTime** | Time taken to encrypt a message payload. | Milliseconds (ms) |
| **DecryptionTime** | Time taken to decrypt a received message. | Milliseconds (ms) |
| **RoundTripTime** | Time taken for a `GetClock` request-response cycle (latency). | Milliseconds (ms) |
## Architecture
The telemetry service uses a high-performance, non-blocking architecture:
1. **Capture**: Metrics are pushed to a `System.Threading.Channels` queue, ensuring the critical network path is never blocked.
2. **Aggregation**: A background worker aggregates samples into **1-second buckets**.
3. **Rolling Windows**: Averages are calculated on-the-fly for **1m, 5m, 10m, and 30m** windows.
4. **Persistence**: Aggregated data is automatically persisted to a local binary file (`cbddc_metrics.bin`) every minute.
## Configuration
Telemetry is enabled by default when using the standard DI extensions.
### Dependency Injection
When using `AddCBDDCNetwork`, the `INetworkTelemetryService` is automatically registered as a singleton.
```csharp
services.AddCBDDCCore()
.AddCBDDCNetwork<MyNodeConfiguration>();
```
Use `INetworkTelemetryService` to access metric data programmatically:
```csharp
public class MyService
{
private readonly INetworkTelemetryService _telemetry;
public MyService(INetworkTelemetryService telemetry)
{
_telemetry = telemetry;
}
public void PrintMetrics()
{
var snapshot = _telemetry.GetSnapshot();
// Access snapshot[MetricType.CompressionRatio][60] for 1-minute average
}
}
```
## Security & Privacy
- Telemetry data is **stored locally** (on the device/server).
- No data is sent to external servers or other peers.
- Metric data does not contain any PII or payload content.
## Viewing Metrics
You can visualize these metrics in a custom dashboard (desktop or web) for real-time monitoring.

View File

@@ -0,0 +1,66 @@
# Peer Deprecation & Removal Runbook
Operational workflow for safely deprecating or removing peers in clusters using
peer-confirmed pruning.
## When to use this runbook
- A site is permanently decommissioned.
- A peer has been unreachable long enough to block prune progress.
- A peer is being replaced and should stop gating prune decisions.
## Decision matrix
| Scenario | Action |
|------|-----------|
| Peer is temporarily offline and expected to return soon | Keep tracking; monitor lag and confirmations. |
| Peer should stay configured but must stop gating pruning | `RemovePeerTrackingAsync(nodeId, removeRemoteConfig: false)` |
| Peer is permanently removed from topology | `RemoveRemotePeerAsync(nodeId)` |
## Procedure
1. Confirm peer intent (temporary outage vs. decommission).
2. Inspect health payload:
- `peersWithNoConfirmation`
- `laggingPeers`
- `lastSuccessfulConfirmationUpdateByPeer`
3. If deprecating from prune gating only, run:
```csharp
await peerManagement.RemovePeerTrackingAsync(
nodeId: "peer-to-deprecate",
removeRemoteConfig: false,
cancellationToken);
```
4. If permanently removing, run:
```csharp
await peerManagement.RemoveRemotePeerAsync("peer-to-remove", cancellationToken);
```
5. Re-check `/health` and verify status transition:
- `Degraded`/`Unhealthy` should clear if the removed peer was the cause.
6. Confirm maintenance logs no longer report prune blocking for that peer.
## Post-change verification
- Peer no longer appears in active tracked peers.
- `maxLagMs` trends with only current active peers.
- Pruning resumes with a valid effective cutoff (or a known non-peer reason).
## Emergency path
If pruning is blocked and storage pressure is high:
1. De-track the clearly retired peer first (`removeRemoteConfig: false`).
2. Validate pruning resumes.
3. Perform full peer removal after change-control approval.
## Re-activation path
If a deprecated peer returns and should gate pruning again:
1. Ensure peer config is enabled/available.
2. Allow sync to re-register and refresh confirmations.
3. Watch health payload until peer exits `peersWithNoConfirmation`.

280
docs/persistence-providers.md Executable file
View File

@@ -0,0 +1,280 @@
# CBDDC Persistence Providers
CBDDC supports multiple persistence backends to suit different deployment scenarios.
## Overview
| Provider | Best For | Performance | Setup | Production Ready |
|----------|----------|-------------|-------|------------------|
| **SQLite (Direct)** | Embedded apps, single-node | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ✅ Yes |
| **EF Core (Generic)** | Multi-DB support, migrations | ⭐⭐⭐ | ⭐⭐⭐ | ✅ Yes |
| **PostgreSQL** | Production, high load, JSON queries | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | ✅ Yes |
## SQLite (Direct)
**Package:** `ZB.MOM.WW.CBDDC.Persistence.Sqlite`
### Characteristics
-**Zero configuration**: Works out of the box
-**Excellent performance**: Native SQL, no ORM overhead
-**WAL mode**: Concurrent readers + writers
-**Per-collection tables**: Optional for better isolation
-**Snapshots**: Fast reconnection with `SnapshotMetadata`
-**Portable**: Single file database
-**Limited JSON queries**: Uses `json_extract()`
### When to Use
- Building single-node applications
- Embedded scenarios (desktop, mobile)
- Development/testing
- Maximum simplicity required
- File-based portability important
### Configuration
```csharp
// Legacy mode (simple)
services.AddCBDDCSqlite("Data Source=cbddc.db");
// New mode (per-collection tables)
services.AddCBDDCSqlite(options =>
{
options.BasePath = "/var/lib/cbddc";
options.DatabaseFilenameTemplate = "cbddc-{NodeId}.db";
options.UsePerCollectionTables = true;
});
```
### Performance Tips
1. Enable WAL mode (done automatically)
2. Use per-collection tables for large datasets
3. Create indexes on frequently queried fields
4. Keep database on fast storage (SSD)
## EF Core (Generic)
**Package:** `ZB.MOM.WW.CBDDC.Persistence.EntityFramework`
### Characteristics
-**Multi-database support**: SQL Server, MySQL, SQLite, PostgreSQL
-**EF Core benefits**: Migrations, LINQ, change tracking
-**Type-safe**: Strongly-typed entities
- ⚠️ **Query limitation**: JSON queries evaluated in-memory
- ⚠️ **ORM overhead**: Slightly slower than direct SQL
### When to Use
- Need to support multiple database backends
- Team familiar with EF Core patterns
- Want automated migrations
- Building enterprise applications
- Database portability is important
### Configuration
#### SQLite
```csharp
services.AddCBDDCEntityFrameworkSqlite("Data Source=cbddc.db");
```
#### SQL Server
```csharp
services.AddCBDDCEntityFrameworkSqlServer(
"Server=localhost;Database=CBDDC;Trusted_Connection=True;");
```
#### PostgreSQL
```csharp
services.AddDbContext<CBDDCContext>(options =>
options.UseNpgsql(connectionString));
services.AddCBDDCEntityFramework();
```
#### MySQL
```csharp
var serverVersion = ServerVersion.AutoDetect(connectionString);
services.AddCBDDCEntityFrameworkMySql(connectionString, serverVersion);
```
### Migrations
```bash
# Add migration
dotnet ef migrations add InitialCreate --context CBDDCContext
# Apply migration
dotnet ef database update --context CBDDCContext
```
## PostgreSQL
**Package:** `ZB.MOM.WW.CBDDC.Persistence.PostgreSQL`
### Characteristics
-**JSONB native storage**: Optimal JSON handling
-**GIN indexes**: Fast JSON path queries
-**High performance**: Production-grade
-**Connection resilience**: Built-in retry logic
-**Full ACID**: Strong consistency guarantees
- ⚠️ **Future feature**: JSONB query translation (roadmap)
### When to Use
- Production deployments with high traffic
- Need advanced JSON querying (future)
- Require horizontal scalability
- Want best-in-class reliability
- Cloud deployments (AWS RDS, Azure Database, etc.)
### Configuration
```csharp
services.AddCBDDCPostgreSql(
"Host=localhost;Database=CBDDC;Username=user;Password=pass");
// With custom options
services.AddCBDDCPostgreSql(connectionString, options =>
{
options.EnableSensitiveDataLogging(); // Dev only
options.CommandTimeout(30);
});
```
### JSONB Indexes
For optimal performance, create GIN indexes via migrations:
```csharp
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql(@"
CREATE INDEX IF NOT EXISTS IX_Documents_ContentJson_gin
ON ""Documents"" USING GIN (""ContentJson"" jsonb_path_ops);
CREATE INDEX IF NOT EXISTS IX_Oplog_PayloadJson_gin
ON ""Oplog"" USING GIN (""PayloadJson"" jsonb_path_ops);
");
}
```
### Connection String Examples
#### Local Development
```
Host=localhost;Port=5432;Database=CBDDC;Username=admin;Password=secret
```
#### Production with SSL
```
Host=prod-db.example.com;Database=CBDDC;Username=admin;Password=secret;SSL Mode=Require
```
#### Connection Pooling
```
Host=localhost;Database=CBDDC;Username=admin;Password=secret;Pooling=true;Minimum Pool Size=5;Maximum Pool Size=100
```
## Feature Comparison
| Feature | SQLite (Direct) | EF Core | PostgreSQL |
|---------|----------------|---------|------------|
| **Storage Format** | File-based | Varies | Server-based |
| **JSON Storage** | TEXT | NVARCHAR/TEXT | JSONB |
| **JSON Indexing** | Standard | Standard | GIN/GIST |
| **JSON Queries** | `json_extract()` | In-Memory | Native (future) |
| **Concurrent Writes** | Good (WAL) | Varies | Excellent |
| **Horizontal Scaling** | No | Limited | Yes (replication) |
| **Migrations** | Manual SQL | EF Migrations | EF Migrations |
| **Connection Pooling** | N/A | Built-in | Built-in |
| **Cloud Support** | N/A | Varies | Excellent |
## Performance Benchmarks
_These are approximate figures for comparison:_
### Write Performance (docs/sec)
| Provider | Single Write | Bulk Insert (1000) |
|----------|--------------|-------------------|
| SQLite | 5,000 | 50,000 |
| EF Core (SQL Server) | 3,000 | 30,000 |
| PostgreSQL | 8,000 | 80,000 |
### Read Performance (docs/sec)
| Provider | Single Read | Query (100 results) |
|----------|-------------|---------------------|
| SQLite | 10,000 | 5,000 |
| EF Core (SQL Server) | 8,000 | 4,000 |
| PostgreSQL | 12,000 | 8,000 |
_*Benchmarks vary based on hardware, network, and configuration_
## Migration Guide
### From SQLite to PostgreSQL
1. Export data from SQLite
2. Set up PostgreSQL database
3. Update connection configuration
4. Import data to PostgreSQL
5. Verify functionality
### From EF Core to PostgreSQL
1. Change NuGet package reference
2. Update service registration
3. Generate new migrations for PostgreSQL
4. Apply migrations
5. Test thoroughly
## Recommendations
### Development
- **Use**: SQLite (Direct)
- **Why**: Fast, simple, portable
### Testing
- **Use**: SQLite (Direct) or EF Core with SQLite
- **Why**: Disposable, fast test execution
### Production (Low-Medium Scale)
- **Use**: SQLite (Direct) with per-collection tables
- **Why**: Excellent performance, simple ops
### Production (High Scale)
- **Use**: PostgreSQL
- **Why**: Best performance, scalability, reliability
### Enterprise
- **Use**: EF Core with SQL Server or PostgreSQL
- **Why**: Enterprise support, compliance, familiarity
## Troubleshooting
### SQLite: "Database is locked"
- Ensure WAL mode is enabled (automatic)
- Increase busy timeout
- Check for long-running transactions
### EF Core: "Query evaluated in-memory"
- Expected for complex JSON queries
- Consider PostgreSQL for better JSON support
- Use indexes on frequently queried properties
### PostgreSQL: "Connection pool exhausted"
- Increase `Maximum Pool Size`
- Check for connection leaks
- Consider connection pooler (PgBouncer)
## Future Enhancements
- **JSONB Query Translation**: Native PostgreSQL JSON queries from QueryNode
- **MongoDB Provider**: NoSQL option for document-heavy workloads
- **Redis Cache Layer**: Hybrid persistence for high-read scenarios
- **Multi-Master PostgreSQL**: Active-active replication support

267
docs/production-hardening.md Executable file
View File

@@ -0,0 +1,267 @@
# Production Hardening - Implementation Guide
## Quick Reference
### Configuration (appsettings.json)
```json
{
"CBDDC": {
"Network": {
"TcpPort": 5000,
"UdpPort": 6000,
"RetryAttempts": 3
},
"Persistence": {
"DatabasePath": "data/cbddc.db",
"EnableWalMode": true,
"CacheSizeMb": 50,
"EnableAutoBackup": true,
"BackupPath": "backups/"
},
"Sync": {
"EnableOfflineQueue": true,
"MaxQueueSize": 1000
}
}
}
```
### DI Setup
```csharp
services.Configure<CBDDCOptions>(configuration.GetSection("CBDDC"));
services.AddSingleton<RetryPolicy>();
services.AddSingleton<DocumentCache>();
services.AddSingleton<OfflineQueue>();
services.AddSingleton<SyncStatusTracker>();
services.AddCBDDCHosting(options =>
{
options.Cluster.NodeId = "server-01";
options.Cluster.TcpPort = 5001;
options.Cluster.PeerConfirmationLagThresholdMs = 30_000;
options.Cluster.PeerConfirmationCriticalLagThresholdMs = 120_000;
});
```
### Health Check
```csharp
app.MapHealthChecks("/health");
```
### Peer Confirmation Lag Thresholds
`CBDDCHealthCheck` evaluates tracked peers using confirmation lag thresholds:
- `PeerConfirmationLagThresholdMs` (default `30000`) marks peers as lagging and
returns `Degraded`.
- `PeerConfirmationCriticalLagThresholdMs` (default `120000`) marks critical lag and
returns `Unhealthy`.
Thresholds are clamped so critical is never lower than lag.
### Health Status Interpretation
For the `cbddc` health check:
| Status | Meaning | Typical operator action |
|------|-----------|-------------|
| Healthy | All active tracked peers have confirmations and lag is within threshold. | No action required. |
| Degraded | At least one tracked peer is lagging, or at least one tracked peer has no confirmation rows yet. | Investigate slow/unconfirmed peers, confirm whether any should be untracked/deprecated. |
| Unhealthy | At least one tracked peer exceeds critical lag threshold, or persistence check throws. | Page on-call, verify storage/network path, and evaluate emergency peer de-tracking for permanently retired peers. |
Health payload fields:
- `trackedPeerCount`
- `peersWithNoConfirmation`
- `maxLagMs`
- `laggingPeers`
- `lastSuccessfulConfirmationUpdateByPeer`
Use these fields to distinguish temporary lag from stale peer registrations.
### Offline Queue
```csharp
// Enqueue during offline
if (!isOnline)
{
offlineQueue.Enqueue(new PendingOperation
{
Type = "put",
Collection = "users",
Key = "user1",
Data = user
});
}
// Flush when back online
var (successful, failed) = await offlineQueue.FlushAsync(async op =>
{
var collection = database.Collection(op.Collection);
if (op.Type == "put" && op.Data != null)
await collection.Put(op.Key, op.Data);
else if (op.Type == "delete")
await collection.Delete(op.Key);
});
```
### Document Cache
```csharp
var cache = new DocumentCache(maxSizeMb: 50);
// Check cache first
var cached = cache.Get("users", "user1");
if (cached != null) return cached;
// Load from database
var doc = await store.GetDocumentAsync("users", "user1");
if (doc != null) cache.Set("users", "user1", doc);
```
### SQLite Backup
```csharp
await store.BackupAsync("backups/backup-20260115.db");
```
### Retry Policy
```csharp
var retry = new RetryPolicy(logger, maxAttempts: 3, delayMs: 1000);
await retry.ExecuteAsync(
() => tcpClient.ConnectAsync(endpoint),
"TCP Connect"
);
```
### Error Handling
Use specific exceptions for robust control flow:
```csharp
try
{
await operation();
}
catch (DocumentNotFoundException ex)
{
// Handle specific document missing case
logger.LogWarning("Document {Key} missing", ex.Key);
}
catch (CBDDCConcurrencyException ex)
{
// Handle conflict (though LWW usually resolves it automatically)
logger.LogWarning("Concurrency conflict: {Message}", ex.Message);
}
catch (NetworkException ex)
{
logger.LogError(ex, "Network operation failed");
syncTracker.RecordError(ex.Message, peerNodeId, ex.ErrorCode);
}
catch (PersistenceException ex) when (ex is DatabaseCorruptionException)
{
logger.LogCritical(ex, "Database corruption detected!");
// Attempt recovery or alert admin
}
```
## Error Codes
| Code | Exception | Description |
|------|-----------|-------------|
| NETWORK_ERROR | NetworkException | Network operation failed |
| PERSISTENCE_ERROR | PersistenceException | Database operation failed |
| SYNC_ERROR | SyncException | Synchronization failed |
| CONFIG_ERROR | ConfigurationException | Invalid configuration |
| TIMEOUT_ERROR | TimeoutException | Operation timed out |
## Logging Levels
- **Trace**: Internal details (cache hits/misses)
- **Debug**: Debugging info (sync operations)
- **Information**: Normal events (peer discovered, backup created)
- **Warning**: Recoverable errors (queue full, retry attempt, documents not found)
- **Error**: Failures requiring attention (sync failed, corruption detected)
- **Critical**: System failures (database initialization failed)
## Best Practices
1. **Always use structured logging**
```csharp
_logger.LogInformation("User {UserId} synced {Count} documents", userId, count);
```
2. **Wrap network operations with retry policy**
```csharp
await _retryPolicy.ExecuteAsync(() => client.ConnectAsync(), "Connect");
```
3. **Check cache before database**
```csharp
var doc = _cache.Get(collection, key) ?? await _store.GetDocumentAsync(collection, key);
```
4. **Enable offline queue for LAN instability**
```csharp
if (options.Sync.EnableOfflineQueue && !isOnline)
_offlineQueue.Enqueue(operation);
```
5. **Periodic health checks**
```csharp
var timer = new Timer(async _ =>
{
var report = await healthCheckService.CheckHealthAsync(r => r.Name == "cbddc");
var entry = report.Entries["cbddc"];
if (entry.Status != HealthStatus.Healthy)
{
_logger.LogWarning(
"CBDDC health is {Status}. LaggingPeers={LaggingPeers} UnconfirmedPeers={UnconfirmedPeers}",
entry.Status,
entry.Data["laggingPeers"],
entry.Data["peersWithNoConfirmation"]);
}
}, null, TimeSpan.Zero, TimeSpan.FromMinutes(5));
```
## Deployment Checklist
- [ ] Configuration file created (appsettings.json)
- [ ] Log directory permissions set
- [ ] Backup directory configured
- [ ] Database file location specified
- [ ] Network ports configured (firewall)
- [ ] Health check endpoint tested
- [ ] Offline queue tested
- [ ] Backup/restore tested
- [ ] Graceful shutdown tested
## Troubleshooting
### Database corruption
```csharp
try
{
await store.CheckIntegrityAsync();
}
catch (DatabaseCorruptionException)
{
// Restore from backup
File.Copy("backups/latest.db", options.Persistence.DatabasePath, overwrite: true);
}
```
### Network issues
```
Check sync tracker:
- Last sync time
- Active peers
- Recent errors
```
### Performance degradation
```csharp
var stats = cache.GetStatistics();
if (stats.HitRate < 0.5)
{
// Consider increasing cache size
options.Persistence.CacheSizeMb = 100;
}
```

53
docs/querying.md Executable file
View File

@@ -0,0 +1,53 @@
---
layout: default
title: Querying
---
# Querying
CBDDC allows querying local collections using a rich set of operators and idiomatic C# syntax, including LINQ support.
## Basic Querying
You can query documents using the `Find` method on a `PeerCollection`.
```csharp
var users = await peerStore.GetCollection<User>("users");
// Precise match
var fabio = await users.Find(u => u.FirstName == "Fabio");
// Comparisons
var adults = await users.Find(u => u.Age >= 18);
// Logical Operators
var activeAdmins = await users.Find(u => u.IsActive && u.Role == "Admin");
```
## Serialization Consistency
CBDDC respects your configured serialization settings. If you use `snake_case` in your JSON serialization but standard C# PascalCase properties, the query translator automatically handles the mapping.
```csharp
// Definition
public class User
{
[JsonPropertyName("first_name")]
public string FirstName { get; set; }
}
// Query
// This translates to a SQL query checking json_extract(data, '$.first_name')
var result = await users.Find(u => u.FirstName == "Fabio");
```
## Supported Operators
- `==` (Equal)
- `!=` (Not Equal)
- `>` (Greater Than)
- `<` (Less Than)
- `>=` (Greater Than or Equal)
- `<=` (Less Than or Equal)
- `&&` (AND)
- `||` (OR)

108
docs/remote-peer-configuration.md Executable file
View File

@@ -0,0 +1,108 @@
# Remote Peer Configuration
Use `IPeerManagementService` to manage persistent remote peers and their pruning
tracking lifecycle at runtime.
## Supported peer operations
- Add a static remote peer
- Remove a configured peer and tracking
- Remove tracking only (keep static peer config)
- Enable/disable a peer
- List all configured peers
## Add a static peer
```csharp
await peerManagement.AddStaticPeerAsync(
"branch-2",
"branch2.example.com:9000",
cancellationToken);
```
## Peer lifecycle model
Peer management now has two independent dimensions:
- **Remote peer configuration**: whether a static peer exists in remote peer config.
- **Peer confirmation tracking**: whether the peer participates in
peer-confirmed prune gating.
Common states:
- **Enabled + tracked**: normal operation, peer is eligible for sync and prune gating.
- **Disabled + tracked**: peer config retained, outbound static sync disabled, still
counted as tracked until tracking is removed/deactivated.
- **Enabled/disabled + untracked**: peer config may remain, but peer is excluded from
prune confirmation gating.
- **Removed**: peer config removed and tracking removed.
## Remove tracking only (deprecate peer from pruning)
Use this when a peer should no longer gate pruning but you are not removing static
configuration yet.
```csharp
await peerManagement.RemovePeerTrackingAsync(
nodeId: "branch-2",
removeRemoteConfig: false,
cancellationToken);
```
## Remove a peer and tracking (full removal)
`RemoveRemotePeerAsync` performs full cleanup by removing both peer tracking and
static peer configuration.
```csharp
await peerManagement.RemoveRemotePeerAsync("branch-2", cancellationToken);
```
Equivalent explicit call:
```csharp
await peerManagement.RemovePeerTrackingAsync(
nodeId: "branch-2",
removeRemoteConfig: true,
cancellationToken);
```
## Enable or disable a peer
```csharp
await peerManagement.DisablePeerAsync("branch-2", cancellationToken);
await peerManagement.EnablePeerAsync("branch-2", cancellationToken);
```
## List configured peers
```csharp
var peers = await peerManagement.GetAllRemotePeersAsync(cancellationToken);
foreach (var peer in peers)
{
Console.WriteLine($"{peer.NodeId} @ {peer.Address} ({peer.Type}) Enabled={peer.IsEnabled}");
}
```
## Storage model
Remote peer configuration is persisted in the peer configuration store and synced across nodes as part of CBDDC metadata replication.
Fields stored per peer:
- `NodeId`
- `Address`
- `Type`
- `IsEnabled`
- `InterestingCollections`
## Notes
- Authentication for sync handshakes is based on the cluster shared token (`AuthToken`) from node configuration.
- Disabled peers remain persisted but are excluded from active sync.
- Peer tracking removal is implemented as deactivation in the confirmation store,
which removes the peer from active prune-gating.
- Re-observed peers can be re-registered and become active for tracking again.
- For rollout steps and production operations, see:
- [Upgrade: Peer-Confirmed Pruning](upgrade-peer-confirmed-pruning.html)
- [Peer Deprecation & Removal Runbook](peer-deprecation-removal-runbook.html)

145
docs/security.md Executable file
View File

@@ -0,0 +1,145 @@
# Network Security
**CBDDC v0.6.0** introduces optional **secure networking** to protect data in transit between peers using industry-standard cryptography.
## Overview
The security layer provides:
- **ECDH (Elliptic Curve Diffie-Hellman)** key exchange for establishing shared secrets
- **AES-256-CBC** encryption for all synchronized data
- **HMAC-SHA256** authentication to prevent tampering
- **Perfect Forward Secrecy** - each session uses unique ephemeral keys
## When to Use Secure Networking
### ✅ Recommended For:
- **Sensitive data** (customer information, financial records, health data)
- **Compliance requirements** (GDPR, HIPAA, PCI-DSS)
- **Untrusted network segments** within your LAN
- **Production deployments** where data confidentiality is important
### ⚠️ Consider Performance Impact:
- Encryption adds ~5-10ms latency per message
- CPU overhead for encryption/decryption
- Not necessary for public/non-sensitive data
## How It Works
### Handshake Process
```
Peer A Peer B
| |
| 1. Generate ECDH keypair |
|------ Public Key A ---------------->| 2. Generate ECDH keypair
| |
|<----- Public Key B -----------------|
| |
3. Compute shared secret 3. Compute shared secret
4. Derive encryption keys 4. Derive encryption keys
| |
|==== Encrypted Communication ====|
```
### Encryption Details
1. **Key Exchange**: NIST P-256 elliptic curve (secp256r1)
2. **Encryption**: AES-256 in CBC mode with random IV per message
3. **Authentication**: HMAC-SHA256 over ciphertext
4. **Message Format**: `IV (16 bytes) + Ciphertext + HMAC (32 bytes)`
## Usage
### Enable Security
```csharp
using CBDDC.Network.Security;
// Register secure handshake service
services.AddSingleton<IPeerHandshakeService, SecureHandshakeService>();
// Network will automatically use encryption if service is registered
services.AddCBDDCNetwork(nodeId, tcpPort, authToken);
```
### Console Sample (--secure flag)
```bash
# Start with encryption enabled
dotnet run --secure
# Or without encryption (faster, less secure)
dotnet run
```
### UI Clients
If you build a UI client, enable secure mode by default to avoid mixed-security clusters.
### Verify Security Status
UI clients should display security status:
- **🔒 Encrypted** - Secure communication active
- **🔓 Plaintext** - No encryption (Console sample default)
## Configuration
### Optional: Custom Security Settings
Currently, security uses default secure parameters. Future versions may support:
- Custom curve selection
- Cipher suite configuration
- Certificate pinning
## Security Considerations
### ✅ What This Protects Against:
- **Eavesdropping**: Data is encrypted in transit
- **Tampering**: HMAC prevents message modification
- **Man-in-the-Middle**: ECDH provides authenticity
### ⚠️ Out of Scope:
- **Data at Rest**: SQLite databases are NOT encrypted. Use OS-level encryption if needed.
- **Authentication**: No peer identity verification beyond shared auth token.
- **Public Internet**: Still designed for trusted LANs. Use VPN/firewall for internet exposure.
## Performance Impact
Benchmarks on typical workloads:
| Operation | Plaintext | Encrypted | Overhead |
|-----------|-----------|-----------|----------|
| Small sync (10 docs) | 15ms | 22ms | +47% |
| Large sync (1000 docs) | 450ms | 520ms | +16% |
| Handshake | N/A | 8ms | Initial |
**Recommendation**: Enable for production. Disable for development/testing if needed.
## Compatibility
- **Secure ↔ Secure**: ✅ Works
- **Plaintext ↔ Plaintext**: ✅ Works
- **Secure ↔ Plaintext**: ❌ Connection fails (by design)
All nodes in a network must use the **same security mode**.
## FAQ
**Q: Can I use this over the public internet?**
A: While encryption helps, CBDDC is still designed for LANs. Use VPN, firewall rules, and consider TLS/SSL wrapping for internet exposure.
**Q: How do I encrypt the SQLite database?**
A: Use SQLCipher or OS-level disk encryption (BitLocker, LUKS, FileVault).
**Q: Can different nodes use different keys?**
A: No. All nodes in a mesh share the same `authToken`. Key rotation is not yet supported.
**Q: What about .NET Standard 2.0 support?**
A: Security works on all target frameworks (netstandard2.0, net6.0, net8.0) with appropriate polyfills.
---
**See Also:**
- [Getting Started](getting-started.html)
- [Architecture](architecture.html)
- [Production Hardening](production-hardening.html)

View File

@@ -0,0 +1,69 @@
# Upgrade Notes: Peer-Confirmed Pruning
This guide covers adopting peer-confirmed pruning semantics introduced across
Phases 1-4.
## What changed
1. Oplog pruning now uses an **effective cutoff**:
- `min(retention cutoff, confirmation cutoff)` when peer confirmations are complete.
- prune is skipped for that cycle when an active tracked peer is missing required confirmation.
2. Peer tracking is now a managed lifecycle:
- `RemovePeerTrackingAsync(nodeId, removeRemoteConfig: false)` deprecates a peer from prune gating.
- `RemoveRemotePeerAsync(nodeId)` removes both static peer config and tracking.
3. Hosting health now includes confirmation lag semantics:
- `Degraded` for lagging or unconfirmed tracked peers.
- `Unhealthy` for critical lag or storage failures.
## Upgrade impact to expect
- During initial rollout, a peer may appear in `peersWithNoConfirmation` until the
first successful confirmation update.
- Any stale active tracked peer can block prune progress and/or keep health degraded.
## Recommended rollout sequence
1. Upgrade one node and validate health payload and pruning logs.
2. Upgrade remaining nodes in the cluster.
3. Audit peer inventory and remove/deprecate stale peers.
4. Tune lag thresholds after observing normal confirmation latency.
## Peer inventory and cleanup
List configured peers:
```csharp
var peers = await peerManagement.GetAllRemotePeersAsync(cancellationToken);
```
Deprecate from pruning only:
```csharp
await peerManagement.RemovePeerTrackingAsync(
nodeId: "retired-peer",
removeRemoteConfig: false,
cancellationToken);
```
Fully remove peer + tracking:
```csharp
await peerManagement.RemoveRemotePeerAsync("retired-peer", cancellationToken);
```
## Validation checklist
- `/health` returns `Healthy` or expected transient `Degraded` during warm-up.
- `laggingPeers` and `peersWithNoConfirmation` converge toward zero for active peers.
- Maintenance logs no longer report prune skip reasons for retired peers.
## Rollback/mitigation
If rollout exposes unexpected persistent degradation:
1. Remove tracking for permanently retired peers.
2. Temporarily raise lag thresholds to reduce alert noise while investigating.
3. Keep full peer removal for nodes that are confirmed decommissioned.
For a detailed operator procedure, see
[Peer Deprecation & Removal Runbook](peer-deprecation-removal-runbook.html).