Event Types
SeaWatch writes 24 event types, grouped below by what they are about. Each carries a success or failure status alongside its details.
Authentication
| Event | Description | Actor | Target |
|---|---|---|---|
login_success | User successfully authenticated | User | User |
login_failure | Authentication attempt failed | None | User (if identified) |
login_success details:
{
"client_id": "my-frontend",
"grant_type": "password",
"ip_address": "203.0.113.42"
}
login_failure details:
{
"reason": "invalid_credentials",
"client_id": "my-frontend",
"username": "alice"
}
Failed logins have no actor
When a login fails, the user is not yet authenticated, so actor_id is typically null. The target_id is set when the user could be identified (valid username, wrong password).
Password
| Event | Description | Actor | Target |
|---|---|---|---|
password_reset | Password was reset by an admin | Admin | User |
password_reset_requested | User requested a password reset email | System | User |
password_reset_completed | User completed the password reset flow | User | User |
These three events trace the full password reset lifecycle. A password_reset_requested without a matching password_reset_completed might indicate a user who abandoned the flow, or an attacker testing email enumeration.
User
| Event | Description | Actor | Target |
|---|---|---|---|
user_created | New user account created | Admin or System | User |
user_email_verified | A user confirmed their email address | User | User |
user_deleted | User account deleted | Admin | User |
User creation events record whether the account came from an admin, from self-registration, or from a client’s service account.
Role
| Event | Description | Actor | Target |
|---|---|---|---|
role_assigned | Role assigned to a user | Admin | User |
role_unassigned | Role removed from a user | Admin | User |
role_created | New role created | Admin | Role |
role_removed | Role deleted | Admin | Role |
Role events are critical for access control audits. They answer: “Who granted this user admin permissions, and when?”
Client
| Event | Description | Actor | Target |
|---|---|---|---|
client_created | New OAuth2 client registered | Admin | Client |
client_deleted | Client removed | Admin | Client |
client_secret_rotated | Client secret was regenerated | Admin | Client |
client_secret_viewed | Someone read a client’s secret | Admin | Client |
client_secret_rotated invalidates the old secret, so an unexpected one is worth a look. client_secret_viewed is the quieter sibling: nothing changed, but a credential left the system, and that is exactly the trail you want during an incident.
Sessions
| Event | Description | Actor | Target |
|---|---|---|---|
session_created | A user session was established | User | User |
session_revoked | A session was revoked | Admin or User | User |
Maintenance
| Event | Description | Actor | Target |
|---|---|---|---|
client_maintenance_enabled | Maintenance mode was turned on | Admin | Client |
client_maintenance_disabled | Maintenance mode was turned off | Admin | Client |
Maintenance mode blocks access while it is on, subject to a whitelist. Both transitions are recorded, so a window that stayed open longer than intended is visible after the fact.
| Event | Description | Actor | Target |
|---|---|---|---|
email_sent | A transactional email was handed to the SMTP server | System | User |
email_not_sent | Delivery failed | System | User |
email_not_sent is the one to alert on. A password reset that never left the building looks identical to a user ignoring it, unless you are watching this event.
Federation
| Event | Description | Actor | Target |
|---|---|---|---|
identity_provider_link_removed | A federated identity was unlinked from a user | Admin or User | User |
Configuration
| Event | Description | Actor | Target |
|---|---|---|---|
realm_config_changed | Realm settings were modified | Admin | Realm |
Configuration changes reach far: turning registration on, changing a token lifetime, lowering a lockout threshold. SeaWatch records what changed, not just that something did.
The `unknown` type
Reading an event whose stored type matches no known variant yields unknown rather than a guess. A row written by a newer version, or a corrupt one, can never be misread as a successful login.