truuimage · Legal
Data Retention
Last updated: 2026-08-14
Overview
This page states, per category of data, what truuimage keeps, for how long, and the specific mechanism in the code that enforces it, not a general promise, but the actual behaviour. One scheduled task, run every 5 minutes, executes twelve independent cleanup steps that do almost all of the automatic cleanup described below: expiring abandoned scans and refunding their credit, retrying any original-image delete that did not succeed the first time, removing expired upload tickets and the bytes they point at, pruning old rate-limit counters (both this app's own table and Better Auth's separate one), pruning old audit-log, revoked-API-key, and Stripe-webhook-event rows, pruning expired sessions and expired sign-in/verification codes, settling any purchase that has sat unresolved for more than a day by checking directly with Stripe, and retrying any account-deletion request that did not finish on its first attempt (see "Account data and deletion" below).
What we keep, and for how long
- Original uploaded image
- Deleted as soon as the scan finishes (completed, failed, or expired) typically within seconds; up to about 5 minutes plus one cron cycle in the stuck-scan case below. Honest edge case: if the stuck-scan expiry keeps failing to refund the credit it owes back, the scan never reaches a final status, and its original is retained until that failure is fixed.
- The full-resolution image bytes you submit for a scan.
- Deleted automatically once the scan reaches a final state, whatever the outcome. If a delete does not succeed, a background job retries it every few minutes until it does.
- Abandoned upload
- Removed within roughly 5–10 minutes of being abandoned.
- Bytes you uploaded that never turned into a scan (e.g. you closed the tab mid-upload).
- The upload ticket behind it expires on its own, and a background job then removes both the stored file and the ticket.
- Thumbnail
- Indefinitely, for as long as your account and its scan history exist.
- A 512px-long-edge WebP downscale of your image, with EXIF, GPS, ICC, IPTC, and XMP metadata stripped.
- Metadata is stripped when the thumbnail is made, not on request, so the stored copy never carries it. The thumbnail is kept so your scan report keeps working; deleting your account removes it immediately.
- Scan record
- Indefinitely, for as long as your account exists.
- Status, filename, byte count, a content hash, the verdict, the confidence score and tier, and timestamps.
- This is your scan history, so nothing prunes it on a timer. Deleting your account removes it.
- Raw detector result
- Indefinitely, in the same row as the scan record above.
- The detection model's full response payload, stored verbatim.
- Kept so the evidence breakdown on your report can be shown again later without re-running the model on your image.
- Scan feedback (your corrections)
- Indefinitely while your account exists, no automatic prune exists for it on its own, because it is tied to the scan record it belongs to, which is itself retained indefinitely (see the row above). Deleting your account deletes every scan_feedback row outright, that is a real, working deletion path today, not the same thing as the scan row itself being deleted (it is not, see "Account data and deletion" below for exactly what each table does on deletion).
- The verdict you say is actually correct, an optional generator you named, and any free-text notes you chose to type, exactly as you wrote them; we do not redact or review them first. The whole row is retained together; there is no partial-deletion path that keeps some of these fields and drops others.
- Recorded only when you use the "Was this correct?" control on a report, and kept to help us find where the detector gets it wrong. Deleting your account removes every correction you submitted.
- Abandoned / stuck scan
- Automatically expired, and its credit refunded, once it has been non-terminal for 5 minutes.
- A scan that never reached a terminal status, e.g. a worker crashed mid-analysis.
- A background check runs every few minutes, marks the scan expired and refunds the credit. If the refund itself keeps failing, the scan is deliberately left open rather than closed without refunding you.
- Audit log entries
- 400 days.
- Security-relevant events, sign-in, sign-up, API-key creation/revocation, billing reconciliation, each with an IP address and user-agent string when the event arrived on an attributable HTTP request. Several call sites (API-key actions, settings actions, some auth database hooks) record the event without a request in hand, and those two fields are simply absent (NULL) on that row.
- Kept so a security question can be answered after the fact, then removed automatically once the window passes.
- Rate-limit counters, application routes
- 1 day past the window it counted.
- How many requests a given API key or IP address made against this app's own routes, in a fixed time window.
- A counter, not a record of what you did. Removed automatically once the window it measured has passed.
- Rate-limit counters, sign-in / sign-up
- At most 25 hours past the last request in that bucket (the widest window any of this app's rules for it uses, plus the same one-day buffer the application table above gets).
- A separate counter for the sign-in and sign-up routes, keyed by IP address and the specific sign-in/sign-up/OTP path requested, a different table from the application one above.
- A separate counter protecting sign-in and sign-up from guessing attacks. Removed automatically once the window it measured has passed.
- Session (sign-in state)
- Up to 30 days, pruned once the session's own expiry has actually passed.
- Your session token, plus, for security, the IP address and user-agent string of the device that signed in.
- A session lasts up to 30 days and is extended while you keep using it. An expired session is refused immediately, before it reaches the application, and the record is removed shortly afterwards.
- Sign-in & verification codes
- Expires with the code itself, a short and fixed number of minutes, then removed on the next cleanup pass at the latest.
- A one-time code and the email address it was sent to (the verification table's identifier column), for email verification, sign-in, and password reset.
- An expired code is refused the moment it is presented, before anything deletes it, so cleaning these up can never invalidate a code you could still have used.
- Revoked API keys
- 90 days from the moment it was revoked.
- A revoked key's name, display prefix, and last-used time. Never the secret itself.
- Kept for a short period after you revoke a key so you can still see what it did before you turned it off, then removed automatically.
- Live API keys
- For as long as the key remains unrevoked.
- Only a SHA-256 hash of the key, a display prefix, and usage metadata, never the plaintext secret.
- Only a hash is stored, never the secret. The key itself is shown once, when you create it, and cannot be recovered from us afterwards.
- Purchases, credit ledger, and current balance
- Indefinitely, as a financial and accounting record, linked to your account.
- Which pack you bought, the amount, currency, Stripe identifiers (including the checkout session and payment intent ids), and every individual movement of your credit balance (an append-only ledger), plus a separate cached row holding your current balance, updated on every movement.
- Kept as a financial and accounting record. These entries have to survive account deletion where tax and accounting law requires it, so they are not removed with the rest of your data; see "Account data and deletion" below for exactly what remains.
- Account data
- For as long as your account exists, see "Account data and deletion" below for exactly what changes the moment you delete it.
- Your name, email address, password hash, and two-factor secret if enabled.
- You can delete your own account from account settings; no request to us is needed. See "Account data and deletion" below for the mechanism and the full breakdown.
Account data and deletion
Deletion is self-service, from a destructive-action panel on your account settings page: type your account email address to confirm, and deletion starts immediately. The request is recorded so it cannot be lost, along with how far it has got.
The first thing it does, before touching stored images, Stripe, or anything else, is revoke your password, your two-factor secret, every API key and every signed-in session, and anonymise your account. From that moment nothing can authenticate as this account again, however long the rest takes. You are signed out of your current browser straight away rather than waiting for the session to expire.
If the rest cannot finish in one pass, usually because a scan you submitted is still being analysed, occasionally because a storage or payment-provider delete is briefly slow, a background job retries the whole request every few minutes until it completes. Deletion is never abandoned half-done. A scan still in progress is resolved automatically (see "Abandoned / stuck scan" above) well before that becomes a real wait.
Deleted: your password and two-factor secret; every API key and signed-in session; every sign-in/verification code tied to your account, all revoked immediately, as the first step, described above. The original image bytes and thumbnails for every scan you made (purged from storage, same mechanism as the table above, deferred only for a scan still being analyzed, until it reaches a final state); the free-text notes AND the verdict/generator on any scan feedback you gave (the whole row is deleted, not just the notes); and your Stripe customer record (removed via the Stripe API, see the note below). Your account row itself is not deleted, it is anonymised in place: it is marked deleted, your email is replaced with a non-routable placeholder, and your name and profile image are cleared. Your real email address is freed by this, it can be used to register a new, unrelated account afterward, and nothing links the two.
Retained, anonymised: your scan history (status, verdict, confidence, and the full detection result) stays, with the filename and the image's content hash overwritten so the row can no longer be matched back to the original file. Your purchases, credit ledger, and current balance are not anonymised by this process at all, see the "Purchases, credit ledger, and current balance" row above: those three tables intentionally have no link to the account row that changed, so they simply keep pointing at your (now-anonymised) account id, unaffected, as the financial record they exist to be. Security log entries (sign-in history, IP address, user agent) already tied to your account before deletion, plus one new entry recording that the deletion happened, are kept for the same 400 days as every other account's security log, see "Audit log entries" above, still linked to your (anonymised) id for that whole window.
Stripe, honestly: deleting your Stripe customer record removes it from our records and from Stripe's own live dashboard and API. It does not purge whatever Stripe itself separately retains under its own data-retention obligations: invoices, charges, and payment-method records tied to a closed customer are commonly kept by payment processors independent of what their API exposes. We ask Stripe to remove its copy; we cannot make a stronger claim than that about what Stripe keeps afterward.
For anything account settings does not expose, contact privacy@resonixlabs.io.
Questions
Questions about this page or a specific retention window: privacy@resonixlabs.io.