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:
1 parent
b6f8e860e1
commit
a52a8a781c
2 files changed
+50
-31
No files matched your search
@@ -1,13 +1,14 @@
|
|||||||
# vaultik (ваултик)
|
# vaultik (ваултик)
|
||||||
|
|
||||||
`vaultik` is an incremental backup tool. It encrypts your files with a public
|
`vaultik` is an MIT-licensed command-line tool for incremental, encrypted
|
||||||
key and uploads them to an S3-compatible object store, a local directory or an
|
backups, written in Go by [@sneak](https://sneak.berlin). It encrypts your files
|
||||||
rclone remote.
|
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
|
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 and need it only to restore.
|
||||||
|
|
||||||
## Why
|
## Rationale
|
||||||
|
|
||||||
Other backup tools, such as `restic`, `borg` and `duplicity`, are designed for
|
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
|
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
|
- Encryption with [age](https://age-encryption.org/) (X25519 and
|
||||||
ChaCha20-Poly1305), to one or more public keys.
|
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
|
- Deduplication. Identical content is stored once, across files and across
|
||||||
snapshots.
|
snapshots.
|
||||||
- zstd compression at a level you choose.
|
- 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
|
- **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.
|
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
|
```sh
|
||||||
|
# Install vaultik.
|
||||||
go install sneak.berlin/go/vaultik/cmd/vaultik@latest
|
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.
|
# Write a default config file. The command prints the path it wrote.
|
||||||
vaultik config init
|
vaultik config init
|
||||||
|
|
||||||
@@ -301,13 +300,15 @@ It needs:
|
|||||||
|
|
||||||
It does not need the private key.
|
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 size has changed since the last backup.
|
||||||
- its modification time
|
- Its modification time has changed.
|
||||||
- its mode
|
- Its mode has changed.
|
||||||
- its owner or group
|
- Its owner or group has changed.
|
||||||
- its link target
|
- 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.
|
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 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.
|
destination.
|
||||||
- It removes unfinished snapshots from the local index. It also removes the
|
- It removes unfinished snapshots from the local index. It also removes the
|
||||||
file, chunk and blob records that no snapshot uses.
|
file, chunk and blob records that no snapshot uses.
|
||||||
@@ -811,7 +812,8 @@ A path from `--config` or `VAULTIK_CONFIG` must exist.
|
|||||||
scheme.
|
scheme.
|
||||||
- `chunk_size`
|
- `chunk_size`
|
||||||
- The average size of a chunk. Default: `10MB`. Minimum: `1MB`.
|
- 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.
|
- A smaller size finds more duplicate content and makes the index larger.
|
||||||
- `blob_size_limit`
|
- `blob_size_limit`
|
||||||
- The largest size of a blob. Default: `10GB`.
|
- 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
|
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
|
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
|
therefore does not notice an emptied destination. Its next backup would not
|
||||||
out the data its local index records as stored. After you empty a destination,
|
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.
|
run `vaultik database delete` on such a machine before it backs up again.
|
||||||
|
|
||||||
### Changing the destination
|
### 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
|
The local index belongs to one destination. It records which blobs are stored
|
||||||
there.
|
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
|
local index. Later, these commands refuse to run when the config names a
|
||||||
different destination:
|
different destination:
|
||||||
|
|
||||||
@@ -1187,14 +1189,30 @@ destination, what it protects and the risks it accepts.
|
|||||||
destination is not affected. See
|
destination is not affected. See
|
||||||
[Schema migrations](docs/DATAMODEL.md#schema-migrations).
|
[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.
|
- [ARCHITECTURE.md](ARCHITECTURE.md) describes how the code is organised.
|
||||||
- [docs/DATAMODEL.md](docs/DATAMODEL.md) describes the local index.
|
- [docs/DATAMODEL.md](docs/DATAMODEL.md) describes the local index.
|
||||||
- [docs/REPOSTRUCTURE.md](docs/REPOSTRUCTURE.md) describes the layout of the
|
- [docs/REPOSTRUCTURE.md](docs/REPOSTRUCTURE.md) describes the layout of the
|
||||||
destination.
|
destination.
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
Two documents give the rules for work on this repository:
|
||||||
|
|
||||||
- [`AGENTS.md`](AGENTS.md) holds the rules for AI agents.
|
- [`AGENTS.md`](AGENTS.md) holds the rules for AI agents.
|
||||||
- [`REPO_POLICIES.md`](REPO_POLICIES.md) holds the repository policies.
|
- [`REPO_POLICIES.md`](REPO_POLICIES.md) holds the repository policies.
|
||||||
|
|
||||||
@@ -1216,9 +1234,10 @@ pass.
|
|||||||
|
|
||||||
### Entrypoints
|
### Entrypoints
|
||||||
|
|
||||||
The scripts in `script/` are the entrypoints. The `Makefile` targets only call
|
This repository follows the
|
||||||
them. The layout follows
|
[Scripts to Rule Them All](https://github.com/github/scripts-to-rule-them-all)
|
||||||
[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/bootstrap`: installs what development needs. It installs Go from
|
||||||
`script/install-go` when the machine has none, and it fails without Docker.
|
`script/install-go` when the machine has none, and it fails without Docker.
|
||||||
|
|||||||
@@ -61,8 +61,8 @@ func TestDefaultConfigTemplateParses(t *testing.T) {
|
|||||||
// testRecipient is a valid age public key.
|
// testRecipient is a valid age public key.
|
||||||
const testRecipient = "age1278m9q7dp3chsh2dcy82qk27v047zywyvtxwnj4cvt0z65jw6a7q5dqhfj"
|
const testRecipient = "age1278m9q7dp3chsh2dcy82qk27v047zywyvtxwnj4cvt0z65jw6a7q5dqhfj"
|
||||||
|
|
||||||
// TestConfigSetRecipientOnFreshConfig follows the README quickstart: on the
|
// TestConfigSetRecipientOnFreshConfig follows the README's getting started
|
||||||
// file `config init` writes, `config set age_recipients.0` and
|
// 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.
|
// `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;
|
// 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.
|
// once it loads, a recipient that is not a valid key is.
|
||||||
|
|||||||
Reference in new issue
Block a user