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.)
โ”‚
โ”œโ”€โ”€ plugins/ # Plugin registry + built-in transformers
โ”‚ โ”œโ”€โ”€ registry.go # Plugin registration and lookup
โ”‚ โ”œโ”€โ”€ custom_header.go # Header injection plugin
โ”‚ โ”œโ”€โ”€ 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
โ”‚ โ”œโ”€โ”€ anthropic_image_fix.go # Image extraction from tool results
โ”‚ โ”œโ”€โ”€ anthropic_usage_fix.go # Anthropic usage block normalization (MiniMax-M3 etc.)
โ”‚ โ”œโ”€โ”€ 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

๐Ÿ› ๏ธ 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)