Audit log: the record panel, an honest count line, no nested scroll #342

Merged
Cordy merged 12 commits from audit-record into main 2026-08-27 22:41:51 +00:00
Owner

Mockup-approved implementation of the Audit log page (both tabs).

Backend

Query already computed the number of matching events (the scan has to visit every candidate line either way) and discarded it at the truncation, so the panel could only ever say "200 shown" and never what it was 200 of. QueryWithTotal returns it; Query delegates. No extra I/O.

Info() describes the record itself: event count, oldest event, month-file directory, retention. dir and retainMonths were unexported with no accessor, which is the whole reason this was previously filed as "blocked on the backend". Counting is a newline scan cached on (size, mtime), so a rotated month is read once per process and only the live month is rescanned.

New endpoint GET /api/v1/admin/audit/info, 404 when there is no logger — the same signal the query endpoint gives, which the panel already reads as "not enabled on this server".

Tests in internal/audit/record_test.go: total beyond the limit, total under a filter, counts/months/oldest, cache invalidation after an append, empty dir, nil logger.

Frontend

  • The clipped last row was not a missing scrollbar. ul.plainlist caps every list at 45vh with its own overflow-y:auto; inside an already-scrolling page that is a nested scroll region, and the macOS overlay thumb stays invisible until touched. The admin rule already won for overflow; only max-height survived. Now overridden — the page scrolls, the log does not.
  • #audit-count was never in SET_SLOTS. The filter and the list are moved into the admin shell at runtime; the count line between them was not, so it stayed parked inside the closed <dialog> and has never once been visible on the admin page. Added.
  • Count line now reads "200 shown of 12,481 matching events". The sentence stays one translatable string because word order around the two numbers differs per language.
  • audCap em dash removed, per the standing copy rule.
  • Settings gains "The record": Events / Oldest / Written to / Retention, with the Append-only chip, plus the note explaining why export is a file copy rather than a download button.
  • The record table reuses the Overview's table rules by widening those selectors rather than copying the declarations, so the two cannot drift apart.
  • Refusal chips read denied · <reason>, where the reason stays the server's own recorded words.

All new copy is in en/de/fr/it.

Mockup-approved implementation of the Audit log page (both tabs). ## Backend `Query` already computed the number of matching events (the scan has to visit every candidate line either way) and discarded it at the truncation, so the panel could only ever say "200 shown" and never what it was 200 *of*. `QueryWithTotal` returns it; `Query` delegates. No extra I/O. `Info()` describes the record itself: event count, oldest event, month-file directory, retention. `dir` and `retainMonths` were unexported with no accessor, which is the whole reason this was previously filed as "blocked on the backend". Counting is a newline scan cached on (size, mtime), so a rotated month is read once per process and only the live month is rescanned. New endpoint `GET /api/v1/admin/audit/info`, 404 when there is no logger — the same signal the query endpoint gives, which the panel already reads as "not enabled on this server". Tests in `internal/audit/record_test.go`: total beyond the limit, total under a filter, counts/months/oldest, cache invalidation after an append, empty dir, nil logger. ## Frontend - **The clipped last row** was not a missing scrollbar. `ul.plainlist` caps every list at `45vh` with its own `overflow-y:auto`; inside an already-scrolling page that is a nested scroll region, and the macOS overlay thumb stays invisible until touched. The admin rule already won for `overflow`; only `max-height` survived. Now overridden — the page scrolls, the log does not. - **`#audit-count` was never in `SET_SLOTS`.** The filter and the list are moved into the admin shell at runtime; the count line between them was not, so it stayed parked inside the closed `<dialog>` and has never once been visible on the admin page. Added. - **Count line** now reads "**200** shown of **12,481** matching events". The sentence stays one translatable string because word order around the two numbers differs per language. - **`audCap`** em dash removed, per the standing copy rule. - **Settings gains "The record"**: Events / Oldest / Written to / Retention, with the `Append-only` chip, plus the note explaining why export is a file copy rather than a download button. - The record table reuses the Overview's table rules by **widening those selectors** rather than copying the declarations, so the two cannot drift apart. - Refusal chips read `denied · <reason>`, where the reason stays the server's own recorded words. All new copy is in en/de/fr/it.
scout: dump audit UI regions of index.html
Some checks failed
scout-audit.yml / scout: dump audit UI regions of index.html (push) Failing after 0s
ecbe2710a0
Query already computed the number of matching events -- the scan has to
visit every candidate line either way -- and threw it away at the
truncation, so the admin panel could only say "200 shown" and never what
it was 200 of. QueryWithTotal returns it; Query delegates.

Info answers the questions an auditor asks before reading any events:
how many are there, since when, where do the files live, how long are
they kept. Counting is a newline scan cached on (size, mtime), so a
rotated month is read once per process and only the live month is
rescanned.
audit log: the record panel, an honest count line, no nested scroll
All checks were successful
ci / test-and-build (pull_request) Successful in 38s
274834482b
The last row looked cut off because ul.plainlist caps every list at 45vh
with its own scrollbar; inside an already-scrolling page that is a nested
scroll region whose macOS overlay thumb is invisible until touched. The
page scrolls, the log does not.

The count line said only '200 shown' because Query discarded the match
total -- and it was invisible anyway: #audit-count was never added to
SET_SLOTS, so it stayed parked in the closed dialog while the filter and
the list moved into the admin shell.

Settings gains 'The record': events recorded, oldest event, the month-file
path, and the retention policy, plus the note explaining why export is a
file copy rather than a download button.
Cordy merged commit b31b690e28 into main 2026-08-27 22:41:51 +00:00
Cordy deleted branch audit-record 2026-08-27 22:41:53 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: Cordy/Cairn#342
No description provided.