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

backup

package
v0.2.52 Latest Latest
Warning

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

Go to latest
Published: Oct 31, 2026 License: GPL-3.0 Imports: 33 Imported by: 0

Documentation

Overview

Package backup creates integrity-checked, portable snapshots without copying live WAL files.

On SQLite a snapshot holds a VACUUM INTO copy (ether.db). On PostgreSQL, where file copies do not apply, it holds a logical dump of every table (database.jsonl, see store.ExportLogical) and the manifest records dialect "postgres"; a restored dump is imported into an empty database on the next start (ImportPending).

Index

Constants

View Source
const EncryptedExtension = ".etherbackup"

EncryptedExtension names exported bundles.

View Source
const LogicalDumpFile = "database.jsonl"

LogicalDumpFile is the PostgreSQL bundle's database entry. Left in a data directory by a restore, it is imported into the (empty) database at start.

Variables

View Source
var (
	// ErrPasswordPolicy rejects an export password that is too short.
	ErrPasswordPolicy = errors.New("backup password must be at least 12 characters")
	// ErrBadPassword is a wrong password or a damaged first chunk; the
	// two cannot be told apart.
	ErrBadPassword = errors.New("wrong password, or the backup is damaged")
	// ErrDamaged is an encrypted backup that fails authentication after
	// its first chunk, or is truncated or extended.
	ErrDamaged = errors.New("encrypted backup is damaged or incomplete")
	// ErrNotEncrypted is a file without the encrypted backup header.
	ErrNotEncrypted = errors.New("not an encrypted Ether backup")
)

Functions

func FilesystemWarning

func FilesystemWarning(ctx context.Context, dataDir string) string

FilesystemWarning detects the mount containing dataDir on Linux and macOS.

func ImportPending

func ImportPending(ctx context.Context, db *sql.DB, dataDir string) (bool, error)

ImportPending imports a restored logical dump (LogicalDumpFile in dataDir) into db, which must be freshly migrated and empty, then renames the dump so it is imported only once. It reports whether it imported.

func IsEncrypted added in v0.2.4

func IsEncrypted(b []byte) bool

IsEncrypted reports whether b starts with the encrypted backup magic.

func NewDecryptReader added in v0.2.4

func NewDecryptReader(r io.Reader, password string) (io.Reader, error)

NewDecryptReader checks the header of an encrypted bundle, derives the key from password and authenticates the first chunk, so a wrong password fails here (ErrBadPassword) before anything is read from the bundle.

func NewEncryptWriter added in v0.2.4

func NewEncryptWriter(w io.Writer, password string) (io.WriteCloser, error)

NewEncryptWriter returns a writer that encrypts a bundle to w under password. Close must be called to write the final chunk; it does not close w.

func RestoreBundle

func RestoreBundle(ctx context.Context, r io.Reader, destination string) error

RestoreBundle validates all content before atomically installing into a new directory. It deliberately refuses existing destinations and never replaces a running database.

func RestoreEncrypted added in v0.2.4

func RestoreEncrypted(ctx context.Context, r io.Reader, password, destination string) error

RestoreEncrypted is RestoreBundle for a password-encrypted bundle (NewEncryptWriter). Nothing is installed unless the whole file authenticates, including its final chunk and the absence of anything after it.

Types

type Manifest

type Manifest struct {
	Version int               `json:"version"`
	Created time.Time         `json:"created"`
	SHA256  map[string]string `json:"sha256"`
	// Dialect is "postgres" for a logical dump; empty means SQLite.
	Dialect string `json:"dialect,omitempty"`
}

type Node

type Node struct {
	ID, Name string
}

Node identifies the cluster node a Service runs on.

type Service

type Service struct {
	// contains filtered or unexported fields
}

func New

func New(db *sql.DB, configPath, dir string, retain int) *Service

func (*Service) Configure

func (s *Service) Configure(ctx context.Context, v Settings) error

func (*Service) Create

func (s *Service) Create(ctx context.Context) (Snapshot, error)

func (*Service) Handler

func (s *Service) Handler() http.Handler

Handler is the owner's backup API, mounted behind the admin API's requireAuth (which also enforces the CSRF header):

GET  /               bundles, schedule and a filesystem warning
POST /settings       change the schedule              owner, setup complete
POST /               create a bundle now              owner, setup complete
POST /export/{name}  {"password"} → .etherbackup      owner, setup complete
POST /restore        stage an uploaded bundle (below)

A bundle holds config.json, the database and the credential key that unseals them, so it leaves the server only encrypted with a password the owner chooses (encrypted.go). Bundles kept in the data directory stay owner-only (0600) files.

Restore takes the raw file (application/octet-stream). An encrypted bundle needs its password in X-Ether-Backup-Password (base64 of the UTF-8 password) and is accepted wherever the admin API is: from the owner, or before setup completes from the local network (a fresh server being restored from backup). An unencrypted bundle from before encryption needs the signed-in owner and X-Ether-Backup-Legacy: plaintext. Either way the bundle is only validated and staged in a new directory; nothing replaces the running server until the operator restarts Ether from that directory.

func (*Service) List

func (s *Service) List() ([]Snapshot, error)

List is this node's own bundles (its data directory's backups).

func (*Service) Run

func (s *Service) Run(ctx context.Context, onResult func(error))

Run takes scheduled backups until ctx ends. onResult hears every scheduled attempt: its error, or nil once one succeeds. A failing backup is retried with backoff (15 minutes, doubling, at most the interval), not every minute.

func (*Service) SetCluster

func (s *Service) SetCluster(n Node)

SetCluster marks the server as clustered: every bundle is recorded in the shared database with the node that holds it (so any node lists the whole cluster's backups), and the shared backup directory setting is honoured. A standalone server never calls it and keeps its bundles in its data directory only.

func (*Service) Settings

func (s *Service) Settings(ctx context.Context) (Settings, error)

func (*Service) Snapshots

func (s *Service) Snapshots(ctx context.Context) ([]Snapshot, error)

Snapshots lists backups for the owner: this node's bundles on a standalone server, every node's (with where each is) on a clustered one.

type Settings

type Settings struct {
	IntervalHours int   `json:"intervalHours"`
	Retain        int   `json:"retain"`
	LastRun       int64 `json:"lastRun"`
	// SharedDir (clustered servers) is a directory every node can reach,
	// at the same path, where scheduled and manual backups are written
	// instead of the writing node's data directory. Empty keeps them on
	// the node that made them.
	SharedDir string `json:"sharedDir,omitempty"`
}

type Snapshot

type Snapshot struct {
	Name    string    `json:"name"`
	Size    int64     `json:"size"`
	Created time.Time `json:"created"`
	// Clustered servers only: the node that wrote the bundle and holds it
	// (unless Shared), whether it is in the shared backup directory, and
	// Remote when it is on another node's disk (download it there).
	Node   string `json:"node,omitempty"`
	NodeID string `json:"nodeId,omitempty"`
	Shared bool   `json:"shared,omitempty"`
	Remote bool   `json:"remote,omitempty"`
}

Jump to

Keyboard shortcuts

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