diff --git a/README.md b/README.md index fe2ed2f..5ae3fda 100644 --- a/README.md +++ b/README.md @@ -599,7 +599,7 @@ both are set. For an implementation-level view of the internals — the data model, the `fx` dependency-injection wiring, and the scanner — see -[`ARCHITECTURE.md`](ARCHITECTURE.md). +[`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md). ### remote storage layout @@ -718,8 +718,8 @@ format does and does not protect. ## configuration reference Run `vaultik config init` to generate a fully commented config file; a complete -annotated example also lives in [`config.example.yml`](config.example.yml). Key -fields: +annotated example also lives in +[`configs/config.example.yml`](configs/config.example.yml). Key fields: | Field | Default | Description | | -------------------- | ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | diff --git a/config.example.yml b/configs/config.example.yml similarity index 100% rename from config.example.yml rename to configs/config.example.yml diff --git a/ARCHITECTURE.md b/docs/ARCHITECTURE.md similarity index 98% rename from ARCHITECTURE.md rename to docs/ARCHITECTURE.md index 83bfe36..d345555 100644 --- a/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -95,7 +95,7 @@ chunks: - `ID`: UUID assigned at creation - `Hash`: `hex(SHA256(SHA256(uncompressed blob contents)))`, computed before compression and encryption (see - [docs/REPOSTRUCTURE.md](docs/REPOSTRUCTURE.md#blobs-directory-blobs)) + [docs/REPOSTRUCTURE.md](REPOSTRUCTURE.md#blobs-directory-blobs)) - `UncompressedSize`: Total raw chunk data before compression - `CompressedSize`: Size after zstd compression and age encryption - `CreatedTS`, `FinishedTS`, `UploadedTS`: Lifecycle timestamps @@ -467,14 +467,14 @@ unplugged volume's empty mount point for a destination whose snapshots are all gone. When the look-up fails for any reason other than not found, for example when S3 refuses it to credentials that allow only PUT, the check is skipped, so a host with such credentials does not notice an emptied destination; see -[docs/REPOSTRUCTURE.md](docs/REPOSTRUCTURE.md#marker-file-vaultik-destinationtxt). +[docs/REPOSTRUCTURE.md](REPOSTRUCTURE.md#marker-file-vaultik-destinationtxt). The `file://` backend never creates its destination directory, so a write to a missing one fails. The `{remote-key}` directory name is a one-way double SHA-256 hash of the human snapshot ID, so the human ID (hostname, snapshot name, timestamp) is never written to the store as a directory name. See -[docs/REPOSTRUCTURE.md](docs/REPOSTRUCTURE.md#remote-key-derivation) for the +[docs/REPOSTRUCTURE.md](REPOSTRUCTURE.md#remote-key-derivation) for the derivation and a worked example. ## Thread Safety diff --git a/docs/REPOSTRUCTURE.md b/docs/REPOSTRUCTURE.md index 78a30f6..81f34c9 100644 --- a/docs/REPOSTRUCTURE.md +++ b/docs/REPOSTRUCTURE.md @@ -304,7 +304,7 @@ so an operator can weigh them rather than discover them. 4. **A snapshot is marked complete in the local DB only after its metadata is uploaded** - `finalizeSnapshotMetadata` runs `ExportSnapshotMetadata` first and records completion (`MarkSnapshotComplete`) only once the export succeeds - (see the backup data flow in [ARCHITECTURE.md](../ARCHITECTURE.md)). A crash + (see the backup data flow in [ARCHITECTURE.md](ARCHITECTURE.md)). A crash during the export therefore leaves the snapshot incomplete, so the next backup's `PruneDatabase` drops it and re-backs-up its data, rather than leaving a completed-looking row in the local index with no matching metadata diff --git a/TODO.md b/docs/TODO.md similarity index 99% rename from TODO.md rename to docs/TODO.md index 3489623..edb3a3e 100644 --- a/TODO.md +++ b/docs/TODO.md @@ -23,6 +23,11 @@ runs the release build in `.gitea/workflows/release.yml`. # Completed Steps +- 2026-11-10: `script/fmt` and `script/fmt-check` now format and check all + Markdown with `prettier` + ([issue #411](https://git.eeqj.de/sneak/vaultik/issues/411)). The architecture + document and this file moved to `docs/`, and the example config to `configs/`. + - 2026-10-15: Stopped `snapshot create --prune` pruning after a backup in which a volume mounted below a configured path in an earlier snapshot of the same name was not mounted @@ -1206,4 +1211,4 @@ runs the release build in `.gitea/workflows/release.yml`. # Future Steps -Work planned after 1.0 is listed in the README [roadmap](README.md#roadmap). +Work planned after 1.0 is listed in the README [roadmap](../README.md#roadmap). diff --git a/internal/config/config.go b/internal/config/config.go index 01cf8cb..12015f7 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -56,7 +56,7 @@ var ( errRecipientNotX25519 = errors.New( "not a valid recipient; only X25519 age1... public keys are supported") errNoSnapshots = errors.New( - "at least one snapshot must be configured (see config.example.yml)") + "at least one snapshot must be configured (see configs/config.example.yml)") errSnapshotNoPaths = errors.New("snapshot must have at least one path") errChunkSizeTooSmall = errors.New("chunk_size must be at least 1MB") errBlobSizeTooSmall = errors.New( diff --git a/internal/config/config_test.go b/internal/config/config_test.go index b1af7cd..8b6f43d 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -90,12 +90,12 @@ func TestConfigLoad(t *testing.T) { } // TestExampleConfigIsScrubbedAndLoads checks that the shipped -// config.example.yml carries only neutral placeholders (no real credentials, -// private addresses, or internal host names) and still parses. +// configs/config.example.yml carries only neutral placeholders (no real +// credentials, private addresses, or internal host names) and still parses. func TestExampleConfigIsScrubbedAndLoads(t *testing.T) { t.Parallel() - examplePath := filepath.Join("..", "..", "config.example.yml") + examplePath := filepath.Join("..", "..", "configs", "config.example.yml") cfg, err := Load(examplePath) if err != nil { @@ -361,7 +361,7 @@ func TestDocumentedSizeDefaults(t *testing.T) { } // TestDocumentedSizeExamples checks the 1MB minimum chunk_size and that -// every size the README, config.example.yml and the config init template +// every size the README, configs/config.example.yml and the config init template // list as an example is accepted. func TestDocumentedSizeExamples(t *testing.T) { t.Parallel()