chore: replace em dashes with hyphens, add no-em-dash rule to CLAUDE.md
Deploy / deploy (push) Successful in 2m47s

This commit is contained in:
2026-09-10 09:18:57 +00:00
parent e8455c99fe
commit 7762629634
22 changed files with 126 additions and 122 deletions
+26 -26
View File
@@ -7,16 +7,16 @@ sidebar_label: Backup and restore
`vantagectl` is a separate command-line tool that backs up and restores the
MongoDB database behind a Vantage control plane. It talks to MongoDB directly,
never to the Vantage API, so it works against a control plane that is down,
half-migrated, or gone exactly the situation a backup tool has to survive.
half-migrated, or gone - exactly the situation a backup tool has to survive.
For the store-level overview what holds what, and why the database alone is
not a backup see [Backups](./backups.md). This page covers the tool.
For the store-level overview - what holds what, and why the database alone is
not a backup - see [Backups](./backups.md). This page covers the tool.
:::danger The key comes first
Vantage encrypts SSH private keys, key passphrases, vault secrets, SSO client
secrets and console credentials with `KEY_ENCRYPTION_KEY`. **It is not in your
backup, and it is not recoverable.** A database restored without it is
permanently unreadable not degraded, not partially readable, unreadable.
permanently unreadable - not degraded, not partially readable, unreadable.
Store it wherever you store the credentials you could not rebuild: a password
manager, a secrets vault outside this control plane, a piece of paper in a
@@ -26,7 +26,7 @@ safe. Anywhere but next to the archive.
## What a backup holds
Every collection in the database, the index definitions each one needs to be
useful again, and a SHA-256 **fingerprint** of `KEY_ENCRYPTION_KEY` never the
useful again, and a SHA-256 **fingerprint** of `KEY_ENCRYPTION_KEY` - never the
key itself. The fingerprint is what lets a later `restore` or `verify` tell you
that the key you are holding is the wrong one, before it writes a database
nobody can read.
@@ -37,8 +37,8 @@ nobody can read.
true whenever Redis itself restarts.
- **The vulnerability database.** It is re-pulled automatically on next boot.
- **Agent state on managed servers.** Nothing needs re-enrolling: agents
reconnect on their own, because `servers.agent_token_hash` the thing an
agent authenticates with is itself in the backup.
reconnect on their own, because `servers.agent_token_hash` - the thing an
agent authenticates with - is itself in the backup.
:::note Pin the version
The image is published on each `vantagectl/v*` release and tagged with that
@@ -80,7 +80,7 @@ backup:
```
`backup.enabled` defaults to `false`, and the chart refuses to render if it is
turned on without both `backup.image` and `backup.pvcName` a backup needs a
turned on without both `backup.image` and `backup.pvcName` - a backup needs a
known image and somewhere durable to land, and guessing at either is worse than
refusing to start. `backup.exclude` names collections to leave out (recorded in
the archive's manifest, so an archive never claims to be complete when it is
@@ -88,13 +88,13 @@ not), and `backup.successfulJobsHistoryLimit` / `backup.failedJobsHistoryLimit`
/ `backup.resources` behave exactly as they do on any other `CronJob`.
`backup` refuses to run without `KEY_ENCRYPTION_KEY` set in the environment,
unless you pass `--allow-no-key` for a deployment that genuinely stores no
unless you pass `--allow-no-key` - for a deployment that genuinely stores no
encrypted data. Everywhere else, treat the refusal as the tool doing its job.
## Where to put the archive
`--out -` streams the tarball to stdout instead of writing a file, and every
line of progress output goes to stderr so piping the archive into something
line of progress output goes to stderr - so piping the archive into something
else is always safe, nothing progress-related lands in the stream.
Into `restic`:
@@ -109,7 +109,7 @@ Into S3:
vantagectl backup --out - | aws s3 cp - s3://my-backups/vantage-$(date +%F).tar.gz
```
An archive is as sensitive as a raw database dump it carries every SSH key
An archive is as sensitive as a raw database dump - it carries every SSH key
assignment, every secret group, every session-adjacent setting, in a form the
right `KEY_ENCRYPTION_KEY` can decrypt. Whatever you pipe it into should
encrypt it at rest; `vantagectl` itself does not.
@@ -123,23 +123,23 @@ vantagectl verify /backups/vantage-backup-vantage-20260907T020000Z.tar.gz \
Each line of output answers a different question:
- **`Archive`** every member's checksum still matches; the tarball has not
- **`Archive`** - every member's checksum still matches; the tarball has not
been truncated or corrupted.
- **`Archive key`** / **`Your key`** the fingerprint stored in the archive
- **`Archive key`** / **`Your key`** - the fingerprint stored in the archive
next to the fingerprint of the `KEY_ENCRYPTION_KEY` in your environment.
- **`Key match`** whether those two fingerprints agree.
- **`Live probe`** given `--mongo-uri`, `verify` goes one step further and
- **`Key match`** - whether those two fingerprints agree.
- **`Live probe`** - given `--mongo-uri`, `verify` goes one step further and
decrypts a real ciphertext value from that database with the key you hold.
A fingerprint match proves two archives agree about a key; only the probe
proves the key in your hand actually reads the data.
`verify` exits non-zero the moment anything above is wrong, which is what makes
it worth putting on a schedule a backup job that "succeeded" last night is
it worth putting on a schedule - a backup job that "succeeded" last night is
not the same claim as a backup that will actually restore.
## Looking inside an archive
`inspect` prints an archive's manifest and touches no database at all no
`inspect` prints an archive's manifest and touches no database at all - no
`--mongo-uri`, no key. It is what to run against an archive of unknown origin,
before deciding whether it is the one you want:
@@ -173,8 +173,8 @@ collection named in the archive before loading it. `--force` always needs a
second assurance, in one of two forms:
- `--confirm-db NAME`, naming the target exactly. A mismatch is refused. This
works everywhere on a terminal and in a Kubernetes Job, a CI step or a cron
entry alike and is the form to script.
works everywhere - on a terminal and in a Kubernetes Job, a CI step or a cron
entry alike - and is the form to script.
- Nothing, on a terminal: `--force` alone prompts you to type the target
database's name back, a deliberate pause before something destructive.
@@ -190,7 +190,7 @@ log lines are still there, joined to freshly restored runs. Dropping them
instead would delete data you never asked to delete.
`restore` also refuses when the archive's key fingerprint does not match the
`KEY_ENCRYPTION_KEY` in your environment see "When the key is wrong" below.
`KEY_ENCRYPTION_KEY` in your environment - see "When the key is wrong" below.
## The restore drill
@@ -210,7 +210,7 @@ monthly:
```
3. Drop the scratch database. It served its purpose.
The failure this catches is not "the archive is corrupt" `verify` alone
The failure this catches is not "the archive is corrupt" - `verify` alone
catches that. It is "the archive is fine but nobody can actually stand a
control plane back up from it," which only a real restore proves.
@@ -221,13 +221,13 @@ If `restore` finds the archive's key fingerprint does not match the
`--ignore-key-mismatch` proceeds anyway, but says plainly which collections
will come back with ciphertext nobody can read:
- `keys` SSH private keys and passphrases
- `secrets` the vault
- `auth_providers` OIDC/SSO client secrets
- `console_sessions` RDP/VNC credentials
- `keys` - SSH private keys and passphrases
- `secrets` - the vault
- `auth_providers` - OIDC/SSO client secrets
- `console_sessions` - RDP/VNC credentials
There is no way to recover that ciphertext afterwards. If you have reached
this point, the right key was lost along with the chance to read those rows
this point, the right key was lost along with the chance to read those rows -
the fix is to re-enter each of them by hand (re-upload SSH keys, re-save vault
secrets, reconfigure SSO), not to keep searching for a way to decrypt what is
already in the database.
+2 -2
View File
@@ -27,7 +27,7 @@ Store the key somewhere other than the server it protects.
:::info Use `vantagectl`
[**Backup and restore**](./backup-and-restore.md) is the supported way to take
and restore a backup. It writes an archive that carries a fingerprint of
`KEY_ENCRYPTION_KEY` never the key so a restore taken with the wrong key
`KEY_ENCRYPTION_KEY` - never the key - so a restore taken with the wrong key
**refuses** rather than silently producing a database whose secrets nobody can
read. It also checksums every archive member before writing anything, and
refuses to restore into a database that already holds data. A plain
@@ -52,7 +52,7 @@ docker compose exec -T mongo mongodump --archive --gzip --db vantage \
:::warning
This archive records nothing about which `KEY_ENCRYPTION_KEY` it was taken
under. Restoring it with the wrong key produces a database that looks intact
and is not every secret in it is silently unreadable until something tries
and is not - every secret in it is silently unreadable until something tries
to decrypt one.
:::