Files

5.4 KiB

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:

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.