Skip to content
Docs BETA

Read and manage the audit trail

Goal

For platform teams. In this task you read who did what in the control plane. You set how long events are kept. You also remove a person from the trail when someone asks you to. The audit trail is the list of the actions that people and the control plane made.

Before you start

Steps

Know what is recorded

The control plane records these actions. Each action has its own name:

Action When it is recorded
provisioner.registered A provisioner is registered.
provisioner.updated A registration is changed.
provisioner.deregistered A registration is removed.
application.registered An application is registered.
apply.requested Someone asks for an apply. The event is recorded when the apply starts, and also when the control plane refuses it because another apply holds the lock.
run.abandoned An operator or the control plane releases a run.
audit.subject_erased An operator erases a user from the trail.

A plan, a validate and every read are not recorded.

Each event holds these facts:

Field What it holds
occurredAt The time of the action.
action The name of the action.
outcome performed, or refused with a reason such as apply_in_progress.
actorKind The account type: person, service or system.
actorSubject The ID of the user, as the identity provider gives it. It is never a name. A system event has none.
authority The role that the user acted as, for example operator or owner:finance-team. It is system for an event of the control plane itself.
targetType and targetId What the action was about: a provisioner, a run, an application or an erasure.
applicationId, environment, runId The application, the environment and the run, when the action has them.
traceId The trace of the call, when the control plane sends traces.

An event is written in the same step as its action. If the control plane cannot write the event, it does not do the action, and it answers 503.

Know the events of the control plane

The control plane writes some events itself. For example, it releases a run that wrote no event for a long time. Such an event has the account type system, the role system and no user ID. The CLI prints a dash in the column of the user ID. The JSON answer has no actorSubject:

TIME                      ACTION         OUTCOME    KIND    SUBJECT  AUTHORITY  TARGET
2026-10-28T21:21:24.131Z  run.abandoned  performed  system  -        system     run:401e08b9-db9e-40e4-b42f-8844df431b31
{
  "items": [
    {
      "id": "677f492a-5e45-48fa-8c00-2483ddb3e899",
      "occurredAt": "2026-10-28T21:21:24.131Z",
      "actorKind": "system",
      "authority": "system",
      "action": "run.abandoned",
      "outcome": "performed",
      "targetType": "run",
      "targetId": "401e08b9-db9e-40e4-b42f-8844df431b31",
      "applicationId": "0008a264-25c7-4502-94bf-e237fe92e034",
      "environment": "test",
      "runId": "401e08b9-db9e-40e4-b42f-8844df431b31"
    }
  ],
  "nextCursor": null
}

An owner sees these events too, because the run belongs to an application of the owner.

Read the trail with the CLI

joomiz audit prints the newest events first. The default is 50 events, and --limit takes a number from 1 to 1000:

joomiz audit --from 2026-10-28T19:18:00Z --limit 8
TIME                      ACTION                  OUTCOME                      KIND    SUBJECT                               AUTHORITY           TARGET
2026-10-28T19:20:16.016Z  run.abandoned           performed                    person  5d0f2c6e-9a41-4e1b-8c37-2b7f6a1d9e03  operator            run:74c4e9a7-beb9-4557-bcdb-c5ccc2d730f4
2026-10-28T19:20:15.006Z  apply.requested         refused (apply_in_progress)  person  e2b84d17-6c90-4a35-9f1e-0d3a58b7c641  owner:finance-team  application:dc891440-ffd2-4471-b5a8-24fc5214b9bf
2026-10-28T19:20:14.490Z  apply.requested         performed                    person  e2b84d17-6c90-4a35-9f1e-0d3a58b7c641  owner:finance-team  application:dc891440-ffd2-4471-b5a8-24fc5214b9bf
2026-10-28T19:19:00.680Z  provisioner.updated     performed                    person  5d0f2c6e-9a41-4e1b-8c37-2b7f6a1d9e03  operator            provisioner:f690396e-d272-454e-886a-2f3e8e8cb458
2026-10-28T19:19:00.498Z  application.registered  performed                    person  e2b84d17-6c90-4a35-9f1e-0d3a58b7c641  owner:finance-team  application:dc891440-ffd2-4471-b5a8-24fc5214b9bf

