Files
backup-tool/docs/superpowers/specs/2026-07-27-backup-tool-reimplementation-design.md
T

34 KiB

Backup Tool v2 Reimplementation Design

Status: Decision-closed baseline
Date: 2026-07-27
Supersedes: 2026-05-11-backup-tool-design.md for new implementation work
Evidence base: current backend, frontend, tests, deployment files, README, and prior design
Normative language: MUST, MUST NOT, SHOULD, and MAY have their RFC 2119 meanings.

1. Reason for Existence

This document is the implementation contract for a from-scratch v2. It separates behavior that exists, behavior that is broken, and previously proposed features; then defines one coherent product, integrity model, API, architecture, and delivery boundary. An implementation is conformant only when its tests prove the acceptance invariants in §22.

2. Executive Decision

Backup Tool v2 is a self-hosted, single-organization backup appliance for a trusted administrator. It backs up local files, SSH/SFTP trees, PostgreSQL databases, and MySQL databases into administrator-defined local repositories; schedules and monitors work; verifies, restores, retains, and safely deletes backups.

V2 is a clean protocol revision, not a line-for-line rewrite. It keeps useful domain concepts—Source, Job, Schedule, Execution, Backup, dashboard, REST API, React UI, SQLite, manual and cron triggers—but rejects accidental compatibility with unsafe or nonfunctional behavior.

The central design decision is an immutable, content-addressed repository. Each backup has a complete versioned manifest and is independently restorable; incremental mode reduces transfer/storage by reusing blobs rather than creating fragile restore chains.

3. Current-State Audit

Area Current evidence Classification V2 treatment
Source CRUD local, ssh, database; arbitrary JSON config Implemented Preserve concept; replace with typed configs and write-only secrets
Job CRUD Source, strategy, destination path, excludes, enabled Implemented Preserve; target a repository ID, add typed policy fields
Manual run In-process FastAPI background task; returns message only Implemented but fragile Durable execution resource; return 202 and execution ID
Scheduling One five-field cron row per job; startup-only sync Partly implemented Durable, timezone-aware, immediately synchronized scheduler
Local backup Full recursive copy2 into timestamp directory Implemented Replace storage protocol; preserve local source capability
SSH/database backup Adapter connects, engine still traverses a local path Broken Implement real adapter enumeration/stream/dump paths
Incremental Links latest full metadata but copies every file Broken/misnamed Complete manifests plus deduplicated blobs
Exclusions Stored but ignored by engine Broken Gitignore-compatible normalized-path matching
Retention Engine reads nonexistent job fields Unreachable First-class policy, preview, audit, safe GC
Backups List/get/delete metadata; payload deletion inconsistent Partly implemented Snapshot catalog, verification, restore, download, tombstone deletion
Executions List/get with basic counters and raw error string Implemented State machine, progress, reason codes, cancellation, protected logs
Settings Arbitrary string key/value API; UI does not save Placeholder Typed settings only; no arbitrary public key/value store
Dashboard Backend aggregate exists; frontend calls different routes Contract mismatch One generated OpenAPI contract
Security No auth; credentials stored and returned in plaintext Unacceptable Mandatory admin auth, encrypted secret references, redaction
Integrity Aggregate hash excludes paths/metadata; no restore/check Insufficient Per-blob digest, authenticated manifest, verify and restore
Deployment API embeds worker/scheduler; SQLite; Docker/nginx Implemented One image/package, explicit web/scheduler/worker roles

Current tests establish only source/job CRUD, 404 behavior, local full backup, manual-run acknowledgement, schedule creation, and incremental-without-baseline fallback. They are migration evidence, not authority to preserve weak status codes, exact error strings, timestamp paths, or checksums.

4. Product Definition

4.1 Users and jobs to be done

  • Administrator: configure repositories, sources, credentials, jobs, schedules, retention, and security.
  • Operator: run/cancel jobs, inspect progress and failures, verify backups, restore data, and review storage health.
  • A single person MAY hold both roles. V2 has no tenant boundary or general-purpose RBAC.

