Security and production
Before using it officially, go through this page from start to finish. Most incidents come from two things: keys not backed up, or services exposed to the public network.
Two keys §
GPT-Load uses two keys for completely different purposes:
| Key | Function | What happens if lost |
|---|---|---|
| AUTH_KEY | Log into the management UI | Just change one key, data is unaffected |
| ENCRYPTION_KEY | Upstream credentials stored encrypted | Credentials that have been encrypted are permanently irrecoverable |
Set both keys explicitly in .env, or let the first startup generate auth.key and encryption.key in the data directory.
This release does not provide a way to replace ENCRYPTION_KEY. If the key is changed or lost, stored channel credentials cannot be decrypted and must be re-entered. In other words, this key should be treated like the database.
Backup §
Back up the database together with its encryption key. Auto-generated auth.key and encryption.key are stored in the data directory; explicitly configured AUTH_KEY and ENCRYPTION_KEY must be backed up separately from their original environment variables or secrets manager.
SQLite uses WAL, so do not archive the data volume while the service is running. The actual Compose volume name also changes with the project name and must not be hard-coded as gpt-load-data. Follow the complete procedure in Database and backup.
The backup file itself contains decryptable credentials. Treat it as sensitive data: store it encrypted, and do not put it in a public cloud drive or repository.
See Database and backup before changing database drivers or moving servers.
Network boundary §
The service listens only on 127.0.0.1, so only the local machine can reach it. This is intentional: if the management UI is exposed publicly, anyone with AUTH_KEY can reveal every upstream credential.
Changing HOST to 0.0.0.0 and publishing the port directly on a public IP is common and dangerous. The correct approach is via a reverse proxy, with TLS and access control added.
For remote access, choose from lowest to highest risk:
- SSH Port Forwarding — Safest; no service configuration changes are required:
ssh -L 3001:127.0.0.1:3001 user@your-server
# Then open http://127.0.0.1:3001 in the local browser - Intranet / VPN — Expose the service only inside a controlled network.
- Reverse proxy + TLS + access control — Use this only when public access is required; see the next section.
Reverse proxy §
Put Nginx, Caddy, or another reverse proxy in front and do at least three things: Enable HTTPS, restrict sources, do not expose management UI and data plane together.
server {
listen 443 ssl;
server_name gateway.example.com;
# Certificate configuration omitted
# Management side: only allow trusted sources
location /api/ {
allow 203.0.113.0/24;
deny all;
proxy_pass http://127.0.0.1:3001;
}
# Data side: open as needed, note that streaming responses should disable buffering
location / {
proxy_pass http://127.0.0.1:3001;
proxy_buffering off;
proxy_read_timeout 600s;
}
}proxy_buffering off and a sufficiently long read timeout are required. If the proxy buffers a streaming response, the client receives no incremental output.
By default, client IP is the connection peer. Behind a reverse proxy, configure CLIENT_IP_HEADER and restrict TRUSTED_PROXIES to the actual proxy IPs/CIDRs, then restart. Do not directly trust client-supplied headers. The resolved address is shared by logs, IP restrictions and security events.
With a trusted proxy list, X-Forwarded-For is scanned right to left, skipping trusted proxies and selecting the first untrusted address, or the leftmost when all are trusted. Missing or invalid headers fall back to the connection address. Empty TRUSTED_PROXIES directly trusts the selected header (the leftmost X-Forwarded-For address); use this only when the ingress proxy guarantees overwriting or sanitizing it.
File Permissions §
Managed data directories are tightened to permissions appropriate for the runtime identity. This is handled automatically at startup and normally needs no intervention:
- Data directory:
0700(owner read/write/execute only). - Database file and both keys:
0600(owner read/write only). - On Windows, foreground runs use the current user's ACL; installer-managed services use the service SID and local Administrators ACL.
If directory ownership or link type cannot be verified, the program will refuse to start instead of weakening its checks. This prevents credentials from being written to a location with unknown permissions.
Secrets to protect §
To hide sensitive text before a business request leaves the gateway, or decide whether to forward it using rules, configure the two features below. They do not replace this page's access-control and backup requirements.
- Request redaction: fixed replacement and reversible encryption
- JEV AI Guardrails: rules, warnings, and blocking
- Two keys — Never commit them, paste them into an issue, or post them in a chat group.
- Backup files — They contain credentials that can be decrypted.
- Screenshot — Management UI captures may include account emails and key suffixes; redact them before sharing.
- AccessKey — It can be revoked, but usage incurred during a leak still costs money.
Report security issues privately through SECURITY.md and do not open public issues.
Original request body recording is off by default. When enabled, it stores client content before request redaction and may contain personal information, passwords, keys, or trade secrets; redaction rules do not protect this record. Administrators and the corresponding key holders can view it, and databases and backups must be protected as sensitive content. Enable it only with authorization to record and after notifying users, then turn it off when finished. Saved records still follow log retention.
Production checklist §
Verify every item before sending production traffic:
- Key — Set
AUTH_KEYandENCRYPTION_KEYexplicitly, or verify that their generated values are backed up. - Backup — Back up the database with
encryption.keyand verify that it can be restored. - Network — Do not bind the service directly to
0.0.0.0on the public internet. Use SSH forwarding, a private network, or a TLS reverse proxy. - Management UI — Restrict access to
/apiby source; do not expose it publicly. - AccessKey — Issue one per application with appropriate rate and cost limits.
- Monitoring — Know where to inspect health and request logs; see Monitoring and troubleshooting.
- Boundary awareness — Understand that cost is estimated and 2.0 targets single-instance deployments.
2.0 guarantees correctness only for a single application instance and does not share state across instances. Do not place a load balancer across replicas: scheduling, cooldown, and rate limiting would be calculated independently. For more scale, create separate deployments by workload.