Audit retention, erasure and legal holds
For: tenant admins and governance reviewers.
Retention
How long a tenant's audit entries, job summaries, usage rows and payloads are kept is set per tenant. Without a rule, everything is kept.
oz0 retention set 365 --reason "records policy 4.2" --decision-ref "Dnr 2026/31" # at least 31 days; 0 keeps everything
oz0 retention show
Retention, legal holds and the rest of the governance settings change only with a reason, which the audit entry keeps; with four eyes on, a shorter retention or a lifted hold waits for another admin.
An edge runs the retention job under a cluster lease, deleting once an hour what is older than the rule. The audit chain loses its oldest part and starts after it, so what is left still verifies; the deletion is in the log as retention.applied.
Stored payloads (External Storage) are kept at least 90 days whatever the rule, because a job that runs that long replays its first steps from them.
While a tenant is under a legal hold, none of its stored payloads is collected, however old. An eval case kept from a job goes with the job's data: the retention job removes it from the workspace too.
Temporal history retention
Temporal's histories follow it too. Each tenant's Temporal namespace keeps closed jobs' histories as long as the rule says (without a rule, what the namespace keeps, as before: 7 days unless you changed it), within its backend's limits: the edge sets it on the embedded and an external Temporal, and on Temporal Cloud, at most 90 days, says what to set. oz0 retention show shows what each namespace keeps; see how long histories are kept.
High-risk agent records
High-risk agents keep six months at least. The EU AI Act asks for the logs of high-risk systems to be kept at least six months. An agent marked high in the inventory keeps its entries 184 days whatever the tenant's rule. Between the tenant's rule and the floor, the other agents' entries lose their actor, target and detail instead of being deleted (redacted in audit verify), so the chain stays whole.
Agent keep-for periods
Agents with a keep-for period keep their jobs at least that long. An agent's keep_for, or a tenant's rule for it (oz0 retention keep-for), keeps its job summaries past the tenant's rule until the period ends (kept_for_agents in retention.applied), raises the tenant's Temporal namespace to the longest period (one number for the whole namespace), and where the backend keeps less, keeps the jobs' histories as held copies (retention.keep_for.copied) that retention deletes when each period ends. The audit trail follows the tenant's rule and the high-risk floor as before.
Erasures in the audit log
None of these entries names a person; they keep only the hash.
| Entry | What it records |
|---|---|
subject.erase | An applied erasure: keys destroyed and entries redacted. |
subject.erase.deferred | One entry per agent whose keep-for period defers jobs: the period, until when, the reason keep_for and the legal basis its rule records, if any. |
subject.erase.done | The edge deleted the jobs. For a deferred erasure, this follows the last deferred job. |
subject.erase.unnameable | A deferred job's pass met a stored job ID Temporal cannot be asked about, with jobs_unnameable and jobs_partly_found. |
subject.erase.requested, subject.erase.approved, subject.erase.rejected, subject.erase.applied | The request and decision under the second-person erasure policy. |
subject.erase.refused | A refusal under the off policy or a legal hold, with the reason. |
tenant.erasure_policy | The tenant's erasure policy. |
retention.keep_for | A keep-for rule, with basis and basis_was when it records a legal basis. |
retention.keep_for.plugin | A plugin version brought, changed or dropped an agent's keep_for. |
subject.rule.set, subject.rule.deleted | Mapping rules. |
A restore that applies an erasure again from the erasure ledger records it as subject.erase by system:restore with reapplied (backups).
An erasure by a spelling that had another hash before subjects were matched by one normal form also erases what was recorded under that hash, as a second subject.erase with erasure_of (the first erasure) and spelling_before_normal_form.
Legal hold
oz0 retention hold --reason "Case 2026-114"
oz0 retention release --reason "Case closed"
A legal hold stops retention and erasure for the tenant until it is released. Placing and releasing it are in the log, with who and why; releasing it waits for another admin when the tenant has four eyes on. A hold a cluster admin placed, for an authority's request say, only a cluster admin can release; the tenant's own admins see who placed it.
Legal holds and Temporal's histories
Temporal deletes histories by namespace, and the retention assigned to a closed run is fixed when it closes. A hold therefore preserves a separate copy of each closed run's history and referenced External Storage payloads.
Capture starts after a settling delay and runs in bounded rounds, each tenant in turn, reading a page of events at a time and writing small events in batches; a pass goes on for two minutes while captures have work left, each tenant's round having an even share of what is left of them and stopping at the end of the capture that takes it past that share, and a pass finds a bounded number of closed runs. What is left of either starts the next pass at once.
A busy backlog, an outage or a missing source can make it take longer. Five minutes is not a completion guarantee.
Archive format and limits
- As Temporal holds it. The edge preserves the history and serialized payloads without decrypting or resealing them. Tenant and person encryption still applies.
New copies use immutable objects and chunks of at most 1 MiB rather than one aggregate gzip blob, so the old 32 MiB compressed and 128 MiB aggregate capture limits do not apply to the new format.
Each original payload still has the 32 MiB payload limit, and capture accepts at most 256 MiB of JSON for one history event.
Those bounds apply to an individual source object, not the whole run. Database capacity and the availability of the source still limit what can be captured.
Archives are included in supported backups.
The archive's encrypted field records encryption-configuration metadata; it does not attest that every original payload was encrypted.
Verify completeness
- Complete only after verification. The edge verifies every required event, payload, chunk, size and checksum before publishing a complete copy and recording
retention.hold.copied(orretention.keep_for.copied).
Pending work, retries and partial copies survive an edge restart.
Malformed or missing external references keep a copy partial; raw history already preserved remains durable.
So does a run that names more than 65,536 distinct stored payloads, more than Temporal's default event limit allows a run to name one per event: the references past that are not indexed, its events are kept, and it stays partial with invalid_reference.
Partial copies have no complete export or copied audit. An existing row alone is not proof of complete preservation.
Capture progress and recovery
- Progress and recovery.
oz0 retention held --captureslists capture state, retry timing and fixed failure codes without exposing content. Follow its--page-tokenfor another page.
A capture whose stored record is damaged keeps its row as unreadable, with its IDs written out and the failure integrity: it is passed over until the record is repaired, the captures beside it go on, and the namespace's Temporal line in oz0 retention show says the tenant has runs that are not kept.
When a tenant's Temporal stops answering, the keeper stops asking for that tenant after 90 seconds of unanswered lists, and of unanswered history reads, until its next pass, so the tenants after it wait about four minutes at most in a pass; a job it did not ask about keeps its retries.
A source expiry is shown as unknown or estimated: Temporal does not expose the actual deletion deadline assigned to an old run, and the namespace's current retention cannot establish it.
Investigate invalid_reference, payload_missing, history_unavailable, repeated storage failures and a growing backlog while the source is still available.
Malformed references need investigation; they do not become complete merely through retry.
Restore the missing source or storage access and let the durable retry run.
A job kept for a keep-for period whose discovery failed has a retry of its own, after one minute and then twice as long each time up to an hour, ten in all, after which the repair lap's walk of the agent's closed jobs comes back to it.
One job, run or agent that fails holds no other back.
A run the archive refuses, or one under an ID from before IDs were checked, is passed over: oz0 retention show says so in the namespace's detail, and the edge's log says when it passes one.
Such a run is not kept, so a job ID with a line break, a carriage return or NUL has no retained copy; one with a tab or a backslash is kept.
An older partial copy can seed repair from bytes it already retained, even if Temporal has gone; bytes missing from every source cannot be recovered by adding a hold later.
List and export held copies
- Listed and read.
oz0 retention heldlists stored copies.
A copy whose record cannot be read, because its job ID is from before IDs were checked or its record is damaged, keeps its place in the list as unreadable, with its ID written out and nothing else, and cannot be downloaded; the other copies are as they were.
Its historical count can include an older partial-format copy; use capture status and a verified export to assess completeness.
oz0 retention held --job JOB --run RUN --out DIR writes history.json and the referenced serialized payloads under payloads/<digest>.
Large new-format copies download incrementally into a new or empty directory, with identity, ordering, size and checksum checks and a final check that the archive is still available.
Choose a private parent directory because temporary files are staged beside --out.
Unix files use mode 0600 and directories 0700; on Windows the parent's ACL governs access.
A failed new-format export leaves no completed-looking destination.
Publication uses an atomic no-overwrite rename on Linux, macOS and Windows; other platforms refuse the new-format export rather than risk replacing another directory.
Reading any content requires the content permission and a reader whom the tenant's walls do not close off, and is audited as retention.hold.read.
Older archive formats and clients
- Older copies remain readable. Existing
oz0-held-history/1downloads keep their original decoder limit of 256 MiB decompressed JSON.
A modern CLI marks missing-payload downloads as .partial, returns a failure and never calls them complete.
An old CLI can read new-format copies through the existing unary API when the response fits 32 MiB including metadata and contains at most 4,096 objects; a larger one asks for an updated CLI's bounded export.
These new-format budgets do not reduce older-format download capacity.
Encrypted serialized payloads can be submitted to the edge's codec endpoint by an authorized reader.
A bare External Storage reference still reads the ordinary payload store; the exported payload files are the independent preserved bytes.
Holds and erasures during capture
- Current holds and erasures apply. Releasing a hold permits cleanup only when no current hold or granted keep-for period protects the copy.
A re-hold protects the data still present.
Erasure cancels capture and refuses later reads so a stale worker cannot recreate an erased tree; it cannot recall files a reader already downloaded.
A later or restored hold also blocks final physical cleanup of an already accepted erasure's retained archives and job summaries while it lasts; reads stay denied and destroyed keys stay destroyed (holds added after acceptance).
Restoring an older backup still needs the supported erasure-ledger procedure to reapply later erasures before serving data.
The separate physical-erasure scan of original Temporal history still has its 128 MiB bound: an oversized scan can leave physical cleanup pending rather than claim it finished. This archive change does not remove that limitation.
Place holds early and leave enough backend retention for discovery, capture and outage recovery. A source that expired before capture may already be lost. Job files passed between tools are separate from the External Storage payloads in an archive.
oz0 retention held
oz0 retention held --captures --json
oz0 retention held --job job-0192... --run 0192... --out ./case-2026-114
The console has retention and the legal hold under Govern, Privacy, Retention, with what Temporal keeps and the number of copies.
Export and verify evidence bundles
Export and verify evidence bundles for tenant admins and governance reviewers.
Command rules
Decide the shell commands a harness runs, part by part: the platform's default rules ask about catastrophic commands on every node, and the tenant's own rules Allow, Ask or Refuse by the command's parse, never its text.