4.2 Required workflows

  1. Complete first-run admin setup and repository initialization.
  2. Add and test a local or SSH/SFTP source without exposing its secret; add PostgreSQL/MySQL sources when v2.1 capabilities are installed.
  3. Create an enabled job with source, repository, mode, exclusions, schedule, and retention.
  4. Run manually or by cron; observe queue, progress, logs, outcome, and effective strategy.
  5. Browse, filter, verify, pin, restore, or request deletion of a committed backup; download becomes available after v2.0.
  6. Preview and apply retention; run repository reconciliation and garbage collection.
  7. See dashboard health, capacity, next runs, active work, failures, and verification status.
  8. Configure signed webhooks and email subscriptions, inspect delivery history, and export an offline key-recovery bundle.

4.3 Initial release scope

V2.0 MUST ship local and SSH/SFTP sources, local repositories, optional-at-creation repository encryption, full and deduplicating incremental backups, exclusions, durable execution, cron, retention, verification, restore, authenticated UI/API, signed webhooks, email notifications, offline recovery bundles, Docker deployment, and operational diagnostics. PostgreSQL/MySQL sources and generated TAR downloads are committed v2.1 scope. V2 provides no v1 API, metadata importer, or legacy-payload compatibility.

4.4 Non-goals

Multi-tenancy, enterprise RBAC, horizontal workers, distributed scheduling, continuous data protection, database PITR/WAL/binlog capture, cloud repositories, tape management, cross-repository deduplication, and source-native snapshot orchestration are deferred.

5. Normative Decisions

ID Decision Consequence
D1 External resources use UUIDv7 and /api/v2; no v1 API or compatibility shim exists Clean protocol break; sortable non-enumerable IDs
D2 One install serves one organization and one initial admin Security remains strong without premature tenancy
D3 Web, scheduler, worker, and migrate are separate runtime roles from one codebase/image API never performs backup I/O; no duplicate embedded schedulers
D4 SQLite/WAL is the v2 metadata DB and supports one scheduler plus one worker process Simple single-node deployment; horizontal scale explicitly unsupported
D5 Execution claims live in the DB with leases, heartbeats, idempotency keys, and retries Restart-safe, effectively-once publication
D6 Jobs target configured repositories, never arbitrary destination paths Enforce containment, capacity, permissions, and repository policy
D7 Backup = complete logical snapshot manifest over immutable content-addressed blobs Every retained backup restores independently
D8 Repository encryption is operator-selected at creation; compression/encryption policies are then immutable Plaintext and encrypted repositories are supported without mixed-policy ambiguity
D9 Exclusions use gitignore semantics over normalized relative POSIX paths Same predictable result across adapters
D10 One cron schedule per job; IANA timezone is required Preserve simple model; make DST behavior explicit
D11 Disabled jobs reject manual and scheduled starts One unambiguous meaning for enabled
D12 Source/job deletion archives configuration and stops work; it never silently deletes backups Data lifecycle remains explicit and auditable
D13 OpenAPI generates the TypeScript client and CI rejects drift Frontend/backend cannot diverge silently
D14 Alembic is the only schema evolution path; startup never calls create_all Repeatable upgrades and rollback planning
D15 V2.0 ships local and SSH/SFTP sources; PostgreSQL/MySQL follow in v2.1 Remote file backup is launch scope without delaying on database dump compatibility
D16 Certified single-node scale is 100 jobs, 1M entries or 10 TiB per backup, and 100k backups Release tests use a documented reference host and publish throughput rather than claiming a hardware-independent rate
D17 Encrypted repositories use a separately passphrase-protected offline recovery bundle Host loss is recoverable without weakening live key storage
D18 V2 is a clean start with no v1 metadata or payload migration Reimplementation carries no backward-compatibility code or legacy format liability
D19 Generated TAR download follows in v2.1; verified restore is the v2.0 extraction path Initial release keeps one secure data-output workflow
D20 Signed webhooks and email notifications ship in v2.0 for all operational event families Alerts are real product behavior, not placeholder settings
D21 Notifications retry transient failures and preserve delivery history Webhooks are at-least-once with stable event IDs; email delivery remains auditable

6. System Architecture

Browser/CLI -> Web API -> SQLite metadata + durable execution queue
                    |                 ^
                    v                 |
               SSE event stream   Scheduler (single leader)
                                      |
                                      v
