diff --git a/01-RESEARCH/030-backups-against-our-own-mistakes/00-overview.md b/01-RESEARCH/030-backups-against-our-own-mistakes/00-overview.md index 9a2a9a6..25468bf 100644 --- a/01-RESEARCH/030-backups-against-our-own-mistakes/00-overview.md +++ b/01-RESEARCH/030-backups-against-our-own-mistakes/00-overview.md @@ -1,5 +1,6 @@ --- -status: active +status: graduated +became: [03-DESIGN/01-to-be/43-backups-against-mistakes.md, 02-DECISIONS/0214-backups-guard-against-mistakes-and-stay-on-the-machine.md] initiated: 2026-10-05 touches: [04-ISSUES/242-the-mesh-has-no-backups, 02-DECISIONS/0030-data-outlives-the-mesh-that-declared-it.md, 02-DECISIONS/0053-a-step-that-runs-on-a-schedule.md, 03-DESIGN/01-to-be/31-a-module-declares-its-fail2ban-jail.md] --- diff --git a/03-DESIGN/01-to-be/43-backups-against-mistakes.md b/03-DESIGN/01-to-be/43-backups-against-mistakes.md new file mode 100644 index 0000000..fa0b46c --- /dev/null +++ b/03-DESIGN/01-to-be/43-backups-against-mistakes.md @@ -0,0 +1,81 @@ +--- +layer: to-be +status: designed +code: [] +updated: 2026-10-05 +decisions: + - 02-DECISIONS/0214-backups-guard-against-mistakes-and-stay-on-the-machine.md + - 02-DECISIONS/0053-a-step-that-runs-on-a-schedule.md + - 02-DECISIONS/0085-a-secret-is-a-provision.md + - 02-DECISIONS/0112-a-module-definition-names-no-node-mesh-or-path.md +--- + +# 43 — Backups against mistakes: a module declares its data, the node keeps restore points + +**Every machine with data keeps a restore point of it for every night of the last two weeks, every +week of the last two months and every month of the last half year, on the machine itself.** It is +there for the day a person, an agent or the mesh does something wrong — drops a database, empties a +bucket, runs a bad migration — and not for the day a disk dies (ADR 0214). + +## The shape + +- **A node seat, `node-backup`,** held on each machine by one module, named for the tool it wraps. + It keeps one encrypted, deduplicating repository on the machine and runs a nightly scheduled step + (ADR 0053). The repository's key is a secret provisioned to the holder (ADR 0085), held in the + vault, so a person can open the repository without the holder running. +- **A module declares its data in its manifest,** naming no node and no absolute path (ADR 0112), + in one of two forms: + - **a dump** — for a store provider: the command that writes a consistent, logical copy of each + database it serves, run by the provider in its own container, its output handed to the holder. + The provider covers every consumer it provisions, so a module that only *uses* a database + declares nothing. + - **paths** — for files a module keeps itself (mailboxes, the forge's attachments, a service's + state directory), named through the module's own directory references. +- **The mesh composes the declarations per node,** as it composes jails (to-be 31) and filters: the + holder receives, as contributions, exactly the data of the modules assigned to its machine. A + module assigned is covered the next night; a module unassigned stops being backed up, and its + restore points age out by the rotation, never at once. + +## A night + +Each declared dump runs and writes a full logical copy; each declared path is read as it stands. All +of it goes into the repository as one snapshot, tagged by module. The repository keeps only chunks it +has not seen, so the object store's first night costs its full size and later nights cost what +changed. Then the rotation prunes to 14 daily, 8 weekly and 6 monthly snapshots. A dump that fails +fails the night for that module only; the others are still taken. + +## The verbs + +On the seat, for a person or an agent: + +- **what is backed up here** — each module, what it declared, its last good night and its size; +- **take one now** — for one module or all, before a risky act; a migration or a database's retirement + calls it first; +- **restore** — one module's database or path, from a named night, **beside** the live one: a database + as `_restore` owned by the consumer's role, a path as `.restored-`. Swapping it + in stays a person's act. Nothing restores over live data. + +## Where the repository lives + +On the machine, outside every directory the mesh manages, on a filesystem other than the live data's +where the machine has one. The holder's module names the place as a machine setting, never a path in +a manifest. Not off-site: a lost machine loses its backups with its data, by the operator's choice. + +## Proving it + +- A night that fails, or does not run, reaches the operator's output channel, naming the module. +- Weekly, the holder checks the repository's integrity and restores the newest dump of one database, + in rotation, into a throwaway instance with no network, comparing table row counts with the live + database. +- The mesh's status lists every machine whose last good night is older than 48 hours. + +## Not in this design + +- Copies off the machine, and encryption to anyone but the mesh's own vault. +- The media library and anything else a module declares no data for. +- Recreatable things: container images, the artifact registry, caches. + +## How it is checked + +The catalogue check refuses a module that provides a store seat and declares no dump. The weekly +restore test above, and the 48-hour status line, are the running checks. diff --git a/04-ISSUES/242-the-mesh-has-no-backups/00-report.md b/04-ISSUES/242-the-mesh-has-no-backups/00-report.md index 247096f..0821cef 100644 --- a/04-ISSUES/242-the-mesh-has-no-backups/00-report.md +++ b/04-ISSUES/242-the-mesh-has-no-backups/00-report.md @@ -3,7 +3,7 @@ status: open opened: 2026-10-05 located-in: [] fixed-by: -amended-design: +amended-design: 03-DESIGN/01-to-be/43-backups-against-mistakes.md --- # 242 — The mesh has no backups