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:
parent
a6aa23eee2
commit
08368b3e01
|
|
@ -76,7 +76,7 @@ collection name, and architectural fact. Never assume.
|
||||||
| Batch | Status | Findings | Changes made |
|
| 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. |
|
| 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 | | |
|
| 3 | todo | | |
|
||||||
| 4 | todo | | |
|
| 4 | todo | | |
|
||||||
| 5 | todo | | |
|
| 5 | todo | | |
|
||||||
|
|
|
||||||
|
|
@ -532,13 +532,26 @@ Thread questions follow the same sealed flow but use a different path:
|
||||||
email: string
|
email: string
|
||||||
displayName: string
|
displayName: string
|
||||||
photoUrl: string | null
|
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
|
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
|
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
|
premium: bool
|
||||||
expiresAt: Timestamp | null
|
expiresAt: Timestamp | null
|
||||||
updatedAt: Timestamp # last entitlement change
|
updatedAt: Timestamp
|
||||||
productId: string | null # RevenueCat product identifier (e.g. 'closer_monthly')
|
productId: string | null # RevenueCat product identifier (e.g. 'closer_monthly')
|
||||||
eventType: string # RevenueCat event type ('INITIAL_PURCHASE', 'RENEWAL', 'CANCELLATION', etc.)
|
eventType: string # RevenueCat event type ('INITIAL_PURCHASE', 'RENEWAL', 'CANCELLATION', etc.)
|
||||||
/fcmTokens/{tokenId} # owned by the user
|
/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
|
id, userIds[2], inviteCode, createdAt
|
||||||
streakCount, lastAnsweredAt
|
streakCount, lastAnsweredAt
|
||||||
currentQuestionId, activePackId
|
currentQuestionId, activePackId
|
||||||
encryptionVersion, wrappedCoupleKey, kdfSalt, kdfParams
|
encryptionVersion (always 2), wrappedCoupleKey, kdfSalt, kdfParams
|
||||||
encryptionMigrationUsers: map<uid, bool>
|
|
||||||
/daily_question/{YYYY-MM-DD}
|
/daily_question/{YYYY-MM-DD}
|
||||||
questionId, date, assignedAt, expiresAt
|
questionId, date, assignedAt, expiresAt
|
||||||
/answers/{userId} # schemaVersion 2 (enc:v1:) or 3 (sealed:v1:)
|
/answers/{userId} # schemaVersion 2 metadata, or schemaVersion 3 sealed fields
|
||||||
/releaseKeys/{recipientId} # keybox:v1:
|
/answers/{userId}/secure/{doc} # schemaVersion 2 encryptedPayload (read-gated)
|
||||||
|
/answers/{userId}/releaseKeys/{recipientId} # schemaVersion 3 keybox:v1:
|
||||||
/threads/{threadId}
|
/threads/{threadId}
|
||||||
questionId, createdAt, createdByUserId
|
questionId, createdAt, createdByUserId
|
||||||
/messages/{userId} # schemaVersion 3 sealed
|
/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}
|
/couples/{coupleId}/date_matches/{matchId}
|
||||||
userIds, dateIdeaId, createdAt
|
userIds, dateIdeaId, createdAt
|
||||||
/couples/{coupleId}/date_plans/{planId}
|
/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}
|
/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}
|
/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,
|
# 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
|
# how_well, wheel) and challenge-day / memory-lane subcollections are documented in their own
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue