A lightweight LLM API gateway that proxies requests to upstream providers (OpenAI, Anthropic, Google Gemini), logs usage metrics, and calculates costs.
- Multi-provider support - Route requests to OpenAI, Anthropic, and Google Gemini
- API key management - Create, list, and revoke API keys with built-in CLI commands
- Automatic cost calculation - Track spending with real-time pricing data from llm-prices.com
- Usage logging - Store request logs in PostgreSQL with token counts, latency, and costs
- Custom metadata - Attach custom headers (
X-Majordomo-*) for tracking by user, feature, environment, etc. - Body storage - Optionally store full request/response bodies in S3 or PostgreSQL
- Zero-config provider detection - Automatically detects provider from request path
- Getting Started Guide - Full walkthrough with SDK integration examples
- Go 1.25+
- PostgreSQL 14+
- (Optional) S3-compatible storage for body logging
git clone https://github.com/superset-studio/majordomo-gateway.git
cd majordomo-gateway
make buildpsql -U postgres -c "CREATE DATABASE majordomo;"
psql -U postgres -d majordomo -f schema.sqlCopy the example environment file and edit it:
cp .env.example .env
# Edit .env with your PostgreSQL credentials./bin/majordomo keys create --name "My First Key"Save the returned key (it won't be shown again). Keys have the format mdm_sk_....
make runThe gateway starts on http://localhost:7680 by default.
curl -X POST http://localhost:7680/v1/chat/completions \
-H "X-Majordomo-Key: mdm_sk_your_key_here" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-4o",
"messages": [{"role": "user", "content": "Hello!"}]
}'Configuration is loaded from majordomo.yaml (or /etc/majordomo/majordomo.yaml). Environment variables with MAJORDOMO_ prefix override config file values.
server:
host: "0.0.0.0"
port: 7680
read_timeout: 30s
write_timeout: 120s
storage:
postgres:
host: localhost
port: 5432
database: majordomo
max_conns: 20
logging:
body_storage: "none" # "none", "postgres", or "s3"
s3:
enabled: true
bucket: "majordomo-logs"
region: "us-east-1"
pricing:
remote_url: "https://www.llm-prices.com/current-v1.json"
refresh_interval: 1h| Variable | Description |
|---|---|
MAJORDOMO_STORAGE_POSTGRES_HOST |
PostgreSQL host |
MAJORDOMO_STORAGE_POSTGRES_PORT |
PostgreSQL port |
MAJORDOMO_STORAGE_POSTGRES_USER |
PostgreSQL user |
MAJORDOMO_STORAGE_POSTGRES_PASSWORD |
PostgreSQL password |
MAJORDOMO_STORAGE_POSTGRES_DATABASE |
PostgreSQL database |
MAJORDOMO_S3_ACCESS_KEY_ID |
AWS/S3 access key |
MAJORDOMO_S3_SECRET_ACCESS_KEY |
AWS/S3 secret key |
| Header | Required | Description |
|---|---|---|
X-Majordomo-Key |
Yes | Your Majordomo API key (mdm_sk_...), validated against the database |
X-Majordomo-Provider |
No | Force a specific provider (openai, anthropic, gemini) |
X-Majordomo-* |
No | Custom metadata (stored with request log) |
Authorization |
Yes | Upstream provider API key (Bearer sk-...) |
The gateway automatically detects the provider from the request path:
| Path pattern | Provider |
|---|---|
/v1/chat/completions |
OpenAI |
/v1/messages |
Anthropic |
*generateContent* |
Gemini |
Override with X-Majordomo-Provider header if needed.
Attach metadata to requests for analytics:
curl -X POST http://localhost:7680/v1/chat/completions \
-H "X-Majordomo-Key: mdm_sk_your_key_here" \
-H "X-Majordomo-User-Id: user_123" \
-H "X-Majordomo-Feature: chat" \
-H "X-Majordomo-Environment: production" \
...Metadata is stored in the raw_metadata JSONB column.
Create and manage API keys using the majordomo keys command:
# Create a new API key
majordomo keys create --name "Production API"
majordomo keys create --name "Dev Key" --description "For local development"
# List all API keys
majordomo keys list
# Get details for a specific key
majordomo keys get <key-id>
# Update key metadata
majordomo keys update <key-id> --name "New Name"
majordomo keys update <key-id> --description "Updated description"
# Revoke a key (permanent)
majordomo keys revoke <key-id>API keys use the format mdm_sk_<random>. The plaintext key is only shown once at creation time - store it securely. Keys are validated on every request and cached in memory for 5 minutes.
The quickest way to run the gateway with PostgreSQL:
cp .env.example .env
# Edit .env — set MAJORDOMO_STORAGE_POSTGRES_PASSWORD at minimum
make compose-up # or: docker compose up --build -dThe gateway is available at http://localhost:7680. The database schema is applied automatically on first start.
# Verify it's running
curl http://localhost:7680/readyz # {"status":"ok"}
# Stop everything
make compose-downIf you already have a PostgreSQL instance:
docker build -t majordomo-gateway .
docker run -p 7680:7680 \
-e MAJORDOMO_STORAGE_POSTGRES_HOST=host.docker.internal \
-e MAJORDOMO_STORAGE_POSTGRES_USER=postgres \
-e MAJORDOMO_STORAGE_POSTGRES_PASSWORD=secret \
majordomo-gateway| Endpoint | Purpose | Healthy | Unhealthy |
|---|---|---|---|
GET /health |
Liveness probe | 200 ok |
— |
GET /readyz |
Readiness probe (pings DB) | 200 {"status":"ok"} |
503 {"status":"error",...} |
┌─────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ Client │────▶│ Majordomo Gateway│────▶│ LLM Provider │
└─────────────┘ └──────────────────┘ └─────────────────┘
│
┌───────────┴───────────┐
▼ ▼
┌──────────────┐ ┌─────────────┐
│ PostgreSQL │ │ S3 │
│ (logs, costs,│ │ (request/ │
│ metadata) │ │ response │
└──────────────┘ │ bodies) │
└─────────────┘
- Client sends request with
X-Majordomo-Keyheader - Gateway validates the API key against the database (returns 401 if invalid/revoked)
- Gateway detects provider from path or
X-Majordomo-Providerheader - Request is forwarded to upstream provider
- Response is parsed for token usage
- Cost is calculated using current pricing data
- Request log is written to PostgreSQL asynchronously (linked to API key)
- (Optional) Full request/response bodies stored in S3
- Response is returned to client
The gateway uses three tables:
api_keys- Majordomo API keys with hashes, status, and usage countsllm_requests- Request logs with token counts, costs, and metadata (referencesapi_keys)llm_requests_metadata_keys- Tracks metadata keys for selective indexing
See schema.sql for the full schema.
make build # Build binary
make run # Build and run
make test # Run tests
make test-cover # Run tests with coverage
make lint # Run linter
make fmt # Format code
make compose-up # Start gateway + postgres via Docker Compose
make compose-down # Stop compose stackMIT License - see LICENSE for details.