stemedb/crates/stemedb-rpc/proto/sync.proto
jordan 2b0923f20e feat: Distributed replication foundation (Phase 6A) - HLC, Merkle trees, CRDT stores, sync protocol
- Add Hybrid Logical Clock (HLC) for causality tracking across nodes
- Implement Merkle tree for efficient diff/sync with BLAKE3 hashing
- Add CRDT-aware stores for assertions and votes with vector clocks
- Create stemedb-sync crate with anti-entropy and gossip protocols
- Add stemedb-rpc crate with gRPC sync service (proto definitions)
- Implement SupersessionChain for tracking assertion lifecycles
- Add Aphoria application for code analysis/reporting
- Add battery11 replication test scaffolding
- Fix .gitignore to exclude nested target directories

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 19:31:54 -07:00

101 lines
2.7 KiB
Protocol Buffer

syntax = "proto3";
package stemedb.sync.v1;
// SyncService enables node-to-node replication for StemeDB.
//
// The service supports two sync patterns:
// 1. Gossip: Push new assertions to peers immediately after ingestion
// 2. Anti-Entropy: Periodic Merkle root exchange and diff-based sync
service SyncService {
// Gossip pushes a new assertion to a peer.
// Called immediately after local ingestion to propagate data quickly.
rpc Gossip(GossipRequest) returns (GossipResponse);
// ExchangeRoots compares Merkle roots to detect divergence.
// If roots differ, the caller should fetch missing assertions.
rpc ExchangeRoots(RootExchangeRequest) returns (RootExchangeResponse);
// FetchAssertions retrieves assertion data by hash.
// Used after ExchangeRoots to pull missing assertions.
rpc FetchAssertions(FetchRequest) returns (FetchResponse);
// Ping checks if a peer is alive and returns basic metadata.
rpc Ping(PingRequest) returns (PingResponse);
}
// GossipRequest pushes a single assertion to a peer.
message GossipRequest {
// BLAKE3 hash of the assertion (32 bytes)
bytes assertion_hash = 1;
// Serialized assertion data (rkyv format)
bytes assertion_data = 2;
// HLC timestamp components for causal ordering
uint64 hlc_time = 3;
uint32 hlc_counter = 4;
bytes hlc_node_id = 5; // 16 bytes
}
message GossipResponse {
// True if the assertion was accepted (stored or already existed)
bool accepted = 1;
// Error message if rejected (e.g., validation failure)
string error = 2;
}
// RootExchangeRequest initiates Merkle root comparison.
message RootExchangeRequest {
// Local Merkle root hash (32 bytes)
bytes merkle_root = 1;
// Number of assertions in local tree
uint64 assertion_count = 2;
}
message RootExchangeResponse {
// Remote Merkle root hash (32 bytes)
bytes merkle_root = 1;
// Number of assertions in remote tree
uint64 assertion_count = 2;
// True if roots match (trees are identical)
bool roots_match = 3;
}
// FetchRequest asks for assertion data by hash.
message FetchRequest {
// List of assertion hashes to fetch (max 1000 per request)
repeated bytes hashes = 1;
}
message FetchResponse {
// Retrieved assertions (may be fewer than requested if not found)
repeated AssertionData assertions = 1;
}
// AssertionData pairs a hash with its serialized data.
message AssertionData {
// BLAKE3 hash of the assertion (32 bytes)
bytes hash = 1;
// Serialized assertion data (rkyv format)
bytes data = 2;
}
// PingRequest is a health check with node identity.
message PingRequest {
// Sender's node ID (16 bytes)
bytes node_id = 1;
}
message PingResponse {
// Responder's node ID (16 bytes)
bytes node_id = 1;
// Number of assertions on this node
uint64 assertion_count = 2;
}