Activity Log — Reference
The Activity Log is the platform's operator-facing event surface. Every administrative action that mutates the site — a user edit, a content publish, a setting change, an impersonation entry, a delete, a permission grant — emits an entry here. The log is the answer to "who did what, when, and against which record."
This page is a reference for platform engineers and integrators who need to read the log surface, scope retention, build downstream consumers, or extend the event schema. Customer-facing how-tos live in the customer docs set; this page describes the shape of the surface, not the steps to drive it.
Overview
The Activity Log lives under the Tools module in SG-Admin, rendered as a single full-page table with column filters, free-text search, and a per-row detail drawer. The surface is read-only at the UI layer — the only writes that touch it come from the platform itself, fired as side effects of other modules' write actions.
The module is paired by convention: the writer side is implicit — every other SG-Admin module emits events into a central event sink, and the Activity Log surface consumes from that sink to render the table. There is no operator-driven write path. Engineers reading the source will see the consumer half as a single controller file; emitters are distributed across every module that mutates state.
A second surface — system events — rides alongside operator events on the same table, filterable by source. System events cover platform-level changes (scheduled job runs, cache purges, backup completions, automated retention sweeps) and are emitted by the platform itself, not by an operator session.
Where it lives in SG-Admin:
- Sidebar: SG-Admin → Tools → Activity Log
- URL prefix:
/sg-admin/activity-log/ - View templates:
application/views/Admin/ActivityLog/
┌──────────────────────────────────────────────────────────────────────────┐│ SG-Admin → Tools → Activity Log [Filter ▾] │├──────────────────────────────────────────────────────────────────────────┤│ When Actor Action Target ││ ─────────────── ───────────────── ────────────── ────────────────────││ 2 min ago Jerome Cruz user.edit James Compton ││ 14 min ago (system) backup.created site-snapshot ││ 1 hr ago Maria Reyes post.publish "Spring launch" ││ 3 hr ago Jerome Cruz settings.write general → site_name ││ yesterday James Compton page.delete "Old pricing" ││ yesterday (system) cache.purge all surfaces ││ ││ [⋯ View details] [⋯ Export visible] [⋯ Filter by actor] │└──────────────────────────────────────────────────────────────────────────┘Actions
The Activity Log surface exposes the following operations. Each is described by what it does to the data, not by its internal method name.
List and filter
Returns the events visible to the current operator, paginated, with when / actor / action / target / source columns. Supports column sort, free-text filter across actor name and target label, and per-page count. The filter UI surfaces the canonical action vocabulary (user., post., page., settings., cache., backup., cron.*, etc.) as a dropdown so operators can scope by event family.
View entry detail
Opens a side drawer carrying the full event record — actor, action, target, before / after diff for edits, source IP, user agent, and the structured payload the emitter wrote. The diff renders only the fields that changed; unchanged fields collapse.
Export visible
Streams the currently filtered result set to CSV. Useful for audit handoff or compliance review. Export respects the active filter, the active search term, and the active date range. The CSV columns mirror the data model below.
Search by target
A focused mode where the operator pastes a record identifier (a user ID, a page ID, a post slug) and gets the full event history for that record. Used during incident review when the question is "what happened to this record?"
Search by actor
The inverse — pin a single actor and get every event they emitted within the active date range. Used during access review when the question is "what did this operator touch?"
Engineering note — the log surface is consumer-only. No operator action writes to the log directly; every entry is a side effect of a write elsewhere in the platform. Building an integration that "files an event" means firing the underlying write action, not posting to the log.
Retention sweep
A platform-emitted system event. The retention worker runs on the schedule configured under Settings → General, deletes entries older than the configured horizon, and emits a single retention.sweep event recording the count of rows removed. Sweeps cannot be triggered from the operator UI; they fire on the Cron Jobs schedule.
Data model
An Activity Log entry carries the following fields. Field names below are the conceptual shape — the on-disk column names match closely but are not contractually stable across releases.
| Field | Type | Notes |
|---|---|---|
id | integer | Primary key. Append-only — entries are never edited. |
occurred_at | timestamp | When the underlying action committed. Source of truth for ordering. |
actor_id | integer | User id of the operator. Null when source is system. |
actor_label | string | Display name captured at emit time. Survives downstream user renames. |
source | enum | operator, system, api, cron. |
action | string | Canonical action slug — user.edit, post.publish, cache.purge, etc. |
target_type | string | Record family — user, post, page, setting, cache, backup. |
target_id | string | Identifier of the affected record. String, since some target types use slugs. |
target_label | string | Display label captured at emit time. Survives target renames or deletes. |
diff | json | Field-level before / after for edit actions. Null for create / delete. |
payload | json | Free-form structured data emitted by the source module. |
ip | string | Source IP of the operator session. Null for system / cron. |
user_agent | string | Browser / client identifier. Null for system / cron. |
Label preservation. Actor and target labels are snapshotted at emit time. If a user is later renamed or a post is later retitled, prior log entries still display the original label, with the live record reachable via the identifier.
Diff scope. The diff field records only the fields that changed. A user.edit that touched only the phone number records a diff with one key, not the entire profile.
ACTIVITY LOG ENTRY (append-only)├── id integer primary key├── occurred_at timestamp event time├── actor_id integer operator user.id (null if system)├── actor_label string snapshot of name at emit time├── source enum operator | system | api | cron├── action string canonical slug (e.g. user.edit)├── target_type string record family├── target_id string record identifier├── target_label string snapshot of label at emit time├── diff json before/after, edits only├── payload json emitter-defined structured data└── session ip, user_agent↓ on retention sweepENTRIES OLDER THAN HORIZON(configured under Settings → General)permanently removedPermissions
Access to the Activity Log surface is gated at two layers.
Layer 1 — admin gate. Every action under SG-Admin passes through the platform's standard admin access check at request entry. An unauthenticated request never reaches the Activity Log surface.
Layer 2 — per-action capability. Within SG-Admin, each Activity Log action checks a capability associated with the calling operator's role. The default role configuration ships with three roles — Administrator, Editor, Viewer — and the capability map is:
| Capability | Administrator | Editor | Viewer |
|---|---|---|---|
| List own events | ✔ | ✔ | ✔ |
| List all events | ✔ | — | — |
| View entry detail (own) | ✔ | ✔ | ✔ |
| View entry detail (any) | ✔ | — | — |
| Export visible | ✔ | — | — |
| Search by target | ✔ | — | — |
| Search by actor (own) | ✔ | ✔ | ✔ |
| Search by actor (any) | ✔ | — | — |
| View system events | ✔ | — | — |
Self-scoping rule. Operators without the broader "list all events" capability see only their own emitted entries. The list silently filters to actor_id = current_user_id — there is no surface error, the rows do not render.
Audit of the audit. Reads of the Activity Log are not themselves logged. Exports are — every CSV export emits a log.export entry recording the actor, the active filter, and the row count. Compliance reviewers can therefore reconstruct who pulled which slice of the log, even though they cannot reconstruct who browsed it.
REQUEST│▼┌───────────────────────────┐│ Admin gate │ unauth → reject│ (every /sg-admin/* call) │└─────────────┬─────────────┘│ authed▼┌───────────────────────────┐│ Capability check │ role lacks cap → reject│ (per-action) │ (custom roles override defaults)└─────────────┬─────────────┘│ allowed▼┌───────────────────────────┐│ Self-scoping filter │ no "all events" cap → silently│ │ scope to actor_id = self└─────────────┬─────────────┘│ scoped▼rows render│▼(export action emits log.export entry)Related references
- Settings — Reference. Owns the retention horizon, the system fallback account label, and the role capability map that gates Activity Log access.
- Users — Reference. Actor identity. Soft-deleted users remain referenceable from log entries via the snapshotted
actor_label. - Cron Jobs — Reference. Owns the schedule that fires the retention sweep. Sweep completion writes a single system event back into this surface.
- Backups — Reference. Backup creation, restore, and download all emit events into this surface as system entries.
- Cache — Reference. Cache purge and warm-up emit system entries here. Useful for correlating a content change with the cache invalidation that followed.
- Notifications — Reference. Operator notification preferences can subscribe to specific action slugs in this surface — for example, alert on every
settings.writeagainst the security family. - API Keys — Reference. Source =
apientries carry the calling token identifier in the payload. Token rotation events themselves are logged here.
/docs/activity-log.