Grove includes an optional CRDT (Conflict-Free Replicated Data Type) layer that enables offline-first, multi-node, and eventually-consistent use cases. It tracks field-level changes with Hybrid Logical Clocks and merges them automatically during sync.
Why CRDTs?01
Traditional databases rely on a single source of truth. When multiple nodes make concurrent edits, you need conflict resolution. CRDTs solve this mathematically: any two nodes that have seen the same set of updates will converge to the same state, regardless of the order updates arrive.
Grove CRDTs are useful for:
Offline-first apps that queue changes locally and sync when connectivity returns
Multi-node deployments where edge servers process writes independently
Real-time collaboration where multiple users edit the same document
Event sourcing with guaranteed convergence across replicas
Five CRDT Types02
Grove supports five field-level CRDT types. Tag struct fields to opt in:
| Type | Tag | Merge Strategy | Use Case |
| LWW-Register | crdt:lww | Highest HLC timestamp wins | Strings, booleans, JSON objects |
| PN-Counter | crdt:counter | Per-node max of increments/decrements | View counts, scores, balances |
| OR-Set | crdt:set | Add-wins union with observed-remove | Tags, categories, member lists |
| RGA List | crdt:list | Union all nodes, DFS traversal sorted by HLC | Ordered collections, task lists, kanban boards |
| Nested Document | crdt:document | Per-path field merge via MergeEngine | JSON-like nested structures, forms, user profiles |
Fields without crdt: tags work normally with zero overhead.
How It Works03
Model (struct tags)
|
Plugin (Grove hook)
|
Shadow Table (_<table>_crdt)
|
HLC Clock (Hybrid Logical Clock)
|
Syncer (pull/push protocol)
|
Transport (HTTP / SSE / WebSocket / custom)
|
Rooms / Presence ── Remote Node
|
Time-Travel (optional history)Struct tags declare which fields are CRDT-enabled and their type
Plugin intercepts mutations (insert/update/delete) via Grove hooks and records changes in shadow tables
Shadow tables store per-field CRDT state alongside the primary table without modifying its schema
HLC clock generates causally-ordered timestamps without coordination between nodes
Syncer orchestrates two-phase sync: pull remote changes and merge, then push local changes
Transport handles the network layer (HTTP, SSE streaming, WebSocket, or custom protocols)
Rooms provide scoped collaborative spaces with participant management, cursor tracking, and typing indicators
Time-Travel optionally records field-level history for audit trails, undo, and debugging
Architecture04
grove/crdt/
crdt.go Core types: CRDTType, State, FieldState, ChangeRecord
clock.go HLC implementation
register.go LWW-Register merge
counter.go PN-Counter merge
set.go OR-Set merge (add-wins)
list.go RGA List merge (ordered collections)
document.go Nested Document merge (per-path heterogeneous)
merge.go MergeEngine: field + state merging
plugin.go Plugin (Grove hook integration)
plugin_hooks.go CRDTPlugin interface + interceptors
hooks.go PostMutation/PreQuery hooks
metadata.go Shadow table read/write
sync.go Syncer: push/pull orchestration + StreamSync
sync_hooks.go SyncHook interface for intercepting sync data
transport.go Transport interface, HTTPClient, StreamingTransport
transport_ws.go WebSocket transport (bidirectional multiplexed)
server.go SyncController + HTTPHandler (standalone)
presence.go PresenceManager: in-memory ephemeral state with TTL
presence_types.go Presence types: PresenceState, PresenceUpdate, PresenceEvent
room.go RoomManager: room CRUD, join/leave, participant limits
timetravel.go Time-travel: field history, state-at-time queries
validation.go Schema + field validation rules
metrics.go Sync/merge/presence metric collectors
options.go Functional options
inspect.go Debug/inspect utilities
migrations.go Shadow table DDLKey Concepts05
HLC (Hybrid Logical Clock) combines wall-clock time with a logical counter and node ID for total ordering without coordination
Shadow tables (
_<table>_crdt) store CRDT metadata alongside primary data; your primary table schema is never modifiedTombstones mark deleted records so deletions propagate during sync; they expire after a configurable TTL (default: 7 days)
Sync hooks intercept changes flowing between nodes for validation, filtering, transformation, or audit logging
Transport is pluggable: use HTTP, SSE streaming, WebSocket, or implement your own for gRPC, NATS, etc.
Presence provides ephemeral, real-time awareness (typing indicators, cursors, online users) with TTL-based cleanup, SSE broadcast, and React hooks
Rooms provide scoped collaborative spaces with participant limits, cursor tracking, typing indicators, and lifecycle hooks
Time-Travel optionally records field-level history for querying state at any point in time, audit trails, and undo
WebSocket transport enables bidirectional multiplexed sync over a single persistent connection
Plugins allow intercepting merge, metadata, presence, room, time-travel, and connection events for custom logic
Validation enforces schema and field-level rules before merge operations
Metrics collect sync, merge, and presence statistics for observability
Quick Example06
import (
"github.com/xraph/grove/crdt"
"github.com/xraph/grove/hook"
)
// 1. Define a CRDT-enabled model
type Document struct {
grove.BaseModel `grove:"table:documents,alias:d"`
ID string `grove:"id,pk"`
Title string `grove:"title,crdt:lww"`
ViewCount int64 `grove:"view_count,crdt:counter"`
Tags []string `grove:"tags,type:jsonb,crdt:set"`
}
// 2. Create plugin and attach to DB
plugin := crdt.New(crdt.WithNodeID("node-1"))
db.Hooks().AddHook(plugin, hook.Scope{Tables: []string{"documents"}})
// 3. Sync with a remote node
syncer := crdt.NewSyncer(plugin,
crdt.WithTransport(crdt.HTTPTransport("https://cloud.example.com/sync")),
crdt.WithSyncTables("documents"),
)
report, err := syncer.Sync(ctx)Next Steps07
Getting Started for a complete setup walkthrough
CRDT Types for deep dives into LWW, Counter, Set, List, and Document semantics
Sync Protocol for transport, syncer, and hook details
SSE Streaming for real-time change propagation
Presence & Awareness for ephemeral real-time state (typing indicators, cursors, online users)
Room Management for scoped collaborative spaces with participant limits and lifecycle hooks
Time-Travel & History for querying state at any point in time
Plugin System for intercepting merge, presence, room, and connection events
WebSocket Transport for bidirectional multiplexed sync
TypeScript Client for browser and React integration