The columns are the time, the action, the outcome, the account type, the user ID, the role and the target. The filters are:

Option What it keeps
--action=<name> One action, for example run.abandoned.
--application=<id> Events of one application.
--run=<id> Events of one run.
--outcome=<outcome> performed or refused.
--from=<time> and --to=<time> Events at or after the first time, and before the second. Use an ISO 8601 time.
--actor=<subject> Events of one user ID. Only an operator may use it.

This shows who released a run:

joomiz audit --from 2026-10-28T19:18:00Z --action run.abandoned
TIME                      ACTION         OUTCOME    KIND    SUBJECT                               AUTHORITY  TARGET
2026-10-28T19:20:16.016Z  run.abandoned  performed  person  5d0f2c6e-9a41-4e1b-8c37-2b7f6a1d9e03  operator   run:74c4e9a7-beb9-4557-bcdb-c5ccc2d730f4

This shows the events of one run. Here the apply of a team, and the release by an operator:

joomiz audit --run 74c4e9a7-beb9-4557-bcdb-c5ccc2d730f4
TIME                      ACTION           OUTCOME    KIND    SUBJECT                               AUTHORITY           TARGET
2026-10-28T19:20:16.016Z  run.abandoned    performed  person  5d0f2c6e-9a41-4e1b-8c37-2b7f6a1d9e03  operator            run:74c4e9a7-beb9-4557-bcdb-c5ccc2d730f4
2026-10-28T19:20:14.490Z  apply.requested  performed  person  e2b84d17-6c90-4a35-9f1e-0d3a58b7c641  owner:finance-team  application:dc891440-ffd2-4471-b5a8-24fc5214b9bf

This shows the refused applies:

TIME                      ACTION           OUTCOME                      KIND    SUBJECT                               AUTHORITY           TARGET
2026-10-28T19:20:15.006Z  apply.requested  refused (apply_in_progress)  person  e2b84d17-6c90-4a35-9f1e-0d3a58b7c641  owner:finance-team  application:dc891440-ffd2-4471-b5a8-24fc5214b9bf

Read the trail in the dashboard

The page Audit of the Platform dashboard shows the same events, newest first. The columns are Time, Action, Outcome, Authority, Actor, Subject and Target. The filters are the action, the outcome, the application ID, the run ID, and the times From and To. The button Load more events shows the next page. The filters stay in the address of the page, so you can share a search. Use the Platform dashboard describes the other pages.

The column Subject shows the user ID as plain text, (erased) after an erasure, and a dash for an event of the control plane itself. A person with the role owner:<team> also sees a dash for the event of an operator.

See what an owner sees

A person with the role owner:<team> reads the events of the applications of that team. The ID of an operator is hidden from that person. The CLI shows (hidden) in the column:

TIME                      ACTION                  OUTCOME                      KIND    SUBJECT                               AUTHORITY           TARGET
2026-10-28T19:20:16.016Z  run.abandoned           performed                    person  (hidden)                              operator            run:74c4e9a7-beb9-4557-bcdb-c5ccc2d730f4
2026-10-28T19:20:15.006Z  apply.requested         refused (apply_in_progress)  person  e2b84d17-6c90-4a35-9f1e-0d3a58b7c641  owner:finance-team  application:dc891440-ffd2-4471-b5a8-24fc5214b9bf
2026-10-28T19:20:14.490Z  apply.requested         performed                    person  e2b84d17-6c90-4a35-9f1e-0d3a58b7c641  owner:finance-team  application:dc891440-ffd2-4471-b5a8-24fc5214b9bf
2026-10-28T19:19:00.498Z  application.registered  performed                    person  e2b84d17-6c90-4a35-9f1e-0d3a58b7c641  owner:finance-team  application:dc891440-ffd2-4471-b5a8-24fc5214b9bf

