SapixDBSapixDB/Docs
Home
Protocol Specification

SapixDB Wire Protocol

This document specifies the SapixDB wire protocol in sufficient detail for a third-party developer to implement a conforming client independently. It covers the transport layer, versioning, authentication, request and response formats, mandatory cryptographic fields, and error handling.

Protocol Version: 1.0Transport: HTTP/1.1, HTTP/2Default Port: 7475 (sapixdb)Serialisation: JSON

§1

Protocol Overview

The SapixDB protocol is a JSON-over-HTTP application protocol. All communication between a client and a SapixDB agent uses standard HTTP/1.1 or HTTP/2 framing with JSON-encoded request and response bodies. The protocol is stateless at the HTTP level; persistent state is maintained within the agent's strand (the append-only hash-linked record chain).

The SapixDB protocol is not compatible with gRPC. A conforming SapixDB client cannot be an unmodified gRPC client for the following reasons, each of which creates an incompatibility at the wire level:

  • Serialisation: SapixDB uses Content-Type: application/json. gRPC uses binary Protocol Buffers with Content-Type: application/grpc. A gRPC client cannot parse SapixDB's JSON responses.
  • Path structure: gRPC mandates /{package}.{Service}/{Method}. SapixDB uses resource-oriented paths such as /v1/agents/{id}/query. gRPC clients perform strict path validation and will reject SapixDB paths.
  • Mandatory cryptographic response fields: Every SapixDB record response includes content_hash and parent_hash as BLAKE3 hash fields. A conforming client must validate these fields. No gRPC client produces or validates these fields.
  • Ed25519 record signatures: Every record carries an Ed25519 signature from the owning agent, included as a first-class field. This is a SapixDB protocol primitive with no gRPC equivalent.

§2

Transport and TLS

2.1 TLS Requirements

All SapixDB connections must use TLS 1.3. Plaintext connections are rejected by default. Servers must not offer TLS versions below 1.2. The recommended configuration is TLS 1.3-only.

Server authentication via a valid TLS certificate is required. Clients must validate the server certificate chain and hostname. Self-signed certificates may be used in development but must not be accepted without explicit operator configuration.

2.2 HTTP Version

SapixDB agents accept HTTP/1.1 and HTTP/2. HTTP/2 is preferred for its multiplexing properties when a client issues concurrent queries. The server negotiates the HTTP version via ALPN during TLS handshake.

2.3 Default Port

The registered port for SapixDB client connections is 7475 (service name: sapixdb). All query, read, write, agent management, and semantic query operations are multiplexed on this single port. Administrative operations may be served on a separate operator-configured port (default: the dynamic range). Inter-agent mesh traffic may also be served on a separate operator-configured port.


§3

Versioning

All SapixDB endpoints are versioned via the URL path prefix. The current stable version is v1, expressed as /v1/ at the start of every endpoint path.

When a new major version introduces breaking changes, it is served under a new path prefix (e.g. /v2/) while /v1/ continues to be served for a documented deprecation period. Clients must specify the version explicitly in every request path. A request to /records (without a version prefix) returns 404 Not Found.

Within a major version, new fields may be added to response objects and new endpoints may be added. Existing fields are never removed within a major version. Clients must treat unknown fields in responses as ignorable (forward-compatible parsing).

3.1 Version Header

Responses from a SapixDB agent include the following header identifying the agent's protocol version:

Response Header
X-SapixDB-Protocol-Version: 1.0

Clients may use this header to verify they are communicating with a compatible agent version. Future major versions will increment the major number.


§4

Authentication

SapixDB supports three authentication mechanisms. The server advertises which mechanisms are accepted via theWWW-Authenticateheader on a 401 response. Anonymous access is disabled by default.

4.1 Bearer Token (JWT / API Key)

Clients pass a signed token or opaque API key in the HTTP Authorization header:

HTTP
Authorization: Bearer <token>

JWT tokens must be signed with Ed25519 (alg: EdDSA). The token payload must include sub (client identity),iat, and exp. Tokens with an expiry more than 24 hours in the future are rejected.

4.2 Mutual TLS (mTLS)

Clients may authenticate by presenting a client certificate during TLS handshake. The agent verifies the certificate against its configured trust store. mTLS is required for privileged administrative operations when the agent is configured to enforce administrator authentication.

4.3 API Key

Opaque API keys are accepted via the Bearer scheme. Keys are generated viaPOST /v1/auth/keysand stored as a BLAKE3 hash in the agent's policy store.


§5

Request Format

All requests with a body must set Content-Type: application/json. Request bodies are UTF-8 encoded JSON objects. Maximum request body size is 64 MB (configurable). Requests that exceed the limit receive 413 Content Too Large.

GET requests do not have a body; parameters are passed as URL query string fields.

5.1 Required Headers

HTTP
POST /v1/records HTTP/1.1
Host: your-agent.example.com:7475
Authorization: Bearer <token>
Content-Type: application/json
Content-Length: <n>

{ "payload": { ... } }

5.2 Idempotency Key

