Retention and deletion
How long TDACorp Identity keeps each kind of data, which background job deletes it and how often it runs, what survives erasure, and how to export your data. Every period below was read from the code or configuration that enforces it.
On this page
Draft
Status
Last reviewed 5 October 2026. TDACorp Identity is pre-production. Periods are quoted from constants and configuration in the repository. Where a period is a default that a setting can change, the table says so. Where a period is not enforced by code, the table says that too.
How deletion runs
Retention is carried out by background workers, not by database timers. Two things decide when a worker runs.
| Mechanism | Detail |
|---|---|
| Daily scheduled job | One Vercel cron, 0 4 * * *, calls the internal tick route. On the Hobby plan a cron may fire any time in the hour from 04:00 to 04:59 UTC |
| Opportunistic ticks | An ordinary request can also trigger a due-tick, at most once every 30 seconds per instance, with a short time budget |
| External pinger | An optional pinger calling the same route more often. It is chosen by the owner and is not configured in this repository |
| Due-tick rule | A worker runs when its own interval has elapsed and a tick arrives |
Because of this, a job with a short interval is still only as prompt as the next tick, and on the current hosting plan background work can run up to about a day late. Each pass is bounded and idempotent: most deletes are bounded (at most 500 rows per statement and 20 batches per pass), and some, such as session retention, audit compaction and orphan accounts, have their own per-pass caps, so a backlog drains over successive passes.
Workers that delete or expire data
| Worker | Default interval | Enabled in the production configuration | What it does |
|---|---|---|---|
housekeeping | 1 hour | Yes ([workers.housekeeping] enabled = true) | Deletes expired, spent or abandoned short-lived rows. Runs session, token-family and (when flagged) audit retention |
deactivation_sweep | 1 hour | Yes (the default is on) | Erases personal data 30 days after deactivation and revokes the bindings kept for those 30 days |
data_export | 5 minutes | Yes (the default is on) | Builds exports and deletes expired export files |
email_drain | 15 seconds | Yes (the default is on) | Sends queued email and deletes finished queue rows after 7 days |
ops_monitor | 24 hours in production | Yes | Raises operational alerts. It deletes nothing |
audit_anchor | 24 hours | No. Off by default, needs a publish target | Anchors the audit chain. Anchors are currently published by an operator command |
Retention schedule
Accounts, sessions and credentials
| Data | Retention | Enforced by | Notes |
|---|---|---|---|
| Active account records | For as long as the account exists | No timer | No automatic deletion timer applies to an active account's own records |
| Live session | Ends after 12 hours idle or 14 days in total | Session lookup | The cookie lives at most as long as the absolute limit. A "keep me signed in" option exists behind a flag that is not enabled in production |
| Ended session: network address | Nulled once the session is older than 90 days | housekeeping, setting [retention] session_ip_days | Default 90 days, counted from when the session was created. Administrators can see the address and it is included in your own data export |
| Ended session: row | Deleted once it ended more than 90 days ago | housekeeping, setting [retention] session_delete_days | Default 90, minimum 31. The database also refuses to delete a session that ended less than 30 days ago or still holds a network address. A session still referenced by a token family or similar record is kept |
| OAuth refresh tokens and token families | Deleted once the family ended more than 90 days ago | housekeeping, using session_delete_days | The database enforces a floor of 14 days after the family ended. A live family is never deleted. Rows hold ids and scopes, not names |
| Relying-party sessions | Deleted once ended, on the same 90-day cutoff | housekeeping | Linkage records of ids and timestamps |
| Device records | Kept while the account exists. The name is cleared at erasure | Erasure | Removing a device from your account removes it from the list |
| Credentials: password hash, passkeys, authenticator secret, recovery code hashes | While the account exists. Deleted at erasure | Erasure | A removed credential is removed when you remove it |
| Personal access tokens | While the account exists, or until revoked. Deleted at erasure | Erasure | Stored only as hashes |
| Connected-app consents and memberships | While the account exists | None | Withdrawing consent revokes access immediately |
Sign-in, registration and recovery records
| Data | Retention | Enforced by | Notes |
|---|---|---|---|
| Login transaction and its cookie | 10 minutes. The row is deleted an hour after it expires | housekeeping | Expiry grace is 3600 seconds |
| Magic link for sign-in | Valid for 15 minutes by default. An expired link that was never used is deleted; a used link's row stays until the email address row is deleted | housekeeping | |
| Email verification link | Valid for 24 hours | housekeeping | |
| Unverified email address | Deleted 24 hours after the claim was made | housekeeping | |
| Abandoned self-registration | The unverified account with no credential is removed after 48 hours | housekeeping | Kept if anything still references it |
| Primary-email revert link | Valid for 24 hours | Link expiry | Sent to the old address when the primary email changes |
| Recovery link | Valid for 15 minutes. The recovery hold lasts 24 hours | Link expiry | |
| Unopened or abandoned recovery request | Deleted once expired. A request that neither completed nor was cancelled within 7 days is deleted | housekeeping | A held request is never auto-completed |
| Completed or cancelled recovery request | Deleted 14 days after the outcome | housekeeping | The outcome stays in the audit log |
| Recovery device cookie | 30 days | Cookie Max-Age | See cookies |
| Passkey challenges | Deleted once expired | housekeeping | Single use and short lived |
| Authorization codes and pushed authorization requests | Deleted an hour after they expire | housekeeping | |
| Step-up proofs | 10-minute window by default. Rows deleted an hour after expiry | housekeeping | |
| Replay-protection records | Deleted an hour after the replay window closes | housekeeping | |
| Rate-limit counters | Deleted after 7 days idle | housekeeping | An idle counter has refilled to capacity, so deleting it changes no decision |
| Sign-in alert records | 7 days. Deleted when used or expired | Sign-in alert delivery | Holds a label, country, network address and method |
Email, exports and delivery records
| Data | Retention | Enforced by | Notes |
|---|---|---|---|
| Email queue | A finished (sent, suppressed or failed) row is deleted 7 days after it was created. A pending row waits for delivery | email_drain | The recipient address and body are stored sealed, with a hash of the address for de-duplication |
| Email send budget counters | Send counters | Not pruned by the paths read for this page | Counts only, no addresses |
| Personal-data export file | Downloadable for up to 7 days (604,800 seconds, the default and the maximum), then deleted | data_export | The export is stored in the primary database as a sealed blob: each file is encrypted with AES-256-GCM under its own random key, which the key provider wraps. The signature is over the plaintext, so a downloaded file still verifies |
| Export request record | Kept | None | It holds no personal data of its own, and the audit log names it |
| Settled outbox and delivery attempts | Delivered rows are deleted 30 days after delivery | housekeeping | Kept for operator inspection. Rows for Shared Signals deliveries are kept while a delivery references them |
| Orphan webhook delivery attempts | Deleted 30 days after creation | housekeeping | |
| Client error reports | Only stored when the hosted error-report setting is on. It is not set in the production configuration | The error-report writer | Rows idle for 30 days are deleted when the next report is written, and the table is capped at 2,000 rows |
Audit log
| Data | Retention | Enforced by | Notes |
|---|---|---|---|
| Audit events | Default 2920 days (8 years). The setting allows 365 to 3650 days, with an internal floor of 2920 days for TDACorp's own organization | Database guards plus the audit_retention_prune flag | Pruning is off, so audit records are currently retained indefinitely |
| Audit compaction | Only when the prune flag is on, and only rows older than the cutoff and strictly below the newest signed anchor | housekeeping | A tombstone and an audit.compacted row record each compaction. At most 10,000 rows per pass. The database enforces a 365-day floor |
| Audit anchors | Never updated | Grants and a compaction function | No update is granted. When audit pruning is on (it is off by default), anchors at or below a compaction boundary are deleted after a copy is kept in the compaction record; the newest anchor is always kept |
Deactivation and erasure
| Data | Retention | Enforced by | Notes |
|---|---|---|---|
| Deactivated account | 30 days, then erased | deactivation_sweep | The clock starts at the latest deactivation audit event. A deactivated account with no dated event is reported and never erased |
| Bindings and grants of a deactivated account | Kept 30 days, then revoked | deactivation_sweep | |
| Erased account | Terminal state | Erasure | See below for what survives |
Backups and logs
| Data | Retention | Enforced by | Notes |
|---|---|---|---|
| Database history window | Six hours was measured for the earlier platform's projects. The window for this platform's database is recorded as unverified until measured | Neon plan | Not controlled by this platform's code |
| Nightly encrypted dump | Not yet running. Retention is a lifecycle rule on the storage bucket, to be set by the owner at 30 to 90 days | Owner configuration, not code | No number is stated here because none is set |
| Monthly restore drill | Restores the newest dump to a scratch database | Workflow | Not yet run |
| Request log (platform host logs) | One JSON line per request. The hosting plan retains runtime logs for 1 hour | Hosting plan | The line carries an HMAC of the caller address, never the address |
| Request log (telemetry vendor) | Only if the optional collector is configured. The vendor's own retention applies | Vendor setting | No collector is configured in the deployment documents |
| Heartbeat and worker records | Updated in place | None | Hold worker names and times, no personal data |
| ICT log retention for CERT-In | Not addressed | None | The deployment plan lists this as not covered |
What happens at erasure
Deleting an account deactivates it at once. Every session, token and personal access token is revoked and the person cannot sign in again. Thirty days later the sweep erases the account.
| Data | At erasure |
|---|---|
| Profile (display name, locale, time zone) | Deleted |
| Email addresses | Deleted |
| Credentials: password, passkeys, authenticator secret, recovery codes, federated links, personal access tokens | Deleted. A pending authenticator enrolment is deleted too |
| Profile picture | Deleted |
| Notification preferences and security-event read markers | Deleted |
| Sign-in alert records and session context (label, country, city) | Deleted |
| Stored export files | Deleted, whatever their 7-day expiry |
| Device records | The name is cleared. The row stays so past sessions keep resolving |
| Break-glass credential records | Redacted: the label is cleared and the credential is marked revoked |
| Audit events | Kept. They hold the account identifier, event kinds and times, with hashes and 2-character masks instead of addresses |
| Linkage-only records: token families, relying-party sessions | Kept, as ids, scopes and timestamps, until their own retention deletes them |
| Sessions | The network address is nulled by the session retention above; the user-agent hash is removed only when the session row itself is deleted, which happens only once it has no remaining references |
| Invitation records | The address an invitation was sent to is kept, because the record cannot be edited after the fact |
| The account itself | Moves to erased, which is terminal. The subject identifier is retained |
Note
Whether to change what survives erasure is an open owner decision. The audit log is designed to reference identifiers and hashes rather than names or email addresses, but every event kind has not been audited for that.
Export your data
| Item | Detail |
|---|---|
| Where | Privacy and data, in your account |
| Requirement | Signed in, with a second-factor sign-in or step-up in the last 30 minutes |
| Format | A signed JSON document, plus a CSV form |
| Contents | Profile, emails, credential metadata (kind, status, label, timestamps), sessions, consents, memberships and your own audit events. Event payloads and other people's actions on your account are not included |
| Excluded | Password hashes, authenticator secrets, recovery code hashes, passkey public keys, token hashes and session secrets |
| Limits | Each list is capped, and the file says which lists were cut |
| Availability | You are emailed when it is ready. You can download it for 7 days, after which it is deleted |
| If you cannot sign in | Email security@tdacorp.in. No response time is promised |
Legal holds and statutory retention
| Item | State |
|---|---|
| Legal hold process | None is documented in the repository. No code path suspends deletion for a person |
| Statutory audit retention | The specification cites an 8-year retention for the internal organization's audit log, reflected in the 2920-day floor |
| Log retention law | The specification cites 180 days for CERT-In ICT logs. The deployment documents record log retention as not addressed |
| Erasure versus retention | Where a retained record cannot be edited, it is listed above rather than hidden |
Note
Pending legal review by the owner: whether a legal hold procedure is needed, and the retention basis for each kept record.
What this page does not claim
| Not claimed | Detail |
|---|---|
| Deletion at an exact moment | Jobs run when a tick arrives. Erasure happens at least 30 days after deactivation, and can run up to about a day late |
| Total erasure | Audit references, linkage records, invitation addresses and some session fields survive, as listed |
| Deletion from backups | A backup is not edited. No backup retention period is set in code, and the nightly dump workflow has not yet run |
| Automatic audit pruning | It is off. Audit records are retained indefinitely at present |
| Strength of export file encryption beyond what the key provider gives | Export files are sealed per file, but the pre-production key provider is a software one, so the database plus its master key could open them |
| A legal hold or statutory retention process | None is documented |
| Retention in other systems | Vendors keep logs and records under their own terms. See the subprocessor register |
| That a setting cannot change a period | Several periods are defaults controlled by configuration and are quoted as such |
| That the privacy policy and this page agree on every detail | The privacy policy states that session records survive erasure. The code also contains the session retention above. See the open owner decision on privacy |