# Data lifecycle & privacy

This page answers the questions a data-protection review asks: **what is
stored, where, for how long, and how do I delete it.** It matters most for the
on-premise edition, where all of this lives in _your_ infrastructure and
nothing is ever phoned home.

## What is stored

| Data                             | Where                                                                                                   | Contains personal data?                                        |
| -------------------------------- | ------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- |
| **Document content**             | `collab_snapshots` (periodic full snapshots) + `collab_entries` (the append-only patch log)             | Whatever your users type; treat it as user content.            |
| **Edit attribution**             | `collab_entries.user_id`: the `userId` from the resolved identity, per sequenced batch                  | Yes; links an edit to a user id.                               |
| **Comment threads**              | `collab_comments` (`data` JSONB: body, quote, resolved, replies, and each author's `userId`/name/color) | Yes; author identity plus free-text bodies.                    |
| **Document registry**            | `collab_documents` (doc id + created-at)                                                                | No (unless the doc id itself is personal).                     |
| **Presence / cursors**           | In memory only, never persisted                                                                         | Transient.                                                     |
| **Identity (name, color, role)** | Not stored as such; resolved per connection from _your_ token on every join                             | Comes from your identity provider; the server does not own it. |

In the default **memory** storage everything above lives in the server
process and is lost on restart (evaluation only). In **postgres** storage it
lives in the four tables in _your_ database.

> **Note: Identity is your data, resolved not stored**
>
> The server never persists a user's name or role independently. It calls
> your `checkAuthentication` hook on every connection and takes the identity
> from your token. What persists is only the `userId` reference on entries
> and comments. Deleting or renaming a user is done in your identity
> provider; see [right to erasure](#right-to-erasure) for removing their
> document-level data here.

## Lifecycle

- **Live session (in memory).** A room is created when the first client joins
  and holds the document + a bounded OT window in memory. After the **last**
  client disconnects it is swept from memory after `EMPTY_ROOM_TTL_MS`
  (default 10 minutes). This frees memory; it does **not** delete persisted
  data.
- **Persistence (in your DB).** Every sequenced entry is written to
  `collab_entries` before it is broadcast; a full snapshot is written every
  `SNAPSHOT_EVERY` entries. This is retained **indefinitely** unless you enable
  pruning.
- **Retention / pruning.** With `RETENTION_SNAPSHOTS > 0`, on each new snapshot
  the server keeps only the newest N snapshots and the entries after the oldest
  kept one; older history (and its edit attribution) is deleted. Leave it at
  `0` (the default) to keep a full audit trail; set it to bound growth and
  shorten how long historical edits are retained.
- **Comments** persist until explicitly deleted (by the author, an `owner`, or
  document erasure).

## Transport & at-rest

- **In transit:** the server speaks plain WS/HTTP; you terminate **TLS** at a
  reverse proxy in front of it (browsers require `wss://`). See
  [Deployment → nginx](deployment.md#behind-nginx-websocket-proxying).
- **At rest:** encryption is your database's responsibility (e.g. Postgres
  TDE / encrypted volumes). The server stores content as JSONB; it does not add
  its own encryption layer.
- **Secrets:** the `DATABASE_URL` and your `JWT_SECRET` are provided via env /
  `.env`; manage them with your secret store.

## Right to erasure

Two levels, both irreversible:

- **Delete a single comment thread**: the author, or any `owner`, via the
  `comment.delete` message; removes it from `collab_comments`.
- **Erase an entire document and all of its data** (snapshots, patch log
  including edit attribution, and comments) via an admin-only endpoint:

    ```
    DELETE {routePrefix}/docs/{docId}
    Authorization: Bearer <token>     # requires the `admin` action (owner role)
    ```

    ```bash
    curl -X DELETE -H 'Authorization: Bearer <owner-token>' \
      https://collab.example.com/collab/docs/report-42
    # → { "success": true }
    ```

    The live session (if any) is evicted; connected clients reconnect into a
    fresh, empty document. In Postgres a single `DELETE FROM collab_documents`
    cascades to entries, snapshots and comments via foreign keys.

At the storage layer this is `CollabStorage.deleteDoc(docId)`. Custom
[storage adapters](storage.md) implement it too, so erasure works regardless of
backend.

> **Warning: Erase when idle**
>
> Erasing a document with an active editing session interrupts it: clients
> reconnect into a new empty document. Schedule erasure for idle documents,
> or accept the reconnect for a hard, immediate takedown.

## Retention knobs

| Setting               | Default           | Effect on data retention                                                                                        |
| --------------------- | ----------------- | --------------------------------------------------------------------------------------------------------------- |
| `EMPTY_ROOM_TTL_MS`   | `600000` (10 min) | How long an idle document stays **in memory** after the last user leaves. Not deletion.                         |
| `SNAPSHOT_EVERY`      | `200`             | Snapshot cadence; smaller = more snapshot rows, faster recovery.                                                |
| `RETENTION_SNAPSHOTS` | `0` (keep all)    | `> 0` prunes the patch log to the last N snapshots, which bounds growth and shortens historical-edit retention. |
| `STORAGE`             | `memory`          | `memory` = nothing persists across restart; `postgres` = persists in your DB.                                   |

See [Configuration](configuration.md) for the full list and
[Security](security.md) for the threat model.
