Skip to content

mherrera53/backup-manager

Repository files navigation

Backup Manager

A native macOS app for safely pulling, sanitizing, and sharing production database backups.

Swift Platform CI License: MIT

Overview * Features * Requirements * Getting started * Architecture * Security * Contributing


Overview

Pulling a production database dump to a laptop is normally a trade-off between speed and safety: either you skip sanitization to save time, or you sanitize by hand and risk missing a column. Backup Manager removes that trade-off by turning the whole flow into a single, auditable pipeline:

Production DB --SSH/SSM tunnel--> Dump --> Temp import --> Sanitize PII --> Export --> Upload to S3 --> Share

Every step -- the tunnel, the dump, the column-level sanitization rules, the upload, and the share link handed to a teammate -- is driven from one SwiftUI app, logged to an audit trail, and never touches your shell history with a plaintext password.

Features

Area What it does
Bastion connections SSH, AWS SSM, or direct TCP tunnels into RDS/MySQL clusters, grouped by bastion so one host can serve multiple databases
Sanitization engine 9 column-level strategies -- fake email/name/phone, SHA-256 hash, mask, nullify, static value, character scramble, and full-table truncation
DEFINER rewriting Rewrites view/trigger/routine DEFINER clauses to the local MySQL user on import, so dumps from a shared RDS account import cleanly on a laptop
Turbo import mode Temporarily tunes InnoDB (buffer pool, redo log, flush behavior) for fast bulk import, then restores safe defaults automatically
Pipeline orchestration Multi-stage runner with live progress, retry-from-failed-stage, and a persistent audit log per run
S3 distribution Uploads the sanitized dump and generates a time-boxed presigned URL with a generated password
Team distribution Looks up members of a GitHub organization and notifies a recipient directly -- no more pasting links into chat
Parallel compression Uses pigz for multi-core gzip, so large dumps compress/decompress in a fraction of the single-threaded time
Encrypted local state Profile data is encrypted at rest with AES-GCM; RDS credentials are read from and written to the macOS Keychain, never stored in plaintext
Bilingual UI Full English and Spanish localization

Requirements

System

  • macOS 14.0 (Sonoma) or later
  • Apple Silicon or Intel

CLI tools -- auto-detected on first launch, with an in-app install hint if missing:

Tool Purpose Install
mysql / mysqldump Database connection and dumps brew install mysql-client
pigz Parallel gzip compression brew install pigz
pv Transfer progress monitoring brew install pv
aws S3 upload and presigned URLs brew install awscli
gh GitHub org lookup and distribution brew install gh
ssh SSH tunnels Pre-installed on macOS
mydumper / myloader (optional) Faster parallel dump/restore for large databases brew install mydumper
brew install mysql-client pigz pv awscli gh

Getting started

Option A -- download a release (recommended)

Grab the latest .zip from Releases, unzip it, and drag Backup Manager.app into /Applications. Every release is signed with a Developer ID Application certificate and notarized by Apple, so it opens with a plain double-click -- no Gatekeeper warning, no right-click -> Open workaround.

Every tagged release (vX.Y.Z) is built, signed, and notarized automatically by the release workflow -- the binary you download is built directly from that tag's source, not uploaded by hand.

Option B -- build from source

git clone https://github.com/mherrera53/backup-manager.git
cd backup-manager
./build.sh

build.sh compiles a universal binary (arm64 + x86_64), assembles a signed .app bundle, and installs it to /Applications/Backup Manager.app. On first launch, the onboarding flow checks for missing CLI tools and walks you through creating your first bastion profile.

Local development

swift build                                  # debug build
swift build -c release --arch arm64          # release build, single arch

Cutting a release

Push a tag matching vX.Y.Z and CI builds the universal binary, packages the .app, and publishes a GitHub Release with the zip attached:

git tag v1.0.0
git push origin v1.0.0

Architecture

Sources/BackupManager/
+-- Models/
|   +-- SanitizationProfile.swift   AppConfig, Bastion, DatabaseConfig, TableRule
|   +-- BackupFile.swift            PipelineRun, PipelineStage, LogEntry
|   +-- DefaultProfiles.swift       First-run defaults, rule templates
|   +-- Distribution.swift          Distribution records and config
+-- Services/
|   +-- DatabaseService.swift       MySQL operations, tunnels, turbo mode
|   +-- ProfileStore.swift          Encrypted persistence (AES-GCM)
|   +-- KeychainService.swift       macOS Keychain integration
|   +-- S3Service.swift             AWS S3 upload/list
|   +-- ShellService.swift          Shell command execution
|   +-- ShareLinkService.swift      Presigned URL + shortlink generation
|   +-- GitHubOrgService.swift      GitHub org member lookup and distribution
|   +-- CLIDetector.swift           CLI tool auto-detection
+-- ViewModels/
|   +-- AppViewModel.swift          Main app state and pipeline orchestration
+-- Views/
|   +-- ContentView.swift           App shell, sidebar, navigation
|   +-- ProfileEditorView.swift     Bastion editor
|   +-- PipelineView.swift          Pipeline execution with activity feed
|   +-- SanitizationRulesView.swift Rule editor with schema browser
|   +-- BackupsView.swift           Local backup file browser
|   +-- S3BrowserView.swift         S3 object browser with share links
|   +-- DistributionView.swift      Team distribution via GitHub
|   +-- OnboardingView.swift        First-run setup wizard
+-- Styles/GlassStyles.swift        Shared view modifiers
+-- Localization.swift              Bilingual string helper
+-- Utilities.swift                 Formatters and helpers
+-- App.swift                       Entry point

Data model

AppConfig (global)
+-- local: LocalConnection            One MySQL local config
+-- s3: S3Config                      One S3 config
+-- backupDirectory: String
+-- bastions: [Bastion]
    +-- Bastion
        +-- connection: SSH | SSM | Direct
        +-- RDS endpoint + credentials (Keychain-backed)
        +-- databases: [DatabaseConfig]
            +-- DatabaseConfig
                +-- name
                +-- tableRules: [TableRule]
                    +-- columns: [ColumnRule]
                        +-- strategy: SanitizationStrategy

Pipeline modes

Mode Flow
Backup Pipeline Tunnel -> remote dump -> import to temp DB -> sanitize -> export -> upload to S3 -- the full end-to-end run
Sanitize Local Clone local DB -> sanitize -> export -- sanitizes an existing local database without touching remote servers
Refresh Local Import dump -> replace local DB -- imports a backup file directly into local MySQL, with a safety backup taken first

Sanitization strategies

Strategy Applied as Typical use
Fake Email CONCAT('user_', id, '@sanitized.test') Email columns
Fake Name CONCAT('Persona_', id) Name columns
Fake Phone CONCAT('+1555', LPAD(id, 7, '0')) Phone numbers
SHA-256 Hash SHA2(column, 256) Tokens, password hashes
Mask a***z (first + last char kept) IDs, addresses
Nullify NULL Coordinates, secrets
Static Value Custom replacement Company/tenant names
Scramble Reversed substring Usernames
Truncate Table TRUNCATE TABLE Logs, audit trails

Security

  • No credentials in source. All sensitive data lives in user config files (outside the repo, under ~/Library/Application Support/BackupManager/) and the macOS Keychain.
  • Encrypted local config. ProfileStore seals profile data with AES-GCM using a key derived from the machine hostname and username -- the encrypted blob is portable to nowhere else by design.
  • Keychain-backed passwords. KeychainService reads/writes RDS credentials through the macOS Keychain rather than storing them in any JSON file.
  • Shell and SQL escaping. All user-controlled strings are escaped before shell interpolation or SQL generation.
  • MYSQL_PWD over argv. Passwords are passed to mysql/mysqldump via environment variable, never as a command-line argument (which would leak into ps).
  • Audit trail. Every pipeline run is logged to ~/Library/Application Support/BackupManager/audit/audit_YYYY-MM-DD.log.
  • Signed and notarized releases. Every tagged release is signed with a Developer ID Application certificate and notarized by Apple's notary service before publishing -- verify any release with spctl -a -vvv --type execute "Backup Manager.app", which should print source=Notarized Developer ID.

Found a security issue? Please see SECURITY.md before opening a public issue.

Local storage layout

~/Library/Application Support/BackupManager/
+-- app_config.json                 Bastion and global config
+-- profiles.enc                    Encrypted legacy profiles (AES-GCM)
+-- default_rules.json              Sanitization rule templates
+-- distribution_history.json       Distribution records
+-- distribution_config.json        Distribution settings
+-- audit/
    +-- audit_YYYY-MM-DD.log        Daily audit logs

None of the above ever leaves ~/Library/Application Support except the sanitized dump itself, which is uploaded to the S3 bucket you configure.

Contributing

Contributions are welcome -- see CONTRIBUTING.md for the development workflow, coding conventions, and PR checklist.

Changelog

See CHANGELOG.md.

License

MIT -- see LICENSE.

About

Native macOS app for MySQL backup sanitization, SSH/SSM tunneling, and secure team distribution

Resources

Contributing

Security policy

Stars

Watchers

Forks

Releases

Packages

Used by

Contributors

Languages