Skip to content

Quickstart ​

Run a Sovereign Engine node, write through the SDK, and read it back. This guide mirrors the operator + developer getting-started paths from the engine source.

1. Provision a mesh CA (dev) ​

Use pkg/crypto's dev-mesh CA to mint a CA and per-node leaves (or supply your own PKI and point --tls-cert --tls-key --tls-ca at it):

go
ca, err := crypto.NewMeshCA()
caPath, err := ca.WriteCAPEM(".")             // writes ca.pem into the dir; returns its path
leaf, err := ca.IssueLeaf(nodeID)             // nodeID hex; localhost is added to DNSNames internally
certPath, keyPath, err := leaf.WritePEM(".")  // writes cert.pem and key.pem; returns their paths

Dev mesh only

This is a development CA, not production PKI. Leaf rotation and CRL revocation are wired (a revoked or expired cert is rejected at the handshake), but an offline root, intermediate CAs, HSM-backed key custody, and OCSP are not — supply your own PKI for production.

2. Derive node identity ​

Pass --identity-seed (an Ed25519 seed); the nodeID is the first 16 bytes of the derived pubkey and must equal engine.localNodeID.

3. Start a node ​

bash
cmd/sovereign-node \
  --bind :443 \
  --peers peer1:443,peer2:443 \
  --tls-cert cert.pem --tls-key key.pem --tls-ca ca.pem \
  --metrics-addr :9100 \
  --control-addr :4433 \
  --arena-mib 4096 \
  --wal-path /var/lib/sovereign/wal \
  --lsm-root /var/lib/sovereign/lsm \
  --gossip-tick 1s \
  --batch-size 100

The defaults are conservative: pruning OFF, reaper OFF.

Two flags gate the client path

--control-addr is off by default — the SDK and the /v1/* API only listen once you set it. The time-travel reads (/v1/query, /v1/range) additionally need both --wal-path and --lsm-root, because they read the durable tier, not just the live state.

4. Opt into durability & compaction ​

Once you trust the T_gc floor:

bash
  --wal-checkpoint-interval N \
  --compaction-prune-enable \
  --compaction-prune-horizon-ns <T_gc> \
  --compaction-prune-backoff-ns <backoff>

Add --compaction-reap-enable once you trust your storage layer's existence-probe — the reaper never auto-ONs.

5. Observe ​

Scrape /metrics for sovereign_* (labelled) and supremum_* (cumulative) series; hit /livecheck for liveness; arm --otel for OTel export to the operator log.

6. Write & read via the SDK ​

go
client, _ := sovereign.DialWithCerts(":4433", "cert.pem", "key.pem", "ca.pem", "node-1")

// Originator path: routes through Gossiper.InsertLocalEvents → bridge.PutLocal (WAL fsync)
// InsertLocal returns (dotHex, error) — the causal dot is the write's receipt
dotHex, err := client.InsertLocal("entity-1", `{"v":42}`)

// Get — Payload only on the originator; peers return the digest, not a value
res, _ := client.Get("entity-1")
// res.PayloadDigest is always set; res.Payload is non-empty only on the originator

root, _ := client.MerkleRoot()

Honest boundary — originator vs. peer

GetResult.Payload is non-empty only on the originator (cache hit); peers return Payload=="" + PayloadDigest!="" because the engine stores only the PayloadDigest on a joined CRDTEntry (TestClientGetOnPeerReturnsDigestNotValue). The SDK does not claim linearizability — InsertLocal returns at LOCAL-apply; peer convergence is eventual (next gossip sweep).

Direct engine use ​

For direct engine use, the pkg/sync public surface is:

go
eng, err := sync.NewDeltaCRDTEngine(nodeID, initialCounter, arenaSize)
dot := eng.InsertLocal(entityID, entry)      // 0 allocs/op hot path
eng.Join(delta)                              // frozen merge-union
remoteDigest := eng.GenerateDigest()         // IBLT
delta := eng.GenerateDelta(remoteDigest)     // minimal missing set; defer delta.Release()
eng.ApplyCRDTDeltaEvent(wire)                // wire-integrity + Join
wire, _ := sync.BuildCRDTDeltaEvent(entityID, payload, entry)

Next ​

Engineered from first principles to solve the distributed state problem without the latency tax of traditional consensus.