Files
dynagate/README.md
T

9.3 KiB

Dynagate LLM gateway

Dynagate is a lightweight, high-performance LLM gateway written in Go that acts as a proxy for OpenAI-compatible APIs. It provides seamless model routing, automatic key and model failover, live configuration hot-reloading without restarts, token authentication, and custom header injection with zero third-party dependencies.

Features

  1. OpenAI-compatible endpoints:

    • /v1/models (GET): Dynamically lists unique active model IDs.
    • /v1/chat/completions (POST): Proxies non-streaming and streaming (text/event-stream) completions.
    • /v1/images/generations (POST): Proxies image generation requests.
  2. Zero-downtime live-reloading:

    • Watches models.csv (overridable via command-line flags) continuously using a background thread and automatically reloads configuration updates without dropping active connections.
  3. Fallback and retry orchestration:

    • Attempts keys configured for the requested model sequentially.
    • Cascades automatically to the next model in the priority chain (wrapping around to cover all entries) if all keys for the requested model fail.
    • Updates the "model" field dynamically in the outgoing request body before proxying, ensuring upstream compatibility.
  4. Flexible authorization:

    • Optional gateway token authentication via command-line key file.
    • Key options ("", "-") to strip authorization headers when proxying to local or open upstreams.
    • Special "-blank-" key config to pass an empty Authorization: Bearer header.
  5. External config updater:

    • Executes a periodic CLI command (-csv-updater) at a custom interval (-csv-update-interval) to fetch, generate, or download fresh model listings.
  6. Custom header injection (extra field):

    • Sets or overrides custom headers dynamically per model deployment using a JSON configuration string.

Tech stack

  • Language: Go 1.26+
  • Dependencies: None (pure Go standard library)
  • Deployment Platform: Linux/Docker/bare VPS (runs as a standalone static binary)

Prerequisites

  • Go 1.26 or higher (if building from source)
  • Make (optional, for build commands)
  • Curl (for testing API endpoints)

Getting started

1. Installation

Using go install: To install the latest version directly:

go install codeberg.org/luxferre/dynagate@latest

Or from within a cloned repository directory:

go install .

Building from source manually:

git clone https://codeberg.org/luxferre/dynagate.git
cd dynagate
make build

This produces a size-optimized, stripped static binary dynagate (~6.5MB) in the root directory.

2. Configure models

Create a models.csv file in the same directory. The file supports both header and header-less formats.

Example models.csv with Header:

model,key,endpoint,extra
gpt-4o,sk-proj-key1,https://api.openai.com/v1,{"Openai-Organization":"org-123"}
gpt-4o,sk-proj-key2,https://api.openai.com/v1,
gpt-3.5-turbo,-blank-,https://api.openai.com/v1,
deepseek-chat,sk-ds-key,https://api.deepseek.com/v1,{"X-Custom-Env":"production"}
ollama-llama3,-,http://localhost:11434/v1,

3. Setup gateway key (optional)

To restrict access to your gateway, write a single plaintext key to a file (e.g. gatekey.txt):

my-secure-gateway-token

4. Run the server

./dynagate -port 8080 -csv models.csv -key gatekey.txt

Configuration reference

Command line flags

Flag Type Default Description
-port int 8080 Port the gateway listens on.
-csv string models.csv Path to the model configuration CSV.
-key string "" Path to a file containing the gateway's access token. If blank, authentication is disabled.
-csv-updater string "" Command/script to run periodically to update models.csv.
-csv-update-interval int 10 Frequency in minutes to invoke -csv-updater.

Model CSV format rules

The gateway maps columns dynamically by looking at the header row. If no header is present, it defaults to:

  • Column 0: model (Model ID)
  • Column 1: key (API Key / Authorization Token)
  • Column 2: endpoint (Upstream base URL endpoint)
  • Column 3: extra (Optional JSON string object containing header mappings)

Auth override key values:

  • "" or "-": The gateway will strip the Authorization header completely (useful for local LLM endpoints or unauthenticated proxies).
  • "-blank-": The gateway will attach exactly Authorization: Bearer without any key appended.
  • Any other string: The gateway will attach Authorization: Bearer <key>.

