Skip to main content

Point-in-time snapshots for S3 buckets and DynamoDB tables.

Project description

NSHM Backup Solution

AWS-native backup management CLI for NSHM datasets (ToshiAPI, THS, static reports, Weka). Replaces AWS Backup ($1,700 NZD/month before) with incremental S3 sync + 2-tier Glacier Instant Retrieval lifecycle and DynamoDB Point-in-Time exports — running at **$108 NZD/month** in production since May 2026 (per ADR-006).

Implementation Status

Phase Status Description
Phase 1 ✅ Complete CLI skeleton with Typer + Config system + S3 backup operations
Phase 2 ✅ Complete DynamoDB PITR export + EventBridge scheduling
Phase 3 ✅ Complete Slack + SNS-email; daily health report; Lambda-error alarm (ADR-005); YAML-managed recipients (ADR-008)
Phase 4 ✅ Complete Restore (S3 + DynamoDB, cross-account)
Phase 5 ✅ Complete Testing, validation, event audit log, daily-canary restore tests
Phase 6 ✅ Complete Production cutover (all 4 sources live since April 2026; AWS Backup decommissioned May 2026)
Phase 7 ✅ Complete ADR-009 signal-class taxonomy + class-1 head-check tagging; validated end-to-end against real AWS (sandbox cycles 0–3, June 2026)

Tests: 430+ passing · Coverage: 76% · Lint: ruff + mypy clean


Features (implemented)

  • Configuration: YAML config with Pydantic validation, alias→ARN mapping
  • S3 Backup: Incremental sync with 2-tier lifecycle policy — Standard → Glacier Instant Retrieval at 30 days; objects retained forever (ADR-006)
  • DynamoDB Backup: Point-in-Time export to S3, idempotent export bucket setup
  • EventBridge Scheduling: Create/enable/disable weekly/daily/hourly rules; localised time input and display
  • Lambda Handler: EventBridge-triggered backup orchestration (S3 + DynamoDB)
  • Restore: S3 (direct copy + S3 Batch Operations) and DynamoDB PITR restore with async status tracking
  • Testing: backup test integrity (ETag diff + PITR check) and backup test restore (sample restore)
  • Event audit log: Append-only JSONL log in backup bucket (_events/) — all backup/restore events recorded
  • Daily health report (backup health-report): per-source status + inventory freshness + source-vs-backup divergence (both directions) + class-1 head-check tagging + sampled restore verification, delivered to Slack and SNS-email. Fires automatically at 10:45 NZT (≈60 min after the 09:45 NZT scheduled backup); canary (weka) tested daily, large sources rotated through Mon/Wed/Fri. Signal classification follows ADR-009 (class 1 RED / class 2 ℹ info / class 3 yellow). See docs/user-guide/health-report.md.
  • Lambda-error alarm: CloudWatch alarm on backup Lambda Errors → SNS → email, fires within ~5 min of any hard failure. Complementary to the daily report (ADR-005 fast path).
  • YAML-managed notification recipients: notifications.alerts.emails + notifications.reports.email.addresses lists in backup-config.yaml; backup notifications apply reconciles SNS subscriptions to match. See docs/operations/enabling-notifications.md.
  • Dry-run mode: All mutating operations support --dry-run
  • JSON output: --output json for scripting
  • Localised timestamps: CLI input/output in NZDT/NZST/AEST/AEDT

Installation

uv sync --all-extras      # installs all deps including dev and docs extras

The backup command is registered as a console script and available immediately after install.


Usage

Global flags

backup --help
backup --dry-run run --source toshi     # Simulate without executing
backup --verbose run --source all       # Detailed logging
backup --output json schedule show      # Machine-readable output

Configuration

backup config show                      # Display full loaded config
backup config validate                  # Validate backup-config.yaml
backup config show --key retention      # Show a specific section

Run backup

backup run --source toshi               # S3 sync + DynamoDB PITR export for toshi
backup run --source ths                 # S3 sync for ths
backup run --source all                 # All sources
backup run --source toshi --full-sync   # Force full copy (skip ETag check)
backup --dry-run run --source toshi     # Preview without executing

Schedule management

backup schedule show                    # List EventBridge rules with localised run times

# --time accepts UTC (HH:MM), localised (HH:MM TZ), or full datetime (YYYY-MM-DD HH:MM TZ)
backup schedule add --source toshi --frequency weekly --time '02:00 NZST'
backup schedule add --source toshi --frequency weekly --time '2026-03-29 12:15 NZDT'  # day-of-week from date
backup schedule add --source toshi --frequency daily  --time '01:00 NZST'
backup schedule add --source toshi --frequency hourly --time '00:30'       # :30 past each hour

# Daily health report (ADR-005 slow path) — dispatched via the same Lambda
backup schedule add --source _health --task-type health_report \
    --frequency daily --time '14:30 NZST'

backup schedule enable --source toshi                      # Enable all rules for toshi
backup schedule enable --source toshi --frequency weekly   # Enable weekly only
backup schedule disable --source toshi                     # Disable all rules for toshi
backup schedule remove --source toshi --frequency daily    # Delete rule entirely

Restore

backup restore run --source toshi --buckets nzshm-toshi-api-data
backup restore run --source toshi --tables ToshiAPI-FileTable --to-point-in-time '2026-03-25 07:50 NZDT'
backup restore status --source toshi

Testing

backup test integrity --source toshi             # ETag diff + PITR check
backup test restore --source toshi               # Sample restore (direct copy)
backup test restore --source toshi --use-batch   # Sample restore via S3 Batch Operations
backup test alert                                # Force Lambda-error alarm to verify alarm path

Daily health report (ADR-005 slow path)

backup health-report preview                     # Build + print, skip restore tests
backup health-report run                         # Full report (incl. restore tests)
backup health-report run --send                  # Build + deliver via Slack + SNS-email
backup health-report run --weekday 0             # Force rotation (0=Mon … 6=Sun)

Operator guide: docs/user-guide/health-report.md.

Notification subscriptions

backup notifications show                        # List confirmed/pending subscribers
backup notifications apply                       # Reconcile SNS to match backup-config.yaml lists
backup notifications apply --dry-run             # Preview without changing SNS

Recipient runbook: docs/operations/enabling-notifications.md.

Event audit log

backup events --source toshi                  # Show recent backup/restore events
backup events --source toshi --limit 50

Status & reporting

backup status                                  # Live per-source backup state (implemented)
backup status --source toshi --output json     # Machine-readable status
backup events --source toshi                   # Recent backup/restore event log
backup report --period 30d                     # Aggregate report (stub — coming soon)
backup costs predict                           # Cost forecasting (stub — coming soon)

Configuration

Copy backup-config.example.yaml to backup-config.yaml and fill in your account ID and resource names:

general:
  region: ap-southeast-2
  environment: production
  lambda_arn: null          # Set after first serverless deploy

sources:
  toshi:
    display_name: "ToshiAPI"
    s3_buckets:
      - arn:aws:s3:::YOUR-TOSHI-BUCKET-NAME
    dynamodb_tables:
      - arn:aws:dynamodb:ap-southeast-2:ACCOUNT_ID:table/ToshiAPI-FileTable
      - arn:aws:dynamodb:ap-southeast-2:ACCOUNT_ID:table/ToshiAPI-ThingTable
    dynamodb_export_format: DYNAMODB_JSON

  ths:
    display_name: "THS_dataset_prod"
    s3_buckets:
      - arn:aws:s3:::YOUR-THS-BUCKET-NAME
    dynamodb_export_format: DYNAMODB_JSON

retention:
  hot_days: 30      # S3 Standard
  warm_days: 120    # Glacier Instant (must be >= hot_days + 90; Deep Archive transition derived from this)
  max_age_days: 365

