Hosted Backups Design

Status: design. Target: ADR-0030.

Goal

Two independent backup paths so a single operator failure cannot lose founder data.

  1. Founder-owned GitHub mirror. Every commit on the tenant volume is pushed to a founder-owned GitHub repo within 60 seconds. This is the canonical recovery path.
  2. Encrypted daily snapshot to founder-owned S3. A nightly tarball encrypted with the tenant DEK, uploaded to a bucket the founder controls (BYO bucket + IAM role).

Why founder-owned

If we hold the only copy, we are the single point of failure. Both paths land in storage the founder controls. We hold a write role, not a read role.

Path 1: GitHub mirror

Setup

  1. Founder connects GitHub via OAuth (Settings → Backups).
  2. We create (or accept) a private repo lycato-<slug> under the founder's account or org.
  3. We store the install token in the tenant secret store (encrypted with the tenant DEK).

Per-commit push

Recovery

If the tenant container is gone, the founder clones the GitHub repo locally and runs:

git clone [email protected]:<founder>/lycato-<slug>.git company
cd company
lycato doctor                       # confirms repo health
lycato migrate                      # if schema version is behind
lycato cockpit                      # back to work

The founder is now running self-hosted. They can stay there or provision a fresh hosted tenant and push the repo in.

Path 2: Encrypted S3 snapshot

Why also have S3

The GitHub mirror covers the markdown + git history. It does NOT cover:

A full S3 snapshot covers everything, encrypted, so a true "clone the box" recovery is possible.

Format

Encryption

Retention

Restore

Founder-initiated only:

  1. Cockpit → Settings → Restore from snapshot → pick date.
  2. We provision a temporary container.
  3. Container fetches the encrypted tarball, decrypts with the tenant DEK, mounts a new volume.
  4. Founder reviews; on confirm, the new volume replaces the live one.

No operator-initiated restore. The founder owns the path back.

Disaster scenarios

Scenario Recovery path
Container crash Auto-restart; data preserved on volume
Volume corruption Latest S3 snapshot → restore
Hosted operator data loss Founder's GitHub repo → self-host
Founder loses GitHub access S3 snapshot (founder still owns bucket)
Founder loses both We retain a 90-day operator backup as last-resort, encrypted with our HSM key, restored only on signed founder request via privacy@Lycato. Not a replacement; insurance.

Audit

Every backup attempt + every restore writes to:

  1. Tenant audit log (.founder/audit/YYYY-MM.jsonl).
  2. Control-plane audit_ops table (operator-visible).
  3. Push notification to founder for restores.

What's intentionally absent