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

# Audit Log

> Who changed what on the control plane, and what is deliberately never recorded

Relay keeps an append-only record of administrative activity: every change made through the control API, every request that was refused, and every login. It answers "who changed this, when, and from where" without an external tool.

Inference traffic is not audited here; every inference request already produces a [usage event](https://docs.wyolet.com/concepts/usage.md).

## What is recorded

One row per HTTP request against the control API, when the request earns one:

| Situation | Recorded |
| - | - |
| A mutating action (create, update, delete, rotate, generate, reload, …) | Yes, allowed or refused |
| A request refused by authorization | Yes, with `status: denied` |
| Login (successful or failed) and logout | Yes |
| A read that succeeded | No |

Each row carries the actor (user id and name, session id, client IP, or `anonymous` with the attempted username for a failed login), the action, the target resource with its owner and project or team scope, the outcome (`allowed`, `denied` or `error`, plus the HTTP status), and the request's method, path and id. An apply records one row per change it made.

The `relay secret` commands, which change the [secret key ring](https://docs.wyolet.com/concepts/secret-keys.md) directly in Postgres, write rows too: the actor kind is `cli`, its name the operating-system user, the status the one the control API would have answered, and the path the command (`relay secret rotate`). They write synchronously and fail the command when the write fails. Writes made by a [SCIM](https://docs.wyolet.com/concepts/scim.md) client carry the actor kind `scim-client` and the client's name.

## What is never recorded

Values. A write records only the JSON paths it touched, such as `metadata.displayName` and `spec.enabled`, never the content of those fields before or after. Creates and deletes record `*` rather than list every field.

Paths that name a credential are dropped entirely: `value`, `keyHash`, `previousKeyHash`, `password`, `passwordHash`, `token` and `secret`. A rotation is visible as an event, but the audit log never becomes a map of where credentials live.

## Reading it back

```bash theme={null}
curl -H "Authorization: Bearer $RELAY_ADMIN_TOKEN" \
  "http://localhost:8081/api/audit?resource_kind=policy&status=denied&limit=50"
```

| Parameter | Matches |
| - | - |
| `actor_id`, `actor_name` | The acting user |
| `action` | Any of the given actions, such as `policies.update` (repeat the parameter for more than one) |
| `resource_kind` | Any of the given kinds, in the singular form (`policy`, `host-key`) |
| `resource_id` | One resource |
| `scope` | Any of the given scopes, `project:<id>` or `team:<id>` |
| `status` | `allowed`, `denied` or `error` |
| `from`, `to` | An RFC 3339 time window |
| `limit` | Page size; default 100, maximum 10,000 |
| `cursor` | The `next_cursor` of the previous response |

Results are newest first. Reading the audit log needs `read` on `audit`: an admin has it, and so does the built-in `auditor` role.

## Retention

The `audit` settings section holds `retentionDays` (default `365`). Rows older than the window are removed by an hourly pass; a change takes effect on the next pass, without a restart. Set it to `0` to keep rows forever, for example when you archive the `audit_events` table yourself. Only an admin may change this section.

```bash theme={null}
curl -X PUT -H "Authorization: Bearer $RELAY_ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"retentionDays": 90}' \
  http://localhost:8081/api/settings/audit
```

## Delivery guarantees

Audit writes from the control API are asynchronous and bounded: an admin request never waits on the audit store. When the queue is full, or a batch write fails twice, the rows are dropped rather than blocking the control plane. Drops are counted in `relay_audit_dropped_total`; alert on it if you need a complete audit trail.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.