restore:
  auto_approve_threshold: 100    # NZD — auto-approve below this
  dual_approval_threshold: 500   # NZD — two approvers above this

Deployment

Prerequisites

npm install -g serverless
uv sync --all-extras
cp backup-config.example.yaml backup-config.yaml   # edit with real values

Deploy Lambda

serverless deploy                  # Deploy to AWS
serverless deploy --stage prod     # Production stage

# After deploy, update lambda_arn in backup-config.yaml, then re-deploy
# to wire up EventBridge targets.

Add schedules after deploy

backup schedule add --source toshi --frequency weekly --time 14:00
backup schedule add --source ths   --frequency weekly --time 14:00
backup schedule show

Sandbox testing

See scripts/sandbox_setup.sh — creates lightweight source resources (S3 buckets + DynamoDB tables with PITR, seeded with sample data) in a sandbox AWS account so you can run backup run and backup schedule against real AWS without touching production.

# One-time setup
scripts/sandbox_setup.sh setup

# Run backup against sandbox resources
backup run --source toshi
backup run --source all --dry-run

# Tear down all sandbox resources when done
scripts/sandbox_setup.sh teardown

See backup-config.sandbox.yaml for the matching config.


Development

make test                 # All tests with coverage
make lint                 # ruff + mypy
make fmt                  # ruff format + ruff --fix
make check                # lint then test
make upgrade              # upgrade deps (1-week safety margin)

uv run pytest tests/test_foo.py   # single file

Architecture

EventBridge (cron)
  ├── per-source backup at 13:05 NZST → Lambda → S3 sync + DynamoDB PITR export
  └── daily health-report at 14:30 NZST → Lambda → Slack + SNS-email

CloudWatch alarm on Lambda Errors → SNS alerts topic → email subscribers

Backup bucket naming (in the backup account, region-suffixed):

  • S3: bb-{source}-s3-{label}-{region}-{source_account_id}
  • DynamoDB export: bb-{source}-dynamo-{region}-{source_account_id}

IAM: the Lambda role denies s3:DeleteObject and s3:DeleteBucket on real backup buckets (delete-protected; lifecycle expiration still fires). Scoped Allow for both on the bb-restore-test-* name pattern so the restore-test workflow can clean up its temp buckets. DynamoDB restores always go to a new table (never overwrite in-place).


Documentation

Operations:

Architecture / design:

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

aws_snapshot-0.3.0.tar.gz (537.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

aws_snapshot-0.3.0-py3-none-any.whl (132.7 kB view details)

Uploaded Python 3

File details

Details for the file aws_snapshot-0.3.0.tar.gz.

File metadata

  • Download URL: aws_snapshot-0.3.0.tar.gz
  • Upload date:
  • Size: 537.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for aws_snapshot-0.3.0.tar.gz
Algorithm Hash digest
SHA256 01caf1641d4c2766bbe5d94e43aa7e8937fc5b10e3006dbcacdd06b9773aee9e
MD5 a271cc7882b34e0a55f84b9f9eb330df
BLAKE2b-256 f4888ddaa774100f079375842e332296868f74b33717e13a875a12051fe67d3e

See more details on using hashes here.

Provenance

The following attestation bundles were made for aws_snapshot-0.3.0.tar.gz:

Publisher: release.yml on GNS-Science/aws-snapshot

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file aws_snapshot-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: aws_snapshot-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 132.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for aws_snapshot-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 86398a3607d17cef91702c61de02c5b9e538eae66606726245fd27e67d72a6f8
MD5 65800ab22e7fd85bd747056ad238b81b
BLAKE2b-256 2af924dc1c75ac37bd974fd95d9599df977a6aa8863b9e9e5a1ab7d07a3f6d57

See more details on using hashes here.

Provenance

The following attestation bundles were made for aws_snapshot-0.3.0-py3-none-any.whl:

Publisher: release.yml on GNS-Science/aws-snapshot

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page