Skip to content

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.

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 [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 [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

Lists the streams the runtime holds:

NAME STATE NODES PROJECT
motion_watcher kept 6 /home/you/motion-watcher
garage 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 <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 <STREAM>

Loads a stopped kept stream again, or retries a failed one. Prints <stream> started (<n> nodes).

tatolab rm <STREAM>

Unloads a stream and forgets it. It’s the only command that removes a kept stream’s record.

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.

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 <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 --stream <STREAM> [-f] [filters]
tatolab logs <RUNTIME_ID-STREAM> [-f] [filters]
tatolab logs --list

With --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 <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
tatolab mcp

Connects an MCP client to the runtime over standard input and output. You don’t run it yourself; your client does:

Terminal window
claude mcp add tatolab -- tatolab mcp
claude mcp add lab -- ssh lab-machine .local/bin/tatolab mcp

A stream the client loads attached lives as long as this connection. See Agents and MCP.

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.

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:

Terminal window
tatolabd

It 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.