Skip to main content

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.

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.

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.
1

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.
2

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.
3

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.
4

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.
5

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.
6

Media Server

Plex, Jellyfin, or any other media server that watches the VFS mount path picks up the new file automatically. The request-stream-link and post-process-media-item flows handle health-checking the stream URL and requesting subtitles after playback is confirmed.

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 its validator() function, and registers only those that pass.
ComponentPurpose
DataSourcesHTTP API clients with built-in rate limiting, caching, and retry logic (extends BaseDataSource)
Resolverstype-graphql resolver classes that are merged into the Apollo schema at startup
HooksTyped event handlers registered against specific RivenEvent types
Settings SchemaZod schema validated at startup; invalid configuration disables the plugin gracefully
ValidatorAsync function called during bootstrap to confirm the plugin is ready (credentials reachable, etc.)

Plugin Lifecycle

1

Discovery

The Plugin Registrar machine scans all installed packages for Riven plugin manifests.
2

Validation

Each plugin’s validator() is called. Failures are collected as invalidPlugins — they do not crash Riven but are logged as warnings.
3

Registration

Valid plugins register their GraphQL resolvers, DataSources, and event hooks. BullMQ queues and workers are created per subscribed event type.
4

Running

Plugin workers consume events from their queues and can emit new events back into the Main Runner via sendEvent.
If a plugin fails validation, Riven still starts but logs a warning. Check the startup logs to identify which plugins are disabled and why.

Monorepo Structure

riven-ts/
├── apps/
│   ├── riven/           # Core application (state machines, queues, GraphQL, VFS)
│   └── wiki/            # Documentation site (this site)
├── packages/
│   ├── core/            # Shared infrastructure: database, logging, config
│   ├── plugin-*/        # First-party plugins (Torrentio, Plex, Overseerr, …)
│   ├── util-plugin-sdk/ # Public SDK: DTOs, event schemas, BaseDataSource, GraphQLContext
│   ├── util-plugin-testing/    # Vitest helpers and mock factories for plugin tests
│   ├── util-rank-torrent-name/ # Standalone torrent name ranking library
│   └── feature-settings/       # Settings management and validation
Plugin packages follow the packages/plugin-* convention. When you install a new plugin, the Plugin Registrar picks it up automatically on the next startup—no code changes to the core are required.

Build docs developers (and LLMs) love