Files
wireguard-admin/docs/BACKUP_FORMAT.md
T

117 lines
5.4 KiB
Markdown

# Backup archive format
Backups are uncompressed tar archives with a versioned TOML manifest. Version 1
uses only fixed logical member names and is parsed without extracting files.
The archive is an opaque saved-state copy; its tar checksum detects accidental
header damage but does not authenticate contents or make the archive tamper-proof.
## Members and limits
The first member is always `manifest.toml`. The only permitted payload names are
`settings.toml`, `server.conf`, `internal-state.toml`, and `vault.toml`, in that
order when present. Settings and server configuration are required. Internal
state and vault are optional components: a missing component is recorded as
`present = false` and `length = none`, which means restore removes that owned
component. Absence is not represented by an empty placeholder file.
| Member | Maximum bytes | Presence |
| --- | ---: | --- |
| `manifest.toml` | 64 KiB | Required, first |
| `settings.toml` | 1 MiB | Required |
| `server.conf` | 4 MiB | Required |
| `internal-state.toml` | 2 MiB | Optional |
| `vault.toml` | 16 MiB | Optional |
The archive is limited to 26 MiB and five entries. Only checksummed UStar regular
files are accepted. Absolute names, path separators, traversal, links, device or
special entries, sparse entries, GNU/PAX extension headers, nonzero path prefixes,
unexpected metadata fields, duplicate/missing/unexpected members, size mismatches,
truncation, nonzero padding, and nonzero trailing data are rejected. The reader
checks declared sizes before allocating payload buffers and never follows a
tar-supplied path. It does not restore files or validate their application
semantics; restoration must run settings, configuration, state, and vault
validators before mutation.
## Manifest v1
The manifest is UTF-8 TOML with unknown fields denied:
```toml
version = 1
created_at_utc = "2026-10-19T12:30:00Z"
reason = "pre-commit"
[historical_apply]
status = "unknown"
[[members]]
name = "settings.toml"
present = true
length = 420
[[members]]
name = "server.conf"
present = true
length = 640
[[members]]
name = "internal-state.toml"
present = false
[[members]]
name = "vault.toml"
present = true
length = 8192
```
`reason` is one of `manual`, `pre-commit`, `pre-restore`, or `pre-restart`.
Timestamps are canonical RFC 3339 UTC values with whole-second precision. The
historical apply record is either `status = "unknown"` or
`status = "verified"` with a `verified_at_utc` timestamp. It is historical
metadata only: a pre-operation reason or a verified marker does not establish
that these bytes match the currently running interface. Application-created
backups derive this record from the timestamp stored in the internal-state bytes
captured with the saved set. A successful Commit or restore clears that timestamp
for its new saved generation; a failed operation leaves it intact. Reopening may
show the saved generation's historical verification time, but runtime observation
always begins as unknown.
The inventory always has exactly four records in the specified order. Required
members have `present = true` and an exact byte `length`; optional absence has
`present = false` and omits `length` (TOML has no null value). Present optional members follow the required
members in fixed order. The manifest itself is excluded from its payload
inventory. Readers reject unsupported versions and any discrepancy between
inventory and tar entries.
## Publication and trust boundary
Creation receives exact saved bytes from a trusted caller. It does not discover
files, inspect the current working directory, recurse into the archive directory,
or read client source/output directories. The caller must supply installed bytes,
never a proposed draft or decrypted vault records. New archives are fully
serialized and synced in a private temporary file, then published without
replacement under a timestamped collision-safe name; the archive directory is
synced after publication. The archive file is mode `0600` from temporary-file
creation. A write or file-sync failure removes the unpublished temporary file;
a publication or directory-sync failure returns an error and blocks the protected
operation. A complete archive may already be visible if failure occurs after its
atomic publication, and a failed temporary cleanup may leave a hidden `0600`
temporary file. Neither condition is reported as backup success. Readers should
expose the list and preview without passwords, but must keep payload bytes out of
Debug output and logs.
The application restore preview validates both the archive structure and its
settings, server document, optional internal state, and opaque vault before it
returns an opaque confirmation value. That value owns the validated payload
bytes, so confirmation applies the exact content shown during preview even if
the selected archive path changes afterward. Confirmation first completes a
`pre-restore` archive and then uses the saved-file transaction journal for the
full replacement. Dropping the preview is cancellation and does not discard a
draft. Historical verification metadata is informational; opening or restoring
an archive always leaves current runtime state unknown.
This module only creates, lists, and validates archives. It does not write saved
state, apply WireGuard changes, or restore content. A backup is not encrypted:
it can contain the server's private configuration and the encrypted vault, so
filesystem access must remain restricted to root.