Skip to content

Streams

mr-data stream registers the connector document, accepts it, records a bounded window or keeps listening, and names the recording a recipe binds.

Terminal window
mr-data stream {document,series,registry,probe,capture,subscribe,subscription,status,recordings} [WORD] [ID] [options] [--json]

A websocket venue’s bytes do not exist until somebody records them. One name carries nine verbs.

A build reads recorded batches and takes no duration of its own. The recipe names the recording rather than the venue, so a run never holds a socket open.

The order:

  1. Register the connector document.
  2. Create a registry that accepts it.
  3. probe it to see what it classifies.
  4. capture a bounded window, or subscribe to keep listening.
  5. recordings to see what a build would bind.
  6. Name the digest in the recipe.
Verb WORD ID What it does
document register or show document id, with show Register a connector document, or read one back.
series register none Register the rotating roster a subscription follows.
registry create none Accept a set of documents.
probe none none Listen briefly and record nothing.
capture none none Record one bounded window and store it.
subscribe none none Keep listening, checkpointing what a build continues from.
subscription show, pause, resume, stop subscription id Read one, or move it.
status the capture or probe id none Read a capture or a probe back.
recordings none none Name what a build would bind for one venue, and until when.
Flag Argument Required Meaning Default
--json none no One JSON object instead of human lines. off
--file FILE with document register, series register The document to register, as a path to a JSON file. A connector document is at most 256 KiB, a series document at most 64 KiB. The reader is strict JSON, and a connector document carries no fractional number anywhere. none
--name NAME no With document register or series register, the name to hold the registered document under. It starts with a lowercase letter, carries lowercase letters, digits, dots and dashes, ends .json, and is at most 133 characters including that suffix. derived from what the document calls itself
--digest DIGEST with registry create One registered connector-document digest, repeatable up to 32 times. With series register, the one connector document the series rotates members of. That creates one registry over the document and the series together. none
--document-digest DIGEST probe, capture, subscribe, recordings The connector document to act against, by the digest its registration printed. none
--registry REGISTRY probe, capture, subscribe The registry that holds the document, by the identifier its creation printed. none
--series SERIES no With subscribe, the series whose roster this subscription follows, by the id the series document declares. Lowercase letters, digits and dashes, starting with a letter, at most 32 characters. none
--member MEMBER probe and capture. With subscribe, unless --series One member of the venue to listen to: a channel, a ticker, a market. Repeatable, up to 256, and each one distinct. A probe and a capture name their members outright and take no --series. none
--seconds N one of these three, with capture How long to record for, 1 to 600. none
--max-events N one of these three, with capture Stop after this many messages, 1 to 10000. none
--max-bytes N one of these three, with capture Stop after this many recorded bytes, 1 to 8388608. none
--secret NAME no The stored credential the venue authenticates with, by the name mr-data keys set printed. The value never appears here and never leaves the vault. none
--kind capture or probe no With status, which resource the identifier names. A probe and a capture live on separate routes, and an identifier does not say which. capture
--watch none no With capture, keep reading the record back until it stops, and print what it stopped on. The capture runs under the bound it was given either way. off

A subscription names its members outright or names a series, never both. Naming both, or neither, returns an error before Studio resolves a credential.

A capture states at least one stopping condition. Without one, the socket would remain open. The three ceilings are hard recorder limits. A request above a limit returns an error instead of being lowered.

A member name is [A-Za-z0-9][A-Za-z0-9._:@/-]{0,255}. The client checks it, so a mistyped member never becomes a 422 from Studio.

mr-data stream document register --file connector.json --json
{
"schema_version": "mostlyright-thin-client-v4-stream-document.v2",
"status": "stream_document_registered",
"lane": "hosted",
"stream_connector_document_id": "4d6e9b05-2f71-48c3-b05a-7e1c9d2a6f83",
"document": {
"document_name": "kalshi.json",
"document_digest": "9f3c…",
"document_kind": "connector",
"connector_id": "kalshi-markets",
"series_id": null,
"endpoint_host": "api.elections.kalshi.com",
"created_at": "2026-09-12T09:00:00Z"
},
"recipe_reference": { "stream_connector_document_digest": "9f3c…" },
"note": "The document is admitted. A recipe names this venue by its digest … and a build reads the batches a capture or a subscription sealed under it rather than reaching the venue itself."
}