Manual testing with curl

1. Model listing (authenticated)

curl -i -H "Authorization: Bearer my-secure-gateway-token" http://localhost:8080/v1/models

2. Chat completions proxying

curl -i -X POST http://localhost:8080/v1/chat/completions \
  -H "Authorization: Bearer my-secure-gateway-token" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-4o",
    "messages": [{"role": "user", "content": "Explain gravity in one sentence"}]
  }'

3. Chat completions streaming

curl -i -X POST http://localhost:8080/v1/chat/completions \
  -H "Authorization: Bearer my-secure-gateway-token" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-4o",
    "stream": true,
    "messages": [{"role": "user", "content": "Count from 1 to 5"}]
  }'

Troubleshooting

Dynamic loading failures

  • Symptom: Config manager reports file not found warnings at startup.
  • Solution: The gateway is built to run even if models.csv is initially missing. Create or write to the CSV configuration file and the hot-reloader will parse and activate it within 1 second.

Port conflicts

  • Symptom: Server listen failed: listen tcp :8080: bind: address already in use.
  • Solution: Change the listening port using the -port command line flag (e.g. ./dynagate -port 9090).

401 Unauthorized errors

  • Symptom: Requests return {"error": {"code": "invalid_api_key", ...}}.
  • Solution: Check that you are passing the correct Bearer token configured in the -key token file. Trim any whitespace, and verify it starts with Bearer .

FAQ

Why roll yet another LLM gateway?

First, security reasons. After the supply chain attacks on LiteLLM and other large gateway projects, the author stopped trusting them and wanted to build something that doesn't have any third-party dependencies. As such, Dynagate can be considered a drop-in replacement for LiteLLM proxy mode if you only need OpenAI-compatible completions and model listing endpoints.

Second, flexibility. A lot of existing LLM interaction tools, including gateways, don't support keyless or empty-key access that local inference engines or some public LLM providers offer, and also make it complicated and/or impossible to inject custom HTTP headers into outgoing requests.

Third, simplicity. Dynagate was designed with instant failover (without any extra setup) and instant configuration reload in mind, but without any extra overhead. This makes it much faster than even e.g. Bifrost, which is also written in Go and considered pretty fast on its own.

Fourth, pluggable external commands to dynamically update the entire model configuration, running in a specified interval. This is something yet unheard in any existing LLM gateway.

Is it compatible with OpenClaw?

Yes, but that's not tested yet. It is 100% compatible at least with PicoClaw, and was particularly tuned to address PicoClaw's incompatibility with keyless LLM setups, and these two services work together very nicely. The author personally uses Dynagate + PicoClaw setup on a quite weak fileserver machine in combination with free-tier (keyless access) OpenCode Zen and some other models (see the included models.csv as an example).

To configure models accessible through Dynagate in PicoClaw, select "LiteLLM" as a provider, then use [your_dynagate_url]/v1 as an endpoint. Specify any string as an API key if you didn't enable authentication in Dynagate.

Why was CSV format chosen for model configuration (as opposed to JSON, YAML, TOML etc)?

The entire initial idea behind Dynagate was for it to be able to accept dynamically generated configs on the fly. The means of generating these configs may be as straighforward as using POSIX AWK, command-line tools like Miller and/or plain shell scripts. The only format simpler than CSV to fulfill this task would be TSV (tab-separated values), but it is prone to errors when manually copying the contents between environments may inadvertently convert tab characters to spaces. As a universal tabular data format, CSV is also widely supported by various spreadsheet applications, where one may save current model setup, exporting it to models.csv as necessary without even having to restart the gateway.

Additionally, as you may have seen in the example, you can specify the same model ID with different keys and endpoints, which will automatically enable same-model failover. With a hierarchical format like JSON, this setup would require extra parameter nesting or some other complications, or just making the config look much bulkier than it should.

Credits

Created by Luxferre in 2026, released into the public domain with no warranties.