> ## Documentation Index
> Fetch the complete documentation index at: https://docs.oximail.ch/llms.txt
> Use this file to discover all available pages before exploring further.

# Compliance: journaling & erasure

> The two compliance primitives for regulated organizations: mail journaling (record-keeping) and account erasure as a durable, provable phase machine (GDPR Art. 17 / DSAR).

Regulated organizations — finance, health, legal, public sector — face two obligations that pull in opposite directions: **keep a record** of business communications, and **erase a person's data** on request, provably. OxiMail ships both as first-class server features: per-organization mail journaling, and an account-erasure engine that runs as a durable phase machine and leaves a non-identifying completion record you can produce months later.

<Note>
  This page describes what the server does, not what your regulator requires. Map these mechanisms onto your own legal obligations (GDPR, Swiss FADP, sector rules) with counsel.
</Note>

## Mail journaling

Journaling silently copies matching mail to a **journal mailbox** or an **external archiver**, per organization. It is the Postfix `always_bcc` / `sender_bcc_maps` / `recipient_bcc_maps` feature set, done as data rather than as config file edits — the typical use is an Art. 30 GDPR record-keeping duty or a sector archiving rule.

**Off by default.** No rules means no journaling; nothing is copied until an administrator creates a rule.

### Rules

A journaling rule has three dimensions:

| Dimension       | Values                            | Meaning                                             |
| --------------- | --------------------------------- | --------------------------------------------------- |
| **Direction**   | `inbound`, `outbound`, `both`     | Which mail flow the rule watches.                   |
| **Match**       | `always`, `sender`, `recipient`   | Everything, or only mail from / to a given address. |
| **Destination** | a mailbox, or an external address | Where the copy goes.                                |

The two destination forms differ in custody:

* **Mailbox** — the copy is re-ingested into a local journal mailbox, so it is encrypted at rest like all other mail and stays inside the organization's storage boundary.
* **External** — a copy is relayed to an external archiver (a dedicated archiving service or another server you operate).

### Invariants

* **Relay traffic is never journaled.** Mail relayed for `service` accounts keeps the [ephemeral-retention promise](./outbound-relay#retention-the-relay-is-not-an-archive) — a smarthost does not archive its clients' mail.
* **A journal copy is never re-journaled.** An `X-Oximail-Journaled` header guards both destination forms, including across an external round-trip, so rules cannot create a mail loop.
* **Destinations are organization-isolated.** A rule can only target a mailbox inside its own organization (the storage-layer tenant isolation, ADR-015).
* **Journaling is best-effort and never blocks mail.** A journaling failure is logged loudly but the delivered or submitted message is unaffected — record-keeping must not become a delivery outage (ADR-017).
* **One evaluation per message.** Inbound mail is evaluated once after the delivery loop; outbound mail once after a non-relay submission is enqueued.

### Managing rules

By CLI:

```bash theme={null}
oximail journaling list    <tenant>
oximail journaling add     <tenant> ...      # direction, match, destination
oximail journaling remove  <tenant> <rule-id>
oximail journaling enable  <tenant> <rule-id>
oximail journaling disable <tenant> <rule-id>
```

Or over JMAP, for admin tooling: `JournalingRule/get` and `JournalingRule/set` under the `urn:oximail:params:jmap:v2:admin` capability (admin-only).

## Account erasure (GDPR Art. 17 / DSAR)

Deleting an account is not a `DELETE` statement — it is a multi-step operation with legal weight, and a crash or an operator retry must never leave it half-done or run it twice. OxiMail runs every account erasure as a **phase machine recorded in the database**:

```text theme={null}
enqueued → agency-frozen → residue-redacted → identity-stripped → data-swept → completed
```

The properties that make it trustworthy:

* **At most one live erasure per account, by construction.** A partial unique index makes a concurrent second erasure impossible across processes — an admin-API delete and a CLI delete on the same box cannot race.
* **Durable and resumable.** Each phase records its progress. A driver that dies mid-flight (crash, proxy timeout) leaves a job that a maintenance worker adopts and resumes, using a lease with heartbeat; a boot-time sweep catches orphans. The pass is decoupled from the HTTP connection that requested it, so a proxy timeout cannot cancel it.
* **Visible when stuck.** A job parked on a durable failure shows in `oximail erasure list --stuck` and in the `oximail_erasure_jobs_stuck` metric — an enqueued erasure reaches a terminal state or surfaces as stuck, never silently half-done.
* **Thin adapters.** The admin API and the CLI drive the same orchestration; there is one erasure engine.

### What each phase does

1. **Agency freeze.** Before anything is erased, the account loses its ability to *produce* new data: it is disabled, its tokens are revoked, SMTP submission answers `550`, vacation auto-replies are suppressed, and its scheduled / undo-window sends are cancelled at the source. The erasure then **refuses to proceed while any undelivered outbound queue entry remains** (fail-loud; the queue drains bounded by the retry lifetime, or an operator cancels entries via the queue API). Every path that emits mail on behalf of an account is named in a registry with the gate that stops it, enforced by a source audit (ADR-118).
2. **Residue redaction.** Copies of mail the leaver *sent* to colleagues keep naming them in the survivors' mailboxes — in structured columns and inside the raw message headers. The pass rewrites those copies through the normal ingestion path: identity headers collapse onto a non-routable sentinel (`former-user@<domain>.invalid`), address columns are redacted structure-aware (a homonymous recipient is never touched), and the message **body is untouched** — it is the recipients' correspondence. Message-ID tokens, the survivors' own To/Cc records, and trace-header-only appearances are documented out of scope.
3. **Identity strip.** The subject's directory principals are PII-scrubbed and kept as **tombstones** — graph anchors that prevent chat and notification history from being silently misattributed, while carrying no identity. Every grant of the leaver is stripped from every `shareWith` map (each surviving owner gets a recorded change to resync on), actor snapshots in survivors' notifications lose name and email, and a tombstoned principal is refused as a new grantee, so a re-share cannot defeat the strip. Resource and location principals the leaver merely *created* (meeting rooms) survive intact — they belong to the organization.
4. **Data sweep.** The account's rows are deleted table by table, its documents are removed from the full-text search index (the index has no encryption to drop, so it is swept explicitly), and its blobs are crypto-erased by dropping the account keys. The sweep fails loud on a real error instead of declaring success over surviving rows.
5. **Completion.** One durable, **non-identifying** record is written: outcome counters plus an optional DSAR ticket reference, keyed to the identity tombstone. Any error text on the job row is cleared at terminal, so no failure message can carry a personal address into the durable record.

### The reversal buffer

Erasure is deliberately hard to undo — but an operator error (wrong account) needs an honest recovery path. During the identity strip, each stripped share grant's key-delta (the leaver's own bits per object, never the whole map) is captured **encrypted to the organization's master key** into a reversal buffer with a disclosed TTL:

* Default **7 days**, anchored at enqueue time; per-organization override `erasure.reversal_ttl_days`.
* `oximail erasure undo <job-id>` merges each delta back into the *current* map under compare-and-set — an owner's intervening edit is never clobbered.
* `--grantee <principal>` restores the grants onto a **different live principal**: after a completed erasure this is the real recovery, re-provision the person and re-grant to their new identity (validated, provenance audited).
* Past the deadline the restore is refused server-side, even while the rows physically remain; a reaper deletes expired rows unconditionally.

### Proving it happened

Give the erasure a DSAR reference at delete time, and resolve it later:

```bash theme={null}
oximail account delete --account jane@example.com --dsar TICKET-4711 --yes
# ... months later, for the auditor:
oximail erasure prove --dsar TICKET-4711
```

`prove` resolves the reference to the non-identifying completion record — the durable evidence that the Art. 17 erasure was performed (Art. 5(2) accountability). Exit code `1` means no record exists.

### The audit log

The `audit_log` is append-only with a long retention horizon, and erasure must reach it too — without touching accountability:

* A **retention worker** purges rows past the configured horizon. The horizon has a single source of truth, shared with the `auditLogRetentionDays` capability the server advertises, so the two cannot drift; a last-sweep gauge feeds a staleness alert.
* Account erasure **de-identifies** the audit rows it must retain: the fact survives (action, outcome, resource, timestamp) while the identity is scrubbed (client IP, user agent, identifying detail keys). The principal id remains as the tombstone anchor. *Erase the identity, keep the fact.*

## CLI quick reference

| Command                                                                                | Purpose                                                 |
| -------------------------------------------------------------------------------------- | ------------------------------------------------------- |
| `oximail journaling list\|add\|remove\|enable\|disable <tenant>`                       | Manage journaling rules.                                |
| `oximail account delete --account <email> [--dsar <ref>] --yes`                        | Enqueue an erasure (with optional DSAR reference).      |
| `oximail erasure list [--stuck]`                                                       | List erasure jobs; `--stuck` shows only parked ones.    |
| `oximail erasure undo <job-id> [--object-id <id>] [--grantee <principal>] [--dry-run]` | Restore stripped share grants from the reversal buffer. |
| `oximail erasure prove --dsar <ref>`                                                   | Resolve a DSAR reference to its completion record.      |

See the [CLI reference](./cli) for every flag, and [Operations](./operations) for the metrics and workers involved.
