Repository navigation
Conversation
/security/audit-log lists the unified audit trail, newest first, for org admins. - A new Activity section in the Security rail links to it. The page reads the session's grants first: without audit_log:read it says who can read the log and asks the API for nothing. - Events are grouped by day. A row shows when, the platform, who acted, the platform's action name with its category, the target and a mark for a refusal. Opening a row shows everything recorded about the event and its payload. - Category, outcome and actor filters live in the URL. The platform filter appears once a second platform writes events. - Load more continues from the last event shown. A failed page keeps the list and offers a retry. - jsonBlock moves from the settings audit log into the page kit, and both pages use it. - Six lab scenarios under /lab/security?state=audit-log* and a scene test.
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the
|
This comment has been minimized.
This comment has been minimized.
- A next page is neither asked for nor accepted while other filters are loading, and an answer has to match the cursor it was asked from. - A session read that fails no longer locks an admin out: the API is asked and its refusal is what shows the admins-only state. - The rows dim while other filters load. - Nothing the row cuts off is lost: full values on hover, and the detail lists the action, actor, actor type and target. The refusal reason shows under the action. - Anything but a success is marked, so an unknown outcome does not read as one. - The outcome filter offers what a platform writes today. A footnote says allowed reads are in the Settings audit log, not here. - Kit: tableGroupRow (the releases list moves onto it), dayEyebrow, refusedBadge and expandChevron (the settings audit log moves onto them), prettyJson, and a controlled value on nativeSelect so a filter change keeps keyboard focus. - The page entry takes the auth mode as an argument so the hosted gate is tested.
This comment has been minimized.
This comment has been minimized.
🍁 Maple PR previewNote Preview resources were removed when the preview label came off. Final commit |
Its label replaces its content for a screen reader, and the content was the only place the time was. The label now carries the action and the timestamp.
This comment has been minimized.
This comment has been minimized.
A screen reader heard "All outcomes, Denied": the select was named after its clear option. They are now Category and Outcome. The scene test's second page also carries a cursor on its own last event instead of the first page's.
- An empty answer to the previous filters no longer shows as "no matches" while the new filters load: the list shows loading rows until they answer. - Row times tick: the clock is read every minute. - A Maple event's id links to the same entry in the Settings audit log, and the Settings lookup field now opens an entry when its alog_ id is pasted. - The footnote opens the Data access tab, where the reads it names are. - The lab fixture records a refused request the way the API now does.
This comment has been minimized.
This comment has been minimized.
Tested on the preview, with tracesA separate agent signed in to Passed
Found and fixed since (
Not fixed here, existing behavior
Not tested: a non-admin viewing the page (the only account is an admin; a key gets 403 and no session gets 401 from the endpoint), a "Yesterday" heading, and arrow keys on a closed native select in headless Chromium. Traces (
|
| Span | Count | p50 | p95 | max |
|---|---|---|---|---|
browser GET of the events endpoint |
65 | 315 ms | 2061 ms | |
web worker GET /_api/* |
66 | 156 ms | 1430 ms | |
maple-api GET /internal/audit-trail/events |
72 | 136 ms | 394 ms | |
AuditTrailService.list |
69 | 48 ms | 105 ms | 148 ms |
OrgMembersService.listMembers |
55 | 94 ms | 186 ms | 269 ms |
AuditTrailService.write (consumer, off the response path) |
11 | 296 ms | 330 ms | 333 ms |
AuditEvents.processBatch |
47 | 317 ms | 663 ms | 694 ms |
- One warehouse query per list request. No N+1.
- The API answers in 136 ms at the median. Most of that is the member directory read that names Maple's own users (94 ms), not the warehouse (48 ms). It could be cached if this page gets heavy use.
- The slow tail is between the browser and the API, not in this code: the same 1.4 to 2.6 second p95 shows on every other request the shell makes on this preview (
LoadInfraSurfaces,LoadCustomer), which points at cold preview workers. - Slowest list traces:
d6c0e5eb417298ef1d448aef13fc5f1e(148 ms),7ad1ffeb979b811a9e32145ee2082459(144 ms).
… audit log filter The field was rebuilt from the URL on every route change, so typing an actor and then picking a category threw the text away. It now follows the URL only when it holds nothing unapplied, which keeps Back clearing an applied actor.
… window Retesting on the preview showed the Settings lookup change did not work: that page cannot fetch an entry by id alone, so pasting an alog_ id set the URL and opened nothing. The lookup is back to what it was. The link from the Security page now goes through the same builder as the Settings page's own entry links (the minute ending at the entry, changes and reads both shown), so it finds old entries too.
Retest on the preview (
|
| Item | Result |
|---|---|
| No "no matches" while the next filter loads (response delayed 3 s) | Pass: loading rows, then 50 events |
A scope refusal is a Permissions event with a POST /v2/dashboards style target and its reason |
Pass |
| Event id links to the same entry in the Settings audit log | Pass |
Pasting an alog_ id into the Settings lookup opens the entry |
Fail |
| Footnote opens the Data access tab | Pass |
| Row times tick without a reload | Pass |
| Selects are named Category and Outcome; a row toggle names the action and time | Pass |
| Keyboard change of a select keeps focus on it across the URL change; Back restores the value | Pass |
| Regression: new events appear, detail opens, Load more 50 to 68 | Pass |
Correction to my earlier comment. I wrote that the Settings lookup now opens an entry from its alog_ id. It did not: that page has no way to fetch an entry by id alone, so the lookup set the URL and opened nothing. 9f6d7fc removes that change and leaves the lookup as it was. The link from this page now goes through the same builder as the Settings page's own entry links (the minute ending at the entry, changes and reads both shown), so it also finds entries that are no longer on the first page. That last part is covered by a unit test but has not been re-run on the preview.
Also since the retest: an actor typed but not yet applied survives a change of another filter (d3a9f40).
Maple review🟢 Confidence 10/10 · safe to merge. The admin gate, draft-carry-over and stale-cursor guards are each pinned by a scene test, and the moved kit pieces match the markup they replace. Adds the admin-only
Fixed since the last review
Production impactOpen errors in the changed files (2)
After this merges, Maple checks whether they stop.
|
Final check on the preview (
|

Second layer of the unified audit log, on top of #2510: the page that shows it.
What changed
/security/audit-log, linked from a new Activity section in the Security rail. Events are newest first and grouped by day. Each row shows when, who acted, the platform's action name with its category, the target, and a mark for anything that did not succeed, with the reason under the action.audit_log:readit says who can read the log and asks the API for nothing. If the session read fails, the API is asked and its refusal decides, so an admin is never locked out by a failed session read.Kit changes, each with the existing copies moved onto it:
tableGroupRowin the table kit (the releases list's day rows).dayEyebrow,refusedBadge,expandChevron,jsonBlockandprettyJsonin the page kit (the settings audit log).nativeSelecttakes a controlledvalue, so a filter change keeps keyboard focus instead of remounting the select.The rail entry is visible to everyone who can open the Security hub. I did not add a separate rollout flag: the hub itself is still behind
securityAudit.Screenshots
The two existing pages that moved onto the shared kit pieces. Each pair of files is byte-identical.
Verified
oxlint -c .oxlintrc.effect.jsonis clean on the changed files.ui:indexis current,ui:patterns:checkpasses, the UI kit baseline is unchanged (689)./lab/security?state=audit-log,-detail,-empty,-filtered-empty,-member,-error) in headless Chromium at 1440 and 390 wide.preview:webdeploy against real rows by a separate agent; results and the trace numbers are in the comments below. The API answers the list in 136 ms at the median with one warehouse query per request.Not verified on the preview: a non-admin viewing the page (the only preview account is an admin). That state is covered by scene tests and the lab.