RevenueCat isn't set up, so exporting revenueCatWebhook forced a Secret Manager entry:
defineSecret('REVENUECAT_SIGNING_KEY') runs at module load, and Firebase validates every
declared secret across the whole codebase at deploy time (even functions excluded via --only),
failing with "no latest version of the secret". Comment out the export so the file isn't loaded
during discovery — no secret, no validation. revenueCatWebhook.ts (already migrated to v2) is
untouched; re-enable by uncommenting the export, seeding the real key, and deploying it.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
||
|---|---|---|
| .github | ||
| .vscode | ||
| app | ||
| docs | ||
| firestore-tests | ||
| functions | ||
| gradle | ||
| iphone | ||
| qa | ||
| scripts | ||
| seed | ||
| server | ||
| .eslintrc.js | ||
| .firebaserc | ||
| .gitignore | ||
| .gitleaksignore | ||
| .prettierrc | ||
| Engineering_Reference_Manual_Plan.md | ||
| Future.md | ||
| IOS_E2EE_STATUS.md | ||
| README.md | ||
| UI_UPGRADE.md | ||
| build.gradle.kts | ||
| firebase.json | ||
| firestore.indexes.json | ||
| firestore.rules | ||
| gitleaks-current.json | ||
| gitleaks-history.json | ||
| gradle.properties | ||
| gradlew | ||
| gradlew.bat | ||
| local.properties.example | ||
| seed_generator.py | ||
| settings.gradle.kts | ||
| storage.rules | ||
README.md
Closer
A private space for two.
Daily questions, mutual reveals, shared games, and calm rituals for couples.
Screenshots · What makes it different · Status · Run locally · QA
Android reference app · iOS scaffold in progress · Firebase backend · RevenueCat billing · Private repo
Closer is a native relationship app for couples who want a quieter way to check in with each other. Each partner answers privately, reveals intentionally, and keeps a record of the conversations that matter.
It is not a social network, therapy replacement, or productivity tracker. There are no public feeds, likes, followers, or infinite-scroll loops. The product loop is small on purpose:
answer honestly -> reveal together -> keep the conversation going
Screenshots
Fresh Android dark-mode captures from the current emulator build.
| Ready | Your turn | Reveal ready |
![]() |
![]() |
![]() |
| Play | This or That | Today | Challenge |
![]() |
![]() |
![]() |
![]() |
What makes it different
| Private first Partners answer independently before seeing each other's response. |
Mutual reveal The app is built around intentional sharing, not performative posting. |
Couple-owned trust Android encrypts answers, chat, history, and media on-device with couple-owned keys. |
| One subscription per couple Premium unlocks for both partners through server-verified entitlements. |
Curated prompts Question packs are written and reviewed, not generated at answer time. |
Calm by design No feeds, likes, followers, public profiles, or pressure mechanics. |
Product surface
| Area | Free | Premium |
|---|---|---|
| Daily question | Included | Included |
| Private answers + mutual reveal | Included | Included |
| Curated question packs | Free + mixed access | Full access |
| Spin the Wheel | Included | Included |
| This or That / How Well Do You Know Me | Included | Included |
| Recent answer history | Included | Included |
| Memory Lane capsules | Limited | Full access |
| Desire Sync | Limited | Full access |
| Connection Challenges | Free + mixed access | Full access |
| Date ideas, matches, and bucket list | Included | Premium ideas gated |
| Push reminders + quiet hours | Included | Included |
| Account deletion | Included | Included |
Data export is not currently offered. The in-app privacy copy intentionally says so until a real export flow exists.
Platform status
| Platform | Status | Notes |
|---|---|---|
| Android | Reference implementation | Kotlin, Jetpack Compose, Material 3, Hilt, Room, DataStore, Firebase, Tink |
| iOS | Scaffold in progress | SwiftUI screen parity exists; pairing is blocked until E2EE interop is complete |
| Backend | Shared source of truth | Firebase Auth, Firestore, Cloud Functions, FCM, App Check |
| Billing | Server verified | RevenueCat webhook writes Firestore entitlements observed by the app |
Android is the product source of truth today. iOS has the app shell, Firebase/RevenueCat integration, and SwiftUI screens, but end-to-end pairing waits on CryptoKit/Tink interoperability.
Architecture
Android (Kotlin/Compose) iOS (SwiftUI)
Hilt · Room · DataStore MVVM · AppState · SPM
Tink AEAD · Argon2id CryptoKit interop in progress
\ /
\ /
v v
Firebase + RevenueCat
Auth · Firestore · Functions · FCM
App Check · server-verified billing
Core rules:
- Couple-scoped Firestore data. Users only read/write their own couple surface.
- Server-mediated pairing. Invite lifecycle runs through Cloud Functions.
- Local-first question content. Prompts ship with the app; assignment/sync uses Firebase.
- Server-verified premium. Clients observe entitlements; they do not self-grant access.
- No anonymous auth. Accounts use email/password or Google sign-in.
Security and privacy
- Android encrypts answer content, chat messages, media, capsules, and backed-up conversation history on-device before sync.
- The server stores ciphertext for private content and never receives plaintext answers.
- Recovery phrase wrapping uses Argon2id-derived keys.
- Partner-assisted restore lets a trusted partner help restore the couple key to a new device.
- Quiet hours are enforced server-side before push delivery.
- Account deletion exists; data export does not yet.
The canonical technical reference is docs/Engineering_Reference_Manual.md.
Local development
Prerequisites
- Android Studio, Android SDK, and JDK 17
- Node 20 for Firebase Functions tooling
- Firebase project with Auth, Firestore, Cloud Messaging, Crashlytics, Analytics, and App Check
app/google-services.json- RevenueCat project and Android API key
For iOS work:
- macOS, Xcode 16, XcodeGen, and iOS 17+
iphone/Closer/GoogleService-Info.plist- RevenueCat iOS API key
Android
cp local.properties.example local.properties
sdk.dir=/path/to/Android/Sdk
RC_API_KEY_ANDROID=your_revenuecat_android_key
RC_API_KEY_IOS=your_revenuecat_ios_key
./gradlew :app:assembleDebug
./gradlew :app:installDebug
./gradlew :app:testDebugUnitTest
iOS
cd iphone
xcodegen generate
xed Closer.xcodeproj
xcodebuild -project iphone/Closer.xcodeproj \
-scheme Closer \
-destination 'platform=iOS Simulator,name=iPhone 15' \
build
Firebase Functions
cd functions
npm install
npm run build
npm test
npm run serve
QA and release
The repo keeps repeatable QA checks close to the code:
| Check | Purpose |
|---|---|
scripts/theme-scan.sh |
Finds hardcoded theme/color regressions |
scripts/wiring-scan.sh |
Finds orphaned routes, dead actions, and silent wiring breaks |
| docs/qa/ | Manual QA playbooks and private MVP checklist |
| docs/release/ | Internal testing, store assets, and release prep |
Live emulator fixtures and related credentials are intentionally local-only. Keep them in gitignored
files under qa/, never in the tracked README or public docs.
Repository map
app/ Android app
iphone/ iOS app
functions/ Firebase Cloud Functions
server/ Optional Express service
seed/ Question content and seed tooling
scripts/ Static QA scanners
qa/ Local-only emulator fixture notes and smoke helpers
docs/ Architecture, release, screenshots, and QA docs
firestore.rules Firestore rules source of truth
Roadmap
In progress:
- iOS E2EE interoperability with Android's Tink key material
- More on-device/instrumented smoke coverage
- Activity
uiModesync for in-app theme and dark artwork - Internal-testing release configuration, legal/support URLs, and RevenueCat offering validation
Out of scope for now:
- AI-generated core questions
- Group relationship spaces beyond dyadic couples
- Wear OS / watchOS companions
- Live video or voice sessions
Project docs
| Doc | Purpose |
|---|---|
| docs/Engineering_Reference_Manual.md | Architecture, security model, data model, and known landmines |
| docs/brand/ | Visual identity, asset system, generated art |
| docs/release/ | Release prep and store assets |
| docs/qa/ | Manual QA checklist |
| Future.md | Backlog and roadmap |
| HISTORY.md | Changelog and release notes |
| PROJECT.md | Scope, feature matrix, architectural decisions |
License
Private project. All rights reserved.