Source <- adapter <- Worker -> Repository (staging, blobs, manifests, quarantine)
                         |-> verify / restore / retention / reconcile / GC
  • Web: session/API-token auth, validation, OpenAPI, CRUD, dashboard queries, SSE, static UI. MUST NOT enumerate sources, run dumps, copy files, or mutate repository objects.
  • Scheduler: computes due occurrences and transactionally enqueues them. Exactly one supported instance with SQLite.
  • Worker: leases executions, performs bounded blocking/async I/O, reports heartbeats/progress, publishes backups, restores, verifies, and reconciles.
  • Migrate/admin: Alembic upgrades, repository init/check, secret-key rotation, offline recovery-bundle export/validation, and metadata backup/restore.
  • Frontend: React/TypeScript responsive SPA using only generated client types. Production is same-origin behind one reverse proxy; FastAPI static serving MAY be used only if packaging tests prove it.

7. Domain Model and Invariants

Entity Required data Key invariants
User UUID, username, Argon2id hash, state, timestamps First run creates one admin; hash never returned
API Token owner, hash, scopes, expiry/revocation Plain token shown once
Secret encrypted payload, key ID, purpose, version API returns reference/status only
Repository name, root, format version, compression/encryption policy, state Root under configured allowlist; policy immutable after init
Source name, kind, typed public config, secret refs, state Type changes require replacement; test result stored separately
Job source, repository, requested mode, excludes, retention, enabled At most one active execution; archived refs remain readable
Schedule job, cron, timezone, misfire grace, overlap policy, enabled At most one per job; (schedule, nominal time) unique
Execution job, trigger, state, attempt, lease, progress, reason, timestamps Monotonic transitions; every terminal state has reason code
Backup execution, manifest ID/digest, logical/stored bytes, integrity, pin/tombstone Immutable after commit except lifecycle/integrity annotations
Restore backup, selection, destination, overwrite policy, state/result Durable execution with containment and audit
Audit Event actor, action, resource, outcome, request ID, time Append-only; secrets excluded
Notification Subscription channel, event filters, destination config/secret, state Webhook URL or email recipients are validated; secret fields are write-only
Notification Delivery event ID, subscription, attempt, state, response class, timestamps Durable retry history; payload contains no secret material
Idempotency Record actor, key, operation, request digest, response resource Same key+payload returns same result; mismatch is 409

All times MUST be timezone-aware UTC RFC 3339 externally. Foreign keys, uniqueness, delete behavior, and indexes MUST be explicit. Public list APIs MUST use stable cursor pagination. Raw ORM objects MUST NOT serve as API/domain models.

8. Typed Configuration

Input Default Rule
DATABASE_URL SQLite under data dir Absolute, startup-validated; SQLite foreign keys, WAL, busy timeout enabled
DATA_DIR /var/lib/backup-tool Metadata and runtime state only
REPOSITORY_ROOTS none Required allowlist of canonical destination roots
LOCAL_SOURCE_ROOTS none Required allowlist for local sources
MASTER_KEY_FILE none Required before storing secrets; mode/owner checked
PUBLIC_BASE_URL loopback URL Used for cookies, redirects, and origin validation
CORS_ORIGINS empty Same-origin default; explicit exact origins only
WORKER_CONCURRENCY 1 V2 SQLite limit remains one active job globally by default
MIN_FREE_BYTES / MIN_FREE_PERCENT documented safe values Repository preflight and alert thresholds
LOG_LEVEL INFO Structured logs; runtime override may be typed setting

Environment MUST control deployment invariants. Typed DB settings control operator preferences such as default timezone, audit retention, verification cadence, SMTP transport, and notification retry limits. Notification destinations and credentials use typed subscription/secret resources, not arbitrary settings. Unknown settings MUST be rejected.

9. Source Adapter Contract

Every adapter implements validate_config, probe, enumerate_entries, open_content, capture_consistency_metadata, and close. Engine code MUST obtain source bytes only through this contract.

Common rules: stream with backpressure; bounded timeouts/concurrency; normalized relative paths; common exclusion matcher; preserve directories, files, symlinks, and selected mode/mtime metadata; do not follow external symlinks; report unsupported special files explicitly; classify errors as auth, trust, unavailable, permission, timeout, source-changed, unsupported-entry, transient-I/O, or internal.

