Majordomo Gateway
A lightweight LLM API gateway that proxies requests to upstream providers (OpenAI, Anthropic, Google Gemini), logs usage metrics, and calculates costs.
Features
- 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
Quick Start
Prerequisites
- Go 1.25+
- PostgreSQL 14+
- (Optional) S3-compatible storage for body logging
1. Clone and build
2. Set up the database
3. Configure
Copy the example environment file and edit it:
4. Create an API key
Save the returned key (it won't be shown again). Keys have the format mdm_sk_....
5. Run
The gateway starts on http://localhost:7680 by default.
6. Make a request
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
Configuration is loaded from majordomo.yaml (or /etc/majordomo/majordomo.yaml). Environment variables with MAJORDOMO_ prefix override config file values.
Example configuration
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
Environment variables
| 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 |
Usage
Headers
| 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-...) |
Provider detection
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.
Custom metadata
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.
CLI Commands
API Key Management
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.
Docker
Build
Run
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
Architecture
┌─────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ Client │────▶│ Majordomo Gateway│────▶│ LLM Provider │
└─────────────┘ └──────────────────┘ └─────────────────┘
│
┌───────────┴───────────┐
▼ ▼
┌──────────────┐ ┌─────────────┐
│ PostgreSQL │ │ S3 │
│ (logs, costs,│ │ (request/ │
│ metadata) │ │ response │
└──────────────┘ │ bodies) │
└─────────────┘
Request flow
- 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
Database schema
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
Development
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
License
MIT License