๐๏ธ Architecture
Tresor follows a clean layered structure with three core concerns: storage, engine, and API. The codebase is written in Go with no external web framework โ it uses net/http ServeMux directly for all routing.
๐ Directory Structureโ
cmd/ # CLI entry points (Cobra)
โโโ root.go # Root command, --config flag
โโโ run.go # Daemon startup
โโโ rule.go # Rule & alias subcommands
โโโ version.go # Version display
internal/
โโโ config/ # YAML configuration loader with auto-detect
โ โโโ config.go
โ
โโโ store/ # SQLite data layer + upsert logic
โ โโโ store.go # Schema creation, migrations, busy timeout
โ โโโ rule.go # Rule CRUD + pattern matching
โ โโโ downstream.go # Downstream CRUD + model management
โ โโโ alias.go # Alias CRUD + group operations
โ โโโ write_config.go # YAML write-back on mutation
โ
โโโ engine/ # Core gateway handler + pipeline execution
โ โโโ engine.go # Gateway forwarding, model resolution, auth, auto-translation, retry on empty response
โ โโโ pipeline.go # Transformer orchestration, streaming
โ โโโ retry.go # Empty response detection + retry with exponential backoff
โ โโโ types.go # Interfaces (RequestTransformer, etc.)
โ
โโโ jsplugins/ # JavaScript API V2 packages, runtime, validation, cache
โ โโโ api.d.ts # Public hook/effect/context contract
โ
โโโ plugins/ # Composite registry + native format converters only
โ โโโ registry.go # Native converter and external package lookup
โ โโโ openai2anthropic.go # OpenAI โ Anthropic format conversion
โ โโโ anthropic2openai.go # Anthropic โ OpenAI format conversion
โ โโโ openai2responses.go # OpenAI Chat Completions โ Responses API
โ โโโ anthropic2responses.go # Anthropic Messages โ Responses API
โ โโโ responses2openai.go # Responses API โ OpenAI Chat Completions
โ โโโ responses2anthropic.go # Responses API โ Anthropic Messages
โ โโโ openai2gemini.go # OpenAI โ Gemini format conversion
โ โโโ anthropic2gemini.go # Anthropic โ Gemini format conversion
โ โโโ gemini2openai.go # Gemini โ OpenAI format conversion
โ โโโ gemini2anthropic.go # Gemini โ Anthropic format conversion
โ โโโ gemini2responses.go # Gemini โ Responses API conversion
โ โโโ streaming.go # SSE parsing and chunk transformation
โ โโโ streaming_test.go # Comprehensive streaming transform tests
โ
โโโ api/ # Admin REST API + embedded web UI
โ โโโ router.go # Route registration, method enforcement, auth endpoints
โ โโโ rules.go # Rule endpoints
โ โโโ downstreams.go # Downstream endpoints + plugins list + fetch-models
โ โโโ aliases.go # Alias endpoints + group operations
โ โโโ config.go # Runtime config endpoints (proxy mode, auth keys, password, default tab, retry on empty)
โ โโโ icons.go # Model icon serving (public endpoint)
โ โโโ icons_admin.go # Icon index refresh (admin endpoint)
โ โโโ embed.go # Embedded web UI via //go:embed
โ
โโโ middleware/ # Cookie-based session auth for admin API
โ โโโ auth.go
โ
โโโ icons/ # Model icon fetching, pattern matching, CDN caching
โ โโโ fetcher.go # Icon fetching and caching
โ โโโ index.go # CDN index management
โ โโโ patterns.go # Model-to-icon pattern matching
โ โโโ ratelimit.go # Rate limiting for icon fetcher
โ
โโโ inspect/ # Bounded in-memory store for captured request/response bodies
โ โโโ store.go # Inspect store for Logs inspector
โ
โโโ proxy/ # Outbound proxy mode implementation
โ โโโ proxy.go # Mode resolution (auto/env/windows/none)
โ โโโ system_windows.go # Windows registry proxy reading
โ
โโโ api/web/ # Embedded SPA
โโโ index.html
โโโ style.css
โโโ app.js
e2e/ # End-to-end integration tests
main.go # Binary entry point
๐ Extensible traffic pipelineโ
Tresor combines built-in automatic API format conversion with a general-purpose JavaScript API V2 transformation layer. Plugins can modify request metadata and bodies, response headers and bodies, and SSE events, or collect upstream streams into buffered responses. Routing rules compose these transformations and scope them to selected traffic. Explore Ladbaby/Tresor-plugins for examples and inspiration; plugins are not limited to those examples.
internal/jsplugins/ validates manifests and source digests, manages an immutable package cache, and executes synchronous CommonJS hooks.
The runtime supports hot reload without restarting the gateway: install and enable a new package version to update future requests.
See the Plugin System guide for lifecycle and trust boundaries.
๐ ๏ธ Technology Stackโ
| Concern | Choice | Reason |
|---|---|---|
| Language | Go 1.26+ | Fast compilation, cross-platform CLI, native concurrency |
| Router | net/http ServeMux | No external framework dependency |
| CLI Framework | cobra | De facto standard for Go CLI applications |
| Database | modernc.org/sqlite | Pure Go SQLite โ no CGO dependency |
| Config | YAML | Human-readable, portable, version-controllable |
| Web UI | Embedded via //go:embed | No separate frontend deployment needed |
๐ Request Flowโ
Client Request
โ
โผ
โโโโโโโโโโโโโโโโโโโโ
โ 1. Auth Check โ โ Validate proxy_api_keys if configured
โโโโโโโโโโฌโโโโโโโโโโ
โ
โโโโโโโโโโผโโโโโโโโโ
โ 2. Model Extract โ โ Parse model name from request body
โโโโโโโโโโฌโโโโโโโโโ
โ
โโโโโโโโโโผโโโโโโโโโ
โ 3. Model Resolve โ โ The forwarding gate:
โ โ โข Active exact alias? โ use alias downstream, rewrite model
โ โ โข Active regex alias? โ pattern match, use downstream, rewrite
โ โ โข No alias? โ find downstream by output_model_ids
โ โ โข Neither? โ return 404 "unknown model"
โโโโโโโโโโฌโโโโโโโโโ
โ
โโโโโโโโโโผโโโโโโโโโ
โ 4. Rule Match โ โ Collect ALL matching enabled rules (path+model)
โ โ Priority: exact path+model > exact path > wildcard *
โ โ Filter by: match_format, match_downstream_format,
โ โ match_downstreams. Rules never override downstream.
โ โ Pipelines from all matching rules are concatenated.
โโโโโโโโโโฌโโโโโโโโโ
โ
โโโโโโโโโโผโโโโโโโโโ
โ 5. Auto- โ โ If request format != downstream api_formats,
โ Translation โ automatically insert format converter (prepended/
โ โ appended around rule pipelines)
โ โ Supports: OpenAI Chat Completions, Anthropic Messages,
โ โ OpenAI Responses API
โโโโโโโโโโฌโโโโโโโโโ
โ
โโโโโโโโโโผโโโโโโโโโ
โ 6. Request โ โ Execute request transformers sequentially
โ Pipeline โ (body + headers may change)
โโโโโโโโโโฌโโโโโโโโโ
โ
โโโโโโโโโโผโโโโโโโโโ
โ 7. Forward โ โ Send transformed request to downstream server
โ to Downstream โ Strip client auth, inject downstream API key
โโโโโโโโโโฌโโโโโโโโโ
โ
โโโโโโโโโโผโโโโโโโโโ
โ 8. Retry on โ โ (if retry_on_empty is enabled)
โ Empty โ If downstream returns HTTP 200 but produces no
โ Response โ content (empty choices, thinking-only output,
โ โ etc.), retry up to 3 times with exponential
โ โ backoff. Retries are transparent to the client.
โโโโโโโโโโฌโโโโโโโโโ
โ
โโโโโโโโโโผโโโโโโโโโ
โ 9. Response โ โ Execute response transformers sequentially
โ Pipeline โ Non-streaming: full body transform
โ โ Streaming (SSE): event-by-event transform
โโโโโโโโโโฌโโโโโโโโโ
โ
โโโโโโโโโโผโโโโโโโโโ
โ 10. Return to โ โ Final response to client
โ Client โ
โโโโโโโโโโโโโโโโโโโโ
Retryโ
When retry_on_empty: true is set in the config, the gateway automatically retries downstream requests that return HTTP 200 OK but produce no useful content. This protects against scenarios where:
- The downstream LLM server accepts the request but produces no text output (transient server-side issues)
- A model generates only "thinking" or "reasoning" blocks without actual text content
- The response is garbled or empty for other reasons
How it works:
- Only retries on HTTP 200 with empty content. Non-200 responses (4xx, 5xx) are never retried โ LLM client SDKs handle their own retry logic for HTTP errors.
- Maximum 3 retries with exponential backoff (500ms, 1s, 2s base) and 0โ25% random jitter.
- Streaming responses: A
bufferedWriterholds all SSE events until content is confirmed. When the first content-producing line is detected (text, tool calls, etc.), the buffer flushes to the client. If no content arrives, the stream is discarded and retried. - Non-streaming responses: The full body is read, response transformers are applied, then the format-specific empty check runs. Empty bodies are discarded and retried.
- Empty detection per format:
- OpenAI Chat Completions: Empty choices, or no text/tool_calls/refusal in both
messageanddeltafields - Anthropic Messages: Empty content array, or blocks containing only
thinking(notext, notool_use) - OpenAI Responses: Empty output array, or message items with no
output_text - Google Gemini: Empty candidates, or parts with no
text/functionCall(parts withthought:trueexcluded)
- OpenAI Chat Completions: Empty choices, or no text/tool_calls/refusal in both
- Transparent to client: The client sees nothing during retries. Only the final successful response is delivered.
- Exhaustion: If all retries are exhausted, the gateway returns HTTP 502 with error "empty response after retries exhausted".
- Client disconnection during retry aborts the loop immediately.
๐ก Aggregated Model Listโ
Tresor exposes /v1/models and /models endpoints that aggregate all known model IDs from downstream output_model_ids and alias input/output model IDs. Regex aliases are excluded from the input model ID list since they represent patterns rather than concrete model names. The response is formatted as an OpenAI-style model list, so OpenAI-compatible clients can discover available models through Tresor.
๐พ Data Layerโ
๐๏ธ SQLite Schemaโ
The database uses WAL mode with a 5000ms busy timeout. Four tables:
downstreams โ Provider endpoints
| Column | Type | Description |
|---|---|---|
id | TEXT PRIMARY KEY | Unique identifier |
name | TEXT NOT NULL | Display name |
base_url | TEXT NOT NULL | API base URL |
api_key | TEXT NOT NULL | Authentication key |
api_formats | TEXT (JSON) | API format(s) this downstream speaks |
created_at | DATETIME | Creation timestamp |
updated_at | DATETIME | Last update timestamp |
output_model_ids โ Model-to-downstream mapping
| Column | Type | Description |
|---|---|---|
downstream_id | TEXT REFERENCES downstreams | Foreign key |
model_id | TEXT | Model identifier |
(Composite unique on both columns)
rules โ Conditional transform pipelines with format-aware matching
| Column | Type | Description |
|---|---|---|
id | TEXT PRIMARY KEY | Unique identifier |
name | TEXT NOT NULL | Display name |
pattern_path | TEXT NOT NULL | URL path to match |
pattern_model | TEXT | Optional model filter |
match_format | TEXT (JSON) | Input request formats to match |
match_downstream_format | TEXT (JSON) | Downstream API formats to match |
match_downstreams | TEXT (JSON) | Downstream IDs to match |
pipeline_config | TEXT (JSON) | Ordered plugin list |
is_enabled | INTEGER | Toggle (0/1) |
created_at | DATETIME | Creation timestamp |
updated_at | DATETIME | Last update timestamp |
aliases โ Model name mappings
| Column | Type | Description |
|---|---|---|
id | TEXT PRIMARY KEY | Unique identifier |
input_model_id | TEXT NOT NULL | Client-facing model name (group key) |
downstream_id | TEXT NOT NULL REFERENCES downstreams | Target provider |
output_model_id | TEXT NOT NULL | Actual model to forward as |
is_active | INTEGER DEFAULT 0 | Active flag (one per group) |
is_regex | INTEGER DEFAULT 0 | Treat input_model_id as a regex pattern |
group_order | INTEGER DEFAULT 0 | Display order for group reordering |
created_at | DATETIME | Creation timestamp |
updated_at | DATETIME | Last update timestamp |
sessions โ Active admin sessions (multi-session support)
| Column | Type | Description |
|---|---|---|
token | TEXT PRIMARY KEY | Session token (UUID) |
created_at | DATETIME | Session creation timestamp |
Indexes on foreign keys and query patterns ensure efficient lookups. Regex patterns are cached (via sync.Map) to avoid recompilation on every request.
๐ Upsert Semanticsโ
On startup, YAML data is merged into the database:
- Downstreams: Matched by ID โ updated if exists, inserted if new.
api_formatscarried over. - Rules: Matched by ID โ updated if exists, inserted if new.
match_format,match_downstream_format,match_downstreamsserialized as JSON arrays. - Aliases: Special logic โ runtime-active aliases not in YAML are preserved (protects hot-switched state); stale YAML aliases not matching DB are deleted; new YAML aliases are created
- Model IDs: All YAML models replace existing ones for each downstream
Rows that exist only in the database (created via web UI or API) are preserved.
๐ฅ Cascade Deleteโ
Deleting a downstream:
- Removes the downstream ID from
match_downstreamsarrays on all referencing rules - Deletes all aliases pointing to that downstream
๐ Admin APIโ
Public Endpoints (no auth required)โ
| Method | Path | Purpose |
|---|---|---|
| GET | /api/health | Health check |
| GET | /api/auth/status | Check whether auth is enabled |
| POST | /api/auth/login | Verify password, obtain session |
| GET | /api/version | Binary version and build time |
Protected Endpointsโ
All other /api/* endpoints require authentication. The admin API uses cookie-based session authentication with multi-session support: after logging in via POST /api/auth/login, a session cookie (tresor_token, Path=/api/, HttpOnly, SameSite=Lax, 365-day expiry) is issued. Multiple simultaneous sessions are supported โ logging in from a second browser doesn't invalidate the first. Logging out from one browser only clears that session; others remain active. Authenticated requests send the cookie (for browser/SSE) or use it as a Bearer header. CLI commands use the raw admin_password as Bearer token (backwards compat). Session tokens persist across daemon restarts (stored in SQLite). Login is rate-limited (5 attempts per 60s per IP). Changing the admin password clears all active sessions.
Key alias endpoints include POST /api/aliases/reorder for drag-and-drop group reordering, which accepts {"order": ["gpt-4o", "claude-sonnet"]}.
Log Endpointsโ
| Method | Path | Purpose |
|---|---|---|
| GET | /api/logs | Get recent log entries (newest first, filtered by log level) |
| GET | /api/logs/stream | SSE stream of log entries (sends initial batch + live updates) |
| GET | /api/log_level | Get current log level |
| PUT/POST | /api/log_level | Update log level |
Runtime Configuration Endpointโ
| Method | Path | Purpose |
|---|---|---|
| GET | /api/config | Get current runtime settings (proxy mode, proxy keys, password status, default tab, log level, retry on empty) |
| PUT | /api/config | Update runtime settings live (pushes changes to running engine + auth middleware, writes back to YAML) |