Files
dragonsstash/.superpowers/sdd/scope-correction-implementation-report.md
T

7.6 KiB

Scope correction implementation report

Summary

Implemented the approved backup scope correction for Dragon's Stash. Backups and restores now cover only:

  • PostgreSQL logical custom-format dump plus manifest/migration metadata.
  • tdlib_state worker Telegram session volume.
  • tdlib_bot_state bot Telegram session volume.

The implementation no longer treats manual_uploads, completed local STL binaries, or tmp_zips as protected backup data. Future channel forwarding and archive/STL-content integrity auditing remain out of scope.

Changed files

  • docker-compose.yml

    • Removed the backup service's read-only manual_uploads:/data/uploads mount.
    • Kept the normal operational app/worker manual_uploads mounts.
    • Kept both TDLib backup mounts.
  • scripts/backup/container-entrypoint.sh

    • Removed /data/uploads as a required mounted directory.
    • Removed uploads from the manifest volumePaths.
    • Removed the source:uploads Restic tag.
    • Removed uploads from the Restic source list.
    • Kept database dump, manifest, worker TDLib, and bot TDLib sources/tags.
  • scripts/backup/restore.sh

    • Removed restored uploads variables.
    • Removed uploads staging validation.
    • Removed manual uploads volume discovery, safety archive, replacement, and rollback.
    • Removed retained/manual upload file-path verification.
    • Removed temporary database verification that existed only for local upload file references.
    • Preserved guarded restore-live confirmation, backup mount/repository checks, service stop/start handling, safety PostgreSQL dump, TDLib volume safety archives, TDLib volume replacement/rollback, and pg_restore --list/pg_restore --exit-on-error validation.
  • prisma/schema.prisma

    • Removed ManualUploadFile.retainedAt.
  • prisma/migrations/20260722100000_remove_retained_manual_files/migration.sql

    • Added forward migration: ALTER TABLE "manual_upload_files" DROP COLUMN IF EXISTS "retainedAt";
    • Preserved the existing committed migration that added retainedAt.
  • src/app/api/uploads/route.ts

    • Removed retainedAt: new Date() from manual upload file creation.
  • worker/src/manual-upload.ts

    • Restored final best-effort cleanup of /data/uploads/<uploadId> using the older path.join("/data/uploads", uploadId) behavior.
  • scripts/backup/README.md

    • Rewrote backup set and restore rehearsal docs around PostgreSQL plus both TDLib volumes only.
    • Removed local STL file, retainedAt, upload path, retained file reference, and restored checksum checks.
    • Clarified that STL binaries remain in Telegram and recovery preserves database mappings/Telegram IDs.
    • Kept monthly restic check --read-data and disposable restore rehearsal runbook.
    • Explicitly left future channel forwarding and archive/STL-content integrity auditing out of scope.
  • README.md

    • Updated the production backup summary to name PostgreSQL logical dump plus Telegram session volumes as the protected set.
    • Clarified that manual_uploads and temporary ZIPs are excluded and STL binaries remain in Telegram.

Verification

  • git diff --check

    • Passed.
  • bash -n scripts/backup/container-entrypoint.sh scripts/backup/run-backup.sh scripts/backup/restore.sh

    • Local bash failed because Windows only had the WSL shim and no installed WSL distribution.
    • Passed via Docker fallback: docker run --rm --entrypoint bash -v E:\Projects\DragonsStash:/work:ro -w /work postgres:16-alpine -n scripts/backup/container-entrypoint.sh scripts/backup/run-backup.sh scripts/backup/restore.sh
  • npx prisma validate

    • Passed.
  • npm run build

    • Passed.
  • cd worker && npm run build

    • Passed.
  • Focused backup/restore scope assertions

    • Backup shell paths assertion passed: no manual_uploads, /data/uploads, retainedAt, upload source tag, or upload-restore helper references in scripts/backup/*.sh.
    • Compose backup service assertion passed: no manual_uploads, /data/uploads, retainedAt, or source:uploads in the backup service block.
    • Active retainedAt assertion passed: no retainedAt in active Prisma schema, upload API, worker source, or backup shell scripts.
    • Active backup/restore upload-source assertion passed: no manual_uploads, /data/uploads, or data/uploads in backup/restore shell scripts.
    • Remaining expected matches are limited to normal operational app/worker upload mounts and paths, docs stating exclusions, and the historical add/drop migrations.

Concerns

  • None for implementation scope.
  • Environment note: local Bash is unavailable because WSL has no installed distribution; Bash syntax was verified inside Docker instead.

Restore Path Scope Review-Finding Fix

Date: 2026-07-22

Fix

Addressed the Important restore finding by replacing both unrestricted restic restore calls in scripts/backup/restore.sh with a shared filtered restore wrapper. The wrapper restores only the backup source paths actually written by scripts/backup/container-entrypoint.sh:

  • /staging/backup-*/database.dump
  • /staging/backup-*/manifest and /staging/backup-*/manifest/**
  • /data/tdlib-worker and /data/tdlib-worker/**
  • /data/tdlib-bot and /data/tdlib-bot/**

Added an explicit restored-tree guard that refuses unexpected restored content: top-level restored directories other than staging and data, direct data/* entries other than tdlib-worker and tdlib-bot, and direct staging/backup-*/* entries other than database.dump and manifest. This rejects old/broad snapshots that would otherwise restore data/uploads, temporary ZIP or database volume trees, or other unexpected volume content.

Preserved the existing guarded live-restore confirmation, staging-directory validation, mount/repository checks, snapshot verification, custom PostgreSQL-dump validation, service stop/start lifecycle, health check, safety database dump, TDLib safety archives, and rollback of exactly the PostgreSQL database plus the two TDLib volumes.

Added scripts/backup/restore-path-assertions.sh, a focused shell assertion harness that stubs Docker/Restic and verifies both staging and live restore use the expected include filters and that unexpected restored data-volume content is rejected explicitly.

Addressed the Minor documentation gap in the root README backup section by stating that forwarding behavior and archive/STL-content integrity auditing are future work outside the backup scope.

Verification

  • Red check before implementation:

    & 'C:\Program Files\Git\bin\bash.exe' -lc 'scripts/backup/restore-path-assertions.sh'
    ASSERTION FAILED: database dump include filter missing
    
  • Bash syntax check:

    & 'C:\Program Files\Git\bin\bash.exe' -lc 'bash -n scripts/backup/container-entrypoint.sh scripts/backup/run-backup.sh scripts/backup/restore.sh scripts/backup/restore-path-assertions.sh'
    [passed with no output]
    
  • Whitespace check:

    git diff --check
    warning: in the working copy of '.superpowers/sdd/scope-correction-implementation-report.md', LF will be replaced by CRLF the next time Git touches it
    warning: in the working copy of 'README.md', LF will be replaced by CRLF the next time Git touches it
    warning: in the working copy of 'scripts/backup/restore.sh', LF will be replaced by CRLF the next time Git touches it
    [exit 0]
    
  • Focused restore-path assertions:

    & 'C:\Program Files\Git\bin\bash.exe' -lc 'scripts/backup/restore-path-assertions.sh'
    restore-path assertions passed
    

Concerns

  • None for implementation scope.
  • Git Bash was available and used for shell syntax/assertion checks, so Docker fallback was not needed for the final syntax verification.