An owner cannot filter by user ID. The control plane answers 403 for --actor.

Read the trail with HTTP

The call is GET /audit-events. The query names are in snake_case: application_id, run_id, action, outcome, actor_subject, from, to, cursor and page_size. The page size is from 1 to 100, and the default is 50. When more events exist, the answer has a nextCursor. Send it as cursor for the next page. The control plane sends each answer on one line. The examples here have line breaks, so that you can read them. The field actorSubject is missing for an event of the control plane itself and for the event of an operator that an owner reads. For an erased event it is missing too, and the field actorErased is true. Control plane API reference shows the call with a token that is not on the command line.

curl -s -w '\nHTTP %{http_code}\n' --variable '%JOOMIZ_TOKEN' --expand-header 'Authorization: Bearer {{JOOMIZ_TOKEN}}' "$JOOMIZ_SERVER_URL/audit-events?from=2026-10-28T19:18:00Z&action=run.abandoned&page_size=1"
{
  "items": [
    {
      "id": "e64214df-2097-47e1-b0dd-003a31b9f86d",
      "occurredAt": "2026-10-28T19:20:16.016Z",
      "actorKind": "person",
      "actorSubject": "5d0f2c6e-9a41-4e1b-8c37-2b7f6a1d9e03",
      "authority": "operator",
      "action": "run.abandoned",
      "outcome": "performed",
      "targetType": "run",
      "targetId": "74c4e9a7-beb9-4557-bcdb-c5ccc2d730f4",
      "applicationId": "dc891440-ffd2-4471-b5a8-24fc5214b9bf",
      "environment": "test",
      "runId": "74c4e9a7-beb9-4557-bcdb-c5ccc2d730f4"
    }
  ],
  "nextCursor": null
}
HTTP 200

A bad value answers 400 with a text that names the rule:

{"detail":"audit query is invalid: outcome must be one of performed, refused","status":400,"title":"Invalid audit query"}
HTTP 400

Set how long events are kept

The control plane deletes events that are older than joomiz.audit.retention. The value is from 90 to 3650 days. The default is 400 days. The environment variable is JOOMIZ_AUDIT_RETENTION, for example 400d. A value outside the range stops the start of the control plane, and the error names the setting. A job runs every hour and deletes the old events in small steps. Settings reference lists the settings of the job.

Erase a person from the trail

A person can ask to be removed from the records. You erase the user ID of that person. The events stay, with their action, their target, their role and their time. Only the ID is removed, and the events show (erased) in its place.

  1. Get the user ID of the person from your identity provider. Write the request and the date in your own records. Joomiz does not keep the erased ID.
  2. Find the events of that ID. This shows what the erasure will change:
joomiz audit --from 2026-10-28T19:18:00Z --actor 5d0f2c6e-9a41-4e1b-8c37-2b7f6a1d9e03
TIME                      ACTION               OUTCOME    KIND    SUBJECT                               AUTHORITY  TARGET
2026-10-28T19:20:16.016Z  run.abandoned        performed  person  5d0f2c6e-9a41-4e1b-8c37-2b7f6a1d9e03  operator   run:74c4e9a7-beb9-4557-bcdb-c5ccc2d730f4
2026-10-28T19:19:00.680Z  provisioner.updated  performed  person  5d0f2c6e-9a41-4e1b-8c37-2b7f6a1d9e03  operator   provisioner:f690396e-d272-454e-886a-2f3e8e8cb458
  1. Ask another operator to send the erasure. The body has exactly one field, subject:
curl -s -w '\nHTTP %{http_code}\n' --variable '%JOOMIZ_TOKEN' --expand-header 'Authorization: Bearer {{JOOMIZ_TOKEN}}' -X POST -H 'Content-Type: application/json' -d '{"subject": "5d0f2c6e-9a41-4e1b-8c37-2b7f6a1d9e03"}' "$JOOMIZ_SERVER_URL/audit-erasures"
{
  "id": "c0799010-9fba-4c48-8415-6637281cb15b",
  "erasedEvents": 2
}
HTTP 201

