A private space for two.
Private daily questions, intentional reveals, shared games, and calm rituals for couples.
|
|
|
|
|
---
## Why Closer exists
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.
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.
---
## What Closer does
| Feature | Free | Premium |
| --- | --- | --- |
| Daily question (text / scale / multi / this-or-that) | โ
| โ
|
| 6,000+ prompts ยท 22 question packs | โ
(free) + ๐๏ธ (premium tiers) | โ
(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 | โ
| โ
|
One subscription unlocks premium for **both** partners โ `couples/{coupleId}/entitlements` is per-couple, not per-user.
---
## Platform status
| 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` |
> ๐ 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.
---
## 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 |
| Architecture | Clean architecture โ `core/` ยท `data/` ยท `domain/` ยท `ui/` |
| State | ViewModel ยท Kotlin Coroutines ยท Kotlin Flow |
| 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 ยท 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)
| Layer | Stack |
| --- | --- |
| 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, 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
```text
.
โโโ app/ # Native Android app (Kotlin)
โ โโโ src/main/java/app/closer
โ โโโ 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 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 + 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
### 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
- **Android config:** `app/google-services.json`
- **iOS config:** `iphone/Closer/GoogleService-Info.plist`
- **Billing:** RevenueCat project with Android + iOS API keys
- **Node 20** for Firebase Functions tooling
### Local config
```bash
# Android
cp local.properties.example local.properties
# iOS
cp iphone/Closer/GoogleService-Info.plist.example iphone/Closer/GoogleService-Info.plist
```
```properties
sdk.dir=/path/to/Android/Sdk
RC_API_KEY_ANDROID=your_revenuecat_android_key
RC_API_KEY_IOS=your_revenuecat_ios_key
```
### Android
```bash
./gradlew :app:assembleDebug
./gradlew :app:installDebug
```
```bash
./gradlew :app:compileDebugKotlin # fast verification
./gradlew :app:testDebugUnitTest # 205 unit tests
```
### iOS
```bash
cd iphone
xcodegen generate
xed Closer.xcodeproj
```
```bash
xcodebuild -project iphone/Closer.xcodeproj \
-scheme Closer \
-destination 'platform=iOS Simulator,name=iPhone 15' \
build
```
### Firebase Functions
```bash
cd functions
npm install
npm run build
npm run serve
```
```bash
npm test # 24 functions tests
```
### Optional server
```bash
cd server
npm install
npm run dev
```
---
## Security & privacy
- ๐ **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.
> 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.
---
## Roadmap
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.
---
## Project history & docs
| 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 |
---
## License
Private project. All rights reserved.