mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-11 13:52:20 +00:00
docs(self-hosting): VAPID generate button, env names, backup and whitelist notes
Document the new Generate keys button in notifications.md as the easiest Web Push setup path, demoting the CLI to an alternative. Note that clientManaged medication-reminder suppression is an iOS-app-only contract so a PWA-only operator does not go hunting for a web toggle. Add the VAPID env-var names and APNS_KEY_B64 to .env.production.example (the loader and APNs sender read them, but the example omitted them), both as commented placeholders, with a note that Web Push needs no Apple account and that these vars are not in the compose whitelist. Add a Backup and restore section to getting-started.md: a local pg_dump one-liner and the load-bearing warning that the ENCRYPTION_KEYS / ENCRYPTION_KEY must be backed up alongside the dump or the encrypted data is unrecoverable, cross-linking the off-host backup and key-rotation runbooks. Add troubleshooting entries for the compose env-var whitelist and pull_policy: always.
This commit is contained in:
1 parent
d0ba4b061b
commit
266109f501
3 files changed
+110
-21
No files matched your search
+23
-1
@@ -74,13 +74,35 @@ API_TOKEN_HMAC_KEY="<openssl rand -hex 32>"
|
||||
# OURA_REDIRECT_URI=""
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Web Push -- VAPID keys (optional; the admin panel is the easier path)
|
||||
# -----------------------------------------------------------------------------
|
||||
# Web Push needs NO Apple account and no paid service -- just a VAPID keypair.
|
||||
# The EASIEST path is the admin panel: /admin -> Web Push VAPID -> "Generate
|
||||
# keys" mints and stores the pair for you (private key encrypted at rest). Use
|
||||
# these env vars only if you prefer env-config over the DB. The loader reads
|
||||
# DB first, then env (src/lib/notifications/vapid-config.ts). NOTE: these are
|
||||
# NOT in docker-compose.yml's `environment:` whitelist -- if you go the env
|
||||
# route under compose, add them there too or the values never reach the
|
||||
# container (see docs/self-hosting/getting-started.md Troubleshooting).
|
||||
# VAPID_PUBLIC_KEY=""
|
||||
# VAPID_PRIVATE_KEY=""
|
||||
# VAPID_SUBJECT="mailto:you@example.com"
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# APNs -- iOS push (optional, all-or-none)
|
||||
# -----------------------------------------------------------------------------
|
||||
# Only the native iOS app uses APNs, and it needs a paid Apple Developer
|
||||
# Program membership. Web Push (above) needs NO Apple account -- it covers
|
||||
# every browser and the installed PWA on iPhone. Leave this whole block unset
|
||||
# unless you ship the native app. Key-source precedence when more than one is
|
||||
# set: APNS_KEY_B64 > APNS_KEY > APNS_KEY_FILE.
|
||||
# APNS_KEY_ID=""
|
||||
# APNS_TEAM_ID=""
|
||||
# APNS_BUNDLE_ID=""
|
||||
# APNS_KEY="" # OR APNS_KEY_FILE -- either one satisfies the manifest
|
||||
# APNS_KEY_B64="" # PEM body base64-encoded -- recommended (escapes newlines)
|
||||
# APNS_KEY="" # OR APNS_KEY (raw PEM) OR APNS_KEY_FILE -- one satisfies the manifest
|
||||
# APNS_KEY_FILE=""
|
||||
|
||||
|
||||
|
||||
@@ -175,3 +175,51 @@ NEXT_PUBLIC_DASHBOARD_SNAPSHOT=false pnpm build
|
||||
- **OAuth callbacks loop back to localhost.** Confirm `APP_URL` and
|
||||
`NEXT_PUBLIC_APP_URL` both point at the public hostname, not at
|
||||
`localhost`, and restart `app` after the change.
|
||||
- **An env var in `.env` seems ignored / a feature stays "off".** The
|
||||
compose `environment:` block is a **whitelist** — a variable set in
|
||||
`.env` only reaches the container if `docker-compose.yml` lists it
|
||||
under `environment:`. Vars not listed never propagate, even with
|
||||
`${VAR}` substitution at compose-up. If a setting looks configured but
|
||||
is silently dead, check the var is in that block (add it if you run a
|
||||
custom compose); the supported alternative for most secrets (VAPID,
|
||||
GitHub token, GlitchTip DSN) is the **admin panel**, which stores them
|
||||
in the database and sidesteps the whitelist entirely.
|
||||
- **A new image doesn't pick up / `:latest` looks stale.** `docker-compose.yml`
|
||||
sets `pull_policy: always`, which is load-bearing: without it Docker
|
||||
re-uses the cached `:latest` digest and silently skips the registry
|
||||
round-trip on `compose up`. If you pin a custom compose, keep
|
||||
`pull_policy: always` on the `app` service, and verify the running
|
||||
build with `GET /api/version` (it returns `version` + `buildSha`).
|
||||
|
||||
## Backup and restore
|
||||
|
||||
Your database is the only stateful piece — back it up, and back up the
|
||||
key that decrypts it.
|
||||
|
||||
**Local snapshot (the common single-box case).** Dump the `db` service
|
||||
with `pg_dump`:
|
||||
|
||||
```bash
|
||||
# The bundled db service uses user `healthlog` and database `healthlog`.
|
||||
docker compose exec -T db pg_dump -U healthlog healthlog \
|
||||
| gzip > healthlog-$(date +%F).sql.gz
|
||||
```
|
||||
|
||||
Restore into a fresh database the same way:
|
||||
|
||||
```bash
|
||||
gunzip -c healthlog-2026-01-01.sql.gz \
|
||||
| docker compose exec -T db psql -U healthlog healthlog
|
||||
```
|
||||
|
||||
> **Back up your `ENCRYPTION_KEYS` / `ENCRYPTION_KEY` alongside the dump.**
|
||||
> HealthLog encrypts sensitive columns at rest (AES-256-GCM). A database
|
||||
> dump **without the encryption key is unrecoverable** — the encrypted
|
||||
> data cannot be read back. Store the key (and any retired keys still in
|
||||
> the rotation map) somewhere separate from the dump but equally durable.
|
||||
> Losing the key means losing the encrypted data, full stop.
|
||||
|
||||
For off-host encrypted backups to S3/R2/B2 see
|
||||
[`docs/ops/backup-restore.md`](../ops/backup-restore.md); for rotating the
|
||||
encryption key safely (keep the old key until zero rows remain on it) see
|
||||
[`docs/ops/encryption-key-rotation.md`](../ops/encryption-key-rotation.md).
|
||||
@@ -34,35 +34,46 @@ mobile — even when the HealthLog tab is closed, as long as the browser
|
||||
or installed PWA is alive. It is the broadest-reach channel and the one
|
||||
to set up first.
|
||||
|
||||
### 1. Generate a VAPID keypair
|
||||
### 1. Generate and store a VAPID keypair
|
||||
|
||||
VAPID is the signing scheme that lets a push service trust your server.
|
||||
Generate a keypair once with the bundled `web-push` CLI:
|
||||
You also choose a **subject** — a `mailto:` address the push service can
|
||||
contact about your sender, e.g. `mailto:you@example.com`.
|
||||
|
||||
The server loads VAPID config from the database first and falls back to
|
||||
environment variables (`src/lib/notifications/vapid-config.ts`). The admin
|
||||
panel is the easiest path because it survives without touching `.env`.
|
||||
|
||||
**Admin panel — Generate keys (easiest).** Sign in as the admin user,
|
||||
open `/admin`, and find the **Web Push VAPID** card
|
||||
(`src/components/admin/web-push-vapid-section.tsx`). Click **Generate
|
||||
keys**: the server mints a fresh keypair, stores the private key encrypted
|
||||
at rest with your `ENCRYPTION_KEY`, seeds a placeholder subject, and fills
|
||||
in the public key. Edit the subject to your real `mailto:` address and
|
||||
save. The card shows a "configured" badge once all three fields are set —
|
||||
no shell, no copy-paste.
|
||||
|
||||
> If a keypair already exists, **Generate keys** asks you to confirm before
|
||||
> replacing it. Regenerating invalidates every existing browser
|
||||
> subscription, so each device has to re-subscribe afterwards. Only
|
||||
> regenerate when you mean to.
|
||||
|
||||
**Admin panel — paste an existing pair.** If you already have a keypair
|
||||
(e.g. copied from another deployment), paste the public key, the private
|
||||
key, and the subject into the same card and save instead of generating.
|
||||
|
||||
**CLI — generate a pair yourself.** You can also mint one with the bundled
|
||||
`web-push` CLI and paste the result into the card or your `.env`:
|
||||
|
||||
```bash
|
||||
npx web-push generate-vapid-keys
|
||||
```
|
||||
|
||||
That prints a **public key** and a **private key** (both Base64URL). You
|
||||
also choose a **subject** — a `mailto:` address the push service can
|
||||
contact about your sender, e.g. `mailto:you@example.com`.
|
||||
That prints a **public key** and a **private key** (both Base64URL).
|
||||
|
||||
### 2. Store the keys
|
||||
### 2. Store the keys via environment variables (alternative)
|
||||
|
||||
The server loads VAPID config from the database first and falls back to
|
||||
environment variables (`src/lib/notifications/vapid-config.ts`). Either
|
||||
path works; the admin panel is the easier one because it survives without
|
||||
touching `.env`.
|
||||
|
||||
**Admin panel (preferred).** Sign in as the admin user, open
|
||||
`/admin`, and find the **Web Push VAPID** card
|
||||
(`src/components/admin/web-push-vapid-section.tsx`). Paste the public key,
|
||||
the private key, and the subject, then save. The private key is encrypted
|
||||
at rest with your `ENCRYPTION_KEY`, exactly like the other secrets in the
|
||||
admin panel. The card shows a "configured" badge once all three fields
|
||||
are set.
|
||||
|
||||
**Environment variables (alternative).** If you would rather keep
|
||||
If you would rather keep
|
||||
secrets out of the database, set them in `.env` / the compose
|
||||
`environment:` block instead:
|
||||
|
||||
@@ -179,6 +190,14 @@ Two gotchas worth knowing before you blame a stale token:
|
||||
`BadEnvironmentKeyInToken`. Confirm the key shows **Sandbox &
|
||||
Production**; a wrongly scoped key cannot be reconfigured — re-issue it.
|
||||
|
||||
> **`clientManaged` is iOS-app-only.** The native iOS app can opt to run
|
||||
> its own local medication reminders and ask the server to stop sending
|
||||
> the duplicate `MEDICATION_REMINDER` push (the `clientManaged` flag on
|
||||
> `PATCH /api/auth/me/notification-prefs`). This is a native-app contract:
|
||||
> a web/PWA-only self-hoster never needs it and there is no toggle for it
|
||||
> in the web UI. If you only run the PWA, ignore it entirely — your
|
||||
> medication reminders come from the server cron over the channels above.
|
||||
|
||||
## Which channel should I pick?
|
||||
|
||||
| Your setup | Recommended channel |
|
||||
|
||||
Reference in new issue
Block a user