Upload Backup Data and Manage Snapshots
Once you have a dump on disk, Backup() uploads it as a new snapshot. The same client manages the full snapshot lifecycle: list, inspect, prune, delete, and restore.
import (
sdkclient "github.com/lighthouse-web3/baas-go-sdk/client"
sdktypes "github.com/lighthouse-web3/baas-go-sdk/types"
)
All snippets assume an authenticated client (see Authentication).
Upload a backupโ
Call Backup() on the dump file or the whole db-dumps directory. For daily jobs, keep one stable dump path (for example ./db-dumps/app.dump or ./db-dumps/app.sql) and overwrite that file each run before calling Backup().
snapshot, err := client.Backup([]string{"./db-dumps"}, &sdktypes.BackupOptions{
Description: "nightly database backup",
Tags: map[string]string{
"type": "db-backup",
"env": "prod",
"db": "app_db",
},
Hostname: "backup-runner-01",
})
if err != nil {
log.Fatal(err)
}
log.Printf("snapshotId=%s totalSize=%d chunks=%d", snapshot.SnapshotID, snapshot.TotalSize, snapshot.TotalChunks)
Tip: using a fixed filename in the dump directory enables dedup-friendly, incremental uploads while still creating a fresh snapshot each run.
Watch live progress (optional)โ
opts := &sdktypes.BackupOptions{
Description: "nightly",
OnProgress: func(e sdktypes.ProgressEvent) {
log.Printf("[%s] %d/%d stored=%dB ratio=%.2f",
e.Phase, e.Current, e.Total, e.StoredBytes, e.CompressionRatio)
},
}
snapshot, err := client.Backup([]string{"./db-dumps"}, opts)
Useful BackupOptionsโ
| Field | Meaning |
|---|---|
Description | Human label for the snapshot. |
Tags | Arbitrary map[string]string metadata. |
Hostname | Override the recorded hostname (defaults to the machine's). |
SourceID | Logical source identity. Leave empty and the SDK manages it (see below). |
ParentSnapshotID | Hint the previous snapshot for faster incremental diffing. |
DisablePackCompression | Turn off zstd compression for already-compressed data. |
Encryption | Enable client-side encryption โ see Encryption. |
WorkspaceID | Back up into a different workspace for this call only. |
Sources & SourceIDโ
If you don't set SourceID, the SDK creates/reads a stable id from a .lighthouse/source_id file inside the backup target path. This is what lets the portal group every snapshot of the same machine+path under one Backup Source. Keep that .lighthouse directory and successive backups line up as a coherent history; set an explicit SourceID only if you want to control grouping yourself.
Verify in the portal: open Backup Sources โ your source โ the new snapshot is at the top of the list. See View backups & snapshots in the portal.
List recent snapshotsโ
resp, err := client.ListSnapshots("", 20) // cursor, limit
if err != nil {
log.Fatal(err)
}
for _, s := range resp.Snapshots {
log.Printf("id=%s createdAt=%d desc=%s size=%d", s.SnapshotID, s.CreatedAt, s.Description, s.TotalSize)
}
// resp.Cursor (if non-nil) โ pass back as the cursor arg to get the next page.
Inspect one snapshotโ
s, err := client.GetSnapshot("snapshot-id")
if err != nil {
log.Fatal(err)
}
log.Printf("rootTreeHash=%s paths=%v tags=%v encrypted=%v",
s.RootTreeHash, s.Paths, s.Tags, s.Encryption != nil)
Prune snapshots (retention)โ
Pruning applies a retention policy in one call โ keep the latest N and/or drop everything older than a cutoff. Always dry-run first.
import "time"
keep := 14
before := time.Now().AddDate(0, 0, -30).UTC().Format(time.RFC3339) // older than 30 days
// Step 1: DRY RUN โ see what would be deleted, delete nothing.
preview, err := client.PruneSnapshots(sdktypes.PruneRequest{
KeepLatest: &keep,
Before: before,
DryRun: true,
})
if err != nil {
log.Fatal(err)
}
log.Printf("would delete %d snapshot(s): %v", preview.Count(), preview.SnapshotIDs)
// Step 2: execute for real.
result, err := client.PruneSnapshots(sdktypes.PruneRequest{
KeepLatest: &keep,
Before: before,
DryRun: false,
})
if err != nil {
log.Fatal(err)
}
log.Printf("pruned %d snapshot(s)", result.Count())
How retention combines: when both are set, a snapshot is deleted only if it is both outside the
KeepLatestnewest set and older thanBefore. Set just one for a simpler policy.PruneResponse.Count()returns candidates on a dry run and deletions on a real run.
Delete one snapshotโ
err = client.DeleteSnapshot("snapshot-id")
if err != nil {
log.Fatal(err)
}
This deletes the snapshot record; content-addressed chunks still referenced by other snapshots are retained automatically. It is irreversible โ confirm the id with GetSnapshot first if you are scripting deletions.
Restore a snapshot (recovery drill)โ
Restoring downloads the snapshot's chunks, (decrypts โ) decompresses โ verifies integrity against expected hashes, then reassembles the original files into a target directory. A recovery drill = restore into a scratch directory and confirm the contents, without touching production data.
import (
"os"
"path/filepath"
)
target, _ := os.MkdirTemp("", "lh-restore-drill-*")
log.Printf("restoring into %s", target)
err = client.Restore("snapshot-id", target, &sdktypes.RestoreOptions{
OnProgress: func(e sdktypes.ProgressEvent) {
log.Printf("[%s] %d/%d", e.Phase, e.Current, e.Total)
},
})
if err != nil {
log.Fatal(err)
}
// Sanity-check the restored tree.
_ = filepath.Walk(target, func(p string, info os.FileInfo, err error) error {
if err == nil && !info.IsDir() {
log.Printf("restored: %s (%d bytes)", p, info.Size())
}
return nil
})
Then restore the database from the dump:
- PostgreSQL:
pg_restore ... ./restore-check/db-dumps/app.dump - MySQL:
mysql ... < ./restore-check/db-dumps/app.sql - Other databases: see the matching tutorial.
Recovery-drill checklist:
- โ Restore into a fresh, empty directory (never over live data).
- โ
Leave checksum verification on (don't set
SkipChecksumVerification). - โ Confirm file count, sizes, and spot-check contents.
- โ Note the wall-clock time โ that's your real RTO (recovery time).
- โ Delete the scratch directory when done.
Run this drill on a schedule (e.g. monthly) so you find restore problems before a real outage.
Client-side encryption (optional)โ
With encryption enabled, data is encrypted on your machine (AES-GCM) before upload; the server only ever stores ciphertext and a wrapped key. You hold a passphrase-protected keyfile (the Tenant Master Key, TMK). Lose the keyfile/passphrase โ the data is unrecoverable โ there is no server-side reset.
import "github.com/lighthouse-web3/baas-go-sdk/encrypt"
// Create a keyfile once.
_, err := encrypt.GenerateKeyfile("/secure/lh.keyfile", os.Getenv("LH_KEYFILE_PASSPHRASE"), nil)
if err != nil {
log.Fatal(err)
}
enc := &sdktypes.EncryptionOptions{
KeyfilePath: "/secure/lh.keyfile",
Passphrase: os.Getenv("LH_KEYFILE_PASSPHRASE"),
}
// Backup with encryption.
snapshot, err := client.Backup([]string{"./db-dumps"}, &sdktypes.BackupOptions{
Description: "nightly-encrypted",
Encryption: enc,
})
// Restore an encrypted snapshot โ supply the same keyfile/passphrase.
err = client.Restore("snapshot-id", target, &sdktypes.RestoreOptions{
Encryption: enc,
})
A snapshot's Encryption field (non-nil) tells you whether it was encrypted. The SDK also supports TMK rotation (client.RotateTMK(...)) to re-wrap a snapshot's data key under a new master key without re-encrypting any data blobs.