Help us shape the future of Go! Take the 2026 Go Developer Survey

backup

package
v0.19.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 3, 2026 License: AGPL-3.0 Imports: 26 Imported by: 0

Documentation

Overview

Package backup makes and lists data-directory backups: a consistent copy of the database (VACUUM INTO) and a tar.gz of data trees and local recovery configuration, both under <data>/backups/<UTC timestamp>. `gorun backup` and the admin's System → Backups share it.

Index

Constants

View Source
const (
	AnalyticsNotRegular      = "analytics_not_regular"
	AnalyticsCopyFailed      = "analytics_copy_failed"
	AnalyticsIntegrityFailed = "analytics_integrity_failed"
	AnalyticsSettingUnread   = "analytics_setting_unreadable"
)

Codes that start Result.AnalyticsSkipped ("<code>: <detail>").

View Source
const AnalyticsFile = "analytics.db"

AnalyticsFile is the analytics database in the data directory. Backup knows it only by this name (it never imports the analytics module): when present, it is copied to <stamp>-analytics.db as an optional member.

View Source
const ManifestVersion = 1

ManifestVersion stays 1: the analytics member is an optional field, so a manifest without it (every manifest before it existed) reads, verifies and restores exactly as before.

View Source
const (

	// MaxEncryptedFile is DARE's cryptographic plaintext bound, less our authenticated marker.
	// Uploads use bounded buffers; provider object-size limits still apply.
	MaxEncryptedFile int64 = (1 << 48) - int64(len(contentMarker))
)
View Source
const SettingSkipAnalytics = "analytics.backup_exclude"

SettingSkipAnalytics is the JSON-bool setting ("Leave analytics out of backups") that callers map to Options.SkipAnalytics. It must match analytics.SettingBackupExclude.

Variables

View Source
var Dirs = []string{"media", "overrides", "extensions", "form-uploads"}

Dirs are the data-directory trees archived next to the database copy.

View Source
var ErrPartialRestore = errors.New("backup: an interrupted restore left a partial directory")

ErrPartialRestore reports that an interrupted restore left its staging directory (see PartialPath); remove it before restoring again.

Functions

func CheckManifestHMAC added in v0.18.0

func CheckManifestHMAC(m *Manifest, key []byte) (ok bool, err error)

CheckManifestHMAC reports whether m was signed with key. Unsigned (legacy) manifests return false, nil — callers decide whether to warn.

func Decrypt

func Decrypt(src io.Reader, key []byte, name string) ([]byte, error)

Decrypt is the bounded in-memory convenience API. Large recovery/readback uses DecryptTo.

func DecryptTo added in v0.18.3

func DecryptTo(dst io.Writer, src io.Reader, key []byte, name string) (int64, error)

DecryptTo reads v2 streams and legacy v1 files. Only authenticated chunks are written, but callers must consume EOF and reject all output if any error occurs. v1 decoding retains its original bounded whole-file authentication.

func Dir

func Dir(dataDir string) string

Dir is the backups directory of dataDir.

func Encrypt

func Encrypt(src io.Reader, key []byte, name string) ([]byte, error)

Encrypt is the bounded in-memory convenience API. Uploads must use EncryptTo.

func EncryptTo added in v0.18.3

func EncryptTo(dst io.Writer, src io.Reader, key []byte, name string) (int64, error)

EncryptTo writes a v2 DARE20 stream with a fresh file key bound to name. Memory usage is independent of the input size. Only ciphertext is written.

func EncryptedSize added in v0.18.3

func EncryptedSize(size int64) (int64, error)

EncryptedSize returns the v2 ciphertext size, including the authenticated envelope.

func File

func File(dataDir, name string) (string, error)

File returns the path of the backup file name (<stamp>.db, <stamp>.tar.gz, <stamp>-analytics.db or <stamp>.manifest.json); any other name, or a missing file, is fs.ErrNotExist.

func ManifestFile added in v0.2.0

func ManifestFile(dataDir, stamp string) (string, error)

ManifestFile returns the path of <stamp>.manifest.json under the backups directory when it exists as a regular file.

func PartialPath added in v0.2.0

func PartialPath(destination string) string

PartialPath is the staging directory Restore fills before it renames it to destination: a hidden sibling, so an interrupted restore never looks complete.

func Prune added in v0.2.0

func Prune(dataDir string, keep int) (removed int, err error)

Prune deletes the oldest complete local backup pairs under dataDir so that at most keep complete pairs remain (newest first). A complete pair has both .db and .tar.gz. Incomplete stamps are left alone. keep < 1 is a no-op. Each pruned stamp removes .db, .tar.gz, -analytics.db, .manifest.json and .remote.json when present. Create is unchanged: callers opt into Prune after Create.

func PruneVerified added in v0.4.0

func PruneVerified(dataDir string, keep int) (removed int, err error)

PruneVerified counts only pairs that pass a fresh integrity/archive inspection. Incomplete, legacy, and corrupt copies never displace a known-good copy and are left for the operator to inspect rather than silently deleted.

