Riven is a self-hosted media automation framework built as a TypeScript monorepo. At its core, every media item travels through a deterministic six-stage pipeline orchestrated by XState state machines, processed by BullMQ job queues, and surfaced through an Apollo GraphQL API backed by MikroORM and PostgreSQL. A FUSE virtual file system bridges the debrid layer to your media server without ever copying a file to disk.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/rivenmedia/riven-ts/llms.txt
Use this file to discover all available pages before exploring further.
The Six-Stage Pipeline
Each content request follows the same linear path from discovery to playback. Every stage is independently retryable—a failure at scraping never discards the indexing work already done.Content Request
An external content service (Overseerr, MDBList, Listrr) submits a media
request. Riven creates an
ItemRequest record in PostgreSQL and fires
riven.item-request.create.success into the event bus. The Main Runner
immediately queues the item for indexing.Indexing
The
process-item-request flow fetches rich metadata from TMDB and TVDB,
creating Movie, Show, Season, and Episode entities. Unreleased
items are scheduled for periodic re-indexing; partially released shows are
both scraped and re-indexed for future episodes.Scraping
The
scrape-item flow fans the request out to every enabled scraper plugin
(Torrentio, Comet, StremThru, …). Raw results land in the
scrape-item.parse-scrape-results sandboxed worker queue where they are
parsed and ranked in an isolated worker thread. The best streams are
persisted as Stream entities.Downloading
The
download-item flow works through the ranked stream list via
download-item.rank-streams and download-item.find-valid-torrent,
checking debrid cache availability and triggering a download if needed.
download-item.map-items-to-files and
download-item.validate-torrent-files run in sandboxed worker threads to
map torrent file trees to media item entities.VFS Mount
Once a valid stream link exists, the FUSE virtual file system maps it to
a path under
settings.vfsMountPath. The VFS exposes the file to the OS
without materialising any data locally—reads are served on-demand from
the debrid provider with chunk caching for performance.Core Components
XState v5 State Machines
The entire application lifecycle is driven by XState actors. The
Bootstrap machine initialises infrastructure in strict sequential order.
The Main Runner machine receives every
RivenEvent and dispatches it to
the correct BullMQ flow. The Plugin Registrar machine discovers,
validates, and wires up plugins before the runner starts.BullMQ + Redis Job Queues
All CPU-bound and I/O-bound work is processed asynchronously through
BullMQ. Flow queues handle multi-step pipelines with parent→child job trees.
Sandboxed queues run CPU-heavy tasks (
parse-scrape-results,
map-items-to-files, validate-torrent-files) in isolated worker threads.
Redis provides durability so jobs survive restarts.Apollo GraphQL API
Riven exposes a type-safe GraphQL API built with Apollo Server and
type-graphql. It defaults to http://localhost:3000/graphql and supports
queries for media items, episodes, and the VFS, plus mutations for managing
item requests, resetting items, and sharing diagnostic logs.MikroORM + PostgreSQL
All persistent state lives in PostgreSQL. Core entities include
MediaItem
(polymorphic base for Movie, Show, Season, Episode), ItemRequest,
Stream, and MediaEntry (VFS file mappings). MikroORM provides
identity-map caching, lazy reference loading, and repository-per-entity
query builders.FUSE Virtual File System
The VFS uses
@zkochan/fuse-native to mount a virtual directory. It
requires a /dev/fuse device and the SYS_ADMIN Linux capability (or the
equivalent Docker privilege). The Bootstrap machine mounts the VFS after the
GraphQL server is ready. All VFS entries can be queried through the
vfsEntry, vfsEntryStat, and vfsDirectoryEntryPaths GraphQL queries.Plugin System
Plugins are self-contained npm packages that contribute
DataSources,
GraphQL Resolvers, event Hooks, a Zod SettingsSchema, and a
validator function. Each plugin gets its own BullMQ queue per event type
it subscribes to, named ${eventType}.plugin[${pluginName}].Plugin System
Plugins integrate with Riven at every stage of the pipeline. The framework discovers all packages that export a Riven plugin manifest, validates each one via itsvalidator() function, and registers only those that pass.
| Component | Purpose |
|---|---|
| DataSources | HTTP API clients with built-in rate limiting, caching, and retry logic (extends BaseDataSource) |
| Resolvers | type-graphql resolver classes that are merged into the Apollo schema at startup |
| Hooks | Typed event handlers registered against specific RivenEvent types |
| Settings Schema | Zod schema validated at startup; invalid configuration disables the plugin gracefully |
| Validator | Async function called during bootstrap to confirm the plugin is ready (credentials reachable, etc.) |
Plugin Lifecycle
Validation
Each plugin’s
validator() is called. Failures are collected as
invalidPlugins — they do not crash Riven but are logged as warnings.Registration
Valid plugins register their GraphQL resolvers, DataSources, and event
hooks. BullMQ queues and workers are created per subscribed event type.
If a plugin fails validation, Riven still starts but logs a warning. Check
the startup logs to identify which plugins are disabled and why.