CLI reference
Every tatolab command, its options, and what it prints.
tatolab creates projects and talks to the machine’s runtime. Every command except new, enable-virtual-camera, and logs reading a file from disk needs the runtime running; without it, they stop at once with:
error: no runtime is running on this machine: nothing answers at <socket>. Start one by running `tatolabd` in a terminal.If another user’s runtime holds the machine, the message names it. Run tatolab <command> --help for the same details in your terminal, and tatolab --version for the version.
Projects and streams
Section titled “Projects and streams”tatolab new
Section titled “tatolab new”tatolab new <DIRECTORY> [--test-pattern]Writes a working stream project into DIRECTORY: stream.py, nodes/ (a GPU effect and a CPU meter), pyproject.toml, .python-version (3.12) and .gitignore. The stream shows the camera, inverted, in a window.
| Option | |
|---|---|
--test-pattern |
Use the built-in color bars instead of the camera, for a machine with no camera |
It refuses to write into a directory that already has any of those files, and names them. Afterwards: cd DIRECTORY, uv sync, tatolab dev.
tatolab run
Section titled “tatolab run”tatolab run [TARGET] [-f FILE] [--dir DIR] [--name NAME] [-d]Asks the runtime to load a stream. The runtime runs the stream function with the project’s .venv and starts the graph it builds. Without -d, the stream is attached: its log prints in your terminal, and Ctrl-C, closing the terminal or killing the command stops it.
| Option | |
|---|---|
TARGET |
Which stream: <file>.py[:<function>] or <module>:<function>. Default: the one @stream in stream.py. |
-f, --file FILE |
Load from another file instead of stream.py (not with TARGET) |
--dir DIR |
The project folder, with its .venv. Default: the current folder; parent folders aren’t searched. |
--name NAME |
Load the stream under this name instead of its function’s |
-d, --detach |
Hand the stream to the runtime to keep, print one line, and return |
With -d, it prints <stream> kept (project <dir>). A kept stream runs until tatolab stop or tatolab rm, and the runtime loads it again every time it starts. Running run -d again for the same project and function replaces the kept record. A name already in use is refused, naming the project that has it. A kept stream holds its name even while it’s stopped or failed, until tatolab rm.
tatolab dev
Section titled “tatolab dev”tatolab dev [TARGET] [-f FILE] [--dir DIR] [--name NAME]run, attached, plus a watch on the project’s .py files and pyproject.toml: each save loads the stream again. A save that fails to load prints the error and leaves the previous version running. If the runtime goes away, dev waits for it and loads the stream again. Options as for run, without -d.
tatolab streams
Section titled “tatolab streams”tatolab streamsLists the streams the runtime holds:
NAME STATE NODES PROJECTmotion_watcher kept 6 /home/you/motion-watchergarage failed - /home/you/cameras
garage failed: <the reason>. `tatolab start garage` retries it, `tatolab rm garage` forgets it.States are attached, kept, stopped and failed. With no streams, it prints No streams in this runtime.
tatolab stop
Section titled “tatolab stop”tatolab stop <STREAM>Unloads a stream. A kept stream is recorded as stopped and stays stopped across restarts: <stream> stopped; it stays stopped across restarts until `tatolab start <stream>` . A stopped kept stream still holds its name. Stopping an attached stream ends it, like Ctrl-C in its terminal.
tatolab start
Section titled “tatolab start”tatolab start <STREAM>Loads a stopped kept stream again, or retries a failed one. Prints <stream> started (<n> nodes).
tatolab rm
Section titled “tatolab rm”tatolab rm <STREAM>Unloads a stream and forgets it. It’s the only command that removes a kept stream’s record.
tatolab expose
Section titled “tatolab expose”tatolab expose <STREAM> <NODE> <PORT> [--public | --remove]Sets how far one output port can be read from outside its stream, while it runs: private by default, public with --public, internal again with --remove. A reader the new level no longer allows is cut off at once. For a kept stream, the level is recorded and takes precedence over what the stream function exposes, across restarts. See Connect streams.
Observing
Section titled “Observing”tatolab graph
Section titled “tatolab graph”tatolab graph [--stream STREAM]Prints the live graph as JSON: nodes, ports, links, states and counters. Without --stream, every loaded stream, under the machine’s name as machine; with it, that stream alone. See Inspect a running stream for the fields.
tatolab tap
Section titled “tatolab tap”tatolab tap <CHANNEL> [--stream STREAM] [--count N] [--max-bag-bytes BYTES]Collects up to --count bags an output sends within half a second, without slowing it. On a quiet output, it returns a partial sample instead of waiting. The output needs a reader, a link in its own stream or another stream reading it, or the tap is refused with no tappable channel named …. Exposure doesn’t matter.
| Option | |
|---|---|
CHANNEL |
The output’s address: <stream>/<node>/<port>, <node>/<port> with --stream, or <machine>/<stream>/<node>/<port> naming this machine |
--stream STREAM |
The stream, when the channel doesn’t name it |
--count N |
How many bags to collect, at most |
--max-bag-bytes BYTES |
The largest bag returned whole. A bigger bag comes back marked truncated and can’t be decoded. |
tatolab logs
Section titled “tatolab logs”tatolab logs --stream <STREAM> [-f] [filters]tatolab logs <RUNTIME_ID-STREAM> [-f] [filters]tatolab logs --listWith --stream, reads a loaded stream’s log records from the runtime. With a log name, reads that stream’s log file from disk, in the project’s .streamlib/ folder; run it in the project folder, and use --list to see the names.
| Option | |
|---|---|
-f, --follow |
Keep following new records |
--level LEVEL |
Only this severity and above: trace, debug, info, warn, error |
--source SOURCE |
Only records from rust (the runtime and built-ins) or python (your nodes) |
--processor ID |
Only records from one node, by its ID |
--pipeline ID |
Only records carrying this pipeline_id, a field the runtime sets on some of its own records |
--rhi |
Only GPU-layer records |
--intercepted-only |
Only output captured from print(), stdout and stderr |
tatolab exchange
Section titled “tatolab exchange”tatolab exchange <SURFACE_ID> --out <DIR>tatolab exchange --channel <CHANNEL> [--stream STREAM] [--count N] [--every N] [--field NAME] --out <DIR>Turns frames into exact, full-resolution PNG files, and prints each file’s path. With a surface ID (as a bag carries it, such as 7#42), exchanges that frame. With --channel, samples an output and exchanges the frame each bag names; no window or display is needed.
| Option | |
|---|---|
--out DIR |
Where to write the PNGs; created if needed, never emptied |
--channel CHANNEL |
An output’s address, as for tap |
--count N |
Frames to save. Default 1. |
--every N |
Save every Nth sampled bag. Default 1. |
--field NAME |
The bag key holding the surface ID. Default surface_id. |
--stream STREAM |
The stream, when the channel doesn’t name it |
Agents
Section titled “Agents”tatolab mcp
Section titled “tatolab mcp”tatolab mcpConnects an MCP client to the runtime over standard input and output. You don’t run it yourself; your client does:
claude mcp add tatolab -- tatolab mcpclaude mcp add lab -- ssh lab-machine .local/bin/tatolab mcpA stream the client loads attached lives as long as this connection. See Agents and MCP.
tatolab enable-virtual-camera
Section titled “tatolab enable-virtual-camera”tatolab enable-virtual-camera [--print]Linux only. Grants this machine’s users the permission VirtualCameraSink needs to create v4l2loopback cameras: it loads the module with no devices at boot, and lets the logged-in user create cameras. It’s one privileged step, behind your desktop’s password prompt (sudo in a terminal without a desktop). --print shows the files and commands without changing anything. See Virtual camera.
The runtime: tatolabd
Section titled “The runtime: tatolabd”tatolabd is the runtime itself. The Mac app and the Linux installer run it for you; after a source build, run it in a terminal of its own:
tatolabdIt runs in the foreground until Ctrl-C or a stop signal. A second tatolabd on the same machine is refused with a message naming the first one’s user, process ID and executable.