Write operations (POST to /v1/records) optionally accept an idempotency key. When present, a duplicate request with the same key within a 5-minute window returns the original response rather than creating a new record:

HTTP
X-SapixDB-Idempotency-Key: <client-generated UUID>

§6

Response Format

All SapixDB responses use Content-Type: application/jsonand UTF-8 encoding. HTTP status codes follow standard semantics (2xx success, 4xx client error, 5xx server error).

6.1 Response Headers

Response Headers
X-SapixDB-Protocol-Version: 1.0
X-SapixDB-Agent-ID: <agent_id>
X-SapixDB-Agent-Sig: <base64url-Ed25519-signature>
Content-Type: application/json
X-SapixDB-Protocol-VersionstringREQUIRED
Protocol version served by this agent. Currently 1.0.
X-SapixDB-Agent-IDstringREQUIRED
The agent ID of the agent that produced this response. Used by clients operating in multi-agent environments to confirm which agent handled the request.
X-SapixDB-Agent-Sigbase64url stringREQUIRED
Ed25519 signature over the SHA-256 hash of the response body, signed by the agent's private keypair. Clients may verify this against the agent's public key (retrievable via GET /v1/status). A conforming client must reject responses with invalid or missing signatures when signature verification is enabled.

§7

Cryptographic Primitives

Cryptographic integrity is a first-class primitive of the SapixDB protocol, not an optional extension. The following fields are present in every record returned by any endpoint and are mandatory for a conforming client to process:

7.1 Record Structure

Every record response object includes the following mandatory fields:

Record Object (JSON)
{
  "record_id":    "019...",
  "content_hash": "3a7f9c2b...",
  "parent_hash":  "1e5d8a4c...",
  "timestamp_hlc": 1748736000065536,
  "timestamp_ms":  1748736000,
  "payload_b64":  "<base64-msgpack-encoded payload>",
  "payload":      { ... },
  "flags":         0,
  "schema_version": 1
}
record_idstring (UUID v7)REQUIRED
Unique identifier for this record. UUID version 7 (time-ordered). Globally unique within the agent's strand.
content_hashstring (BLAKE3, 64 hex chars)REQUIRED
BLAKE3 hash of the canonical serialised record payload. This is the record's permanent identity on the chain. Clients that cache or index records must use this field as the stable key. The hash is computed over the MessagePack encoding of the payload before storage.
parent_hashstring (BLAKE3, 64 hex chars) | nullREQUIRED
BLAKE3 hash of the preceding record in the strand. null only for the genesis record (the first record in a strand). Forms the cryptographic chain: a conforming client can verify the entire strand by traversing parent_hash links from any record back to genesis, recomputing BLAKE3 at each step.
timestamp_hlcuint64
Hybrid Logical Clock timestamp. Encoding: timestamp_hlc = (unix_ms << 16) | counter. Monotonically increasing across writes on the same agent. Upper 48 bits are wall-clock milliseconds; lower 16 bits are a counter for sub-millisecond ordering.
timestamp_msuint64
Wall-clock milliseconds extracted from the HLC: timestamp_hlc >> 16. Provided as a convenience field; clients should use timestamp_hlc for ordering when sub-millisecond precision is required.
payload_b64string (base64-encoded MessagePack)
The raw payload as base64-encoded MessagePack. This is the canonical form over which content_hash was computed.
payloadobject | null
The payload decoded from MessagePack to a JSON object. Present when the payload is a valid MessagePack map. Clients should use this field for application-level access; use payload_b64 for cryptographic verification.
flagsuint8
Bitmask. 0 = normal record;1 = supersession (this record replaces a previous one);2 = tombstone (logical deletion).

7.2 Ed25519 Agent Keypair

Each SapixDB agent holds a permanent Ed25519 keypair. The keypair is derived deterministically from a 32-byte seed (SAPIX_KEYPAIR_SEED_HEX). The private key never leaves the agent process.

The agent's Ed25519 public key is returned by GET /v1/statusin the public_key_hex field. A client that has cached this key can verify the X-SapixDB-Agent-Sig header on every response without trusting the transport layer alone.

7.3 Chain Verification

A conforming client can independently verify the integrity of any segment of a strand using the following algorithm:

Pseudocode
function verify_chain(records):
  for i = 1 to len(records) - 1:
    expected_parent = blake3(msgpack_encode(records[i-1].payload))
    if records[i].parent_hash != expected_parent:
      raise IntegrityError(f"chain broken at record {i}")
  return true

The endpoint GET /v1/strand/verify performs this verification server-side across the entire strand and returns a summary. Clients may also perform this verification locally using the exported strand.


§8

Core Endpoint Reference

