# Audit log

An append-only record of who did what, when, and to which resource. Nothing in Opafra edits or deletes an entry once it is written.

Source: https://opafra.com/docs/governance/audit-log

---

Every consequential action writes an entry: who did it, what they did, which resource it
touched, when, and from where. Entries are never updated and never deleted.

## What an entry holds

| Field | What it is |
|---|---|
| Actor | The user id, plus their email **as it was at the time** |
| Action | A dotted verb, such as `execution.start` or `ssh_key.generate` |
| Resource | The type and id of the thing affected |
| Summary | A one-line human description |
| Metadata | Structured detail specific to the action |
| IP address | Where the request came from |
| Timestamp | When it happened |

The email is stored as a snapshot rather than looked up through the user record. That is
deliberate: deleting a user must not turn their history into a row of blanks. The trail
still says who did it after they have left.

## What gets recorded

Every area that changes state is covered:

- **Runs**: starting, aborting, and every approval decision
- **Approvals**: approve, reject, and the plan revision each decision applied to
- **Inventory**: creating, updating and deleting servers
- **Credentials**: generating, importing, updating and deleting SSH keys
- **Secrets**: connecting and disconnecting providers, and reading a secret
- **Access**: environment grants and revocations, access requests and their decisions
- **Membership**: invitations, removals, and role changes
- **Git**: connections, sources, syncs, and webhook state
- **Sessions**: opening a web SSH session
- **Organisation**: settings changes, switching, leaving, deletion

> **Note**
>
> `secret.read` is on that list. Resolving a secret reference is itself an event, so the
> trail answers not only who changed what but who caused a credential to be read.

## Approvals carry the revision

An approval entry records both the plan revision that was **requested** and the revision
at the moment of **decision**, plus whether they differed.

Without that pair, the trail can say a run was approved but cannot say what was approved.
With it, a plan edited between the request and the decision is visible in the record months
later, which is the question an auditor actually asks.

## Append-only

Nothing in Opafra updates or deletes an audit entry. There is no edit path, no cleanup
job, and no endpoint that would let one be rewritten.

> **Important**
>
> This means the log grows and is meant to. It is a record, not a cache, and an audit trail
> that can be trimmed by whoever is being audited is not an audit trail.

## One honest limitation

Writing an entry is deliberately allowed to fail without failing the action it describes.
If the audit write errors, the error is logged and the original operation still completes.

The reasoning: an audit failure that blocked a production run would turn a bookkeeping
problem into an outage, and refusing to run because a log row could not be written is
worse than running with a gap. It does mean the trail is not a guaranteed-complete ledger
under database failure. It is complete in normal operation, and honest about the edge.

## What it does not cover

**Sign-in events are not recorded.** The log covers what people did once inside, not the
authentication itself. Membership changes, role changes and token creation are all
recorded; a successful or failed sign-in is not.

## Reading it

The audit log is **Admin and above**. Operators change production; reading the record of
who changed what is a separate job, matching how the [roles](/docs/access/organisations-and-roles)
are split.

Entries are scoped to your tenant. An organisation sees its own events, a personal
workspace sees its own, and neither can see the other.

Filter by action, by resource, by actor, or search the summaries, then narrow by date.
The common questions are "what did this person do", "what happened to this server", and
"what happened between these two times", and the indexes exist for all three.

## What is never in it

Secret values. Metadata carries identifiers, fingerprints, revision numbers and decisions,
never credentials. An SSH key's audit entry names its fingerprint and type, not any part
of the key.

## Next steps

- [Safety model](/docs/security/safety-model) for how the audit log fits with the other controls
- [Organisations and roles](/docs/access/organisations-and-roles) for who can read it
