The fastest path from zero to a working plugin is the Turborepo generator built into the monorepo. It creates the full package structure, registers the plugin as a workspace dependency inDocumentation 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.
apps/riven, and auto-formats the output — so you start with valid, compiling code and only need to fill in your integration logic.
Prerequisites
Before you start, make sure your environment meets these requirements:Node.js 24.15+
The monorepo uses Node.js 24 LTS. Install via
pnpm env use --global lts.pnpm 11.5+
All workspace operations use pnpm. Install from pnpm.io.
Riven monorepo
A cloned and bootstrapped checkout of
rivenmedia/riven-ts with dependencies installed.Bootstrap the monorepo
If you have not already set up the monorepo, install dependencies and generate any required API schemas first:You should also have Docker running so the supporting services (Redis, PostgreSQL) can be started:
Run the plugin generator
From the repository root, run the Turborepo generator for plugins:The generator prompts you for a plugin name in kebab-case form. For example, entering After confirming, the generator:
my-service produces @repo/plugin-my-service under packages/plugin-my-service/.- Creates the full plugin package under
packages/plugin-my-service/ - Adds
"@repo/plugin-my-service": "workspace:^"as a dependency in bothapps/rivenandapps/wiki - Runs Prettier over all generated files
Understand the generated structure
The generator produces a complete, compiling plugin with every layer pre-wired:Anything you add under
docs/plugins/my-service/ is automatically picked up by the wiki — no registration required.Define your plugin config
Open The Symbol value —
lib/my-service-plugin.config.ts. The generated file already reads the package name from package.json and wraps it in a Symbol — this is the canonical pattern used by every first-party plugin:"@repo/plugin-my-service" — is used as the BullMQ queue name prefix and as the GraphQL context injection key. Keep it stable across restarts by deriving it from the package name rather than creating an anonymous Symbol.Define your settings schema
Open Users configure each field through an environment variable of the form:
lib/my-service-settings.schema.ts. The generated file starts with an empty schema; add your fields here. Annotate every field with .describe() — the description is included in the generated settings documentation page.Here is a realistic example modelled after the TVDB plugin, which requires an API key:The environment variable prefix is derived from the package name:
@repo/plugin-my-service becomes REPO_PLUGIN_MY_SERVICE. Riven strips these variables from process.env before importing any plugin, so they are invisible to all other plugins.Implement your DataSource
Open
lib/datasource/my-service.datasource.ts. The generated class extends BaseDataSource and provides a validate stub. Fill in baseURL, rateLimiterOptions, and any authentication logic in willSendRequest, then add your API methods.The example below is modelled after the production TVDB datasource, which injects a bearer token on every request:Wire up the GraphQL resolver
The generated resolver in The settings resolver extends the global
lib/schema/my-service.resolver.ts already uses @PluginDataSource() to inject the DataSource. This pattern is taken directly from the Comet plugin:Settings GraphQL type so clients can query available setting keys:Assemble the plugin manifest
Open
lib/index.ts. The generated file exports a plugin constant that combines everything. Add event hooks relevant to your plugin type. This example adds a scrape hook — following the same pattern as plugin-comet:The
hooks object may be empty ({}) — you are not required to subscribe to any events. The resolvers array must contain at least one entry; the generated resolver satisfies this requirement out of the box.Add the plugin to apps/riven
The generator already added After adding or modifying the dependency, regenerate the lockfile:
"@repo/plugin-my-service": "workspace:^" to apps/riven/package.json and apps/wiki/package.json. You do not need to edit those files manually.If for some reason you created the package by hand, add the dependency yourself:Run the test suite
The generator also creates a Or watch for changes:To run the full monorepo test suite (requires a local
validate.spec.ts test in lib/datasource/__tests__/ using the @repo/util-plugin-testing context helpers and MSW for mocking HTTP responses. Run only your plugin’s tests during development:redis-server):Start Riven in development mode
With services running and your Check for startup logs confirming your plugin was registered and validated. If the validator returns
.env.riven file configured, start the application:false, the plugin appears in the logs as invalid — it remains loaded but receives no events. Use the Bull Board dashboard at http://localhost:4000 to inspect queue activity.Real-world plugin examples
Two first-party plugins make good reference implementations for different plugin roles:plugin-comet
A scraper plugin. Implements
riven.media-item.scrape.requested to query the Comet torrent indexer API and return a map of info-hash → stream-title strings. No API key required; only a configurable base URL via CometSettings.plugin-tvdb
A metadata provider plugin. Implements
riven.media-item.index.requested.show to fetch full series metadata from TVDB v4 (with automatic JWT token refresh) and episode data from TVMaze for timezone information. Uses two DataSources in a single plugin.Comet plugin config (scraper)
TVDB plugin config (metadata provider with two DataSources)
Next steps
Building a DataSource
Rate limiting, caching options, willSendRequest patterns, and error handling in depth
GraphQL Resolvers
Query and mutation resolvers, PluginContext injection, and settings resolvers
Event Hooks
Full event catalogue, handler signatures, and return type requirements
Settings
Zod schema patterns, environment variable naming, and the generated docs pipeline