Plugin discovery
Plugins live in theplugins/ directory at the root of the gateway repository. Each plugin occupies its own subdirectory:
plugins_enabled array in conf.json:
conf.json
npm run build-plugins) reads conf.json, iterates over each enabled plugin directory, imports its manifest.json, and auto-generates plugins/index.ts. That generated file exports a plugins object keyed by plugin ID, which the gateway imports at startup.
The
default plugin ships with the gateway and is almost always included in plugins_enabled. It provides built-in guardrails such as regexMatch, contains, wordCount, and webhook.Plugin structure
Every plugin directory must contain:| File | Purpose |
|---|---|
manifest.json | Declares the plugin’s ID, name, credentials schema, and available functions |
<functionId>.ts | One TypeScript file per function declared in the manifest |
*.test.ts | (Recommended) Jest test file for the plugin |
handler that conforms to the PluginHandler type.
TypeScript interfaces
All types are defined inplugins/types.ts:
plugins/types.ts
PluginContext
PluginContext carries the live request and response data your handler can inspect. Key properties:
context.request.json— The parsed request body sent to the LLM provider.context.response.json— The parsed response body from the LLM provider (available inafterRequestHookonly).context.requestType— One ofchatComplete,complete,embed, ormessages.context.provider— The target LLM provider (e.g.openai,anthropic).context.metadata— Arbitrary key-value metadata forwarded with the request.
PluginHandlerResponse
Handlers must resolve to aPluginHandlerResponse:
| Field | Type | Description |
|---|---|---|
error | any | null on success, or the caught error object |
verdict | boolean | true = check passed, false = check failed |
data | any | Arbitrary diagnostic data returned to the caller |
transformedData | any | Modified request/response payload (transformer plugins only) |
transformed | boolean | Set to true when transformedData carries changes |
Hook types
Two hook points are supported:beforeRequestHook
Executed before the gateway forwards the request to the LLM provider. Use this hook to:- Validate or reject incoming prompts.
- Enforce model allowlists.
- Mutate the request payload before it reaches the model.
beforeRequestHook guardrail returns verdict: false, the gateway can short-circuit the request entirely and return an error to the caller without ever contacting the LLM.
afterRequestHook
Executed after the LLM response is received but before it is returned to the client. Use this hook to:- Validate LLM output against a schema or content policy.
- Detect PII or sensitive data in responses.
- Mutate or redact response content before delivery.
afterRequestHook guardrail returns verdict: false, the gateway can return the response with HTTP status 246 to signal that guardrails failed.
Function types
guardrail
Aguardrail function evaluates content and returns a boolean verdict. The verdict determines whether the request or response is allowed to proceed. Guardrails are the most common plugin type.
transformer
Atransformer function mutates the request or response payload. It returns transformed: true along with a transformedData object containing the modified payload. The addPrefix built-in function is an example of a transformer.
Request pipeline
The following diagram shows where plugins execute within a single gateway request: All enabledbeforeRequestHook plugins run in sequence before the upstream call. All enabled afterRequestHook plugins run in sequence after the upstream response is received. A single false verdict is sufficient to trigger the configured failure action (deny or flag).
Utility helpers
plugins/utils.ts exports helpers for common tasks:
| Helper | Description |
|---|---|
getText(context, eventType) | Extracts the relevant text from the request or response based on requestType |
getCurrentContentPart(context, eventType) | Returns the raw content and text array for the active part |
setCurrentContentPart(context, eventType, transformedData, textArray) | Writes modified text back into the request or response payload |
post(url, data, options, timeout) | HTTP POST with timeout and structured error handling |
HttpError | Typed error for non-2xx HTTP responses |
TimeoutError | Typed error for fetch timeouts |
getText in almost every guardrail — it handles all requestType variants automatically so you do not need to branch on chatComplete vs complete vs embed yourself.