docs(deploy): document user-owned session cutover
This commit is contained in:
@@ -60,6 +60,11 @@ The frontend depends on the core health check and proxies `/health` and `/api/*`
|
||||
application health endpoint intentionally checks process readiness only; external dependency
|
||||
diagnostics are exposed by `tht doctor` and do not prevent the UI from starting.
|
||||
|
||||
`docker-compose.dev.yml` is deliberately local: both published ports bind to `127.0.0.1`,
|
||||
`THT_SESSION_STORAGE=local`, and `THT_HOME=/data/local-home`. Do not set
|
||||
`THOTH_PUBLIC_EXPOSURE=true` for that profile; the backend rejects that public/local combination
|
||||
at startup.
|
||||
|
||||
Run the end-to-end packaging check with:
|
||||
|
||||
```sh
|
||||
@@ -192,6 +197,76 @@ Compound providers are deliberately unsupported: `amazon-bedrock`, `azure-openai
|
||||
values. Selecting one fails before Pi starts; ambient AWS, Azure, and Cloudflare credentials are
|
||||
still scrubbed. Supporting them requires a future dedicated provider-specific configuration.
|
||||
|
||||
## User-owned session server cutover
|
||||
|
||||
The server profile stores sessions and per-user preferences directly in PostgreSQL schema
|
||||
`thoth_sessions`; it does not use PostgREST, browser storage, a shared session directory, or a
|
||||
dual write. Start from [`deploy/compose.session-server.yaml.example`](deploy/compose.session-server.yaml.example)
|
||||
and copy [`deploy/workspaces/server-sessions.yaml.example`](deploy/workspaces/server-sessions.yaml.example)
|
||||
to the untracked `deploy/workspaces/server-sessions.yaml` mounted into the core container.
|
||||
|
||||
The runtime login needs membership in the no-login database role `thoth_sessions_runtime` only.
|
||||
The distinct, one-shot migrator login needs migration authority and uses
|
||||
`thoth_sessions_migrator`; it must never be mounted into `core`. Set the non-secret endpoint and
|
||||
role fields in the protected deployment environment:
|
||||
|
||||
```dotenv
|
||||
AUTH_MODE=upstream
|
||||
THOTH_PUBLIC_EXPOSURE=true
|
||||
THT_SESSION_STORAGE=postgres
|
||||
THT_SESSION_DB_HOST=sessions-db.internal
|
||||
THT_SESSION_DB_PORT=5432
|
||||
THT_SESSION_DB_NAME=thoth
|
||||
THT_SESSION_RUNTIME_USER=thoth_sessions_app
|
||||
THT_SESSION_MIGRATOR_USER=thoth_sessions_migrate
|
||||
THT_SESSION_DB_SSLMODE=verify-full
|
||||
THT_SESSION_RUNTIME_PASSWORD_SOURCE=/secure/thoth/session-runtime-password
|
||||
THT_SESSION_MIGRATOR_PASSWORD_SOURCE=/secure/thoth/session-migrator-password
|
||||
THT_SESSION_CA_SOURCE=/secure/thoth/session-ca.pem
|
||||
```
|
||||
|
||||
The overlay mounts the runtime password at `/run/secrets/session_runtime_password`, the CA at
|
||||
`/run/secrets/session_ca.pem`, and passes those paths—not their contents—to the server workspace.
|
||||
It mounts `session_migrator_password` only to `session-migrate`. The backend refuses a server
|
||||
session store without upstream authentication, direct DB host/name/runtime user/password-file,
|
||||
`verify-ca` or `verify-full`, and an absolute CA path.
|
||||
|
||||
Perform the cutover in one maintenance window, with the Task 4 portal proxy headers and Task 5
|
||||
backend principal parser deployed together. Neither change is safe to deploy independently: Task
|
||||
4 clears the legacy identity header and Task 5 rejects it. Drain/stop active Pi work, enable a
|
||||
maintenance response at the portal, then run the migrator once and inspect its pristine JSON:
|
||||
|
||||
```sh
|
||||
docker compose -f compose.yaml -f deploy/compose.session-server.yaml \
|
||||
--profile session-migrate run --rm session-migrate
|
||||
```
|
||||
|
||||
It must report no pending or drifted migrations before starting the replacement core. `/health`
|
||||
is a liveness probe and remains `200`; any request that needs unavailable repository storage
|
||||
returns a fixed `503` before a Pi process starts. Verify this with an authenticated request after
|
||||
the replacement core is healthy, then remove maintenance mode.
|
||||
|
||||
Do not import the three legacy server filesystem sessions: they have no trusted owner binding.
|
||||
During the same maintenance window, archive the exact three reviewed IDs, verify the generated
|
||||
archive and `.sha256`, then rerun the command with `--delete` to remove only those three source
|
||||
directories:
|
||||
|
||||
```sh
|
||||
./docker/cutover-legacy-sessions.sh \
|
||||
/secure/thoth/legacy-sessions /secure/backups/thoth-legacy-sessions-2026-07-16.tar \
|
||||
SESSION_ID_1 SESSION_ID_2 SESSION_ID_3
|
||||
# After independent archive review, use a new backup filename:
|
||||
./docker/cutover-legacy-sessions.sh --delete \
|
||||
/secure/thoth/legacy-sessions /secure/backups/thoth-legacy-sessions-2026-07-16-delete.tar \
|
||||
SESSION_ID_1 SESSION_ID_2 SESSION_ID_3
|
||||
```
|
||||
|
||||
The helper refuses to overwrite an existing backup and refuses any count other than three
|
||||
distinct IDs. Never run it against a live path without the maintenance gate. Roll back application
|
||||
code only by keeping PostgreSQL as the single source of truth and deploying a compatible fixed
|
||||
release. Do not restore filesystem persistence, do not re-import the archive, and never dual-write
|
||||
sessions to database and files.
|
||||
|
||||
## Reproducible image verification
|
||||
|
||||
Base images use exact tags and immutable multi-platform manifest digests. Dependency update and
|
||||
|
||||
Reference in New Issue
Block a user