CXDB is an AI Context Store for agents and LLMs, providing fast, branch-friendly storage for conversation histories and tool outputs with content-addressed deduplication.
Built on a Turn DAG + Blob CAS architecture, CXDB gives you:
- Branch-from-any-turn: Fork conversations at any point without copying history
- Fast append: Optimized for the 99% case - appending new turns
- Content deduplication: Identical payloads stored once via BLAKE3 hashing
- Type-safe projections: Msgpack storage with typed JSON views for UIs
- Built-in UI: React frontend with turn visualization and custom renderers
The fastest way to try CXDB is with the pre-built Docker image:
# Run the server (binary protocol :9009, HTTP :9010)
docker run -p 9009:9009 -p 9010:9010 -v $(pwd)/data:/data cxdb/cxdb:latest
# Create a context and append a turn
curl -X POST http://localhost:9010/v1/contexts
# => {"context_id": "1", "head_turn_id": "0", "head_depth": 0}
curl -X POST http://localhost:9010/v1/contexts/1/turns \
-H "Content-Type: application/json" \
-d '{
"type_id": "com.example.Message",
"type_version": 1,
"payload": {"role": "user", "text": "Hello!"}
}'
# View in the UI
open http://localhost:9010Prerequisites:
- Rust 1.75+ (for the server)
- Go 1.22+ (for the client SDK and gateway)
- Node.js 20+ with pnpm (for the frontend)
# Clone the repository
git clone https://github.com/strongdm/cxdb.git
cd cxdb
# Build the server
cargo build --release
# Run the server
./target/release/ai-cxdb-store
# Build the gateway (optional - for OAuth and frontend serving)
cd gateway
go build -o bin/gateway ./cmd/server
./bin/gateway
# Build the frontend (optional - for UI development)
cd frontend
pnpm install
pnpm build# Build the image
docker build -t cxdb:latest .
# Run with persistent storage
docker run -p 9009:9009 -p 9010:9010 \
-v $(pwd)/data:/data \
-e CXDB_DATA_DIR=/data \
cxdb:latestA Turn is an immutable node in a directed acyclic graph (DAG):
Turn {
turn_id: u64 # Unique, monotonically increasing
parent_turn_id: u64 # 0 for root turns
depth: u32 # Distance from root
payload_hash: [32]byte # BLAKE3 hash of payload
}
Turns form chains:
root (turn_id=1) → turn_id=2 → turn_id=3 (head)
↓
turn_id=4 → turn_id=5 (alternate branch)
A Context is a mutable branch head pointer:
Context {
context_id: u64
head_turn_id: u64 # Current tip of this branch
}
Forking is O(1): create a new context pointing to an existing turn.
All turn payloads are stored in a content-addressed blob store:
- Each blob is identified by
BLAKE3(payload_bytes) - Identical payloads are deduplicated automatically
- Blobs are compressed with Zstd level 3
- Stored in
blobs/blobs.packwith an index atblobs/blobs.idx
CXDB supports typed payloads with forward-compatible schema evolution:
-
Go writers define types with numeric field tags:
type Message struct { Role string `cxdb:"1"` Text string `cxdb:"2"` }
-
Registry bundles are published to the server, describing field types and names
-
Rust server uses the registry to project msgpack → typed JSON for readers
-
React UI consumes typed JSON with safe u64 handling and custom renderers
Renderers are JavaScript modules that visualize turn payloads:
// Renderer for com.example.Chart turns
export default function ChartRenderer({ data }) {
return <LineChart data={data.points} />;
}Renderers are:
- Loaded from CDN (ESM modules)
- Sandboxed with CSP
- Hot-swappable without server restart
CXDB is a three-tier system:
┌─────────────────┐
│ React UI │ (Frontend - TypeScript/Next.js)
│ :3000 │
└────────┬────────┘
│ HTTP/JSON
↓
┌─────────────────┐
│ Go Gateway │ (OAuth proxy + static serving)
│ :8080 │
└────────┬────────┘
│ HTTP/JSON
↓
┌─────────────────┐
│ Rust Server │ (Storage + binary protocol)
│ :9009 binary │
│ :9010 HTTP │
└─────────────────┘
│
↓
┌─────────────────┐
│ Storage │
│ - turns/ │ (Turn DAG)
│ - blobs/ │ (Blob CAS)
│ - registry/ │ (Type descriptors)
└─────────────────┘
Go writers (clients) connect via binary protocol (:9009) for high-throughput turn appends.
HTTP readers (UI, tooling) use the JSON gateway (:9010) for typed projections and registry access.
- Getting Started: docs/getting-started.md
- Architecture: docs/architecture.md
- Binary Protocol: docs/protocol.md
- HTTP API: docs/http-api.md
- Type Registry: docs/type-registry.md
- Renderers: docs/renderers.md
- Deployment: docs/deployment.md
- Troubleshooting: docs/troubleshooting.md
- Development: docs/development.md
package main
import (
"context"
"log"
"github.com/strongdm/cxdb/clients/go"
)
func main() {
// Connect to CXDB
client, err := cxdb.Dial("localhost:9009")
if err != nil {
log.Fatal(err)
}
defer client.Close()
// Create a context
ctx, err := client.CreateContext(context.Background(), 0)
if err != nil {
log.Fatal(err)
}
// Append a turn
payload := map[string]interface{}{
"role": "user",
"text": "What is the weather?",
}
turn, err := client.AppendTurn(context.Background(), &cxdb.AppendRequest{
ContextID: ctx.ContextID,
TypeID: "com.example.Message",
TypeVersion: 1,
Payload: payload,
})
if err != nil {
log.Fatal(err)
}
log.Printf("Appended turn %d at depth %d", turn.TurnID, turn.Depth)
}# List contexts
curl http://localhost:9010/v1/contexts
# Get turns from a context (typed JSON projection)
curl http://localhost:9010/v1/contexts/1/turns?limit=10
# Get raw msgpack bytes
curl http://localhost:9010/v1/contexts/1/turns?view=raw
# Page through history
curl "http://localhost:9010/v1/contexts/1/turns?limit=10&before_turn_id=100"import { useTurns } from '@/lib/hooks/useTurns';
function ConversationView({ contextId }: { contextId: string }) {
const { turns, loading, error } = useTurns(contextId);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<div>
{turns.map(turn => (
<TurnCard key={turn.turn_id} turn={turn} />
))}
</div>
);
}CXDB is configured via environment variables:
| Variable | Default | Description |
|---|---|---|
CXDB_DATA_DIR |
./data |
Storage directory |
CXDB_BIND |
127.0.0.1:9009 |
Binary protocol bind address |
CXDB_HTTP_BIND |
127.0.0.1:9010 |
HTTP gateway bind address |
CXDB_LOG_LEVEL |
info |
Log level (debug, info, warn, error) |
CXDB_ENABLE_METRICS |
false |
Enable Prometheus metrics |
See docs/deployment.md for production configuration.
# Run all tests
make test
# Format code
make fmt
# Lint
make clippy
# Run dev stack (backend + gateway + frontend in tmux)
make dev
# Stop dev stack
make dev-stopSee docs/development.md for details.
We welcome contributions! Please:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Before submitting:
# Run pre-commit checks
make precommitPlease read our Code of Conduct and Contributing Guide.
For security issues, please email security@strongdm.com instead of using the public issue tracker.
See SECURITY.md for our security policy.
CXDB is licensed under the Apache License 2.0. See LICENSE for details.
Copyright 2025 StrongDM Inc.
Built with:
- Rust - Server implementation
- Go - Client SDK and gateway
- React + Next.js - Frontend
- BLAKE3 - Content hashing
- MessagePack - Binary serialization
- Zstd - Compression