Roots App

A private family media sharing platform where the server is explicitly untrusted. All user content is encrypted client-side before upload, and the server stores and routes data it cannot decrypt.

Technical Lead & Co-Founder, March 2025 to Present. Solo-built the entire technical stack.

133

Cloud Functions

1,494

Automated Tests

47+

Firestore Collections

8

Storage Buckets

Architecture

Flutter mobile client communicating over HTTPS with Firebase Cloud Functions v2 on GCP. Firestore for structured data, Cloud Storage for encrypted media. Clean architecture with BLoC state management, dependency injection via GetIt (76 registrations), and 13 feature modules.

Flutter Client (iOS/Android)
  |
  +-- BLoC (13 state managers)
  |     +-- Repositories (11 abstract interfaces)
  |           +-- Services (25+ API services)
  |                 +-- ApiClient (HTTP + auth interceptor)
  |
  |------- HTTPS -------|
  |
Firebase Cloud Functions v2 (133 functions)
  +-- Auth middleware (Firebase Admin SDK)
  +-- Family access middleware
  +-- Business logic handlers
  |
  +-- Firestore (47+ collections)
  +-- Cloud Storage (8 buckets, signed URLs)
  +-- Firebase Auth (email/password, Google, Apple)
  +-- Cloud Scheduler + Cloud Tasks

Zero-Knowledge Encryption System

The core technical challenge: families need to share photos and videos privately, with the guarantee that even the server operator cannot access their content. This required designing a custom cryptographic system that handles multi-user access, key distribution, and member revocation without server-side plaintext access.

Threat Model

The server is explicitly untrusted for content access. It can validate family membership, serve encrypted blobs, execute queries against blind indexes, and rate-limit key operations. It cannot read content, derive keys, search plaintext, or impersonate users (private keys never leave the client).

Key Hierarchy

User Master Key (Argon2id from password)
  |
  +-- Family Epoch Keys (rotate on member removal)
  |     Wrapped per-member via X25519 ECDH
  |     Used to wrap: Post Keys, Profile Keys, Folder Keys
  |
  +-- Post Keys (unique per post, random AES-256)
        Wrapped with Family Epoch Key
        Encrypts: metadata + all media variants

The Revocation Problem

The obvious approach (single shared key per family) breaks on member revocation. Removing a member would require re-encrypting every piece of media in the family vault. With per-family key hierarchies and individual user key wrapping, revocation only requires re-wrapping the family key for remaining members. This tripled the implementation complexity but made the system viable for real use.

Searchable Encryption

Posts are searchable by tag, type, and location without the server seeing plaintext. HMAC-SHA256 blind indexes are computed client-side and stored per-family context. The server matches on hash values during queries, never seeing the actual search terms.

Design Influences

Async key distribution from Signal (new members receive historical keys from an existing member, not the server). Envelope encryption from Proton (content encrypted with a random DEK, DEK wrapped with a KEK, multiple wrapped copies per access context). The multi-family key isolation and HMAC-based blind indexing are novel to Roots, as neither Signal nor Proton support these patterns.

MCP Servers

Two custom Model Context Protocol servers built to give Claude Code a single source of truth for the project's API contracts and product requirements.

API Documentation Server

Four tools: list/get API schemas, list/get endpoint definitions. Loads Swagger documentation from the backend and exposes it as structured tools. The AI agent queries real API specs during development instead of relying on its own assumptions.

Epics Management Server

Four tools plus two resources: list/search epics and user stories, browse the epic catalog by module, view requirement-to-story mappings. Parses markdown specification files and caches results in memory.

Why This Matters

Both servers follow a dual-mode pattern (HTTP standalone or stdio for MCP-hub integration). They give the AI agent a structured interface to the project's actual specifications, reducing drift between what the AI thinks the API does and what it actually does. This is the same class of tooling that powers agentic coding platforms at scale.

Infrastructure

Terraform manages three Firebase environments (dev/staging/prod) with modular IaC for Firebase services, Firestore, Storage, and Cloud Functions. State stored in GCS. GitHub Actions handles CI with test automation and Terraform plan/apply.

  • Per-function service accounts with least-privilege IAM
  • Storage lifecycle tiering (STANDARD to NEARLINE to COLDLINE)
  • KMS encryption support (configurable, enabled in prod)
  • Security scanning with Trivy and Checkov (weekly + PR scans)
  • CI pipeline: analyze, unit tests (parallel by layer), integration tests, widget tests, E2E on iOS/Android simulators

Tech Stack

Frontend

Flutter, Dart, BLoC

Backend

Node.js, TypeScript, Cloud Functions v2

Database

Firestore, 47+ collections, 15+ compound indexes

Storage

Cloud Storage, 8 buckets, signed-URL delivery

Infrastructure

Terraform, GitHub Actions, Docker

Security

X25519 ECDH, AES-256-GCM, HMAC-SHA256, Argon2id

Auth

Firebase Auth (email, Google, Apple OAuth)

AI Tooling

Claude Code, custom MCP servers (2)