# How the Vaultwarden Event Logging System Works: Architecture and Event Types

> Explore the Vaultwarden event logging system architecture and event types. Understand how user actions, ciphers, and more are tracked in this detailed technical breakdown.

- Repository: [Daniel García/vaultwarden](https://github.com/dani-garcia/vaultwarden)
- Tags: architecture
- Published: 2026-03-07

---

**Vaultwarden captures every significant vault action in an `event` database table using a three-tier architecture (Model, Service, API), with event types organized into numeric ranges (1000–2199) that correspond to users, ciphers, collections, groups, and organizations.**

The Vaultwarden event logging system provides comprehensive audit trails for self-hosted password managers. As implemented in the dani-garcia/vaultwarden repository, this subsystem tracks security-relevant actions through a structured database schema and REST API. Understanding the event architecture helps administrators configure retention policies and interpret the numeric event type codes used throughout the system.

## Three-Layer Architecture

### Model Layer ([`src/db/models/event.rs`](https://github.com/dani-garcia/vaultwarden/blob/main/src/db/models/event.rs))

The **Model** layer defines the `Event` struct and the `EventType` enum, which enumerates every supported audit event. This file contains the database schema mapping, serialization logic, and query methods including `Event::clean_events` for retention management.

### Service Layer ([`src/api/core/events.rs`](https://github.com/dani-garcia/vaultwarden/blob/main/src/api/core/events.rs))

The **Service** layer implements the core logging helpers `_log_user_event` and `_log_event`. These functions construct `Event` instances, populate foreign-key fields (`user_uuid`, `org_uuid`, `cipher_uuid`, etc.), and persist records via `Event::save` or `Event::save_user_event`.

### API Layer ([`src/api/core/events.rs`](https://github.com/dani-garcia/vaultwarden/blob/main/src/api/core/events.rs))

The **API** layer exposes read-only endpoints (`/organizations/:id/events`, `/ciphers/:id/events`, `/organizations/:org_id/users/:member_id/events`) and the `/collect` endpoint for client-submitted events. These handlers translate query parameters into `NaiveDateTime` ranges and return paginated JSON responses.

## Event Creation and Routing

All logging operations first check `CONFIG.org_events_enabled()` in [`src/config.rs`](https://github.com/dani-garcia/vaultwarden/blob/main/src/config.rs). If disabled, the function returns immediately without writing to the database.

### User-Centric Events (1000–1099)

User events such as `UserLoggedIn` (1000) and `UserChangedPassword` (1001) flow through `log_user_event` → `_log_user_event`. This creates one global event plus duplicate entries for every organization the user belongs to, ensuring complete audit trails across organizational boundaries.

### Organization and Object Events

Events in ranges 1100–2199 route through `_log_event`, which matches the `event_type` numeric code to populate specific foreign-key columns:

- **1100–1199**: Cipher operations (stores `cipher_uuid`)
- **1300–1399**: Collection changes (stores `collection_uuid`)
- **1400–1499**: Group lifecycle (stores `group_uuid`)
- **1500–1599**: Organization membership (stores `org_user_uuid`)
- **1600–1699**: Organization-level actions (no source object required)
- **1700–1799**: Policy updates

## Querying Events with Pagination

The API returns pages of **30 events** (defined by `Event::PAGE_SIZE` in [`src/db/models/event.rs`](https://github.com/dani-garcia/vaultwarden/blob/main/src/db/models/event.rs)).

When a page contains exactly 30 results, the API generates a **continuation token** containing the ISO-8601 timestamp of the last event. Clients pass this token as the `end` parameter to retrieve subsequent pages, enabling efficient chronological traversal of large audit logs.

## Event Retention and Cleanup

Vaultwarden automatically purges old events via the `event_cleanup_job` defined in [`src/api/core/events.rs`](https://github.com/dani-garcia/vaultwarden/blob/main/src/api/core/events.rs). This background task invokes `Event::clean_events` to delete records older than the number of days specified by `CONFIG.events_days_retain()`, preventing unbounded database growth.

## Complete Reference of Vaultwarden Event Types

The `EventType` enum in [`src/db/models/event.rs`](https://github.com/dani-garcia/vaultwarden/blob/main/src/db/models/event.rs) maps numeric codes to semantic meanings following Bitwarden’s upstream specification:

- **1000–1099**: User actions (`UserLoggedIn`, `UserFailedLogIn`, `UserClientExportedVault`)
- **1100–1199**: Cipher operations (`CipherCreated`, `CipherUpdated`, `CipherAttachmentCreated`, `CipherClientCopiedPassword`)
- **1300–1399**: Collection management (`CollectionCreated`, `CollectionUpdated`)
- **1400–1499**: Group administration (`GroupCreated`, `GroupDeleted`)
- **1500–1599**: Membership events (`OrganizationUserInvited`, `OrganizationUserConfirmed`, `OrganizationUserApprovedAuthRequest`)
- **1600–1699**: Organization changes (`OrganizationUpdated`, `OrganizationPurgedVault`)
- **1700–1799**: Policy modifications (`PolicyUpdated`)
- **1800–2199**: Reserved for future provider, domain, and secret retrieval events (currently unimplemented)

## Practical Examples

### Logging a User Login Event

```rust
use vaultwarden::api::events::log_user_event;
use vaultwarden::db::{DbConn, models::UserId};
use std::net::IpAddr;

let conn: DbConn = /* obtain from pool */;
let user_id = UserId::new("c0ffee-dead-beef-1234");
let device_type = 1; // Browser client
let ip: IpAddr = "203.0.113.42".parse().unwrap();

// Creates global event + per-organization copies
log_user_event(1000, &user_id, device_type, &ip, &conn).await;

```

### Submitting Events via the Collect API

```bash
curl -X POST https://vault.example.com/collect \
  -H "Content-Type: application/json" \
  -d '[
    {"type": 1100, "date": "2024-03-07T12:34:56Z", "cipher_id": "uuid-here"},
    {"type": 1000, "date": "2024-03-07T12:35:10Z"}
  ]'

```

### Retrieving Organization Events

```http
GET /organizations/123e4567-e89b-12d3-a456-426655440000/events?start=2024-03-01T00:00:00Z&end=2024-03-07T23:59:59Z

```

Response includes `continuationToken` for pagination:

```json
{
  "data": [{"type": 1600, "organizationId": "...", "date": "2024-03-06T18:22:10Z"}],
  "object": "list",
  "continuationToken": "2024-03-06T18:22:10Z"
}

```

## Summary

- Vaultwarden event logging uses a three-layer architecture spanning [`src/db/models/event.rs`](https://github.com/dani-garcia/vaultwarden/blob/main/src/db/models/event.rs) (data) and [`src/api/core/events.rs`](https://github.com/dani-garcia/vaultwarden/blob/main/src/api/core/events.rs) (service/API)
- The system creates **duplicate user events** across all user organizations while maintaining **single records** for organization-scoped actions
- Event types are categorized into numeric ranges (1000–2199) covering users, ciphers, collections, groups, and policies
- Pagination uses **continuation tokens** based on timestamps with a fixed page size of 30 events
- Automatic cleanup runs via `event_cleanup_job` respecting the `events_days_retain` configuration

## Frequently Asked Questions

### How do I enable event logging in Vaultwarden?

Set the environment variable `ORG_EVENTS_ENABLED=true` or configure `org_events_enabled` in your settings. This flag is checked at runtime by `_log_user_event` and `_log_event`; when disabled, all logging calls return immediately without database writes.

### What is the difference between user events and organization events?

User events (1000–1099) are replicated across every organization the user belongs to via `_log_user_event`, ensuring compliance visibility for all admins. Organization events (1600–1699) and object-specific events (1100–1599) create single records scoped to specific organizations via `_log_event`.

### How long does Vaultwarden retain audit events?

Retention is controlled by `EVENTS_DAYS_RETAIN` (default varies by configuration). The `event_cleanup_job` periodically executes `Event::clean_events` to hard-delete records older than this threshold from the `event` table.

### Can I query events for specific ciphers or users?

Yes. The API provides targeted endpoints: `/ciphers/:id/events` for item-specific history and `/organizations/:org_id/users/:member_id/events` for per-member audit trails. Both support `start` and `end` date parameters with continuation token pagination.