The Go control plane talks to the C++ engine (carded) over a Unix domain
socket using a small framed binary protocol. All multi-byte integers are
little-endian. Floats are IEEE-754 binary64 (Go math.Float64bits /
C++ memcpy-bitcast).
Every request and response is a single frame:
[u32 length][payload] // length excludes the 4-byte prefix; max 64 MiB
- Request payload:
[u8 op][body] - Response payload:
[u8 status][body]—status == 0is OK.
| op | name | request body | response body |
|---|---|---|---|
| 0 | Ping | (none) | (none) |
| 1 | Write | u32 count, then count × {u64 series_id, i64 ts_ms, f64 value} |
u64 accepted |
| 2 | Query | u64 series_id, i64 start_ms, i64 end_ms, u8 agg |
see below |
| 3 | Stats | (none) | u64 series, u64 samples, u64 chunks, u64 chunk_bytes |
| code | meaning |
|---|---|
| 0 | Raw (return points) |
| 1 | Sum |
| 2 | Min |
| 3 | Max |
| 4 | Avg |
| 5 | Count |
| 6 | Rate (per-second, over the window) |
- Raw (
agg == 0):u32 count, thencount × {i64 ts_ms, f64 value}. - Aggregation (
agg != 0):u8 has_scalar, thenf64 scalar(has_scalar == 0means the series/range was empty).
series_id = FNV-64a(metric, sorted(labels)), computed in the control plane
(engineclient.SeriesID). Labels are sorted before hashing so the id is
independent of map/iteration order — this is what makes routing the same
series to the same shard deterministic across nodes.
- This protocol is the Phase-1 transport. The ingest fast path is slated to move to a shared-memory ring buffer (the engine and control plane already run on the same host and share a socket today); query/admin stay here.
- A real deployment would add per-request ids, a protocol version byte, and optional batching of query results into time-bucketed pages.