func PruneVerifiedKeeping added in v0.15.0

func PruneVerifiedKeeping(dataDir string, keep int, stamp string) (int, error)

PruneVerifiedKeeping reserves one retention slot for the just-created pair, even when the host clock moved backwards since an earlier run.

func PruneVerifiedKeepingWith added in v0.18.0

func PruneVerifiedKeepingWith(dataDir string, keep int, stamp string, key []byte) (int, error)

PruneVerifiedKeepingWith is PruneVerifiedKeeping with a site secret for HMAC.

func PruneVerifiedWith added in v0.18.0

func PruneVerifiedWith(dataDir string, keep int, key []byte) (removed int, err error)

PruneVerifiedWith is PruneVerified, verifying keyed manifest HMACs with key.

func RemovePartial added in v0.2.0

func RemovePartial(destination string) error

RemovePartial deletes the leftover staging directory for destination (see PartialPath). It only removes that exact sibling name under the destination's parent via os.OpenRoot — never a path outside restores/.

func RestoreDestination added in v0.2.0

func RestoreDestination(dataDir, stamp string) (string, error)

RestoreDestination is the confined path for an admin restore of stamp: <dataDir>/restores/<stamp>. The directory must not exist before Restore.

func Restored added in v0.2.0

func Restored(dataDir, stamp string) (string, bool)

Restored reports whether a previous restore for stamp is present under <dataDir>/restores/<stamp> (directory with gorun.db).

func StoreUploadReceipt

func StoreUploadReceipt(res Result, receipt UploadReceipt) error

func StoreUploadReceiptWithKey added in v0.18.3

func StoreUploadReceiptWithKey(res Result, receipt UploadReceipt, key []byte) error

StoreUploadReceiptWithKey authenticates the manifest identity, destination, remote object keys and readback result with the existing site secret.

func ValidStamp added in v0.2.0

func ValidStamp(s string) bool

ValidStamp reports whether s is a backup timestamp name.

Types

type Entry

type Entry struct {
	Stamp     string
	Time      time.Time
	DBSize    int64 // -1 when the .db is missing
	FilesSize int64 // -1 when the .tar.gz is missing
	// AnalyticsSize is the size of <stamp>-analytics.db, -1 when absent.
	AnalyticsSize int64
	LocalState    string
	HasManifest   bool
	Remote        UploadReceipt
}

Entry is one backup in the backups directory (either file may be missing).

func List

func List(dataDir string) ([]Entry, error)

List returns the backups of dataDir, newest first.

func ListWithKey added in v0.18.3

func ListWithKey(dataDir string, key []byte) ([]Entry, error)

ListWithKey verifies historical readback receipts with the site secret. Presence-only local metadata remains distinct from an integrity inspection.

type ExternalRequirement

type ExternalRequirement struct {
	Kind      string `json:"kind"`
	Reference string `json:"reference"`
	State     string `json:"state"`
}

type FileDigest

type FileDigest struct {
	Name   string `json:"name"`
	Size   int64  `json:"size"`
	SHA256 string `json:"sha256"`
}

type Inspection

type Inspection struct {
	Stamp                string `json:"stamp"`
	CheckedAt            string `json:"checked_at"`
	LocalComplete        bool   `json:"local_complete"`
	IntegrityOK          bool   `json:"integrity_ok"`
	ForeignKeyViolations int    `json:"foreign_key_violations"`
	ArchiveFiles         int    `json:"archive_files"`
	RecoveryKeyArchived  bool   `json:"recovery_key_archived"`
	// Analytics reports that the backup has the optional analytics.db
	// member (restore puts it back as <destination>/analytics.db).
	Analytics            bool                  `json:"analytics_included"`
	Consistency          string                `json:"consistency"`
	Readiness            string                `json:"readiness"`
	Errors               []string              `json:"errors"`
	Warnings             []string              `json:"warnings"`
	CutoverChecklist     []string              `json:"cutover_checklist"`
	ExternalRequirements []ExternalRequirement `json:"external_requirements"`
	Remote               UploadReceipt         `json:"remote"`
}

func Inspect

func Inspect(manifestPath string) (*Inspection, error)

Inspect never creates a DB, secret or receipt, and makes no network requests. Readiness tiers:

  • blocked: local pair cannot be restored (errors)
  • review_required: local pair usable, but site-specific warnings need attention before cutover
  • local_ok: local checksums/integrity passed; cutover_checklist still lists secrets/themes/writes/remote

func InspectWith added in v0.18.0

func InspectWith(manifestPath string, key []byte) (*Inspection, error)

InspectWith is Inspect, verifying a keyed manifest HMAC when key is the 32-byte site secret. When a key is supplied, an HMAC is required (omitting or stripping it is treated as tampering). Without a key, legacy unsigned manifests are still accepted on digest checks alone.

func Restore

func Restore(manifestPath, destination string) (report *Inspection, resultErr error)

