# The Datastream — Specification Id: 7 Last modified: Last reviewed: --- ## 1. What the datastream is The datastream is a **per-node, append-only telemetry pipe**. Every stream is produced by one node incarnation, identified by `(node, life)`. A node can feed zero, one, or many local/remote consumers: the mux is drained once, then the endpoint fans out the resulting events to subscribers. Its core rule is unchanged: > **Nothing between a producer and a view ever interprets a producer payload.** The current implementation is catalog-aware. Producers register a channel name and content class with their stream owner, receive a stream-local numeric `ChannelId`, and submit opaque payload bytes on that id. A single per-stream **mux** queues accepted payloads and stamps each drained frame with a position, interleaving every channel into one ordered stream. A **catalog** maps numeric channel ids back to human-readable names and decode/display metadata. A best-effort **transport** carries catalog declarations and frame events. **Ingest** reconstructs streams by `StreamId` and position into a **store**. **Views** resolve channel names from metadata and decode bytes back into meaning — and only here, at read time, does anything look inside a producer payload. ```text producers register names, submit opaque bytes by ChannelId │ ▼ endpoint / catalog allocate ids, declare stream/channel metadata │ ▼ per-stream MUX queue payloads, assign positions on drain │ ▼ endpoint fanout drain once, broadcast catalog-aware events │ ▼ transport best-effort: may drop / reorder / delay / duplicate │ ▼ ingest reconstruct each stream by position │ ▼ store (truth for frames) whole, append-only, position-keyed │ ▼ views read-time projections; payload decoding lives here ``` This still collapses counters, gauges, histograms, logs, events, traces, and binary blobs into one mechanism: positioned bytes on named channels. The pipe stores and moves frames uniformly because it does not know what a payload "means." That distinction exists in a view: "the last frame on this channel" is a gauge, "every frame on this channel" is an event log, and both are projections over the same stored frames. ### 1.1 Consequences that the rest of this spec spells out - There is **one data-entry shape** (§4): register or reuse a channel id, then submit bytes on that id. - A channel name is not a queue, counter, or buffer, but it **is cataloged** (§5): the stream owner allocates a stream-local numeric id and declares its name/content metadata. - The **store is frame truth**, not catalog truth (§7). Durable name recovery requires catalog descriptors alongside numeric frames. - All **producer-payload semantics live in views** (§8). Catalog content classes help route and display; they do not let mux, transport, ingest, or store inspect producer payloads. --- ## 2. The data model ### 2.1 The frame remains the unit of ordered data ```rust pub struct Frame { pub channel: ChannelId, // stream-local numeric lane pub position: Position, // mux-assigned order within the stream pub payload: Vec, // opaque bytes — never interpreted by the pipe } ``` A typed record, a log line, and a binary blob are all frames: bytes on a stream-local channel id at a stream-local position. `payload` is opaque to the mux, transport, ingest, and store. There is still **no timestamp field on `Frame`**. Time, wall-clock correlation, latency, or tracing data is producer payload, not a datastream-owned sidecar or property on the frame envelope. ### 2.2 Stream identity — incarnations never merge ```rust pub struct StreamId { pub node: NodeId, pub life: Lifetime } ``` `StreamId` is the ingest key. Two streams with the same `node` but different `life` are **different streams and must never merge**. A node that dies and is restarted (re-rented, re-scheduled, or bootstrapped for a new run) begins a new `life`, so the fresh stream does not append to or collide with the prior one. Restart is visible, not silently glued over. The stream descriptor declares where a stream came from: ```rust pub enum StreamOrigin { Orchestrator, Bootstrap, RemoteNode } pub struct StreamDescriptor { pub stream: StreamId, pub label: Option, pub origin: StreamOrigin, } ``` `StreamOrigin` is subscription metadata. It is not a clock and does not order streams. ### 2.3 Position: per-stream, monotonic when assigned ```rust pub struct Position(pub u64); ``` - **One sequence per stream**, not per channel. A stream's mux holds a single counter shared across every channel. A single channel therefore has sparse, non-contiguous positions interleaved with every other channel. - **Assigned while draining.** `submit` only attempts to enqueue payload bytes. `drain` assigns positions to accepted payloads. A queue-full rejection happens before position assignment and therefore does not create a position gap. - **Not comparable across streams.** Positions order frames within one `StreamId` only. There is no global stream order. **Why one counter and not one per channel.** It keeps the mux a single position authority once frames leave the queue and makes assigned-frame loss detection a whole-stream property: a hole means *something already assigned* was lost. A source needing contiguous domain accounting carries its own sequence number inside its payload. ### 2.4 Channels: numeric ids plus catalog descriptors A raw frame contains a numeric channel id: ```rust pub struct ChannelId(pub u32); ``` `ChannelId` values are **stream-local**. `ChannelId(7)` in one stream is not the same channel as `ChannelId(7)` in another stream unless their descriptors say so. The current endpoint allocates user channels starting at `ChannelId(1)`; `ChannelId(0)` is not allocated by the public registration path. The human-readable channel name and display/routing metadata live in a channel descriptor: ```rust pub enum ChannelContentKind { Bytes, TextStream, JsonRecord } pub enum ChannelContent { Bytes, TextStream, JsonRecord { schema: Option }, } pub struct ChannelDescriptor { pub stream: StreamId, pub id: ChannelId, pub name: String, pub label: Option, pub content: ChannelContent, } pub struct ChannelRef { pub stream: StreamId, pub channel: ChannelId, } ``` The globally resolved raw channel identity is `ChannelRef`, not a bare `ChannelId`. A view usually needs the `ChannelDescriptor` for that `ChannelRef` to recover the channel name and choose a decode/display path. ### 2.5 Datastream events The live endpoint/subscription path carries catalog-aware events: ```rust pub struct FrameDelivery { pub channel: ChannelRef, pub position: Position, pub payload: Vec, } pub enum DatastreamEvent { StreamDeclared(StreamDescriptor), ChannelDeclared(ChannelDescriptor), Frame(FrameDelivery), StreamEnded(StreamId), } ``` `FrameDelivery` is the live-event form of a frame: stream and channel are resolved into a `ChannelRef`, then position and payload follow. The older `Delivery { stream, frame }` shape still exists for ingest, store tests, and legacy adapters (§6.1, §7.1). `StreamDeclared` is part of the event vocabulary, but current QUIC transport puts the stream descriptor in the stream header and does not emit a separate `StreamDeclared` event. `StreamEnded` is also part of the event vocabulary, but current `DatastreamEndpoint` exposes no public `end_stream` method; terminal source records remain a producer convention until a stream-ending API is added (§5.7). --- ## 3. Addressing ### 3.1 Raw and resolved coordinates A stored raw frame is addressed by: ```text raw-frame @ (stream, numeric-channel, position) └ node/life ┘ ChannelId order ``` A named channel source is addressed by resolving that raw channel through the catalog: ```text source @ (stream, channel-name) └ node/life ┘ descriptor.name ``` A payload datum is therefore either: - raw: `(StreamId, ChannelId, Position)`, sufficient for storage and ordering; - resolved: `(StreamId, ChannelDescriptor.name, Position)`, required for human-facing selection and decoding. No bare `ChannelId` is globally meaningful. A consumer that receives a frame before its descriptor can store it raw, but cannot render a stable name until the catalog descriptor arrives or is recovered from durable catalog state. ### 3.2 Channel names: structured catalog paths, not wire ids Channel names remain dotted paths, but they are catalog descriptor fields rather than the frame's wire identity. The mux, ingest, and store do not split names; subscription matching and views may match names through descriptors. A channel name is a dotted sequence of segments: ```text channel := namespace ( "." qualifier )* namespace := owning subsystem e.g. datastream, host, runtime, proc, mvp, transport, dist, identity qualifier := instance-key | leaf instance-key identifies a dynamic source; leaf names the signal ``` Current names and families seen in code include: | Path or family | Owner / meaning | |-----------------------------------------------------|-----------------| | `datastream.health` | datastream self-health record | | `host.cpu`, `host.gpu`, `host.net` | host hardware samples | | `runtime.actors` | swactor runtime actor stats | | `proc.