- Project Name: Gemini Proxy Gateway
- Type: Secure API Gateway / Reverse Proxy
- Core Functionality: A Go-based gateway that proxies requests to Google's Gemini API while hiding the master API key, providing per-client authentication, rate limiting, usage quotas, and an admin web interface.
- Target Users: Developers who want to provide Gemini API access to multiple clients/applications without exposing their master API key.
┌─────────────┐ ┌─────────────────┐ ┌─────────────┐
│ Client │─────>│ Gemini Proxy │─────>│ Gemini API │
│ (App/User) │ │ Gateway │ │ (Google) │
└─────────────┘ └─────────────────┘ └─────────────┘
│
▼
┌─────────────────┐
│ Admin Web UI │
│ (Dashboard) │
└─────────────────┘
Config File: config.yaml
server:
host: "0.0.0.0"
port: 8080
# Auto HTTPS configuration
https:
enabled: false
cert_file: ""
key_file: ""
# Let's Encrypt auto TLS
letsencrypt:
enabled: false
domain: ""
email: ""
admin:
username: "admin"
# Password hashed with bcrypt
password_hash: "$2y$10$..."
# Session secret for cookies
session_secret: "change-me-in-production"
gemini:
# Master API key (hidden from clients)
api_key: "AIza..."
# Default model if not specified
default_model: "gemini-2.0-flash"
# Allowed models (whitelist)
allowed_models:
- "gemini-2.0-flash"
- "gemini-2.0-flash-lite"
- "gemini-pro"
- "gemini-pro-vision"
# Timeout for upstream requests
timeout_seconds: 120
# Default limits for new clients
defaults:
rate_limit:
requests_per_minute: 60
requests_per_hour: 1000
requests_per_day: 10000
quota:
max_input_tokens_per_day: 1000000
max_output_tokens_per_day: 500000
max_requests_per_day: 1000
# Database
database:
type: "sqlite" # or "postgres"
path: "./data/gateway.db"
# For postgres:
# host: "localhost"
# port: 5432
# username: "postgres"
# password: ""
# database: "gemini_gateway"
# Logging
logging:
level: "info" # debug, info, warn, error
file: "./logs/gateway.log"- Each client gets a unique API key (UUID v4 format:
gm_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx) - Keys are stored as SHA-256 hashes in the database
- API keys are shown only once at creation time
- Keys can be rotated/regenerated by admins
- Session-based authentication with secure cookies
- Passwords stored as bcrypt hashes
- Session timeout: 24 hours
- CSRF protection on all admin forms
Per-client rate limits stored in database:
- Requests per minute (burst limit)
- Requests per hour
- Requests per day
Implementation: Token bucket algorithm with in-memory caching for performance.
Per-client quotas:
- Max input tokens per day
- Max output tokens per day
- Max requests per day
Token counting: Parse Gemini response to extract token usage counts.
Endpoint: POST /v1beta/models/{model}:generateContent
Request Flow:
- Client sends request with
Authorization: Bearer {api_key} - Gateway validates API key, checks rate limits and quotas
- Gateway forwards request to Gemini API with master key
- Gateway logs usage stats
- Gateway returns response to client
Request Headers:
Authorization: Bearer {client_api_key}X-Forwarded-For(for IP-based limiting if needed)
Request Body: Forwarded as-is to Gemini API
Response Body: Forwarded as-is from Gemini API
Base URL: /admin
Pages:
-
Login Page (
/admin/login)- Username/password form
- CSRF protection
-
Dashboard (
/admin/dashboard)- Total requests today
- Total tokens used today
- Active clients count
- Error rate
- Recent activity log
-
Clients (
/admin/clients)- List all clients with:
- Client name/description
- API key (masked, with reveal button)
- Rate limits
- Quota usage
- Created date
- Status (active/disabled)
- Create new client form
- Edit client form
- Delete client (with confirmation)
- Regenerate API key
- List all clients with:
-
Client Detail (
/admin/clients/{id})- Detailed statistics
- Usage history (charts)
- Request logs
-
Settings (
/admin/settings)- Server configuration
- Gemini API key (masked)
- Default limits
UI Framework: TailwindCSS with vanilla Go templates (no heavy JS frameworks)
- Per-client usage tracking
- Per-model usage tracking
- Daily/weekly/monthly aggregations
- Real-time dashboard updates
- HTTP -> HTTPS redirect (when HTTPS enabled)
- HSTS headers
- TLS 1.3 only (when available)
- All client API keys hashed with SHA-256
- No API key logging
- Request/response sanitization
- Input validation
- Session-based auth with secure cookies
- CSRF tokens on all forms
- Password hashing with bcrypt (cost factor 10+)
- Session timeout
- Login rate limiting (prevent brute force)
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: geolocation=(), microphone=(), camera=()
- Model whitelist enforcement
- Max request size limit (10MB)
- Timeout on upstream requests
- All admin actions logged
- All API requests logged (with truncated details)
- Error logging with stack traces
CREATE TABLE clients (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
description TEXT,
api_key_hash TEXT NOT NULL UNIQUE,
is_active BOOLEAN DEFAULT true,
rate_limit_minute INTEGER DEFAULT 60,
rate_limit_hour INTEGER DEFAULT 1000,
rate_limit_day INTEGER DEFAULT 10000,
quota_input_tokens_day INTEGER DEFAULT 1000000,
quota_output_tokens_day INTEGER DEFAULT 500000,
quota_requests_day INTEGER DEFAULT 1000,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);CREATE TABLE request_logs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
client_id TEXT NOT NULL,
model TEXT NOT NULL,
status_code INTEGER,
input_tokens INTEGER DEFAULT 0,
output_tokens INTEGER DEFAULT 0,
latency_ms INTEGER,
error_message TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (client_id) REFERENCES clients(id)
);CREATE TABLE daily_usage (
id INTEGER PRIMARY KEY AUTOINCREMENT,
client_id TEXT NOT NULL,
date DATE NOT NULL,
total_requests INTEGER DEFAULT 0,
total_input_tokens INTEGER DEFAULT 0,
total_output_tokens INTEGER DEFAULT 0,
UNIQUE(client_id, date),
FOREIGN KEY (client_id) REFERENCES clients(id)
);| Method | Path | Description |
|---|---|---|
| POST | /v1beta/models/:model:generateContent | Proxy to Gemini |
| GET | /v1beta/models | List available models |
| GET | /admin | Admin dashboard |
| GET | /admin/login | Admin login page |
| POST | /admin/login | Admin login action |
| POST | /admin/logout | Admin logout |
| GET | /admin/clients | List clients |
| POST | /admin/clients | Create client |
| GET | /admin/clients/:id | Client details |
| PUT | /admin/clients/:id | Update client |
| DELETE | /admin/clients/:id | Delete client |
| POST | /admin/clients/:id/regenerate | Regenerate API key |
| GET | /admin/stats | Statistics API |
- Proxy Functionality: Client can make requests to Gemini API using their API key
- Security: Master API key is never exposed to clients
- Rate Limiting: Requests are properly limited per client
- Quotas: Daily token and request quotas are enforced
- Admin UI: Can create, edit, delete clients
- Stats: Dashboard shows accurate usage statistics
- HTTPS: Auto HTTPS works with Let's Encrypt (optional)
- Production Ready: Proper error handling, logging, security headers
- Language: Go 1.21+
- Web Framework: Chi (lightweight, middleware support)
- Database: SQLite (default) or PostgreSQL
- ORM: GORM
- Session: SCS (secure cookie sessions)
- HTTPS: certmagic (Let's Encrypt)
- Templating: Go html/template
- CSS: TailwindCSS (CDN for simplicity)
- Charts: Chart.js
gemini-proxy/
├── cmd/
│ └── server/
│ └── main.go
├── internal/
│ ├── config/
│ │ └── config.go
│ ├── models/
│ │ └── models.go
│ ├── handlers/
│ │ ├── proxy.go
│ │ └── admin.go
│ ├── middleware/
│ │ ├── auth.go
│ │ ├── ratelimit.go
│ │ └── security.go
│ ├── services/
│ │ ├── gemini.go
│ │ ├── client.go
│ │ └── stats.go
│ └── templates/
│ └── admin.go
├── config.yaml
├── go.mod
├── go.sum
└── README.md