diff --git a/README.md b/README.md index 01866c88..9735eae0 100644 --- a/README.md +++ b/README.md @@ -1,235 +1,257 @@ # Closer -> *Your private moments stay private. Your information is securely encrypted, never read, and never sold. Only you and your paired partner have the key.* -> -> *Private daily questions for couples who want honest answers before shared conversations.* +> ๐Ÿ” **Private daily questions for couples โ€” end-to-end encrypted, never read, never sold.** +> *You and your paired partner hold the only key.* -**Product goal:** private, mutual-reveal relationship questions with real encryption and calmer UX. +A native couples-relationship app that turns check-ins into small, intentional rituals: one daily question, curated conversation packs, private answers, mutual reveal, gentle reminders, and shared date planning โ€” with **real E2EE** and **calmer UX**. -Closer is a **couples relationship app** built for partners who want a steadier way to talk, reflect, and make time for each other. It turns relationship check-ins into small, intentional rituals โ€” one daily question, curated conversation packs, private answers, partner reveal flows, gentle reminders, and shared date planning. +Not a social network. Not therapy. Not a productivity tracker. **No public feeds, no likes, no followers, no infinite scroll.** -The app is **not** a social network, **not** a therapy replacement, and **not** a productivity tracker. There are no public feeds, no likes, no followers, and no infinite scroll. The core loop is simple: **answer honestly, choose what to reveal, and keep a record of the conversations that mattered.** - -Closer is built as native apps on **Android (Kotlin / Jetpack Compose)** and **iOS (SwiftUI)**, both backed by the same Firebase stack and shared data model. The Android app is the reference implementation; iOS has full screen parity but cannot yet pair end-to-end because the invite/pairing Cloud Functions require Android's E2EE key material. +The core loop is simple: *answer honestly โ†’ choose what to reveal โ†’ keep a record of the conversations that mattered.* --- -## Pricing Philosophy +

+ Android + iOS + Backend + Min Android + Min iOS + Kotlin + Swift + License +

-> *No shady trials. No cancellation friction. One subscription per couple, not per person.* +--- -Subscription apps for couples have a trust problem โ€” confusing trial wording, hard-to-cancel flows, and partners getting double-billed. Closer treats pricing the same way it treats relationships: **clear, straightforward, and built on honesty.** +## TL;DR -| Principle | How Closer Handles It | +| What | Why it matters | | --- | --- | -| Free tier | Generous by default. Daily questions, basic packs, recent history, spin wheel โ€” all free, no time limit. | -| One sub per couple | One premium purchase unlocks it for both partners. No second billing. | -| Trial wording | No auto-renewal trials that silently convert. If there's a free period, you'll know exactly when and how it ends. | -| Cancellation | Clear, actionable instructions in-app and in docs. No hidden unsubscribe paths. | -| Restore | Works with one tap. If it breaks, support fixes it โ€” no escalation maze. | +| ๐Ÿ” **Real E2EE** | Answer content is encrypted on-device. Server only sees ciphertext. Couple-owned keys via Tink (Android). | +| ๐Ÿ’‘ **One subscription per couple** | No double-billing partners. Premium unlocks for both โ€” server-verified. | +| ๐Ÿšซ **No engagement traps** | No infinite scroll. No likes. No follower counts. One daily question is the loop. | +| ๐ŸŒ— **Decoupled theme + art** | In-app light/dark controls art; system theme isn't required to match. | +| ๐Ÿ“ฑ **Native on both platforms** | Kotlin/Compose on Android, SwiftUI on iOS โ€” same Firebase backend, same data model. | +| ๐Ÿงช **QA you can run** | `scripts/theme-scan.sh` (Pass C) and `scripts/wiring-scan.sh` (Pass N) catch the silent-dead-feature and theme-hardcoding classes before merge. | --- ## Screenshots -

- Closer onboarding screen - Closer home dashboard - Closer daily question answer screen - Closer question pack library +

+ Onboarding + Home + Daily question + Question packs + Answer history

-

- Closer answer history - Closer play hub - Closer date planning screen - Closer settings screen +

