feat(deploy): db-init one-shot service — paste-and-go bootstrap for the runtime role

The role split previously required a one-time manual script run on
existing volumes (initdb.d only fires on fresh ones). This folds the
bootstrap into the compose file the way split-account deployments
standardly do it: a one-shot db-init service runs z_rls_role.sh
(idempotent, network mode via PGHOST=db + PGPASSWORD from DBPASS) on
every 'compose up', and the app gates on
db-init: condition: service_completed_successfully.

- Zero manual steps for fresh AND existing deployments: update compose,
  add DBSRVPASS to .env, 'compose pull && up -d'.
- Security posture improves: the superuser password is now held ONLY by
  the init service — the app container connects exclusively as the
  non-superuser runtime role.
- z_rls_role.sh gains dual connection modes (local socket for docker
  exec, network for the init service) with the DBPASS requirement
  enforced only in network mode.
- Verified on the dev stack: init runs and exits clean, app gates on it
  and comes up healthy, second 'up' re-converges idempotently, invariant
  test passes through the new dependency chain.

The initdb.d mount stays as-is (harmless double coverage on fresh
volumes; both paths are idempotent).
This commit is contained in:
John O'Keefe committed 2026-10-29 11:17:42 -04:00
1 parent 1457dc7289
commit 313268a7ae
3 files changed
+41 -13

No files matched your search

+23
View File
@@ -26,6 +26,27 @@ services:
env_file:
- .env
# One-shot bootstrap: creates the bookhoard_srv runtime role and
# transfers object ownership to it (RLS prerequisite). Idempotent —
# safe on every startup; the app waits for its success. Passwords
# accept inline values or .env, like the rest of this file.
db-init:
image: postgres:15-alpine
container_name: bookhoard_db_init
environment:
POSTGRES_USER: postgres
POSTGRES_DB: bookhoard
PGHOST: db
DBPASS: ${DBPASS}
DBSRVPASS: ${DBSRVPASS}
volumes:
- ./database/schema/z_rls_role.sh:/z_rls_role.sh:ro
command: bash /z_rls_role.sh
depends_on:
db:
condition: service_healthy
restart: "no"
# Bookhoard Application
# In production this image is pulled from the Gitea container registry.
# Override IMAGE_TAG in .env to pin or rollback a specific version (defaults to "latest").
@@ -78,6 +99,8 @@ services:
depends_on:
db:
condition: service_healthy
db-init:
condition: service_completed_successfully
volumes:
- ./uploads:/app/uploads
- bookhoard_conversion_cache:/app/cache/kepub