Tatolab
The problem
Section titled “The problem”Live data tends to stay inside the program that produces it. A camera feed, a microphone, a sensor, or the output of a model usually belongs to the one process that opened it. Everything else that needs it, such as a recorder, a dashboard, a teammate’s script or an agent, needs capture code of its own, a way to move the data, a format both sides agree on, and a way to find out why it stopped.
Tatolab gives live data one shape and one home on each machine:
- A stream is a function that connects nodes: sources, steps that transform data, and consumers. Data moves between them as bags of named values.
- One runtime per machine,
tatolabd, runs every stream on that machine, from any project. - Every output has an address,
<stream>/<node>/<port>. Expose one, and other streams on the machine can read it. The stream that produces it doesn’t change, and doesn’t need to know who’s reading. An exposed output also gets a URL, served over MoQ by a relay the runtime runs on the machine. - You can see inside while it runs: the live graph, a sample of what any connected output carries, and a JSON Lines log for every stream.
Two streams, sharing
Section titled “Two streams, sharing”Here, one stream opens a camera and reports motion. A second stream, in another project, reads those reports and writes them to a file:
from tatolab.stream import CameraSource, StreamBuilder, stream
from nodes.motion_detector import MotionDetector
@streamdef motion_watcher(stream_builder: StreamBuilder) -> None: """Watches the camera for motion, and shares the camera and the reports.""" camera = stream_builder.add(CameraSource, name="camera") detector = stream_builder.add(MotionDetector, name="detector") stream_builder.connect(camera.output("video"), detector.input("video")) stream_builder.expose(camera.output("video")) stream_builder.expose(detector.output("motion"))from tatolab.stream import StreamBuilder, stream
from nodes.motion_event_log import MotionEventLog
@streamdef motion_log(stream_builder: StreamBuilder) -> None: """Writes each start and stop of motion to motion-events.jsonl.""" event_log = stream_builder.add(MotionEventLog) stream_builder.connect( stream_builder.remote_output("motion_watcher/detector/motion"), event_log.input("motion"), )The camera is opened once. Each node you write runs in its own process, so a slow or failing reader can’t stall the stream it reads from. Either stream can be stopped, changed and started again without touching the other. The tutorial builds both, node by node.
What Tatolab takes care of
Section titled “What Tatolab takes care of”| You’d otherwise write | Tatolab |
|---|---|
| A way for a second program to get the same live data | Expose an output, and any other stream on the machine reads it by its address. |
| Isolation, so one bad consumer can’t take everything down | Every node you write runs in its own process, with your project’s packages. A reader that falls behind drops data at its own input, and the drop is counted. |
| Logging and a way to see what’s happening | tatolab graph shows every node, link and counter; tatolab tap samples a port; each stream writes a JSON Lines log. |
| A way for an agent to work with it | The runtime serves MCP. An agent can read the graph, sample ports, look at frames, read logs, and add or connect nodes. Computer-use agents such as Grok Bot and Muse also open an exposed output by its URL. See Agents and MCP. |
| The fiddly media parts | Built-in camera, microphone, speaker, window, hardware H.264 and H.265, Opus and MP4 recording. Frames stay in GPU memory and reach PyTorch without a trip through the CPU, for the streams that need it. |
At a glance
Section titled “At a glance”- Platforms. Linux on x86_64 with a Vulkan GPU (we test on NVIDIA), and Apple silicon Macs with macOS 15 or later. The runtime needs a GPU. Windows isn’t supported.
- SDKs. Python, with
tatolab-streamfrom PyPI. See Python SDK. - Installing. One command: Homebrew on a Mac,
curlon Linux. See Install. - Sharing on one machine. Streams read each other’s exposed outputs through shared memory, and agents on the machine work with every stream.
- URLs for every exposed output. The runtime runs a MoQ (Media over QUIC) relay on each machine, and the relay serves each exposed output at a URL: tools on the machine read private outputs, your other machines read public ones over your tailnet, and a relay on the internet that the machine joins carries them further. See Across machines.
- Screen capture. A screen, or a window such as a simulator or a browser, becomes a stream you can share like any other.
Where to start
Section titled “Where to start”- New to Tatolab? Install it, then follow the quickstart: a running stream in about five minutes, once Tatolab is installed.
- Want the whole picture? How Tatolab works explains streams, nodes, exposure and the runtime.
- Ready to build? The tutorial builds a stream, shares its output with a second one, and hands both to an agent.
- Have a specific job? Share covers exposing outputs and agents. Build covers nodes, audio, recording, GPU effects and models. Run covers keeping streams running, inspecting them, logs and deploying to a machine.
- Wondering if it fits your problem? Read the use cases, or browse the examples.
Get help
Section titled “Get help”- Found a bug or a confusing page? Open an issue on GitHub, or email hello@tatolab.com.
- Using an AI assistant? Every page here is also Markdown: add
.mdto the address, or point your assistant at /llms.txt.