All paths are relative to the agent base URL (e.g. https://agent.example.com:7475). All endpoints require authentication unless otherwise noted.

8.1 Health and Status

HTTP
GET /v1/health          → 200 OK  { "ok": true }
GET /v1/status          → AgentStatus object

/v1/health is unauthenticated./v1/statusreturns agent metadata including the agent's Ed25519 public key (public_key_hex), record count, and protocol version.

8.2 Genesis

HTTP
POST /v1/genesis        → RecordView  (creates the root record)

Body:
{
  "agent_id":    "string",
  "description": "string (optional)"
}
Note: Each strand may have only one genesis record. Calling this endpoint on an already-initialised strand returns 409 Conflict.

8.3 Records — Write

HTTP
POST /v1/records        → RecordView  (append a record)
POST /v1/records/json   → RecordView  (append; body is the payload directly)

Body for /v1/records:
{
  "payload":    object,     ← required: the data to store
  "supersedes": string,     ← optional: content_hash of the record this supersedes
  "flags":      uint8       ← optional: 0=normal, 2=tombstone
}

8.4 Records — Read

HTTP
GET /v1/records/:hash        → RecordView  (by content_hash)
GET /v1/records/:hash/json  → payload object only
GET /v1/strand/head          → { head_hash, sequence, agent_id, timestamp_hlc }
GET /v1/strand/records       → { records: RecordView[], total, offset }
GET /v1/strand/as-of?ts=<ms> → { as_of_ts, records: RecordView[] }
GET /v1/strand/export        → newline-delimited JSON (full strand export)
GET /v1/strand/verify        → { valid: bool, record_count, broken_at_sequence? }

8.5 SaQL Queries (Tier 2 Structured)

HTTP
POST /v1/query    → QueryResult

Body — one of the following query types:

  { "type": "latest",     "limit": <1–1000> }
  { "type": "chain_head" }
  { "type": "hash",       "content_hash": "<64-hex-char BLAKE3>" }
  { "type": "time_range", "from_ts": <uint64 HLC>, "to_ts": <uint64 HLC> }
  { "type": "as_of",      "timestamp_hlc": <uint64>, "limit": <1–1000> }

8.6 Semantic Queries (Tier 1 Natural Language)

HTTP
POST /v1/query/semantic    → SemanticResponse

Body:
{
  "query":       "string (natural language)",
  "llm_url":     "string (optional: BYOK LLM base URL)",
  "llm_api_key": "string (optional: BYOK LLM API key)",
  "llm_model":   "string (optional: BYOK model name)"
}

Response:
{
  "source":  "cache" | "heuristic" | "model",
  "plan":    { ... },    ← compiled SaQL plan, for client inspection
  "records": RecordView[],
  "count":   integer
}

The source field indicates how the plan was produced: from the plan cache, the built-in heuristic planner, or an LLM. Theplan field is the compiled SaQL query object (same shape as Tier 2 query bodies) returned for client-side caching and transparency.

8.7 Multi-Agent Queries

HTTP
POST /v1/agents/:agent_id/query    → QueryResult  (same body as /v1/query)
GET  /v1/agents/:agent_id/status   → { agent_id, record_count, chain_head }
GET  /v1/agents/:agent_id/strand/head

8.8 Organisms (Named Agent Groups)

HTTP
POST /v1/organisms           → OrganismResponse
GET  /v1/organisms           → { organisms: [...] }
GET  /v1/organisms/:id       → OrganismResponse
DELETE /v1/organisms/:id     → 204 No Content

POST body:
{
  "organism_id": "string",
  "agents":      ["string", ...],  ← agent names to create
  "zone":        "governed" | "protected" | "immutable_core" | "free"
}

§9

Error Responses

Error responses use standard HTTP status codes and return a JSON body with a single error field containing a human-readable message:

Error Response
HTTP/1.1 404 Not Found
Content-Type: application/json
X-SapixDB-Protocol-Version: 1.0

{
  "error": "agent 'my-agent' not found — create it with POST /v1/agents"
}
400Bad RequestMalformed JSON body or invalid field value.
401UnauthorizedMissing or invalid authentication credentials.
403ForbiddenAuthenticated but not authorised for this operation.
404Not FoundAgent, record, or resource does not exist.
409ConflictDuplicate genesis; idempotency key collision.
413Content Too LargeRequest body exceeds the size limit.
422Unprocessable EntitySemantic query could not be interpreted.
500Internal Server ErrorAgent storage or runtime error.

§10

Port Usage

SapixDB is designed to operate all client traffic — reads, writes, queries, agent management, and semantic queries — over a single registered port (7475). This follows the RFC 7605 principle of in-band multiplexing as the preferred alternative to multiple registered ports.

10.1 Inter-agent Mesh Traffic

Replication and mesh synchronisation between SapixDB agent instances is deployment-internal traffic. It does not require a separately registered port. The inter-agent port is user-configurable via the SAPIX_MESH_PORT environment variable and defaults to a value in the dynamic range (49152–65535). Network operators should restrict this port to internal network segments.

10.2 Administrative Access

Administrative operations (key management, agent lifecycle, audit log access) are served over the primary port 7475 using a privileged authentication tier. Operators may optionally configure a separate admin port via SAPIX_ADMIN_PORT, which defaults to the dynamic range. The admin port uses the same wire protocol with mTLS required.


SapixDB Wire Protocol Specification v1.0
© 2026 Sensart Technologies LLC. All rights reserved.