OmniRoute Plugin System: How the SDK and Marketplace Work Together
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): Exports thedefinePlugin()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): Orchestrates discovery, validation, sandboxing, and persistence. It scans the plugin directory (default~/.omniroute/plugins/or the path set inOMNIROUTE_PLUGIN_PATH), validatesplugin.jsonmanifests against the Zod schema insrc/lib/plugins/manifest.ts, and stores state in the SQLitepluginstable (src/lib/db/plugins.ts). - Marketplace REST API (
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) walks the plugin directory and reads each plugin.json. The manifest schema in 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) runs inside a dedicated worker thread (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 and 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=1only 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):
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):
{
"name": "example",
"version": "1.0.0",
"main": "plugin.ts",
"hooks": { "onRequest": true },
"requires": { "permissions": [] }
}
Deploy via CLI:
omniroute plugins install ~/.omniroute/plugins/example
omniroute plugins activate example
Summary
- The OmniRoute plugin SDK in
src/lib/plugins/sdk.tsprovides thedefinePlugin()factory and helper utilities for declaring hooks and schemas. - The Plugin Manager (
src/lib/plugins/manager.ts) handles discovery, validates manifests againstsrc/lib/plugins/manifest.ts, and executes code in sandboxed worker threads (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 atopen-sse/handlers/chatCore/pluginOnRequest.tsandpluginOnResponse.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 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) 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. Runtime configuration is exposed via the REST API (/api/plugins/:name/config) and validated against the JSON schema declared in the plugin manifest.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →