How the Automatic Backup System for Key Shares Works in mpcium

mpcium protects node secret key-shares by running a periodic, encrypted, incremental backup job that snapshots the BadgerDB store, encrypts the dump with AES-256-GCM, and writes versioned backup files to disk.

The fystack/mpcium repository implements a threshold-signature scheme where each node holds sensitive key-share material in a local BadgerDB instance. To prevent catastrophic data loss, the automatic backup system for key shares creates encrypted, incremental snapshots at configurable intervals. This article explains the architecture, execution flow, and restoration process based on the actual source code.

Backup Architecture Overview

The system consists of three tightly-coupled components that handle scheduling, execution, and recovery:

Component Responsibility Key Implementation
Backup starter Reads configuration, creates a time.Ticker, and calls the store’s Backup() method at regular intervals. StartPeriodicBackup in cmd/mpcium/main.go
Backup executor Loads the latest backup version, runs Badger’s incremental DB.Backup, skips when nothing changed, encrypts the payload, writes a self-describing file, and updates the version record. badgerBackupExecutor in pkg/kvstore/badger_backup.go
Restore logic Scans the backup directory, sorts files chronologically, decrypts each one, and loads the data into a fresh Badger instance. RestoreAllBackupsEncrypted / loadEncryptedBackup in pkg/kvstore/badger_backup.go

Starting the Periodic Backup Job

When the node initializes, the CLI parses backup_enabled (default true) and backup_period_seconds from the configuration. If enabled, main.go invokes:

stopBackup := StartPeriodicBackup(ctx, badgerKV, backupPeriodSeconds)
defer stopBackup()

StartPeriodicBackup creates a time.NewTicker and launches a goroutine that, on every tick, invokes badgerKV.Backup(). The returned stopBackup function cancels the context, cleanly halting the background job during shutdown.

Executing an Incremental Backup

BadgerKVStore.Backup() delegates to the executor defined in pkg/kvstore/badger_backup.go. The badgerBackupExecutor performs the following steps:

1. Version Bookkeeping

LoadVersionInfo reads latest.version (or creates a default record) to obtain the last since offset and version counter.

2. Incremental Badger Dump

The executor calls b.DB.Backup(&plain, since), which streams only the changes since the stored offset. Badger returns nextSince. If the dump is empty or nextSince == since, the backup is skipped to avoid writing redundant files.

3. Encryption

The plain dump (plain.Bytes()) is encrypted with AES-256-GCM via encryption.EncryptAESGCM. The encryption key is derived from the same password used for Badger’s on-disk encryption (appConfig.BadgerPassword).

4. Metadata Creation

A JSON object (BadgerBackupMeta) records:

  • Algorithm (AES-256-GCM)
  • Nonce (base64)
  • Timestamps
  • Input Since and output NextSince offsets
  • Key identifier (sha256(key)[:16])

5. File Formatting

The backup file consists of:

  • A magic header ("MPCIUM_BACKUP")
  • A big-endian uint32 length of the metadata JSON
  • The metadata JSON bytes
  • The ciphertext

The filename encodes the node ID, creation time, and version:


backup-<nodeID>-<YYYY-MM-DD_HH-MM-SS>-<version>.enc

6. Persist Version

After a successful write, SaveVersionInfo updates latest.version with the new counter and nextSince.

Restoring from Backup Files

When a node reconstructs its Badger DB after a crash or during initial sync, the restore logic in pkg/kvstore/badger_backup.go executes:

  1. Creates the target directory for the new database.
  2. Opens a fresh Badger instance with the original encryption key.
  3. Iterates over all backup-*.enc files in lexical order (SortedEncryptedBackups).
  4. For each file:
    • Verifies the magic header.
    • Reads the metadata length, unmarshals the JSON, and extracts the nonce.
    • Decrypts the ciphertext with encryption.DecryptAESGCM.
    • Calls db.Load to stream the plaintext backup into Badger.

