Skip to content

Architecture Overview

Apple Juicer runs as five services behind Docker Compose. The FastAPI backend is the hub: the browser talks to it, it owns the database and the job queue, and it mounts the backups read-only. The slow work — decrypting and parsing — runs in a separate RQ worker so the API never blocks on it.

CLIENT SERVER QUEUE · STORES DISK (READ-ONLY)

FETCH ENQUEUE DISPATCH WRITE SQL READ DOWNLOAD USER Browser SPA Frontend nginx + React API FastAPI backend routers + services async SQLAlchemy QUEUE Redis RQ · default WORKER RQ worker decrypt + parse DATA PostgreSQL backups + artifacts MOUNT Backup files Finder / iTunes SOLID = sync request AMBER = async job dispatch DASHED = on-demand file read

The split follows one rule: anything slow or CPU-heavy goes to the worker, so the API stays responsive.

  • FastAPI backend. Serves the REST API for discovery, decryption, unlocking, manifest browsing, and the per-artifact views. It owns the database connection and enqueues jobs.
  • RQ worker. Pulls jobs off Redis and runs the long operations: decrypting a backup and parsing its SQLite artifacts into Postgres. It shares the backend’s Docker image, so it has the same parsers and models.
  • PostgreSQL. The system of record. Holds the backup registry, the normalized artifact tables, and the search index. Tests and local runs fall back to SQLite.
  • Redis. The job queue (default) plus RQ’s own job metadata and heartbeats. It does not store session tokens or decrypted data.
  • React frontend. The investigator UI, built with Vite and served by nginx. It calls the backend with an API token and, once a backup is unlocked, a session token.

Five flows cover everything the tool does, in the order you hit them:

  1. Discover. BackupRegistry scans the configured backup directory, records each backup’s metadata in Postgres, and serves the list at GET /backups.
  2. Decrypt. Posting the passphrase enqueues a worker job. The worker unlocks the keybag and Manifest with iphone-backup-decrypt, extracts the artifact databases, and marks the backup DECRYPTED. The UI polls until it finishes.
  3. Index. Once decrypted, a job parses each artifact database into normalized rows and populates the cross-artifact search index. The backup moves to INDEXED.
  4. Browse. The frontend reads the per-artifact endpoints, the global search, and the timeline straight from Postgres. No backup payloads are touched.
  5. Download. When you open an attachment or photo, the backend reads that one file from disk (or the decrypted store), streams it, and deletes any temporary copy afterward.

The backend holds no durable state of its own: Postgres is for persistence, Redis is for coordination, and the backups stay on disk. You can restart the API without losing anything, and a heavy indexing run never freezes the UI. If a backup is large, you watch the status tick over while the worker does the work.