Now onboarding Access by request

Your application defines the network.

Turn anything into a realtime stream, then decide exactly who can reach it: your own machines, your team, or any app or agent through a URL. Nothing leaves your machine until you say so.

Works with
  • macOS
  • Linux
  • Windows

Working today. We're letting teams in a few at a time.

Pull it up anywhere

Every exposed stream is a URL. Open it in a browser, hand it to an agent, or pipe it into the tools you already use.

live.tatolab.com/acme/pitch/video
# record the live video, no player needed
$ ffmpeg \
    -i https://live.tatolab.com/acme/pitch/video.ts \
    -c copy pitch.mp4

# or let the CLI look up the URL
$ ffmpeg -i "$(tatolab url pitch/video --as ts)" \
    -c copy pitch.mp4
Output
Input #0, mpegts, from 'https://live.tatolab.com/s/7c…':
  Stream #0:0: Video: h264 (High), 1920x1080, 23.98 fps
Code

The code behind the feed.

One Python file wires the camera, the detector and the encoder together, and names what the stream offers. A second decides who can reach each part. Agents use it like any other tool.

from tatolab.stream import CameraSource, H264Encoder, Stream

from processors.player_detector import PlayerDetector   # your own processors
from processors.highlight import Highlight
from processors.ptz import FollowBall


def setup(rt: Stream) -> None:
    camera    = rt.add(CameraSource)
    detector  = rt.add(PlayerDetector)
    highlight = rt.add(Highlight)
    follow    = rt.add(FollowBall)
    encoder   = rt.add(H264Encoder)

    rt.connect(camera.output("video"), detector.input("video"))
    rt.connect(camera.output("video"), encoder.input("video"))
    rt.connect(detector.output("detections"), highlight.input("detections"))
    rt.connect(detector.output("detections"), follow.input("detections"))

    # Name what this stream offers. Nothing leaves the machine yet.
    rt.offer("video", encoder.output("encoded_video"))
    rt.offer("detections", highlight.output("detections"))
    rt.offer("commands", highlight.input("commands"))
Compose

Wire in anything.

Anything with an API can join a stream: a camera SDK, a robot's driver, a model, or a library in any language with an FFI. Expose it to Python and wire it in.

  • Any API, any language

    Use the SDKs and drivers you already have. Code in C, C++, Rust, or anything else with an FFI becomes a processor once Python can call it.

  • Python is the wiring

    Add processors and connect an output to an input. Every connection is one line you can read.

  • Isolated and live

    Each Python processor runs in its own process, so a slow or crashing stage can't stall the others. Add, remove and rewire them while the stream runs, from code or from an agent.

import ctypes

from tatolab.stream import input, processor

# The camera vendor's C SDK. A library in C++, Rust, Go, or any
# language with an FFI wires in the same way.
ptz = ctypes.CDLL("libvendorptz.so")


@processor
class FollowBall:
    """Pans the pitch camera to keep the ball in frame."""

    @input(delivery_profile="newest")
    def detections(self) -> dict: ...

    def setup(self, ctx) -> None:
        self.cam = ptz.ptz_connect(b"192.168.1.40")

    def process(self, ctx) -> None:
        event = ctx.inputs.read("detections")
        if event and event["ball"]:
            x, y = (ctypes.c_float(v) for v in event["ball"])
            ptz.ptz_center(self.cam, x, y)
Stream anything

If Python can produce it, you can stream it.

Video, audio, tensors, JSON, bytes. Streams carry any payload, with no schema to register.

CamerasVideo, straight onto the GPU
MicrophonesAudio, on the device's own clock
ScreensA display or a window
Sensors and robotsReadings, state and commands
ModelsDetections, tracks, transcripts
Anything elseWhatever your processor writes
Expose

Define what goes out.

Nothing leaves your machine until you expose it. You choose who can reach each stream, and who can write back.

Private by default

A stream stays on the machine that runs it until you expose it.

pitch/video        private
pitch/detections   private

Peer to peer

Expose a stream to your own machines and team. They connect directly, with no port forwarding.

pitch.expose("detections",
             to="peers")

Public, one stream at a time

Give one stream a URL anyone you share it with can open. Nothing else goes with it.

pitch.expose("video",
             to="public")

Two-way

Let the apps and agents you allow send data back in, like what the feed should highlight.

pitch.expose("commands",
             to="peers",
             write=True)

Encrypted

Streams are encrypted before they leave your machine. Already on Tailscale? Run over your tailnet too.

$ tailscale up    # optional

Your own nodes and relay

Run the whole path yourself, or use ours.

nodes   your machines
relay   yours, or ours
Compare

Not a tunnel. Not a VPN.

Tunnels put a port on the network. VPNs put a device on it. Tatolab puts the streams you expose on it.

Tunnelngrok and similarDevice networkTailscale and similarTatolabApplication-defined
What's on the networkA port on one machineWhole devicesStreams you expose
Addressed byA URL per portA device name or IPA stream name, or a URL
Who can use itAnyone with the URLDevices on the networkThe people, apps and agents you allow
Configured inA CLI or config fileAn admin consoleYour code, the CLI, or an agent
Already on Tailscale? Run over your tailnet to encrypt everything. It isn't required.

Compose your first stream.

Tell us what you'd stream. We're letting teams in a few at a time, and we'll reach out to get you set up.

We'll only use this to contact you about Tatolab. See our privacy policy.