Adapter Typed fields and behavior
Local canonical root, cross-filesystem flag, symlink policy; root must be allowlisted
SSH/SFTP host, port 22, username, remote root, auth secret, pinned known-host key; unknown/changed keys fail closed
PostgreSQL host/socket, port 5432, database, username, secret, TLS, dump format; stream pg_dump, record tool/server versions; full logical snapshots only
MySQL host/socket, port 3306, database, username, secret, TLS, consistency/locking policy; stream mysqldump; full logical snapshots only

A source probe validates connectivity, trust, permissions, required native tooling, and capability metadata but MUST NOT persist or return credentials.

10. Repository and Backup Protocol

<repository>/repository.json
             blobs/sha256/<first-two>/<digest>
             manifests/<backup-uuid>.json
             staging/<execution-uuid>/
             quarantine/

repository.json declares repository UUID, format version, digest algorithm, compression, encryption, and creation metadata. Blobs are immutable and addressed by SHA-256 of plaintext content; stored representation MAY be compressed/encrypted and MUST use authenticated encryption when enabled.

Each manifest is canonical JSON and binds format version, repository/source/job/execution IDs, requested/effective mode, UTC times, source consistency metadata, exclusion policy/version, normalized path, entry type, logical size, blob digest, selected portable metadata, link target, metadata-support flags, aggregate logical/stored counts, encryption key ID, and manifest digest/signature.

  • Full: enumerate and read every included file; verify every new stored object.
  • Incremental: compare against latest compatible committed manifest and reuse blobs. Metadata shortcuts MAY skip reads only when adapter evidence is declared reliable; a full run remains the correctness fallback.
  • Missing incremental baseline becomes effective full with reason baseline_missing, preserving the useful current behavior explicitly.
  • Every manifest is complete. parent_backup_id MAY record lineage but MUST NOT be needed to restore.
  • Empty sources fail unless job explicitly sets allow_empty; an unexpectedly empty previously nonempty source always requires operator-visible confirmation.
  • Publication uses unique staging, capacity preflight, temp objects, fsync where supported, atomic rename, verification, metadata commit, and reconciliation markers. Wall-clock names never identify new backups.
  • A backup is visible as committed only after its manifest and all referenced blobs are durable and verified.

11. Execution, Concurrency, and Recovery

queued -> preparing -> running -> verifying -> committed
   |          |           |           |-> failed
   |          |           |-> cancelling -> cancelled
   |          |-> failed
   |-> cancelled

Enqueue returns a durable execution immediately. Only one nonterminal execution per job is allowed; duplicates return 409 with the active resource. Scheduler occurrence keys and client Idempotency-Key prevent duplicate publication. Worker claims use lease expiry and heartbeat.

Retries reuse the execution ID and increment attempt only for classified transient errors. Cancellation is cooperative and cannot turn a committed backup into cancelled. Process loss before publication leaves recoverable staging; startup reconciliation either resumes a safe step, requeues, or fails with worker_lost. Errors expose stable code plus redacted operator text; sensitive diagnostics remain in access-controlled structured logs.

Progress includes phase, files/bytes scanned, read, stored, deduplicated, warnings, throughput, and heartbeat time. Blocking filesystem, Paramiko, and subprocess work MUST run outside the web event loop.

12. Scheduling

Schedules MUST persist five-field cron, IANA timezone, enabled state, next occurrence, last enqueue outcome, misfire grace, and overlap policy. API writes MUST use semantic cron validation and take effect without restart.

Defaults: 15-minute misfire grace, coalesce missed occurrences into one, prohibit overlap, skip nonexistent DST wall times, and execute repeated DST wall time once at its earliest instant. Enqueue is transactionally unique by schedule and nominal UTC occurrence. Disabled schedule or job does not enqueue.

13. Retention, Deletion, and Garbage Collection

Policies MAY combine keep-last, keep-for-days, daily, weekly, monthly, and pinned backups. Keep rules form a union: a backup survives if any rule keeps it. The newest committed backup is retained unless the operator explicitly purges the job.

Retention MUST provide preview and apply operations. Deletion MUST first create a tombstone/audit event; GC removes unreferenced blobs only after a grace period and a second reference scan. Failed physical deletion remains deletion_failed and retryable. Unknown objects are quarantined, never automatically destroyed. Source/job archive does not delete backups. Explicit purge requires confirmation and produces a durable report.

