architecture · mediamesh · infrastructure

MediaMesh Architecture: From Graph Compiler to Streaming Engine

Chris Mahoney

We could draw you a diagram full of boxes that don’t exist yet and arrows that describe aspirations instead of connections. Plenty of architecture docs do exactly that. Ours doesn’t: every service below is something actually running in the stack right now, and every arrow is a real connection between two of them — nothing is on the page just to look complete.

The one node worth fixing your eye on

Everything in MediaMesh exists to get a validated script to one place, and everything after that place is just a different shape the result can leave in.

A browser talks HTTPS to a single TLS-termination point, which routes requests to the Builder UI, to Watch Party, or to the consumer-facing viewer app, depending on the path. The Builder talks to a backend API that compiles a saved graph into a .liq script and validates it. That validated script goes to the engine — and the engine is the node that matters. It’s where a saved graph actually becomes a live signal. Everything upstream exists to get a script to it; everything downstream (viewers, an Icecast mount, a recording sidecar) is just a different destination for the stream that comes out.

The core loop

  1. The Builder is the React app you build graphs in — what you’re actually looking at in the browser.
  2. The API takes a saved graph, type-checks it against real Liquidsoap semantics, and compiles it into a .liq script. A graph that doesn’t type-check doesn’t become a running process — the compiler catches it first.
  3. The engine is where Liquidsoap actually runs the compiled script. One process per channel, not a shared runtime — each process owns its own settings (video frame size, for one), which can’t be shared across channels running at different resolutions simultaneously. That’s not a scaling compromise; it’s what makes running mismatched channels side by side possible at all.
  4. The running process pushes the result out — HLS files, RIST/SRT/RTMP endpoints, NDI, or an Icecast mount, depending on what you actually wired into the graph.

The supporting cast

Not every service is something you touch day to day, but each one earns its place:

  • The TLS/routing layer is the single choke point for HTTPS across the whole stack — everything public-facing goes through it. Consistent with a policy we hold to elsewhere: nobody touches ports, routing, or exposure unilaterally. Changes here get discussed first, always.
  • A sidecar service handles the jobs that don’t belong in the core streaming loop: recording a channel’s output to disk as MP4, serving WebRTC formats, per-channel monitoring and statistics, and eventually acting as a forwarding unit for per-user video outside a WebRTC call.
  • A stock Icecast server sits in the stack as what we’d call a convenience test server — but it’s real and usable, not a placeholder.
  • Watch Party’s own service is a genuinely separate audience-facing surface from the Builder, not a mode of it.
  • The consumer viewer app reads MediaMesh’s publish manifest and lives in a sibling repo, built and run as part of the same compose stack.
  • A couple of services exist for later, not for today: an in-browser code editor instance (future internal tooling, not part of the current workflow) and a self-hosted STUN/TURN server (wired in for future WebRTC NAT traversal, not load-bearing yet). We’d rather tell you a service isn’t pulling its weight yet than pretend it’s doing something it isn’t.

No database yet, on purpose

There’s deliberately no real database running today. Channel graphs, media pool entries, generated playlists, and other state all live as JSON and state files on disk. That was a simplicity choice made early, not an oversight — and it’s a choice we expect to revisit. The plan is a real database once user accounts and channel metadata need to be handled properly at scale, not before. Until then, a file on disk you can read directly beats a database abstraction you can’t inspect without a query.

Why this is worth publishing at all

Most vendors will tell you their platform is “built on modern, scalable infrastructure” and leave it there. We’d rather show you the actual services, the actual connections between them, and the actual limitations we haven’t solved yet — a database we know we need and haven’t built, services staged for later rather than pretended into the present. If you’re evaluating whether to build on top of this, that’s the diagram that actually tells you something.