From 121c49b35b8254a816eadf57c0ca6c49224f22c0 Mon Sep 17 00:00:00 2001 From: Gronod Date: Sun, 17 May 2026 10:06:43 +0100 Subject: [PATCH] docs: update ARCHITECTURE.md and README for 1.0.x fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ARCHITECTURE.md: - Cookie secure flag: NODE_ENV → TRUST_PROXY (3 locations) - upgrade-insecure-requests: document it gates on TRUST_PROXY not NODE_ENV - Docker image note: NODE_ENV=production no longer implies secure cookies - Security checklist: clarify TRUST_PROXY enables secure cookie + CSP + HSTS - dashboard.js route table: add /stream endpoint note - NODE_ENV env var table: correct description README.md: - qBittorrent availability: note red highlight when < 100% - Login side-effects: secure cookie gated on TRUST_PROXY not NODE_ENV --- README.md | 2 +- docs/ARCHITECTURE.md | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 3154fa6..3e60666 100644 --- a/README.md +++ b/README.md @@ -263,7 +263,7 @@ sofarr polls all configured services in the background and caches the results. D ### For qBittorrent Downloads - **Seeds** - Number of seeders - **Peers** - Number of peers -- **Availability** - Percentage available in swarm +- **Availability** - Percentage available in swarm (shown in red when below 100%) ## API Endpoints diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index d588c67..c1a80e9 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -203,7 +203,7 @@ sofarr/ | Module | Mount Point | Auth Required | CSRF Required | Purpose | |--------|------------|:-------------:|:-------------:|--------| | `auth.js` | `/api/auth` | No | No | Login, session check, CSRF token, logout | -| `dashboard.js` | `/api/dashboard` | Yes (`requireAuth`) | Yes | Aggregated download data, status, cover-art proxy | +| `dashboard.js` | `/api/dashboard` | Yes (`requireAuth`) | Yes (except `/stream` — GET) | SSE stream, aggregated download data, status, cover-art proxy | | `emby.js` | `/api/emby` | Yes (`requireAuth`) | Yes | Proxy to Emby API | | `sabnzbd.js` | `/api/sabnzbd` | Yes (`requireAuth`) | Yes | Proxy to SABnzbd API | | `sonarr.js` | `/api/sonarr` | Yes (`requireAuth`) | Yes | Proxy to Sonarr API | @@ -293,7 +293,7 @@ For each connected user the server: 6. Sets an `httpOnly` `emby_user` cookie containing only `{ id, name, isAdmin }`: - **`rememberMe: true`** → persistent cookie, `Max-Age` 30 days - **`rememberMe: false`** → session cookie (no `Max-Age`; expires when browser closes) - - `secure` flag enabled when `NODE_ENV=production` + - `secure` flag enabled when `TRUST_PROXY` is set (i.e. a TLS-terminating reverse proxy is in front) - Signed with HMAC when `COOKIE_SECRET` is set 7. Issues a `csrf_token` cookie (`httpOnly: false` so JS can read it) containing a 32-byte random hex token 8. Returns `{ success: true, user, csrfToken }` — the SPA stores `csrfToken` in memory and sends it as `X-CSRF-Token` on all subsequent state-changing requests @@ -657,7 +657,7 @@ The status panel refreshes on a fixed 5-second timer and shows each SSE client w | Variable | Required | Default | Description | |----------|:--------:|---------|-------------| | `PORT` | No | `3001` | Server listen port | -| `NODE_ENV` | No | — | Set to `production` to enable `secure` cookies and HTTPS upgrades | +| `NODE_ENV` | No | — | Set to `production` for production logging and startup validation. Does **not** control cookie `secure` flag or CSP `upgrade-insecure-requests` (both gated on `TRUST_PROXY`). | | `DATA_DIR` | No | `./data` | Directory for `tokens.json` and `server.log`. Must be writable by the server process. In Docker: `/app/data` (named volume). | | `COOKIE_SECRET` | No | — | If set, signs all session cookies with HMAC-SHA256. Strongly recommended in production. | | `TRUST_PROXY` | No | — | Express `trust proxy` setting. Set to `1` (or a hop count) when behind a reverse proxy (nginx, Traefik, etc.) so `req.ip` and `req.secure` are correct. | @@ -724,7 +724,7 @@ The production image uses a two-stage build on `node:22-alpine`: 2. **`runtime` stage** — copies `node_modules`, `server/`, `public/`, and `package.json`. Runs as the built-in non-root `node` user (UID 1000). `/app/data` is owned by `node` for writable token store and logs. Key environment variables set in the image: -- `NODE_ENV=production` — enables secure cookies and HTTPS upgrade CSP directive +- `NODE_ENV=production` — enables production startup validation and logging - `DATA_DIR=/app/data` — token store and log file location ### Docker Compose @@ -762,7 +762,7 @@ volumes: - **Set `COOKIE_SECRET`** — enables HMAC-signed cookies, preventing client-side forgery. - **Set `TRUST_PROXY=1`** when behind a reverse proxy — ensures `req.secure` is `true` so the `secure` cookie flag is enforced and HTTPS-upgrade CSP fires. - **Mount a named volume** for `DATA_DIR` — token store and log file survive container recreates. -- **Use HTTPS** — the CSP includes `upgrade-insecure-requests` in production and the HSTS header is set with a 1-year `maxAge`. +- **Use HTTPS** — set `TRUST_PROXY=1` to enable the CSP `upgrade-insecure-requests` directive, the `secure` cookie flag, and HSTS (1-year `maxAge`). - **Rate limiting** — the login endpoint is limited to 10 failed attempts per IP per 15 minutes; all API endpoints share a 300 req/15 min window. Set `TRUST_PROXY` correctly so the client IP is not the proxy's IP. ### CI / CD