This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
Configuration
Configuration is read from environment variables. loadConfig() validates critical values and fails fast instead of starting in an insecure or broken state.
Required
| Variable | Description |
|---|---|
JWT_SECRET |
Required. At least 32 characters. Used to sign HS256 access tokens. |
If no database URL or PostgreSQL credentials are supplied, the backend uses SQLite at DATABASE_PATH.
Server
| Variable | Default | Description |
|---|---|---|
NODE_ENV |
development |
Runtime mode label |
HOST |
0.0.0.0 |
Listen address |
PORT |
6767 |
HTTP port |
PUBLIC_BASE_URL |
https://account.games.emilyh.de |
Public backend base URL used in e-mail links |
APP_PUBLIC_URL |
https://games.emilyh.de |
Public web app base URL used in password-reset links |
CORS_ORIGINS |
* |
Comma-separated allowed origins |
REQUEST_LOGGING |
true |
Log one line per request (no bodies, tokens or query strings) |
Database
| Variable | Default | Description |
|---|---|---|
DATABASE_URL |
— | Optional explicit PostgreSQL URL. Overrides POSTGRES_* |
POSTGRES_HOST |
db |
PostgreSQL host |
POSTGRES_PORT |
5432 |
PostgreSQL port |
POSTGRES_DB |
— | Database name |
POSTGRES_USER |
— | Database user |
POSTGRES_PASSWORD |
— | Database password |
DATABASE_PATH |
./data/accounts.db |
SQLite file when PostgreSQL is not configured |
DATABASE_URL is normally built by the backend from POSTGRES_* with encodeURIComponent, so passwords containing @, :, /, ?, # or % work correctly. An explicit DATABASE_URL remains available as an override.
Changing POSTGRES_PASSWORD only initializes a new volume. It does not alter an existing PostgreSQL role. Use ALTER ROLE against the running database, then update the environment. Never delete the data directory to "fix" authentication.
Authentication
| Variable | Default | Allowed | Description |
|---|---|---|---|
JWT_TTL_SECONDS |
2592000 (30 days) |
300–31536000 | Access-token lifetime |
BCRYPT_ROUNDS |
12 |
10–15 | bcrypt cost factor |
REGISTRATION_ENABLED |
true |
boolean | Allow new accounts |
PASSWORD_RESET_TTL_MINUTES |
30 |
5–1440 | Password-reset link lifetime |
Passwords must be 8–128 characters. They are pre-hashed with SHA-256 and then hashed with bcrypt, so the full password is used rather than being silently truncated at bcrypt's 72-byte limit.
Features
| Variable | Default | Description |
|---|---|---|
LEADERBOARD_ENABLED |
true |
Enable competitive leaderboard routes |
CHESS_ENABLED |
true |
Enable online chess routes |
EMAIL_CONFIRMATION_ENABLED |
true |
Mark new e-mail addresses unverified and send confirmation mail |
UPDATE_ENABLED |
true |
Enable the release proxy |
UPDATE_SOURCE_URL |
Gitea releases API for the app | Upstream release feed |
UPDATE_CACHE_SECONDS |
300 |
Release cache lifetime, 30–3600 |
E-mail confirmation is opt-in at deployment level and never blocks account use. If SMTP is not configured, accounts still work and addresses remain unconfirmed.
SMTP
| Variable | Default | Description |
|---|---|---|
SMTP_HOST |
— | SMTP server; enables e-mail when set with SMTP_FROM |
SMTP_PORT |
587 |
SMTP port |
SMTP_SECURE |
inferred | Implicit TLS. Defaults to true only on port 465 |
SMTP_REQUIRE_TLS |
inferred | Require STARTTLS. Defaults to true on 25, 587 and 2525 |
SMTP_USER |
— | SMTP username |
SMTP_PASSWORD |
— | SMTP password |
SMTP_FROM |
— | From address; required to enable e-mail |
TLS rules
- Port 465 → implicit TLS (
secure=true). - Ports 25, 587, 2525 → STARTTLS (
secure=false,requireTls=true). SMTP_SECUREcan override the inferred mode, but settingSMTP_SECURE=trueon a STARTTLS port is automatically corrected to STARTTLS with a warning.- The server logs
[mail] SMTP host=… port=… secure=… requireTLS=…at startup.
A mismatch here is the usual cause of tls_validate_record_header: wrong version number.
Management panel
| Variable | Default | Description |
|---|---|---|
MANAGEMENT_ENABLED |
true |
Enable the panel when a password is set |
MANAGEMENT_USER |
admin |
Basic-auth username |
MANAGEMENT_PASSWORD |
— | Basic-auth password; empty disables the panel |
The panel is a read-only HTML page at /management. It is disabled unless MANAGEMENT_PASSWORD is non-empty. Store the password in a root-only file outside Git.
Proxies and client IPs
| Variable | Default | Description |
|---|---|---|
TRUSTED_PROXIES |
— | Comma-separated IPs, CIDR ranges or presets |
TRUST_PROXY |
false |
Legacy Express trust-proxy fallback |
Accepted TRUSTED_PROXIES entries:
- IPv4 or IPv6 addresses.
- CIDR ranges such as
10.0.0.0/8or2001:db8::/32. - Presets:
loopback,linklocal,uniquelocal.
Invalid entries fail startup. When TRUSTED_PROXIES is set, it takes precedence over TRUST_PROXY.
Correct proxy configuration is required for rate limits and request logs to see the real client IP. Do not trust all proxies on a public server.
Example production environment
NODE_ENV=production
PORT=6767
PUBLIC_BASE_URL=https://account.example.com
APP_PUBLIC_URL=https://games.example.com
CORS_ORIGINS=https://games.example.com
JWT_SECRET=<at-least-32-random-characters>
JWT_TTL_SECONDS=2592000
BCRYPT_ROUNDS=12
POSTGRES_HOST=db
POSTGRES_PORT=5432
POSTGRES_DB=universal_gaming
POSTGRES_USER=universal_gaming
POSTGRES_PASSWORD=<strong-password>
REGISTRATION_ENABLED=true
LEADERBOARD_ENABLED=true
CHESS_ENABLED=true
EMAIL_CONFIRMATION_ENABLED=true
PASSWORD_RESET_TTL_MINUTES=30
SMTP_HOST=mail.example.com
SMTP_PORT=587
SMTP_SECURE=false
SMTP_REQUIRE_TLS=true
SMTP_USER=noreply@example.com
SMTP_PASSWORD=<smtp-password>
SMTP_FROM=noreply@example.com
TRUSTED_PROXIES=10.0.0.0/8
MANAGEMENT_ENABLED=true
MANAGEMENT_USER=admin
MANAGEMENT_PASSWORD=<strong-password>
REQUEST_LOGGING=true
Generate secrets with:
openssl rand -base64 48
Never commit .env, SMTP passwords, the management password or the JWT secret.
Universal Gaming Accounts
- Home
- Deployment
- Configuration
- API Reference
- Database and Migrations
- Security
- Operations
- Development and Testing
- Troubleshooting
Repositories
The accounts backend is optional. Universal Gaming remains fully playable offline without it.