# How to Develop Plugins for Motrix Using the Plugin SDK: A Complete Guide

> Learn to develop Motrix plugins using the official Plugin SDK. This guide covers ES2020 modules, QuickJS sandboxing, and packaging with the SDK toolchain for powerful extensions.

- Repository: [Dr_rOot/Motrix](https://github.com/agalwood/Motrix)
- Tags: how-to-guide
- Published: 2026-08-19

---

**Motrix treats plugins as self-contained ES2020 modules that run inside a QuickJS sandbox, orchestrated by the Plugin Registry, Plugin Host, and Capability Bridge, which you can build and package using the official `@motrix/plugin-sdk` toolchain.**

Motrix is a full-featured, open-source download manager that supports secure extensibility through a sandboxed plugin system. To develop plugins for Motrix using the Plugin SDK, you create isolated modules that interact with the application via a controlled Runtime API rather than direct system access. This guide examines the internal architecture, lifecycle hooks, and development workflow based on the actual implementation in the `agalwood/Motrix` repository.

## Understanding the Motrix Plugin Architecture

The plugin system relies on three coordinated components that manage discovery, execution, and API bridging.

### The Three Core Components

**Plugin Registry** — Located in [`src/core/plugin/plugin-registry.ts`](https://github.com/agalwood/Motrix/blob/main/src/core/plugin/plugin-registry.ts), this component scans the `plugins/` (community) and `builtin-plugins/` (built-in) directories, parses each [`motrix-plugin.json`](https://github.com/agalwood/Motrix/blob/main/motrix-plugin.json) manifest, resolves internationalization strings, and maintains an in-memory index of available plugins via the `byId` map.

**Plugin Host** — Implemented in [`src/core/plugin/host/plugin-host.ts`](https://github.com/agalwood/Motrix/blob/main/src/core/plugin/host/plugin-host.ts), this manages worker threads for active plugins, validates permissions through `ffmpegSatisfies` checks, verifies built-in signatures via `verifyBuiltinSignature`, and instantiates the sandbox environment.

**Capability Bridge** — Embedded within the Plugin Host, this injects the `motrix` runtime object into the QuickJS sandbox, forwarding hook invocations and locale changes between the main process and the isolated plugin context.

## Plugin Structure and Manifest Requirements

Every Motrix plugin follows a strict package layout to ensure the registry can parse and load it correctly.

### File Structure

```

my-plugin/
├─ src/                # TypeScript/JavaScript source

│   └─ index.ts
├─ motrix-plugin.json # Manifest (required)

└─ package.json

```

### The motrix-plugin.json Manifest

The registry loads this file using `parseManifest` from [`src/core/plugin/manifest/parse.ts`](https://github.com/agalwood/Motrix/blob/main/src/core/plugin/manifest/parse.ts). It declares the plugin's `id`, `version`, `permissions`, `activationEvents`, and the `main` entry point. Internationalization placeholders are resolved at load time by `resolveManifestI18n` in [`src/core/plugin/manifest/i18n-resolve.ts`](https://github.com/agalwood/Motrix/blob/main/src/core/plugin/manifest/i18n-resolve.ts), which merges locale JSON files from the `l10n/` directory into the manifest.

### Sandbox Constraints

Plugins operate without Node.js built-ins and cannot access the filesystem or network directly. All external interactions must be declared as **capabilities** (e.g., `ffmpeg`, `storage`, `network`) and are gated by the host before activation.

## Development Workflow with the Plugin SDK

The official SDK provides a complete toolchain for scaffolding, building, and packaging extensions.

### Creating a Scaffold

Run the following command to generate a starter repository with a valid TypeScript configuration and sample hook:

```bash
npx create-motrix-plugin my-plugin

```

### Development and Build Process

Use the watch mode to compile changes into a single ES2020 bundle:

```bash
npm run dev

# or

pnpm dev

```

This outputs to [`dist/plugin.js`](https://github.com/agalwood/Motrix/blob/main/dist/plugin.js), which the SDK validates against the `@motrix/plugin-manifest-schema`.

### Packaging and Installation

Package your plugin into a signed `.moext` file (a tarball) for distribution:

```bash
pnpm exec motrix-plugin pack

```

Install the resulting `plugin-name-version.moext` through the Motrix UI via *Plugins* → *Install from file*, or drop it into the `plugins/` directory manually. The `PluginRegistry` will detect it on the next discovery scan.

## How Motrix Loads and Activates Plugins

The lifecycle from disk to execution follows a strict sequence defined in the core source files.

### Discovery and Registration

When Motrix starts, `PluginRegistry.performDiscover()` reads every subdirectory under the user data `plugins/` folder. It invokes `parseManifest` on each [`motrix-plugin.json`](https://github.com/agalwood/Motrix/blob/main/motrix-plugin.json) and populates the registry's internal index.

### Internationalization Resolution

The registry calls `loadLocale` to fetch locale JSON files, then uses `resolveManifestI18n` to merge translations into the manifest object for display in the UI.

### Activation and Worker Initialization

When a plugin is enabled, `PluginHost.activate(id)` executes the following steps:

1. **Validation**: Checks that the plugin is enabled and that the active plugin cap is not exceeded.
2. **Capability Check**: Performs optional `ffmpeg` version validation via `ffmpegSatisfies`.
3. **Bundle Loading**: Reads `bundle.moext` for built-ins or [`dist/plugin.js`](https://github.com/agalwood/Motrix/blob/main/dist/plugin.js) for community plugins, verifying signatures for built-in packages.
4. **Bridge Creation**: Instantiates a `CapabilityBridge`, passing the sandbox the manifest, bundle source, and effective permissions calculated as `required ∪ (optional ∩ granted)`.

### Hook Invocation and Lifecycle Management

Plugins register hooks via `motrix.hooks.register('resolve', handler)`. When Motrix needs to trigger a hook, it calls `PluginHost.invokeHook(id, hookName, …)`, which marshals data into the worker thread and returns the result.

If a plugin remains idle for `idleDisposeMs` (default **5 minutes**), or during application shutdown, `PluginHost.deactivate(id)` tears down the worker, executes any `onDeactivate` handlers with a strict time budget, and updates the `PluginStateStore`.

## Understanding Permissions and Grants

The permission system distinguishes between mandatory and optional capabilities.

### Required vs. Optional Permissions

**Required permissions** listed in the `permissions` array of [`motrix-plugin.json`](https://github.com/agalwood/Motrix/blob/main/motrix-plugin.json) are always granted at activation. **Optional permissions** declared under `optionalPermissions` require explicit user approval and are managed by an optional `GrantsManager` wired into `PluginHost` via `opts.pluginGrants`.

### The Grants System

The host resolves the effective permission set by intersecting user grants with optional declarations before creating the bridge. This ensures the sandbox cannot exceed its granted capabilities regardless of the code it executes.

## Practical Example: Creating a URL Resolver Plugin

The following example demonstrates a plugin that modifies download URLs before the engine processes them.

### Plugin Source

```typescript
// src/index.ts
import { motrix } from '@motrix/plugin-api'

motrix.hooks.register('resolve', async (ctx) => {
  if (ctx.url.startsWith('http')) {
    const url = new URL(ctx.url)
    url.searchParams.set('utm_source', 'motrix')
    return { url: url.toString() }
  }
  return undefined
})

```

### Manifest Configuration

```json
// motrix-plugin.json
{
  "id": "example-resolver",
  "name": "Example URL Resolver",
  "version": "1.0.0",
  "description": "Adds a utm_source query param to HTTP URLs.",
  "main": "dist/plugin.js",
  "permissions": [],
  "optionalPermissions": [],
  "contributes": {
    "hooks": {
      "resolve": {
        "role": "resolve",
        "description": "Modify URLs before download starts."
      }
    }
  },
  "engines": {
    "motrix": ">=2.5.0"
  }
}

```

Run `pnpm exec motrix-plugin pack` to generate `example-resolver-1.0.0.moext`, then install it through the Motrix Plugins UI.

## Key Implementation Files in the Codebase

| Path | Purpose |
|------|---------|
| [`src/core/plugin/plugin-registry.ts`](https://github.com/agalwood/Motrix/blob/main/src/core/plugin/plugin-registry.ts) | Scans directories, parses manifests, and maintains the plugin index. |
| [`src/core/plugin/host/plugin-host.ts`](https://github.com/agalwood/Motrix/blob/main/src/core/plugin/host/plugin-host.ts) | Manages worker lifecycles, permission gating, and bridge instantiation. |
| [`src/core/plugin/manifest/parse.ts`](https://github.com/agalwood/Motrix/blob/main/src/core/plugin/manifest/parse.ts) | Validates and normalizes [`motrix-plugin.json`](https://github.com/agalwood/Motrix/blob/main/motrix-plugin.json) against the schema. |
| [`src/core/plugin/manifest/i18n-resolve.ts`](https://github.com/agalwood/Motrix/blob/main/src/core/plugin/manifest/i18n-resolve.ts) | Merges locale files into manifests for UI display. |
| [`src/shared/types/plugin.ts`](https://github.com/agalwood/Motrix/blob/main/src/shared/types/plugin.ts) | Contains TypeScript definitions for manifests, capabilities, and runtime state. |
| `src/renderer/routes/plugins/*` | React components for the plugin management UI. |
| `tests/fixtures/plugins/*/dist/plugin.js` | Example compiled bundles used in the test suite. |

## Summary

- Motrix plugins are **ES2020 modules** executed inside a **QuickJS sandbox** with no direct Node.js or filesystem access.
- The **Plugin Registry** ([`src/core/plugin/plugin-registry.ts`](https://github.com/agalwood/Motrix/blob/main/src/core/plugin/plugin-registry.ts)) handles discovery and manifest parsing, while the **Plugin Host** ([`src/core/plugin/host/plugin-host.ts`](https://github.com/agalwood/Motrix/blob/main/src/core/plugin/host/plugin-host.ts)) manages worker threads and permissions.
- All external interactions require explicit **capabilities** declared in [`motrix-plugin.json`](https://github.com/agalwood/Motrix/blob/main/motrix-plugin.json) and enforced by the **Capability Bridge**.
- Use `npx create-motrix-plugin` to scaffold, `npm run dev` to build, and `motrix-plugin pack` to generate distributable `.moext` files.
- Plugins communicate with Motrix via **hooks** registered through `@motrix/plugin-api`, invoked through `PluginHost.invokeHook()`.

## Frequently Asked Questions

### What is the QuickJS sandbox in Motrix?

The QuickJS sandbox is an isolated JavaScript runtime environment that executes plugin code without access to Node.js built-ins or the host filesystem. According to the `agalwood/Motrix` source code, the Capability Bridge injects a controlled `motrix` object into this sandbox, allowing plugins to interact with the application only through approved API methods like `motrix.hooks.register` and `motrix.storage`.

### How does Motrix handle plugin permissions?

Motrix enforces a two-tier permission system: **required** permissions (always granted) and **optional** permissions (requiring user approval). The Plugin Host calculates the effective permission set as the union of required permissions and the intersection of optional permissions with user grants before activating the plugin, ensuring the sandbox cannot access unapproved capabilities.

### Can Motrix plugins access the network directly?

No. Plugins cannot access the network, filesystem, or Node.js APIs directly. Any network activity must be requested through the `network` capability in [`motrix-plugin.json`](https://github.com/agalwood/Motrix/blob/main/motrix-plugin.json) and executed via the Runtime API methods exposed by the Capability Bridge, which the Plugin Host validates against the granted permissions before allowing the operation.

### What is the `.moext` file format?

The `.moext` file is a signed tarball containing the compiled plugin bundle ([`dist/plugin.js`](https://github.com/agalwood/Motrix/blob/main/dist/plugin.js)) and its assets. When you run `motrix-plugin pack`, the SDK creates this file, which Motrix installs by copying into the `plugins/` directory and verifying signatures for built-in packages via `verifyBuiltinSignature` in the Plugin Host.