Skip to content

feat(audit): activity audit log + skills UI improvements - #10

Merged
dfradehubs merged 9 commits into
mainfrom
feat/audit-log
Jul 24, 2026
Merged

dfradehubs merged 9 commits into
mainfrom
feat/audit-log

Conversation

@dfradehubs

Copy link
Copy Markdown
Owner

Summary

Adds an Audit section under /admin: a detailed log of user activity — who ran what, the prompt sent, the response returned, errors, duration, and which agent/MCP/skill/group was used — with retention configurable from the central admin settings. Also bundles the requested skills reader UI improvements.

Distinct from Observability (chat_events), which stores only aggregate metrics with no content. Audit stores content (truncated) for review/compliance.

Backend

  • audit_events table (migration 000036): content + metadata, own retention.
  • models.AuditEvent + filter, postgres repo (Insert/List/Cleanup), repository.AuditEventRepository.
  • audit.RecordEvent helper: truncates prompt/response to a configurable limit and inserts asynchronously (never blocks the request hot-path).
  • Capture on all surfaces:
    • MCP facade (Claude/Cursor/Codex): agent chat, MCP tool, skill read, group debate
    • Web/API: agent proxy chat, MCP chat (single + multi), group debate turns
  • Retention in central settings (Admin → General Configuration): observability_retention_days (migrated from YAML), audit_retention_days, audit_max_content_chars. The cleanup job now reads retention from settings for both chat_events and audit_events.
  • API: GET /api/admin/audit with filters (range, user, group, category, request_id) + offset pagination.

Web

  • /admin/audit: activity table with expandable rows (prompt / response / error), filters (range, member, category, request ID) and pagination; nav entry.
  • Skills reader improvements: wider panel, proper scroll, collapsible sidebar section (like MCP), and frontmatter stripped from the displayed content.

Test plan

Verified end-to-end against the Docker dev stack:

  • Migration 000036 applies (audit_events table)
  • Skill read via MCP facade → event captured (source=mcp, skill_read, content)
  • Agent chat via web → event captured (source=web, agent, chat, prompt+response)
  • GET /api/admin/audit returns events with filters/pagination
  • Retention settings exposed in GET /api/admin/settings (observability + audit + max content)
  • go build, go vet, go test ./internal/{handlers,mcpserver,models,settings} green; web npm run lint 0 errors
  • MCP-web and group-web capture use the same helper/pattern as the verified paths, but were not exercised individually (need an MCP server + a group with a moderator configured in dev)
  • Visual QA of /admin/audit and the skills reader (browser extension not connected during dev; pages compile and build)

Notes

  • Privacy: audit stores prompts/responses → mitigated with configurable truncation, configurable retention, and admin-only access. No PII scrubbing / SIEM export (out of scope until requested).

🤖 Generated with Claude Code

Audit log capturing per-interaction detail (prompt, response, error, duration,
resource) for admin review, plus configurable retention from central settings.

Backend:
- migration 000036: audit_events table (content + metadata, distinct from the
  metrics-only chat_events)
- models.AuditEvent + AuditEventFilter, postgres repo (Insert/List/Cleanup),
  repository.AuditEventRepository interface
- audit.RecordEvent helper: truncates content to a configurable limit and
  inserts asynchronously (never blocks the hot-path)
- captured on the MCP facade (Claude/Cursor/Codex): agent chat, MCP tool,
  skill read, group debate
- settings: observability_retention_days (migrated from YAML), audit_retention_days,
  audit_max_content_chars — all editable in Admin → General Configuration; cleanup
  job now reads retention from settings for both chat_events and audit_events
- GET /api/admin/audit with filters (range, user, group, category, request_id, paging)

Web:
- /admin/audit page: activity table with expandable rows (prompt/response/error),
  filters and pagination; nav entry
- skills reader: wider panel, proper scroll, collapsible sidebar section,
  frontmatter stripped from displayed content
Instrument the web/API surfaces with the same audit.RecordEvent helper used by
the MCP facade: agent proxy chat (proxy.go), MCP chat single + multi (mcp.go),
and group debate turns (group_chat.go). Audit repo injected into ProxyHandler
and MCPHandler via setters (both already use a variadic chatEventRepo).
…ges, Max selector

- drop the request_id filter (not populated yet)
- time range options: 30m/1h/6h/12h/24h/7d
- add a Max results selector (50/100/250/500); repo limit cap raised to 1000
…calls

- migration 000037: add client, session_id, token_usage, llm_model columns
- AuditEvent gains Client, SessionID, ToolCalls (name+args+result), TokenUsage, LLMModel
- facade captures the MCP client (user-agent) and agent session_id
- web surfaces capture session_id, tool calls with args/result, and (MCP chat)
  token usage + LLM model
- audit UI shows client on the row and a metadata line + expandable tool calls
  in the detail view
- backend: filter audit events by session_id (?session=)
- UI: Session column (click a session id to filter by it) + Session search box
- time range options are generated up to the configured audit retention
  (audit_retention_days), always ending with a full-window option; if retention
  is raised to 90 days the range reaches 90 days
- replace prev/next with numeric pagination (First « ‹ 1 2 3 … N › Last »),
  page-size selector relabeled 'Per page'
Show the Error section whenever status is error (with error_type and the
message, or 'Unknown error' when the message is empty) instead of only when a
message exists.
HIGH:
- truncate tool-call arguments/result to audit_max_content_chars too (not just
  prompt/response), so the privacy/size limit actually bounds all stored content
- MCP group tool: audit failures too and measure real wall-clock duration
  (auditStart) instead of the 15s-granularity progress counter
- add tests for the audit helper (truncation, no-op guards) and the admin audit
  handler (filter parsing, empty result)

MEDIUM:
- group-debate web events now capture the prompt sent to each agent
- populate user_groups on web-surface events (proxy/mcp/group) so the group
  filter matches them, not only facade events
- drop the never-populated request_id and ttfb_ms fields/column/index (YAGNI)
@dfradehubs
dfradehubs merged commit 9641707 into main Jul 24, 2026
2 checks passed
@dfradehubs
dfradehubs deleted the feat/audit-log branch July 24, 2026 09:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant