Sync the self-hosting stack

This commit is contained in:
2026-10-21 10:11:48 +02:00
parent 6db65866d2
commit e210b4db11
7 changed files with 82 additions and 25 deletions
+10 -7
View File
@@ -347,11 +347,14 @@ follow their own upstream tags, `postgres:16-alpine` and `redis:7-alpine`. Schem
themselves when the api starts, and the api and the web are versioned independently — their numbers
are not meant to match, because usually only one side changed.
**A schema change is one-way.** Jarvis has no migration history: each boot force-syncs the database
to the schema its image carries, adding what a release added and dropping what it removed, without
prompting. Pulling an older api image does not undo that — unlike the image itself, the schema stays
where the newer build left it. Restoring a dump is the only way back, which is what makes the one
below a prerequisite rather than a precaution.
**A schema change is one-way.** Jarvis has no migration history: each boot syncs the database to
the schema its image carries, adding what a release added. A release that would DROP a table or a
column is refused instead: the schema is not touched, the api logs which columns or tables it would
drop and stays down, restarting into the same refusal. If those drops are intended, set
`SCHEMA_ACCEPT_DATA_LOSS=true` in `.env`, `docker compose up -d` once, then remove the line. Pulling
an older api image does not undo a change already applied — unlike the image itself, the schema
stays where the newer build left it. Restoring a dump is the only way back, which is what makes the
one below a prerequisite rather than a precaution.
```sh
docker compose exec -T postgres pg_dump -U jarvis jarvis | gzip > jarvis-$(date +%F).sql.gz
@@ -559,8 +562,8 @@ These are deliberate and documented rather than surprises waiting to be found.
request. The exception is changing your own password: it revokes every *other* session and keeps
the one you are changing it from, so a token stolen from that session stays valid until it expires
— 15 minutes by default (`JWT_ACCESS_TTL`). An admin-forced reset drops every session.
- **The api container runs as root**, and so does nginx's master process in the web container,
though its workers drop privileges. Neither image declares a `USER`.
- **Neither container runs as root**: the api runs as `node` (uid 1000) and the web's nginx as
`nginx`, listening on 8080. Secret files mounted into them must be readable by that user.
- **`/api/health` answers 200 with `status: "degraded"`** when the database is unreachable, so the
container healthcheck alone is not a liveness signal for the database.
- **Outbound mail is Microsoft Graph only.** There is no SMTP and no environment variable for any of