MainSDK
Search⌘K
Interfaces

Reliability and operations

Backups and restore drills

backup

Declare how much data you can lose and how fast you must restore, check your provider can meet it, and prove it with timed restore drills.

Building for 1.2ships as a beta APIPhase 3 · Mobile and regulatedSpec backup-restore-evidenceFree in your app, Apache-2.0

Status Status is where the work is: Planned (in a milestone, not started), Building (tickets in progress), Preview (usable, may change), then the stability it ships at.

Stability The promise an interface ships with.

  • Stable Follows semantic versioning; breaking changes need a new major, 12 months of overlap and a codemod.
  • Beta Usable in production; may change before it becomes stable.
  • Experimental May change or go away; needs an explicit opt-in in main.yaml.
Stability levels
Updated 2026-10-28

backup turns "we have backups" into proof: declare your RPO and RTO, let mainsdk check test your provider against them, and record timed restore drills as signed evidence.

At a glance

backup:
  rpo: 15m
  rto: 1h
  keep: 35d
  drill: { every: 30d, to: scratch }

Calls

  • runDrill({ driver, target, profile, at })
  • signDrill(result, key)
  • verifyDrill(evidence, trusted)
  • backupIssues(profile, declared, drill)
  • drillControls({ standard, evidence, now })
  • backupBadge({ evidence, now })

Guarantees

Whatever the driver.

  • Backups encrypted, copied off-site and immutable for the retention period
  • The badge turns amber when no restore drill passed in 30 days
  • Restore per tenant, not only all or nothing
  • MainSDK never runs or stores backups itself; the provider does

Drivers

6 drivers. Select one to see its line in main.yaml.

main.yamlLocal (tests and drills without a provider)
backup:
  driver: local

Change this one line to swap providers; your calls stay the same.Built-in and local drivers integration

Codes

CodeWhere it appearsMeaning
MAIN-BACKUP-0001Checkmainsdk check, before deployThe provider's best RPO (PITR step, else snapshot gap) is above backup.rpo. Fix: turn on PITR or raise backup.rpo
MAIN-BACKUP-0002Checkmainsdk check, before deployThe provider keeps restore points for less than backup.keep. Fix: raise the provider retention or lower backup.keep
MAIN-BACKUP-0003Checkmainsdk check, before deployThe last drill took longer than backup.rto. Fix: shrink the restore path or raise backup.rto to what a drill measured
MAIN-BACKUP-0006Checkmainsdk check, before deploybackup.rpo, backup.rto, backup.keep or backup.drill is missing or not a duration. Fix: set durations such as 15m, 1h, 35d
MAIN-BACKUP-0004RuntimeError responses and logsSee the spec
MAIN-BACKUP-0005RuntimeError responses and logsSee the spec

Features

Nothing here has shipped yet. Each feature lands in its phase; the spec ticket builds it.

FeaturePhaseMilestoneSpec ticket
Backup and point-in-time restore in the db and files contractsPhase 31.2backup-restore-evidence#backup-capability
Provider backup settings readPhase 31.2backup-restore-evidence#provider-readers
RPO, RTO and retention checkPhase 31.2backup-restore-evidence#rpo-rto-check
Timed restore drill into scratchPhase 31.2backup-restore-evidence#drill-restore
Signed drill evidencePhase 31.2backup-restore-evidence#drill-evidence

Quick start

Declare your targets in main.yaml, then run a drill.

main.yaml
db:
  driver: postgres
backup:
  rpo: 15m
  rto: 1h
  keep: 35d
  drill: { every: 30d, to: scratch }
bash
npx mainsdk drill restore --at "2027-07-01T10:00:00Z" --to scratch
text
 restored orders-db (Neon branch) in 6m 12s · RTO 1h  · RPO 15m  (PITR window 7d)
 evidence signed: .mainsdk/evidence/drill-2027-07-14.json

In code, runDrill, signDrill, verifyDrill, backupIssues, drillControls and backupBadge from mainsdk/backup are the calls behind the command. A driver's optional snapshot() only asks your provider for a snapshot.

Configuration

Every key below lives under backup:.

main.yaml
backup:
  rpo: 15m          # duration: the most data you can lose
  rto: 1h           # duration: the longest time a restore may take
  keep: 35d         # duration: how long the provider keeps backups
  drill:
    every: 30d      # how often a drill should pass
    to: scratch     # where drills restore; never production
KeyTypeWhat it does
rpodurationRequired. Checked against the provider's point-in-time window and snapshot frequency
rtodurationRequired. A drill that takes longer fails RTO
keepdurationRetention you expect the provider to hold
drill.everydurationDrill cadence; the trust badge turns amber when no drill passed in 30 days
drill.tostringThe restore target for drills, scratch in every example; never production

The backup driver follows your data drivers: each db and files driver declares its backup capabilities (point-in-time window, snapshot frequency, off-site copy, object lock) in its capability profile. How you select a separate backup driver, such as pgBackRest or AWS Backup, is not specified yet.

Overlays by environment

Production usually needs tighter targets than staging. Put the difference in an environment overlay:

main.env.prod.yaml
backup:
  rpo: 5m
  rto: 30m

See main.yaml for overlay order.

What you get

  • A check that your database and file providers can meet the RPO, RTO and retention you declared.
  • Timed restore drills that restore into a scratch environment, never production, and delete it afterwards.
  • Signed drill evidence your auditors can verify, and a trust badge that turns amber when no drill passed in 30 days.
  • No backups run or stored by MainSDK: your provider does that, and backup exposes its backup and point-in-time restore through one interface.

The calls, all from mainsdk/backup:

CallWhat it does
runDrill({ driver, target, profile, at })Restores the point at into scratch, times it and checks the rows, then deletes the target
signDrill(result, key)Signs a drill result as evidence
verifyDrill(evidence, trusted)Checks the signature and the trusted keys
backupIssues(profile, declared, drill)The findings behind MAIN-BACKUP-0001 to 0003: where the provider or the last drill misses your targets
drillControls({ standard, evidence, now })The control results (A1.2, A1.3, CC7.5; 8.13, 5.30; PCI 12.10.1) that assure reads
backupBadge({ evidence, now })The badge state: current, or amber when no drill passed in 30 days

Examples

Check your provider meets your targets

mainsdk check reads the provider's backup settings and fails with the gap when they cannot meet your RPO and RTO.

main.yaml
backup:
  rpo: 5m
  rto: 1h
bash
npx mainsdk check --json

With a provider that takes daily snapshots and has no point-in-time recovery, the check fails with a MAIN-BACKUP- finding that names the provider setting and the change needed, such as turning on point-in-time recovery.

Run a restore drill in CI

Drills are free to run locally and in your own CI. A drill restores into a new branch or database, times it, verifies row counts or checksums, and tears the scratch environment down.

.github/workflows/restore-drill.yml
name: restore-drill
on:
  schedule:
    - cron: "0 4 1 * *"
jobs:
  drill:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v4
      - run: pnpm install --frozen-lockfile
      - run: npx mainsdk drill restore --at "$(date -u -d '1 hour ago' +%Y-%m-%dT%H:%M:%SZ)" --to scratch

If the restore takes 72 minutes against rto: 1h, the drill fails RTO and the evidence records the measured time. How a drill authenticates to your provider in CI is not specified yet.

Attach drill evidence to an audit

Each drill is signed evidence that mainsdk assure includes for SOC 2 (A1.2, A1.3, CC7.5), ISO 27001 (8.13, 5.30) and PCI DSS.

bash
npx mainsdk assure --standard soc2 --json

After a passing drill, A1.3 passes with the drill's signed evidence attached, and its signature verifies.

Errors and checks

When the provider's backup settings cannot meet the declared RPO or RTO, mainsdk check fails with a finding that names the setting to change: MAIN-BACKUP-0001 (RPO), MAIN-BACKUP-0002 (retention), MAIN-BACKUP-0003 (last drill over RTO) or MAIN-BACKUP-0006 (a target missing or not a duration). At run time, MAIN-BACKUP-0004 means a drill was asked to restore outside scratch, and MAIN-BACKUP-0005 means the provider returned no settings. Fix the named provider setting, or relax the target.

A failed drill (RTO missed, checksum mismatch) is recorded in the evidence file with the measured values. The launch report shows the trust badge amber with "restore drill overdue" when the last passing drill is older than 30 days.

Swapping drivers

Backups follow your data driver. Change the db driver and the backup capabilities come from the new driver's profile; your backup: block and drills stay the same.

main.yaml
db:
  driver: neon   # was postgres; point-in-time recovery comes from Neon

Testing

Point-in-time windows and restores need a real provider, so the drill itself is your test: run mainsdk drill restore on a schedule in CI, as in the example above. Each db and files driver's declared backup capabilities are checked against a test project by its conformance suite; see the Testing kit.

Security and privacy

  • MainSDK never holds backup copies or pays for storage; the provider does. A "backup now" call only asks the provider for a snapshot.
  • Drills never restore into production, and the scratch environment is deleted at the end.
  • Backups are erased for one person by deleting their key, through keys and privacy.
  • Drills are free locally and in your CI.

FAQ

Does MainSDK back up my database? No. Your provider does. MainSDK checks its settings, runs drills and keeps the evidence.

Why did my badge turn amber? No restore drill passed in the last 30 days. Run mainsdk drill restore.

Can a drill touch production? No. Drills restore only into a new branch or scratch database.

Next

Was this page helpful?

↑↓ moveEnter openEsc close

Not decided yet