mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-10 21:32:18 +00:00
Every configured key id now has a canary in encryption_key_canaries, checked at boot. A key that cannot open its canary, or existing data written before the canary existed, puts the process in a refusal state: every API route answers 503 encryption.key_mismatch, /api/health reports reason encryption_key_mismatch, the pages render one explanation, and neither the queue producer nor the worker starts. The process stays up so the reason is visible instead of hiding in a crash loop. The admin Encryption page gains a "Back up your encryption key" step: key id and fingerprint (never the key), where the key lives per platform, a check of a pasted copy that is compared and discarded, and a confirmation bound to the active key id and fingerprint. The overview gets a tile and admins get a dashboard banner while it is due. HEALTHLOG_PLATFORM picks the platform the step opens on. Adds the TrueNAS self-hosting page and key backup sections in the docs.
88 lines
4.8 KiB
Markdown
88 lines
4.8 KiB
Markdown
# HealthLog on TrueNAS SCALE
|
||
|
||
HealthLog runs on TrueNAS SCALE as a Docker app: one `app` container from
|
||
`ghcr.io/mbombeck/healthlog` and one PostgreSQL 16 database. The app container
|
||
is stateless; everything you care about lives in the database dataset and in
|
||
one value you must keep yourself: the encryption key.
|
||
|
||
## What the app needs
|
||
|
||
| Setting | Value |
|
||
| ------------ | ---------------------------------------------------------------------------------------- |
|
||
| Image | `ghcr.io/mbombeck/healthlog`, tag `X.Y.Z` (amd64 and arm64) |
|
||
| Port | `3000` (HTTP) |
|
||
| User | runs as uid `1001`; no extra capabilities, `no-new-privileges` is fine |
|
||
| Health check | `GET /api/health`, `200` when ready |
|
||
| Start period | at least 60 s, with more retries than the image's 3 × 30 s: migrations run at every boot |
|
||
| Database | PostgreSQL 16 (17 and later are not a drop-in), no extensions |
|
||
| Volumes | none for the app; the database gets its own dataset |
|
||
|
||
## Environment
|
||
|
||
| Variable | Required | Notes |
|
||
| ----------------------- | -------- | ------------------------------------------------------------------------------------------- |
|
||
| `DATABASE_URL` | yes | `postgresql://<user>:<password>@<host>:5432/<db>` |
|
||
| `ENCRYPTION_KEY` | yes | 64 hex characters: `openssl rand -hex 32`. Keep a copy outside the NAS (see below). |
|
||
| `API_TOKEN_HMAC_KEY` | yes | 64 hex characters, generated the same way. Changing it signs everyone out. |
|
||
| `NEXT_PUBLIC_APP_URL` | yes | the address people open, for example `http://truenas.local:30180` |
|
||
| `APP_URL` | yes | the same address |
|
||
| `SESSION_COOKIE_SECURE` | no | `false` for a LAN install served over plain `http://`; leave unset behind an HTTPS proxy |
|
||
| `HEALTHLOG_PLATFORM` | no | `truenas`: the admin's key backup step opens on the TrueNAS instructions |
|
||
| `GEOLITE2_DIR` | no | a mounted directory of GeoLite2 `.mmdb` files, so sign-in locations are resolved on the NAS |
|
||
|
||
`HEALTHLOG_PROCESS_TYPE` stays unset (`all`): one container serves the web app
|
||
and runs the background jobs.
|
||
|
||
## Plain HTTP on the LAN
|
||
|
||
Most NAS installs are reached over `http://`. Set `SESSION_COOKIE_SECURE=false`
|
||
for that. If it is left at its default, the browser drops the sign-in cookie
|
||
and the login page says so before you type a password: "This server only sets
|
||
its sign-in cookie over https://".
|
||
|
||
## Back up your encryption key
|
||
|
||
HealthLog encrypts tokens, notes, documents and in-database backups with
|
||
`ENCRYPTION_KEY`. The key is not stored in the database. If the app is deleted
|
||
and reinstalled with a new key over the old database dataset, the data is
|
||
still there but cannot be read.
|
||
|
||
1. Copy `ENCRYPTION_KEY` from the app's settings (Apps, HealthLog, Edit,
|
||
Environment) into your password manager.
|
||
2. Sign in as the admin and open Admin, Encryption. The step shows the key id
|
||
and a fingerprint, the first 12 hex characters of SHA-256 over the key.
|
||
Compare it with your copy:
|
||
|
||
```bash
|
||
echo -n "<your key>" | xxd -r -p | sha256sum | cut -c1-12
|
||
```
|
||
|
||
or paste the copy into "Check my copy" (it is compared and dropped, never
|
||
stored or logged).
|
||
|
||
3. Confirm the backup. Until you do, admins see a reminder on the dashboard.
|
||
After a key rotation the step comes back for the new key.
|
||
|
||
## When the key does not match the database
|
||
|
||
At every start HealthLog checks that its key opens the data already in the
|
||
database. If it does not, it refuses to serve instead of failing on every
|
||
encrypted value: pages show "HealthLog cannot open its data", API requests
|
||
answer `503` with `meta.errorCode` `encryption.key_mismatch`, `/api/health`
|
||
answers `503` with `reason: "encryption_key_mismatch"`, and no background jobs
|
||
run. TrueNAS shows the app as unhealthy rather than crashed, so the reason
|
||
stays visible.
|
||
|
||
Fix it one of three ways, then restart the app:
|
||
|
||
1. Put the original `ENCRYPTION_KEY` back.
|
||
2. Point `DATABASE_URL` at the database that belongs to this key.
|
||
3. Start fresh with an empty database. The old data and its backups need the
|
||
old key.
|
||
|
||
## Updates
|
||
|
||
Pick the new tag, take a snapshot of the database dataset first, and update.
|
||
Migrations run on start; the first start of a new install applies all of them,
|
||
which is why the start period above is generous.
|