This document is the interoperability contract for clients that do not embed
corium-peer. The canonical service schema is
corium.proto; protocol v1 is
served by PeerServerService over gRPC/HTTP2.
Every transact/subscribe request sends protocol_version = 1. A mismatch is
FAILED_PRECONDITION, never silent downgrade. Deployments use TLS and may
require authorization: Bearer <token> metadata. Standard gRPC status codes
are used: malformed EDN/query input is INVALID_ARGUMENT, an unknown
database/entity is NOT_FOUND, budget exhaustion is INVALID_ARGUMENT, and
upstream loss is UNAVAILABLE.
All protobuf bytes value fields contain one Corium composite item. Integers
use unsigned LEB128 varints; signed integers use zig-zag then LEB128. Counts
precede container contents. Multi-byte fixed scalars are big-endian.
| Tag | Meaning | Payload |
|---|---|---|
00 | nil | none |
10 | boolean | one byte, 00 or 01 |
20 | long | zig-zag varint |
30 | double | sortable IEEE-754 bits, 8 bytes |
40 | instant | Unix milliseconds, zig-zag varint |
50 | UUID | 16 bytes |
61 | keyword | interned UTF-8 name |
71 | string | interned UTF-8 text |
81 | bytes | length varint + bytes |
90 | entity ref | unsigned varint |
a0/a1/a3 | list/vector/set | count + items |
a2 | map | pair count + alternating key/value items |
a4 | tagged literal | interned tag + item |
a5 | symbol | interned UTF-8 text |
An interned string starts with varint 0, then byte-length and new UTF-8
bytes. This defines the next 1-based table slot. Later occurrences encode
that non-zero slot directly. The table is per top-level message. Implementors
can validate their codec against
codec.rs tests.
DbViewSpec names the database plus at most one of as_of, since,
history, as_of_instant, or since_instant. No selector means current. The
instant selectors carry Unix milliseconds and are resolved server-side against
:db/txInstant to the last transaction committed at or before them; an instant
older than the database resolves to basis 0. Query database views bind positionally
to $, $2, and so on; args is a composite vector for remaining :in
bindings. fuel = 0 requests the server default, otherwise the server clamps
it to its configured ceiling.
Query relations/collections stream chunks whose rows decode to vectors and
must be concatenated. Tuple/scalar results contain one item in one chunk.
Always inspect shape; stop only after last = true. Datoms and transaction
ranges use the same chunk/last rule. Transact provides read-your-writes on
the serving peer before it responds.
Subscribe.from_basis_t is exclusive. The first item is a handshake, then
the server backfills every t > from_basis_t without gaps and continues with
live reports, index announcements, and heartbeats. The handshake's
heartbeat_interval_ms (0 from older servers) is the server's heartbeat
cadence; treat silence for a few multiples of it as a dead upstream and
reconnect. After reconnect, send the last fully applied basis and
deduplicate by transaction number.
Protocol v1 does not expose schema plan/apply operations. Those remain
administrative Catalog calls rather than PeerServerService calls. The
proposed schema migration design requires a
later protocol version because schema can then change while a subscription is
live.
That version adds a schema basis and generation to the handshake and a resulting
schema generation to tx reports. The handshake snapshot is effective at the
subscriber's requested from_basis_t. Subsequent reports advance schema and
data together in transaction order. A subscriber from basis 0 receives the
creation-time pre-basis schema seed in the handshake. The seed is not a
transaction and is never emitted by the exclusive t > from_basis_t backfill.
The server rejects an older client during protocol version validation. Thus,
the client cannot replay data against the wrong schema.
Thin clients that merely query through a peer server need no local schema planner: the server applies generations before it serves each database value. Clients consuming raw tx reports must apply schema datoms before other datoms from the same report. They must invalidate affected local assumptions. They must persist their last fully applied basis and schema generation together.
The language-neutral behavioral corpus is in
tests/conformance. The gRPC replay harness
is conformance_grpc.rs.
A client is conformant when it produces the same decoded EDN values for that
corpus, honors every result shape and time view, rejects protocol mismatch,
and resumes subscriptions gaplessly.
Can you improve this documentation? These fine people already did:
Casey Marshall & ClaudeEdit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |