docs(manual): Batch 3 — correct E2EE and Firestore data model (user fields, couple fields, date plans, preferences, bucket list, secure subdoc)

This commit is contained in:
null 2026-06-28 11:06:20 -05:00
parent a6aa23eee2
commit 08368b3e01
2 changed files with 31 additions and 11 deletions

View File

@ -76,7 +76,7 @@ collection name, and architectural fact. Never assume.
| Batch | Status | Findings | Changes made |
|---|---|---|---|
| 1 | ✅ done | `core/feature/` note inaccurate (dir doesn't exist); `data/questions/` listed `QuestionDao` but it's in `data/local/` | Corrected `core/feature/` note; moved `QuestionDao` to `data/local/`; kept `QuestionJsonParser` in `data/questions/`; updated older-description note. |
| 2 | in progress | | |
| 2 | ✅ done | No anonymous sign-in or account linking in code; Android uses legacy Google Sign-In SDK (idToken), not Credential Manager; `encryptionMigrationUsers` field does not exist | Removed anonymous auth and account-linking claims; corrected Google Sign-In description; removed `encryptionMigrationUsers` from couples model and added note that `encryptionVersion` is always `2`. |
| 3 | todo | | |
| 4 | todo | | |
| 5 | todo | | |

View File

@ -532,13 +532,26 @@ Thread questions follow the same sealed flow but use a different path:
email: string
displayName: string
photoUrl: string | null
sex: string # 'male' | 'female' | '' (set during onboarding)
partnerId: string | null # deprecated mirror of the paired user's uid; prefer coupleId lookups
coupleId: string | null
hasPremium: bool # server-only write
plan: string # 'free' or premium product id; client-written, not server-authoritative
createdAt: Timestamp
lastActiveAt: Timestamp
fcmToken: string | null # legacy single-device FCM token; still written by older flows
platform: 'android' | 'ios' | null
/entitlements/premium # written by Cloud Functions only; readable by owner + current partner (see Couple-shared premium)
notifPartnerAnswered: bool
notifChatMessage: bool
quietHoursEnabled: bool
quietHoursStartMinutes: int
quietHoursEndMinutes: int
timezone: string
# NOTE: hasPremium is NOT a current root field. Premium state is stored in the
# server-only /entitlements/premium subdoc and observed by the client from there.
/entitlements/premium # written by Cloud Functions only; readable by owner + current partner
premium: bool
expiresAt: Timestamp | null
updatedAt: Timestamp # last entitlement change
updatedAt: Timestamp
productId: string | null # RevenueCat product identifier (e.g. 'closer_monthly')
eventType: string # RevenueCat event type ('INITIAL_PURCHASE', 'RENEWAL', 'CANCELLATION', etc.)
/fcmTokens/{tokenId} # owned by the user
@ -572,12 +585,12 @@ Thread questions follow the same sealed flow but use a different path:
id, userIds[2], inviteCode, createdAt
streakCount, lastAnsweredAt
currentQuestionId, activePackId
encryptionVersion, wrappedCoupleKey, kdfSalt, kdfParams
encryptionMigrationUsers: map<uid, bool>
encryptionVersion (always 2), wrappedCoupleKey, kdfSalt, kdfParams
/daily_question/{YYYY-MM-DD}
questionId, date, assignedAt, expiresAt
/answers/{userId} # schemaVersion 2 (enc:v1:) or 3 (sealed:v1:)
/releaseKeys/{recipientId} # keybox:v1:
/answers/{userId} # schemaVersion 2 metadata, or schemaVersion 3 sealed fields
/answers/{userId}/secure/{doc} # schemaVersion 2 encryptedPayload (read-gated)
/answers/{userId}/releaseKeys/{recipientId} # schemaVersion 3 keybox:v1:
/threads/{threadId}
questionId, createdAt, createdByUserId
/messages/{userId} # schemaVersion 3 sealed
@ -627,11 +640,18 @@ Thread questions follow the same sealed flow but use a different path:
/couples/{coupleId}/date_matches/{matchId}
userIds, dateIdeaId, createdAt
/couples/{coupleId}/date_plans/{planId}
title, dateTime, status: 'draft' | 'planned' | 'completed' # server-created by createDateMatch
dateIdeaId, scheduledDate, scheduledTime (encrypted), status: 'draft' | 'planned' | 'completed'
budget (encrypted), duration (encrypted)
activity (encrypted), food (encrypted)
conversationPrompts (encrypted), optionalChallenge (encrypted)
createdAt, updatedAt
/couples/{coupleId}/date_plan_preferences/{prefId}
categories: map<category, weight> # plaintext per-user weights
dateIdeaId, preferredDate, preferredTime (encrypted)
budget (encrypted), duration (encrypted)
createdAt, updatedAt
/couples/{coupleId}/bucket_list/{itemId}
title, category, addedByUserId, completedAt
title, description, category, addedBy, addedAt
completedBy, completedAt, isCompleted
# NOTE: this list is not exhaustive. Game-session subcollections (this_or_that, desire_sync,
# how_well, wheel) and challenge-day / memory-lane subcollections are documented in their own