Values are illustrative. document show answers the same schema with status: "stream_document_reported" and no recipe_reference. The server computes the digest over its own canonical form. This client computes none, as with recipe registration.

mr-data stream registry create --digest … --json
{
"schema_version": "mostlyright-thin-client-v4-stream-registry.v2",
"status": "stream_registry_created",
"lane": "hosted",
"stream_connector_registry_id": "7e3a1c60-5b84-4297-a1d3-0f6b8c4e2d59",
"document_digests": ["9f3c…", "a1b2…"]
}
mr-data stream series register --file series.json --digest … --json
{
"schema_version": "mostlyright-thin-client-v4-stream-series.v1",
"status": "stream_series_registered",
"lane": "hosted",
"stream_connector_document_id": "b2c3d4e5-6f70-4182-93a4-5b6c7d8e9f01",
"document_digest": "b3c4…",
"series_id": "kalshi-btc-hourly",
"stream_connector_registry_id": "7e3a1c60-5b84-4297-a1d3-0f6b8c4e2d59",
"note": "The series is admitted. A subscription follows it by naming the series id and a registry that holds both this document and the connector document it rotates."
}
mr-data stream probe --document-digest … --registry … --member … --json
{
"schema_version": "mostlyright-thin-client-v4-stream-probe.v1",
"status": "stream_probe_started",
"lane": "hosted",
"probe": {
"probe_id": "c8d92f41-0a63-4b57-8e94-1d5f7a0c3b62",
"status": "queued",
"connector_document_digest": "9f3c…",
"stream_connector_registry_id": "7e3a1c60-5b84-4297-a1d3-0f6b8c4e2d59",
"members": ["BTC-15M"],
"secret_name": "kalshi-ws"
},
"note": "A probe seals nothing: it reports which message types the document classified and which it did not. mr-data stream status c8d92f41-0a63-4b57-8e94-1d5f7a0c3b62 --kind probe reads it back."
}
mr-data stream capture --seconds 60 --watch --json
{
"schema_version": "mostlyright-thin-client-v4-stream-capture.v1",
"status": "stream_capture_settled",
"lane": "hosted",
"polls": 12,
"capture": {
"capture_id": "1b7f4e28-6c05-4d39-b82f-9a3e0d5c7146",
"status": "succeeded",
"desired_state": null,
"connector_document_digest": "9f3c…",
"connector_config_digest": "c5d6…",
"stream_connector_registry_id": "7e3a1c60-5b84-4297-a1d3-0f6b8c4e2d59",
"members": ["BTC-15M"],
"until": "2026-09-12T09:01:00Z",
"secret_name": "kalshi-ws",
"tripped_reason": null,
"sealed_sources": [],
"event_shape_descriptor": {},
"bookmark": null,
"expected_watermark": null
}
}

Without --watch the status reads stream_capture_started, and the payload carries a note naming the two commands that read it back. --watch keeps asking while the resource says queued, dispatched, claimed or running, and stops on anything else. A word this build does not know ends the wait, and the command prints it rather than spinning.

