Skip to content

bloodf/durindoor

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

182 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Ancient stone portal glowing green in dark ruins

DurinDoor β€” Speak, friend, and enter. One guarded gateway for every AI provider

npm License Stars CI

Add provider credentials once in a self-hosted dashboard, then point any OpenAI-compatible tool at a single local endpoint with unified usage tracking, provider combos, and request logs.

πŸ—ΊοΈ Explore

πŸšͺ Why DurinDoor?

DurinDoor is a self-hosted AI gateway that sits between your tools and your providers. It keeps provider credentials on your own machine and exposes one local API every OpenAI-compatible client can speak.

  • One local endpoint β€” Configure tools once against http://localhost:20128/v1 instead of wiring each app to a different provider.
  • Reusable provider connections β€” Add an OAuth login, API key, cookie, or compatible endpoint once; every tool shares it.
  • Account and combo fallback β€” Chain multiple connections for resilience, or stack models behind one stable name so a failed attempt retries the next option.
  • Format translation β€” Send OpenAI-shaped requests and have them translated for Claude, Gemini, Kiro, Cursor, Ollama, Vertex, and other upstream formats.
  • Usage visibility β€” Inspect provider, model, tokens, cost estimates, latency, and fallback outcome per request in the dashboard.
  • Self-hosted state β€” Storage, credentials, and logs live in your DATA_DIR; you control where it runs and who can reach it.

DurinDoor is a fork of 9router. Existing installations can keep their data, keys, and headers.

🧭 How it works

flowchart LR
    Client[OpenAI-compatible tool] -->|/v1 request + DurinDoor key| Route[/Compatibility API/]
    Route --> Handler[Auth + model/alias/combo resolution]
    Handler --> Core[Routing and translation core]
    Core -->|translate request| Provider[Upstream provider]
    Provider -->|stream or JSON| Core
    Core -->|translate response| Client
    Core --> DB[(SQLite usage + logs)]
Loading
  1. A client sends a request to /v1 with a DurinDoor API key and a model string (provider model, alias, or combo name).
  2. DurinDoor validates the key, resolves the model, and selects an available provider connection.
  3. If the client and provider speak different formats, the request and response are translated; the upstream call is made with stored credentials.
  4. Usage, latency, and outcome are written to local SQLite, and the response streams back to the client in the client's format.

The model field accepts several shapes, resolved before any upstream call:

Model string Example Resolves to
Provider model openai/gpt-4.1 A provider ID plus an upstream model.
Provider alias cc/claude-sonnet A registry alias for an upstream model.
Compatible node openai-compatible-lab/model-name A custom provider node plus its model.
Model alias daily-coder A user-defined alias mapped to another model.
Combo name coding-default An ordered fallback chain of models.

Credential selection avoids accounts that are temporarily locked, expired, or excluded by the current fallback attempt, and refreshes OAuth tokens when the upstream supports refresh.

See Architecture and Smart Routing for the internal request lifecycle and failure handling.

⚑ Quick start

Requirements: Node.js 20.20.2 and npm 10.8.2 (also bundled in the Docker image).

Install the CLI globally and start the gateway:

npm install -g durindoor
durindoor

On first run DurinDoor creates DATA_DIR and initializes the database. Default surfaces:

Surface URL
Dashboard http://localhost:20128/dashboard
API base http://localhost:20128/v1
Health check http://localhost:20128/api/health
  1. Open the dashboard and sign in with the INITIAL_PASSWORD you set (or the local default). Change it before exposing the instance.
  2. Add a provider connection under Providers (OAuth login, API key, compatible endpoint, or local provider).
  3. Create a DurinDoor API key under API Keys. New keys have the shape sk-<machine>-<key>-<crc>; older sk-* keys remain supported. The full key is shown once β€” store it.
  4. Send a request:
curl http://localhost:20128/v1/chat/completions \
  -H "Authorization: Bearer YOUR_DURINDOOR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "MODEL_ID",
    "messages": [
      {"role": "user", "content": "Reply with one short sentence."}
    ],
    "max_tokens": 32
  }'

Replace MODEL_ID with a model ID, alias, or combo name from your dashboard.

Other ways to run DurinDoor

Run without installing (npx, evaluation only)
npx durindoor

Use npx for quick evaluation. For daily use, install globally or run from source.

Run from source
git clone https://github.com/bloodf/durindoor.git
cd durindoor
npm install --no-audit --no-fund
npm run build
npm start

For development, npm run dev serves on port 20127 (production uses 20128).

