Backups: a daily dedupe-friendly tar to a folder or S3, with restore (#5918-#5921)
CI & Build / Python lint (push) Successful in 3s
CI & Build / Build now, or wait for Android? (push) Successful in 3s
Android / Build, or is the channel already serving this? (push) Successful in 4s
Android / Core and FFI clippy and tests (push) Skipped
Android / Kotlin + Rust (APK) (push) Skipped
Android / Build the server image (push) Skipped
Desktop (Tauri) / Build, or is the channel already serving this? (push) Successful in 3s
CI & Build / Web typecheck and unit tests (push) Successful in 10s
CI & Build / Python tests (push) Successful in 14s
CI & Build / integration (push) Successful in 1m38s
Desktop (Tauri) / Web tests, clippy, Rust tests and rustfmt (push) Successful in 1m41s
CI & Build / Build & push image (push) Successful in 41s
Desktop (Tauri) / Windows installer (cross-compiled) (push) Successful in 2m18s
Desktop (Tauri) / Tauri desktop (Linux) (push) Successful in 3m5s
Desktop (Tauri) / Update manifest (push) Successful in 3s

#5918 "Backup file": an uncompressed tar of per-table JSON Lines (primary-key
order, sorted keys) plus every attachment file, with fixed tar headers, a
dated name, and an atomic write through a dot-named .part file. Unchanged
rows are the same bytes every day, so restic off-site stores only the edits.
No credential ever enters a backup.

#5919 "Backup destinations": a folder, or an S3-compatible bucket through a
stdlib SigV4 put/list client (checked against AWS's published signatures),
with an upload limit. Test destination only lists, so an append-only key works.

#5920 "Backup job": daily at a chosen hour, Back up now, a 1/2/4 h retry
ladder, a one-hour deadline, an advisory lock, the last 30 runs, and an
overdue verdict on Settings -> Backups.

#5921 "Restore": python -m inkwell.restore FILE into an empty instance, an
integration test that backs up and restores, and docs/backups.md with
Fabled Archives as the worked example.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-16 13:54:51 -04:00
co-authored by Claude Opus 5.5
parent c0de50d85d
commit edcb5c4c11
18 changed files with 2069 additions and 13 deletions
+7 -7
View File
@@ -14,13 +14,10 @@
# Every push publishes an immutable :<sha> image for exactly this.
#
# Schema migrations run automatically at container start (see the Dockerfile CMD),
# so an upgrade is just a pull and a restart. Take a backup first anyway:
#
# docker compose exec -T db pg_dump -U inkwell inkwell > backup.sql
#
# Attachments are files, not rows — they live in the `inkwell-data` volume and
# a pg_dump does NOT contain them. Back up both or you'll restore notes whose images
# are gone.
# so an upgrade is just a pull and a restart. Take a backup first anyway: Settings →
# Backups → Back up now writes one file holding the notes AND their attachments
# (docs/backups.md). A bare pg_dump does not: attachments are files in the
# `inkwell-data` volume, not rows.
services:
db:
@@ -70,6 +67,9 @@ services:
# Uploaded attachments. /var/inkwell is fixed in the app (Config.DATA_DIR),
# not configurable — mount it or lose every image on container recreation.
- inkwell-data:/var/inkwell
# Daily backups to a folder (Settings → Backups, docs/backups.md): mount the
# share they should land in. Not needed when they go to an S3 bucket.
# - /mnt/backups/inkwell:/backups
# WHERE THE APP IS REACHABLE FROM. Three shapes, and the right answer is
# different for each — the default serves the first.
#