Distributed Mode
Scale SapixDB horizontally with Cell Division — one command bootstraps a full replica from your existing strand. Choose CP or AP consistency per deployment. Coordinate multi-agent writes safely with Saga transactions.
Cell Division
POST /v1/cluster/divide is the single command that creates a replica. SapixDB pushes every existing block to {endpoint}/v1/mesh/ingest, then registers the new agent as a peer. All subsequent writes fan out automatically.
POST /v1/cluster/divide
Content-Type: application/json
{ "endpoint": "http://replica-agent:7475" }
# Response
{
"replica_agent_id": "a7f3c2d1",
"blocks_pushed": 1842,
"endpoint": "http://replica-agent:7475"
}/v1/mesh/ingest internal endpoint and streams all records in genesis → tip order. The replica is immediately readable after the call returns.Node Roles
CAP Consistency
SapixDB defaults to AP (Available + Partition-tolerant). Switch to CP (Consistent + Partition-tolerant) to block writes cluster-wide when any node becomes unreachable.
- Writes always succeed on reachable nodes
- Divergent state reconciled on partition heal
- Best for high-throughput ingest, IoT, analytics
- Writes return 503 if any node is unreachable
- Guarantees all nodes see same strand tip
- Best for financial ledgers, audit trails, SOX
# Switch to CP consistency
POST /v1/cluster/consistency
Content-Type: application/json
{ "consistency": "cp" }
# Response
{ "consistency": "cp", "previous": "ap" }HTTP API
| Method | Path | Description |
|---|---|---|
POST | /v1/cluster/divide | Register replica + push all blocks |
GET | /v1/cluster/nodes | List all peer nodes |
GET | /v1/cluster/status | Live reachability + partition flag |
DELETE | /v1/cluster/nodes/:agent_id | Remove node from mesh |
POST | /v1/cluster/consistency | Switch CP / AP |
POST | /v1/saga | Create and execute saga transaction |
GET | /v1/saga | List all sagas (most recent first) |
GET | /v1/saga/:id | Fetch saga with full step detail |
Cluster status response
{
"nodes": [
{
"agent_id": "primary-001",
"endpoint": "http://primary:7475",
"role": "primary",
"added_at_ms": 1715000000000,
"last_sync_ms": 1715001234567,
"reachable": true
},
{
"agent_id": "replica-002",
"endpoint": "http://replica:7475",
"role": "replica",
"added_at_ms": 1715000050000,
"last_sync_ms": 1715001230000,
"reachable": true
}
],
"partitioned": false,
"consistency": "ap"
}Saga Transactions
A saga is a sequence of writes distributed across multiple agents. If any step fails, SapixDB writes TOMBSTONE compensation records (flags: 2, _saga_compensation: true) to each previously-committed agent in reverse order, preserving strand immutability while logically undoing the effect.
POST /v1/saga
Content-Type: application/json
{
"steps": [
{
"target_agent_url": "http://ledger-agent:7475",
"payload_b64": "<base64 msgpack>",
"flags": 0
},
{
"target_agent_url": "http://audit-agent:7475",
"payload_b64": "<base64 msgpack>",
"flags": 0
}
]
}{
"id": "a3f8c201",
"coordinator_agent_id": "primary-001",
"state": "committed",
"created_at_ms": 1715001000000,
"completed_at_ms": 1715001000042,
"steps": [
{
"step_id": "0",
"target_agent_url": "http://ledger-agent:7475",
"status": "applied",
"record_hash": "b3d4e5f6a7b8c9d0..."
},
{
"step_id": "1",
"target_agent_url": "http://audit-agent:7475",
"status": "applied",
"record_hash": "c4e5f6a7b8c9d0e1..."
}
]
}{
"id": "b2e9f301",
"state": "compensated",
"steps": [
{
"step_id": "0",
"status": "compensated",
"compensation_hash": "d5f6a7b8c9d0e1f2..."
},
{
"step_id": "1",
"status": "failed",
"error": "connection refused"
}
]
}flags: 2 and _saga_compensation: true in the payload, so the full transaction history — including the rollback — is preserved in the strand.Cross-node Graph Traversal
Edges can reference agent IDs on any cluster node. When the coordinator receives a GET /v1/graph/traverse request, it resolves which node owns each agent in the traversal path and fans out sub-queries automatically. Results are merged before returning.
GET /v1/graph/traverse?from=agent-a&depth=2&direction=outbound
Python SDK
Install the sapixdb-distributed package:
pip install sapixdb-distributed
import asyncio
from sapixdb_distributed import DistributedClient
async def main():
async with DistributedClient("http://primary:7475") as dist:
# Replicate to a new node
result = await dist.divide("http://replica:7475")
print(f"Replica {result.replica_agent_id}: {result.blocks_pushed} blocks synced")
# Check health
status = await dist.cluster_status()
for node in status.nodes:
mark = "✓" if node.reachable else "✗"
print(f" {mark} {node.agent_id} ({node.role})")
# Switch to CP consistency for a SOX-regulated window
await dist.use_cp()
print("Switched to CP consistency")
asyncio.run(main())import asyncio
from sapixdb_distributed import DistributedClient, SAGA_COMMITTED
async def transfer(amount: float, from_url: str, to_url: str):
async with DistributedClient(from_url) as dist:
saga = await dist.execute_saga([
{
"target_agent_url": from_url,
"payload": {"action": "debit", "amount": amount, "currency": "USD"},
},
{
"target_agent_url": to_url,
"payload": {"action": "credit", "amount": amount, "currency": "USD"},
},
])
if saga.state == SAGA_COMMITTED:
print(f"Transfer committed: saga {saga.id}")
else:
print(f"Transfer rolled back: {saga.state}")
for step in saga.steps:
if step.error:
print(f" Step {step.step_id}: {step.error}")
asyncio.run(transfer(100.0, "http://ledger-a:7475", "http://ledger-b:7475"))from sapixdb_agent import SapixClient
import base64, msgpack
async def main():
async with SapixClient("http://primary:7475") as client:
# Cluster
nodes = await client.cluster_nodes()
status = await client.cluster_status()
# Saga (encode payloads yourself)
def encode(obj):
return base64.b64encode(msgpack.packb(obj, use_bin_type=True)).decode()
saga = await client.saga_execute([
{"target_agent_url": "http://agent-a:7475", "payload_b64": encode({"x": 1}), "flags": 0},
{"target_agent_url": "http://agent-b:7475", "payload_b64": encode({"x": 2}), "flags": 0},
])
print(saga["state"])SDK API Reference
| Method | Returns | Description |
|---|---|---|
divide(endpoint) | DivideResponse | Register replica and push all blocks |
list_nodes() | ClusterNodesResponse | Return all registered cluster nodes |
cluster_status() | ClusterStatusResponse | Live reachability check + partition flag |
remove_node(agent_id) | None | Remove node from mesh |
set_consistency(mode) | ConsistencyResponse | Switch 'cp' or 'ap' |
use_cp() | ConsistencyResponse | Shorthand for CP consistency |
use_ap() | ConsistencyResponse | Shorthand for AP consistency |
is_healthy() | bool | True if no unreachable nodes and not partitioned |
execute_saga(steps) | SagaTransaction | Encode payloads and run distributed saga |
list_sagas() | SagaListResponse | All sagas, most recent first |
get_saga(saga_id) | SagaTransaction | Single saga with full step detail |