Files
dotcom-arcade/docs/09-deployment/environment-variables.md

306 lines
14 KiB
Markdown

# Environment Variables
Configuration reference for all Dotcom Arcade services.
## Backend Variables
Location: `backend/.env` (local development)
**Server:**
| Variable | Default | Description |
| ---------- | ----------- | ------------------------------------------ |
| `NODE_ENV` | development | Environment: development, production, test |
> **Note:** Port (3100) and bind address (0.0.0.0) are hardcoded in the backend.
> In Docker, use `API_PORT` to map a different host port to the container.
**Paths:**
| Variable | Default | Description |
| ---------------------- | ------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| `FLASHPOINT_PATH` | `D:/Flashpoint` (dev), `/data/flashpoint` (prod) | Root Flashpoint directory. All other paths auto-derived. Edition (Infinity/Ultimate) auto-detected from `version.txt`. |
| `FLASHPOINT_GAMES_PATH`| `<FLASHPOINT_PATH>/Data/Games` | Override game ZIP directory (rarely needed). |
| `APP_VERSION` | set by the Docker build | Release version the server reports on Settings → Update. Baked into the official image from the release tag; set it by hand for a non-Docker deployment, or update checks stay off. |
**Authentication & Security:**
| Variable | Default | Description |
| -------------------- | --------------------- | ---------------------------------------------------- |
| `JWT_SECRET` | auto-generated (dev) | Secret for JWT signing (**required in production**) |
| `JWT_EXPIRES_IN` | 1h | Access token expiration (15m, 1h, 7d, etc.) |
| `BCRYPT_SALT_ROUNDS` | 10 | Password hash cost (higher = more secure but slower) |
| `DOMAIN` | http://localhost:5173 | Allowed CORS origin(s), comma-separated (see below) |
| `COOKIE_SECURE` | auto | Secure flag on auth cookies: `auto`, `true`, `false` |
| `UV_THREADPOOL_SIZE` | 16 | Concurrent file I/O threads (Node's own default is 4) |
| `DB_WORKER_COUNT` | 4 | Read-only SQLite query workers (1-16) |
Generate secure JWT secret:
```bash
openssl rand -hex 64
```
**COOKIE_SECURE — auth cookies over HTTP and HTTPS:**
Browsers discard a cookie marked `Secure` when it arrives over plain HTTP, which
logs users straight back out. `auto` therefore decides per request: cookies are
marked `Secure` only on connections that actually used TLS, so one deployment can
serve a LAN over HTTP and the internet over HTTPS at the same time.
Detection reads `X-Forwarded-Proto`, which is trusted only from a proxy. Set
`COOKIE_SECURE=true` if your TLS-terminating proxy does not send that header, and
`false` only if you deliberately want the flag off everywhere.
**DOMAIN — allowed origins:**
`DOMAIN` accepts one origin or a comma-separated list, so a single deployment can
be reached over several addresses:
```bash
DOMAIN=http://192.168.0.118,https://flashpoint.example.com
```
The first entry is the primary origin, used where a single value is required.
Requests from any listed origin are accepted, as are requests from hostnames in
the domains table.
Two notes that save debugging time:
- Same-origin requests are always allowed, so `SERVE_FRONTEND=true` deployments
work at any address without listing it here. You only need `DOMAIN` when the UI
is served from a different origin than the API, or behind a reverse proxy that
rewrites the `Host` header.
- Default ports are normalized away: `http://host:80` and `http://host` are
treated as the same origin, as are `https://host:443` and `https://host`.
Non-default ports must be written out, for example `http://host:8080`.
**Rate Limiting:**
| Variable | Default | Description |
| ------------------------- | ------- | ----------------------- |
| `RATE_LIMIT_WINDOW_MS` | 60000 | Time window (ms) |
| `RATE_LIMIT_MAX_REQUESTS` | 100 | Max requests per window |
**Logging:**
| Variable | Default | Description |
| ----------- | ------------------------------ | -------------------------------------------- |
| `LOG_LEVEL` | info | Level: error, warn, info, debug |
| `LOG_FILE` | `/app/logs/backend.log` (prod) | Log file path (unset in dev = stdout only) |
**Database Performance:**
| Variable | Default | Description |
| --------------------- | ----------- | ---------------------------------------------------------------- |
| `ENABLE_LOCAL_DB_COPY`| false | Copy flashpoint.sqlite locally (for network storage SMB/NFS) |
| `SQLITE_CACHE_SIZE` | -64000 | SQLite page cache size (negative = KB, -64000 = 64MB) |
| `SQLITE_MMAP_SIZE` | 268435456 | Memory-mapped I/O size in bytes (256MB) |
| `ENABLE_CACHE_PREWARM`| true | Pre-load common queries on startup |
**Game Content Serving:**
| Variable | Default | Description |
| -------------------- | ------- | ------------------------------------------------ |
| `ENABLE_CGI` | false | Enable PHP CGI execution for legacy game content |
**Frontend Serving:**
The backend serves the built React UI in the single-image deployment, so the app
and API share one origin.
| Variable | Default | Description |
| -------------------- | ----------------------------- | -------------------------------------------------------------- |
| `SERVE_FRONTEND` | true in production, else false | Serve the built frontend and SPA fallback from the backend |
| `FRONTEND_DIST_PATH` | `<backend>/../frontend/dist` | Location of the built frontend (rarely needed) |
| `RUFFLE_DATA_PATH` | `/app/data/ruffle` in production | Where Ruffle is installed. Must sit on a mounted volume, or an in-app update is lost when the container is recreated |
**Images and updates:**
| Variable | Default | Description |
| ---------------- | -------- | ------------------------------------------------------------------------------------------------- |
| `IMAGE_PREFIX` | - | Registry path to pull images from, ending in `/`. Unset builds them from source |
| `IMAGE_TAG` | `latest` | Image tag to build or pull |
| `APP_UPDATE_URL` | - | "Latest release" API URL (GitHub or Gitea) that Settings checks for new versions; off when unset |
**Streaming:**
Games that can't run in a browser, such as Java applets and Shockwave, can be
streamed by the session manager under `streaming/`. It runs under the
`streaming` compose profile. See [Streaming](../10-features/13-streaming.md).
| Variable | Default | Description |
| --------------------- | ----------------------------- | --------------------------------------------------------------- |
| `STREAMING_API_URL` | `http://session-manager:8080` | Session manager's internal API |
| `STREAMING_STREAM_URL` | `http://session-manager:8081` | Where the app proxies `/stream/` to |
| `STREAMING_API_TOKEN` | - | Token shared with the session manager. Streaming is off without it |
**Trusted Header Login:**
See also [Proxy Sign-In](../10-features/14-proxy-sign-in.md).
When a reverse proxy such as Authentik (forward auth) authenticates users in
front of flashpoint-web, it can pass the signed-in username in a header so
users don't log in twice. The backend believes the header only on connections
from `TRUSTED_PROXIES`, checked against the connection's own address, not
`X-Forwarded-For`. The proxy must always set the header, overwriting anything a
client sends, and flashpoint-web must not be reachable from untrusted networks
except through it.
Users are matched by username, so an existing account of that name is signed
in; otherwise an account is created (the first one as admin, as with
registration). New accounts get a random password. While signed in through the
proxy, users can set a password from Settings without knowing the current one,
then log in with it when reaching flashpoint-web without the proxy, such as on a
LAN.
| Variable | Default | Description |
| ------------------------- | ------- | ------------------------------------------------------------------------ |
| `TRUSTED_HEADER_USERNAME` | - | Header holding the username, e.g. `X-authentik-username` |
| `TRUSTED_HEADER_EMAIL` | - | Optional header holding the email used for new accounts |
| `TRUSTED_PROXIES` | - | Comma-separated proxy addresses or CIDR ranges allowed to set the header |
With nginx and Authentik's forward auth, each protected `location` needs these
lines once. Authentik's generated nginx snippet already includes them; adding
them again sends each header twice:
```nginx
auth_request_set $authentik_username $upstream_http_x_authentik_username;
auth_request_set $authentik_email $upstream_http_x_authentik_email;
proxy_set_header X-authentik-username $authentik_username;
proxy_set_header X-authentik-email $authentik_email;
```
**OpenTelemetry:**
| Variable | Default | Description |
| ------------------------------ | -------------------------- | ---------------------------------------- |
| `OTEL_ENABLED` | false | Enable distributed tracing and metrics |
| `OTEL_EXPORTER_OTLP_ENDPOINT` | http://localhost:4318 | OTLP collector endpoint |
| `OTEL_API_KEY` | - | API key for authentication |
| `OTEL_SERVICE_NAME` | flashpoint-web | Service name in traces/metrics |
| `OTEL_TRACES_ENABLED` | true | Enable trace export (when OTEL enabled) |
| `OTEL_METRICS_ENABLED` | true | Enable metrics export (when OTEL enabled)|
| `OTEL_METRICS_EXPORT_INTERVAL`| 60000 | Metrics export interval (ms) |
| `OTEL_LOG_LEVEL` | info | OTEL SDK log level |
## Frontend Variables
The frontend has no environment variables of its own. API calls use relative
paths (`/api`), so they resolve against whatever origin serves the app.
- **Development**: Vite dev server on port 5173 proxies `/api` to `http://localhost:3100`
- **Production**: The backend serves the built UI and the API from the same origin
## Docker Environment Variables
Location: `.env` in project root (used by docker-compose)
**Required:**
| Variable | Description |
| ---------------------- | --------------------------------------------------------- |
| `FLASHPOINT_HOST_PATH` | Host path to Flashpoint installation (bind-mounted as ro) |
| `JWT_SECRET` | Secret for JWT signing (generate with `openssl rand -hex 64`) |
**Port Mapping:**
| Variable | Default | Description |
| ---------- | ------- | ------------------------------------------------------ |
| `WEB_PORT` | 80 | Host port mapped to the container (serves UI + API) |
**Container Settings:**
| Variable | Default | Description |
| ----------- | ------- | ---------------------------------------------------- |
| `PUID` | 1000 | Container user ID (match host user: `id -u`) |
| `PGID` | 1000 | Container group ID (match host user: `id -g`) |
| `TZ` | UTC | Container timezone |
| `IMAGE_TAG` | latest | Docker image tag (production compose only) |
| `DATA_PATH` | ./data | Host path for persistent data (production compose) |
| `LOGS_PATH` | ./logs | Host path for log files |
All backend variables listed above are also passable via docker-compose. See
`docker-compose.yml` for the full list with defaults.
## Docker Compose Setup
**Create .env file:**
```bash
cat > .env << EOF
FLASHPOINT_HOST_PATH=/path/to/flashpoint
JWT_SECRET=$(openssl rand -hex 64)
DOMAIN=https://flashpoint.example.com
WEB_PORT=80
LOG_LEVEL=warn
EOF
```
**Start services:**
```bash
docker compose up -d
```
## Environment Templates
**Development (`backend/.env`):**
```bash
NODE_ENV=development
FLASHPOINT_PATH=D:/Flashpoint
JWT_SECRET=development-secret-change-in-production
DOMAIN=http://localhost:5173
RATE_LIMIT_MAX_REQUESTS=1000
LOG_LEVEL=debug
```
**Production (`.env` for Docker):**
```bash
FLASHPOINT_HOST_PATH=/data/flashpoint
JWT_SECRET=CHANGE-THIS-TO-A-RANDOM-64-CHARACTER-STRING
DOMAIN=https://flashpoint.example.com
WEB_PORT=80
LOG_LEVEL=warn
```
## Validation and Defaults
**Required Variables:**
**Backend (local dev):**
- `FLASHPOINT_PATH` (only this is required; others auto-derive)
**Docker Production:**
- `FLASHPOINT_HOST_PATH` (must point to Flashpoint installation)
- `JWT_SECRET` (must be changed from default)
**Verify configuration:**
```bash
cd backend
node -e "require('dotenv').config(); console.log(process.env.FLASHPOINT_PATH)"
```
## Security Best Practices
1. Never commit .env files to version control
2. Use strong JWT secrets (64+ characters) in production
3. Restrict CORS origins to your domain only (no wildcards)
4. Use HTTPS in production (update DOMAIN to https://)
5. Limit exposed ports (don't expose all services)
6. Rotate secrets periodically
7. Use environment-specific files (.env.production, .env.development)
8. Store secrets in secure management system (AWS Secrets Manager, HashiCorp
Vault)
9. Use read-only mounts for Flashpoint data in Docker
## Next Steps
- [Docker Deployment](./docker-deployment.md)
- [Security Considerations](./security-considerations.md)