Restore writes only into a new directory. It never merges into an existing installation; the caller must review external requirements before booting it. Everything is written into PartialPath(destination) first and renamed to destination only after the database and archive checksums verify, so destination exists only for a complete restore.

A backup with the optional analytics member restores it as <destination>/analytics.db, verified like gorun.db. The staging directory is new and empty, so no analytics.db-wal or -shm can sit beside it. A backup without the member restores no analytics.db: Restore never touches the live data directory, so an existing analytics.db stays where it is (copy it into the new directory before cutover to keep it). Restore never runs against the live tree, so a running analytics store needs no Reopen.

func RestoreWith added in v0.18.0

func RestoreWith(manifestPath, destination string, key []byte) (report *Inspection, resultErr error)

RestoreWith is Restore, verifying a keyed manifest HMAC when key is set.

type Manifest

type Manifest struct {
	FormatVersion      int        `json:"format_version"`
	Stamp              string     `json:"stamp"`
	CreatedAt          string     `json:"created_at"`
	Database           FileDigest `json:"database"`
	Archive            FileDigest `json:"archive"`
	Consistency        string     `json:"consistency"`
	RecoveryFiles      []string   `json:"recovery_files"`
	Limitations        []string   `json:"limitations"`
	EncryptedFileLimit int64      `json:"encrypted_file_limit"`
	// Analytics is the optional <stamp>-analytics.db member: absent when
	// the backup skipped analytics or there was no analytics.db.
	Analytics *FileDigest `json:"analytics,omitempty"`
	// HMAC is hex(HMAC-SHA256(site secret, domain || canonical digests)).
	// Empty on legacy manifests written before keyed integrity.
	HMAC string `json:"hmac,omitempty"`
}

func ReadManifest

func ReadManifest(path string) (*Manifest, error)

func (*Manifest) Members added in v0.19.0

func (m *Manifest) Members() []FileDigest

Members are the backup files the manifest binds, in upload order: database, archive and, when present, the analytics copy.

type Options

type Options struct {
	// IncludeSecrets archives secret.key and gorun.toml in plain text
	// (`gorun backup --include-secrets`). Keep such archives offline and
	// encrypted; the admin never sets it.
	IncludeSecrets bool
	// SecretKey (32 bytes) HMACs the manifest so a writable backups
	// directory alone cannot forge digests. Prefer the site secret.
	SecretKey []byte
	// SkipAnalytics leaves <dataDir>/analytics.db out of the backup.
	// Callers set it from the setting named by SettingSkipAnalytics.
	SkipAnalytics bool
	// SkipAnalyticsReason, when set with SkipAnalytics, marks the skip as
	// involuntary (for example the setting could not be read): it is copied
	// to Result.AnalyticsSkipped so callers warn about it. A deliberate
	// exclusion leaves it empty.
	SkipAnalyticsReason string
}

Options adjust Create.

type Result

type Result struct {
	Stamp, DB, Files string
	Manifest         string
	// Analytics is the <stamp>-analytics.db copy, or "" when the backup
	// has no analytics member (skipped, or no analytics.db to copy).
	Analytics string
	// AnalyticsSkipped is "<code>: <detail>" when analytics.db existed (or
	// may have) but was left out against the operator's choice: the copy
	// failed, the copy failed its integrity check, or the exclusion setting
	// could not be read. The core backup still succeeds without the member;
	// callers must surface this as a warning. Empty otherwise.
	AnalyticsSkipped string
	Count            int // archived files
}

Result names the files one backup wrote.

func Create

func Create(dataDir, dbPath string, now time.Time) (Result, error)

Create backs up the database at dbPath and the data trees of dataDir, named after now, without the recovery secrets (see Options).

func CreateWith

func CreateWith(dataDir, dbPath string, now time.Time, opts Options) (Result, error)

CreateWith is Create with options.

type UploadFile

type UploadFile struct {
	Name      string `json:"name"`
	State     string `json:"state"`
	ObjectKey string `json:"object_key,omitempty"`
	Code      string `json:"code,omitempty"`
	CheckedAt string `json:"checked_at"`
}

type UploadReceipt

type UploadReceipt struct {
	State            string       `json:"state"`
	Files            []UploadFile `json:"files"`
	ReadBackVerified bool         `json:"read_back_verified"`
	Stamp            string       `json:"stamp,omitempty"`
	DestinationID    string       `json:"destination_id,omitempty"`
	ManifestHMAC     string       `json:"manifest_hmac,omitempty"`
	HMAC             string       `json:"hmac,omitempty"`
	DatabaseSHA256   string       `json:"database_sha256,omitempty"`
	ArchiveSHA256    string       `json:"archive_sha256,omitempty"`
	// AnalyticsSHA256 is set when the manifest has an analytics member; the
	// receipt then lists it as a third file.
	AnalyticsSHA256 string `json:"analytics_sha256,omitempty"`
}

UploadReceipt records acknowledgments; keyed receipts may additionally attest to authenticated readback at upload time, not continued remote availability.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL