Skip to content

Architecture

Component topology, deployment patterns, data model, and API surface of Zitadel.

Component Topology

flowchart TB
    subgraph Clients["Client Applications"]
        Browser["Browser\n(Login UI)"]
        Console["Console\n(Angular Admin UI)"]
        Apps["Your Applications\n(Web, Mobile, API)"]
    end

    subgraph Zitadel["Zitadel Server (Go Binary)"]
        direction TB
        APIGW["API Gateway\n(connectRPC / gRPC / REST)"]

        subgraph Services["Domain Services"]
            AuthSvc["Authentication\nService"]
            UserSvc["User\nService"]
            OrgSvc["Organization\nService"]
            ProjSvc["Project\nService"]
            SessSvc["Session\nService"]
            ActSvc["Actions\nService"]
        end

        subgraph Core["Core Engine"]
            Cmd["Command Handlers\n(Write Path)"]
            Query["Query Handlers\n(Read Path)"]
            ES["Eventstore\n(Events2 Table)"]
            ProjW["Projection\nWorkers"]
        end

        subgraph Ext["Extensibility"]
            Actions["JS Actions\nRuntime"]
            Hooks["Webhook\nDispatcher"]
            SCIM["SCIM 2.0\nServer"]
        end
    end

    subgraph Data["Data Layer"]
        PG["PostgreSQL\n(Events + Projections)"]
        Redis["Redis\n(Query Cache)"]
    end

    subgraph External["External Systems"]
        IdP["Identity Providers\n(Google, GitHub, Azure AD)"]
        SMTP["SMTP / SMS\n(Notifications)"]
        OTel["OpenTelemetry\nCollector"]
    end

    Browser --> APIGW
    Console --> APIGW
    Apps --> APIGW

    APIGW --> Services
    Services --> Core
    Cmd --> ES
    ProjW --> ES
    Query --> Redis
    ProjW --> PG
    ES --> PG
    Query --> PG

    APIGW --> Ext
    Actions --> Hooks
    SCIM --> UserSvc

    AuthSvc --> IdP
    ActSvc --> SMTP
    APIGW --> OTel

    style Zitadel fill:#1565c0,color:#fff
    style Data fill:#2e7d32,color:#fff
    style External fill:#e65100,color:#fff

Monolithic Binary, Modular Internals

Zitadel ships as a single Go binary with modular internal packages:

Package Purpose
internal/eventstore/ Event store abstraction, PostgreSQL implementation, push/pop logic
internal/command/ Write-side command handlers, business rule validation
internal/query/ Read-side query handlers, projection consumers
internal/auth/ OIDC, OAuth 2.0, SAML 2.0 protocol implementations
internal/authz/ Authorization resolution, permission checking
internal/api/ gRPC/REST API layer, connectRPC integration
internal/actions/ JavaScript runtime for custom auth flows
internal/crypto/ Encryption, hashing, key management
internal/webauthn/ FIDO2/WebAuthn authentication
internal/notification/ Email, SMS delivery pipeline
internal/idp/ External identity provider integrations
internal/user/ User domain logic (human + machine)
internal/org/ Organization domain logic
internal/project/ Project, application, role management
internal/i18n/ Internationalization
internal/cache/ Redis caching abstraction

API Surface

Zitadel exposes a dual-versioned API surface:

V1 APIs (Legacy, Context-Based)

API Scope Base Path Purpose
Auth API Per-user /auth/v1/ User-specific ops (profile, tokens, MFA)
Management API Per-org /management/v1/ Org admin ops (users, projects, apps)
Admin API Instance-wide /admin/v1/ Instance-level configuration
System API Self-hosted /system/v1/ Superordinate control

V2 APIs (Current, Resource-Oriented)

