backups go to another machine, and old ones are pruned
Three things. The local directory defaults to <officerdev root>/dockers/postgres/dumps, derived from OFFICER_ROOT rather than written down — the same reasoning as rootPath: a path fixed at install freezes the layout of the machine it was installed on, and a restore elsewhere then points somewhere that is not there while looking deliberately chosen. The destination is SSH, and it is now what "set up" MEANS: a dump sitting on the machine it came from is a second copy on one disk, lost to the same failure that would make anybody want it. So local is staging and the copy off the box is the backup, and a schedule with nowhere to send it does not count as configured. Testable, in two steps reported separately, because they fail for different reasons and the fixes differ: the connection is keys and networking, the directory is permissions on the far side. It tests what is in the FORM rather than what is stored — testing a saved destination while somebody looks at a different one is worse than not testing. Everything goes out with BatchMode=yes. Nobody is at a terminal when this runs, so a key that has lost access must FAIL rather than block forever on a prompt nothing will answer. It is also why no credential is stored: there is none to store and none to leak. The remote path is the one value here a shell interprets — `ssh host mkdir -p <path>` runs through it — so shell characters are refused rather than escaped. Refusing is checkable by reading it; escaping is checkable by being right about every shell. And rotation keeps the newest N per database at both ends. Per database, because two sharing one budget means a busy one evicts the other's history. Sorted by NAME rather than mtime: the timestamp is in the filename and is sortable, while mtime stops meaning "when this was taken" the moment a file is copied. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -115,8 +115,36 @@ export const fofosAdminSettings = pgTable('fofos_admin_settings', {
|
||||
* half-configured state to interpret — it is off.
|
||||
*/
|
||||
backupCron: text('backup_cron'),
|
||||
|
||||
/**
|
||||
* Where dumps are written on THIS machine, before they are sent anywhere.
|
||||
*
|
||||
* Null means the default, computed at read time from `OFFICER_ROOT` — the same reasoning as
|
||||
* `rootPath` above: a path written in at install freezes the layout of the machine it was installed
|
||||
* on, and a restore elsewhere then points at a directory that is not there while looking chosen.
|
||||
*/
|
||||
backupDir: text('backup_dir'),
|
||||
|
||||
/**
|
||||
* Where they are COPIED to. `user@host`, and an absolute path on it.
|
||||
*
|
||||
* A backup that only exists on the machine it came from is not a backup — it is a second copy on the
|
||||
* same disk, lost to the same failure. So the local directory is staging and this is the destination.
|
||||
*
|
||||
* Authenticated by the owner's existing SSH key, never a password: there is nobody at a terminal when
|
||||
* this runs, and a credential stored here would be one this plugin had to hold and could leak.
|
||||
*/
|
||||
backupSshHost: text('backup_ssh_host'),
|
||||
backupSshPath: text('backup_ssh_path'),
|
||||
|
||||
/**
|
||||
* How many dumps to keep, per database, in each place.
|
||||
*
|
||||
* Null means keep everything, which is a real choice and a bad default — a nightly dump of a small
|
||||
* site fills a disk slowly enough that nobody notices until something else fails.
|
||||
*/
|
||||
backupKeep: integer('backup_keep'),
|
||||
|
||||
/**
|
||||
* When the last one finished, which is what makes the schedule survive a restart.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user