Infrastructure · Data Protection

How Boom protects — and restores — every client's data

Plain-English tour of the backup & security system across all 14 SaaS apps and Boom Online Ordering: what's protected, where it lives, what it costs, and how we roll a single client back to a point in time — whether they're a restaurant on BOO or a service business on the SaaS suite. Status badges below mark what's live today vs. in progress vs. planned — this is the honest picture, gaps included.

Live In progress Planned Open gap

🧭 The model in one screen

Everything Boom stores is sorted into three tiers by how much it would hurt to lose it. The higher the tier, the tighter the backup. Two numbers drive every decision:

RPO — Recovery Point Objective

"How much data can we afford to lose?"

The age of the newest backup you can fall back to. An RPO of 1 hour means a failure could cost up to the last hour of changes. Money data targets an RPO of seconds.

RTO — Recovery Time Objective

"How fast can we be back up?"

How long a restore takes. A tenant row-level restore is minutes; a full-cluster rebuild is longer. Lower RTO costs more (hot standby vs. cold backup).

One-line promise to clients: your data is copied off-box on a schedule, stored in a separate provider you can't accidentally delete, and any single account can be rolled back to an earlier point without touching anyone else's.

🗂️ Three data tiers

Not all data is equal. A lost blog draft is an annoyance; a lost restaurant payment is a crisis. Each tier gets protection matched to its stakes.

Tier 1 · Critical

Money & identity Hardening

Boom Online Ordering transactions, payouts, Stripe records, and login credentials/secrets. Losing or corrupting this = lost revenue and trust.

Plan: isolate BOO's transaction DB from the shared Postgres, add point-in-time recovery (PITR), an append-only order ledger, idempotency keys, and nightly Stripe reconciliation.

~ seconds
Target RPO
minutes
Target RTO
Tier 2 · Important

App databases & uploads Partly live

Every SaaS tenant's records — customers, reviews, loyalty points, documents, menus — plus client-uploaded files (images, PDFs, videos).

Method: hourly logical dump of the shared Supabase Postgres (all tenants) shipped off-site to Backblaze B2. User uploads still need to be included (see gaps).

~ 1 hour
RPO (DB)
< 1 hour
RTO
Tier 3 · Rebuildable

Code & media Covered

Application source code and delivered media. If lost, it can be regenerated or re-deployed — no unique customer state at risk.

Method: code is versioned in GitHub; media is served from Bunny CDN. Bunny is delivery, not backup — master media files also copy to B2.

git commit
RPO
redeploy
RTO

📦 What each app holds & how it's backed up

Every app writes to the shared Supabase Postgres (so one hourly dump covers them all), except BOO's money data, which is being moved onto its own isolated, PITR-protected database.

ProductSensitive data it holdsTierBackup path
🍽️ Boom Online OrderingOrders, payments, payouts, customer PII1Isolated DB + PITR → B2 Planned
📍 LocaleyReviews, citations, customer contacts2Hourly dump → B2
🎁 RewardeeLoyalty points, gift-card balances2Hourly dump → B2
🖊️ SignneeSigned documents, signer identity2Hourly dump + file store → B2
💬 ReplyeeChat transcripts, captured leads2Hourly dump → B2
✓ ApproveeClient projects, feedback, approvals2Hourly dump → B2
📊 DasheeClient dashboards, connected metrics2Hourly dump → B2
🖥️ DisplayeePlaylists, screen config, hosted video2Hourly dump + Bunny masters → B2
🛡️ ComplieeScan history, site issue reports2/3Hourly dump (re-scannable)
📈 RankeeRank history, audits, keywords2/3Hourly dump (re-crawlable)
📣 Addee · ✍️ Bloggy · 📅 PostteeGenerated creative, drafts, schedules3Hourly dump; AI-regenerable
🔳 QRcodeeQR targets, scan analytics3Hourly dump → B2
🛟 AssisteeSession logs (self-hosted RustDesk)3Droplet + config backup

🎯 Pinpoint restore — one client, one point in time

