|
|
|
@@ -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.
|