# OmniRoute Plugin System: How the SDK and Marketplace Work Together

> Discover how the OmniRoute plugin system, SDK, and marketplace enable seamless router extensions. Develop and deploy add-ons without touching core code.

- Repository: [Diego Rodrigues de Sa e Souza/OmniRoute](https://github.com/diegosouzapw/OmniRoute)
- Tags: internals
- Published: 2026-08-04

---

**The OmniRoute plugin system combines a type-safe SDK, sandboxed execution environment, and RESTful marketplace API to let developers extend the router without modifying core code.**

The **OmniRoute** repository (`diegosouzapw/OmniRoute`) ships a production-grade plugin framework that isolates third-party extensions in worker threads while exposing a clean JavaScript SDK for hook registration. This article breaks down how the plugin SDK, manager, and marketplace API interact to load, secure, and execute custom logic.

## Core Components of the Plugin Architecture

The framework consists of three tightly-coupled layers:

- **Plugin SDK** ([`src/lib/plugins/sdk.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/plugins/sdk.ts)): Exports the `definePlugin()` factory and helper utilities (`blockRequest`, `modifyBody`, `addMetadata`) that authors use to declare hooks, permissions, and configuration schemas.
- **Plugin Manager & Registry** ([`src/lib/plugins/manager.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/plugins/manager.ts)): Orchestrates discovery, validation, sandboxing, and persistence. It scans the plugin directory (default `~/.omniroute/plugins/` or the path set in `OMNIROUTE_PLUGIN_PATH`), validates [`plugin.json`](https://github.com/diegosouzapw/OmniRoute/blob/main/plugin.json) manifests against the Zod schema in [`src/lib/plugins/manifest.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/plugins/manifest.ts), and stores state in the SQLite `plugins` table ([`src/lib/db/plugins.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/db/plugins.ts)).
- **Marketplace REST API** ([`docs/reference/API_REFERENCE.md`](https://github.com/diegosouzapw/OmniRoute/blob/main/docs/reference/API_REFERENCE.md)): Exposes endpoints under `/api/plugins/*` for listing, installing, upgrading, activating, and configuring plugins via HTTP.

## Plugin Discovery and Loading Process

When the server initializes, the `pluginManager.loadAll()` method triggers a strict lifecycle:

### 1. Discovery and Validation

The scanner ([`src/lib/plugins/scanner.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/plugins/scanner.ts)) walks the plugin directory and reads each [`plugin.json`](https://github.com/diegosouzapw/OmniRoute/blob/main/plugin.json). The manifest schema in [`src/lib/plugins/manifest.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/plugins/manifest.ts) validates required fields (name, version, main entry point) and optional hooks declarations.

### 2. Sandboxed Execution

Each plugin’s entry point (default [`index.js`](https://github.com/diegosouzapw/OmniRoute/blob/main/index.js)) runs inside a dedicated **worker thread** ([`src/lib/plugins/pluginWorker.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/plugins/pluginWorker.ts)). This isolates memory and I/O from the host process. If the environment variable `OMNIROUTE_PLUGINS_ALLOW_EXEC` is set to `1`, the worker may spawn a child process for privileged operations.

### 3. Hook Registration

The object returned by `definePlugin()` declares hooks such as `onRequest`, `onResponse`, `onError`, `onInstall`, `onActivate`, `onDeactivate`, and `onUninstall`. The manager registers these in a per-plugin registry and wires them into the request pipeline via [`open-sse/handlers/chatCore/pluginOnRequest.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/handlers/chatCore/pluginOnRequest.ts) and [`pluginOnResponse.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/pluginOnResponse.ts).

## Hook Execution and Request Pipeline

During runtime, the **OmniRoute plugin system** executes hooks in a specific order:

- **`onRequest`**: Fires when a request enters the router. Plugins can mutate the request, block it entirely, or add metadata.
- **`onResponse`**: Fires after the upstream response is received. Plugins can transform the body or emit analytics.
- **`onError`**: Invoked when the router encounters an error, allowing plugins to handle or log failures.
- **Lifecycle hooks** (`onInstall`, `onActivate`, `onDeactivate`, `onUninstall`): Run during state transitions triggered by the marketplace API.

Each hook is wrapped with a **per-plugin rate limiter** to prevent a misbehaving extension from degrading request latency.

## Marketplace API and Lifecycle Management

The marketplace is not a separate service but a set of REST endpoints that treat any URL or local path as a distribution source.

### Installation Flow

Clients POST to `/api/plugins/install` with either a local filesystem path or a remote tarball URL. The manager downloads the archive (if remote), verifies the SHA-256 hash to detect tampering, extracts it, and runs the same manifest validation used during local discovery.

### Version Management

The `upgrade` method compares semantic versions using an internal `compareSemver` utility. It refuses downgrades, ensuring only newer versions replace existing installations.

### Activation and Configuration

After installation, plugins can be auto-activated if `enabledByDefault` is true in the manifest, or manually via `POST /api/plugins/:name/activate`. Activation spins up the sandbox and registers hooks. Plugins exposing a `configSchema` in their manifest support dynamic configuration via `GET` and `PUT /api/plugins/:name/config`.

## Security and Isolation Controls

The framework implements defense-in-depth for third-party code:

- **SHA-256 Integrity**: Every plugin installed from a URL is hashed on download; subsequent loads verify the hash against the stored value.
- **Permission Model**: Plugins declare required capabilities (e.g., file-system access, network calls) in `manifest.requires.permissions`. The manager validates these against the host policy before activation.
- **Environment Overrides**: Set `OMNIROUTE_PLUGINS_ALLOW_EXEC=1` only when child-process privileges are required; otherwise, worker threads enforce strict isolation.

## Building a Plugin with the SDK

The following example creates a request-blocking plugin using the **OmniRoute plugin SDK**.

TypeScript source ([`src/plugins/example/plugin.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/plugins/example/plugin.ts)):

```typescript
import { definePlugin, blockRequest } from "omniroute/plugins/sdk";

export default definePlugin({
  name: "example",
  version: "1.0.0",
  description: "Blocks requests containing the word “spam”.",
  hooks: {
    onRequest: true,
  },
  async onRequest({ request }) {
    if (request.body?.includes("spam")) {
      // Use an SDK helper to abort the request with a 400 error.
      return blockRequest(400, "Spam content is not allowed");
    }
  },
});

```

Corresponding manifest ([`plugin.json`](https://github.com/diegosouzapw/OmniRoute/blob/main/plugin.json)):

```json
{
  "name": "example",
  "version": "1.0.0",
  "main": "plugin.ts",
  "hooks": { "onRequest": true },
  "requires": { "permissions": [] }
}

```

Deploy via CLI:

```bash
omniroute plugins install ~/.omniroute/plugins/example
omniroute plugins activate example

```

## Summary

- The **OmniRoute plugin SDK** in [`src/lib/plugins/sdk.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/plugins/sdk.ts) provides the `definePlugin()` factory and helper utilities for declaring hooks and schemas.
- The **Plugin Manager** ([`src/lib/plugins/manager.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/plugins/manager.ts)) handles discovery, validates manifests against [`src/lib/plugins/manifest.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/plugins/manifest.ts), and executes code in sandboxed worker threads ([`src/lib/plugins/pluginWorker.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/plugins/pluginWorker.ts)).
- The **marketplace API** exposes REST endpoints for install-by-URL flows, semantic versioning upgrades, and runtime activation.
- Hooks (`onRequest`, `onResponse`, `onError`, etc.) are wired into the core pipeline at [`open-sse/handlers/chatCore/pluginOnRequest.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/open-sse/handlers/chatCore/pluginOnRequest.ts) and [`pluginOnResponse.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/pluginOnResponse.ts).
- Security relies on SHA-256 verification, declarative permissions, and per-plugin rate limiting.

## Frequently Asked Questions

### How do I install a plugin from a remote URL in OmniRoute?

Post the URL to `POST /api/plugins/install`. The manager downloads the tarball, verifies its SHA-256 hash, validates the [`plugin.json`](https://github.com/diegosouzapw/OmniRoute/blob/main/plugin.json) manifest, and extracts it to the configured plugin directory.

### What hooks can an OmniRoute plugin implement?

Plugins can implement `onRequest`, `onResponse`, `onError`, `onInstall`, `onActivate`, `onDeactivate`, and `onUninstall`. These are declared in the `hooks` field of the object returned by `definePlugin()`.

### How does OmniRoute isolate plugins from the core system?

By default, plugins run inside worker threads ([`src/lib/plugins/pluginWorker.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/plugins/pluginWorker.ts)) that isolate memory and I/O. Only when `OMNIROUTE_PLUGINS_ALLOW_EXEC=1` is set can a plugin spawn a child process, and even then, the manager enforces permission checks and rate limits.

### Where does OmniRoute store plugin state and configuration?

Plugin metadata and state persist in the SQLite `plugins` table defined in [`src/lib/db/plugins.ts`](https://github.com/diegosouzapw/OmniRoute/blob/main/src/lib/db/plugins.ts). Runtime configuration is exposed via the REST API (`/api/plugins/:name/config`) and validated against the JSON schema declared in the plugin manifest.