The Beacon Stack
TV, movies, and BitTorrent — each app works standalone, and all
three share indexers, quality profiles, and settings through Pulse
when you want them to. One docker compose up
for the whole stack.
The stack
Four apps, one config layer
Pilot, Prism, and Haul each run independently with local configuration. Pulse is the optional hub that makes all three share a single indexer list, one set of quality profiles, and a common settings namespace.
Pulse
control plane
Service registry and shared configuration layer. Beacon apps register here,
but so can anything else — the Go SDK at pkg/sdk lets any
service tap into shared indexers, profiles, and settings. Optional for a
single install, essential the moment you run two.
Pilot
TV series managerTracks a TV library via TMDB, polls indexers for new episodes, grabs through your download client, and files completed downloads into season folders. Dead-torrent detection and one-click Sonarr import included.
:8383 Deep dive →Prism
Movie collection managerEdition-aware release scoring, full release decision explainability, and a native Radarr v3 API so Overseerr and Jellyseerr connect without modification. Import from a running Radarr instance in one pass.
:8282 Deep dive →Haul
BitTorrent clientBuilt on anacrolix/torrent. Accurate ETAs from exponential moving averages, three-level stall classification, and rename-on-complete using media metadata passed by Pilot or Prism. VPN-aware dashboard.
:8484 Deep dive →How it connects
How Pulse connects everything
Each Beacon service registers with Pulse on startup and heartbeats periodically. Shared state — indexers, quality profiles, settings — flows outward from Pulse to every subscribed service. If you move Pilot to a new host, every other service picks up the new address on the next heartbeat.
Pulse isn't Beacon-only. Any Go service can join through the client SDK
at pkg/sdk — register, discover peers, pull assigned indexers,
and subscribe to shared config with one client instance.
Registration
Register once, configured automatically
When Pilot starts, it registers with Pulse: name, URL, capabilities. Pulse
returns a tailored config bundle — only the indexers tagged tv,
the quality profiles this service subscribes to, and the shared settings
relevant to its namespace.
Add a new indexer to Pulse and tag it movies. Prism picks it up on
the next heartbeat. Tag it tv too and Pilot gets it. One API key to
update, not three.
{
"name": "pilot",
"type": "media-manager",
"url": "http://pilot:8383",
"capabilities": [
"supports_torrent",
"supports_usenet"
]
} {
"indexers": [
{ "name": "NZBgeek", "tags": ["tv"] },
{ "name": "Prowlarr", "tags": ["tv"] }
],
"quality_profile": {
"name": "HD-1080p",
"managed_by_pulse": true
},
"config": {
"media.library_root": "/tv",
"download.preferred_protocol": "torrent"
}
} Architecture
One stack, four apps
Why it exists
Built for people who run their own stack
Register once, configure everywhere
Add an indexer to Pulse, tag it, and every subscribed service picks it up. One API key, one quality profile, one place to change things when they break.
Standalone or connected
Pilot, Prism, and Haul each run fine with local config. Pulse is purely additive — bring it in when you're ready for the shared layer.
Zero telemetry, MIT licensed
No analytics, no crash reporting, no update checks. Everything runs in your network and stays there. All four apps are MIT licensed and built in the open.
Getting started
Up in three commands
The full stack — Postgres, Pulse, Pilot, Prism, Haul, and a VPN container
— lives in beacon-stack/stack. Point it at your media
directories and run.
# Clone and configure $ git clone https://github.com/beacon-stack/stack $ cp .env.example .env # Set MEDIA_DIR, TV_DIR, MOVIES_DIR in .env # Start everything $ docker compose up -d