# Post-Deployment Verification Checklist Use this after every deploy (staging and production). ## Preconditions 1. Deployment completed successfully. 2. Migrations completed successfully. 3. Correct environment flags: - `AUTH_DISABLED=false` - `ALLOW_INSECURE_AUTH_FOR_DEV=false` 4. Test DB preflight (for DB-backed suites): - `TEST_DATABASE_URL` points to a reachable PostgreSQL instance. - `TEST_DATABASE_URL` database name is not `skymoney` and is clearly test-only (for example `skymoney_test`). - `bash ./scripts/validate-test-db-target.sh` passes before DB-backed suites run. - Example quick check: ```bash echo "$TEST_DATABASE_URL" ``` Expected: - single valid URL value - host/port match the intended test database (for local runs usually `127.0.0.1:5432`) 5. Compose/DB safety preflight: - `COMPOSE_PROJECT_NAME=skymoney` is set for deploy runtime. - `docker-compose.yml` volume `pgdata` is pinned to `skymoney_pgdata`. - `scripts/validate-prod-db-target.sh` passes for current `.env`. - `scripts/guard-prod-volume.sh` passes (or explicit one-time rebuild override is documented). - deploy runbook acknowledges forbidden destructive commands in prod: - `prisma migrate reset` - `prisma migrate dev` - `prisma db push --accept-data-loss` - `docker compose down -v` / `docker-compose down -v` ## Database recoverability and safety checks ### 0) Capture current container and volume bindings ```bash docker ps --format '{{.Names}}' docker inspect --format '{{json .Mounts}}' docker volume ls | grep -E 'pgdata|skymoney|postgres' PROD_DB_VOLUME_NAME=skymoney_pgdata ALLOW_EMPTY_PROD_VOLUME=0 DOCKER_CMD="sudo docker" bash ./scripts/guard-prod-volume.sh ``` Expected: - production Postgres uses `skymoney_pgdata`. - no unexpected new empty volume silently substituted. ### 0.1) Validate latest backup artifact exists and verifies ```bash ls -lt /opt/skymoney/backups | head LATEST_DUMP="$(ls -1t /opt/skymoney/backups/*.dump | head -n 1)" sha256sum -c "${LATEST_DUMP}.sha256" ``` Expected: - latest dump and checksum exist. - checksum verification returns `OK`. ### 0.2) Restore drill into isolated test DB (same VPS) ```bash RESTORE_DB="skymoney_restore_test_$(date +%Y%m%d%H%M)" \ BACKUP_FILE="$LATEST_DUMP" \ RESTORE_DATABASE_URL="postgres://:@127.0.0.1:5432/${RESTORE_DB}" \ DATABASE_URL="postgres://:@127.0.0.1:5432/skymoney" \ ./scripts/restore.sh ``` Expected: - restore completes without manual edits. - key tables readable in restored DB. ## A01 smoke checks Replace `${API_BASE}` with your deployed API base URL. ### 1) Protected route requires auth ```bash curl -i "${API_BASE}/dashboard" ``` Expected: - HTTP `401` - response body includes `UNAUTHENTICATED` ### 2) Spoofed identity header is ignored ```bash curl -i -H "x-user-id: spoofed-user-id" "${API_BASE}/dashboard" ``` Expected: - HTTP `401` ### 3) Admin rollover is not publicly callable ```bash curl -i -X POST "${API_BASE}/admin/rollover" \ -H "Content-Type: application/json" \ -d '{"dryRun":true}' ``` Expected: - HTTP `401` or `403` (must not be publicly callable) ## A09 smoke checks ### 4) Security events are emitted for failed auth attempts Trigger a failed login attempt: ```bash curl -i -X POST "${API_BASE}/auth/login" \ -H "Content-Type: application/json" \ -d '{"email":"nonexistent@example.com","password":"WrongPass123!"}' ``` Expected: - HTTP `401` - API logs include a structured `securityEvent` for `auth.login` with `outcome=failure` - log entry includes `requestId` ## A10 smoke checks ### 5) Production origin configuration is public and non-local Verify production env/config: - `APP_ORIGIN` uses public HTTPS host (not localhost/private IP ranges) Expected: - API boots successfully with production env validation. ## Automated regression checks Run in CI against a prod-like environment: ```bash cd api npm test -- tests/auth.routes.test.ts tests/access-control.account-delete.test.ts tests/access-control.admin-rollover.test.ts SECURITY_DB_TESTS=0 npx vitest run -c vitest.security.config.ts SECURITY_DB_TESTS=1 npx vitest run -c vitest.security.config.ts ``` Expected: - all tests pass Note: - A06/A07 runtime suites require PostgreSQL availability. - `SECURITY_DB_TESTS=0` runs non-DB security controls only. - `SECURITY_DB_TESTS=1` includes DB-backed A06/A07/forgot-password suites. ## Sign-off 1. Record outputs in `evidence-log-template.md`. 2. Review open residual risks in `residual-risk-backlog.md`. 3. Record backup + restore drill evidence. 4. Mark release security check as pass/fail.