API Base Path Purpose
User Service /v2/users/ User management (preferred for new integrations)
Session Service /v2/sessions/ Session lifecycle management
Organization Service /v2/orgs/ Organization management
Project Service /v2/projects/ Projects, apps, roles
Action Service /v2/actions/ Actions and webhooks
Feature Service /v2/features/ Feature flag management
Settings Service /v2/settings/ Login, password, branding policies
Authorization Service /v2/authorizations/ User authorization (grants)
OIDC Service Standard OIDC endpoints OpenID Connect operations
SAML Service Standard SAML endpoints SAML 2.0 operations
SCIM 2.0 /scim/v2/{orgID}/ User provisioning
Web Key Service /v2/webkeys/ JWT signing key management

API Conventions

Operation Method Pattern
Create POST /v2/<resource>
Update POST /v2/<resource>/<id>
Delete DELETE /v2/<resource>/<id>
Set PUT /v2/<resource>
Get GET /v2/<resource>/<id>
Search POST /v2/<resource>/search

All APIs are generated from Protocol Buffer definitions in proto/zitadel/. The server supports connectRPC, standard gRPC, and HTTP/1.1 simultaneously.

Data Model

Hierarchical Resource Model

erDiagram
    INSTANCE ||--o{ ORGANIZATION : contains
    ORGANIZATION ||--o{ USER : manages
    ORGANIZATION ||--o{ PROJECT : owns
    ORGANIZATION ||--o{ IDP_CONFIG : configures
    ORGANIZATION ||--o{ POLICY : enforces
    PROJECT ||--o{ APPLICATION : registers
    PROJECT ||--o{ ROLE : defines
    PROJECT ||--o{ PROJECT_GRANT : shares
    USER ||--o{ USER_GRANT : receives
    USER ||--o{ SESSION : creates
    USER ||--o{ IDP_LINK : links
    PROJECT_GRANT }o--|| ORGANIZATION : "granted to"
    USER_GRANT }o--|| PROJECT : "scoped to"
    USER_GRANT }o--|| USER : "assigned to"
    ROLE }o--|| USER_GRANT : "included in"

    INSTANCE {
        string instance_id PK
        string domain
    }
    ORGANIZATION {
        string org_id PK
        string name
        string state
    }
    USER {
        string user_id PK
        string org_id FK
        string type
        string state
        string email
        string phone
    }
    PROJECT {
        string project_id PK
        string org_id FK
        string name
        string state
    }
    APPLICATION {
        string app_id PK
        string project_id FK
        string name
        string type
    }
    ROLE {
        string role_key PK
        string project_id FK
        string display_name
        string group
    }
    PROJECT_GRANT {
        string grant_id PK
        string project_id FK
        string granted_org_id FK
        string state
    }
    USER_GRANT {
        string grant_id PK
        string user_id FK
        string project_id FK
        string org_id FK
    }

Eventstore Schema

The eventstore uses a custom PostgreSQL schema optimized for append-only operations:

Table Purpose
eventstore.events2 Immutable event log with composite PK (aggregate_id, sequence)
eventstore.unique_constraints Enforces uniqueness across events (for example, unique email per instance)
projections.* Materialized read-model tables rebuilt from events

Events are inserted atomically via the eventstore.push PostgreSQL function, which handles sequence assignment and unique constraint checking in a single transaction.

Deployment Topologies

Single Instance (Development)

flowchart LR
    subgraph Host["Docker Host"]
        ZA["Zitadel\n:8080"]
        PG["PostgreSQL 17\n:5432"]
    end

    ZA --> PG

Kubernetes Production (HA)

flowchart TB
    subgraph LB["Load Balancer"]
        NGINX["NGINX Ingress\n(TLS Termination)"]
    end

    subgraph K8s["Kubernetes Cluster"]
        subgraph ZA["Zitadel Pods (HPA)"]
            Z1["zitadel-7d4f8"]
            Z2["zitadel-a3b2c"]
            Z3["zitadel-e9f1g"]
        end

        Init["Init Job\n(DB Migrations)"]
        Setup["Setup Job\n(Schema + Defaults)"]

        subgraph Data["Data Plane"]
            PGHA["PostgreSQL HA\n(Patroni / CloudSQL / RDS)"]
            Redis["Redis\n(Cache)"]
        end
    end

    NGINX --> Z1
    NGINX --> Z2
    NGINX --> Z3
    Z1 --> PGHA
    Z2 --> PGHA
    Z3 --> PGHA
    Z1 --> Redis
    Z2 --> Redis
    Z3 --> Redis

    style K8s fill:#1565c0,color:#fff
    style Data fill:#2e7d32,color:#fff

Multi-Cluster / SaaS

flowchart TB
    subgraph Cloud["Zitadel Cloud"]
        ZC["Managed Zitadel\nInstance"]
        PG_C["Managed PostgreSQL"]
    end

    subgraph Remote1["Remote App Cluster 1"]
        App1["Application A"]
    end

    subgraph Remote2["Remote App Cluster 2"]
        App2["Application B"]
    end

    App1 -->|"OIDC / SAML"| ZC
    App2 -->|"OIDC / SAML"| ZC
    ZC --> PG_C

    style Cloud fill:#1565c0,color:#fff

Sources


How It Works

How Zitadel's event-sourced CQRS engine processes commands, projects read models, handles authentication sessions, and triggers extensibility actions.

Event Sourcing Pipeline

Zitadel's core architecture is built on Command Query Responsibility Segregation (CQRS) with event sourcing. Every state mutation flows through a strict pipeline:

flowchart LR
    subgraph Write["Write Path"]
        API["API Layer\n(gRPC / REST)"]
        Cmd["Command\nValidation"]
        ES["Eventstore\n(PostgreSQL)"]
    end

    subgraph Project["Projection Workers"]
        Sub["Event\nSubscriber"]
        RM["Read Model\nTables"]
    end

    subgraph Read["Read Path"]
        Query["Query\nHandlers"]
        Cache["Redis\nCache"]
    end

    API --> Cmd --> ES
    ES --> Sub --> RM
    Query --> Cache --> RM

Write Path — Command Processing

  1. API layer receives a gRPC or REST request, authenticates the caller via Bearer token
  2. Authorization check — the proto-annotated permission (for example, user.write) is validated against the caller's memberships
  3. Command handler validates business rules (unique constraints, state preconditions, policy compliance)
  4. Event creation — the handler produces one or more domain events (for example, user.human.added, user.human.password.changed)
  5. Atomic persistence — events are appended to the eventstore.events2 table in PostgreSQL using the custom eventstore.push function
  6. Unique constraint enforcement — PostgreSQL advisory locks (pg_advisory_xact_lock_shared) prevent concurrent creation of duplicate resources

Event Structure

Every event in the store contains:

Field Purpose
aggregate_type Entity category (user, org, project, and more)
aggregate_id Unique entity identifier
event_type Specific mutation (for example, user.human.added)
sequence Per-aggregate monotonically increasing counter
position Global ordering across all aggregates
instance_id Tenant isolation key
creation_date Timestamp
payload Protocol Buffer encoded event data

Read Path — Projection Workers

  1. Background projections subscribe to the event stream
  2. Each projection builds an optimized read-model table (for example, projections.users, projections.orgs, projections.project_roles)
  3. Projections track their last processed position. This enables idempotent replay
  4. Query handlers serve API reads from projection tables
  5. Redis cache (optional) reduces database load for hot queries

Authentication Session Lifecycle

OIDC Authorization Code Flow (with PKCE)

sequenceDiagram
    participant User as Browser
    participant App as Application
    participant ZA as Zitadel
    participant ES as Eventstore

    User->>App: Click "Login"
    App->>ZA: GET /oauth/v2/authorize?client_id=...&redirect_uri=...&code_challenge=...
    ZA->>User: Show Login UI (passkey / password / IdP)
    User->>ZA: Authenticate (WebAuthn / password / MFA)
    ZA->>ES: Append session.created, auth.succeeded events
    ZA->>App: 302 Redirect with authorization code
    App->>ZA: POST /oauth/v2/token (code + code_verifier)
    ZA->>ES: Append token.issued event
    ZA->>App: Access token + ID token + refresh token
    App->>ZA: GET /api (Authorization: Bearer <access_token>)
    ZA->>App: JSON response

Session Model (V2 API)

Zitadel's V2 Session API provides fine-grained session management:

  1. Session creation — POST /v2/sessions creates a new session with an optional initial check (password, passkey, or IdP)
  2. Session updates — PUT /v2/sessions/{id} adds further checks (MFA step, TOTP verification)
  3. Session token — returned after successful authentication, used for stateless session tracking
  4. Session termination — DELETE /v2/sessions/{id} invalidates the session

Passkey Authentication Flow

sequenceDiagram
    participant Browser as Browser
    participant ZA as Zitadel Server
    participant Auth as Authenticator\n(Platform/Roaming)

    Browser->>ZA: POST /v2/sessions (user identifier)
    ZA->>ZA: Generate WebAuthn challenge
    ZA->>Browser: Return challenge + allowed credentials
    Browser->>Auth: navigator.credentials.get({challenge, ...})
    Auth->>Auth: User verification (biometric / PIN)
    Auth->>Browser: Signed assertion
    Browser->>ZA: POST /v2/sessions/{id} (assertion response)
    ZA->>ZA: Verify signature against stored public key
    ZA->>ZA: Append session.checked.webauthn event
    ZA->>Browser: Session token

Authorization Resolution

When an API request arrives, Zitadel resolves permissions through:

  1. Token introspection — extract user ID and scope from the Bearer token
  2. Membership lookup — query the user's memberships across organizations, projects, and IAM scopes
  3. Role resolution — for each membership, resolve the assigned roles and their associated permissions
  4. Context filtering — if the permission requires a context (for example, project.write:project-123), verify the target resource matches
  5. Grant policy check — verify the request passes the organization's policy constraints
flowchart TB
    Req["Incoming API Request"]
    Token["Token Introspection"]
    Member["Membership\nLookup"]
    Roles["Role\nResolution"]
    Perms["Permission\nExpansion"]
    Check["Context\nCheck"]
    Allow{"Allowed?"}

    Req --> Token --> Member --> Roles --> Perms --> Check --> Allow
    Allow -->|Yes| Handler["API Handler"]
    Allow -->|No| Deny["403 Permission Denied"]

Actions & Webhooks Pipeline

Zitadel's extensibility layer intercepts authentication and registration flows:

  1. Trigger point — a defined event (for example, pre creation, post creation, pre userinfo, post authentication) fires
  2. Action execution — JavaScript functions (V2 Actions) or HTTP targets are invoked
  3. Request modification — actions can modify tokens, add claims, block requests, or redirect users
  4. Webhook delivery — events are POSTed to configured HTTP endpoints with full event payload
  5. Error handling — failed actions can be configured to block or allow the flow

Action Execution Context

// Example V2 Action: Add custom claims to token
function addAction(token, api) {
    // token contains current JWT claims
    // api provides methods to modify the response
    api.v1.user.setMetadata("custom_role", "premium");
    // Block the request if condition not met
    if (!isAuthorized(token.user)) {
        api.v1.blockRequest("User not authorized");
    }
}

Multi-Tenancy Hierarchy

Zitadel enforces a strict hierarchy for resource isolation:

flowchart TB
    Instance["Instance\n(Self-hosted deployment\nor Zitadel Cloud tenant)"]
    Org1["Organization A\n(Tenant)"]
    Org2["Organization B\n(Tenant)"]
    Proj1A["Project: Billing API"]
    Proj1B["Project: User Portal"]
    Proj2A["Project: Partner Portal"]
    App1["App: Web Client\n(OIDC)"]
    App2["App: Mobile App\n(OIDC)"]
    App3["App: API Service\n(SAML)"]
    Roles1["Roles: admin, editor, viewer"]
    Grant1["Project Grant\n→ Org B"]

    Instance --> Org1
    Instance --> Org2
    Org1 --> Proj1A
    Org1 --> Proj1B
    Org2 --> Proj2A
    Proj1A --> App1
    Proj1A --> App2
    Proj1B --> App3
    Proj1A --> Roles1
    Proj1A -.->|"Delegates\nadmin, editor"| Grant1
    Grant1 -.-> Org2

Project Grants enable cross-organization authorization: Organization A defines roles on a project and grants a subset to Organization B. Organization B can then self-manage user assignments within the delegated roles.

Projection Rebuilding

Because all state is derived from events, read models can be rebuilt from scratch:

  1. Truncate the projection table
  2. The projection worker replays all events from position 0
  3. The read model is reconstructed to match the current event log
  4. This enables zero-downtime schema migrations and data repairs

Sources


Security

Authentication mechanisms, authorization model, encryption, audit capabilities, and threat considerations for Zitadel.

Authentication Mechanisms

Protocol Support Matrix

Protocol Type Status Use Case
OpenID Connect Standard Certified Web and mobile SSO, API authentication
OAuth 2.0 Standard Full API authorization, machine-to-machine
SAML 2.0 Standard Full Enterprise SSO, legacy application integration
WebAuthn / FIDO2 Standard First-class Passwordless authentication, passkeys
LDAP Standard Supported Active Directory / LDAP integration

OIDC Configuration

Zitadel implements a full OpenID Provider with these endpoints:

Endpoint Path
Discovery /.well-known/openid-configuration
Authorization /oauth/v2/authorize
Token /oauth/v2/token
Userinfo /oauth/v2/userinfo
Keys (JWKS) /oauth/v2/keys
Revocation /oauth/v2/revoke
Device Authorization /oauth/v2/device
End Session /oidc/v1/end_session

Supported grant types: Authorization Code (with PKCE), Implicit, Client Credentials, Device Code, Resource Owner Password, Token Exchange.

Multi-Factor Authentication

Method Type Implementation
TOTP (OTP) Time-based Standard RFC 6238, compatible with Google Authenticator, Authy
WebAuthn (U2F) Hardware key FIDO U2F security keys (YubiKey, and more)
Email OTP Out-of-band One-time code sent to registered email
SMS OTP Out-of-band One-time code sent via SMS

Passkeys / Passwordless

Zitadel treats FIDO2/WebAuthn as a primary authentication method:

  • Platform authenticators (Touch ID, Windows Hello, Android fingerprint)
  • Roaming authenticators (YubiKey, Titan Key)
  • Cloud-synced passkeys (iCloud Keychain, Google Password Manager)
  • User verification configurable: required, preferred, discouraged
  • Username + passkey login eliminates password entry entirely

External Identity Providers

Pre-built templates for federated authentication:

Category Providers
Social Google, GitHub, GitLab, Apple
Enterprise Azure AD, Generic OIDC, Generic SAML, LDAP
Custom Any OIDC or SAML 2.0 compliant IdP

Machine-to-Machine Authentication

Method Mechanism Use Case
JWT Profile Private key JWT (RSA/EC) Service accounts with signed JWTs
Personal Access Tokens Static Bearer token Developer API access, CI/CD pipelines
Client Credentials Client ID + secret Simple M2M (less secure than JWT Profile)

Authorization Model

Hierarchical RBAC

Zitadel implements multi-tenant RBAC across three levels:

Instance (IAM-level roles)
  └── Organization (org-level roles)
       └── Project (project-level roles)

Permission Resolution

Permissions follow a dual-mode pattern:

Mode Syntax Meaning
Context-scoped project.write:project-123 Permission applies to a specific resource
Global project.write Permission applies to all resources of that type

Resolution flow: 1. Extract user ID from Bearer token 2. Load user's memberships (org, project, IAM level) 3. Expand the roles of each membership into permissions 4. Apply context filtering for resource-scoped permissions 5. Deny if no matching permission found

Project Grants (Cross-Org Authorization)

Project grants enable B2B multi-tenant scenarios:

  1. Project owner (Organization A) defines roles on a project
  2. Grant creation — owner selects a subset of roles and grants them to Organization B
  3. Self-management — The admins of Organization B assign users to the granted roles
  4. Revocation — owner can revoke the grant at any time, immediately removing access

This is the core mechanism for SaaS providers offering customer-specific access control.

Membership Scopes

Member Type Scope Example
IAM Instance-wide IAM_OWNER, IAM_ORG_MANAGER
ORG Organization ORG_OWNER, ORG_USER_MANAGER
PROJECT Project PROJECT_OWNER, PROJECT_OWNER_VIEWER
PROJECT_GRANT Granted project Self-managed by grantee org

Encryption & Key Management

Data Encryption

Layer Mechanism
At rest PostgreSQL encryption (TDE or disk-level)
In transit TLS 1.2+ for all connections (API, database, LDAP)
Secrets AES-256 encryption for stored secrets using master key
Passwords bcrypt hashing with configurable cost
Master key 32-byte key passed via --masterkey flag or ZITADEL_MASTERKEY env var

JWT Signing Keys

  • RSA and EC key pairs generated automatically
  • Key rotation supported via the Web Key Service API (/v2/webkeys/)
  • JWKS endpoint publishes public keys at /oauth/v2/keys
  • Keys stored encrypted in the eventstore

Audit Trail

Because Zitadel uses event sourcing, every state mutation produces an immutable event:

Capability Detail
Complete audit log Every create, update, delete on any entity is an event
Temporal queries Reconstruct state at any point in time by replaying events
Export Events API for programmatic access. Log store senders for SIEM
Non-repudiation Events cannot be modified after creation (append-only)
Actor tracking Each event records which user/service did the action

SOC/SIEM Integration

Events can be forwarded to external security systems: - Log store senders — configure webhook targets for real-time event streaming - OpenTelemetry — traces and metrics for observability correlation - GCP Error Reporting — via zitadel/sloggcp package

Threat Model

Attack Vectors & Mitigations

Threat Mitigation
Credential theft Passkeys (phishing-resistant), MFA enforcement, token rotation
Token replay Short-lived access tokens, refresh token rotation, token binding
Privilege escalation Hierarchical RBAC, context-scoped permissions, membership auditing
Data exfiltration Encryption at rest, TLS in transit, instance isolation
SQL injection Parameterized queries via Go database/sql, proto-generated API
XSS Login UI is Next.js with CSP headers. Admin console is Angular
CSRF OIDC state parameter, PKCE enforcement for public clients
Brute force Rate limiting, account lockout policies, password complexity rules
Phishing FIDO2/WebAuthn as primary auth (phishing-resistant by design)
Insider threat Immutable audit trail, separation of duties via role hierarchy

Security Policies

Administrators can enforce policies at the instance or organization level:

Policy Scope Controls
Login policy Instance / Org Allowed auth methods, MFA requirement, IdP allowlist
Password policy Instance / Org Min length, complexity, expiry, lockout
Privacy policy Instance / Org TOS URL, privacy policy URL, support email
Branding policy Org Logo, colors, font, theme for login UI
Domain policy Instance User login must match org domain, domain verification

SCIM 2.0 User Provisioning

Built-in SCIM 2.0 server for enterprise user lifecycle management:

Operation Method Endpoint
Create user POST /scim/v2/{orgID}/Users
Get user GET /scim/v2/{orgID}/Users/{id}
List users GET /scim/v2/{orgID}/Users
Replace user PUT /scim/v2/{orgID}/Users/{id}
Update user PATCH /scim/v2/{orgID}/Users/{id}
Delete user DELETE /scim/v2/{orgID}/Users/{id}

Permissions are mapped to Zitadel's internal RBAC: User Write, User Read, User Delete.

Sources