# Data retention and erasure schedule > **Current launch policy — 2026-09-06:** KarmicCompass is for adults aged 18 and over in every launch country. This supersedes prior teen/parental-admission instructions. App-wide access requires adult eligibility; no parental approval exception is offered. Date of birth is self-declared and server-checked, not independently verified. Historical records remain subject to existing retention/withdrawal/erasure rules. Apple: truthful questionnaire plus minimum-age override to 18+ (regional/older-OS mapping may differ). Google Play: only **18 and over**, with **Restrict minor access** enabled. Store settings do not replace in-app/backend enforcement. Privacy v3.5 / Terms v2.8 are verified on the public website. Production app/backend rollout remains separate and pending. **Version:** v3.5 — 2026-09-07 (plain-language policy update; controls unchanged; aligned to Privacy Policy v3.5, 2026-09-07) **Aligned with:** Privacy Policy v3.5 **Owner:** privacy lead **Status:** source behavior documented; production deployment, TTLs, provider retention and backup aging require live evidence before launch. Retention begins from the event stated below. “Deleted with account” means the durable erasure workflow attempts the category. Ordinary retryable `erasureJobs/{uid}` records remain until Apple authorization (when applicable), Auth, Firestore, Sentry and RevenueCat stages complete. The locked-out legacy `manual_required` exception is terminal after supported erasure, never marks Apple complete, and retains its UID/outcome job for 30 days before explicit sweep deletion. `Delete now` skips the grace period but does not synchronously erase data: it durably queues a lock-required job, makes one bounded access-lock attempt, signs the client out, and leaves the worker to retry the lock first and then process destructive stages, ordinarily on its next hourly run. This is not a promise that active data or every backup/provider copy disappears instantly. ## Primary user data | Store/category | Data | Retention target | Source mechanism | Live evidence gate | |---|---|---|---|---| | Firebase Auth | Email, identity providers, account metadata | Account life. `Delete now` skips the seven-day grace, durably queues an irreversible lock-required job and makes one bounded access-lock attempt; if that attempt is incomplete, the worker retries access disabling first, then ordinarily hard-deletes on its next hourly run. Grace-period requests hard-delete after seven days. | Durable lock/erasure Auth stages; client signs out on accepted enqueue | 202/pending-lock, disposable-account and orphan-replay canaries | | `users/{uid}` and `state/*` | Profile, consent, settings, journal window, chat, badges, scores, memories, commitments, insights | Account life; deleted with account. Incomplete sign-up (name stored, no dob, onboarding not completed): erased by the nightly `sweepUnverifiedAccounts` second phase once older than 24 hours — dated from `createdAt` or, since the first write carries none, the Auth record's creation time — unless a deletion is already pending or an entitlement field is present; dry-run until `SWEEP_ABANDONED_SIGNUPS_APPLY=true` | `deleteUserFootprint`; `runAbandonedSignupSweep` → `eraseAccountDurably` (reason `abandoned_signup_no_dob`) | Full/partial failure canary | | `entries`, `entriesArchive`, `chatArchive`, `insights`, `reports` | Long-term journal/chat/report data | Account life; deleted with account | Chunked `deleteUserFootprint` operations | Ten-year account and replay test | | Journal photos in Firebase Storage | User-selected journal images | Until the corresponding entry or account is deleted, whichever comes first | Per-entry deletion, bulk erasure; orphan/quota sweep is a backstop | Entry-delete, token rotation, orphan and erasure test | | Chat photos | Inline image sent for one AI response | Transient at app/backend/provider; not stored by KarmicCompass | Client re-encoding + AI request | Provider terms and malformed-media canary | | Voice recordings | Raw voice-to-text source `.m4a` | Ordinary queue capped at 10 items and 7 days; delete after successful transcription or expiry. If OS removal is unconfirmed, a content-free cleanup tombstone remains until verified absence or App-storage removal | Sandboxed app-private files; not separately encrypted by KarmicCompass; immutable deletion tombstone and retry | Background/kill/offline/deletion/device-backup and failed-OS-delete tests | | Recovered transcript drafts | Transcribed text awaiting exact-once insertion into its intended journal/Mentor draft | Up to 90 days; remove after successful handoff, discard, applicable clear, or account deletion | App-private recovery queue with deterministic insertion ID | Kill/relaunch/cross-device/exact-once and expiry tests | | Journal outbox/quarantine | Pending or failed journal-analysis payload and delivery metadata | Only while needed for retry/safe failure handling; payload follows entry/account controls | Durable local outbox, bounded quarantine, deterministic receipt | Offline/relaunch/replay/quarantine/account-clear tests | | Mentor outbox/quarantine | Pending or failed Mentor-turn payload and delivery metadata | Only while needed for retry/safe failure handling; Clear Chat removes message/archive/current-device retry content; account deletion removes all | Durable local outbox, bounded quarantine, server chat-erasure epoch | Offline/relaunch/cross-device stale-outbox/Clear Chat/account-clear tests | | `journalReceipts` | Content-free deterministic replay-prevention ID/status and irreversible deletion marker | Remains after individual entry deletion to reject delayed scoring and retries; deleted with account | Every journal insert/scoring transaction checks the marker | Replay-after-entry-delete and account-erasure tests | | `mentorTurnReceipts` and chat-erasure epoch | Content-free replay-prevention ID/status and server epoch | Retained through Clear Chat solely to reject stale cross-device outboxes; deleted with account | Server receipt/epoch gate; Clear Chat increments epoch | Clear Chat, offline second device, replay rejection and account-erasure tests | | Mentor 1–5 check-in | One score used to adjust the current Arya prompt | Current app session only; not written to Firestore or the persisted `moodHistory` field | React state cleared with the session/process | Prompt and persistence tests | | Personalised Daily Horoscope | Generated reading and affirmation, keyed by UID/day/sign and a one-way fingerprint of the locally derived theme vector | Until local day rollover; cleared on logout and account deletion | Encrypted MMKV only; process-memory-only fallback when encrypted storage is unavailable; no ordinary AsyncStorage persistence. Wipe increments a generation epoch, clears session/in-flight maps and rejects a late write that completes after logout. | Secure-store unavailable, sign-out, account deletion and deferred in-flight response tests | | `crisisFlags` | Up to first 100 chars of the exact triggering text, provenance and server timestamp | 90 days; also deleted with account or by user control; latest 500 maximum | Authenticated server append assigns identifier/timestamp, prunes by server-owned time and caps the array; daily backstop prune | Endpoint/rules, source-provenance and schedule canaries | | `feedback` | Message, rating, email/UID metadata | 365 days; also deleted with account | Scheduled `pruneUserReports` age sweep | Verify scheduler and query index | | `chatReports` | Reason and bounded locally scrubbed excerpt | 365 days; also deleted with account | Scheduled `pruneUserReports` age sweep | Verify scheduler and report deletion | | Support email | Sender/contact and correspondence | 365 days after resolution unless a longer period is necessary for a specific legal claim | Monitored mailbox/operator process | Determine consumer Gmail vs Workspace; retain applicable terms and actual retention evidence | | Admin `supportInbox` | Contact/account reference, subject, correspondence, status and resolution metadata entered into the operator support system | Open while needed to resolve; 365 days after `resolvedAt`, unless a documented legal hold applies | Resolution stamps `resolvedAt`/`expireAt`; reopening clears them; daily retention sweep and fail-closed read of expired resolved rows | Resolution/reopen/write/read/sweep tests; Firestore TTL evidence | | Admin `supportNotes` | Internal note, operator identity and creation time | 365 days after creation | `expireAt` on write; daily retention sweep and fail-closed read | Write/read/sweep tests; Firestore TTL evidence | | Separate operator-system account records | Support/admin records linked by Firebase UID, `userId` or authentication subject | Applicable category period above; participates in account access/erasure | Authenticated subject lookup plus shared erasure-job/tombstone barrier | Cross-project DSAR, erasure, stale-write and completion-barrier tests | | Email-only legacy support rows | Legacy correspondence with no UID/userId/subject link | Applicable support/legal-hold period; no automatic account match | Verified operator review before disclosure or erasure | Identity/linkage review evidence and audit log | ## Consent, security and operational records | Store/category | Retention target | Mechanism / erasure behavior | |---|---|---| | `deletionRequests/{uid}` | Deletion OTP valid 15 minutes; request/throttle document up to 7 days | Stores the same app-scoped device binding family used for trial enforcement to bind confirmation to the requesting app instance; code rejects expired OTP, `pruneUserReports` sweeps `expireAt`, and the record is deleted with account | | Soft-deletion cancellation credential (HMAC derived from UID + deletion timestamp; not stored as a separate token document) | Up to the seven-day deletion grace; invalid after cancellation, completion or a new deletion cycle | New links use URL fragments; old query links remain a temporary compatibility/log-purge gate | | `appleAuthTokens/{uid}` | While Sign in with Apple remains linked; removed after Apple accepts deletion-time revocation | Server-only/client-denied. Contains one current UID-bound AES-256-GCM encrypted Apple refresh credential, the Apple client identifier and registration timestamps; each later successful exchange atomically replaces the prior credential, and no raw authorization code or readable token is persisted. Apple states that revoking one valid refresh/access token invalidates the associated user authorization. | | `appleAuthorizationCleanup/{randomId}` | Deleted immediately after accepted Apple revocation; otherwise at most 7 days | Rare server-only/client-denied queue created only when a newly issued post-exchange Apple credential cannot be safely attached (including a guarded collision, verification/persistence failure or completed-erasure race) and immediate revocation fails. A pre-authentication collision is sent immediately to a UID-free endpoint that hard-requires valid App Check and the one-way network/app-scoped-device/global limits below; it exchanges then revokes the code, or queues only the issued encrypted credential if immediate revoke fails. The queue contains no UID, Apple subject, raw authorization code or readable token; it contains an AES-256-GCM credential ciphertext envelope bound to the random document ID, public client ID, timestamps, attempt count, bounded safe error code, `nextAttemptAt` and `expireAt`. The hourly sweep retries revoke. At expiry it purges ciphertext and raises an expired-unrevoked alert/count without claiming revocation. It cannot be matched to a per-user DSAR; the separate raw-UID Apple registration throttle remains exported while present. | | `erasureJobs/{uid}` | Ordinary retryable job: until all applicable access-lock (for `Delete now`), Apple authorization, Auth, Firestore, Sentry and RevenueCat stages finish. Locked-out legacy no-token job: 30 days after terminal `manual_required`. | Created before destructive work. `Delete now` returns 202 after durable irreversible enqueue and one bounded lock attempt; the client signs out, and the worker retries disabling access first before Apple → Auth → Firestore/Storage → Sentry → RevenueCat. Grace-period jobs begin at Apple. No TTL may discard unfinished retryable work. If no revocable legacy token exists, supported erasure completes, Apple stays false, the job becomes terminal `manual_required`, receives a 30-day expiry, is excluded from further provider retries and is then deleted by an explicit sweep. It never records Apple as revoked. | | `erasureTombstones/{uid}` | `expireAt` is set 2 hours after successful erasure; the explicit hourly sweep normally removes it on its next run, so ordinary retention may be up to approximately 3 hours. **Exception — active store subscription at erasure:** if the account's store entitlement was `active` or in `billing_grace` when erasure began (store truth only: RevenueCat-written `_entitlementState` `active`/`billing_grace`, or `subscriptionPlan` `basic_active` whose `entitlementSource` is `revenuecat` — trial, sandbox and flag grants, and operator comp grants written by kc-admin with `entitlementSource: admin_override`, do not qualify), the record carries a third field `billingGuard: true` and `expireAt` is sized to the store's own paid-through date: `min(400 days, max(2 hours, max(premiumExpiresAt − erasure time, 0) + 31 days + 30 days))`, i.e. measured from the later of erasure time and the paid-through date: the first renewal that can still arrive (or has just arrived, if erasure followed the 7-day undo window during which store events are not applied), one further renewal cycle, and the store billing-retry/RevenueCat redelivery allowance. Where the profile records no parseable paid-through date the full 400 days applies. A monthly subscriber erased the day after renewing is therefore held roughly 3 months; only an unknown or far-future expiry reaches 400 days | Server-only and client-denied. Contains only `completedAt` and `expireAt` timestamps, plus the `billingGuard` boolean in the exception case — no content, e-mail, device, IP, plan or price. Created in the same transaction that deletes `erasureJobs/{uid}` and `sentryMappings/{uid}`; retained to prevent stale authenticated requests from recreating telemetry identifiers and, in the billing-guard case, so that a RevenueCat renewal for a store subscription the user never cancelled is acknowledged terminally with an operator alert instead of being retried indefinitely against the erased account. The requirement and the paid-through instant (`billingGuardRequired`, `billingGuardStoreExpiryMs`) are recorded on `erasureJobs/{uid}` before the profile is deleted and travel with it into the DSAR export. Not analytics and not dependent on Firestore TTL | | Local filesystem deletion tombstones and encrypted path ledger | Until each raw-audio, legacy or formerly shared path is verified absent, or App storage is removed | Tombstones are immutable and content-free. Exact cleanup paths are encrypted locally and excluded from diagnostics; neither record is a content archive. | | `sentryMappings/{uid}` | Account life, up to 25 derived 16-hex Sentry IDs | Server-only; removed after mapped Sentry purge succeeds. It must remain if purge is incomplete. | | `deletionResidue/{uid}` | Up to 30 days while replaying partial footprint/provider cleanup | Retry worker plus `pruneUserReports` on `expireAt`; it cannot replace the durable unfinished `erasureJobs` record. Every writer merges field-by-field: a footprint re-run unions `failedOps`/`failures` and leaves `attempts` and the `externalPurge` Sentry-pseudonym recovery copy untouched | | `userRevocations/{uid}`, `adminRevocations/{uid}` | Operator-system containment markers written by kc-admin when an operator disables an account or revokes an admin claim; retained after account erasure under kc-admin's documented security-containment exception, with no `expireAt` and no sweep in this repository | Server-only and client-denied (rules consult `exists()` only). On kc-mobile account erasure `footprintOps` privacy-minimises each existing marker with a non-merge write to `revoked` (if set), `revokedAtMs`, `accountErased: true` and `minimisedAt`, removing the redundant `uid` field, operator `reason`, any legacy `email` copy and `updatedAt`; a missing marker is never created. Exported in a DSAR while present. Deleting outright would diverge from kc-admin's erasure inventory, which keeps them | | `userErasureJobs/{uid}` | kc-admin operator-initiated erasure retry/audit journal; kc-admin owns its expiry (reported as 365 days from last write; **UNVERIFIED** from this repository — no TTL policy or sweep for it exists here, so Privacy §11.13 states the criterion, not the figure) | Status/evidence fields only (no content). Not deleted or modified by kc-mobile's footprint: it is the foreign pipeline's live retry handle and stripping it mid-flight would strand that erasure. Exported in a DSAR as a projection of the documented lifecycle fields while present; deletion is kc-admin's TTL/sweep responsibility | | `parentalConsentSends/{uid}` / `users/{uid}.parentalConsent.*` consent receipt fields | Send counter: 60 days from the last send, or account deletion, whichever is first. Consent receipt fields: as required for the active account and consent evidence | The send-counter document id is the raw UID of a self-declared minor and `parentalConsent.parentEmail` is a third party's address, so neither may rest on a Console TTL policy: `pruneUserReports` sweeps `parentalConsentSends` on `expireAt` and `footprintOps` deletes it with the account, while the receipt fields go with `users/{uid}`. Both are declared in Privacy §1.14. Deleted with account unless counsel requires a separately disclosed legal-claims record; no such archive is assumed | | `aiUsage`, `aiUsageJournal`, `aiUsageMeter`, `ttsUsage` | Rolling daily counters; deleted with account | In-document pruning and footprint deletion | | `rateLimits/{uid}`, `identity_{uid}`, `export_{uid}`, `appleToken_{uid}` | Short security windows; deleted with account | `appleToken_{uid}` is a metadata-only raw-UID counter capped at 6 attempts/hour and stamped to expire after approximately 2 hours; it is included in a machine-readable DSAR while present. The per-endpoint buckets sharing `rateLimits/{uid}` (consent receipt, account access status, crisis flag, journal photo download/deletion, safety reports) stamp a timestamp `expireAt` at window end plus 1 hour on every write and never shorten a longer live expiry already on the shared document. Safety reports allow 20 submissions per account per 24-hour window and 1,000 service-wide per UTC day; numeric counters retain no report text. Other shared-bucket writers preserve the longest live expiry. All four join footprint deletion. Main `rateLimits` is **NOT in** the server-side expiry sweep; verify the Console TTL policy on `rateLimits.expireAt` | | `rateLimitsByIp/{hash or service-window}` | Approximately 2 hours | Server-stamped `expireAt` plus the hourly `rateLimitPrune` branch of `sweepErasureOutbox` and the nightly `pruneUserReports` sweep, so a record outlives its stamp by at most one hour. Ordinary IP-throttle records contain only a one-way truncated hash; `appleTokenGlobal_{hour}` contains no UID, IP/hash, code or token and caps service-wide Apple registrations at 1,000/hour. Pre-auth Apple cleanup adds `appleCleanupIp_{networkHmac}` and the historically named `appleCleanupInstall_{deviceBindingHmac}` keyed one-way bindings capped at 3 attempts each/hour plus UID/IP-free `appleCleanupGlobal_{hour}` capped at 1,000/hour. The latter reuses the app-scoped identifier already used for trial and OTP binding. Cleanup counters store purpose/window/count/expiry only — no raw network address, raw app-scoped device binding, UID, Apple subject, code or token. the two endpoints gated by a static shared secret (RevenueCat webhook, orphan-cleanup tool) count rejected credentials per hashed network address in the same collection under `revenuecat_auth_{ipHash}` and `admin_secret_{ipHash}` (`functions/staticSecretGuard.js`), capped at 10 failures per hour, stamped `expireAt` at two windows and swept by the hourly `rateLimitPrune` branch of `sweepErasureOutbox` as well as the nightly `pruneUserReports`. These counters store window start, count, last-seen time and expiry only — no secret, UID or raw address. Hashing (re-audit R2-14 / R3-5): the deletion-code request, deletion-confirmation, RevenueCat-webhook and orphan-cleanup buckets key the truncated SHA-256 as an HMAC with the endpoint's own secret, so the stored id is a keyed one-way binding; the e-mail-domain check binds no secret and uses a plain truncated SHA-256 bucket key. No raw address or key is stored with any counter | | `users/{uid}/journalImageReservations/{id}`, `users/{uid}/journalImageQuota/state`, `journalPhotoDeletionJobs/{sha256(uid,entryId)}` | Reservation: until the upload settles or the reservation expires; quota state: account life; deletion marker: until the 15-minute `sweepJournalPhotoDeletionJobs` run that completes the Storage delete removes it | UID-keyed, content-free (reservation id/state, running photo count, UID + entry id). All three join footprint deletion; reservations are additionally cleared by the reservation sweep. Disclosed in Privacy §1.13.2 and §9.11 | | `deviceIdsByUid/{uid}` | 3 days or account deletion | `pruneUserReports` on `expireAt` plus `deleteByUid` | | `aiUsageInflight/{requestId}` | Cleared when settled; abandoned marker up to 7 days | `pruneUserReports` on `expireAt` | | `processedWebhookEvents/{eventId}` | 30 days | `pruneUserReports` on `expireAt`; may carry app user ID during the window | | `users/{uid}._revenueCatReconcilePending` and `_revenueCatReconcileRequestedAt` | Temporary while authoritative RevenueCat state must be reconciled after reversible deletion grace | Stored only on an existing profile when an active event, refund/expiration, other revocation, or deferred TRANSFER cannot safely mutate a grace-frozen profile. A deferred TRANSFER marks every existing non-erasing concrete participant if any participant is in grace. No new collection is created. The fields are erased with the profile if deletion completes; either cancellation route immediately GETs current subscriber state and clears them after applying it; provider outage leaves them for the existing hourly erasure worker to retry until successful. | | Global/feature AI fuses and UID-free ops metrics | Short operational window defined by job/config | No journal/chat bodies; verify TTL/sweep and log field allowlist live | | Cloud Logging | Target 30 days | Google Cloud log retention; verify actual sink/bucket setting and redaction | ## Trial-abuse records retained after account deletion | Store | Data | Retention | Rationale and deletion behavior | |---|---|---|---| | On-device app-scoped device binding | Normal iOS random app ID in Keychain; normal Android one-way app-scoped SSAID derivation; random ordinary-app-storage fallback if platform/secure storage fails | iOS Keychain may survive reinstall until cleared/reset; Android derivation remains stable until the scoped SSAID changes (for example factory reset, signing/user-profile change); random app-storage fallback lasts with that app storage/backup; last-resort session ID lasts only for the process | One native-safe identifier family supports trial enforcement, the one-way AI-abuse hash, deletion-OTP same-App binding and the short-lived keyed one-way Apple-cleanup limiter. No advertising/tracking use. Account erasure does not remotely clear device-local or hardware-derived state. | | `deviceTrials/{deviceId}` | Stable pseudonymous trial marker | Indefinite unless policy changes | UID is removed and `accountClosed` recorded; marker remains so account deletion cannot reset the one-trial rule. Registration is server-controlled and limited to 128 distinct metadata records per account, including legacy records. The server-only numeric `users/{uid}.deviceTrialRegistrationCount` persists with the account, is included in the profile export, and is erased with the profile | | `deviceTrialLedger/{deviceHash}` | Server-side pseudonymous trial marker | Indefinite unless policy changes | UID becomes `__deleted__`; marker remains for the same fraud-prevention purpose | | `aiUsageByDevice/{deviceHash}` | Bounded device-level usage counters | Day keys are pruned; document expires 60 days after last write | Cannot be reverse-looked up by UID; `pruneUserReports` sweeps `expireAt` | These are not advertising identifiers and are not used for cross-app tracking. The indefinite marker is an explicit legitimate-interest/security exception, not a claim that “all data” disappears immediately. ## Third parties and backups | System | Data | Retention statement for public documents | Required evidence | |---|---|---|---| | Vertex AI | Prompts/responses for inference; no KarmicCompass proxy persistence | Google does not train/fine-tune on Customer Data without prior permission/instruction. Retention is separate: temporary project-isolated service caching and abuse-monitoring logging may apply under governing terms/settings. Exact maximum per verified contract, exception status and production configuration; no zero-retention claim without that evidence. | Paid project, DPA, cache configuration, request/response logging configuration, abuse-monitoring exception status and current service terms | | Gemini Developer API | Retired; no current processing | Not applicable to current source; no API-key secret is bound | Verify absence in the deployed Function and secret bindings | | Sentry | App: opt-in scrubbed diagnostics and mapped pseudonymous IDs. Website: opt-in browser reports only; server reporting is source-disabled because browser consent is unavailable to the server | Applicable plan/data-type retention remains to be verified; unsupported per-user erasure/export is manual-required, with job and mapping retained until confirmed completion | App/browser consent tests, source-disabled website server test, retention setting and disposable-user DSAR test | | RevenueCat | App User ID, receipts/entitlement state | Per RevenueCat/store/legal requirements; erasure retried until success subject to provider/legal limits | DPA, retention and delete-endpoint canary | | Expo/APNs/FCM | Push token and delivery metadata | Per provider terms; linked token removed from account on erasure | Provider terms and token revocation test | | Google email service (consumer Gmail vs Workspace to verify) | Deletion/consent/support messages | Per actual service terms and configured operator rule | Service identity/terms, retention rule and delivery/bounce evidence; do not assume a Workspace DPA | | Sign in with Apple | One current encrypted refresh credential and authorization state; in a rare post-exchange attach/revoke failure, one UID/subject-free encrypted cleanup item | Account life for the UID-bound current credential; a later successful exchange atomically replaces it. One valid credential is programmatically revoked before local hard deletion when available, invalidating the associated authorization; the token record is then deleted. A cleanup item is deleted on accepted revocation or has its ciphertext purged at 7 days with an alert and no revocation claim. A pre-authentication collision goes immediately to a UID-free endpoint that hard-requires App Check and one-way network/app-scoped-device/global limits; it revokes immediately or transfers only the encrypted issued credential to the queue. | All five Apple identifiers/secrets; 65 replacements retain `credentialCount = 1`; one deletion-revoke call; pre-auth and authenticated collision/verification/persistence/erasure-race cleanup canaries; hard-App-Check and 3/network/hour, 3/device-binding/hour, 1,000/global/hour canaries; cleanup immediate-success/hourly-retry/7-day-expiry canaries; legacy terminal-manual evidence | | Apple/Google billing | Store transaction/tax records | Per independent-controller and legal obligations | Store agreements and counsel decision | | Firestore/GCS backups, exports, snapshots | Potential copies of user data | Publish only the maximum age proved by actual configuration; current 90-day wording is an evidence gate, not an assumption | Inventory every backup/export/snapshot and run an erasure-aging test | ## Right-to-erasure outcome When either the delete-now or grace-period durable workflow completes, it removes the live account and per-user footprint, scrubs the UID from trial markers, and retries external providers. Acceptance of `Delete now` is a queued acknowledgement, not that completion. The following may remain for disclosed, bounded or legally justified reasons: - unfinished ordinary `erasureJobs` and related residue until all applicable stages succeed, then the timestamp-only `erasureTombstones/{uid}` completion guard for its short bounded anti-recreation window (or, for an account erased while a store subscription was active, the `billingGuard` variant sized to the paid-through date plus one renewal cycle and the store allowance, at most 400 days); or, for the locked-out legacy no-token exception, a terminal Apple-false `manual_required` UID/outcome job for 30 days before explicit sweep deletion; - de-identified one-trial markers; - short-lived hashed/IP/device usage and webhook-dedupe records; - a random-ID, UID/Apple-subject-free `appleAuthorizationCleanup` item until accepted revocation or the 7-day ciphertext-purge/alert boundary; it cannot be matched to a per-user request, while UID-keyed Apple throttle metadata remains exportable; - configured logs/backups until their proved expiry; - content-free journal/Mentor receipts and the server chat-erasure epoch until account deletion, including through Clear Chat where they are required to reject a stale cross-device outbox; - immutable local deletion tombstones and the encrypted exact-path cleanup ledger until the relevant file/path is verified absent or App storage is removed; - verified-review email-only legacy support rows for their applicable support/legal- hold period; and - provider/store records governed by their verified retention/legal duties; and - legal-claims/tax records only where counsel confirms necessity and disclosure. ## Pre-launch evidence actions 1. Verify production deployment of the durable erasure worker and server-only rules. Prove `Delete now` writes the job before returning 202, performs no synchronous physical-erasure stage, signs the client out, retries an incomplete access lock first and rejects cancellation/restoration. 2. Verify `rateLimits.expireAt` is Active, including the raw-UID `appleToken_{uid}` document; prove the `rateLimitsByIp` sweep removes the UID/IP-free `appleTokenGlobal_{hour}`, keyed one-way `appleCleanupIp_*` and `appleCleanupInstall_*` (app-scoped device binding), and UID/IP-free `appleCleanupGlobal_{hour}` records at approximately 2 hours; and verify every other period has a real sweep, TTL or provider setting. 3. Configure `SENTRY_AUTH_TOKEN`, `REVENUECAT_SECRET_API_KEY`, `APPLE_SIGN_IN_TEAM_ID`, `APPLE_SIGN_IN_KEY_ID`, `APPLE_SIGN_IN_CLIENT_ID`, `APPLE_SIGN_IN_PRIVATE_KEY` and `APPLE_TOKEN_ENCRYPTION_KEY` with least privilege; test complete and interrupted deletion without printing secrets. 4. Alert on aged/retrying ordinary `erasureJobs`, residue and provider failures; verify terminal `manual_required` jobs are never retried and the explicit sweep removes them at 30 days; verify the hourly tombstone sweep and alert on either expiry backlog or sweep failure. 5. Prove the `appleAuthorizationCleanup` collection is client-denied, never carries UID/subject/code/readable token, retries hourly, deletes on accepted revocation and purges ciphertext plus alerts at 7 days without a success claim. Prove the pre-auth collision endpoint has no Firebase authentication/UID, unconditionally rejects a missing or invalid App Check token, reserves keyed one-way network and app-scoped- device-binding counters at 3/hour each plus the 1,000/hour global fuse before Apple, stores no raw cleanup binding/code/token, and revokes immediately or transfers only the encrypted issued credential into this queue. Confirm the binding is the same native-safe identifier family used for trial and deletion-OTP binding, not a web-only Firebase Installation ID. 6. Prove the actual backup/log/provider maximum periods before publishing them. 7. After deploying the fragment-capable cancellation page, expire/revoke legacy query-link tokens, inspect/purge token-bearing Vercel/CDN/application logs under the approved retention/incident process, and record when the query fallback can be removed. 8. Prove journal and Mentor outbox/quarantine bounds, exact-once recovered-transcript insertion, receipt replay prevention, Clear Chat epoch behavior across two devices, failed-OS-delete tombstone retry, encrypted-path-ledger secrecy and entry-photo deletion. 9. Prove the operator system's UID/userId/subject DSAR and erasure coverage, verified review for email-only legacy rows, and the shared erasure-job/tombstone barrier. 10. Reconcile this schedule with Privacy v3.1, RoPA v3.1, Data Safety and App Privacy. ## Plain-language reference crosswalk The customer documents are now authored in `docs/legal/privacy.json` and `docs/legal/terms.json`. The implementation references above retain their pre-rewrite labels against `docs/legal-history/privacy-v3.4.html` and `terms-v2.7.html`. In the current Privacy Policy, sections 1–3 keep their major numbers, former 3a is section 4, and former sections 4–18 are now 5–19. In Terms, sections 1–16 keep their major numbers, former 16a is section 17, and former sections 17–26 are now 18–27. Current user-facing cross-references use these new section numbers.