# Audit log

> Canonical HTML: https://screenrig.ai/docs/audit

The audit log records every change to a project's configuration: who made it, what it changed, when, through which surface, and the value of each changed field before and after. It is searchable, chained so a rewrite is detectable, kept 400 days on every plan, and exports as CSV, XLSX or PDF.

## What is recorded

A change to a screen, a screen folder, a playlist, a media file or folder, an application, a card, a schedule or level, a campaign, a notification, a webhook, a trigger, a source, a fleet link, a report schedule, an alert mute, a member or role, an invitation, a service client, an agent connection, a single sign-on or SCIM setting, a user or group an identity provider provisions, a group binding, or a project or organization setting is one entry. So is a command sent to a screen (reboot, reload, identify, clear cache, restart app, update, toast, screenshot) and a fleet action, with the parameters it carried.

An entry holds:

| Member | Meaning |
| --- | --- |
| `at` | When the change committed (UTC) |
| `actor` | Who: `kind` `person`, `agent`, `service_client`, `staff` or `system`, with `id` and `name` |
| `approved_by`, `review_id` | The person who approved a change an agent proposed, and the review that held it |
| `source` | The surface: `dashboard`, `api`, `cli`, `mcp`, `staff`, `device`, `webhook` or `system` |
| `action` | A stable code, resource type and verb: `screen.update`, `playlist.create`, `webhook.rotate_secret` |
| `resource` | `type`, `id` and the resource's `name` after the change |
| `changes` | Each changed member: `path` (a JSON Pointer such as `/label`), `before` and `after` |
| `revision` | The revision the change produced, linked instead of copied (a playlist revision, a media version, the resource's own revision number) |
| `request_id`, `route`, `client`, `client_ip` | The request it came through |
| `seq`, `prev_hash`, `hash` | The entry's place in the chain |

Values are the stored values: names, emails, addresses, URLs and ids read as they are. A secret (a token, an API key, a password, a webhook or signing secret, a source credential, a pairing code, a receiver URL that is itself the credential) reads `(redacted)` on both sides, so a rotation shows as a change without showing either value. A value over 1 KiB is replaced by its size and SHA-256, and an entry keeps at most 100 changes and 16 KiB; `truncated` says more changed. A playlist's pages and a media file's bytes are not copied: the entry links the revision.

Card data, key/value values (including values written by webhooks and sources) and Player telemetry are data, not configuration. They are not in the audit log; the project event log records that they were written.

## Search

`GET /api/audit` lists entries newest first:

```sh
screenrig audit list --resource-type screen --resource-id scr_ID
screenrig audit list --actor-kind agent --since 2026-10-01T00:00:00Z
screenrig audit list --action 'playlist.*' --source mcp --limit 20
screenrig audit list --field /label
screenrig audit show aud_ID
```

| Filter | Meaning |
| --- | --- |
| `actor` | The person's, agent's or service client's id |
| `actor_kind` | `person`, `agent`, `service_client`, `staff` or `system` |
| `source` | `dashboard`, `api`, `cli`, `mcp`, `staff`, `device`, `webhook` or `system` |
| `resource_type`, `resource_id` | What was changed |
| `action` | An action code, or a prefix ending in `*` |
| `field` | A JSON Pointer: entries that changed this member or one below it |
| `since`, `until` | A time window |
| `cursor`, `limit` | Paging: pass `next_cursor` back as `cursor`; 1 to 200 per page |

The dashboard's Audit page, under Project, lists the same entries with these filters and shows each change as a before and after.

## Who can read it

The project's owner, an administrator of its organization and a staff member shadowing the project read it in the dashboard. An agent or service client with the project capability reads it through the API and the CLI. Changes to an organization's administrators, domains and billing settings are the organization's own log: `GET /api/organizations/{org_id}/audit`, for its administrators.

## Tamper resistance

Nothing writes, edits or deletes an entry: there is no route for it. Each entry is chained to the one before it in its project: its `hash` is the SHA-256 of the previous entry's hash, its position and its content. A few seconds after a change the entry carries its `seq` and `hash`.

```sh
screenrig audit head
screenrig audit verify
screenrig audit verify --anchor 128:3f2c0b6e9a4d81c7f5e0a2b4c6d8e0f1a3b5c7d9e1f30415263748596a7b8c9d
```

`audit verify` (`GET /api/audit/verify`) recomputes every hash from the stored content and checks each link, the continuity of `seq` and the chain head; it reports `hash_mismatch`, `prev_mismatch`, `gap`, `head_mismatch`, `anchor_mismatch` or `floor_mismatch` for the entry at fault and exits 13 when the chain does not verify. A call checks up to 5,000 entries and returns `next_seq` to continue. An anchor is a chain head (`SEQ:HASH`) you kept from an earlier export or `audit head`: verifying against it proves the history up to that entry is unchanged and was not truncated.

## Retention and deletion

Entries are kept 400 days on every plan. An entry past its time is removed oldest first, and the chain records where it now starts (`floor_seq`, `floor_hash`), so a removal anywhere else is detected. When a project is deleted its audit log is deleted with it. `screenrig project export DIR` writes the log as `log/audit.jsonl`, with the chain head in `log/audit-head.json`, so a copy outlives the project and still verifies against its anchor.

## Export

```sh
screenrig audit export --format csv --output audit.csv
screenrig audit export --format xlsx --since 2026-07-01T00:00:00Z --until 2026-10-01T00:00:00Z
screenrig audit export --format pdf --resource-type playlist --output playlist-changes.pdf
```

`GET /api/audit?format=csv|xlsx|pdf` writes the filtered log through the same renderer as every report: one row per changed field, oldest first, the last 30 days unless `since` says otherwise, up to 50,000 rows (`truncated` says when it holds only the first rows). The chain head and the retention floor are in the file, so a copy you keep is an anchor for a later `audit verify`.
