Audit Logging

The complete audit log reference for SikkerKey. Every vault action, its severity classification, and how to consume audit events via the dashboard, email alerts, and webhooks.

Every vault action in SikkerKey is recorded in an append-only audit log. Secret reads, authentication failures, single sign-on, permission changes, machine registrations, team invites, session management, integration activity, billing events, account lifecycle. Every entry records who did what, from where, when, and how sensitive the action was.

Entry Shape

FieldDescription
actionThe operation identifier (e.g. secret_read, auth_failure)
severityOne of critical, high, medium, low, info
actorKindWho performed the action: user, machine, ai_agent, system, or external (an unauthenticated outside party, such as a failed SAML SSO sign-in). The dashboard renders the actor's name and an icon based on this field.
userIdThe vault owner's ID. Set on most entries to scope the row to the owner's audit log; not on its own an indicator that the user performed the action.
machineIdThe machine involved in the action. May be the actor (when actorKind = machine) or the target the user acted on (e.g. on machine_approve).
aiAgentIdThe AI agent involved in the action. AI agents are a separate identity class from machines and have their own ID. Set only when actorKind = ai_agent.
secretIdThe secret involved (if applicable)
sourceIpThe IP address the request originated from. Background tasks use the sentinel string system; Stripe webhook events use stripe.
detailHuman-readable description of what happened
timestampMillisecond-precision Unix timestamp

The actorKind field is the authoritative answer to "who did this." A machine_revoke row records both userId (the human who clicked revoke) and machineId (the machine they revoked), but actorKind = user makes clear which one was the actor. A temp_machine_create row also records both, with actorKind = machine because the bootstrap script (running as the machine) registered itself; the userId is just the owning vault for scoping.

The log is append-only. Entries are never modified or deleted by any operation. Retention is pruned on a schedule according to your plan.

Severity Levels

Severity is assigned automatically per action. The classification drives the dashboard's filter controls, email alert defaults, and webhook routing.

SeverityMeaning
criticalActive attack signal, account compromise, or irreversible destruction. Paging-worthy.
highSecurity-relevant event that may indicate an attack, or a trust boundary expansion. Review-worthy.
mediumLegitimate access or trust boundary change. Worth noting.
lowRoutine administrative change. Recorded for completeness.
infoNormal operation. The bulk of the log volume.

Action Reference

Critical

ActionWhen it fires
auth_failureA machine authentication attempt failed (missing headers, unknown machine, disabled machine, invalid signature, expired timestamp, replayed nonce, suspended owner)
refresh_token_replayA refresh token was used after it had already been rotated. SikkerKey treats this as detected theft: every active session is immediately revoked, and re-login is required on every device.
webauthn_counter_anomalyA passkey assertion presented a signature counter at or below the previous value. The canonical signal of a cloned authenticator. The sign-in is rejected and the credential involved is named in the entry.
webauthn_recoveryAn account was recovered using a passkey recovery code. Every passkey is removed, the require-passkey and passwordless flags are cleared, and every other session is revoked. Fires once per recovery, regardless of how many credentials were wiped.
sensitive_action_lockoutA re-authentication gate hit its attempt cap (password change, 2FA disable, vault destruction, first-passkey registration, employee password change). SikkerKey treats this as an authenticated-session brute-force: the current session is revoked immediately, and further attempts on that gate are rejected for a 15-minute window. The detail names the gate.
project_deleteA project and all its contents were deleted
2fa_disableTwo-factor authentication was disabled on an account
vault_destroyedA vault was permanently destroyed
account_revokedAn account was revoked for policy or security reasons
ip_allowlist_disableThe account-wide IP allowlist was disabled
canary_triggeredA canary secret was read by a granted machine. Signals an active compromise of that machine.
project_freezeA project was frozen in response to a canary trigger. One entry per frozen project.
secret_destroyedA bound secret was destroyed by a TTL trigger. Detail names the trigger source: system:ttl-time for time-based expiry (background pruner), system:ttl-reads for read-count exhaustion (inline post-fetch).

High

ActionWhen it fires
ai_agent_registerA new AI agent registered against a one-time bootstrap token. New management-plane identity in the vault.
ai_agent_revokeAn approved AI agent was revoked and removed. Cascades through scopes, allowlist, and name history.
ai_agent_scopes_updateAn AI agent's scope set was changed. Privilege boundary moved; review the diff in the detail field.
machine_registerA new machine was registered via bootstrap
machine_revokeAn approved machine was revoked and removed
machine_enrollA machine was registered via an enrollment token (multi-use provisioning)
machine_provisionA machine identity was pre-provisioned from the dashboard
machine_provision_downloadA provisioned identity bundle was downloaded
enrollment_token_revokeAn enrollment token was revoked before its expiry
enrollment_token_deniedAn enrollment attempt was denied (invalid token, machine count exceeded)
project_machine_addA machine was linked to a project, gaining access to that project's secrets
project_unfreezeA vault owner cleared a canary-triggered freeze on a project.
secret_read_deniedA machine tried to read a secret it doesn't have access to
secret_read_blockedA machine passed the six-requirement gate but was denied by the bound access policy (time window, IP, rate cap, TTL, or co-sign). The detail names the failing axis.
temp_machine_blockedA temporary machine's signed request was blocked by one of its per-machine guardrails (IP allowlist, geo, or time window). Detail names the failing axis. A series of these is the canonical signal that an out-of-scope party has gotten hold of the identity.
secret_rotate_deniedA machine tried to rotate a secret it doesn't have access to
secret_deleteA secret was moved to trash
secret_hard_deleteA secret was permanently deleted, bypassing trash
secret_exportA bulk export of secrets was performed
permission_grantA machine was granted access to secrets in a project
org_member_acceptA user accepted an organization invite and joined as a member
org_member_template_changeAn organization member's assigned template was changed, moving their capability set
org_member_scope_changeAn organization member's project scope was changed, moving the set of projects their project-scoped capabilities apply to
org_member_removeAn organization member was removed from the vault (recorded on both the owner's and the removed member's logs)
org_member_leaveAn organization member voluntarily left the vault (recorded on both the leaving member's and the owner's logs)
team_member_removeHistorical. No new events fire. A team member was removed from the vault (recorded on the owner's log).
team_revocationHistorical. No new events fire. A team member was removed from the vault (recorded on the member's log).
password_resetA password was reset via the reset flow
password_changeA password was changed by an authenticated user
password_change_failedA password change attempt failed
password_setA password was added to an account that previously had none (e.g. an OAuth-only or passkey-only account opting back into password sign-in)
password_removeA password was removed from an account, leaving the user to sign in with a passkey or OAuth provider only
login_failedA login attempt failed
2fa_failedA two-factor authentication step failed
recovery_code_usedA 2FA recovery code was consumed
webauthn_registerA passkey was registered to the account
webauthn_revokeA passkey was removed from the account
webauthn_failedA passkey ceremony failed (unknown credential, ownership mismatch, assertion verification rejected)
webauthn_policy_updateThe passkey policy changed (require-passkey-at-sign-in or passwordless-sign-in flags)
oauth_linkAn OAuth provider (GitHub or Google) was linked to the account, adding it as a sign-in path
oauth_unlinkA linked OAuth provider was removed from the account
sso_config_updateThe organization's SAML SSO configuration was changed: identity provider entity ID, sign-in URL, signing certificate, default member template, or the enabled / enforce toggles. The signing certificate is the SSO trust anchor, so the entry records its fingerprint, and both the previous and new fingerprints when it changes.
sso_domain_verifyAn email domain was verified for the organization via DNS TXT record, turning on automatic member provisioning for that domain on first SSO sign-in.
sso_login_failedA SAML SSO sign-in was rejected. The detail names a sanitized reason category (invalid signature, audience mismatch, replay, expired assertion, unverified domain, and similar). Attributed to the external actor kind, since the attempt carried no authenticated identity.

Medium

ActionWhen it fires
ai_agent_approveA pending AI agent was approved and can now authenticate against the vault.
ai_agent_denyA pending AI agent was denied and removed before it could be approved.
ai_agent_enableA previously disabled AI agent was re-enabled. Its scopes and allowlist were preserved across the disable, so this re-grants the original capabilities.
ai_agent_disableAn AI agent was disabled. Signed requests by the agent are refused with HTTP 403 until re-enabled. Scopes and allowlist preserved.
ai_agent_allowlist_updateAn AI agent's project allowlist was changed. Project-context routes are filtered to the new list.
machine_approveA pending machine was approved and can now authenticate
machine_denyA pending machine was denied
machine_expireAn ephemeral machine reached its expiration time and was disabled
enrollment_token_createA new enrollment token was generated
temp_machine_token_revokeA temporary machine bootstrap token was manually revoked before use
temp_machine_extendA temporary machine's expiry was pushed further into the future. Capped at a rolling twelve months of remaining lifetime.
temp_machine_extend_revertThe most recent un-reverted extension on a temporary machine was rolled back, restoring the prior expiry
temp_machine_guardrails_updateA temporary machine's per-machine guardrails (IP allowlist, geo, or time window) were changed
project_machine_removeA machine was detached from a project
org_member_inviteAn organization invite was sent (also written to the invitee's log as a notification)
org_member_invite_revokeA pending organization invite was revoked, or an invited user declined the invite
org_member_suspendAn organization member was suspended. Effective immediately on their next request.
org_member_unsuspendA suspended organization member was restored.
org_template_createA new organization template was created
org_template_updateAn organization template was edited (name, description, or capability set). Per-capability diff lives in the template-capability audit.
org_template_archiveA template was archived. Requires no members assigned.
org_template_unarchiveA previously archived template was restored.
org_template_deleteA template was hard-deleted. Requires no members assigned. The capability-change audit rows survive.
vault_context_switchA member picked a vault from the post-login picker, setting their session's active-vault context.
vault_converted_to_organizationA personal vault was converted to an organization. One-way.
sso_domain_addAn email domain was added to the organization for SSO. The domain is unverified and has no effect until its DNS TXT record is confirmed.
sso_domain_removeAn email domain was removed from the organization, ending SSO provisioning for accounts in that domain.
team_permission_updateHistorical. No new events fire. A team member's permissions on a project were changed.
team_inviteHistorical. No new events fire. An invite was sent to a user.
team_invite_cancelledHistorical. No new events fire. The owner cancelled a pending invite.
team_access_revokedHistorical. No new events fire. A team member's access was revoked.
team_leftHistorical. No new events fire. A team member voluntarily left a vault.
team_project_removeHistorical. No new events fire. A team member was removed from a specific project (recorded on the owner's log).
team_project_revocationHistorical. No new events fire. A team member was removed from a specific project (recorded on the member's log).
password_reset_requestA password reset was requested
session_revokeA specific session was revoked
session_revoke_allAll other sessions were revoked
secret_restoreA previously trashed secret was restored
secret_schedule_createAn automatic rotation schedule was created
secret_schedule_updateA rotation schedule was modified
secret_schedule_deleteA rotation schedule was removed
ip_allowlist_addAn IP or CIDR was added to the allowlist
ip_allowlist_removeAn IP or CIDR was removed from the allowlist
ip_allowlist_enableThe IP allowlist was enabled
access_policy_createAn access policy was created in a project
access_policy_updateAn access policy's fields were modified. Affects every secret bound to it on the next fetch.
access_policy_deleteAn access policy was removed. Only possible after every bound secret has been detached.
secret_policy_bindA secret was bound to an access policy
secret_policy_unbindA secret was detached from its access policy and reverts to unconstrained
supabase_integration_connectA Supabase integration was connected via personal access token
sync_config_deleteA managed secret's sync configuration was removed
agent_status_changeA managed secret agent's connectivity state changed
canary_enableA canary secret was armed (reads fire configured triggers).
canary_disableA canary secret was disarmed (reads are still audited but do not freeze).
canary_config_updateA canary's trigger toggles (freeze-project, freeze-related) were changed.
temporary_secret_failedA temporary secret view attempt failed (wrong passphrase destroys the secret)
payment_action_requiredA payment requires additional authentication
payment_failedA payment attempt failed
subscription_past_dueThe subscription entered past-due state
subscription_pausedThe subscription was paused
subscription_cancelA subscription was cancelled

Low

ActionWhen it fires
ai_agent_token_createA one-time AI-agent bootstrap token was issued. The capability change comes when an agent redeems it (ai_agent_register), not at issuance.
team_invite_declinedHistorical. No new events fire. A user declined a vault invite.
bootstrap_token_createA bootstrap token was generated
webauthn_renameA passkey's friendly name was changed
machine_purgeAn expired ephemeral machine was permanently removed by the background cleaner
enrollment_token_expireAn enrollment token reached its expiration time
temp_machine_token_createA temporary machine bootstrap token was issued
temp_machine_createA machine registered via a temporary bootstrap token. Begins in pending state and requires manual approval.
temp_machine_purgeAn expired temporary machine was permanently removed after the 30-day retention period
subscription_initiatedA subscription was initiated
subscription_activatedA subscription became active
subscription_cancel_requestedA cancellation was requested
supabase_integration_disconnectedA Supabase integration was disconnected
temporary_secret_createA one-time temporary secret was created
webhook_createA webhook endpoint was added
webhook_updateA webhook endpoint was modified
webhook_deleteA webhook endpoint was removed

Info

ActionWhen it fires
ai_agent_renameAn AI agent was renamed. Recorded in the agent's name history.
temp_machine_expiredA temporary machine reached its expiration time and was disabled
secret_readA machine successfully read a secret
secret_createA secret was created
secret_updateA secret's value was replaced or rolled back
secret_rotateA secret was rotated
secret_renameA secret was renamed
secret_note_updateA secret's note was edited
project_createA new project was created
project_updateA project was renamed or its description changed
machine_renameA machine was renamed
login_successA user logged in successfully
logoutA user logged out
oauth_loginA user logged in via OAuth
webauthn_loginA user signed in with a passkey, either as the only factor (passwordless mode) or as the second factor after a password
sso_loginA user signed in to an organization vault via SAML SSO
user_registerA new user account was created
username_changeA user changed their account username. The detail records the previous and new username.
2fa_enableTwo-factor authentication was enabled
2fa_setupA 2FA setup step was completed
team_invite_acceptedHistorical. No new events fire. A user accepted a vault invite.
team_joinedHistorical. No new events fire. A user joined a vault (their side).
subscription_cancelledA cancelled subscription reached its end-of-term
sync_config_createA managed secret's sync configuration was created
sync_config_readA managed secret agent fetched its sync configuration
ci_template_renderA CI/CD bootstrap script was generated from the dashboard for an enrollment token. Detail names the target platform, token name, and the number of projects and secrets in scope.
lease_createdA dynamic secret lease was issued
lease_revokedA dynamic secret lease was revoked
temporary_secret_viewedA temporary secret was viewed by the recipient
webhook_testA webhook test delivery was sent
trash_cleanupExpired trashed secrets were permanently deleted by the background pruner

Real-Time Updates

When an audit entry is recorded, it is pushed to the vault owner's dashboard via Server-Sent Events. The audit page updates in place without polling. Team members see events relevant to projects they have access to.

Email Alerts

Vault owners can subscribe to email alerts per action from the Alerts page. When an action with an active alert fires, an email is dispatched asynchronously, the original request is never blocked. Alerts are gated by your plan's email alert feature.

Severity is included in the alert so your inbox rules can triage accordingly. Critical alerts are separated visually from routine ones.

Webhooks

Vault owners can register webhook endpoints that receive audit events as HTTP POST requests. Each webhook can subscribe to specific actions or whole categories. The body includes the full audit entry plus a signature header for origin verification.

See Webhooks for endpoint registration, payload shape, and signature verification.

Historical: Team Member Visibility

This section describes the legacy Teams feature, which has been replaced by Organizations. The team_* rows above remain in the table so audit entries written before the rollover stay readable. No new team_* events fire on current SikkerKey.

When a team member performed an action on a shared project, the audit entry was written under the vault owner's log with the team member's username referenced in the detail. Some actions also wrote a mirrored entry under the team member's own audit log so they could see their own activity history.

Examples of mirrored entries:

  • team_revocation on the member's log when the owner removed them
  • team_project_revocation on the member's log when the owner removed them from a single project
  • Secondary permission_grant, project_machine_add, and similar entries on the acting team member's log when they performed these actions on shared projects

Organization Member Attribution

Actions performed by an organization member are written under the organization vault's audit log with the member's username as the actor. The actor_kind column resolves to user; the userId column carries the member's user id (not the vault owner's). When the action involves a cross-actor notification (e.g. a member accepts an invite, which writes a mirror row to the owner's log), the mirror row carries actor_kind = system so the owner is not labelled as the actor of an action they didn't take. The detail string names the acting member in those cases.

AI Agent Attribution

Actions performed by an AI agent carry the agent's id in the aiAgentId field. The detail string also names the agent (e.g. "AI agent 'codex-prod' rotated secret 'stripe-key' in 'production'") so a reader can identify the actor without joining tables.

In the audit table, AI-agent-attributed entries show the agent's name in the actor column. AI agents and machines are physically distinct identity classes, and an entry never carries both machineId and aiAgentId.

Because every AI agent action signs through the management surface, there is no offline write path: bypassing the audit log requires bypassing the entire request path. See the MCP Security Model for the contract.

Dashboard Filtering

The Audit page supports:

FilterEffect
SearchFull-text over the detail field, debounced
ActionRestrict to one or more action types
SeverityRestrict to one or more severity levels
Source IPExact match on source IP
Time rangeLast hour, 24 hours, 7 days, 30 days

Filters combine with AND. Server-side pagination at 50 entries per page.

What the Audit Log Does Not Contain

  • Decrypted secret values. Reads record that a secret was read, never the plaintext.
  • Passphrases. They are never logged.
  • Private keys. Machine private keys are never transmitted to SikkerKey in the first place.
  • Session cookies or JWT contents.
  • Billing card details. Stripe object IDs appear; card numbers do not.

Retention

Audit log retention is plan-based and pruned automatically by a background task. When a retention window passes, older entries are permanently deleted. Retention is not uniform across severity levels: high-severity entries are kept for twice your plan's window, and critical entries are never pruned. Critical covers authentication failures, disabling two-factor authentication, account revocation, disabling the IP allowlist, project deletion, and vault destruction, so the record of a destructive or security-relevant event outlives routine activity.

Destroying a vault is the one exception to "never pruned." When a vault is destroyed, its audit log is kept for about 30 days and then erased in full, every severity included, along with the rest of the vault's data, by which point the encrypted backups that held it have also expired.