14. Restore, Download, Verification, and Reconciliation

Restore MUST be durable and support dry run, path selection, destination allowlist, and overwrite policy fail|skip|replace. It MUST verify every blob before staged/atomic placement and write a result manifest. Unsafe absolute/traversal paths, device entries, and escaping symlinks are rejected.

Verification modes are metadata-only and full-content; scheduled scrubs MAY run periodically. Backup integrity is unverified|verified|degraded|corrupt. V2.0 exposes verified restore only. V2.1 download streams a generated TAR from a committed manifest and never exposes repository paths. Reconciliation checks DB-to-storage and storage-to-DB, repairs known interrupted transitions, quarantines unknown data, and never silently erases evidence.

15. HTTP API Contract

Conventions: /api/v2; UUID strings; RFC 3339 UTC; JSON; cursor pagination; stable sort; 201 create; 202 enqueue; 204 successful idempotent delete where applicable; RFC 9457 problem details with stable code; Idempotency-Key on enqueue/destructive mutations; optimistic version/ETag on mutable config.

Resource Required operations
Auth setup, login/logout, session, password change; API-token create/list/revoke
Sources list/create/get/update/archive; probe/test; credential rotate
Repositories list/create/get; probe/capacity; verify/reconcile/GC
Jobs list/create/get/update/archive; enqueue execution; retention preview
Schedules get/create-or-replace/update/disable/delete; next occurrences
Executions list/get/cancel/retry; event/log stream via SSE with polling fallback
Backups list/get/pin/unpin; verify; tombstone/delete; purge preview; v2.1 download
Restores list/create/get/cancel; result report
Notifications subscriptions CRUD/test; event catalog; delivery history/retry
Dashboard health/capacity summaries, active/recent executions, next runs, failures
Settings/Audit typed settings get/update; paginated audit events
Administration export/validate offline recovery bundle; rotate keys; metadata backup
Operations /livez, /readyz, version/capabilities; admin actions remain authenticated

OpenAPI is committed/generated in CI; generated frontend client code MUST have no handwritten duplicate resource interfaces.

16. Frontend Information Architecture

Routes: Setup/Login, Dashboard, Sources, Repositories, Jobs, Job Detail/Schedule, Executions, Execution Detail, Backups, Backup Detail/Restore, Settings/Security, and Audit.

Dashboard shows repository health/capacity, active queue, enabled jobs, next runs, backup count/logical/stored/deduplicated bytes, recent failures, and verification warnings. Source/repository forms are type-driven and include test actions. Job creation is a guided flow. Execution detail uses SSE progress and redacted logs. Backup detail exposes integrity, manifest summary, pin, verify, restore, and deletion preview; download appears only when v2.1 capability metadata enables it. Settings includes webhook/email subscriptions, test delivery, retry history, and recovery-bundle export.

Every view defines loading, empty, validation, partial failure, offline/reconnect, expired-session, unsupported-capability, and responsive states. Destructive actions require impact preview and explicit confirmation. Unsupported or unreleased controls are absent, not disabled mockups. Keyboard navigation, visible focus, semantic labels, contrast, reduced motion, and screen-reader announcements are acceptance requirements.

17. Security and Privacy

  • First-run admin auth is mandatory before non-loopback exposure. Passwords use Argon2id; browser sessions use Secure/HttpOnly/SameSite cookies plus CSRF protection.
  • API tokens are hashed, scoped, expiring, and revocable. Same-origin is default; CORS is off unless exact origins are configured.
  • Secret values are write-only, envelope-encrypted with an external master key, versioned, redacted everywhere, and independently rotatable. Encrypted installs MUST support export and validation of a recovery bundle encrypted under a separate recovery passphrase; the bundle is never stored beside the live master key.
  • SSH host keys are pinned. TLS verification is on for database connections. Native dump commands receive secrets without command-line/process-list exposure.
  • Repository and data directories are owned by a non-root service user with restrictive modes. Canonical path containment and symlink policy are enforced before I/O.
  • Audit events cover auth, config, execution, restore, verification, deletion, GC, secret rotation, and migration. Logs, errors, manifests, metrics, and audits MUST NOT contain credentials.
  • Backup encryption uses authenticated encryption; threat model states whether manifests, filenames, sizes, and deduplication equality are confidential.
  • Webhook targets MUST pass scheme, DNS/IP, redirect, and private-network policy checks to prevent SSRF. Webhook signatures use a rotatable secret and stable event ID. SMTP credentials are write-only secrets.

