Skip to content

Audit Log

wg-access-server records the changes that matter for an operator in its normal log output, so they end up wherever the rest of the logs go. Every such record carries an audit field with the action, which is what you filter on:

level=info msg=device.delete audit=device.delete actor=admin actor_is_admin=true actor_provider=simple \
  component=audit device=laptop owner=alice remote_addr=198.51.100.7 trace.id=…

Recorded actions

Action When Fields beside the actor
device.create A device was added device, owner, address
device.delete A device was deleted device, owner, reason*
device.rename A device was renamed device, previous, owner
device.access An admin blocked a device or changed its expiry device, owner, disabled, expires_at**
device.expire A device lost its access because its expiry passed device, owner
device.routes An admin changed the networks behind a device device, owner, routes
device.rotate Somebody gave their device a new key device, owner
user.delete An admin deleted a user, their devices and tokens target_user
user.revoke An admin took somebody's access away target_user, devices_blocked, tokens_deleted, sessions_ended
user.password Somebody changed their own password sessions_ended, tokens_deleted
user.two_factor Somebody turned their own second factor on or off enabled, sessions_ended****, tokens_deleted****
user.two_factor_reset An admin removed somebody's second factor target_user, sessions_ended, tokens_deleted
user.recovery_codes Somebody replaced their own recovery codes codes_left_before
user.passkey_add Somebody registered a passkey of their own passkey (its name), sessions_ended****, tokens_deleted****
user.passkey_rename Somebody renamed a passkey of their own passkey (its new name)
user.passkey_delete Somebody removed a passkey of their own passkey (its id)
session.delete Somebody ended a browser session of theirs session, or sessions and reason***
api_token.create An API token was created token, token_name, expires_at
api_token.delete An API token was revoked token, token_name, owner

* reason=inactive marks a device the server deleted by itself because it exceeded the inactive device grace period.

** The record says what the device's access looks like after the change, not what was changed. expires_at is absent when the device's access does not expire.

*** session names the one session that was ended. "Sign out everywhere else" ends several at once and records sessions as how many that was, with reason=all others; the ids are not worth a record each, they are gone.

**** Only for confirming the code from the app and for the first passkey. Both end the person's other sessions and revoke their API tokens, and these say how many.

user.two_factor, user.two_factor_reset and user.recovery_codes record that it happened, never the secret, a code or a recovery code. codes_left_before is how many unused codes there were when the set was replaced, which is what says whether somebody was running out or had lost the paper.

user.password records that the password changed and how many other sessions and API tokens that ended. Neither the old nor the new password is recorded, in any form.

device.rotate records that the key changed, not the keys: the new public key says nothing an operator needs, and key material does not belong in a log.

device.expire is recorded by the server itself, so its actor is system: nobody asked for it, the date an admin set earlier simply passed. It is recorded once per device, when the peer is removed.

Who did it

Field Meaning
actor The user that made the change, or system for changes wg-access-server made on its own
actor_provider The authentication backend that user logged in with
actor_is_admin Whether the actor acted with admin rights
actor_api_token The id of the API token the change was made with; absent for the web UI
owner The user the affected device belongs to - different from actor means an admin acted on somebody else's device
remote_addr The address the request came from. Behind a reverse proxy this is the proxy, unless trustedProxies names it
trace.id Ties the record to the other log lines of the same request

Only successful changes are recorded: an action that was refused or failed leaves no record, it is logged as an error instead. Reads - listing devices or users - are not recorded, the web UI polls them constantly and the records would drown everything else.

Logins are logged separately: a new session is logged with the provider and the user, and a failed login attempt is logged with a warning (see Authentication).