Run with Docker

Pin a version tag for production; do not rely on latest for stability:

docker run -d \
  --name durindoor \
  -p 127.0.0.1:20128:20128 \
  -v "$HOME/.durindoor:/app/data" \
  -e DATA_DIR=/app/data \
  -e JWT_SECRET="change-me" \
  -e INITIAL_PASSWORD="change-me" \
  ghcr.io/bloodf/durindoor:3.9.0

See Cloud Deployment for the full production compose setup, and Upgrading before moving between versions.

See Installation for the full configuration reference, data paths, and environment variables.

More in the Quick Start and Installation guides.

🧩 Connect your tools

Point any OpenAI-compatible client at one base URL with your DurinDoor key:

Base URL: http://localhost:20128/v1
API key:  your DurinDoor API key
Model:    a model ID, alias, or combo name from the dashboard

Integration guides for popular tools:

Claude-compatible clients may expect ANTHROPIC_BASE_URL; see the per-tool guide.

From Python with the OpenAI SDK:

from openai import OpenAI

client = OpenAI(
    base_url="http://localhost:20128/v1",
    api_key="YOUR_DURINDOOR_API_KEY",
)

resp = client.chat.completions.create(
    model="MODEL_ID",
    messages=[{"role": "user", "content": "Reply with one short sentence."}],
    max_tokens=32,
)
print(resp.choices[0].message.content)

For more SDK examples and the dashboard workflow, see the Usage Guide.

✨ What DurinDoor handles

Compatible API families

Family Notes
Chat Completions OpenAI-compatible /v1/chat/completions, the common entry point for most tools.
Responses /v1/responses with compatibility rewrites for selected clients.
Claude Messages /v1/messages for Claude-compatible clients and translation testing.
Models /v1/models lists models available through configured providers, aliases, and nodes.
Embeddings /v1/embeddings routed to embedding-capable providers.
Images /v1/images/generations and /v1/images/edits. Provider-dependent.
Audio /v1/audio/speech, /v1/audio/transcriptions, /v1/audio/translations, /v1/audio/voices. Provider-dependent.
Web search / fetch /v1/search and /v1/web/fetch through dedicated search providers.
Realtime OpenAI-Realtime-shaped /v1/realtime WebSocket; text conversations only.
Rerank / moderation /v1/rerank and /v1/moderations with providers that support them.
Token counting /v1/messages/count_tokens. Provider support varies.

A route existing does not mean every provider supports it; modalities are provider-dependent.

Routing, combos, and visibility

Capability What it does
Smart routing Resolves model strings, selects an available connection, translates formats, and records usage.
Account fallback Retries another active connection for the same provider and model.
Combos Chain models behind one stable name; a failed member falls through to the next.
Usage and quota tracking Request logs, cost estimates, provider limits, and reset windows in the dashboard.
Compression Opt-in prompt compression before upstream dispatch; fail-open by design.
MCP Gateway Expose multiple MCP servers behind managed keys and routes.
Realtime Text WebSocket sessions in the OpenAI Realtime shape.

Common combo strategies:

Strategy Typical order
Reliability first Most reliable paid provider, then secondary provider, then local fallback.
Cost control Subscription or local model, then low-cost API, then premium model.
Latency first Fast local or small model, then balanced model, then larger model.
Capability first Strongest model, then compatible backup, then simpler fallback.

A combo member can still use multiple accounts for the same provider before falling through to the next member. Use request logs to confirm which model served each request.

Providers and local/custom endpoints

Connection type Credential shape
OAuth Browser login, device code, or OAuth callback.
API key Provider API key or token.
Compatible endpoint OpenAI-compatible or Anthropic-compatible URL.
Local provider Local URL, no remote account.

See Provider Connections, Provider Nodes and Custom Providers, and Free and Local Providers. All modality support is provider-dependent.

πŸ”Œ API surface

Base URL: http://localhost:20128/v1 (use your HTTPS origin for remote deployments).

Authorization: Bearer YOUR_DURINDOOR_API_KEY

Use DurinDoor API keys generated in the dashboard; do not send upstream provider keys to client-facing routes.

