Unfenced - Data Retention
Last updated: 17 September 2026
We keep personal data only as long as it serves the purpose it was collected for, and no longer. This page names how long each category is kept and the mechanism that removes what has aged out. It is the operational companion to the retention section of the Privacy Policy.
What we keep, and for how long
| Data | Where | Default retention | Why |
|---|---|---|---|
| Fetch log and receipt - URL, outcome, timing, token counts, and the extracted markdown returned for each stored read | fetches + fetch_receipts (central), history.jsonl metadata (per worker) | 365 days; a receipt is cascade-deleted with its fetch row | Activity history, exact-result receipts, debugging, and abuse investigation. |
| Session recordings - the step trace of a live run, and its replay frames | recordings (central), frames in object storage | 365 days | Lets you review what an agent did on your behalf. |
| Usage events - the metering rows behind quotas and billing | usage_events (central) | 365 days (metering rows), longer where tax/accounting law requires | Quotas, billing, and the records law requires us to keep. |
| Saved logins and signed-in sessions - sealed passwords, TOTP seeds, cookie jars | credentials, browser_sessions (sealed) | Until you delete them, or you delete your account. Deleting a primary sign-in leaves the worker's shared browser state, which other providers and the agent use - see the known gap in "Erasure" | You control these directly; they are never aged out automatically. |
| Account and profile - your account row, membership, preferences | accounts, profiles, account_prefs | Life of the account | Needed to operate the service for you. |
| Audit trail - what the agent did, host by host | audit_events (per worker) | Life of the account | The record that a delegated action happened, redacted of secrets. |
| Connect recovery backup - Windows-encrypted shared device credentials and reconnect choices | connect_recovery (Connect registry) | Until this PC's account is forgotten or the account is deleted | Restores approved shares on the same Windows computer without asking each friend to pair again. |
| Support requests - issue reports, feature requests, and general messages | support_requests (central) | Until account deletion | Lets the support team review and respond to your request. |
Deleting your account removes all of the above ahead of any schedule - see "Erasure" in the Privacy Policy. The aged-out prune below is a floor for accounts that are still open, not a substitute for erasure.
The prune mechanism
Aging-out is a mechanism plus a policy, and it is off by default. Nothing is deleted on a deploy, and no schedule runs on its own. An operator enables it deliberately, and the first thing it does is report - never delete - until told otherwise.
The runner is scripts/retention-prune.mjs. It is controlled entirely by environment, and refuses to touch anything unless it is switched on:
UNFENCED_RETENTION- must beonfor the runner to do anything at all. Unset or anything else and it exits without connecting to the database.UNFENCED_RETENTION_DAYS- the window, in days. Defaults to a generous 365; rows newer than this are always kept.UNFENCED_RETENTION_APPLY- must be1to actually delete. In any other state the runner is a dry run: it counts what it would remove and prints it, changing nothing.
Deleting rows across accounts needs the central service-role key (SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY), which lives only where an operator runs the prune - never on a worker. Run it from a trusted host, on a schedule you own (a nightly cron), reading the dry-run output first.
Replay frames in object storage are deleted by the prune itself: for every recording it is about to remove it reads that recording's id and frame count first, deletes each object, and only then deletes the row - because the row is the only thing that names them, so a row removed first would orphan its frames permanently. If any object of a recording survives, its row is kept and the run reports it, so the frames stay reachable rather than becoming invisible. The prune refuses to start at all when frames are configured into object storage and it has no credentials to reach them.
A bucket lifecycle rule set to the same window is still worth having as a second line - node scripts/r2-verify.mjs --lifecycle reads the rule back off the bucket and says whether it exists and what window it uses - but nothing here depends on one being present.
Your control
You can delete a saved login at any time from the dashboard, and you can delete your whole account, which erases everything tied to it - including the worker's local browser profile and cookie-jar data.
Deleting one saved login is narrower than deleting the account, and the difference is worth stating because it is the action people take after a scare. It removes the stored secret, and - for an _additional_ signed-in account, which has a cookie jar of its own - that jar with it. A primary sign-in's cookies live in the account's shared jar, which other providers and the agent are also using, so they remain until the account is deleted (packages/server/src/routes/vault.ts:269); nor is the browser profile on the worker's disk cleared by a per-credential delete.
There is no per-item control for a stored signed-in session or for a single recording: those go when the account does, or when the retention window above ages them out. To exercise any data-subject right, contact privacy@unfenced.ai.