Skip to content

Audit log

The operational audit log is a chronological record of who changed what in your workspace administration. It answers questions like “who removed this member?”, “when was that project deleted?”, and “who last changed the workspace settings?” (#859, ADR-0157).

It is Owner/Admin-visible only. A plain Member cannot read the log — the events it records (role changes, removals, ownership transfers) are administration concerns, not team-visible activity.

Every entry captures an actor, an event type, an optional target, and a structured metadata object. The following workspace administration events are recorded:

Event typeWhen it firesTargetMetadata
member_addedA user accepts an invite and joins the workspaceThe new memberrole, source
member_role_changedAn Owner/Admin changes a member’s workspace roleThe memberold_role, new_role
member_status_changedAn Owner/Admin deactivates or reactivates a memberThe memberold_status, new_status, credentials_revoked
member_removedAn Owner/Admin removes a member from the workspaceThe memberrole
ownership_transferredThe Owner transfers workspace ownershipThe new ownernew_owner_user_id
project_createdA project is createdThe project—
project_deletedA project is deleted (soft or hard)The projectmode (soft/hard)
project_restoredA soft-deleted project is restored from TrashThe project—
template_publishedA project is published as a workspace templateThe template—
workspace_settings_changedWorkspace General settings are savedThe workspacefields (the names of the fields that changed)
workspace_export_triggeredA workspace export is startedThe export job—
invite_sent (0.4)An Owner/Admin invites an email addressThe inviterole
invite_accepted (0.4)An invited person joins the workspaceThe inviterole, invited_by, invited_by_id, invited_at
invite_revoked (0.4)An Owner/Admin revokes a pending inviteThe inviterole, invited_by
calendar_changedA shared working-time calendar is editedThe calendarfields
sso_provider_created (0.4)An Admin adds a single sign-on providerThe providerconfig, secret_set, actor_kind
sso_provider_updated (0.4)An Admin changes a provider’s configurationThe providerchanged (per-field before/after), actor_kind
sso_provider_deleted (0.4)An Admin removes a providerThe providerlinked_accounts, locked_out_accounts, confirmed_lockout, config, actor_kind
sso_secret_rotated (0.4)An Admin supplies a new client secret for a providerThe provideractor_kind
sso_account_linked (0.4)A single sign-on identity binds to an existing local accountThe uservia, provider, issuer, subject, role

On a soft delete, the project’s members also receive an in-app project-delete notification so a project never simply vanishes from under the team.

Auth events: some are rows, login success is a log line

Section titled “Auth events: some are rows, login success is a log line”

Single sign-on administration is recorded as audit rows; a successful login is recorded as a log line on the trueppm.auth logger. The split is deliberate (ADR-1120), and knowing which is which is the difference between finding an event and concluding it never happened.

QuestionWhere to look
Who widened the allowed email domains, and to what??event_type=sso_provider_updated
Who rotated the client secret, and when??event_type=sso_secret_rotated
Which accounts got a federated credential??event_type=sso_account_linked
Who signed in, from where, just now?trueppm.auth in your log pipeline
Who failed to sign in?trueppm.auth (auth.login_failed, since 0.3)

Login success is not a row because its rate is set by user traffic rather than by administrative action, and the community audit table has no retention or pruning (see Retention) — a verb that fires per login would grow that table without bound. It also has no before and after to record. Both auth log lines therefore land on one logger, so an operator correlating “somebody was hammering this account, did they get in?” reads one channel:

auth.login_failed username_hash=<sha256> client_ip=<ip>
auth.login_succeeded user_id=<id> method=password|sso:<provider> client_ip=<ip> remember=<bool>

The success line is emitted at INFO, so it requires DJANGO_LOG_LEVEL=INFO — which is the default. It carries the user id, never the email or username; the failure line hashes the submitted identifier for the same reason.

auth.login_succeeded records a new sign-in, not a continued session. Refreshing an access token does not emit a line, so a session sustained by refresh-token rotation produces one entry — at the moment it began. If you are looking for evidence of ongoing access rather than of a sign-in, this channel is the wrong place to look for it.

Each entry stores a human-readable actor_label (the actor’s name or email) at the moment the event is recorded, in addition to a nullable foreign key to the user. If the user is later deleted, the foreign key becomes null but the label remains — the log stays readable. System-initiated events have a blank label and no actor.

GET /api/v1/workspace/audit-events/

The endpoint is cursor-paginated (newest first). The response is an object, not a bare array — and unlike the page-number envelope used elsewhere it carries no count, because a cursor never computes one:

{"next": "…?cursor=cD0yMDI2…", "previous": null, "results": [ … ]}

Follow next until it is null. It supports filtering:

Query parameterDescription
event_typeOne of the event types above. Unknown values return 400.
actorA user id. Returns only events performed by that user.
sinceISO 8601 date or datetime. Returns events at or after this time.
untilISO 8601 date or datetime. Returns events at or before this time.
page_sizePage size (default 50, maximum 200).
  • Owners and Admins can read the log.
  • Members, Schedulers, Viewers, and unauthenticated callers receive 403 (or 401 when not signed in).

The community edition applies no retention or pruning — entries accumulate for the life of the deployment. They are stored in a single indexed table and cursor-paginated, so read performance does not degrade as the log grows, but the table itself is unbounded. Operators who need a retention policy should plan for table growth or upgrade to the Enterprise edition.

The community log is mutable and operational — a convenience record for day-to-day workspace administration. It makes no immutability, signing, or tamper-evidence guarantees.

The Enterprise edition layers a compliance-grade, immutable, signed audit trail on top of the same events, with retention policy, cross-workspace aggregation, and SOC 2-aligned export. It does this by registering a receiver against the OSS audit_event_created signal — the community core never imports Enterprise code. If you need a defensible audit trail for compliance, that is an Enterprise capability.