Files
vantage-shared/backup/manifest.go
T
mrhid6 07c68b170b feat: Add the backup archive manifest
KeyFingerprint is a pointer so an archive that recorded no key is a state
restore can report, not a default it silently treats as a match.
2026-09-07 11:08:02 +00:00

74 lines
2.6 KiB
Go

package backup
import (
"errors"
"fmt"
"time"
)
// FormatVersion is the archive format this build reads and writes. Restore
// refuses anything else rather than guessing at a layout it does not know.
const FormatVersion = 1
// ManifestName is the archive member holding the manifest.
const ManifestName = "manifest.json"
// ErrUnknownFormat is returned for an archive this build cannot read.
var ErrUnknownFormat = errors.New("unsupported archive format version")
// CollectionEntry describes one collection in the archive. Bytes and SHA256
// cover the uncompressed .bson member, which is what restore verifies before
// writing anything.
type CollectionEntry struct {
Name string `json:"name"`
Documents int64 `json:"documents"`
Bytes int64 `json:"bytes"`
SHA256 string `json:"sha256"`
}
// Manifest is the archive's index and its provenance.
//
// KeyFingerprint is a pointer so "this archive recorded no key" is a distinct
// state from "this archive recorded the empty string". A null here is a real
// condition an operator must be told about, not a default.
type Manifest struct {
FormatVersion int `json:"format_version"`
CreatedAt time.Time `json:"created_at"`
VantageVersion string `json:"vantage_version"`
Hostname string `json:"hostname"`
MongoDB string `json:"mongo_db"`
MongoServerVersion string `json:"mongo_server_version"`
KeyFingerprint *string `json:"key_fingerprint"`
Collections []CollectionEntry `json:"collections"`
Excluded []string `json:"excluded"`
}
// Check validates what can be validated without reading the rest of the archive.
func (m Manifest) Check() error {
if m.FormatVersion != FormatVersion {
return fmt.Errorf("%w: archive is version %d, this build reads version %d",
ErrUnknownFormat, m.FormatVersion, FormatVersion)
}
return nil
}
// Collection looks up one entry by name.
func (m Manifest) Collection(name string) (CollectionEntry, bool) {
for _, c := range m.Collections {
if c.Name == name {
return c, true
}
}
return CollectionEntry{}, false
}
// CiphertextCollections names the collections holding AES-GCM ciphertext.
//
// It exists to be printed. When a restore proceeds under a key that does not
// match the archive, this is the list of what will be unreadable afterwards,
// and an operator deserves to see it before the write rather than discover it
// a week later.
func CiphertextCollections() []string {
return []string{"keys", "secrets", "auth_providers", "console_sessions", "settings"}
}