18. Reliability, Performance, and Operations

Certified v2.0 target: one node, one worker, 100 jobs, 1 million entries or 10 TiB logical data per backup, and 100,000 cataloged backups. Release tests MUST run on a documented reference host, publish throughput and resource use, and reject material regression from the accepted baseline; no hardware-independent minimum throughput is claimed. All list APIs paginate. Enumeration and transfer stream with bounded memory. Disk preflight, minimum free space, per-source timeouts, chunk sizing, graceful shutdown, and rate limits are configurable.

/livez proves process liveness. /readyz verifies migrations, DB access, role health, and required repository access. Structured logs correlate request, job, execution, backup, and restore IDs. Metrics include queue age, schedule lag, duration, throughput, logical/stored/deduplicated bytes, failure class, retries, lease expiry, capacity, verification failures, and GC outcomes. Alerts cover repeated failure, stale lease, corrupt backup, low capacity, missed schedule, and repository unavailability.

Metadata backup/restore, master-key handling, repository recovery, interrupted-upgrade rollback, and full disaster recovery require runbooks. Graceful shutdown stops new claims and leaves work at a restart-safe checkpoint.

19. Deployment and Packaging

One versioned OCI image exposes web, scheduler, worker, and migrate commands. Compose runs same-origin reverse proxy/UI, one web, one scheduler, one worker, persistent metadata, and one or more mounted repositories. Images run non-root, pin dependencies, include only required dump clients, publish SBOM/provenance, and pass persistence/permission health tests.

Python 3.12 LTS and a current Node LTS SHOULD be the initial build baseline; exact versions are locked in implementation planning. Production MUST NOT use reload mode. Multiple web processes are allowed only when neither scheduler nor worker starts inside them.

20. Compatibility and Migration

V2 is a clean installation. It provides no /api/v1, compatibility shim, v1 database importer, legacy-directory reader, or payload converter. Existing v1 installations remain separate and MUST be decommissioned or archived by their operator; v2 never opens or mutates their database or backup directories.

Migration within v2 uses forward Alembic revisions and repository-format migrations with preflight, metadata/repository backup, resumability, and explicit rollback instructions. Old integer IDs, timestamp directories, status codes, arbitrary settings, and aggregate checksums are not v2 contracts.

21. Delivery Sequence

  1. Protocol foundation: repository/manifest spec, Alembic baseline, auth/secrets, local adapter, durable queue/leases, full backup, verification, restore, and fault injection.
  2. V2.0 complete product: deduplicating incremental, exclusions, schedules, retention/GC, reconcile, strict SSH/SFTP, generated client/UI, optional repository encryption, offline recovery bundle, signed webhooks, email, delivery history, and certified scale tests.
  3. V2.1 sources and extraction: PostgreSQL/MySQL dumps, native-tool/version checks, integration fixtures, resource limits, and generated TAR downloads.
  4. Operational hardening: periodic scrubs/restore drills, performance tuning, richer notification templates, and optional PostgreSQL metadata design.

Each phase is releasable only if its controls are truthful in API capability metadata, UI, and documentation.