The answer is 201 with the ID of the erasure and the number of events that it erased. If the call stops before it ends, send the same call again. It finishes the work. A call that finds nothing left answers 201 with "erasedEvents": 0:

{
  "id": "8e6b966f-308d-4c53-a68b-68b5b66248fe",
  "erasedEvents": 0
}
HTTP 201
  1. Check the result. The events show (erased), and the filter finds nothing:
TIME                      ACTION                  OUTCOME                      KIND    SUBJECT                               AUTHORITY           TARGET
2026-10-28T19:20:34.554Z  audit.subject_erased    performed                    person  c19a7e44-3b52-4f08-a6d1-7e0b9c53f2a8  operator            audit_events:8e6b966f-308d-4c53-a68b-68b5b66248fe
2026-10-28T19:20:34.536Z  audit.subject_erased    performed                    person  c19a7e44-3b52-4f08-a6d1-7e0b9c53f2a8  operator            audit_events:c0799010-9fba-4c48-8415-6637281cb15b
2026-10-28T19:20:16.016Z  run.abandoned           performed                    person  (erased)                              operator            run:74c4e9a7-beb9-4557-bcdb-c5ccc2d730f4
2026-10-28T19:20:15.006Z  apply.requested         refused (apply_in_progress)  person  e2b84d17-6c90-4a35-9f1e-0d3a58b7c641  owner:finance-team  application:dc891440-ffd2-4471-b5a8-24fc5214b9bf
2026-10-28T19:20:14.490Z  apply.requested         performed                    person  e2b84d17-6c90-4a35-9f1e-0d3a58b7c641  owner:finance-team  application:dc891440-ffd2-4471-b5a8-24fc5214b9bf
2026-10-28T19:19:00.680Z  provisioner.updated     performed                    person  (erased)                              operator            provisioner:f690396e-d272-454e-886a-2f3e8e8cb458
2026-10-28T19:19:00.498Z  application.registered  performed                    person  e2b84d17-6c90-4a35-9f1e-0d3a58b7c641  owner:finance-team  application:dc891440-ffd2-4471-b5a8-24fc5214b9bf
no audit events found

The erasure is itself recorded, with the ID of the operator who sent it. That ID stays until the retention job deletes the event:

TIME                      ACTION                OUTCOME    KIND    SUBJECT                               AUTHORITY  TARGET
2026-10-28T19:20:34.554Z  audit.subject_erased  performed  person  c19a7e44-3b52-4f08-a6d1-7e0b9c53f2a8  operator   audit_events:8e6b966f-308d-4c53-a68b-68b5b66248fe
2026-10-28T19:20:34.536Z  audit.subject_erased  performed  person  c19a7e44-3b52-4f08-a6d1-7e0b9c53f2a8  operator   audit_events:c0799010-9fba-4c48-8415-6637281cb15b

After a restore from a backup

A restore from a backup brings back the events as they were on the day of the backup. The erased IDs return with them. Send the erasure requests again that you received after the date of the backup. Use your records of the requests, because Joomiz does not keep the erased ID.

Check

Troubleshooting

Symptom Cause Fix
403 and Operator role required for --actor or for an erasure You have only the role owner:<team>. Sign in as an operator.
403 and an operator cannot erase their own subject The ID in the body is your own. Ask another operator to send the erasure.
400 and Invalid audit erasure The body is not exactly {"subject": "..."}, or the ID is empty or too long. Send one field subject with 1 to 255 printable characters.
400 and Invalid audit query A filter has a bad value. Read the text. It names the rule.
503 and audit-unavailable The control plane could not write the audit event. Try again later. The action was not done.
--limit must be a number from 1 to 1000 The number is outside the range. Use a smaller number.
The control plane does not start and names joomiz.audit.retention The value is below 90 days or above 3650 days. Set a value in the range.

Next

Check that the control plane is healthy in Check health and follow a run.