---
title: Handle a DSAR (subject access request)
path: guides/handle-a-dsar
status: published
---

# Handle a DSAR (subject access request)

A Data Subject Access Request (GDPR Article 15) asks what personal data you hold
on a person. In ScaiLog you answer it with one command: ScaiLog finds every
entry for the subject, decrypts their PI under the subject's own key, and returns
a single signed report. This needs the `dpo` capability.

## Run the DSAR

Use the subject id you emitted logs under. The CLI prints the report as JSON, or
writes it to a file with `--out`.

```bash
scailog dsar usr_8842 --out dsar.json
# => DSAR report written to dsar.json (1 entries)
```

By default the CLI targets the tenant `default`; pass `--tenant <id>` for
another tenant. The equivalent API call is:

```http
POST /v1/dsar
Authorization: Bearer <dpo-capable key>
Content-Type: application/json

{ "tenant": "default", "subject": "usr_8842" }
```

## What the report contains

The report is a JSON document describing everything ScaiLog collected for the
subject. The top level carries the provenance you need to answer the request and
to prove the answer is complete.

| Field | Meaning |
| --- | --- |
| `report_id` | ULID for this report |
| `type` | `"dsar"` |
| `tenant`, `subject` | who the report is about |
| `requested_by` | the key id that ran it |
| `generated_at` | RFC 3339 timestamp |
| `entry_count` | number of entries found |
| `time_range` | `from`/`to` timestamps of the matched entries |
| `call_sites` | the distinct `call_site_id`s that produced the entries |
| `subject_erased` | `true` if the subject has been erased |
| `entries` | one object per entry (see below) |
| `signature` | Ed25519 signature block (see "Verify the report") |

Each element of `entries` includes `event_id`, `ts`, `service`, `call_site_id`,
`level`, the plaintext `message`, the plain `fields`, and two PI-specific keys:

- `pi_values` — the **decrypted** PI values for that entry, keyed by field name.
- `pi_categories_collected` — for every PI field the entry ever carried, its
  `pi_type` and the policy action applied, e.g. `"email": "direct_identifier:encrypt"`.
  This records what category of PI was collected even for fields that were
  dropped or redacted (and therefore have no value to return).

An entry also carries `tombstoned: true` if it belongs to an already-erased
subject.

## PI decrypts per subject

`pi_values` is populated by decrypting each encrypted field with the subject's
own data-encryption key (DEK). ScaiLog only decrypts fields whose policy action
was `encrypt` and only while the subject's key still exists — so a DSAR is the
sanctioned way to read PI back, and it stops working the moment the subject is
[erased](/docs/scailog/guides/erase-a-subject). Fields that were `redact`ed,
`drop`ped, or `keep`t appear in `pi_categories_collected` but contribute no
ciphertext to decrypt.

## Verify the report

The report is signed with the server's active Ed25519 report key, and the
signature block embeds that key's `key_id` and public key — so old reports stay
verifiable even after the signing key is rotated. To check a report you were
handed, verify it offline against the server's published keys:

```bash
scailog report-key verify dsar.json
# => signature valid — signed by a published report key (key_id …)
```

`scailog report-key show` lists the published keys (active and retired), which
the verifier fetches from `GET /v1/report-keys`. Retired keys stay published, so
a report signed months ago still verifies after one or more rotations.
