Backup & Restore
Relevant source files
The following files were used as context for generating this wiki page:
The Backup & Restore system provides first-party local protection for the canonical PRKS library. It allows you to create verified .prks-backup archives containing the SQLite database and managed research files, and restores them with crash-safe durability guarantees.
Sources: backend/backup_restore.py:1-25, README.md:144-150
Architecture Overview
The system categorizes all persistent storage components into specific classes to determine what belongs in a backup archive. Only canonical state is backed up, while derived state is rebuilt after a restore.
Storage Classification
| Category | Description | Included in Backup | Rebuilt on Restore |
|---|---|---|---|
| Canonical | Relational database (prks_data.db), PDFs, and person portraits. | Yes | No |
| Derived | Thumbnails and search indexes (text and research). | No | Yes |
| Operational | Process locks (root.lock), markers (prks-root.json), and error logs. | No | No |
| Conditional | Files in the processing directory (included only if under storage root). | Optional | No |
Sources: backend/backup_restore.py:91-118, backend/AGENTS.md:104-110
Maintenance Directory Structure
Backup and restore operations occur within a hidden .prks-maintenance directory inside the storage root.
The maintenance subroots are anchored to the storage root and validated to prevent path escape via symlinks.
Sources: backend/backup_restore.py:270-300, tests/test_backup_restore.py:180-220
Backup Process
PRKS creates backups using an online snapshot of the SQLite database and a recursive walk of managed file directories.
Backup Workflow
- Snapshot: Performs an online SQLite backup to a temporary file.
- Inventory: Walks the
pdfs/,people/, and optionalfor_processing/directories. - Archiving: Packs components into a ZIP archive with a
manifest.json. - Verification: Re-opens and validates the archive's integrity and schema.
- Publication: Moves the verified archive to a ready name in the
.prks-maintenance/backupdirectory.
Lifecycle Management
Finished archives are accessible via a one-time token. PRKS expires tokens and reclaims abandoned archives from disk after 60 minutes.
Sources: backend/backup_restore.py:186-210, backend/backup_restore.py:257-265
Restore Process
Restoration is an exclusive operation that replaces the live canonical library. It uses a state machine and a write-ahead journal to ensure the library remains recoverable even if a machine crash occurs mid-process.
Restore Phases
- Staging: The backup is uploaded and extracted to a temporary staging directory.
- Preparation: A restore journal and a rollback tree are created.
- Moving Old: Existing live canonical components are moved to the rollback tree.
- Installing New: Extracted backup components are moved to live paths.
- Commit: The journal phase is updated to
committed, and temporary files are cleared.
Sources: backend/backup_restore.py:1186-1300, backend/AGENTS.md:112-125
Crash Recovery and Durability
PRKS enforces a durability convention using fsync on directories and files. A restore boundary must be established on stable storage before the process advances.
- Rename Durability: Source and destination parent directories are synced after components are moved.
- Journal Durability: The journal is replaced atomically and its parent directory is synced.
- Automatic Recovery: On startup, PRKS checks for an existing journal. If the journal is not
committed, it replays the rollback tree to restore the previous library.
API Endpoints
| Endpoint | Method | Description |
|---|---|---|
/api/backups/progress | POST | Starts backup creation and streams NDJSON progress events. |
/api/backups/download | GET | Downloads a completed backup archive using a one-time token. |
/api/backups/stage | POST | Uploads and validates a backup archive for future restoration. |
/api/backups/restore | POST | Executes the restoration of a staged backup. |
Sources: backend/server.py:1169-1260
Implementation Details
Path Safety
The system uses descriptor-relative, no-follow removal on supported platforms (POSIX) to prevent symlink attacks. It validates that the storage root identity has not changed during destructive operations by comparing os.stat results.
Sources: backend/backup_restore.py:465-537, tests/test_backup_restore.py:180-350
Disk Accounting
PRKS requires a specific disk margin before starting operations to prevent disk exhaustion.
- Backup: Needs space for the snapshot, the archive, and a 64 MiB margin.
- Restore: Uses the
Content-Lengthof the upload and the uncompressed size of the extracted payload to validate space before mutation.
Sources: backend/backup_restore.py:440-455, tests/test_backup_restore.py:1260-1290
Summary
The PRKS Backup & Restore system provides a robust, local-first mechanism for research data preservation. By classifying storage into canonical and derived states and implementing a durable, journaled restoration process, the project ensures that library data survives both application errors and system crashes. Post-restore, derived components like thumbnails and search indexes are automatically reconstructed to return the application to a fully functional state.
Sources: backend/backup_restore.py:1250-1280, backend/AGENTS.md:112-125