Fix the review findings in README.md
check / check (push) Waiting to run

The first sentence now names the licence, the kind of program, the
language and the author. The README gains a Design section, and its
sections carry the names REPO_POLICIES.md asks for: Rationale and
Getting started, which now holds the install command. Entrypoints opens
with the Scripts to Rule Them All statement.

Corrected: when a file is read again, the chunk size limits, and the
first prune step, which looks for a snapshot's manifest. Two sentences
that misparsed are reworded.

A test comment that named the README quickstart follows the rename.

Model: fable-5-1
This commit is contained in:
2026-11-06 19:34:06 +00:00
parent b6f8e860e1
commit a52a8a781c
2 changed files with 50 additions and 31 deletions
+48 -29
View File
@@ -1,13 +1,14 @@
# vaultik (ваултик)
`vaultik` is an incremental backup tool. It encrypts your files with a public
key and uploads them to an S3-compatible object store, a local directory or an
rclone remote.
`vaultik` is an MIT-licensed command-line tool for incremental, encrypted
backups, written in Go by [@sneak](https://sneak.berlin). It encrypts your files
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.
## Why
## Rationale
Other backup tools, such as `restic`, `borg` and `duplicity`, are designed for
machines that can hold a decryption key. `vaultik` is for machines that should
@@ -26,7 +27,8 @@ It was written to meet these requirements together:
- Encryption with [age](https://age-encryption.org/) (X25519 and
ChaCha20-Poly1305), to one or more public keys.
- Incremental backups. A file is read again only when it has changed.
- Incremental backups. A file is read again only when it has changed, or when
part of its content has to be stored again.
- Deduplication. Identical content is stored once, across files and across
snapshots.
- zstd compression at a level you choose.
@@ -57,18 +59,15 @@ It was written to meet these requirements together:
- **Remote key**: a hash of the snapshot ID. It names the snapshot's directory
at the destination, so the destination never shows the snapshot ID.
## Install
## Getting started
Installing needs Go 1.26.1 or later. Generating a key pair needs `age-keygen`,
which comes with [age](https://age-encryption.org/).
```sh
# Install vaultik.
go install sneak.berlin/go/vaultik/cmd/vaultik@latest
```
This needs Go 1.26.1 or later. Generating a key pair needs `age-keygen`, which
comes with [age](https://age-encryption.org/).
## Quick start
```sh
# Write a default config file. The command prints the path it wrote.
vaultik config init
@@ -301,13 +300,15 @@ It needs:
It does not need the private key.
A file is read again when any of these has changed since the last backup:
A file is read again when any of these holds:
- its size
- its modification time
- its mode
- its owner or group
- its link target
- Its size has changed since the last backup.
- Its modification time has changed.
- Its mode has changed.
- Its owner or group has changed.
- Its link target has changed.
- No uploaded blob holds one of its chunks. This happens when the only snapshot
that used the blob holding that chunk is removed.
Every other file is recorded in the new snapshot without being read.
@@ -596,7 +597,7 @@ destination. It needs no private key.
It works in three steps:
- It drops each snapshot from the local index whose directory is gone from the
- It drops from the local index each snapshot whose manifest is gone from the
destination.
- It removes unfinished snapshots from the local index. It also removes the
file, chunk and blob records that no snapshot uses.
@@ -811,7 +812,8 @@ A path from `--config` or `VAULTIK_CONFIG` must exist.
scheme.
- `chunk_size`
- The average size of a chunk. Default: `10MB`. Minimum: `1MB`.
- A chunk is between a quarter of this size and four times this size.
- No chunk is larger than four times this size. Only a file's last chunk can
be smaller than a quarter of it.
- A smaller size finds more duplicate content and makes the index larger.
- `blob_size_limit`
- The largest size of a blob. Default: `10GB`.
@@ -997,8 +999,8 @@ To fix this, mount the volume. If you emptied the destination on purpose, run
The check is skipped when the look-up fails for any other reason. S3 refuses the
look-up to credentials that can only write. A machine with such credentials
therefore does not notice an emptied destination. Its next backup would leave
out the data its local index records as stored. After you empty a destination,
therefore does not notice an emptied destination. Its next backup would not
upload data that its local index says is stored. After you empty a destination,
run `vaultik database delete` on such a machine before it backs up again.
### Changing the destination
@@ -1006,7 +1008,7 @@ run `vaultik database delete` on such a machine before it backs up again.
The local index belongs to one destination. It records which blobs are stored
there.
The first command that changes data records the configured destination in the
The first command that changes data saves the configured destination in the
local index. Later, these commands refuse to run when the config names a
different destination:
@@ -1187,14 +1189,30 @@ destination, what it protects and the risks it accepts.
destination is not affected. See
[Schema migrations](docs/DATAMODEL.md#schema-migrations).
## Development
## Design
These documents are for developers:
A backup passes your files through four parts of the program:
- The `Scanner` walks the configured paths and picks the files to read.
- The `Chunker` cuts each of those files into chunks.
- The `Packer` collects new chunks into blobs. It compresses and encrypts each
blob.
- The `Storer` uploads each blob to the destination.
The backup then uploads the snapshot's database and its manifest. The database
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
destination.
## Development
Two documents give the rules for work on this repository:
- [`AGENTS.md`](AGENTS.md) holds the rules for AI agents.
- [`REPO_POLICIES.md`](REPO_POLICIES.md) holds the repository policies.
@@ -1216,9 +1234,10 @@ pass.
### Entrypoints
The scripts in `script/` are the entrypoints. The `Makefile` targets only call
them. The layout follows
[Scripts to Rule Them All](https://github.com/github/scripts-to-rule-them-all).
This repository follows the
[Scripts to Rule Them All](https://github.com/github/scripts-to-rule-them-all)
standard. The scripts in `script/` are the entrypoints. The `Makefile` targets
only call them.
- `script/bootstrap`: installs what development needs. It installs Go from
`script/install-go` when the machine has none, and it fails without Docker.
+2 -2
View File
@@ -61,8 +61,8 @@ func TestDefaultConfigTemplateParses(t *testing.T) {
// testRecipient is a valid age public key.
const testRecipient = "age1278m9q7dp3chsh2dcy82qk27v047zywyvtxwnj4cvt0z65jw6a7q5dqhfj"
// TestConfigSetRecipientOnFreshConfig follows the README quickstart: on the
// file `config init` writes, `config set age_recipients.0` and
// TestConfigSetRecipientOnFreshConfig follows the README's getting started
// steps: on the file `config init` writes, `config set age_recipients.0` and
// `config set storage_url` give a config that loads with that recipient.
// Neither is refused, although the config does not load until both are set;
// once it loads, a recipient that is not a valid key is.