Self-hosting

Architecture

How Yolorouter is layered — the protocol IR, the request path, and storage options.

  OpenAI Chat Completions ─┐                                        ┌─ OpenAI-native provider
  OpenAI Responses ────────┤                                        ├─ Anthropic-native provider
  Anthropic Messages ──────┼──▶ ┌──────────────────────────────┐ ──▶├─ Gemini-native provider
  Gemini generateContent ──┘    │          Yolorouter          │    └─ local Ollama / vLLM / ...
                                │                              │
                                │  auth · limits · budget      │
  ┌────────────┐   admin UI     │  protocol negotiation + IR   │
  │  operator  │ ─────────────▶ │  model alias · candidates    │
  └────────────┘  embedded Vue  │  key rotation · failover     │
                                │  compression · logging       │
                                └──────────────┬───────────────┘

                                        SQLite / PostgreSQL

Top to bottom: four protocol ingresses → auth, limits and budget → protocol negotiation and IR translation → model aliasing and provider candidates → key rotation and failover → input compression and logging. The admin console is an embedded Vue app; data lands in SQLite or PostgreSQL.

The layers

  • Backend — Go (Gin + GORM), migrations via goose. Layered handler → service → repository, with gateway forwarding and protocol codecs in their own packages.
  • Protocol layer — one intermediate representation (IR) plus a codec set per protocol: request decode, request encode, response decode, response encode, each with its own streaming decoder/encoder pair. Same-protocol requests bypass the IR entirely — the body is passed through and only the model name is rewritten.
  • Frontend — Vue 3 + TypeScript + naive-ui, built with Vite and embedded into the binary via go:embed.
  • Storage — SQLite (pure Go, zero configuration) or PostgreSQL. Upstream keys are encrypted at rest with AES-256.

Where streaming draws the line

Key rotation and failover both happen before the first byte reaches the client. Once streaming starts the provider is locked in — content from two providers is never stitched into one response. That boundary defines how far failure recovery can go: an upstream that dies after the response has begun surfaces as an error to the caller rather than being silently swapped.

Source

Full source, issues and contribution guidelines live on GitHub. Licensed under the Apache License 2.0.