Skip to main content

๐Ÿ—๏ธ 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โ€‹

ConcernChoiceReason
LanguageGo 1.26+Fast compilation, cross-platform CLI, native concurrency
Routernet/http ServeMuxNo external framework dependency
CLI FrameworkcobraDe facto standard for Go CLI applications
Databasemodernc.org/sqlitePure Go SQLite โ€” no CGO dependency
ConfigYAMLHuman-readable, portable, version-controllable
Web UIEmbedded via //go:embedNo 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 bufferedWriter holds 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 message and delta fields
    • Anthropic Messages: Empty content array, or blocks containing only thinking (no text, no tool_use)
    • OpenAI Responses: Empty output array, or message items with no output_text
    • Google Gemini: Empty candidates, or parts with no text/functionCall (parts with thought:true excluded)
  • 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

ColumnTypeDescription
idTEXT PRIMARY KEYUnique identifier
nameTEXT NOT NULLDisplay name
base_urlTEXT NOT NULLAPI base URL
api_keyTEXT NOT NULLAuthentication key
api_formatsTEXT (JSON)API format(s) this downstream speaks
created_atDATETIMECreation timestamp
updated_atDATETIMELast update timestamp

output_model_ids โ€” Model-to-downstream mapping

ColumnTypeDescription
downstream_idTEXT REFERENCES downstreamsForeign key
model_idTEXTModel identifier

(Composite unique on both columns)

rules โ€” Conditional transform pipelines with format-aware matching

ColumnTypeDescription
idTEXT PRIMARY KEYUnique identifier
nameTEXT NOT NULLDisplay name
pattern_pathTEXT NOT NULLURL path to match
pattern_modelTEXTOptional model filter
match_formatTEXT (JSON)Input request formats to match
match_downstream_formatTEXT (JSON)Downstream API formats to match
match_downstreamsTEXT (JSON)Downstream IDs to match
pipeline_configTEXT (JSON)Ordered plugin list
is_enabledINTEGERToggle (0/1)
created_atDATETIMECreation timestamp
updated_atDATETIMELast update timestamp

aliases โ€” Model name mappings

ColumnTypeDescription
idTEXT PRIMARY KEYUnique identifier
input_model_idTEXT NOT NULLClient-facing model name (group key)
downstream_idTEXT NOT NULL REFERENCES downstreamsTarget provider
output_model_idTEXT NOT NULLActual model to forward as
is_activeINTEGER DEFAULT 0Active flag (one per group)
is_regexINTEGER DEFAULT 0Treat input_model_id as a regex pattern
group_orderINTEGER DEFAULT 0Display order for group reordering
created_atDATETIMECreation timestamp
updated_atDATETIMELast update timestamp

sessions โ€” Active admin sessions (multi-session support)

ColumnTypeDescription
tokenTEXT PRIMARY KEYSession token (UUID)
created_atDATETIMESession 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_formats carried over.
  • Rules: Matched by ID โ€” updated if exists, inserted if new. match_format, match_downstream_format, match_downstreams serialized 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_downstreams arrays on all referencing rules
  • Deletes all aliases pointing to that downstream

๐ŸŒ Admin APIโ€‹

Public Endpoints (no auth required)โ€‹

MethodPathPurpose
GET/api/healthHealth check
GET/api/auth/statusCheck whether auth is enabled
POST/api/auth/loginVerify password, obtain session
GET/api/versionBinary 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โ€‹

MethodPathPurpose
GET/api/logsGet recent log entries (newest first, filtered by log level)
GET/api/logs/streamSSE stream of log entries (sends initial batch + live updates)
GET/api/log_levelGet current log level
PUT/POST/api/log_levelUpdate log level

Runtime Configuration Endpointโ€‹

MethodPathPurpose
GET/api/configGet current runtime settings (proxy mode, proxy keys, password status, default tab, log level, retry on empty)
PUT/api/configUpdate runtime settings live (pushes changes to running engine + auth middleware, writes back to YAML)