mr-data stream subscribe --series … --json
{
"schema_version": "mostlyright-thin-client-v4-stream-subscription.v2",
"status": "stream_subscription_started",
"lane": "hosted",
"subscription": { "stream_subscription_id": "e04a7c93-1b28-4f65-9d07-3c5e8b1a2f64", "status": "running", "series_id": "kalshi-btc-hourly", "": "…" },
"note": "A subscription keeps recording until it is stopped. Each build reads forward from the bookmark the last one left, so a refresh continues rather than refetching."
}
mr-data stream subscription show e04a7c93-1b28-4f65-9d07-3c5e8b1a2f64 --json
{
"schema_version": "mostlyright-thin-client-v4-stream-subscription.v2",
"status": "stream_subscription_reported",
"lane": "hosted",
"subscription": { "stream_subscription_id": "e04a7c93-1b28-4f65-9d07-3c5e8b1a2f64", "": "…" },
"recording": {
"status": "running",
"desired_state": "running",
"series_id": "kalshi-btc-hourly",
"members_now": 173,
"members": ["KXBTCD-26SEP12-B1", "…"],
"members_not_shown": 165,
"last_data_at": "2026-09-12T09:00:12Z",
"last_discovery_at": "2026-09-12T08:00:00Z",
"next_discovery_at": "2026-09-12T10:00:00Z",
"next_boundary_at": null,
"gap_count": 0,
"last_gap": null,
"bound_table_ids": ["3f8c2d10-6a4b-4c8e-9d1f-5b7a0c2e4d68"],
"checkpoint_sequence": 41008,
"current_cursor": "…"
}
}

The command counts and samples the roster rather than printing it whole. A series can hold 256 contracts open, and the first eight plus a count is what somebody reads this for. status is stream_subscription_reported, _paused, _resumed or _stopped, one word each for reading one, holding it, letting it run again and ending it.

mr-data stream status 1b7f4e28-6c05-4d39-b82f-9a3e0d5c7146 --json
{
"schema_version": "mostlyright-thin-client-v4-stream-status.v1",
"status": "stream_status_reported",
"lane": "hosted",
"kind": "capture",
"capture": { "capture_id": "1b7f4e28-6c05-4d39-b82f-9a3e0d5c7146", "status": "succeeded", "": "…" }
}

The payload carries the resource under a key named after kind, so a probe answers "kind": "probe" with a probe object.

mr-data stream recordings --document-digest … --json
{
"schema_version": "mostlyright-thin-client-v4-stream-recordings.v1",
"status": "stream_recordings_listed",
"lane": "hosted",
"connector_document_digest": "9f3c…",
"count": 3,
"recordings": { "recording 1": { "expires_at": "2026-09-19T09:00:00Z", "": "…" } },
"note": "A batch that has aged out is refused before a worker is claimed rather than midway through a build, so a recording listed here with an expires_at in the past is a recording to replace rather than to name."
}
Code When
THIN_REQUEST_INVALID A missing --file, --digest, --document-digest or --registry. A registry with no digests, with more than 32, or with a duplicate. series register --digest naming more than one connector. A subscription naming both --series and --member, or neither. A member name outside the grammar, the same member named twice, more than 256 of them, or none at all. A series id outside its grammar. A capture with no bound at all, or a --seconds, --max-events or --max-bytes outside 1 to its ceiling. A --file that is not one readable strict-JSON object, or is over its byte ceiling. A WORD that is not one this verb takes.
THIN_RESPONSE_INVALID Studio started a probe, capture or subscription without identifying it.
THIN_SUBSCRIPTION_REQUIRED A build named a recording that no subscription is producing.
THIN_STREAM_UNAVAILABLE, THIN_STREAM_REFUSED The venue refused, or nothing could reach it.
THIN_STUDIO_* The venue controller’s own refusals, carried through under their own codes.
from a connector document to a bound recording
DOC=$(mr-data stream document register --file kalshi.json --name kalshi.json --json | jq -r .document.document_digest)
REG=$(mr-data stream registry create --digest "$DOC" --json | jq -r .stream_connector_registry_id)
mr-data stream probe --document-digest "$DOC" --registry "$REG" --member BTC-15M --secret kalshi-ws --json
mr-data stream capture --document-digest "$DOC" --registry "$REG" --member BTC-15M \
--secret kalshi-ws --seconds 60 --max-events 5000 --watch --json
mr-data stream recordings --document-digest "$DOC" --json

Then name the venue in the recipe as {"stream_connector_document_digest": "…"}, the digest rather than the endpoint.

  • CLI reference: conventions, exit codes, environment variables
  • Recipes and sources: keys stores the shaped credential a venue authenticates with
  • Tables: the tables a subscription’s recordings keep fresh