@ultimatule/otra
v0.7.2
Published
Self-hostable proxy that pools multiple free-tier Gemini API keys behind a single endpoint with OpenAI and Anthropic dialect support
Maintainers
Readme
OTRA orchestrates requests across your Gemini key pool using smart rotation, live token/request counters, and automatic fallbacks — giving your applications the feel of unlimited quotas through a single master key.
Three wire-format dialects on the same server:
- Native Gemini — zero-translation, identical to
@google/genaiREST surface - OpenAI-compatible — drop-in for LangChain, LiteLLM, Continue.dev, Cursor
- Anthropic-compatible — works with Claude SDKs and Cursor in Anthropic mode
Source: gitlab.com/ultimatule/otra
Quick Start
Option A — npm global install
# 1. Install
npm install -g @ultimatule/otra
# 2. Generate credentials (run in the directory you'll use as your data root)
otra init
# Creates .env with OTRA_MASTER_KEY and OTRA_BOOTSTRAP_ADMIN_TOKEN
# 3. Start (auto-loads .env from current directory — no 'source' needed, works on Windows too)
otra start
# API server: http://localhost:4000
# Dashboard: http://localhost:4001Option B — Docker Compose (recommended for production)
# 1. Clone and configure
git clone https://gitlab.com/Ultimatule/otra.git
cd otra
cp .env.example .env
# 2. Generate master key and admin token, paste into .env
node -e "console.log('OTRA_MASTER_KEY=' + require('crypto').randomBytes(32).toString('base64'))"
node -e "console.log('OTRA_BOOTSTRAP_ADMIN_TOKEN=otra_' + require('crypto').randomBytes(32).toString('base64url'))"
# 3. Start
docker compose up -d
# API server: http://localhost:4000
# Dashboard: http://localhost:4001Adding Your First Provider Key
otra provider-keys add --provider gemini --label "Key 1" --key "AIzaSy..."Or via the dashboard: http://localhost:4001 → Gemini Keys → + Add Gemini Key
Issuing an OTRA Access Key
otra otra-keys issue --label "my-app"
# → otra_XXXXXXXXXXXXXXXXXXXXXXXX (shown once — copy it now)Or via the dashboard: http://localhost:4001 → OTRA Keys → + Create OTRA Key
Using OTRA from Your App
import { GoogleGenAI } from '@google/genai';
const ai = new GoogleGenAI({
apiKey: 'otra_...', // your OTRA key
httpOptions: { baseUrl: 'http://localhost:4000' }, // point at OTRA
});
const res = await ai.models.generateContent({
model: 'gemini-2.0-flash',
contents: 'Hello from OTRA!',
});
console.log(res.text);OTRA also accepts Authorization: Bearer otra_..., x-goog-api-key: otra_..., and x-api-key: otra_....
OpenAI dialect
curl http://localhost:4000/openai/v1/chat/completions \
-H "Authorization: Bearer otra_..." \
-H "Content-Type: application/json" \
-d '{"model":"gemini-2.0-flash","messages":[{"role":"user","content":"Hello"}]}'Anthropic dialect
curl http://localhost:4000/anthropic/v1/messages \
-H "Authorization: Bearer otra_..." \
-H "anthropic-version: 2023-06-01" \
-H "Content-Type: application/json" \
-d '{"model":"gemini-2.0-flash","max_tokens":1024,"messages":[{"role":"user","content":"Hello"}]}'CLI Reference
otra init # Generate .env credentials
otra start # Start both servers (foreground)
otra start --daemon # Start as background daemon (-d short form)
otra stop # Stop the daemon
otra restart # Restart the daemon
otra status # Show PID + API/Admin health
otra healthcheck # Exit 0=healthy / 1=unhealthy (cron/Docker)
otra config show # Show resolved config (secrets masked)
otra config path # Show .env / DB / PID / log file paths
otra config edit # Open .env in $EDITOR / notepad
otra provider-keys add --provider gemini \
--label <name> --key <AIzaSy...> # Add a provider key
otra provider-keys list # List provider keys
otra provider-keys rm <id> # Delete (prompts confirmation)
otra provider-keys rm <id> --force # Delete without prompt
otra otra-keys issue --label <name> # Issue OTRA key (shown once)
otra otra-keys list # List OTRA keys
otra otra-keys revoke <id> # Disable an OTRA keyRunning in the background
otra start --daemon # Start daemon; writes PID to ./data/otra.pid
otra status # Check health
otra stop # Graceful stop (SIGTERM, then SIGKILL after 10 s)
otra restart # stop + start --daemon in one stepWhen running in foreground (otra start), press b to detach to background without restarting. Ctrl+C stops the server.
Windows note:
.envis auto-loaded from the current directory — nosourcecommand needed. Run all commands in the folder that contains your.env.
Configuration
All settings via environment variables. Set in .env or export before otra start.
| Variable | Default | Description |
|---|---|---|
| OTRA_MASTER_KEY | required | 32-byte base64 — encrypts all provider keys at rest |
| OTRA_BOOTSTRAP_ADMIN_TOKEN | required on first run | Initial admin token |
| OTRA_API_PORT | 4000 | Public proxy port |
| OTRA_ADMIN_PORT | 4001 | Dashboard + admin API port |
| OTRA_HOST | 0.0.0.0 | Bind address |
| OTRA_ROTATION | leastUsed | Key rotation strategy (leastUsed | roundRobin | lowestRpd | weightedRemaining | sticky) |
| OTRA_MAX_RETRIES_PER_REQUEST | 8 | Max key rotations per client request |
| OTRA_MODEL_FALLBACK | false | Auto-fallback to alternative model on overload |
| OTRA_ALLOWED_FAILS | 0 | Failures per minute before a key enters cooldown (0 = disabled) |
| OTRA_COOLDOWN_SECONDS | 60 | Seconds a key stays in cooldown before auto-recovery |
| OTRA_DROP_PARAMS | false | Strip unsupported top-level Gemini params before forwarding |
| OTRA_RETAIN_REQUESTS_DAYS | 30 | Days to keep detailed request logs |
| OTRA_DB_PATH | ./data/otra.db | SQLite database path |
| OTRA_REDIS_URL | (unset) | Optional Redis URL for multi-instance deployments |
| OTRA_LOG_LEVEL | info | fatal\|error\|warn\|info\|debug\|trace |
All engine settings can also be changed live from Dashboard → Settings → Engine without restarting.
Dashboard
Open http://localhost:4001 in your browser. Log in with any OTRA admin key.
| Page | What it shows | |---|---| | Overview | Aggregate request count, error rate, latency P95, token usage | | Gemini Keys | All provider keys with live status; bulk import | | OTRA Keys | Issue, edit, enable/disable, and revoke access keys | | Requests | Last 50 requests per page with diagnostics drawer | | Model Aliases | Map public model names to real Gemini models; attach profiles | | Models | Per-model RPM/RPD/TPM limits and today's usage | | Settings | Admin token, engine settings, API usage snippet |
The dashboard is bilingual — click EN | RU in the sidebar footer to switch language.
Security
- Master key encrypts every provider key in the database. Loss = permanent loss of all provider keys. Store in a secrets manager. Never commit to version control.
- OTRA access keys are shown exactly once at creation. Stored as argon2id hashes — unrecoverable.
- OTRA does not terminate TLS. Place a reverse proxy (Caddy, nginx) in front for HTTPS.
- Expose port 4000 (API) via reverse proxy only. Keep port 4001 (admin) internal.
License
MIT © 2025 Ultimatule
