.mcap file, uncompressed, carrying every frame exactly as it was published — wire sequence and timestamp included.
There are three ways to get one, and they answer different questions:
Record a run
Add--record to the run. The bare flag writes into ./recordings/; a custom directory needs the = form.
Ctrl+C. Recording requires the live clock, so it cannot be combined with --time-source virtual or external, and it is refused together with --no-rings.
A recorded run captures the graph, the environment, and the run’s own record of what fired when. That is what makes it re-executable later — see Re-execute your nodes.
cerulion bag info names anything the recorder could not cover, and a re-execution skips them instead of verifying them. Set CERULION_RECORD_DISCOVERY=off to record only the graph’s declared topics.
By default the environment is captured as names for everything and values only for Cerulion’s own variables, so a bag never leaks a secret you happened to export. --record-env all captures every value verbatim for full replay fidelity, and warns loudly that the bag now embeds them.
Record topics that are already live
cerulion bag record taps this machine’s shared memory. It never opens the network, so recording a robot’s topics means running it on the robot and copying the file afterwards.
Exactly one selection source is allowed — combining a positional list with
-a or -e is an error, because the merge you would assume (“union? intersection?”) is not knowable. Narrow with -x instead.
To describe a run rather than a topic set, attach to a live one:
--run means the sole live run; naming one needs the = form, since every word after a bare --run is read as a topic.
Capture the recent past
Every serving graph on Unix already holds a rolling window of roughly the last 30 seconds in memory.cerulion flashback writes that window out — plus the following 15 seconds — as an ordinary bag. Nothing has to be armed in advance.
recordings/flashbacks/. Add --pin to keep one out of routine retention, and --no-wait to return as soon as the request is accepted instead of waiting for the file. CERULION_FLASHBACK=off turns the capture plane off entirely.
Inspect a bag
state: finalized is the one to read first: playback needs the summary footer that a clean recorder shutdown writes, so a bag from a killed recorder is refused by bag play while bag info still reports what it can read.
Play a bag back
Playback republishes the recorded frames onto shared memory, so everything downstream —topic echo, topic hz, Cerulion Studio, a consumer node — sees what a live robot would.
Two behaviors are worth knowing before you interpret a playback:
- A topic that a live producer already owns is refused by name, and the rest of the bag still plays. Topics are single-writer, so a player must not compete with a live producer.
- Each topic keeps its own recorded rate. Stamps from different producers share no number line, so playback runs one timeline per topic, anchored on that topic’s own first frame.
Re-execute your nodes
--resim is the other half of the verb: instead of republishing recorded frames, it re-executes the bagged graph’s nodes against the recorded input, on the recording’s own clock.
--resim all re-executes every node; a node subset is not supported yet and is refused by name rather than quietly widened.
A bare --resim makes no claim about whether the result matches the recording — divergence is the product, so a completed re-execution exits 0. --verify is the verdict: it byte-compares every produced frame and exits non-zero on divergence. Add --report PATH for a machine-readable verdict, --tolerance PATH for fields that may legitimately differ, and --strict-state to refuse the run unless every node’s state was restored.
A passing
--verify is a strong statement: your current code, re-run against real recorded input, produced the same bytes the robot produced. That is the property to put in CI.- Environment divergence is reported, not ignored. A variable that was present at record time and absent at replay is a warning naming the variable; the run continues.
- A bag recorded with
bag recordis not replay-grade. It carries frames, not the graph and trace a re-execution needs. Usegraph run --recordfor a bag you intend to re-execute, andcerulion bag migrateto bring an older replay-grade bag forward.
Next steps
Visualize a running graph
Watch a live run, or a bag playing back, on the Studio stage.
Run a graph across processes
The default deployment shape, and what it means for recordings.