The key capability: roll a single client back to how their data looked at a chosen moment, without disturbing any other tenant. It works in two layers — when (point in time) and who (that one tenant's rows).

Pick the moment
Timestamp before the mistake
Restore that snapshot
Hourly dump (now) · any-second PITR (BOO, planned)
Extract one tenant
Filter by client / restaurant ID

Every tenant-scoped table carries a restaurant_id / client_id and is isolated by Supabase Row-Level Security — so we can lift just that client's rows out of a restored copy and merge them back into live, leaving everyone else untouched.

🍽️ Restaurant client (BOO)

Example: a menu got wiped, or a batch of orders corrupted at 2:14pm
  1. Choose the second just before 2:14pm — PITR replays the write-ahead log to that exact instant.
  2. Spin up a restore copy of the isolated BOO database from Backblaze B2 (originals are immutable — can't be overwritten).
  3. Pull just that restaurant's rows by restaurant_id — menu, orders, customers.
  4. Reconcile payments against Stripe so money records match reality, then merge back to live.

🧰 Service business (SaaS suite)

Example: a law firm's review data or loyalty balances went bad overnight
  1. Choose the hour — restore the most recent hourly Supabase dump from before the problem.
  2. Load it into a scratch database (never onto live) from Backblaze B2.
  3. Extract that one client by client_id across the affected tables.
  4. Merge the good rows back into production; every other tenant is unaffected.
Granularity today vs. target. Right now the SaaS suite restores to the nearest hour (hourly dumps). BOO's move to PITR takes its money data to any-second precision. Both already support per-client extraction because of the tenant-ID + RLS design.

🧱 The stack behind it

Deliberately boring, off-the-shelf pieces — each does one job, and backups land in a provider separate from where the data runs, so a compromise or fat-finger in one place can't reach the copies.

🐘 Supabase Postgres Live
Shared multi-tenant database for the SaaS suite. Self-hosted on the primary droplet; tenant isolation via Row-Level Security.
🪣 Backblaze B2 Wiring
Off-site backup vault (boom-media-backups). Cheap, immutable object storage — the copies live here, away from the app servers.
🔐 Doppler Adopting
Secrets manager for API keys/tokens. Replaces scattered .env files; break-glass export mirrors secrets to B2.
🐙 GitHub Live
Every app's source code, versioned. Tier-3 "backup" — any app can be rebuilt and redeployed from a commit.
🐰 Bunny CDN Live
Delivers images & video fast. Delivery only — master media also copies to B2 so the CDN is never the sole copy.
🗄️ pgBackRest + DO Managed PG Planned
For BOO Tier-1: continuous WAL archiving = point-in-time recovery to any second, on an isolated managed database.

💰 What it costs

Data protection rides mostly on infrastructure already running. The incremental backup/security spend is small relative to what it protects.

PieceWhat it's forEst. monthly
Core infrastructureDroplets, Supabase, app hosting (Vercel/Coolify) — the whole ecosystem$150–260
Backblaze B2Off-site backup storage (~$6/TB/mo) + minimal egress$5–15
DopplerSecrets management (free tier viable early)$0–20
BOO managed Postgres + PITRIsolated transaction DB w/ point-in-time recovery (Tier 1)$15–60
GitHub / BunnyCode hosting + media delivery (existing)included
All-in, fully hardenedEverything above once Tier-1 PITR is live~$175–355/mo

Note: the incremental cost of the backup/DR layer specifically (B2 + Doppler + BOO PITR) is roughly $20–95/mo on top of infrastructure you already run.

🚧 Honest gaps & roadmap

Where the plan is designed but not yet fully in place. These are the things to close next.

Restore drill not yet run. Backups only count once you've proven a restore. Schedule a test: restore one tenant from B2 into a scratch DB and verify. Open
User uploads not in the backup set. The hourly dump covers the database, not client-uploaded files (images/PDFs/video). Add these to the B2 sync. Open
Secrets not backed up. Adopt Doppler across all repos, then set up the break-glass export of secrets → B2. In progress
BOO transaction DB still shares Postgres. Isolate it onto managed PG + PITR so money data isn't coupled to the multi-tenant box. Planned
Verify hourly dump is actually installed + shipping. Confirm the pg_dump cron is running on the droplet and landing in B2 (not just designed). Verify
What's solid: code (GitHub) and media delivery (Bunny) are covered; tenant isolation (RLS + tenant IDs) already enables per-client restore; B2 bucket + Doppler are provisioned. Good

📋 Restore runbook (single client)

The step-by-step when a client needs a rollback. Keep this handy; run it against a scratch database, never live.

  1. Identify scope. Which client (client_id/restaurant_id), which tables, and the exact "good" timestamp before the problem.
  2. Freeze if needed. For active corruption, pause writes for that tenant (feature flag / maintenance) so you're not chasing a moving target.
  3. Pull the backup. Fetch the right artifact from Backblaze B2 — the hourly dump before the timestamp (SaaS), or trigger PITR to the second (BOO).
  4. Restore to scratch. Load into an isolated scratch database. Never restore over live.
  5. Extract the tenant. Select that client's rows across the affected tables from the scratch copy.
  6. Reconcile money (BOO). Cross-check orders/payments against Stripe so financial records match reality.
  7. Merge back. Apply the good rows to production for that tenant only; verify with the client's key screens.
  8. Log it. Record what happened, what was restored, and feed any gap back into this plan.