From 1196bd759839f68c6a2136087e163378a9fbd919 Mon Sep 17 00:00:00 2001 From: clawbot Date: Mon, 9 Nov 2026 02:29:25 +0000 Subject: [PATCH] Say what a backup needs on an rclone remote and fix four more findings - `README.md`, `docs/REPOSTRUCTURE.md`, `internal/cli/snapshot.go`: a backup needs only write permission on S3; an rclone remote that can move objects also needs read and delete, and an upload there stops when its look-up is refused. - `docs/DATAMODEL.md`, `docs/REPOSTRUCTURE.md`: a restore reads no file, chunk or blob rows from the local index, but it does open it. - `README.md`: confirmation prompts go to stdout. - `internal/cli/remote.go`: `remote nuke` leaves the backup destination store empty, not a bucket prefix. - `README.md`: a TODO section, as `REPO_POLICIES.md` requires. Model: fable-5-1 --- README.md | 23 ++++++++++++++++------- docs/DATAMODEL.md | 4 ++-- docs/REPOSTRUCTURE.md | 13 +++++++++---- internal/cli/remote.go | 4 ++-- internal/cli/snapshot.go | 3 ++- 5 files changed, 31 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 16f2aae..50e743e 100644 --- a/README.md +++ b/README.md @@ -311,8 +311,8 @@ It needs: - At least one public key in `age_recipients`. - Read access to the configured paths. -- Permission to write objects at the destination. See - [Destination permissions](#destination-permissions). +- Permission to write objects at the destination, and more on some rclone + remotes. See [Destination permissions](#destination-permissions). - Free temporary space. See [Temporary files](#temporary-files). - The [lock](#locking). @@ -1025,9 +1025,9 @@ A leftover `.partial` file is ignored. You can delete it. Commands need these permissions at the destination: -- `snapshot create` without `--prune` needs only permission to write objects. A - machine you back up can therefore hold credentials that can neither read nor - delete. +- `snapshot create` without `--prune` writes objects. On S3 it needs no other + permission, so a machine you back up can hold credentials that can neither + read nor delete. - These commands list and read: - `snapshot list` - `snapshot verify` @@ -1039,6 +1039,10 @@ Commands need these permissions at the destination: - `remote nuke` - `prune` and `snapshot create --prune` list, read and delete. +An rclone remote that can move objects also needs permission to read and +delete. An upload there looks up the object it replaces and deletes it. It stops +when the look-up is refused. + `snapshot verify` and `snapshot restore` list only when you give a remote key. `snapshot remove --local-only` does not reach the destination. `snapshot remove --dry-run` deletes nothing. @@ -1046,8 +1050,8 @@ Commands need these permissions at the destination: A machine that runs `snapshot create --prune` can delete its own backups. To keep it to write-only credentials, run `prune` from a separate, trusted machine. -A backup tries to look up the marker file and each blob before it writes. It -goes on when the destination refuses those look-ups. +A backup tries to look up the marker file and each blob before it writes. On S3 +it goes on when the destination refuses those look-ups. ### The marker file @@ -1166,6 +1170,7 @@ progress: stdout carries the output you asked for: - status and progress lines +- confirmation prompts - warnings, and the errors that `snapshot create` and `snapshot restore` print about files - tables and reports @@ -1477,6 +1482,10 @@ Each archive holds three files: The release files are not signed, have no software bill of materials and are not built reproducibly. +## TODO + +Planned work is listed in [`TODO.md`](TODO.md). + ## license [MIT](https://opensource.org/license/mit/) diff --git a/docs/DATAMODEL.md b/docs/DATAMODEL.md index e380962..12f8223 100644 --- a/docs/DATAMODEL.md +++ b/docs/DATAMODEL.md @@ -11,8 +11,8 @@ The local index has two jobs: - It is the source of each snapshot's own database. A backup uploads a trimmed, encrypted copy of the index with every snapshot. -A restore does not need the local index. It reads the copy that was uploaded -with the snapshot. +A restore reads no file, chunk or blob rows from the local index. It reads the +copy that was uploaded with the snapshot. ## Terms diff --git a/docs/REPOSTRUCTURE.md b/docs/REPOSTRUCTURE.md index 3670ab7..caf90a1 100644 --- a/docs/REPOSTRUCTURE.md +++ b/docs/REPOSTRUCTURE.md @@ -105,7 +105,7 @@ This has two consequences: A backup looks a blob up by name before it uploads it. It does not upload a blob that is already there. -When the destination refuses the look-up, the backup uploads the blob. An +On S3, when the destination refuses the look-up, the backup uploads the blob. An object with that name is then replaced by one that holds the same chunk data. ## Metadata @@ -314,8 +314,8 @@ Trust the temporary directory as much as the directory you restore into. ### Commands need different permissions -`snapshot create` without `--prune` needs only permission to write objects. -Other commands need more: +`snapshot create` without `--prune` writes objects. On S3 it needs no other +permission. Other commands need more: - These commands list and read: - `snapshot list` @@ -328,6 +328,10 @@ Other commands need more: - `remote nuke` - `prune` lists, reads and deletes. +An rclone remote that can move objects also needs permission to read and +delete. An upload there looks up the object it replaces and deletes it. It stops +when the look-up is refused. + `snapshot verify` and `snapshot restore` list only when they are given a remote key. `snapshot remove --local-only` does not reach the destination. `snapshot remove --dry-run` deletes nothing. @@ -421,7 +425,8 @@ To avoid this case, give each machine and each user its own destination. - the snapshot's `db.zst.age` - the blobs that hold the files to restore -A restore does not read the manifest and does not need the local index. +A restore does not read the manifest, or the file, chunk and blob rows of the +local index. It works in these steps: diff --git a/internal/cli/remote.go b/internal/cli/remote.go index 3dbc486..5b3301c 100644 --- a/internal/cli/remote.go +++ b/internal/cli/remote.go @@ -42,8 +42,8 @@ func newRemoteNukeCommand() *cobra.Command { Short: "Delete ALL snapshot metadata and blobs from the backup destination store", Long: `Removes every snapshot's metadata, every blob and the destination marker from the backup destination store, and each removed snapshot from -the local index. After this command completes successfully the bucket -prefix is empty and the next backup starts from scratch. +the local index. After this command completes successfully the backup +destination store is empty and the next backup starts from scratch. This is destructive and irreversible. Requires --force, and permission to list and delete objects on the destination store.`, diff --git a/internal/cli/snapshot.go b/internal/cli/snapshot.go index 997a6d9..4a6a647 100644 --- a/internal/cli/snapshot.go +++ b/internal/cli/snapshot.go @@ -69,7 +69,8 @@ If no names are provided, all configured snapshots are created. Needs at least one public key in age_recipients, and permission to write objects at the backup destination store. It does not need the age private key. With --prune it also needs permission to list, read and delete objects -there. +there. An rclone remote that can move objects also needs permission to +read and delete. The config is read from the path given by --config or VAULTIK_CONFIG; otherwise from the platform config directory (~/.config/vaultik/config.yml