feat(backups): t-paliad-246 — Backup Mode Slice A (on-demand admin org export)
m/paliad#77 Slice A. Folds the unbuilt t-paliad-214 Slice 3 (org async
export) into a new "Backup Mode" surface gated by adminGate.
m's calls (all 4 material picks per design §2):
- Storage: local disk PALIAD_EXPORT_DIR (LocalDiskStore only)
- Format: .zip bundle (xlsx + JSON + CSV + README) — no-lock-in preserved
- paliadin_turns + paliadin_aichat_conversation: EXCLUDE structurally
- Scheduler (Slice B): nightly 03:00 UTC, env-tunable
Wiring:
- mig 123 adds paliad.backups catalog table (kind/status/storage_uri/
size/row_counts/warnings/error/deleted_at + admin-only RLS).
- ExportService.WriteOrg + orgSheetQueries enumerate 37 entity sheets
+ 12 ref sheets; REPEATABLE READ READ ONLY tx wraps the dump for
snapshot consistency (design §3.3).
- writeBundle + runSheetQuery refactored to take a sqlx.QueryerContext
so both *sqlx.DB (personal/project paths, unchanged) and *sqlx.Tx
(org snapshot path) work.
- BackupRunner orchestrates: catalog INSERT → audit INSERT
(event_type='backup_created') → WriteOrg → ArtifactStore.Put → patch
catalog + audit on success/failure.
- ArtifactStore interface + LocalDiskStore impl (defense-in-depth key
validation + URI-outside-dir guard).
- Sentinel actor for scheduled runs: actor_email='system@paliad',
actor_id=NULL — no phantom user in paliad.users.
- Admin handlers POST /api/admin/backups/run + GET list/get/download
behind adminGate(users, …); /admin/backups page + sidebar entry +
bilingual i18n keys.
- BackupRunner only wired when PALIAD_EXPORT_DIR is set; routes return
503 otherwise (same shape as requireDB).
Tests: 8 pure-function tests cover registry shape (no dups, paliadin
absent both as sheet name and SQL substring, ref__* sheets unscoped,
every sheet has ORDER BY) and LocalDiskStore (round-trip, bad-key
rejection, URI-traversal rejection, mkdir on construction).
go build ./... + go test ./internal/... clean. bun run build clean.
Slice B (BackupScheduler + retention cleanup) and Slice C (UI polish)
are separate follow-ups per head's instruction.