I built a BitTorrent engine that streams to Plex without downloading
Two years ago I got tired of the same problem every self-hoster hits: you want to watch something, the file is not on your NAS, and your options are either wait for a download or pay for a debrid service that does the waiting for you.
I wanted the opposite. I wanted a library that exists without existing. Plex opens a file, and the bytes come from a BitTorrent swarm in real time, as if the file had always been there.
That project is now Tiramisu (formerly GoStream). This is the story of how it works and what it took to make it reliable enough for production use at home.
The core trick: a FUSE filesystem where files are a lie
The whole thing rests on a FUSE virtual filesystem. Tiramisu mounts a directory that looks completely normal: thousands of .mkv files, each with the correct size, each seekable. Plex scans it, builds its library, plays anything.
But none of those files exist on disk. There are no temp files, no partial downloads, no storage quota.
When a player reads a byte at offset X, the filesystem goes to the BitTorrent engine and asks for the piece that contains that byte. The engine fetches it from peers, verifies it, and hands it over. The player never knows the difference.
This is the hard part of streaming torrents: BitTorrent does not work well for sequential reading. Pieces arrive out of order, peers are flaky, and a single missing piece stalls the whole stream. So the engine is a deep fork of anacrolix/torrent and TorrServer with a lot of surgery done on top.
The problems nobody tells you about
Here are three bugs that took real effort to find and fix. All of them only show up under real playback conditions.
The audio desync on resume
Resuming playback from a position beyond 256 MB triggered what we called a pump jump: the read-ahead pump skips ahead to the new position. An early version of that code invalidated the entire read-ahead cache. Every chunk was discarded, FUSE reads fell back to network fetches, and each fetch could take 1 to 8 seconds. On an Apple TV with a DTS-HD track, that gap was enough for the audio to drift out of sync and stay there.
The fix was realizing the cache bytes are deterministic. Same torrent hash, same offset, same bytes, always. Invalidating them was pure waste. Removing the invalidation made resumes instant and audio stayed locked.
The deadlock that only appeared at shutdown
The native reader uses a pipe. The player reads from one end, the engine writes to the other. Closing it was done while holding the mutex. If a read was in flight, it held the same mutex while blocked waiting for pipe data. Close would then wait on the mutex forever. Deadlock, permanent, and only under specific cleanup paths.
The fix is one of those things that looks trivial in hindsight: close the pipe writer before taking the lock. The blocked reader gets EOF, releases the mutex, and close proceeds. Textbook lock ordering, but it took a hung stream to find it.
Peers that poison the swarm
A peer that sends corrupted pieces does not just waste bandwidth. Every corrupted piece triggers verification, then a ban. With in-memory bans, a restart wiped the list, and the same hostile peer got banned again and again. We measured one IP re-banned eight times in six days.
The fix was persistent bans with a 30 day TTL and a per-IP counter, stored in the state database. The system now distinguishes between a peer that failed once and a peer that keeps failing.
Making the swarm behave
Streaming from public trackers means dealing with a swarm that does not care about your playback. The engine runs an adaptive tuning loop that watches system load, torrent speed, and buffer fill, then adjusts connection limits and peer timeouts. On a Raspberry Pi it is the difference between a stream that survives and one that thrashes.
There is also an adaptive shield that detects corrupted pieces during streaming and escalates to strict mode when needed. In the first three weeks after enabling it, it caught almost 700 corruptions and escalated over 300 times. The swarm is a hostile place.
The result
Tiramisu runs as a single Go binary on a Raspberry Pi 4, exposing a FUSE mount to Plex, Jellyfin, or anything else that can read a directory. It integrates with Prowlarr and Torrentio for finding content, has a web dashboard with live metrics, and handles 4K streams on hardware that has no business doing so.
The project is open source on GitHub, GPL-2.0, with Docker images for x86_64 and arm64. It is also listed on awesome-selfhosted.
If you run Plex or Jellyfin and you are tired of waiting for downloads, give it a look. The files may be imaginary, but the streams are real.
Top comments (0)