0a08e7269a fix(eventstore): omit owners on empty unique constraint inserts (#12785)
<!-- CURSOR_AGENT_PR_BODY_BEGIN -->
# Which Problems Are Solved

After #12740, setup on existing deployments (v4.17.x → v4.18.0) fails
before migration 78 can add `eventstore.unique_constraints.owners`.
Starting any setup step pushes `system.migration.started`, which inserts
a unique constraint that names the `owners` column. On upgraded
databases that column does not exist yet, so setup dies on
`76_users14_login_equality_indexes` with `SQLSTATE 42703`.

# How the Problems Are Solved

Empty-owner unique-constraint inserts no longer name the `owners`
column:

- `len(Owners) == 0` → `INSERT (instance_id, unique_type, unique_field)`
- tagged `WithOwners(...)` → unchanged `INSERT (..., owners)`

Setup started/done events never stamp owners, so 76–78 can start.
Migration 78 still adds the column; after that, omitted `owners` uses
`DEFAULT '{}'`. Both v2 and v3 pushers apply the same rule. Mixed pushes
split into two INSERT batches so tagged and empty rows do not share a
column list.

# Additional Changes

- v2 SQL mocks now expect the three-column INSERT for untagged adds,
plus tagged and mixed cases
- Pusher tests cover mixed tagged+empty adds and adds against a table
that does not have `owners` yet
- The missing-column pusher test drops `owners` and pushes in one
transaction (`PushWithClient`), then rolls back so the shared test
schema is not mutated. Other tests may stall on `AccessExclusiveLock`
until rollback.

# Additional Context

- Closes #12780
- Follow-up for PR #12740
- Operators do not need to re-run init or apply DDL outside setup

Verified locally:

- `go test ./internal/eventstore ./internal/v2/eventstore/postgres
./internal/eventstore/v3` (including `-race` on those packages)
- Empty-owner insert succeeds when the `owners` column is missing;
tagged insert still requires the column

<!-- CURSOR_AGENT_PR_BODY_END -->

<div><a
href="https://cursor.com/agents/bc-31bfb5c4-5888-43f0-85cf-af1af097ca30?cursor_ref=pr_footer&cursor_cta=open_in_web"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://cursor.com/assets/images/open-in-web-dark.png"><source
media="(prefers-color-scheme: light)"
srcset="https://cursor.com/assets/images/open-in-web-light.png"><img
alt="Open in Web" width="114" height="28"
src="https://cursor.com/assets/images/open-in-web-dark.png"></picture></a>&nbsp;<a
href="https://cursor.com/background-agent?bcId=bc-31bfb5c4-5888-43f0-85cf-af1af097ca30&cursor_ref=pr_footer&cursor_cta=open_in_cursor"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://cursor.com/assets/images/open-in-cursor-dark.png"><source
media="(prefers-color-scheme: light)"
srcset="https://cursor.com/assets/images/open-in-cursor-light.png"><img
alt="Open in Cursor" width="131" height="28"
src="https://cursor.com/assets/images/open-in-cursor-dark.png"></picture></a>&nbsp;</div>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Silvan <[email protected]>
2026-09-22 15:36:44 +00:00
2023-07-17 10:08:20 +02:00
2026-09-04 06:13:37 +02:00
2024-07-26 22:39:55 +02:00
2021-02-08 16:48:41 +01:00
2025-04-02 16:53:06 +02:00
2026-02-04 11:51:43 +01:00
2026-02-25 19:36:00 -03:00
2026-06-10 11:53:26 +00:00

ZITADEL Logo ZITADEL Logo

GitHub Workflow Status (with event) GitHub contributors

The Identity Infrastructure for Developers

ZITADEL is an open-source identity and access management platform built for teams that need more than basic auth. Whether you're securing a SaaS product, building a B2B platform, or self-hosting a production IAM stack — ZITADEL gives you everything out of the box: SSO, MFA, Passkeys, OIDC, SAML, SCIM, and a battle-tested multi-tenancy model.

No vendor lock-in. No compromise on control. Just a robust, API-first identity platform you can own.


🏡 Website  |  💬 Chat  |  📋 Docs  |  🧑‍💻 Blog  |  📞 Contact


Why ZITADEL

We built ZITADEL to handle the hardest IAM challenges at scale — starting with multi-tenancy.

ZITADEL FusionAuth Keycloak Auth0/Okta
Open-source ✅ ❌ ✅ ❌
Self-hostable ✅ ✅ ✅ ❌
Infrastructure-level tenants ✅ Instances (High scale) ✅ Tenants 🟡 Realms (Scaling limits) ❌ (Multi-tenant = multi-account)
B2B Organizations ✅ Native & Unlimited 🟡 via Entity Management ✅ (Recent addition) 🟡 (Plan/Account dependent)
Full audit trail ✅ Comprehensive Event Stream* 🟡 Audit logs 🟡 Audit logs 🟡 Audit logs
Passkeys (FIDO2) ✅ ✅ ✅ ✅
Actions / webhooks ✅ ✅ 🟡 via SPI ✅
API-first (gRPC + REST) ✅ 🟡 REST only 🟡 REST only 🟡 REST only
SaaS + self-host parity ✅ ✅ ➖ N/A ➖ N/A

ZITADEL Cloud and self-hosted ZITADEL run the same codebase.

Key differentiators for architects:

  • Relational core, event-driven soul — every mutation is written as an immutable event for a complete, API-accessible audit trail. Unlike systems that log only select activities, ZITADEL provides a comprehensive event stream that can be audited or streamed to external systems via Webhooks.
  • Strict multi-tenant hierarchy — Identity System → Organizations → Projects, with isolated data and policy scoping at multiple levels
  • API-first design — every resource and action is available via connectRPC, gRPC, and HTTP/JSON APIs
  • Zero-downtime updates and horizontal scalability without external session stores

Get Started in 3 Minutes

👉 Quick Start Guide

ZITADEL Self-Hosted

# Docker Compose — up and running in under 3 minutes
curl -LO https://raw.githubusercontent.com/zitadel/zitadel/main/deploy/compose/docker-compose.yml \
  && curl -LO https://raw.githubusercontent.com/zitadel/zitadel/main/deploy/compose/.env.example \
  && cp .env.example .env \
  && docker compose up -d --wait

Full deployment guides:

Need professional support for your self-hosted deployment? Contact us.

ZITADEL Cloud (SaaS)

Start for free at zitadel.com — no credit card required. Available in US · EU · AU · CH. Pay-as-you-go pricing.


Integrate with the V2 API

ZITADEL exposes every capability over a typed API. Here's how to create a user with the V2 REST API:

curl -X POST https://$ZITADEL_DOMAIN/v2/users/human \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "username": "[email protected]",
    "profile": { "givenName": "Alice", "familyName": "Smith" },
    "email": { "email": "[email protected]", "sendCode": {} }
  }'

Explore the full API reference — including connectRPC and gRPC transports — or jump straight to quickstart examples.


Features

Authentication

Multi-Tenancy

Integration

Self-Service & Admin

Deployment

Track upcoming features on our roadmap and follow our changelog for recent updates.


Showcase

Login V2

Our new, fully customizable login experience — documentation


Adopters & Ecosystem

Used in production by organizations worldwide. See the full Adopters list — and add yours by submitting a pull request.


How To Contribute

ZITADEL is built in the open and welcoming to contributions of all kinds.

Contributors

Made with contrib.rocks.


Security

Security policy: SECURITY.md

Vulnerability Disclosure Policy — how to responsibly report security issues.

Technical Advisories are published for major issues that could impact security or stability in production.

License

AGPL-3.0 — see LICENSING.md for the full licensing policy, including Apache 2.0 and MIT exceptions for specific directories.

S
Description
No description provided
Readme AGPL-3.0
510 MiB
Languages
Go 74.6%
TypeScript 12%
MDX 7.9%
HTML 2.6%
SCSS 1.2%
Other 1.7%