← Projects

Gallery Sync

KotlinJetpack ComposeRustTauriReactTypeScriptWebSocketTLSSQLiteGitHub ↗

Architecture

Android App  <──── wss:// (LAN, pinned self-signed TLS) ────>  Desktop App
    │                                                                │
  Gallery UI (Compose)                                    Gallery UI (React)
    │                                                                │
  SyncClient (network module)                          sync server (axum + rustls)
    │                                                                │
  media_index.db (SHA-256 hash index, phone-local)          catalog.db (SQLite, PC-local)
                                                        + on-disk thumbnail/original cache
  • Pairing: PC renders a QR (LAN IP, port, one-off session token, TLS public-key fingerprint); phone scans and dials wss://.
  • Catalog sync: phone hashes its media (SHA-256, opt-in/manual — never automatic, to protect battery), sends a paged, delta-aware catalog; the PC diffs against what it already has.
  • Thumbnails: PC auto-requests previews for anything unsent right after a successful pairing/reconnect; phone streams them continuously.
  • Originals (backup): full-resolution files move only on explicit request — either the phone pushing one via its own cloud-icon action, or the PC pushing a backup_request for a multi-selected batch through a live-connection registry that reaches directly into the phone's open socket.
  • Everything phone-sourced is additive, never destructive: a file deleted on the phone is not deleted from the PC's backup — this is a backup tool, not a mirror.

Tech Stack

ComponentChoice
Android frontendKotlin + Jetpack Compose
Android networkingOkHttp WebSocket + custom TLS public-key pinning
Phone-local storageRoom/SQLite (media_index.db)
Desktop frontendReact + TypeScript (Vite)
Desktop backendRust (Tauri, axum, rustls, tokio)
PC-local storageSQLite (catalog.db, via rusqlite)
TransportDirect LAN wss:// — no relay, no cloud