Audit Log — Admin Activity Trail for Compliance
Purpose
This page explains how to use Audit Log to monitor who did what in the HUPH dashboard. Every important admin action (login, FAQ edit, document upload, conversation takeover, role change, etc.) is automatically recorded with full context: user, timestamp, IP, device, browser.
Use audit log for: - Incident investigation — who changed/deleted what - Compliance — regulatory data access logging requirements - Security review — detect suspicious logins or RBAC bypass - Training — understand counselor activity patterns for coaching
Prerequisites
- Role super_admin or system_admin
- Access to
https://admin.huph.val.id/audit
Concept: event categories
| Category | Example actions |
|---|---|
| Authentication | user.login, user.logout, api.auth.pass |
| Content | kb.upload, kb.delete, faq.update, faq.create |
| Conversation | conversation.takeover, conversation.return_to_bot, conversation.language.override |
| Config | cluster.create, cluster.update, cluster.delete, rule.update |
| Admin | user.create, user.role_change, user.deactivate |
| RBAC | rbac.deny.dashboard_counselor (logged when unauthorized access attempts happen) |
Each row captures: - Admin user (who performed) - Action (what they did) - Resource type + ID (the touched object) - Details (JSONB with before/after payload) - IP address, User-Agent, Device type, OS, Browser - Timestamp
Steps
1. Open audit log
Sidebar: ADMIN → Audit Log (or /audit).
Page shows a filtered table:
┌─ Audit Log ────────────────────────────────────────┐
│ Filter: [User ▾] [Action ▾] [Date range] [Search] │
│ │
│ Time User Action Resource │
│ 09:42 Siti (C-CBT) faq.update faq-abc │
│ 09:38 Budi (Admin) kb.upload doc-def │
│ 09:30 Siti (C-CBT) conversation. conv-xyz │
│ takeover │
│ ... │
└────────────────────────────────────────────────────┘
2. Filter by use case
Filter by user → see all activity of one admin (e.g., onboarding new hire for coaching, or investigating staff)
Filter by action → e.g., kb.delete to review document
deletions in KB
Filter by date range → e.g., last 7 days for weekly review
Filter by resource → click a row → side panel shows all actions on that resource (e.g., full edit history of one FAQ)
3. View single event detail
Click a row → side panel:
┌─ Event Detail ─────────────────────────────────────┐
│ Time: 2026-04-23 09:42:15 UTC │
│ User: Siti Aminah (marketing_counselor / CBT) │
│ Action: faq.update │
│ Resource: faq-local #abc-123 │
│ │
│ Before: │
│ answer: "Untuk Informatika, hubungi CIST..." │
│ After: │
│ answer: "Untuk Informatika, hubungi CIST: +│
│ *085196105421* (WhatsApp)..." │
│ │
│ IP: 10.0.12.4 • Device: Mobile (Android) │
│ Browser: Chrome 124 • OS: Android 14 │
│ │
│ [Copy to clipboard] [Export JSON] │
└────────────────────────────────────────────────────┘
3b. Before/After diff — Phase 4 (SHIPPED 2026-04-24)
Rows that mutate config (FAQ, cluster, persona, escalation rule, audience rule, guidance rule, chatbot config) now include two JSON snapshots in the expanded view:
- Before (rose panel) — state of the resource prior to the action
- After (emerald panel) — state after the action
Fields that didn't change are identical across both panels; changed fields differ.
For create/delete events, one panel shows a placeholder:
- — (no prior state) for create
- — (deleted) for delete
Click the chevron on the right of a row to expand/collapse.
3c. "Recent Config Changes" widget on Dashboard
The main dashboard (after login) now surfaces the last 5 config actions at the bottom. Each entry shows the action, actor, list of changed field names, and a relative timestamp.
Purpose: operators can notice anomalies without having to open /audit proactively.
If super_admin opens the dashboard in the morning and sees "faq.bulk_deactivate by
Budi 3 hours ago — 47 rows", that jumps out immediately — no need to remember to
investigate.
Clicking an entry navigates to /audit with the resource_id filter pre-applied.
4. Export for reports
Click Download CSV (top right). File includes all active filters. Good for: - Monthly report to compliance officer - Security team investigation - Backup before purge
Automatic retention
Audit log retains at least 90 days via retention_sweep cron
job. For longer retention, export CSV periodically.
5. RBAC denies — security signal
Filter action = rbac.deny.* shows every unauthorized access
attempt. Normally low (< 10/month). If spiking:
- User may have received internal link without proper role
- Or RBAC system is being probed (repeated attempts)
- Check source IP — if from same IP repeatedly, could be
bot/scanner
Example scenarios
Weekly compliance review. Senior admin filters action =
kb.delete for last 7 days. Review each deletion — was there a
valid reason? If unclear, ask the actor via chat.
"Missing FAQ" investigation. Marketing complains: "The June
scholarship FAQ — where did it go?". Admin opens audit log, filters
by resource type = faq-local and searches keyword "scholarship"
in details. Finds action faq.delete by whom, when, why (details
before contains old content — can manually restore).
New admin quality check. Just hired a marketing_admin. After 2 weeks, super_admin filters by user = "Budi Baru". Reviews what he's been doing — good or needs coaching.
Security alert. Notification mentions 20x failed logins from one
IP. Admin opens audit log, filters action = user.login_failed AND
IP =
Troubleshooting
Event doesn't appear despite confidence it happened. Symptom:
perform an action, but audit log doesn't record it. Cause 1: the
action isn't yet instrumented (not ALL actions are audited, only
sensitive ones). Cause 2: async logger failed in background. Fix:
check huph-api logs (docker logs huph-api | grep audit) —
escalate to developer if error.
CSV export "too many rows" error. Symptom: export > 10k rows fails. Fix: tighten filters (date range, user) then export in batches.
Detail JSON unparseable. Symptom: event detail panel empty or "Invalid JSON". Cause: resource model changed since event was logged, old format. Fix: view raw JSON from database directly (check with developer).
See also
- Getting started — onboarding and roles
- Troubleshooting — general issues
- System health — monitor system in realtime
Feature Coverage widget (super_admin only)
The Dashboard for super_admin now shows a Feature Coverage
card at the bottom. This card aggregates audit_log data, showing
how actively the 15 main admin features were used in the last 7 /
30 / 90 days.
How to read it
- Green status dot — feature was used in the selected time window
- Rose (red) status dot — feature has zero activity. Think:
- Was this feature genuinely unused (e.g., Campaigns while there are no active campaigns)?
- Or is the feature undiscoverable and the team doesn't know it exists?
- For the second case, update the K1 PageHelp banner on that page or add an operator nudge
- "Other (unmapped)" — a new action that hasn't been grouped
into any domain. Hover to see the action list → developer needs
to update
DOMAIN_MAPinapps/api/src/routes/adminUsageStats.ts
Time range
7d— this week's activity (good for weekly check-in)30d— default; monthly activity (good for monthly review)90d— quarterly view (good for "was this feature used at least once in 3 months?")
Per-user detail
This widget aggregates ONLY per feature. For per-user detail (who
did what when), use the regular /audit page with user + action
filters.
Why super_admin only
Feature Coverage shows usage patterns across the entire admin team. This data is sensitive (indirectly implicates feature owners who aren't active). The widget is scoped to super_admin only to prevent it becoming pressure tooling across roles.