Riven is built around a single-process Python application that coordinates every stage of media automation — from discovering what to fetch, through to presenting finished files inside your media server. Understanding how these stages connect helps you configure Riven correctly, diagnose problems faster, and extend behaviour when needed.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/rivenmedia/riven/llms.txt
Use this file to discover all available pages before exploring further.
The Pipeline at a Glance
Every media item travels through the same ordered set of stages. Each stage is handled by a dedicated service; theEventManager moves items between them by dispatching events on a thread-safe queue.
| Stage | Service class | What it does |
|---|---|---|
| Content sources | Overseerr, PlexWatchlist, Listrr, Mdblist, TraktContent | Poll external services for new requests; emit Requested items |
| Indexer | IndexerService | Fetch title metadata (TMDB / TVDB) and advance item to Indexed |
| Scraper | Scraping | Query configured scraper backends; score torrents with RTN; advance to Scraped |
| Downloader | Downloader | Send the winning torrent to the debrid provider; advance to Downloaded |
| Filesystem | FilesystemService → RivenVFS | Register the debrid stream in the FUSE virtual filesystem; advance to Symlinked |
| Updater | Updater | Trigger a library scan in Plex / Jellyfin / Emby; advance to Completed |
| Post-processing | PostProcessing | Run any configured post-processing tasks after Completed |
| Notifications | NotificationService | Deliver Apprise / SSE alerts at configured state transitions |
“Symlinked” is the historical state name for the filesystem step. In practice, no symlinks are created — items are registered in RivenVFS and streamed on demand.
The Program Class
Program (defined in program/program.py) is a threading.Thread subclass and the single owner of all runtime state.
Program.start():
- Registers settings observers so services are re-initialised whenever the settings file changes.
- Creates the data directory and writes default settings if they are missing.
- Bootstraps all external API clients (
bootstrap_apis()). - Validates the database connection; creates the database if it does not exist yet.
- Runs Alembic database migrations (
run_migrations()). - Instantiates all services into the
Servicesdataclass. - Starts the APScheduler background scheduler.
- Calls
super().start()to launch the main event loop thread.
Services Dataclass
All services are held together in a typed dataclass so that any part of the codebase can retrieve them via dependency injection:enabled and initialized properties. The pipeline only routes items to a service when both are True. If no content service is initialised at startup, Riven logs a warning and waits for items to be added manually via the API.
Event-Driven Processing
The main loop (Program.run()) dequeues one event at a time and calls process_event() from state_transition.py:
process_event inspects item.last_state and returns the next Service plus the list of items to forward. State transitions are deterministic:
Paused or Failed states are skipped entirely until they are explicitly retried or unpaused through the API.
Database
Riven uses PostgreSQL as its primary store. The ORM layer is SQLAlchemy with declarative models; schema evolution is managed by Alembic migrations that run automatically on every start. Key tables includeMediaItem (and its polymorphic sub-tables Movie, Show, Season, Episode), FilesystemEntry, Stream, and StreamRelation.
Scheduler
ProgramScheduler wraps APScheduler and registers two categories of jobs:
- Content polling — each enabled content service is polled on a configurable interval (default: every 30 minutes) to discover new requests.
- Retry / maintenance — stale
Scraped,Downloaded, andIndexeditems are periodically re-queued; the schedule backs off exponentially after repeated failures (configurable viascraping.after_2,after_5,after_10).
REST API
Riven exposes a FastAPI application on port 8080. All routes are grouped under/api/v1 and require an API key passed as the X-API-Key header (or apikey query parameter).
http://localhost:8080/docs.
Notifications
Riven ships two notification channels:- Apprise — delivers alerts to any service supported by the Apprise library (Slack, Telegram, Discord, email, etc.) when configured via
notifications.apprise_url. - Server-Sent Events (SSE) — the frontend (and any connected client) subscribes to
GET /api/v1/eventsto receive real-time state change notifications without polling.