22. Acceptance Invariants

  1. Committed backup implies durable verified manifest and all referenced blobs.
  2. Duplicate delivery of one schedule occurrence produces at most one committed backup.
  3. Concurrent starts cannot create two active executions for one job.
  4. Crash at every state transition leaves valid committed data or recoverable/quarantined staging.
  5. SSH/database bytes flow only through their adapter; no local-path fallback exists.
  6. Every committed backup restores without another backup manifest.
  7. Retention cannot make any retained backup unrestorable.
  8. Source/job deletion cannot silently delete backup payloads.
  9. Failed physical deletion remains visible and retryable.
  10. Credentials never appear in read APIs, OpenAPI examples, logs, errors, manifests, audits, or metrics.
  11. Unknown/changed SSH host keys fail before data transfer.
  12. Same normalized paths and rules produce identical exclusions across adapters.
  13. Disabled jobs cannot be enqueued manually or by schedule.
  14. Schedule create/update/delete takes effect without restart and has deterministic DST/misfire behavior.
  15. V2 contains no code path that opens, imports, converts, or mutates v1 metadata or payload formats.
  16. Frontend types/routes are generated from and match committed OpenAPI.
  17. Restore tests cover empty files, Unicode, large files, symlinks, permissions, source deletions, and interrupted writes.
  18. Blob, manifest, or authentication-tag corruption is detected before restore publication.
  19. Empty or unexpectedly vanished sources cannot silently replace a good backup.
  20. Backup/source paths cannot escape configured allowlists through traversal or symlinks.
  21. Recovery-bundle validation proves required keys are present without exposing plaintext key material; restore works after simulated host loss.
  22. Webhooks are signed and delivered at least once with stable event IDs; webhook and email transient failures retry and remain visible in delivery history.

23. Required Test Matrix

Unit: state transitions, exclusion semantics, manifest canonicalization, retention selection, cron/DST, error mapping, secret redaction, event filtering, and notification retries. Contract: OpenAPI golden, problem details, pagination, idempotency, ETag conflicts, webhook schema/signature, and generated client compile. Integration: each adapter, DB migrations, queue leases, crash recovery, repository publish, GC, restore, recovery bundle, SMTP, webhook delivery, and auth/CSRF. Fault injection: process kill, disk full, permission loss, network drop, source mutation, DB lock, checksum corruption, deletion failure, SMTP rejection, and webhook timeout. E2E: setup through restore, notification history, recovery export, and deletion preview in production-like Compose. Security: path traversal, symlink escape, host-key change, token scope/revocation, webhook SSRF/redirect, signature rotation, and secret leakage scan. Performance: certified scale targets with bounded memory and published reference-host throughput.

24. Resolved Product Decisions

Area Approved choice
Source release V2.0 includes local and SSH/SFTP; PostgreSQL/MySQL ship in v2.1
Encryption Operator-selected at repository creation; policy remains immutable
Scale Certify 100 jobs, 1M entries or 10 TiB per backup, and 100k backups on a published reference host
Key recovery Passphrase-protected offline recovery bundle with export and validation workflows
Backward compatibility None: no importer, legacy reader/converter, or v1 API shim
Download Verified restore in v2.0; generated TAR download in v2.1
Notifications Signed webhooks and email both ship in v2.0 for all operational event families
Delivery Retry transient failures, preserve delivery history, and use stable webhook event IDs for at-least-once delivery

25. Risks and Mitigations

Risk Mitigation
Content-addressed storage adds complexity Specify protocol first; golden repository fixtures; crash/corruption tests
SQLite limits concurrency State single-node limits; one scheduler/worker; design claims behind repository interface
Metadata shortcuts miss changed content Declare trust rules; periodic/full modes; verification and scrub
Encryption key loss destroys recoverability External key backup runbook, startup checks, explicit threat model
Notification endpoints create SSRF, spam, and secret risks Validate targets, sign webhooks, constrain retries/rates, protect SMTP credentials
DB dumps vary by server/client Record versions/options; integration compatibility matrix; fail unsupported pairs
Dedup leaks equality/size information Document threat; repository isolation; encryption policy fixed at creation
Scope expands before integrity works Enforce delivery sequence and capability truthfulness

26. Verification

The design artifact itself is valid when it contains normative decisions, current-state treatment, architecture, domain model, protocol, state machine, API/UI, security, operations, migration, tests, acceptance invariants, and open decisions:

python - <<'PY'
from pathlib import Path
p = Path('docs/superpowers/specs/2026-07-27-backup-tool-reimplementation-design.md')
s = p.read_text()
required = ['Current-State Audit', 'Normative Decisions', 'System Architecture',
            'Repository and Backup Protocol', 'Acceptance Invariants',
            'Compatibility and Migration', 'Resolved Product Decisions']
assert all(x in s for x in required)
assert s.count('MUST') >= 20
print('design-spec: OK')
PY

Next step: create the implementation plan beginning with repository/manifest protocol fixtures and failure-oriented acceptance tests—not framework scaffolding.