314 lines
12 KiB
Markdown
314 lines
12 KiB
Markdown
<p align="center">
|
|
<img src="docs/store/feature-graphic-1024x500.png" alt="Closer feature graphic" width="920" />
|
|
</p>
|
|
|
|
<h1 align="center">Closer</h1>
|
|
|
|
<p align="center">
|
|
<strong>A private space for two.</strong><br />
|
|
Daily questions, mutual reveals, shared games, and calm rituals for couples.
|
|
</p>
|
|
|
|
<p align="center">
|
|
<a href="#screenshots">Screenshots</a>
|
|
·
|
|
<a href="#what-makes-it-different">What makes it different</a>
|
|
·
|
|
<a href="#platform-status">Status</a>
|
|
·
|
|
<a href="#local-development">Run locally</a>
|
|
·
|
|
<a href="#qa-and-release">QA</a>
|
|
</p>
|
|
|
|
<p align="center">
|
|
<sub>
|
|
Android reference app · iOS scaffold in progress · Firebase backend · RevenueCat billing · Private repo
|
|
</sub>
|
|
</p>
|
|
|
|
<p align="center">
|
|
<img alt="Android" src="https://img.shields.io/badge/Android-reference%20app-3DDC84?style=flat-square&logo=android&logoColor=white" />
|
|
<img alt="iOS" src="https://img.shields.io/badge/iOS-scaffold%20in%20progress-007AFF?style=flat-square&logo=apple&logoColor=white" />
|
|
<img alt="Firebase" src="https://img.shields.io/badge/Firebase-backend-FFCA28?style=flat-square&logo=firebase&logoColor=black" />
|
|
<img alt="RevenueCat" src="https://img.shields.io/badge/RevenueCat-billing-6D5DF6?style=flat-square" />
|
|
<img alt="License" src="https://img.shields.io/badge/license-private-E5484D?style=flat-square" />
|
|
</p>
|
|
|
|
---
|
|
|
|
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:
|
|
|
|
```text
|
|
answer honestly -> reveal together -> keep the conversation going
|
|
```
|
|
|
|
<h2 align="center">Screenshots</h2>
|
|
|
|
Fresh Android captures from the current emulator build.
|
|
|
|
<table>
|
|
<tr>
|
|
<td align="center"><strong>Ready · dark</strong></td>
|
|
<td align="center"><strong>Ready · light</strong></td>
|
|
<td align="center"><strong>Your turn · dark</strong></td>
|
|
<td align="center"><strong>Reveal · dark</strong></td>
|
|
<td align="center"><strong>Reveal · light</strong></td>
|
|
</tr>
|
|
<tr>
|
|
<td><img src="docs/screenshots/readme/home-daily-ready-dark.png" alt="Home screen with the daily question ready to answer in dark mode" width="160" /></td>
|
|
<td><img src="docs/screenshots/readme/home-daily-ready-light.png" alt="Home screen with the daily question ready to answer in light mode" width="160" /></td>
|
|
<td><img src="docs/screenshots/readme/home-daily-your-turn-dark.png" alt="Home screen after the partner answered and it is your turn in dark mode" width="160" /></td>
|
|
<td><img src="docs/screenshots/readme/home-daily-reveal-dark.png" alt="Home screen with the daily question reveal ready in dark mode" width="160" /></td>
|
|
<td><img src="docs/screenshots/readme/home-daily-reveal-light.png" alt="Home screen with the daily question reveal ready in light mode" width="160" /></td>
|
|
</tr>
|
|
</table>
|
|
|
|
<table>
|
|
<tr>
|
|
<td align="center"><strong>Play</strong></td>
|
|
<td align="center"><strong>This or That</strong></td>
|
|
<td align="center"><strong>Today</strong></td>
|
|
<td align="center"><strong>Challenge</strong></td>
|
|
</tr>
|
|
<tr>
|
|
<td><img src="docs/screenshots/readme/play-dark.png" alt="Play hub in dark mode" width="170" /></td>
|
|
<td><img src="docs/screenshots/readme/this-or-that-dark.png" alt="This or That in dark mode" width="170" /></td>
|
|
<td><img src="docs/screenshots/readme/today-dark.png" alt="Daily question in dark mode" width="170" /></td>
|
|
<td><img src="docs/screenshots/readme/challenge-dark.png" alt="Connection challenge in dark mode" width="170" /></td>
|
|
</tr>
|
|
</table>
|
|
|
|
<h2 align="center">Visual system</h2>
|
|
|
|
Closer uses paired light and dark illustration assets so the product can stay warm without fighting
|
|
the selected theme. The home daily-question card is intentionally people-forward: the ready-to-answer
|
|
and reveal-ready moments use the couple/cards artwork, while quieter waiting states can fall back to
|
|
the answer-card ritual art.
|
|
|
|
<h2 align="center">What makes it different</h2>
|
|
|
|
<table>
|
|
<tr>
|
|
<td width="33%"><strong>Private first</strong><br />Partners answer independently before seeing each other's response.</td>
|
|
<td width="33%"><strong>Mutual reveal</strong><br />The app is built around intentional sharing, not performative posting.</td>
|
|
<td width="33%"><strong>Couple-owned trust</strong><br />Android encrypts answers, chat, history, and media on-device with couple-owned keys.</td>
|
|
</tr>
|
|
<tr>
|
|
<td width="33%"><strong>One subscription per couple</strong><br />Premium unlocks for both partners through server-verified entitlements.</td>
|
|
<td width="33%"><strong>Curated prompts</strong><br />Question packs are written and reviewed, not generated at answer time.</td>
|
|
<td width="33%"><strong>Calm by design</strong><br />No feeds, likes, followers, public profiles, or pressure mechanics.</td>
|
|
</tr>
|
|
<tr>
|
|
<td width="33%"><strong>Theme-aware art</strong><br />Light and dark illustrations are paired for the same product moments.</td>
|
|
<td width="33%"><strong>Human warmth</strong><br />The home ritual leads with people and recognizable Closer card artwork.</td>
|
|
<td width="33%"><strong>Private rituals</strong><br />The visual language reinforces answering alone, revealing together, and continuing gently.</td>
|
|
</tr>
|
|
</table>
|
|
|
|
<h2 align="center">Product surface</h2>
|
|
|
|
| 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.
|
|
|
|
<h2 align="center">Platform status</h2>
|
|
|
|
| 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.
|
|
|
|
<h2 align="center">Architecture</h2>
|
|
|
|
```text
|
|
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.
|
|
|
|
<h2 align="center">Security and privacy</h2>
|
|
|
|
- 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](docs/Engineering_Reference_Manual.md).
|
|
|
|
<h2 align="center">Local development</h2>
|
|
|
|
<details open>
|
|
<summary><strong>Prerequisites</strong></summary>
|
|
|
|
- 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
|
|
|
|
</details>
|
|
|
|
<details>
|
|
<summary><strong>Android</strong></summary>
|
|
|
|
```bash
|
|
cp local.properties.example local.properties
|
|
```
|
|
|
|
```properties
|
|
sdk.dir=/path/to/Android/Sdk
|
|
RC_API_KEY_ANDROID=your_revenuecat_android_key
|
|
RC_API_KEY_IOS=your_revenuecat_ios_key
|
|
```
|
|
|
|
```bash
|
|
./gradlew :app:assembleDebug
|
|
./gradlew :app:installDebug
|
|
./gradlew :app:testDebugUnitTest
|
|
```
|
|
|
|
</details>
|
|
|
|
<details>
|
|
<summary><strong>iOS</strong></summary>
|
|
|
|
```bash
|
|
cd iphone
|
|
xcodegen generate
|
|
xed Closer.xcodeproj
|
|
```
|
|
|
|
```bash
|
|
xcodebuild -project iphone/Closer.xcodeproj \
|
|
-scheme Closer \
|
|
-destination 'platform=iOS Simulator,name=iPhone 15' \
|
|
build
|
|
```
|
|
|
|
</details>
|
|
|
|
<details>
|
|
<summary><strong>Firebase Functions</strong></summary>
|
|
|
|
```bash
|
|
cd functions
|
|
npm install
|
|
npm run build
|
|
npm test
|
|
```
|
|
|
|
```bash
|
|
npm run serve
|
|
```
|
|
|
|
</details>
|
|
|
|
<h2 align="center">QA and release</h2>
|
|
|
|
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/](docs/qa/) | Manual QA playbooks and private MVP checklist |
|
|
| [docs/release/](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.
|
|
|
|
<h2 align="center">Repository map</h2>
|
|
|
|
```text
|
|
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
|
|
```
|
|
|
|
<h2 align="center">Roadmap</h2>
|
|
|
|
In progress:
|
|
|
|
- iOS E2EE interoperability with Android's Tink key material
|
|
- More on-device/instrumented smoke coverage
|
|
- Activity `uiMode` sync 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
|
|
|
|
<h2 align="center">Project docs</h2>
|
|
|
|
| Doc | Purpose |
|
|
| --- | --- |
|
|
| [docs/Engineering_Reference_Manual.md](docs/Engineering_Reference_Manual.md) | Architecture, security model, data model, and known landmines |
|
|
| [docs/brand/](docs/brand/) | Visual identity, asset system, generated art |
|
|
| [docs/release/](docs/release/) | Release prep and store assets |
|
|
| [docs/qa/](docs/qa/) | Manual QA checklist |
|
|
| [Future.md](Future.md) | Backlog and roadmap |
|
|
| [HISTORY.md](HISTORY.md) | Changelog and release notes |
|
|
| [PROJECT.md](PROJECT.md) | Scope, feature matrix, architectural decisions |
|
|
|
|
<h2 align="center">License</h2>
|
|
|
|
Private project. All rights reserved.
|