GitHub - t4db/t4: S3-durable key-value storage with etcd v3 compatibility

3 min read Original article ↗

CI Go Reference Go Report Card License: MIT Docs

An embeddable, S3-durable key-value store for Go, with an etcd-compatible standalone server.

  • Embedded-firstt4.Open(cfg) is the entire API. No sidecar, no daemon.
  • S3-durable — WAL segments and periodic checkpoints are uploaded to S3. A node that loses its disk recovers automatically.
  • Multi-node — Leader elected via an S3 lock. Followers stream the WAL in real time and forward writes transparently.
  • etcd v3 compatible — The standalone binary speaks the etcd v3 gRPC protocol, including multi-key transactions.
  • Twelve-factor config — CLI flags can be supplied through T4_* environment variables.
  • Branches — Fork a database at any checkpoint with zero S3 copies. Each branch writes to its own prefix; shared SST files are deduplicated automatically.

Embedded usage

import "github.com/t4db/t4"

node, err := t4.Open(t4.Config{
    DataDir: "/var/lib/myapp/t4",
})
defer node.Close()

rev, err := node.Put(ctx, "/config/timeout", []byte("30s"), 0)

kv, err := node.Get("/config/timeout")
fmt.Println(string(kv.Value)) // 30s

events, _ := node.Watch(ctx, "/config/", 0)
for e := range events {
    fmt.Printf("%s %s=%s\n", e.Type, e.KV.Key, e.KV.Value)
}

With S3 durability

import (
    "github.com/t4db/t4"
    "github.com/t4db/t4/pkg/object"
)

store, err := object.NewS3StoreFromConfig(ctx, object.S3Config{
    Bucket: "my-bucket",
    Prefix: "t4/",
    Region: "us-east-1",
    // Endpoint: "http://localhost:9000", // MinIO or another S3-compatible store
})
if err != nil {
    return err
}

node, err := t4.Open(t4.Config{
    DataDir:     "/var/lib/myapp/t4",
    ObjectStore: store,
})

Standalone binary

The t4 binary exposes the etcd v3 gRPC protocol. Use etcdctl, the official Go client, or any other etcd v3 compatible tool.

go install github.com/t4db/t4/cmd/t4@latest

# Single node, local only
t4 run --data-dir /var/lib/t4 --listen 0.0.0.0:3379

# Single node with S3
t4 run --data-dir /var/lib/t4 --listen 0.0.0.0:3379 \
           --s3-bucket my-bucket --s3-prefix t4/

# The same configuration can come from environment variables.
T4_DATA_DIR=/var/lib/t4 \
T4_LISTEN=0.0.0.0:3379 \
T4_S3_BUCKET=my-bucket \
T4_S3_PREFIX=t4/ \
T4_S3_REGION=us-east-1 \
t4 run

# Verify
etcdctl --endpoints=localhost:3379 put /hello world
etcdctl --endpoints=localhost:3379 get /hello

For offline inspection of a local data directory, use t4 inspect:

# Show local metadata without starting a server.
t4 inspect meta --data-dir /var/lib/t4

# Explore current keys.
t4 inspect list --data-dir /var/lib/t4 --prefix /config/
t4 inspect get --data-dir /var/lib/t4 /config/timeout

# Explore revision history and changes over time.
t4 inspect history --data-dir /var/lib/t4 /config/timeout
t4 inspect diff --data-dir /var/lib/t4 --from-rev 100 --to-rev 120 --prefix /config/

Multi-node and production setup: see Operations.


Branching

Branches fork a database from an existing S3 checkpoint without copying shared SST files.

# Register the branch against the source prefix.
checkpoint_key=$(t4 branch fork \
  --s3-bucket my-bucket \
  --s3-prefix t4/ \
  --branch-id experiment)

# Start the branch in its own prefix, using the source prefix as its ancestor.
t4 run \
  --data-dir /var/lib/t4-experiment \
  --listen 0.0.0.0:3379 \
  --s3-bucket my-bucket \
  --s3-prefix t4-experiment/ \
  --branch-prefix t4/ \
  --branch-checkpoint "$checkpoint_key"

When the branch is retired, remove its registry entry so future GC can reclaim unneeded source objects:

t4 branch unfork --s3-bucket my-bucket --s3-prefix t4/ --branch-id experiment

Documentation

Full documentation is available at t4db.github.io/t4.

Document Contents
Getting Started Quickstart for standalone server and embedded Go library
API Reference Full Go API — methods, types, errors, branching
Configuration All config fields and CLI flags
Operations Multi-node clusters, S3, TLS, authentication, RBAC, observability
Backup and Restore Checkpoints, point-in-time restore, branching, retention
Security TLS, mTLS, client auth, RBAC setup
Recipes Distributed locks, service discovery, common patterns
Kubernetes Helm chart, StatefulSet deployment
Docker Compose Local, MinIO-backed, and multi-node cluster examples
Architecture Internals — WAL, checkpoints, leader election, replication
Benchmarks T4 vs etcd benchmark results and analysis
Migrating from etcd Compatibility table and migration steps
Troubleshooting Diagnostics, debug logging, and common fixes
FAQ Frequently asked questions