From 34d7d13d3e3ac963212adb7a3d2474e3bd05f036 Mon Sep 17 00:00:00 2001 From: sneak Date: Sun, 8 Nov 2026 23:29:37 +0000 Subject: [PATCH] Fix the four findings of the second review README.md: the opening no longer says the private key is needed only to restore; a backup never needs it, a deep verify does. ARCHITECTURE.md: the concurrency note says each open database uses one connection, since the metadata export opens a second one. docs/REPOSTRUCTURE.md: the size leak is about a secret near the outsider's data in the same blob, not the same chunk, because all new chunks of a blob share one zstd stream. Every path used as link text in the four documents is in backticks. Model: fable-5-1 --- ARCHITECTURE.md | 13 ++++++------- README.md | 10 +++++----- docs/DATAMODEL.md | 2 +- docs/REPOSTRUCTURE.md | 9 +++++---- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index a58cacd..a6f9033 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -9,8 +9,8 @@ things: Two other documents cover the stored data: -- [docs/DATAMODEL.md](docs/DATAMODEL.md) describes the local index. -- [docs/REPOSTRUCTURE.md](docs/REPOSTRUCTURE.md) describes the layout of the +- [`docs/DATAMODEL.md`](docs/DATAMODEL.md) describes the local index. +- [`docs/REPOSTRUCTURE.md`](docs/REPOSTRUCTURE.md) describes the layout of the destination. ## Terms @@ -60,7 +60,7 @@ source files and the reader that reverses it. - `internal/database`: the local index. It holds the schema and the repository types that read and write the tables. - [docs/DATAMODEL.md](docs/DATAMODEL.md#code) says which type serves which + [`docs/DATAMODEL.md`](docs/DATAMODEL.md#code) says which type serves which table. - `internal/storage`: the `Storer` interface and its three implementations, for S3, a local directory and rclone. @@ -225,7 +225,7 @@ file is never held in memory. The blob's name is `hex(SHA256(SHA256(uncompressed contents)))`. It is computed from the chunk data before compression and encryption. -[docs/REPOSTRUCTURE.md](docs/REPOSTRUCTURE.md#blobs) says what follows from +[`docs/REPOSTRUCTURE.md`](docs/REPOSTRUCTURE.md#blobs) says what follows from that. When a blob is closed, `Scanner.handleBlobReady` stores it: @@ -353,7 +353,7 @@ of object, once they are older than `prune_grace_period`: in the local index It deletes nothing when any manifest cannot be read. -[docs/REPOSTRUCTURE.md](docs/REPOSTRUCTURE.md#pruning) explains why this is +[`docs/REPOSTRUCTURE.md`](docs/REPOSTRUCTURE.md#pruning) explains why this is safe, and where it is not. ## The destination @@ -390,8 +390,7 @@ Every command that changes data calls `EnsureStorageBinding` first. ## Concurrency -- The local index is opened with one connection, so its queries run one at a - time. +- Each open database uses one connection, so its queries run one at a time. - The index runs in SQLite's WAL mode. A second process can therefore read it while a backup writes to it. - `Packer` guards its state with a mutex. diff --git a/README.md b/README.md index 7064798..8abef2d 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ with a public key and uploads them to an S3-compatible object store, a local directory or an rclone remote. The machine being backed up holds only the public key. It cannot decrypt its own -backups. You keep the private key somewhere else and need it only to restore. +backups. You keep the private key somewhere else. A backup never needs it. ## Rationale @@ -1167,7 +1167,7 @@ destination still learns these: The last point holds because a remote key is a plain hash of the snapshot ID, made without a secret. -[docs/REPOSTRUCTURE.md](docs/REPOSTRUCTURE.md) describes the layout of the +[`docs/REPOSTRUCTURE.md`](docs/REPOSTRUCTURE.md) describes the layout of the destination, what it protects and the risks it accepts. ## Limitations @@ -1207,9 +1207,9 @@ is a trimmed, encrypted copy of the local index. These documents give the detail: -- [ARCHITECTURE.md](ARCHITECTURE.md) describes how the code is organised. -- [docs/DATAMODEL.md](docs/DATAMODEL.md) describes the local index. -- [docs/REPOSTRUCTURE.md](docs/REPOSTRUCTURE.md) describes the layout of the +- [`ARCHITECTURE.md`](ARCHITECTURE.md) describes how the code is organised. +- [`docs/DATAMODEL.md`](docs/DATAMODEL.md) describes the local index. +- [`docs/REPOSTRUCTURE.md`](docs/REPOSTRUCTURE.md) describes the layout of the destination. ## Development diff --git a/docs/DATAMODEL.md b/docs/DATAMODEL.md index efbbd74..2c1a61b 100644 --- a/docs/DATAMODEL.md +++ b/docs/DATAMODEL.md @@ -135,7 +135,7 @@ One row for each blob. - `uploaded_ts`: when the upload finished. It is `NULL` before that. `blob_hash` is `hex(SHA256(SHA256(uncompressed contents)))`. See -[REPOSTRUCTURE.md](REPOSTRUCTURE.md#blobs). +[`REPOSTRUCTURE.md`](REPOSTRUCTURE.md#blobs). Once a blob is uploaded, `compressed_size` is the size of the object at the destination. A blob that was already there can have a different size from the diff --git a/docs/REPOSTRUCTURE.md b/docs/REPOSTRUCTURE.md index 74db889..f508e0b 100644 --- a/docs/REPOSTRUCTURE.md +++ b/docs/REPOSTRUCTURE.md @@ -149,7 +149,7 @@ The snapshot's database. A restore needs it. age. It is encrypted because it holds file paths, permissions and owners. -[DATAMODEL.md](DATAMODEL.md) describes its tables. +[`DATAMODEL.md`](DATAMODEL.md) describes its tables. ### `manifest.json.zst` @@ -261,15 +261,16 @@ MiB, or 4 MiB at compression levels 1 and 2. A blob is closed at `blob_size_limit` and at the end of each configured path. A chunk that is already stored is never packed again. -Data that an outsider writes compresses smaller when it matches a secret in the -same chunk, so the blob gets smaller. The blob's size therefore shows the +Data that an outsider writes compresses smaller when it matches a secret near it +in the same blob, so the blob gets smaller. The blob's size therefore shows the outsider whether their data matched the secret. This works only when all of these hold: - The outsider can write into a backed-up file. - The outsider can see the blob sizes at the destination. -- The outsider's data and the secret land in the same chunk. +- The outsider's data and the secret are in the same blob, no further apart than + the stream looks back. - The file keeps changing. Back up a directory that outsiders can write to as a snapshot of its own.