+ Play hub + Date planning + Settings + Login + Bucket list (new Pixel 8 emulator)

-

- Closer login screen - Closer bucket list screen (new Pixel 8 emulator) -

- -> **New emulator screenshot:** `docs/screenshots/10-bucket-list.png` is a placeholder for a fresh capture from a Pixel 8 / API 35 emulator showing the bucket list. This Linux environment has no Android SDK/emulator, so the image must be captured on a machine with Android Studio and copied into `docs/screenshots/`. +> ๐Ÿ“ธ The bucket-list screenshot at `docs/screenshots/10-bucket-list.png` is a placeholder for a fresh capture from a Pixel 8 / API 35 emulator. This Linux environment has no Android SDK, so the image must be captured on a machine with Android Studio and copied into `docs/screenshots/`. --- -## What Closer Does +## Why Closer exists -Closer gives couples a private, shared space for guided connection. Every feature should support -the product goal: **private, mutual-reveal relationship questions with real encryption and calmer UX**. +Subscription apps for couples have a trust problem โ€” confusing trial wording, hard-to-cancel flows, partners getting double-billed. Couples products have a *different* trust problem: partners are asked to be vulnerable in the same space where everything else (social, productivity, dating) wants their engagement, their data, and their attention. -- **Daily question** โ€” one prompt a day, with written, scale, multiple-choice, and this-or-that answer modes. -- **Private-first answers** โ€” each partner answers independently before deciding whether to reveal or discuss. -- **Question packs** โ€” 22+ curated categories spanning 6,000+ bundled prompts (communication, conflict, trust, intimacy, parenting, marriage, money, stress, date night, and more). -- **Answer history** โ€” review past questions and answers, delete controls, and partner reveal support. -- **Discussion threads** โ€” question-specific conversation threads and reactions for follow-up. -- **Partner pairing** โ€” 6-character invite code with copy and share, and partner-aware home states. -- **Spin the wheel** โ€” category-based random questions for date nights, road trips, and low-pressure moments. -- **Date tools** โ€” swipe-to-match date ideas, date planning preferences, and a shared bucket list. -- **Settings & privacy** โ€” account, notifications, appearance, subscription, relationship, privacy, and account deletion flows. -- **Subscriptions** โ€” free and premium tiers powered by RevenueCat (Google Play Billing on Android, StoreKit on iOS). +Closer treats both the same way: **clear, straightforward, and built on honesty.** + +- ๐Ÿชž **Private first, reveal second.** Each partner answers independently. Both decide what to share. +- ๐Ÿง  **Curated, not generated.** 6,000+ hand-written prompts across 22 categories โ€” no AI confabulation in the core loop. +- ๐Ÿ’ธ **One sub, not two.** Subscription unlocks for both partners. Server-verified. No silent trial conversions. +- ๐Ÿ”’ **Encryption that earns the word.** Tink AEAD with couple-owned keys. Recovery phrase. Server never sees plaintext. +- ๐ŸŒ™ **Quiet hours, server-side.** Partner pushes respect the *recipient's* in-app window โ€” not just foreground detection. --- -## Product Shape +## What Closer does -Closer is optimized for **short, meaningful sessions** rather than endless engagement. - -| Area | Behavior | -| --- | --- | -| Home | Surfaces the next best action โ€” daily question, partner activity, saved answers | -| Questions | Local-bundled content so the app stays fast and usable without waiting on the network | -| Partner Sync | Firebase Auth + Firestore isolate user, couple, invite, thread, and entitlement data per couple | -| Reminders | FCM + local notification preferences and quiet-hour controls | -| Premium | Unlocks deeper packs, full history, Desire Sync, select Connection Challenges, Memory Lane, and analytics โ€” **one subscription for both partners** | - ---- - -## Platform Status - -| Platform | Stack | Status | +| Feature | Free | Premium | | --- | --- | --- | -| **Android** | Kotlin ยท Jetpack Compose ยท Material 3 ยท Hilt ยท Room ยท DataStore | **Active development** โ€” feature-complete MVP, light/dark theme polished | -| **iOS** | SwiftUI ยท MVVM ยท async/await ยท Firebase iOS SDK | **In progress** โ€” full scaffold + screen parity landed on `dev` branch; pairing is blocked until iOS E2EE keys are wired | -| **Backend** | Firebase Auth ยท Firestore ยท Cloud Functions (TypeScript) ยท FCM ยท App Check | **Shared source of truth** for both platforms | -| **Billing** | RevenueCat ยท Google Play Billing ยท StoreKit | Server-verified entitlements via Cloud Function webhook | +| Daily question (text / scale / multi / this-or-that) | โœ… | โœ… | +| 6,000+ prompts ยท 22 question packs | โœ… | โœ… (incl. premium-only packs) | +| Private answers + mutual-reveal flow | โœ… | โœ… | +| Spin the wheel โ€” category-randomized questions | โœ… | โœ… | +| Recent answer history (last 30 days) | โœ… | โœ… | +| Full answer history (search, filter, export) | โ€” | โœ… | +| Saved spin-wheel sessions | โ€” | โœ… | +| Memory Lane (locked time capsules) | โ€” | โœ… | +| Desire Sync (preferences alignment exercise) | โ€” | โœ… | +| Select Connection Challenges (multi-day programs) | โœ… (free) + ๐ŸŽŸ๏ธ (premium tiers) | โœ… | +| Push reminders with quiet-hour support | โœ… | โœ… | +| Account deletion + data export | โœ… | โœ… | -The Android app is the **reference implementation** โ€” the iOS port is built to mirror it screen-for-screen and consume the same backend. +One subscription unlocks premium for **both** partners โ€” `couples/{coupleId}/entitlements` is per-couple, not per-user. --- -## MVP Feature Scope +## Platform status -### Free Tier โ€” No Time Limit, No Hidden Cap +| Platform | Stack | Status | Notes | +| --- | --- | --- | --- | +| **Android** | Kotlin ยท Jetpack Compose ยท Material 3 ยท Hilt ยท Room ยท DataStore | ๐ŸŸข **Reference implementation** | Feature-complete MVP, light/dark theme polished | +| **iOS** | SwiftUI ยท MVVM ยท async/await ยท Firebase iOS SDK | ๐ŸŸก **Scaffold landed on `dev`** | Full screen parity; pairing from iOS is blocked until E2EE keys are wired (Android-only today) | +| **Backend** | Firebase Auth ยท Firestore ยท Cloud Functions ยท FCM ยท App Check | ๐ŸŸข **Shared source of truth** | 17 callable/trigger/scheduled/webhook functions | +| **Billing** | RevenueCat ยท Google Play Billing ยท StoreKit | ๐ŸŸข **Server-verified** | Webhook โ†’ Firestore entitlements โ†’ `CouplePremiumChecker` | -All of this is free, forever. No credits, no daily limits that magically shrink after a week. - -- **Email or Google sign-up** โ€” no anonymous onboarding flow; accounts are email/password or Google Sign-In (Android uses Credential Manager). -- 6-character invite code pairing (copy or share via any app) -- Daily question with full answer modes (text, scale, multiple choice, this-or-that) -- Private answer reveal flow once both partners have answered -- Question packs โ€” 6,000+ bundled prompts across 22 categories (most packs included free) -- Spin wheel โ€” category-based random questions for date nights -- Recent answer history (last 30 days) -- Push reminders with quiet-hour support -- Account deletion and data export - -### Premium Tier (Per-Couple, Not Per-Person) - -One purchase unlocks premium for both partners. No separate subscriptions. - -- **Unlimited daily questions** with premium-only question packs -- **Full answer history** โ€” search, filter, export the complete timeline -- **Saved spin wheel session history** โ€” revisit what landed -- **Desire Sync** โ€” preferences alignment exercise for tough conversations -- **Select Connection Challenges** โ€” premium multi-day programs; free challenges available to all users -- **Memory Lane** โ€” time capsules with locked prompts that open on future dates -- **Advanced analytics and relationship insights** -- **Priority support** - -### Out of Scope (Future) -- AI-assisted question suggestions -- Native group/relationship types beyond dyadic couples -- Wearable (Wear OS / watchOS) companions -- Live video or voice sessions +> ๐Ÿ“ iOS scaffold has **all 49 screens** mapped to SwiftUI views, Firebase + RevenueCat integration, and full screen parity on the `dev` branch. CryptoKit-based E2EE parity (interop with Android's Tink key material) is the only blocker for end-to-end iOS pairing. --- -## Tech Stack +## Architecture at a glance + +```text + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” + โ”‚ Android (Kotlin/Compose)โ”‚ โ”‚ iOS (SwiftUI) โ”‚ + โ”‚ โ€ข Hilt DI ยท Room ยท DSK โ”‚ โ”‚ โ€ข MVVM ยท AppState ยท SPM โ”‚ + โ”‚ โ€ข Tink AEAD + Argon2id โ”‚ โ”‚ โ€ข CryptoKit (follow-up) โ”‚ + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + โ”‚ โ”‚ + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + โ”‚ + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” + โ”‚ Firebase โ”‚ + โ”‚ โ€ข Auth (email / โ”‚ + โ”‚ Google via โ”‚ + โ”‚ Credential Mgr) โ”‚ + โ”‚ โ€ข Firestore โ”‚ + โ”‚ (couple-scoped) โ”‚ + โ”‚ โ€ข Cloud Functions โ”‚ + โ”‚ โ€ข FCM โ”‚ + โ”‚ โ€ข App Check โ”‚ + โ”‚ (Play Integrity)โ”‚ + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + โ”‚ + โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” + โ”‚ RevenueCat โ”‚ + โ”‚ โ†’ webhook โ”‚ + โ”‚ โ†’ Firestore โ”‚ + โ”‚ entitlements โ”‚ + โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ +``` + +- **Couple-scoped data.** Firestore rules deny cross-couple reads/writes. Users only see their own + their partner's surface. +- **Server-mediated pairing.** 6-character invite codes are enumerable; invite reads/writes are server-side only. +- **Server-verified billing.** RevenueCat โ†’ Cloud Function webhook โ†’ Firestore `users/{uid}/entitlements/premium` โ†’ `CouplePremiumChecker` observes both partners' premium state. +- **Local-first questions.** Prompts ship in the app so daily questions load instantly; only assignment and sync hit the network. + +--- + +## Tech stack ### Android + | Layer | Stack | | --- | --- | | Language | Kotlin 2.x | -| UI | Jetpack Compose, Material 3, Navigation Compose | +| UI | Jetpack Compose ยท Material 3 ยท Navigation Compose | | Architecture | Clean architecture โ€” `core/` ยท `data/` ยท `domain/` ยท `ui/` | | State | ViewModel ยท Kotlin Coroutines ยท Kotlin Flow | -| Dependency Injection | Hilt | -| Local Data | Room ยท DataStore Preferences ยท bundled SQLite seed | -| SDK | min 26 ยท target 35 ยท compile 35 | +| DI | Hilt | +| Local data | Room ยท DataStore Preferences ยท bundled SQLite seed | +| Crypto | Google Tink AEAD + Argon2id (Bouncy Castle KDF) | +| Auth | Firebase Auth ยท **Credential Manager** for Google Sign-In | +| Build | min 26 ยท target 35 ยท compile 35 ยท Java 17 ยท KSP | ### iOS + | Layer | Stack | | --- | --- | | Language | Swift 6.0 | | UI | SwiftUI ยท NavigationStack ยท TabView | | Architecture | MVVM ยท `AppState` ObservableObject ยท `EnvironmentObject` | -| Concurrency | async/await | -| Dependency Management | Swift Package Manager ยท XcodeGen (`project.yml`) | -| SDK | iOS 17+ | +| Concurrency | async/await ยท Swift 6 strict concurrency | +| Dependency management | Swift Package Manager ยท XcodeGen (`project.yml`) | +| Auth | Firebase Auth ยท Google Sign-In SDK | +| Crypto | Apple CryptoKit (E2EE parity โ€” follow-up) | +| SDK | iOS 17.0+ | + +### Backend (shared) -### Backend (Shared) | Layer | Stack | | --- | --- | -| Auth | Firebase Authentication (email/password, Google) โ€” Android uses Credential Manager for Google Sign-In | -| Database | Cloud Firestore | -| Server Logic | Firebase Cloud Functions (TypeScript) | +| Auth | Firebase Authentication โ€” **email/password ยท Google** (Android uses Credential Manager) | +| Database | Cloud Firestore (couple-scoped rules) | +| Server logic | Firebase Cloud Functions (TypeScript) | | Push | Firebase Cloud Messaging (FCM) | -| Security | Firebase App Check ยท Play Integrity (Android) ยท DeviceCheck (iOS) | +| Security | Firebase App Check ยท Play Integrity (Android) ยท DeviceCheck (iOS, planned) | | Billing | RevenueCat (server-verified entitlements) | | Analytics | Firebase Analytics ยท Crashlytics | +> ๐Ÿšซ **No anonymous auth.** There is no anonymous sign-in or account-linking flow in either platform. Accounts are email/password or Google. + --- -## Repository Layout +## Repository layout ```text . -โ”œโ”€โ”€ app/ # Native Android app (Kotlin) +โ”œโ”€โ”€ app/ # Native Android app (Kotlin) โ”‚ โ””โ”€โ”€ src/main/java/app/closer -โ”‚ โ”œโ”€โ”€ core/ # Firebase, analytics, billing, navigation, notifications, security -โ”‚ โ”œโ”€โ”€ data/ # Room, Firestore data sources, repositories, seed parsing -โ”‚ โ”œโ”€โ”€ domain/ # Models and repository contracts -โ”‚ โ””โ”€โ”€ ui/ # Compose screens and feature ViewModels -โ”œโ”€โ”€ iphone/ # Native iOS app (SwiftUI) -โ”‚ โ”œโ”€โ”€ ARCHITECTURE_AUDIT.md # iOS port blueprint (49 screens, schema, models) -โ”‚ โ”œโ”€โ”€ project.yml # XcodeGen project spec -โ”‚ โ”œโ”€โ”€ Package.swift # SPM dependency manifest +โ”‚ โ”œโ”€โ”€ core/ # Firebase, analytics, billing, nav, notifications, security +โ”‚ โ”œโ”€โ”€ data/ # Room, Firestore data sources, repositories, seed parsing +โ”‚ โ”œโ”€โ”€ domain/ # Models + repository contracts +โ”‚ โ””โ”€โ”€ ui/ # Compose screens + feature ViewModels +โ”œโ”€โ”€ iphone/ # Native iOS app (SwiftUI) +โ”‚ โ”œโ”€โ”€ ARCHITECTURE_AUDIT.md # iOS port blueprint (49 screens, schema, models) +โ”‚ โ”œโ”€โ”€ project.yml # XcodeGen project spec +โ”‚ โ”œโ”€โ”€ Package.swift # SPM dependency manifest โ”‚ โ””โ”€โ”€ Closer/ -โ”‚ โ”œโ”€โ”€ Models/ # Firestore + domain codable types -โ”‚ โ”œโ”€โ”€ Core/ # Auth, Billing, Notifications -โ”‚ โ”œโ”€โ”€ Services/ # FirestoreService (callable wrappers) -โ”‚ โ”œโ”€โ”€ Theme/ # CloserTheme (colors, typography, spacing) -โ”‚ โ”œโ”€โ”€ Components/ # Shared SwiftUI components -โ”‚ โ”œโ”€โ”€ Navigation/ # Root ContentView + TabView routing -โ”‚ โ”œโ”€โ”€ Onboarding/ # Onboarding, login, signup, profile creation -โ”‚ โ”œโ”€โ”€ Pairing/ # Invite code, partner confirm, recovery -โ”‚ โ”œโ”€โ”€ Home/ # Home dashboard, streak, partner mirror -โ”‚ โ”œโ”€โ”€ Questions/ # Daily question, answer reveal, history, packs -โ”‚ โ”œโ”€โ”€ Play/ # Play hub + games (ThisOrThat, HowWell, DesireSync) -โ”‚ โ”œโ”€โ”€ Wheel/ # Spin wheel with animated 8-slice picker -โ”‚ โ”œโ”€โ”€ Dates/ # Date swipe, matches, builder, bucket list -โ”‚ โ””โ”€โ”€ Settings/ # Settings, paywall, help, data export -โ”œโ”€โ”€ functions/ # Firebase Cloud Functions (TypeScript) +โ”‚ โ”œโ”€โ”€ Models/ # Firestore + domain codable types +โ”‚ โ”œโ”€โ”€ Core/ # Auth ยท Billing ยท Notifications +โ”‚ โ”œโ”€โ”€ Services/ # FirestoreService (callable wrappers) +โ”‚ โ”œโ”€โ”€ Theme/ # CloserTheme (colors, typography, spacing) +โ”‚ โ”œโ”€โ”€ Components/ # Shared SwiftUI components +โ”‚ โ”œโ”€โ”€ Navigation/ # Root ContentView + TabView routing +โ”‚ โ”œโ”€โ”€ Onboarding/ # Onboarding ยท login ยท signup ยท profile creation +โ”‚ โ”œโ”€โ”€ Pairing/ # Invite code ยท partner confirm ยท recovery +โ”‚ โ”œโ”€โ”€ Home/ # Home dashboard ยท streak ยท partner mirror +โ”‚ โ”œโ”€โ”€ Questions/ # Daily Q ยท answer reveal ยท history ยท packs +โ”‚ โ”œโ”€โ”€ Play/ # Play hub + games (ToT, HowWell, DesireSync, โ€ฆ) +โ”‚ โ”œโ”€โ”€ Wheel/ # Spin wheel +โ”‚ โ”œโ”€โ”€ Dates/ # Date swipe ยท matches ยท builder ยท bucket list +โ”‚ โ””โ”€โ”€ Settings/ # Settings ยท paywall ยท help ยท data export +โ”œโ”€โ”€ functions/ # Firebase Cloud Functions (TypeScript) โ”‚ โ””โ”€โ”€ src/ -โ”‚ โ”œโ”€โ”€ auth/ # Auth flow + invite lifecycle -โ”‚ โ”œโ”€โ”€ billing/ # RevenueCat webhook + entitlement sync -โ”‚ โ”œโ”€โ”€ couples/ # Pairing, leave, daily question triggers -โ”‚ โ”œโ”€โ”€ questions/ # onAnswerWritten, onMessageWritten, threads -โ”‚ โ””โ”€โ”€ server/ # Internal Express webhook service (not client-facing) -โ”œโ”€โ”€ scripts/ # Automated QA / lint scanners -โ”‚ โ”œโ”€โ”€ theme-scan.sh # Pass C: light/dark theme hardcoding scanner -โ”‚ โ””โ”€โ”€ wiring-scan.sh # Pass N: dead-feature / orphan-wiring scanner -โ”œโ”€โ”€ server/ # Optional Express webhook/health service -โ”œโ”€โ”€ seed/ # Question pack JSON and local DB generation -โ”œโ”€โ”€ docs/ # QA notes, release prep, roadmap, screenshots -โ””โ”€โ”€ firestore.rules # Firestore security rules +โ”‚ โ”œโ”€โ”€ auth/ # Auth + invite lifecycle +โ”‚ โ”œโ”€โ”€ billing/ # RevenueCat webhook + entitlement sync +โ”‚ โ”œโ”€โ”€ couples/ # Pairing, leave, daily-question triggers +โ”‚ โ”œโ”€โ”€ questions/ # onAnswerWritten ยท onMessageWritten ยท threads +โ”‚ โ”œโ”€โ”€ games/ # onGameSessionUpdate ยท onGamePartFinished +โ”‚ โ”œโ”€โ”€ notifications/ # quiet-hours helper ยท reminders +โ”‚ โ””โ”€โ”€ server/ # Internal Express webhook service +โ”œโ”€โ”€ scripts/ # Automated QA / lint scanners +โ”‚ โ”œโ”€โ”€ theme-scan.sh # Pass C: light/dark theme-hardcoding scanner +โ”‚ โ””โ”€โ”€ wiring-scan.sh # Pass N: dead-feature / orphan-wiring scanner +โ”œโ”€โ”€ server/ # Optional Express webhook/health service +โ”œโ”€โ”€ seed/ # Question-pack JSON + local DB generation +โ”œโ”€โ”€ docs/ # QA notes ยท release prep ยท roadmap ยท screenshots +โ””โ”€โ”€ firestore.rules # Firestore security rules (single source of truth) ``` +> ๐Ÿงช `scripts/theme-scan.sh` and `scripts/wiring-scan.sh` are run before every QA pass. They statically catch the two costliest QA classes: hardcoded theme colors and silent dead features. + --- -## Getting Started +## Getting started ### Prerequisites - **Android:** Android Studio ยท Android SDK ยท JDK 17 - **iOS:** Xcode 16 ยท macOS ยท [XcodeGen](https://github.com/yonaskolb/XcodeGen) (`brew install xcodegen`) -- **Firebase:** Project with Auth, Firestore, Cloud Messaging, Crashlytics, Analytics, App Check +- **Firebase:** Project with Auth ยท Firestore ยท Cloud Messaging ยท Crashlytics ยท Analytics ยท App Check - **Android config:** `app/google-services.json` - **iOS config:** `iphone/Closer/GoogleService-Info.plist` -- **Billing:** RevenueCat project with Android and iOS API keys +- **Billing:** RevenueCat project with Android + iOS API keys - **Node 20** for Firebase Functions tooling ### Local config @@ -241,8 +263,6 @@ cp local.properties.example local.properties cp iphone/Closer/GoogleService-Info.plist.example iphone/Closer/GoogleService-Info.plist ``` -Add local-only values such as: - ```properties sdk.dir=/path/to/Android/Sdk RC_API_KEY_ANDROID=your_revenuecat_android_key @@ -256,10 +276,9 @@ RC_API_KEY_IOS=your_revenuecat_ios_key ./gradlew :app:installDebug ``` -Useful verification command: - ```bash -./gradlew :app:compileDebugKotlin +./gradlew :app:compileDebugKotlin # fast verification +./gradlew :app:testDebugUnitTest # 205 unit tests ``` ### iOS @@ -270,8 +289,6 @@ xcodegen generate xed Closer.xcodeproj ``` -Build from the command line: - ```bash xcodebuild -project iphone/Closer.xcodeproj \ -scheme Closer \ @@ -288,7 +305,11 @@ npm run build npm run serve ``` -### Optional Server +```bash +npm test # 24 functions tests +``` + +### Optional server ```bash cd server @@ -298,57 +319,50 @@ npm run dev --- -## Data Model +## Security & privacy -Closer combines local-first question content with cloud sync for shared relationship state: +- ๐Ÿ” **E2EE answer content.** Tink AEAD with couple-owned keys. Server never sees plaintext answers or capsule content. +- ๐Ÿง‚ **Key wrapping.** Argon2id KDF over the recovery phrase; keys wrapped client-side. +- ๐Ÿชช **Recovery phrase.** Server-blind; wiped from the inviter on acceptance. +- ๐Ÿšง **Firestore rules.** Couple-scoped; deny-by-default; field allowlists on `users/{uid}` updates; shape-restricted couple create. +- ๐Ÿ›ก๏ธ **App Check.** Play Integrity (Android), DeviceCheck (iOS, planned) โ€” blocks abusive backend access. +- ๐ŸŒ™ **Quiet hours, server-side.** Suppression is enforced where the push is **sent**, not where it might be foregrounded. Client cannot bypass by being backgrounded. +- ๐Ÿ’ธ **Server-verified billing.** Cloud Function writes `users/{uid}/entitlements/premium` from the RevenueCat webhook. Client cannot self-grant. -- **Room (Android) / local bundled JSON (iOS)** stores seeded question categories, questions, and date preferences โ€” the app stays usable offline. -- **DataStore (Android) / UserDefaults (iOS)** stores local settings and lightweight preferences. -- **Firestore** stores users, couples, invites, daily questions, answer history, date matches, bucket-list state, time capsules, gentle reminders, and entitlements. -- **Cloud Functions** handle pairing lifecycle, reminder workflows, billing entitlement sync, RevenueCat webhooks, partner activity triggers (FCM), and security checks. -- **FCM** delivers reminders and partner activity notifications to both platforms. +> Full architecture reference: [`docs/Engineering_Reference_Manual.md`](docs/Engineering_Reference_Manual.md) โ€” the canonical source of truth for the security model, data model, and Cloud Functions wiring. --- -## Privacy & Safety Principles +## Roadmap -- Couple data is **scoped by couple ID** and protected by Firestore security rules โ€” no other couple can read another couple's data. -- Answers are **private first**, then revealed intentionally by the partner. -- Account deletion and privacy screens are first-class surfaces in the app. -- **App Check** (Play Integrity on Android, DeviceCheck on iOS) blocks abusive backend access. -- Subscription state is verified **server-side** via Cloud Functions rather than trusting the client. -- All private answer and capsule content is **end-to-end encrypted** with couple-owned keys (Android uses Tink; iOS uses Apple CryptoKit in a follow-up batch). +In progress: + +- ๐Ÿ” **iOS E2EE parity** (CryptoKit interop with Android's Tink key material) โ€” *unblocks pairing from iOS*. +- ๐Ÿงช **On-device / instrumented test coverage** (Compose UI / Espresso smoke) โ€” currently `app/src/test` only. +- ๐ŸŽจ **Activity `uiMode` sync** to in-app theme (C-DARKART-002) โ€” the dark-variant `-night` PNGs only render in the right combination today; architectural fix in `MainActivity`. +- ๐Ÿ›’ **Real release config** โ€” version, legal/support URLs, RevenueCat offerings verified end-to-end on internal testing. + +Out of scope (for now): + +- AI-assisted question suggestions +- Native group/relationship types beyond dyadic couples +- Wearable (Wear OS / watchOS) companions +- Live video or voice sessions + +See `Future.md` for the full backlog. --- -## Current Status +## Project history & docs -This is a **private MVP / internal testing codebase**. - -### Complete -- Android MVP โ€” onboarding, auth (anonymous/email/Google), pairing, home, daily questions, question packs, answer history, spin wheel, date tools, settings, billing, notifications, Firebase integration, E2EE crypto layer -- iOS port scaffold โ€” all 49 screens mapped to SwiftUI views, Firebase/RevenueCat integration, full screen parity landed on `dev` branch -- Backend โ€” 17 Cloud Functions (callable, triggers, scheduled, webhook) covering full relationship lifecycle -- Seeded question content โ€” 6,000+ prompts across 22 categories - -### In Progress -- iOS Xcode project generation and build verification -- iOS E2EE layer (CryptoKit interop with Android Tink) โ€” pairing from iOS is currently blocked until E2EE keys are wired -- Final QA pass on both platforms - -### Release Prep -- `docs/release/internal-testing-checklist.md` -- `docs/release/store-assets.md` -- `docs/qa/private-mvp-checklist.md` -- `docs/qa/ui-review.md` - ---- - -## Project History - -See `HISTORY.md` for the full changelog and release notes. - -See `PROJECT.md` for detailed scope, feature matrix, and architectural decisions. +| Doc | Purpose | +| --- | --- | +| [`docs/Engineering_Reference_Manual.md`](docs/Engineering_Reference_Manual.md) | Architecture, security model, data model, known landmines | +| [`docs/release/`](docs/release/) | Release prep + store assets | +| [`docs/qa/`](docs/qa/) | QA playbook + private-MVP checklist | +| `Future.md` | Backlog + roadmap | +| `HISTORY.md` | Changelog + release notes | +| `PROJECT.md` | Scope, feature matrix, architectural decisions | ---