Merge the release with the security review fixes into the data-safety review fixes

This commit is contained in:
Marc-André Bombeck committed 2026-10-25 18:36:54 +01:00
commit bcb31c80db
98 files changed
+3166 -608

No files matched your search

+13 -4
View File
@@ -564,8 +564,17 @@ low-friction default.
**5 minutes** of server time. A signed request sends no secret, and a
captured one is useless once the window has passed.
Set `DEPLOY_WEBHOOK_REQUIRE_TIMESTAMP=true` to accept signed requests only.
Use it when the sender, or a small relay in front of the app, can sign:
Two settings tighten this:
- `DEPLOY_WEBHOOK_REQUIRE_TIMESTAMP=true` requires a fresh
`X-Deploy-Webhook-Timestamp` on a request that uses the secret header; one
without it is refused with `401 {"status":"stale"}`. This is what the
setting has always meant. It keeps an old capture out, but not someone who
saw the secret and sends a new request with a new timestamp.
- `DEPLOY_WEBHOOK_REQUIRE_SIGNATURE=true` accepts signed requests only; the
secret header alone is refused with 401. This is the one that stops a
replay. Use it when the sender, or a small relay in front of the app, can
sign:
```sh
TS=$(date +%s)
@@ -579,5 +588,5 @@ curl -X POST https://<host>/api/internal/deploy-webhook \
-d "$BODY"
```
Before 1.46 the strict setting accepted the secret header plus a fresh
timestamp. Such a sender now gets 401 in strict mode and has to sign.
Both may be set; a signed request satisfies both. An operator who set the
timestamp flag before 1.46 keeps exactly the behaviour they had.
+3 -1
View File
@@ -107,10 +107,12 @@ In the admin area, or with a signed-in admin session:
```sh
curl -X POST https://<your-instance>/api/admin/maintenance/measurements \
-H 'content-type: application/json' \
--cookie 'healthlog_session=<session>' \
--cookie '__Host-healthlog_session=<session>' \
-d '{"vacuum": true, "reindex": true}'
```
On a plain-HTTP install the cookie is named `healthlog_session`.
The answer is `202` with `{ "enqueued": true }`, or `enqueued: false` when a
run is already queued or running. A Bearer token is refused: the route is
cookie-only, like every admin route. Without a background worker the route
+30 -3
View File
@@ -17,7 +17,9 @@ Postgres 18 database is up and before the app, and it exits in a second when
there is nothing to move. When the old `pgdata` volume holds a Postgres 16
database and the new one is empty, it:
1. checks there is room on the disk for a second copy;
1. checks that the database service runs Postgres 18, and that there is room
on the disk for a second copy and for the dump the app takes before its
first migration;
2. starts Postgres 16 inside its own container on the old volume, reachable
only from inside that container;
3. streams a dump into Postgres 18 in a single transaction;
@@ -34,8 +36,10 @@ If any step fails, the new database stays empty, the app does not start, and
the log says what happened. A move cut short by a restart or a full disk is
run again from scratch on the next `docker compose up -d`.
The old `pgdata` volume is read and never changed, apart from the marker
file. That is the way back.
The old `pgdata` volume keeps the database as it was. Postgres 16 runs on it
during the move, so it writes a checkpoint there (and recovers first after an
unclean stop), and the marker file is added; no data in it changes. That is
the way back.
## Going back
@@ -55,6 +59,29 @@ HEALTHLOG_IMAGE_REF=:v1.45.0 docker compose up -d
```
Anything written after the move lives only in the Postgres 18 volume.
`db-upgrade` can stay in the file: it sees that the database service runs
Postgres 16 and leaves that volume alone. This also holds after a move that
stopped half-way.
## Updating again after going back
If you used Postgres 16 again after going back, the Postgres 18 volume holds
the state of the day of the move, not what you wrote since. `db-upgrade`
notices that the old database changed after the move and refuses to start on
the old copy. To move the current 16 database, remove the old copy and the
marker, then start again:
```bash
docker compose down
docker volume rm <project>_postgres-data
docker compose run --rm --entrypoint rm db-upgrade \
/var/lib/postgresql/data/HEALTHLOG_MOVED_TO_POSTGRES_18
docker compose up -d
```
`docker volume ls` shows the volume's full name. Removing it deletes what was
written on Postgres 18 before you went back; take a dump of it first if any
of that matters.
## Afterwards