Merge pull request 'to-be 43: backups against mistakes (graduates research 030)' (#90) from design/43-backups-against-mistakes into main

This commit was merged in pull request #90.
This commit is contained in:
2026-10-05 09:35:22 +00:00
3 changed files with 84 additions and 2 deletions
@@ -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]
---
@@ -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 `<name>_restore` owned by the consumer's role, a path as `<path>.restored-<date>`. 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.
@@ -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