# Hosting, backup and recovery The repository contains a deployment configuration; no host has been selected or deployed. HTTP/browser/container tests must pass on a normal host before using it with friends. Real drafting remains data-gated separately. ## Build and bootstrap Keep `product-fpol`, `modulus`, `noto`, and the updated `ligo` repositories beside each other. Compose uses the parent directory as its build context, excludes caches/secrets, exports those local Conan recipes, and builds a release C++ image with GCC 12. The frontend uses a separate Node build stage. First build requires internet access for base images and dependencies. ```sh cp .env.example .env # Set FPOL_DOMAIN to a hostname whose DNS points at your host; no scheme/path. docker compose build # Bootstrap migrations, the partial catalog, and an administrator before serving. docker compose run --rm --no-deps app fpol_admin --database /data/fpol.sqlite \ import /app/data/real/us-2026-10-04-partial.json docker compose run --rm --no-deps app fpol_admin --database /data/fpol.sqlite create-user YOUR_USERNAME docker compose up -d ``` Caddy owns public ports 80/443 and obtains HTTPS certificates. The app's 8080 port is internal only. Both app and worker use UID 10001 and the persistent `fpol_data` volume. Caddy certificates/config have their own volumes. The entrypoint uses umask 077. Health checks call `/api/health`; the worker starts after the app is healthy. No demo mode is enabled. Source activation is explicit; an idle ingestion worker invents no data. Log in, create a league, and share its private invitation link. To reset a password: ```sh docker compose exec app fpol_admin --database /data/fpol.sqlite reset-link USERNAME ``` Privately give that user `https://YOUR_HOST/#reset/TOKEN`. The link expires in 30 minutes and works once. Do not paste passwords, invitations, session tokens or reset links into public logs/issues. ## Source setup Use [the ingestion guide](ingestion.md). Copy reviewed configuration files into the container or mount an operator-controlled directory. Configure, probe, inspect source status and archived payload/mappings, then activate. Production startup never auto-activates sample mappings. Both coverage and the candidate inventory must be complete before real draft start. ## Online backup Use SQLite's online backup API through Noto while the service is running. Choose a new filename each time; the command refuses to overwrite one. ```sh docker compose exec app fpol_admin --database /data/fpol.sqlite \ backup /data/backups/fpol-2026-10-04.sqlite docker compose cp app:/data/backups/fpol-2026-10-04.sqlite ./fpol-2026-10-04.sqlite chmod 600 ./fpol-2026-10-04.sqlite ``` Store a protected off-host copy. Include the deployed source revisions and source configuration files. The database backup contains accounts, sessions, invites, frozen league data, picks, raw source payloads and observation history. Do not copy only the live SQLite main file while WAL writes are active. ## Restore Stop **both** writers. Keep the original database and sidecars as a rollback set. Restore to a stopped app container; adapt the filename to your chosen backup. Do not restore a backup over a running database. ```sh docker compose stop app ingest # The existing container can receive files while stopped. docker compose cp ./fpol-2026-10-04.sqlite app:/data/restore.sqlite docker compose run --rm --no-deps --user root app sh -c 'chown 10001:10001 /data/restore.sqlite && chmod 600 /data/restore.sqlite' # Use the image's unprivileged account, backed by the same volume. docker compose run --rm --no-deps app sh -c ' set -eu test -f /data/restore.sqlite mkdir /data/before-restore for name in fpol.sqlite fpol.sqlite-wal fpol.sqlite-shm; do if test -f "/data/$name"; then mv "/data/$name" /data/before-restore/; fi done cp /data/restore.sqlite /data/fpol.sqlite chmod 600 /data/fpol.sqlite fpol_admin --database /data/fpol.sqlite rules >/dev/null ' docker compose up -d app ingest ``` The ownership step affects only the staged backup, so UID 10001 can read it. Verify SQLite integrity on the offline backup (`PRAGMA integrity_check`), source health, memberships, and the last draft pick. Restored active drafts pause automatically; commissioners resume with a fresh 120-second turn. Restoring an older backup also restores old session/reset state; consider revoking sessions and reset tokens via trusted local SQLite administration after recovery. Migrations are forward-only. Back up before upgrades; reverting application binaries after a schema change requires restoring a compatible database backup too. Never use `docker compose down -v` to restart this app: it deletes persistent volumes.