Overview
ZITADEL implements a hybrid data model that combines relational databases with event sourcing. This architecture provides:- Fast queries: Current state stored in relational tables
- Complete audit trail: Every mutation recorded as an immutable event
- Temporal queries: View system state at any point in time
- Event streaming: Export events to external systems for SIEM integration
Architecture Pattern: Relational data is the system of record for current state. Events provide a complete history and audit trail, but are not the primary source of truth.
How It Works
Write Path: Commands → Events → State
Every mutation in ZITADEL follows this pattern:- Client makes request: Create user, update organization, etc.
- Command layer validates: Business rules, constraints, permissions
- Event is generated: Immutable record of what changed
- Transaction writes both:
- Event to
eventstable - Current state to relational table (e.g.,
users)
- Event to
- Response includes: Generated IDs and timestamps
Code Example: Event Push
Code Example: Event Push
Read Path: Query Relational State
For current state, ZITADEL queries relational tables: Benefits:- Fast lookups by ID, email, username, etc.
- Efficient filtering and pagination
- Standard SQL indexes and query optimization
- No event replay needed for current state
Audit Path: Query Event History
For audit trails and compliance, query the event log:List Events for a User
Event Structure
Event Properties
Every event in ZITADEL contains:| Property | Description | Example |
|---|---|---|
| id | Unique event identifier | "163840776835432709" |
| type | Event type (what happened) | "user.created" |
| aggregateType | Resource type | "user", "org", "project" |
| aggregateId | Resource identifier | "163840776835432705" |
| sequence | Sequential position in aggregate | 1, 2, 3, … |
| timestamp | When the event occurred | "2024-03-01T10:00:00Z" |
| data | Event payload (what changed) | JSON object with change details |
| editor | User/service that made the change | "admin@example.com" |
| instanceId | Instance where event occurred | For multi-tenancy isolation |
Aggregate Types
Events are organized by aggregate — the entity that changed:- user: User creation, updates, deletions
- org: Organization changes
- project: Project and application changes
- instance: Instance-level settings
- iam: System-level changes
- session: Authentication sessions
- action: Action (webhook/script) execution
Event Type Registration
Event Type Registration
From This registration system ensures type safety and proper event handling.
internal/eventstore/eventstore.go:Common Event Types
User Events
user.created— New user addeduser.updated— Profile information changeduser.deleted— User removeduser.email.changed— Email address updateduser.email.verified— Email verification completeduser.phone.changed— Phone number updateduser.password.changed— Password updateduser.mfa.otp.added— TOTP authenticator addeduser.locked— Account lockeduser.unlocked— Account unlocked
Organization Events
org.created— Organization createdorg.updated— Organization details changedorg.domain.added— Custom domain addedorg.domain.verified— Domain ownership verifiedorg.member.added— Member granted org access
Project Events
project.created— Project createdproject.updated— Project settings changedproject.role.added— New role definedproject.application.added— Application added to projectproject.grant.added— Project granted to another organization
Authentication Events
session.created— User logged insession.updated— Session refreshedsession.terminated— User logged outuser.token.added— Personal access token created
Benefits of Event Sourcing
1. Complete Audit Trail
Every change is recorded with:- What changed: Event type and data
- When: Precise timestamp
- Who: User or service that made the change
- Where: Instance and organization context
Unlike systems that only log select activities, ZITADEL provides a comprehensive event stream of all mutations. Nothing is lost or overwritten.
2. Compliance and Forensics
Event sourcing supports:- SOC 2 compliance: Detailed audit logs for access reviews
- GDPR: Track all data changes and access
- Forensic analysis: Reconstruct exactly what happened during an incident
- Regulatory reporting: Export filtered event streams
3. Temporal Queries
View the system state at any point in time:What was the user's email on March 1st?
4. Event Streaming to External Systems
Integrate with SIEM, data warehouses, and analytics:- Webhooks: Real-time event delivery to your endpoints
- Actions: Execute custom code on specific events
- Event log API: Poll for new events
- Database replication: Stream events to external databases
Event Guarantees
Immutability
Events are never modified or deleted:- Once written, events are permanent
- Corrections are new events (e.g.,
user.email.changedagain) - True append-only log
Ordering
Within an aggregate, events are strictly ordered:- Sequence numbers: Monotonically increasing per aggregate
- No gaps: Sequence 1, 2, 3, … with no missing values
- Causal ordering: Events reflect the actual order of operations
Atomicity
Events and state changes are atomic:- Written in a single database transaction
- Either both succeed or both fail
- No inconsistency between events and relational state
Querying Events
Filter by Aggregate
Get all events for a specific resource:Filter by Type
Get specific event types across all aggregates:Filter by Time Range
Get events within a specific period:Pagination
Handle large event streams:Performance Considerations
Why Relational State Matters
Event sourcing without relational state requires:- Replaying all events to get current state (slow for old aggregates)
- Complex event handlers and projections
- Eventually consistent read models
- Instant access to current state (no replay needed)
- Simple queries using standard SQL
- Strong consistency (events and state written together)
Event Table Growth
Events accumulate over time:- Consider partitioning by time (PostgreSQL table partitions)
- Archive old events to cheaper storage
- Keep recent events in high-performance storage
Event table growth is linear with activity, not users. A million users who never change generate no events after creation.
Best Practices
Use Events for Audit, State for Queries
Use Events for Audit, State for Queries
- Query relational tables for current state (fast)
- Query events for audit trails and compliance
- Don’t replay events to get current state
Monitor Event Growth
Monitor Event Growth
- Track event table size
- Set up alerts for unusual event volume
- Plan for archival of old events
Stream Events to External Systems
Stream Events to External Systems
- Use webhooks for real-time integration
- Poll event API for batch processing
- Consider event ordering when processing
Retain Events for Compliance
Retain Events for Compliance
- Define retention policy based on regulations
- Archive (don’t delete) old events
- Encrypt event data at rest
Test Event Handling
Test Event Handling
- Verify events are created for all mutations
- Test webhook delivery and retries
- Validate event payload structure
Next Steps
Architecture
Learn about the overall system architecture
Actions & Webhooks
React to events with custom code and webhooks
Audit Log Integration
Export events to SIEM and compliance systems
API Reference
Explore the event query API