System — audit#
The Audit tab is the agent's own record of what was asked of it: who, from where, with which role, what operation, and whether it worked. It is one of three separate records the product keeps, and the narrowest of the three in scope and the strongest in attribution.
GET /api/audit?limit=300 returns the newest entries first; the tab polls every 10 s.
The default limit is 300 and the tab uses it.
What an entry contains#
| Field | Meaning |
|---|---|
timestamp | RFC 3339, in UTC, stamped by the agent at the moment of the write. |
actor | The account name, the token name, or reconcile-loop for the desired-state loop. |
role | viewer, operator, admin, or system for the loop. |
remote_ip | The client address, as the agent determined it. |
operation | The operation name — the table below. |
path | Configuration path or subject, when the operation has one. |
commands | Rendered commands. Populated by the reconcile loop; the browser-driven paths do not fill it. |
success | Whether it worked. |
error | The router's own words when it did not. |
The table on screen shows five of those: when (as a relative time), the operation, the
path, an ok/failed badge, and the error.
Every operation that is recorded#
| Operation | Written by |
|---|---|
login, login (oidc), logout, denied … | Authentication, including refusals. |
bootstrap-admin | The first-run administrator being created. |
change-password, totp-enable, totp-disable, revoke-session | Account changes. |
create-user, delete-user, create-token, revoke-token, update-settings | Administration. |
oidc-link, oidc-provision, oidc-unlink | Single sign-on identity handling. |
configure, commit, commit-confirm, save, rollback, config-load | The configuration plane. |
power-reboot, power-poweroff | Power. |
image-add, image-delete, image-set_default | Boot images. |
app-pull, app-prepare, app-update, app-restart, app-image-delete | App actions. |
feature-enable, feature-disable | Feature modules. |
license-set, license-remove | Licence changes. |
fleet-configure | A configuration write to a remote router in fleet mode. |
Both outcomes are recorded. A failed commit, a refused login and a rollback the router rejected are all entries, with the reason in the error column — which is what makes this usable as a record rather than as a success log.
Where it is stored#
An append-only file, one JSON object per line, at audit.jsonl in the agent's data
directory — /config/wheelhouse/audit.jsonl on an appliance. Each entry is written and
fsynced immediately.
That last detail is deliberate: an audit log whose last entries are lost to the power cut
that followed the action they describe is not one you can testify from, and the cost is a
few hundred bytes — agent/store.go.
| Property | Value |
|---|---|
| File | <data-dir>/audit.jsonl, mode 0600 |
| Rotation | At 8 MB, keeping exactly one previous generation as audit.jsonl.1 |
| Maximum on disk | Twice the rotation size |
| Served from memory | The newest entries, up to the retention setting, capped at 10 000 |
| Retention setting | audit_retention on Agent settings; default 2000, floor 100 |
The in-memory tail is what GET /api/audit serves. Older entries stay in the file and
are readable there; they are not reachable through the API. Setting audit_retention
above 10 000 is honoured as far as 10 000 and logs a warning saying exactly that,
because a number the settings page echoes back and the agent does not honour is worse
than a refusal.
On start-up the agent reads back the tail, previous generation first, so a restart shortly after a rotation still serves a full window. A line torn by a power cut is skipped; the entries either side of it are still good.
What it cannot do#
Four limits, stated plainly because each of them is the sort of thing a compliance question turns on.
- It cannot leave the box. There is no export, no forwarding, no syslog sink and no API for shipping it anywhere. The journal can be shipped to a collector (Logs); the audit log cannot.
- It is not tamper-evident. It is a plain file with no hash chain, no signature and no external anchor. Anyone with root on the router can edit it.
- It records what went through the agent, and only that. A change made at the console appears in the commit history and the journal, and not here.
- Entries beyond the retention window are not served. They are in the file. Reading
them means reading
audit.jsonlon the box.
Reading it from the API#
curl -sk -H "Authorization: Bearer $T" \
"https://<router>:8443/api/audit?limit=50" | python3 -m json.tool[
{
"timestamp": "2026-09-02T10:14:07Z",
"actor": "kate",
"role": "operator",
"remote_ip": "192.0.2.31",
"operation": "commit",
"success": true
}
]The route needs only the viewer role, which is deliberate: everyone who can see the router can see who changed it.
On the box itself, the file is the API's superset:
sudo tail -n 50 /config/wheelhouse/audit.jsonl | python3 -m json.toolSee also#
- System — history — what each commit changed.
- Logs — the journal, and the one record that can be shipped off-box.
- Agent settings — the retention setting.
- Backup and restore — the audit log is inside
/config, so a/configbackup carries it.
Checked against agent/store.go,
agent/main.go, agent/admin.go,
agent/authhttp.go, agent/apps.go,
agent/parity.go, agent/license.go,
agent/desired.go, agent/oidc.go,
ui/src/pages/System.tsx.