Endpoint Method Purpose
/v1/chat/completions POST OpenAI-compatible chat (most tools).
/v1/responses POST Responses API with compatibility rewrites.
/v1/messages POST Claude Messages for Claude-compatible clients.
/v1/messages/count_tokens POST Token counting (provider-dependent).
/v1/models GET Available models, aliases, and nodes.
/v1/embeddings POST Embeddings.
/v1/images/generations POST Text-to-image.
/v1/images/edits POST Image edits.
/v1/audio/speech POST Text-to-speech.
/v1/audio/transcriptions POST Transcription.
/v1/audio/translations POST Translation.
/v1/audio/voices GET Available voices.
/v1/search POST Web search.
/v1/web/fetch POST Web fetch.
/v1/realtime WS Realtime text WebSocket.
/v1/rerank POST Rerank (provider-dependent).
/v1/moderations POST Moderation (provider-dependent).
/api/health GET Health check (no provider setup needed).

Full details, per-key model and lifetime policy fields, and compatibility notes are in the API Reference.

πŸ—οΈ Architecture

DurinDoor is a Next.js gateway with a dashboard and an OpenAI-compatible compatibility API.

Area Responsibility
Dashboard routes Browser UI for providers, combos, usage, endpoint setup, tools, tunnels, MITM, MCP, and settings.
Compatibility API OpenAI-compatible and related client-facing routes under /v1 and /v1beta.
Routing layer (src/sse) Request entry, auth, model/combo resolution, and service dispatch.
Core engine (open-sse) Provider execution, translation, streaming, fallback, token refresh, and usage extraction.
open-sse translators Convert between OpenAI, Anthropic Claude, Gemini, Responses, Kiro, Cursor, CommandCode, Ollama, Vertex, and other formats.
SQLite persistence Driver, migrations, repositories, and backups under DATA_DIR.

Two fallback layers operate inside the routing core: account fallback picks another active connection for the same provider and model, and combo fallback moves to the next model in an ordered combo chain. The OpenAI-pivot translation layer keeps client and provider formats separate so one client can reach many upstreams.

See Architecture for the request lifecycle, routing internals, and extension points.

πŸ” Security and operations

DurinDoor stores provider credentials and routes model traffic β€” treat it as sensitive infrastructure.

  • Prefer localhost-only binding. The CLI binds 0.0.0.0 by default, so run durindoor --host 127.0.0.1 for local-only use. Source runs default to 127.0.0.1 when HOSTNAME is unset. Docker sets HOSTNAME=0.0.0.0 inside the container; keep it private with host-side mapping such as -p 127.0.0.1:20128:20128. Do not expose the dashboard publicly with only the default password.
  • Separate DurinDoor keys from upstream credentials. Client tools use DurinDoor API keys; upstream provider keys, OAuth tokens, and cookies stay in DATA_DIR and are never sent to client-facing routes.
  • Set explicit production secrets. Define JWT_SECRET, API_KEY_SECRET, and a strong INITIAL_PASSWORD before any remote exposure.
  • Use HTTPS and restrict dashboard access. Reverse proxy with auth, VPN, firewall, or trusted-network allowlist; set AUTH_COOKIE_SECURE=true behind HTTPS.
  • Back up DATA_DIR. Protect db/data.sqlite, db/backups/, auth/, and mitm/ as secrets.
  • Review the security docs before exposure. See Security and Production Hardening, Startup and Runtime Operations, and Environment Variables.
  • Keep request logging off by default. Detailed logs may include prompts, responses, and file names; set ENABLE_REQUEST_LOGS=false unless actively debugging, with a defined retention and access policy.
  • Treat tunnels as exposure. HTTPS tunnels make the gateway reachable from another network β€” keep dashboard auth on, prefer dedicated keys, and disable tunnels when unused.

Compatibility notes

DurinDoor is a fork of 9router. These compatibility names remain supported so prior installations can migrate without losing data:

  • Storage path ~/.9router (override with DATA_DIR).
  • Legacy API keys in the sk-<8 hex> shape alongside current sk-<machine>-<key>-<crc> keys.
  • Internal headers prefixed X-9Router- and their X-DurinDoor- equivalents.

πŸ“š Documentation

The canonical documentation is Markdown in this repository; GitHub-rendered Markdown is the source of truth.

🀝 Contributing

Contributions are welcome. Read Contributing, Local Development, and Architecture before opening a pull request, and follow the pull request template.

πŸ™ Acknowledgments

DurinDoor is a fork of 9router, created by decolua. This project builds on that foundation with a new identity, enhanced features, and an emphasis on keeping the doors of your AI stack open. Upstream documentation and branding remain the property of their respective authors and are not presented as DurinDoor's source of truth.

About

DurinDoor is a self-hosted AI gateway that gives developer tools and applications one stable API in front of many upstream AI providers.

Resources

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Releases

Packages

Used by

Contributors

Languages