Developer Docs

Advanced Audit Logging UI

The Advanced Audit Logging UI replaces the legacy flat Activity Feed in the Organization management dashboard with a full-featured compliance interface. It provides tabular event viewing, client-side filtering by actor and date range, one-click data export (CSV/JSON), and a SIEM webhook lifecycle panel for configuring external event streaming endpoints.


Architecture

The Audit Log tab in /orgs/[slug]/manage renders two autonomous Svelte 5 components:

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  activeTab === 'activity'                        โ”‚
โ”‚                                                  โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”‚
โ”‚  โ”‚       AuditLogPanel.svelte               โ”‚    โ”‚
โ”‚  โ”‚  โ€ข Fetches organizationAuditEvents       โ”‚    โ”‚
โ”‚  โ”‚  โ€ข Client-side Actor + Date filtering    โ”‚    โ”‚
โ”‚  โ”‚  โ€ข CSV / JSON Blob export                โ”‚    โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ”‚
โ”‚                                                  โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”‚
โ”‚  โ”‚       SiemWebhookPanel.svelte            โ”‚    โ”‚
โ”‚  โ”‚  โ€ข CRUD for SIEM webhook endpoints       โ”‚    โ”‚
โ”‚  โ”‚  โ€ข Signing secret one-time reveal        โ”‚    โ”‚
โ”‚  โ”‚  โ€ข Test connectivity                     โ”‚    โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Why Client-Side Filtering?

The backend organizationAuditEvents GraphQL resolver currently supports orgId, limit, and eventType parameters only โ€” it does not natively accept date ranges or actor filters. Rather than extending the backend, we fetch a larger batch (limit: 200) and compute filtered sub-arrays reactively using Svelte 5 $derived.by() blocks. This keeps the backend surface small while providing a rich filtering UX.


Components

`AuditLogPanel.svelte`

Prop Type Description
orgId string Organization ID to load events for

Data flow:

  1. On mount, fetches organizationAuditEvents (limit: 200) via GET_ORG_AUDIT_EVENTS
  2. Extracts unique actors from the response into a $derived dropdown
  3. Filters are applied client-side via $derived.by() computing filteredEvents
  4. Export functions generate Blob URLs and trigger browser downloads

Export pattern: Follows the established DataExportPanel.svelte pattern from $lib/components/settings/ โ€” using URL.createObjectURL() with cleanup via URL.revokeObjectURL().

`SiemWebhookPanel.svelte`

Prop Type Description
orgId string Organization ID to manage webhooks for

Signing secret UX: Follows the same transient one-time reveal pattern used by ScimPanel.svelte in the Enterprise Security module. The generatedSecret state is set on create/rotate mutations and cleared when the admin clicks "I have saved my secret securely".


GraphQL Operations

All operations are defined in src/lib/queries/audit.ts:

Queries

# Fetch audit events for an organization
query GetOrgAuditEvents($orgId: String!, $limit: Int) {
  organizationAuditEvents(orgId: $orgId, limit: $limit) {
    id
    eventType
    actorId
    targetUserId
    metadata
    ipAddress
    createdAt
    actor { id email }
    targetUser { id email }
  }
}

# List SIEM webhooks
query GetSiemWebhooks($orgId: ID!) {
  siemWebhooks(orgId: $orgId) {
    id name url isActive createdAt
  }
}

Mutations

Mutation Returns Notes
createSiemWebhook SiemWebhookWithSecret Includes signingSecret
updateSiemWebhook SiemWebhook Secret not returned
deleteSiemWebhook SiemWebhook Immediate removal
rotateSiemWebhookSecret SiemWebhookWithSecret New signingSecret
testSiemWebhook SiemTestResultType { success, httpStatus, error }

Important: The signingSecret field only exists on the SiemWebhookWithSecret return type (create/rotate mutations). The SiemWebhook entity does not expose it via @Field() โ€” the GET_SIEM_WEBHOOKS query must not request it.


Permission Model

The Audit Log tab uses the same permission check as the existing Activity Feed โ€” visibility is governed by the user's Organization role:

Action Required
View Audit Log / Export data VIEW_AUDIT_LOGS (OWNER, ADMIN)
Manage SIEM webhooks VIEW_AUDIT_LOGS (same check as activity tab)

For the backend SIEM mutations, the MANAGE_WEBHOOKS permission is enforced server-side via the PermissionsGuard. See SIEM Event Streaming for the full permission matrix.


File Reference

File Purpose
src/lib/queries/audit.ts GraphQL queries/mutations + TypeScript interfaces
src/lib/components/audit/AuditLogPanel.svelte Audit event table with filtering and export
src/lib/components/audit/SiemWebhookPanel.svelte SIEM webhook lifecycle management
src/routes/orgs/[slug]/manage/+page.svelte Host page โ€” renders components in activity tab
tests/audit-logs.test.ts Unit tests for export logic
tests/advanced-audit.spec.ts E2E tests for filtering and export flows

Testing

# Unit tests โ€” export logic validation
npx vitest run --environment node tests/audit-logs.test.ts

# E2E tests โ€” filtering, actor selection, JSON export
pnpm test -- tests/advanced-audit.spec.ts

# Build verification
node_modules/.bin/vite build 2>&1 | tail -5