Skip to main content

πŸ”Œ Plugin System

Tresor's JavaScript plugin system lets you build custom traffic modifications without rebuilding or restarting the gateway. Transform requests, responses, and streaming events, then compose your transformations into ordered routing-rule pipelines. Tresor embeds the runtime, so no Node.js installation is required.

✨ What can you build?​

  • Modify request headers, paths, queries, and bodiesβ€”for example, inject instructions or reshape messages and tool data.
  • Rewrite buffered response bodies and headers, normalize provider-specific payloads, or filter unwanted content.
  • Transform SSE events with per-request state, request upstream streaming, and collect streams into buffered responses.
  • Combine multiple plugins and target them by request path, model, client format, downstream format, or downstream ID.

The hooks form a general-purpose traffic transformation layer; the examples below are starting points, not a list of everything the system can do. See the lifecycle and runtime boundaries below for the supported effects and execution model.

πŸ”„ Hot reload​

Install and enable packages while the daemon is runningβ€”no gateway restart, binary rebuild, or client reconfiguration is needed. Installing a new version enables it for future request snapshots. Requests already in flight keep their pinned package, so an update does not replace code partway through a response.

To iterate on your own plugin, edit its source, bump the manifest version, and install the directory again. You can also enable or disable installed versions live. Hot reload is package-based: editing a source file alone does not update the installed package.

API format conversion is built into Tresor and selected automatically; plugins let you customize traffic beyond that baseline.

πŸ› οΈ Built-in format conversion​

Tresor automatically selects converters when the client format differs from the downstream's declared api_formats. These internal converters handle requests, responses, and SSE streams; they are not installable packages and are not offered in the rule plugin picker.

Client formatNative converters
OpenAI Chat Completionsopenai2anthropic, openai2responses, openai2gemini
Anthropic Messagesanthropic2openai, anthropic2responses, anthropic2gemini
OpenAI Responsesresponses2openai, responses2anthropic
Google Geminigemini2openai, gemini2anthropic, gemini2responses

No routing rule is needed for format conversion. Configure downstream formats and model routing normally; do not add converter IDs to user pipelines.

πŸ“¦ Example plugins and inspiration​

Explore Ladbaby/Tresor-plugins for complete, installable examples. Read their source for inspiration, adapt an approach to your own needs, or build a completely different transformation:

IDPurpose
break_repeatInserts a reminder after repeated comparable assistant turns
fix_anthropic_systemMoves system-role messages into Anthropic's top-level system field
fix_anthropic_usageNormalizes incomplete Anthropic usage reporting
force_streamingRequests upstream SSE for buffered clients and reconstructs a JSON response
remove_thinkingRemoves thinking/reasoning response content while preserving visible content and tool inputs

Use the repository's README and each package's manifest for current versions and feature requirements. Plugins are optional and apply only when an enabled routing rule includes their ID.

⚑ Install and enable​

Start the Tresor daemon first. Choose a versioned JSON bundle from Tresor-plugins Releases, not a repository ZIP or an individual JavaScript file.

# Example published bundle; add --config if your daemon uses a custom config path.
tresor plugin install-url "https://github.com/Ladbaby/Tresor-plugins/releases/download/v1.0.2/remove_thinking-2.0.2.json"

# Alternatively, install a local source directory containing manifest.json.
tresor plugin install ./remove_thinking

tresor plugin list

In the dashboard, open Rules > Plugins to install the bundle's HTTPS URL or manage installed versions. Installation enables the installed version immediately; no separate approval or activation step is needed. Only one version per plugin ID is enabled at a time.

Install only trusted code. To check a known release, add --sha256 <canonical-source-digest> using the value in its plugin-digests.txt. This is Tresor's canonical source digest, not a checksum of the downloaded JSON file. Obtain the digest from a trusted source. HTTPS alone is not publisher verification. Signed bundles can additionally be checked with --signature <base64> and --public-key <base64> using a trusted Ed25519 publisher key.

tresor plugin disable remove_thinking 2.0.2
tresor plugin enable remove_thinking 2.0.2
tresor plugin delete remove_thinking 2.0.2

plugin activate <id> <version> remains a compatibility alias for enabling. Changes affect future request snapshots; in-flight requests retain their pinned package.

πŸ“ Add a plugin to a rule​

Enabling a package makes it available, but does not apply it globally. Add it to a routing rule in the Web UI or YAML:

rules:
- id: hide-reasoning
name: Hide reasoning
pattern_path: '*'
pipeline_config:
- plugin_id: remove_thinking
is_enabled: true

In the admin API and database, pipeline_config is an ordered JSON array:

[
{"plugin_id": "fix_anthropic_usage"},
{"plugin_id": "remove_thinking"}
]

Each step contains only plugin_id. There are no per-plugin user settings or configurable failure policies. Hook failures raise errors rather than silently bypassing the plugin. Order matters: each transformation feeds the next. Multiple matching rules contribute pipelines in rule priority order; rules never choose the downstream.

✍️ Write a JavaScript API V2 package​

A plugin is a directory containing manifest.json, a CommonJS entry file, and any package-relative modules. Node.js is not required to run it: Tresor embeds a JavaScript runtime.

Example manifest.json:

{
"id": "example/request-header",
"version": "1.0.0",
"api_version": 2,
"entry": "plugin.js",
"requires": [],
"permissions": [],
"config_schema": {}
}

Example plugin.js (a fixed transformation, not user-configurable):

module.exports = {
request({ request, body, context, state }) {
return {
request: {
setHeaders: { "x-example": ["enabled"] }
}
};
}
};

Inputs are immutable snapshots; return an effect rather than modifying them. Returning undefined leaves the relevant input unchanged. state is mutable per-plugin request state. Payloads use { encoding: "utf8" | "base64", data: string }; headers use arrays of strings. Hook execution is synchronous: Promises are unsupported.

The authoritative contract is internal/jsplugins/api.d.ts. Check it against the Tresor version you target, and use the published packages as complete examples.

πŸ”„ Lifecycle and transport​

HooksPurpose
requestTransform outgoing request metadata/body and optionally request transport behavior
responseHeaders, responseTransform headers and buffered response bodies
upstreamStart, upstreamEvent, upstreamEndProcess upstream SSE before collection or regular response-event transforms
streamStart, streamEvent, streamEndTransform regular response SSE events
collectStart, collectEvent, collectEndCollect upstream SSE into a buffered response
finishCleanup on completion or error

The immutable context distinguishes client, downstream, and current formats; requested versus observed HTTP/SSE transport; stage; phase; and effective limits. Never infer all of these from the original client request.

Declare required host features in the manifest. Upstream hooks require sse.upstream.v2; collection requires sse.collect.v2 and all three collection hooks. Ordinary stream hooks do not automatically run before collection. Error termination provides sanitized failure metadata and statistics, and finish releases managed storage. See api.d.ts for exact hook inputs, effects, and supported feature names.

πŸ“¦ Lint, pack, and publish​

# Static checks only; neither command executes JavaScript.
tresor plugin lint ./my-plugin
tresor plugin lint ./my-plugin --json --strict

# Output must be a new file. Pack prints the canonical source digest.
tresor plugin pack ./my-plugin --output ./my-plugin-1.0.0.json

# Install into the running daemon for integration testing.
tresor plugin install ./my-plugin

Lint findings are advisory unless --strict is used. Lint is not a compatibility proof: test real gateway requests, including malformed payloads, SSE termination, collection, and cleanup. Bump the manifest version whenever published source changes. Publish versioned JSON bundles and canonical digests rather than repository ZIPs.

πŸ” Runtime boundaries​

Plugins are trusted code, not a hard security sandbox. The runtime exposes neither Node/browser APIs nor arbitrary filesystem/network access. Imports are limited to package-relative JavaScript modules and provided tresor/* libraries. Non-empty permissions, user configuration, and logging capabilities are unsupported; plugin logs and execution diagnostics are not collected or published.

Tresor validates effects and enforces host-selected body, event, state, buffer, and wire limits, but these are not a hard memory sandbox. There are no execution timers or watchdogs for initialization or hooks. Authors must ensure their code terminates.

🌐 Admin API​

Package management endpoints require admin authentication:

MethodPathPurpose
GET/api/pluginsEnabled executable external plugins for rule selection; excludes native converters
GET/api/plugin-packagesInstalled versions and availability
POST/api/plugin-packages/installInstall local source as {files: {...}}
POST/api/plugin-packages/install-remoteInstall HTTPS {url, sha256?, signature?, public_key?} bundle
POST/api/plugin-packages/enabledSet {id, version, enabled}
POST/api/plugin-packages/deleteDelete {id, version}
POST/api/plugin-packages/activateCompatibility alias for enabling