Add notification image proxy, expand README, fix compose env-file handling

- Proxy notification image_url server-side (GET /api/v1/notifications/:id/image)
  instead of loading it directly in the browser, so LAN-only image sources
  (e.g. Frigate snapshots) still load for clients off that network
- Tighten CSP img-src to 'self' now that images are same-origin
- Update threat model / architecture docs to match the new image-fetch design
- Expand README: Home Assistant quick-start, Pocket ID/OIDC walkthrough,
  Pangolin note, backup automation, new troubleshooting entries
- Fix docker-compose.yml: document that --env-file .env must be passed
  explicitly (Compose's implicit .env lookup was unreliable with -f from
  a different directory)
- Fix image-proxy tests to build a real session cookie (AUTH_MODE=oidc
  applies inside the test container too, so requireAuth needs one)
This commit is contained in:
siliconsolder committed 2026-09-07 18:49:45 +00:00
1 parent a82c29a69d
commit 079013730b
10 files changed
+332 -32

No files matched your search

+55 -4
View File
@@ -34,6 +34,49 @@ the actual provider details (issuer URL, client ID/secret, redirect URL,
scopes) are editable any time from **Admin → Auth** — no further env file
edits or restarts needed for those.
### Setting up OIDC with Pocket ID
Notizarr handles the OIDC login flow itself (Authorization Code + PKCE) —
it does not delegate auth to the reverse proxy via trusted headers, so
Pangolin (or whatever sits in front of it) only needs to forward HTTP, no
auth injection.
1. In Pocket ID, create a new OIDC client:
- **Name**: `Notizarr`
- **Client's start URL**: your public Notizarr URL (e.g.
`https://notify.example.com`)
- **Callback URLs**: `https://notify.example.com/api/v1/auth/oidc/callback`
— this exact path is hardcoded server-side
([apps/server/src/modules/auth/routes.ts](apps/server/src/modules/auth/routes.ts)),
it has to match exactly
- **Public Client**: **off** — Notizarr is a confidential/server-side
client and needs a client secret. Leaving this on means Pocket ID
never issues one.
- **PKCE**: **on** — Notizarr always sends a PKCE challenge regardless,
so this just means Pocket ID requires it too
- Everything else (logout callback URLs, skip consent, logo) is
optional/cosmetic
2. Save, then open the client back up and find its **Referenser** /
Credentials tab — client secrets in Pocket ID live there, not on the
creation form, and are only shown once when generated.
3. In Notizarr, go to **Admin → Auth** and fill in the Issuer URL, Client
ID, Client Secret, Redirect URL (same value as the Callback URL above),
and Scopes (`openid profile email`). Save.
4. Set `AUTH_MODE=oidc` in `.env` and restart the container — this one
setting is the only part that still needs a restart, since it changes
which routes the server exposes at boot. Everything from step 3 takes
effect immediately without one.
5. Log in once. If you'd previously been running `AUTH_MODE=disabled`, its
implicit "Local Admin" user already claimed the bootstrap-admin slot
(the very first user *ever created*), so your real login likely comes
back as a normal user — see the "First-ever login should be an admin
but isn't" entry under [Troubleshooting](#troubleshooting) to fix that.
**Pangolin**: point it at the web listener only (`notizarr:8080` on the
same Docker network, or the host's `PUBLIC_BASE_URL`) — never at `:8081`,
the LAN-only ingestion listener. See [Unraid deployment](#unraid-deployment-production)
below.
All mutating requests on the web listener (`POST`/`PUT`/`PATCH`/`DELETE`
under `/api/`) require a `X-Requested-With: notizarr` header — this is the
CSRF defense described in the threat model, not optional. The ingestion
@@ -74,7 +117,7 @@ use the dev Docker Compose stack below, which includes one.
## Local development via Docker
```bash
docker compose -f docker/docker-compose.yml up -d
docker compose --env-file .env -f docker/docker-compose.yml up -d
```
Brings up `notizarr-postgres-dev` and `notizarr-dev` (hot-reload: the repo
@@ -91,7 +134,7 @@ anonymous `node_modules` volumes in one go, or the container keeps running
against the old, pre-change `node_modules`:
```bash
docker compose -f docker/docker-compose.yml up -d --build --force-recreate -V
docker compose --env-file .env -f docker/docker-compose.yml up -d --build --force-recreate -V
```
Override host ports if they also collide, either inline or via a
@@ -99,7 +142,7 @@ Override host ports if they also collide, either inline or via a
```bash
HOST_PORT_WEB=28080 HOST_PORT_INGEST=28081 HOST_PORT_PG=25432 \
docker compose -f docker/docker-compose.yml up -d
docker compose --env-file .env -f docker/docker-compose.yml up -d
```
Check health:
@@ -115,7 +158,7 @@ this writes real PNGs into `apps/web/public/icons/`, committed to the repo
rather than regenerated on every build):
```bash
docker compose -f docker/docker-compose.yml exec notizarr pnpm --filter @notizarr/web generate-icons
docker compose --env-file .env -f docker/docker-compose.yml exec notizarr pnpm --filter @notizarr/web generate-icons
```
## Unraid deployment (production)
@@ -242,6 +285,14 @@ work the same way.
## Troubleshooting
- **`docker compose ... exec/logs/etc` fails with `required variable
POSTGRES_PASSWORD is missing a value`.** Compose's implicit `.env`
lookup when the compose file is passed via `-f docker/docker-compose.yml`
from the repo root doesn't reliably resolve to the repo root's `.env` —
it's ambiguous/version-dependent, and got this wrong in practice. Always
pass `--env-file .env` explicitly, on every invocation against this
file, not just `up` (see the comment at the top of
`docker/docker-compose.yml`).
- **A `docker compose up -d --build` didn't pick up a dependency change.**
Anonymous `node_modules` volumes are reused by default — add `-V
--force-recreate` (see [Local development via