Audit log
Every privileged action in a workspace writes a row to that workspace's audit log. The log is the source of truth for "who changed what, when, from where," across membership, secrets, integrations, and platform operations. This page catalogs the events Loha emits, who can view them, what's recorded, and how the log supports PDPA compliance.
Who can see it
The audit log lives at Settings → Audit log inside each workspace.
- Owners and admins can read the full log for their workspace and export it as CSV.
- Members and guests cannot read the audit log.
- Loha staff (super-admins) can read any workspace's audit log for support and incident response. Their reads and writes against a workspace they aren't a regular member of are themselves audit events. See Super-admin activity.
What's recorded
Every audit row carries:
- Actor: the user id, a service account (recorded by id and name when a machine identity took the action, e.g. a CI deploy), or
systemfor migrations and scheduled jobs. - Action: the event type (see the catalog below).
- Resource type & id: what was affected (member, invitation, secret, etc.).
- Result:
successorfailure(denied actions are logged too). - IP address & user agent: derived from the request that triggered the action.
- Timestamp: server time, UTC.
- Details: action-specific JSON (previous role, target email, revoked-session count, etc.).
The schema is strict: action names come from a fixed enum (audit_action in the database). New event types require a schema migration, which means the catalog below is exhaustive at any given point in time.
Event catalog
Membership & invitations
| Action | Trigger | Details recorded |
|---|---|---|
invite_sent | Owner/admin creates a new invitation, or re-sends a pending one. details.resend = true distinguishes the two. | email, role, resend |
invite_accepted | Invitee clicks the link, signs in, confirms. | email, role |
role_changed | Owner/admin updates a member's role. | targetUserId, previousRole, newRole, actorRole |
member_added | Loha staff directly adds a member via super-admin tools (rare). | targetUserId, role |
member_removed | Owner/admin removes a member, OR an owner deletes the workspace. | targetUserId, targetRole, sessionsDeleted, apiKeysRevoked, actorRole |
delete (resourceType invitation) | Owner/admin revokes a pending invitation. | invitedEmail, invitedRole, actorRole |
See Members & roles for the full hierarchy rules these events enforce.
Secrets
| Action | Trigger | Details recorded |
|---|---|---|
secret_access | A pipeline run reads a secret during build execution. | secretName, scope, pipelineRunId |
secret_revealed | An owner/admin reveals a secret value in the dashboard UI. | secretName, scope |
secret_revealed is the high-sensitivity one: it means a human eyeballed the plaintext. If you see it in the log and didn't expect it, investigate.
Integrations & repositories
| Action | Trigger | Details recorded |
|---|---|---|
create (resourceType repository) | Owner/admin connects a new repo. | fullName, provider |
update (resourceType repository) | Owner/admin updates connection settings. | What changed |
delete (resourceType repository) | Owner/admin disconnects a repo. The upstream git repo is untouched. | fullName, provider |
Builds
| Action | Trigger | Details recorded |
|---|---|---|
trigger | A build starts: webhook, manual, schedule, or PAT. | pipelineId, triggerType |
cancel | A user or system cancels a running build. | buildId, reason |
build_retried | Owner/admin re-runs a failed build. | originalBuildId |
logs_downloaded | Owner/admin downloads build logs as an archive. | buildId, size |
API keys (personal access tokens)
| Action | Trigger | Details recorded |
|---|---|---|
api_key_create | A user creates an API key in this workspace. | name, prefix, scopes, expiresAt, ownerUserId, actorRole |
api_key_revoke | A user revokes their own key, or an admin revokes someone else's. | name, prefix, ownerUserId, ownerType, revokedBy (self or admin) |
Keys revoked because their owner was removed from the workspace are recorded on the member_removed event, not as separate api_key_revoke rows.
Workspace lifecycle
| Action | Trigger | Details recorded |
|---|---|---|
settings_change | Owner/admin edits workspace settings (name, logo, notification recipients, default trigger settings). | What changed |
update (resourceType organization) | Lower-level workspace mutation (rare; usually wraps a settings_change). | What changed |
delete (resourceType organization) | Owner deletes the workspace. The audit row survives the deletion under a tombstone organization id. | — |
Ownership transfer surfaces as a pair of role_changed events (old owner → admin, new owner → owner) in the same request.
Authentication
| Action | Trigger | Details recorded |
|---|---|---|
login | Any user signs in. | provider, mfaUsed |
logout | Any user signs out. | — |
access_denied | A request was blocked by RBAC (e.g., admin tried to remove a peer admin). | attemptedAction, reason |
Cross-tenant (super-admin) activity
| Action | Trigger | Details recorded |
|---|---|---|
cross_tenant_access | Loha staff reads data from a workspace they aren't a member of. | route, resourceType |
cross_tenant_mutation | Loha staff writes to a workspace they aren't a member of. | route, resourceType, what changed |
These are the rows that satisfy our transparency commitment. Owners and admins see them under Settings → Security, surfaced separately from regular audit entries.
Retention
Audit rows are retained indefinitely under current policy: they outlive the workspace itself. Deleting a workspace tombstones the organization id but preserves every prior audit row.
We may introduce a retention window for cost reasons in the future. This page will be updated before any change ships, and existing customers will be notified in advance.
Exporting
From Settings → Audit log, click Export to download the current filter view as CSV. Common workflows:
- Compliance review: filter to the period under audit, export, attach to your evidence pack.
- Incident investigation: filter to one user or one resource, export, share with security review.
- Membership reconciliation: filter to
invite_sent,invite_accepted,role_changed,member_removedover a quarter; reconcile against HR offboarding lists.
PDPA & data sovereignty
The audit log is the primary mechanism Loha uses to satisfy PDPA (Personal Data Protection Act B.E. 2562) Article 30 (Records of Processing Activities):
- Every action that touches personal data (member records, email addresses, login events) is recorded with actor, timestamp, and result.
- The log is stored in-country alongside the rest of the workspace data. See Data sovereignty for what stays in Thailand.
- Export is owner/admin-initiated and produces an auditable trail of who requested the export and when.
- Super-admin (Loha staff) access is logged separately and surfaced to the workspace, so customers have an independent verification path.
If your Data Protection Officer or auditor needs the underlying schema, the action enum is defined in audit_action in our database and matches the catalog above one-for-one. We're happy to walk through it on a compliance call: reach out to your account manager.
See also
- Members & roles: the rules that membership events enforce.
- Invitations: how the invitation flow generates audit entries.
- Settings & lifecycle: workspace deletion, ownership transfer, super-admin activity.
- Data sovereignty: the full PDPA + Thai data-sovereignty story.