If any step fails, the restore aborts and cleans up the partially-loaded DB. Successful completion logs a confirmation message.

Configuration and Security

Configuration Knobs

Flag / Viper key Description Default
backup_enabled Enables the periodic job. true
backup_period_seconds Interval between successive backups (seconds). 300 (5 min – defined in pkg/config/constants.go)
backup_dir Destination directory for encrypted backups. ./backups
badger_password Password used both for Badger’s on-disk encryption and backup encryption. must be supplied

These values are read in cmd/mpcium/main.go via viper.SetDefault, viper.GetBool, viper.GetInt, and viper.GetString.

Security Properties

  • Encryption at rest – Backups are AES-256-GCM encrypted with the node’s Badger password, guaranteeing confidentiality even if the backup directory is exposed.
  • Integrity – The GCM authentication tag is bundled in the ciphertext; decryption fails if the data is tampered.
  • Key identification – The metadata stores a truncated SHA-256 of the encryption key, allowing operators to verify the correct key was used during restore.
  • Incremental efficiency – Badger’s native incremental backup means each file contains only the delta since the previous snapshot, reducing I/O and storage overhead.

Code Examples

Starting a Backup Manually

To trigger a backup outside the periodic schedule (e.g., from a REPL or test):

// Assume kvStore is a *kvstore.BadgerKVStore that was created earlier
if err := kvStore.Backup(); err != nil {
    log.Fatalf("Backup failed: %v", err)
}

Restoring a Node from Backup

To reconstruct a database from the encrypted backup directory:

executor := kvstore.NewBadgerBackupExecutor(
    "my-node",               // node ID
    nil,                     // no DB yet – will be created inside RestoreAllBackupsEncrypted
    []byte("my-secret-key"), // same backup encryption key
    "/var/mpcium/backups",   // directory containing backup-*.enc files
)

if err := executor.RestoreAllBackupsEncrypted("./restored-db", []byte("my-secret-key")); err != nil {
    log.Fatalf("Restore failed: %v", err)
}

Summary

  • Automatic scheduling – The StartPeriodicBackup function in cmd/mpcium/main.go launches a background ticker that triggers backups at configurable intervals (default 5 minutes).
  • Incremental snapshots – The badgerBackupExecutor in pkg/kvstore/badger_backup.go uses Badger’s native DB.Backup to capture only changes since the last snapshot, skipping empty cycles.
  • AES-256-GCM encryption – Every backup file is encrypted with the node’s Badger password, bundled with metadata (nonce, timestamps, version info), and written with a magic header for format verification.
  • Versioned file naming – Backups follow the pattern backup-<nodeID>-<timestamp>-<version>.enc, with an internal latest.version tracking the logical offset (Since/NextSince).
  • Restore capabilityRestoreAllBackupsEncrypted scans, decrypts, and loads backup files in chronological order, reconstructing the database state after crashes or migrations.

Frequently Asked Questions

How often does mpcium create automatic backups?

By default, mpcium creates a backup every 300 seconds (5 minutes). You can adjust this interval via the backup_period_seconds configuration key. The ticker logic resides in cmd/mpcium/main.go, and the default value is defined in pkg/config/constants.go.

What encryption algorithm protects the backup files?

Backup files are protected with AES-256-GCM. The encryption uses the same password configured for BadgerDB (badger_password). The implementation in pkg/kvstore/badger_backup.go generates a random nonce for each backup, stores it in the JSON metadata, and appends the GCM authentication tag to the ciphertext to ensure integrity.

Can I restore a node on a different machine using these backups?

Yes. To migrate or recover a node, copy the backup-*.enc files to the new host and invoke RestoreAllBackupsEncrypted from pkg/kvstore/badger_backup.go. You must provide the original badger_password used during backup creation. The restore process opens a fresh Badger instance and replays the incremental dumps in chronological order to reconstruct the key-share database.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →