Commit Graph
681 Commits
Author SHA1 Message Date
75bc058bce feat(eventstore): autovacuum tuning for events2 table (#12449)
# Which Problems Are Solved

In Zitadel's append-only event-sourced architecture, the
`eventstore.events2` table grows indefinitely. PostgreSQL's default
autovacuum uses a percentage-based scale factor, so as the table grows,
the number of changed rows required to trigger a `VACUUM` or `ANALYZE`
drifts towards infinity. Without regular vacuums, the table's Visibility
Map becomes stale, disabling fast Index-Only Scans and forcing expensive
heap reads. Without regular analyzes, query planner statistics become
stale, leading to suboptimal execution plans.

This causes eventstore operations to progressively degrade as `events2`
grows, even without CPU, memory, or I/O saturation. A manual `VACUUM
ANALYZE` immediately restores performance, confirming the root cause.

- If `Eventstore.Autovacuum` is left at its default, `events2` keeps
using PostgreSQL's default, percentage-based autovacuum/autoanalyze
scale factors, which become impractically infrequent on large tables.
- There was previously no supported way to apply static, table-level
autovacuum tuning to `events2` through Zitadel's own configuration/setup
process.

# How the Problems Are Solved

- Added an `Eventstore.Autovacuum` runtime configuration block to
`cmd/defaults.yaml` (disabled by default):
  ```yaml
  Eventstore:
    Autovacuum:
      Enabled: false # ZITADEL_EVENTSTORE_AUTOVACUUM_ENABLED
VacuumThreshold: 50000 # ZITADEL_EVENTSTORE_AUTOVACUUM_VACUUMTHRESHOLD
AnalyzeThreshold: 50000 # ZITADEL_EVENTSTORE_AUTOVACUUM_ANALYZETHRESHOLD
  ```
- Added a repeatable `zitadel setup` migration step
(`cmd/setup/eventstore_autovacuum.go`) that:
- When `Enabled: true`, disables the percentage-based
`autovacuum_vacuum_scale_factor`, `autovacuum_analyze_scale_factor`, and
`autovacuum_vacuum_insert_scale_factor` on `eventstore.events2`, and
applies static thresholds (`autovacuum_vacuum_insert_threshold`,
`autovacuum_vacuum_threshold`, `autovacuum_analyze_threshold`) from the
config instead.
- When `Enabled: false`, resets those storage parameters on
`eventstore.events2` back to the cluster defaults.
- Implements `Repeatable.Check()` so the step only re-runs when the
configuration actually changed since the last `zitadel setup` run.
  - Added documentation in the new "Performance tuning" page.
 
# Additional Changes

- Move the projection documentation into performance tuning page
- Expand and update the projection documentation to the latest state in
zitadel. (contained some stale information)

# Additional Context

Several other issues describe read-performance symptoms consistent with
this same root cause (stale `events2` visibility map / planner
statistics at scale, without resource saturation). Since this PR
addresses the shared root cause:

- Closes #12448
- Closes #10754
- Closes #10260
- Closes #8585
- Closes #9239


---
_Generated by [Claude
Code](https://claude.ai/code/session_01HoKvEY7niCVgLCz7CajBwW)_

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Silvan <27845747+adlerhurst@users.noreply.github.com>
2026-07-16 14:42:11 +00:00
9d60e83d6f Merge commit from fork
* Use slices.Contains over custom function

* Correctly remove roles from granted roles

* fix(setup): repair user grants with stale roles (GHSA-v859-c572-qh5p)

Add setup step 73 that reconciles existing user grants whose roles were
left too broad by the buggy cascade removal in removeRoleFromUserGrant.
The corruption lives in the eventstore event payloads, so the step pushes
a corrective user.grant.cascade.changed event per affected grant (roles
intersected with the currently valid set) and re-triggers the user grant
projection. Runs in the second setup slice, after the projection tables
it reads have been created.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(setup): scope GHSA-v859-c572-qh5p repair to grant-based user grants

Direct user grants can never be hit by this bug (only ChangeProjectGrant's
multi-role cascade to grant-based grants can trigger it), so drop the
direct-grant branch from the finder query to avoid stripping unrelated,
legitimate roles that merely mismatch for other reasons (e.g. stale
role_key drift). Also exclude removed instances from the migration scope,
and log the number of grants fixed per instance.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Livio Spring <9405495+livio-a@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-09 13:28:09 +02:00
Livio Spring 1e0b810dca feat: allow managing invite code in secret generators (#12109)
# Which Problems Are Solved

Zitadel exposes the secrets generator configuration through its admin
api. This allows instance admins to manage them on their own and they
can create overwrite the system / runtime defaults (incl. expiration).
This very much needed in multi-instance scenarios such as zitadel.cloud.
Currently the invite code configuration was not manageable through the
API, but only runtime config.

# How the Problems Are Solved

- added the `invite_code` type to the API allowing it to be set and
retrieved.
- added the type to console's management list
- added the type to be stored on instance setup
- change the `GetSecretGenerator` endpoint to fall back to the runtime
config if no config is stored on the instance itself
- ensure the `length` and at least one charset is enabled, return an
error otherwise
- expiry is not enforced, so 0 allows codes with no expiry (current
state)

# Additional Changes

None

# Additional Context

- closes https://github.com/zitadel/zitadel/issues/10474
2026-07-01 04:53:16 +00:00
f9995ee39c fix: increase performance of ListUser by login name ignore case (#12350)
# Which Problems Are Solved

Login v2 uses the `users.v2.ListUsers`-endpoint to get a user by login
name. This query had an inefficient `WHERE`-clause.

# How the Problems Are Solved

Update the view and use a specific clause for this query.

# Additional information

Added in https://github.com/zitadel/zitadel/pull/10475

---------

Co-authored-by: Marco A. <kwbmm1990@gmail.com>
Co-authored-by: Livio Spring <9405495+livio-a@users.noreply.github.com>
2026-06-30 11:20:21 +00:00
SilvanandLivio Spring 10087e7389 fix: connection handling in setup after migration steps 40, 64 and 70 (#12293)
# Which Problems Are Solved

During the setup step we saw rare cases which caused setup to fail after
executing steps 40, 64 and 70.

# How the Problems Are Solved

Close currently open database connections so that they fetch the correct
type mapping for the `eventstore.command2` database type.

# Additional Changes

Ensure correct order of setup steps 64 and 70.

# Additional Context

None

---------

Co-authored-by: Livio Spring <9405495+livio-a@users.noreply.github.com>
2026-06-16 15:09:00 +00:00
Livio Spring dbb0da71af fix: remove unnecessary entry from default denylist (#12294)
# Which Problems Are Solved

The updated default denylist added an entry for IPv4-mapped IPv6
addresses to prevent IPv6 encapsulation bypasses.
This is not necessary since the IP already gets resolved into v4 and now
blocks them all.

# How the Problems Are Solved

Removed the entry.

# Additional Changes

None

# Additional Context

None
2026-06-16 16:52:26 +02:00
Tim Möhlmann e94d4c3986 feat(crypto): FIPS 140-3 compliant build and runtime checks (#12233)
# Which Problems Are Solved

Enable FIPS 140-3 compliant build.

# How the Problems Are Solved

- Add runtime config validation, if the FIPS flag is enabled fail the
application when a non-compliant hasher is used, or throw a warning when
a legacy verifier is used
- Add a build matrix for FIPS certified build:
  - Go binary is built with `GOFIPS140=certified`
- Login container uses a separate base:
[ubi9](https://catalog.redhat.com/en/software/containers/ubi9/ubi/615bcf606feffc5384e8452e)
from redhat which provides a FIPS certified OpenSSL (used by NodeJS TLS
stack)
- Non-FIPS images where already pushed to both Github Container Registry
and Google Artifact Repository (GAR). Fips images are only pushed to the
GAR.
- Tag versions are suffixed `-fips`. So on release the following images
will be additionally available:

```
europe-docker.pkg.dev/zitadel-common/zitadel-repo/zitadel-login:vX.Y.Z-fips
europe-docker.pkg.dev/zitadel-common/zitadel-repo/zitadel:vX.Y.Z-fips-debug
europe-docker.pkg.dev/zitadel-common/zitadel-repo/zitadel:vX.Y.Z-fips
```
  
# Other changes

- Bumb Go toolchain. At least v1.25.10 is required for a
GOFIPS140=certified setting.
 
# Additional Context

- Closes https://github.com/zitadel/zitadel/issues/4335
- Build [test
run](https://github.com/zitadel/zitadel/actions/runs/27253916052)
pushing FIPS and non-FIPS images
2026-06-16 10:16:49 +02:00
Marco A.andLivio Spring 8e82ec1cb9 Merge commit from fork
* Add DenyLists parsing

* Remove unneeded returned error

* Plug global denylist into Command

* app creation: apply denylist to backchannel logout URI

* Inject denylist to backchannel logout worker

* webhook config: validate against blocked URLs

* Add notificationsWebhook denylist target

* command: Add SMTP endpoint validation against blocklist

* command: Add SMS endpoint validation against blocklist

* Validate webhook endpoint against denylist on channel notification

* Remove unused tests

* handle deprecated denylists

* remove unintended denylist entry in deprecated list

* use single http client

* fix tests

* update comments

* fixes

* cleanup

* address comments

* fix merge

---------

Co-authored-by: Livio Spring <9405495+livio-a@users.noreply.github.com>
2026-06-15 15:36:14 +02:00
Livio Spring d184e976fc Merge commit from fork
* feat(jwt idp): manage and validate audience

* translations

* fix tests

* address comments

* update migration version

* fix merge
2026-06-15 15:27:47 +02:00
Silvanandabhishek kumar gupta 6082e59d47 fix(eventstore): allow overwriting resource owner of events (#12261)
# Which Problems Are Solved

- The eventstore did not support intentionally overwriting the resource
owner when creating events for aggregates that may be reused across
owners.
- Resource owner handling was implicit and could not be controlled per
command/event type.
- We needed a safe way to distinguish between:
  - keeping the existing aggregate owner, and
  - explicitly setting a new owner for specific create-like events.

# How the Problems Are Solved

- Introduced a new eventstore command type with an explicit
enforce_owner flag.
- Updated eventstore.commands_to_events and eventstore.push so owner
assignment is now explicit:
  - if enforce_owner is true, the command owner is written
- if enforce_owner is false, the existing aggregate owner is retained
when present
- Added EnforceResourceOwnerCommand and wiring so command types can opt
in to enforced owner behavior.
- Wired the new behavior through the v3 eventstore push path, including
compatibility fallback for older command type mapping.
- Added migration/setup changes to register and use the new command type
and SQL functions.
- Added and updated tests for owner overwrite and aggregate ID reuse
scenarios.

# Additional Changes

- Added small migration/setup robustness improvements related to
eventstore setup ordering and helper reuse.
- Added focused test coverage for enforced owner behavior and
sequencing.
- Events that currently allow owner changes (implement
EnforceResourceOwner) are:
  - AddedEvent (action)
  - GroupAddedEvent
  - StartedEvent (idp intent)
  - ProjectAddedEvent
  - HumanAddedEvent
  - HumanRegisteredEvent
  - MachineAddedEvent
  - CreatedEvent (schema user)

# Additional Context

- Follow-up for eventstore owner-handling correctness in create flows
and aggregate ID reuse cases.
- No additional issue link was attached for this change.

---------

Co-authored-by: abhishek kumar gupta <abhishek818t@gmail.com>
2026-06-15 11:24:37 +02:00
25e263394e chore: update passwap v0.12.1 and align hash validation defaults/errors (#12179)
# Which Problems Are Solved

- Upgrading to `zitadel/passwap` v0.12.1 introduced new encoded-hash
validation paths that still had review feedback open.
- Secret hasher defaults were internally inconsistent (`Hasher.Cost: 4`
vs `Limits.Bcrypt.MinCost: 10`), which could reject hashes created by
the configured hasher.
- New validation error IDs/messages and test coverage needed to be
aligned with project conventions and expected behavior branches.

# How the Problems Are Solved

- Kept the dependency upgrade to `zitadel/passwap` v0.12.1 and completed
the validation integration.
- Updated `ValidateEncodedHash` error handling in
`internal/crypto/passwap.go` to:
  - use unique random-style error IDs,
  - return `Errors.Hash.NotSupported` for no-verifier cases,
  - keep invalid-hash branches mapped to invalid argument errors.
- Expanded `TestHasher_ValidateEncodedHash` in
`internal/crypto/passwap_test.go` to cover and assert:
  - bounds error branch,
  - no-verifier branch,
  - generic invalid-hash branch,
  - expected ZITADEL error IDs/messages.
- Restored lost inline verifier-context comments for argon2 and md5plain
verifier entries.

# Additional Changes

- Added the missing explanatory `Limits` comment for `SecretHasher` in
`cmd/defaults.yaml`.
- Corrected `SecretHasher.Limits.Bcrypt.MinCost` from `10` to `4` to
match the configured default bcrypt cost and avoid configuration
footguns.

# Additional Context

- Follow-up for PR review feedback in
https://github.com/zitadel/zitadel/pull/12179#pullrequestreview-4313121965

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Livio Spring <9405495+livio-a@users.noreply.github.com>
2026-06-05 14:48:35 +02:00
01fe34a526 fix(oidc): use authenticated encryption for opaque tokens (#12017)
# Which Problems Are Solved

Opaque tokens now use authenticated encryption.

# How the Problems Are Solved

- Upgrade zitadel/oidc to v3.47
- Copy crypto implementation for refresh and session tokens (internal to
zitadel)
- Added config that allows validating old tokens for gradual roll-out

# Additional Changes

- Set NX cache for `integration-test-build` to `false`, working on a
seperate fix.

# Additional Context

- closes #11315

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: muhlemmer <5411563+muhlemmer@users.noreply.github.com>
2026-04-13 10:59:50 +00:00
Mridang Agarwalla 330548e13c feat: support standard OTEL env vars via autoexport (#11864) 2026-04-01 06:14:21 +00:00
Gayathri VijayanandMarco A. 7c3d26b23b feat: rt sessions OTP email challenge (#11941)
# Which Problems Are Solved

Add OTP Email challenge needed for Create/Set session in the RT model.

# How the Problems Are Solved

Introduce `OTPEmailChallengeCommand` (validate/execute/events) for
session creation checks
Add unit tests

# Additional Changes
Remove unused fields from `session_challenge_otp_sms.go`

# Additional Context
- Related to https://github.com/zitadel/zitadel/issues/11035

---------

Co-authored-by: Marco A. <kwbmm1990@gmail.com>
2026-03-31 11:50:53 +00:00
21b28b56ac fix: revert feature key for configs back to ConsoleUseV2UserApi (#11928)
# Which Problems Are Solved

https://github.com/zitadel/zitadel/pull/11390 renamed "Console" to
"Management Console". While
https://github.com/zitadel/zitadel/pull/11706 already reverted an
unintended rename of the feature key to enable the management console to
use the V2 API for user creation. It was now also discovered that the
rename of the feature itself also broke existing (default)
configurations.

# How the Problems Are Solved

Added a `mapstructure` tag on the instance feature to handle existing
configs.

# Additional Changes

Removed unused `TokenExchange` from the default configuration.

# Additional Context

- relates to #11390 
- relates to #11706
- requires backport to v4.x

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Marco A. <marco@zitadel.com>
2026-03-30 15:59:57 +00:00
022bf74060 feat(session): Passkey Check API with relation tables (#11858)
# Which Problems Are Solved

As part of #11035 , this PR implements the Passkey check logic for
session validation

# How the Problems Are Solved

  - Refactor webauth FinishLogin to support new domain model
  - Add webauth config to defaults
  - Implement passkey check logic and tests
- Manual transaction management to avoid stalling the DB while
FinishLogin callback is executed
- Update passkey Type condition to allow passing a text operation
(equal, contains, etc..)

# Additional Context

This is a cherry-picked PR + minor changes, coming from
https://github.com/zitadel/zitadel/pull/11164
- Relates to #11035

---------

Co-authored-by: Fabienne Bühler <fabienne@zitadel.com>
Co-authored-by: Gayathri Vijayan <66356931+grvijayan@users.noreply.github.com>
2026-03-27 12:38:40 +01:00
Gayathri Vijayan f99142bc4a feat: implement otp sms session challenge (#11919)
# Which Problems Are Solved

This PR adds the OTP SMS Challenge needed for CreateSession API
https://github.com/zitadel/zitadel/issues/11035

# How the Problems Are Solved

- Introduce `OTPSMSChallengeCommand` (validate/execute/events) for
session creation checks
- Introduce `GetOTPCryptoGeneratorConfigWithDefault` to fetch
per-instance secret generator settings
- Add unit tests

# Additional Changes

Refactor tests in `session_check_recovery_code_test.go` to make the
helper functions reusable in `session_challenge_otp_sms_test.go`

# Additional Context
Related to https://github.com/zitadel/zitadel/issues/11035
2026-03-26 11:33:44 +00:00
Wim Van LaerandLivio Spring 70adaff793 feat: add option to use x.509 certificate system-api-user tokens (#11876)
# Which Problems Are Solved

System API users currently authenticate using raw RSA public keys
configured via Path or KeyData. This approach doesn't integrate well
with Kubernetes tooling.

# How the Problems Are Solved

Allow for the `path`/`keyData` to be an X.509 certificate. 

The `NotBefore` and `NotAfter` fields of the certificate are beeing
respected when validating the JWT.

# Additional Changes

# Additional Context

- Closes #11442

---------

Co-authored-by: Livio Spring <livio@zitadel.com>
2026-03-23 13:32:27 +00:00
Mridang Agarwalla 471ed4a5d7 feat: add DSN/URL connection string support for PostgreSQL and Redis (#11729) 2026-03-20 15:21:15 +00:00
Livio SpringandStefan Benz ca4bba3992 feat: add delete session on relation tables (#11334)
# Which Problems Are Solved

As part of moving the session API to the relational database, this PR
adds the functionality to delete sessions through the session API v2 and
the OIDC end_session endpoint.

# How the Problems Are Solved

- added `SessionDeleteCommand` in domain package
- added possibility to pass the session token verifier to the domain
package
- use feature flag to switch API and OIDC usage from CQRS to relation
table
- added a `permissionCheck` condition to let permissions be checked
directly in the repository / sql
- part of it is still a placeholder until actual permission tables are
done
- added a `session_deleted` table to store (recently) deleted sessions
to handle necessary checks / cases like delete my own session, which
needs to be idempotent

# Additional Changes

Fixed `Matches` function of `existsCondition` (as missing an
implementation)

# Additional Context

- closes #11037

---------

Co-authored-by: Stefan Benz <46600784+stebenz@users.noreply.github.com>
2026-03-19 08:35:53 +01:00
Silvan 6d90a120a6 feat: allow transactional table setup step to recreate the whole schema (#11833)
## Problem description

- Setup currently creates/updates relational tables, but there is no
built-in way to fully reset the relational schema during iterative
development.
- Re-running setup after schema/projection changes can leave stale
relational objects and projection state behind, which makes local/dev
validation harder.
- There is no explicit, configurable switch in setup steps for
destructive schema recreation behavior.

## How the Problems Are Solved

- Adds a new setup step configuration section for relational tables with
a `ShouldRecreateSchema` flag (default `false`).
- Wires the new config flag into the transactional tables setup step.
- Extends the transactional tables execution logic to optionally:
  - Drop the `zitadel` schema with `CASCADE`
- Clean related projection state entries for relational tables in
`projections.current_states`
  - Recreate tables through the existing setup flow afterward
- Wraps the destructive operations in a transaction and keeps
error/rollback handling plus logging for visibility.

## Additional Changes

- Adds an explicit warning in step configuration that schema recreation
is intended for development and not production use.
- Keeps behavior fully backward-compatible by default
(`ShouldRecreateSchema: false`), so existing setups are unchanged unless
the flag is enabled.

## Additional Context

- Follow-up for relational setup/dev workflow improvements.
- PR: #11833

## How to use it

### Env `export ZITADEL_RELATIONALTABLES_SHOULDRECREATESCHEMA=true`

### Config

Add the following to your custom setup steps config:

```yaml
RelationalTables:
  ShouldRecreateSchema: true
```
2026-03-18 03:53:57 +00:00
Marco A. aa4432dc99 feat(session): IDP Intent Check API with relation tables (#11820)
# Which Problems Are Solved

As part of #11035 , this PR implements the IDP intent check logic for
session validation

# How the Problems Are Solved

- Inject encryption algorithm for IDP intent tokens at DB initialization
  - Add the IDP intent repository as an invoke option
  - Rework crypto mocks using gomock
  - Implement IDP intent check logic
- minor - DB: expand IDP Intent deletion testing table with a test case
where IDP intent is not found

# Noteworthy

I did not implement `domain.Transactional` given the doubts that
@adlerhurst expressed in https://github.com/zitadel/zitadel/pull/11804 .
So, there is no transaction AT ALL in here because the `Execute()`
method contains one single DB call (Delete) and `Validate()` just does
Get calls.

# Additional Context

This is a cherry-picked PR + minor changes, coming from
https://github.com/zitadel/zitadel/pull/11164
- Relates to #11035
- Depends on https://github.com/zitadel/zitadel/pull/11804
2026-03-17 10:50:47 +00:00
Tim MöhlmannandLivio Spring 921414fa16 fix(telemetry): count IDP template data (#11720)
# Which Problems Are Solved

The total number of configured IDPs seemed of.
We were counting only the IDP table for non-templated IDPs.

# How the Problems Are Solved

Add a count trigger migration for the IDP template table.
Entries will be counted under the existing
`ResourceCountIdentityProvider`

# Additional Changes

- none

# Additional Context

- Reported internally
- Implemented in #9979
- Related https://github.com/zitadel/zitadel/issues/9957

Co-authored-by: Livio Spring <livio.a@gmail.com>
2026-03-17 05:12:43 +00:00
Marco A. 3c3201e5a0 feat(session): Password Check API with relation tables (#11804)
# Which Problems Are Solved

As part of #11035 , this PR implements the password check logic for
session validation

# How the Problems Are Solved

- Add system config to default configuration of `domain` package for
easy initialization. Intialize the system settings when Zitadel starts
up
- Add password hasher verify logic to the default configuration of
`domain` package. Initialize it when Zitadel starts up.
  - Add settings repositories with their mocks
  - Implement the logic for doing a password check

# Additional Context

This is a cherry-picked PR + minor changes, coming from
https://github.com/zitadel/zitadel/pull/11164
- Relates to #11035
- Depends on https://github.com/zitadel/zitadel/pull/11777
2026-03-13 11:40:48 +01:00
Livio Spring 4a8a5e2fef Merge commit from fork 2026-03-11 08:21:42 +01:00
5934e07960 fix: recover from request panics (#11713)
# Which Problems Are Solved

Panics may cause a service discruption by unexpectedly closing an
request's connection. Or in the case of gRPC completely killing the
service.

Allthough panics are still individual bugs that need to be solved, this
PR makes sure a panic is gracefully handled and an understandable error
is returned to the client.

# How the Problems Are Solved

- Recover in the middleware interceptors for the 3 API protocols (HTTP,
gRPC, connect).
- HTTP middleware uses formatted responses for:
  - OIDC errors (JSON formatted response)
  - UI (error page rendering)
  - SCIM
- Upon recovery an alert level log is printed (ERROR+4 for stdlib log
handlers)

# Additional Context

- internal observation

---------

Co-authored-by: Livio Spring <livio@zitadel.com>
Co-authored-by: Livio Spring <livio.a@gmail.com>
2026-03-03 11:55:17 +00:00
Florian Forster 92a628d892 feat(database): enhance PostgreSQL setup documentation and commands for non-admin access (#11631)
Users deploying ZITADEL against a managed PostgreSQL service (RDS, Cloud
SQL, Azure Database, etc.) often do not have superuser access and cannot
provide `Admin.*` credentials. The documented workaround — provisioning
the user and database manually and then running `start-from-setup` —
silently skips schema bootstrapping, causing `relation
"eventstore.events" does not exist` errors with no clear recovery path.

The root cause is that `zitadel init` conflates two steps that require
different privileges without exposing them separately:

- **Provisioning step** (`CREATE ROLE`, `CREATE DATABASE`, `GRANT`) —
requires superuser.
- **Schema bootstrapping step** (create
`eventstore`/`projections`/`system` schemas and base tables) — requires
only DB owner.

Users who handle the provisioning step externally have no supported way
to run schema bootstrapping alone.

## Changes

- **`cmd/initialise/verify_schema.go`** (renamed from
`verify_zitadel.go`): Rename `newZitadel()` → `newSchema()` (internal);
rename the `init zitadel` sub-command to `zitadel init schema`
(backwards-compatible alias kept) with a clear description that it
bootstraps the ZITADEL database schema without admin/superuser
privileges. Fix stale error log message to reference `init schema`.
- **`cmd/initialise/verify_schema_test.go`** (renamed from
`verify_zitadel_test.go`): Test file renamed to match source file.
- **`cmd/initialise/init.go`**: Update call site to `newSchema()`. Add
guidance in the `init` command's Long description about using `zitadel
init schema` for users without admin credentials.
- **`cmd/initialise/verify_database.go`**: Add a `pg_database` catalog
pre-check before attempting `CREATE DATABASE`, so `zitadel init` with
`ADMIN=service_user` no longer fails with `permission denied to create
database` when the database was already provisioned externally.
- **`cmd/initialise/verify_database_test.go`**: Add test cases covering
the new catalog-check skip path, the existing error-skip path, and the
error-propagation path when the `pg_database` query itself fails.
- **`apps/docs/content/self-hosting/manage/database/index.mdx`**: Inline
the `_postgres.mdx` partial (now only PostgreSQL is supported), add a
top-level callout, and add a **Managed PostgreSQL / No Admin Access**
section with explicit 3-step instructions and security guidance (strong
passwords, SSL, TLS). Additional improvements: add inline `# Use
'require' or 'verify-full' for production` comments on `Mode: disable`
lines in the YAML example; add clarifying comment to the redundant
`GRANT` in the SQL snippet; replace admonition syntax with proper
Fumadocs `<Callout>` components; add full database connection env vars
to the `start-from-setup` example.
- **`apps/docs/content/self-hosting/manage/updating_scaling.mdx`**:
Rewrite the init phase description to clearly distinguish the
provisioning and schema bootstrapping steps, and document `zitadel init
schema` as the path for manual provisioning. Replace admonition syntax
with proper Fumadocs `<Callout>` components.
- **`apps/docs/content/self-hosting/manage/database/_postgres.mdx`**:
Deleted (content merged into `index.mdx`).

## Problem

This relates to https://github.com/zitadel/zitadel/discussions/9363

I think we can improve our UX in cases where a user wants to use an
external DB and/or does not want to share too broad permissions with
zitadel

## Related problems

* https://github.com/zitadel/zitadel/issues/10432
* https://github.com/zitadel/zitadel/discussions/8583
* https://github.com/zitadel/zitadel/issues/7903
* https://github.com/zitadel/zitadel/issues/9718
* https://github.com/zitadel/zitadel/issues/8012
* https://github.com/zitadel/zitadel/issues/8558

## Related PRs

https://github.com/zitadel/zitadel/pull/11021
2026-03-03 07:05:32 +01:00
Marco A. b2532e9666 Merge commit from fork
* Inject DenyList from config to `StartCommands()`

* Implementation draft

* Move address checker to separate package

* Migrate usages of actions.AddressChecker to denylist.AddressChecker

* Rename denylist package files

* Pass []denylist.AddressChecker to StartCommand

* Add DenyList to defaults.yaml and add custom config parser

* net: add HostnameToIPList function

* denylist: Add IsHostBlocked()

* actions: use denylist.IsHostBlocked()

* command: Inject ip lookup function + extend add target validationt test

* command: Unexport ChangeTarget.IsValid()

* command: Add denyList check on ChangeTarget validation

* command: Export ActionsV2DenyList and IPLookupFunction params

* Lint fix

* Check denylist during action execution

* Fix integration tests

* Apply suggestions

* Add `mapstructure.StringToSliceHookFunc()` to decode `HTTPConfigDecodeHook`
2026-02-25 06:33:28 +01:00
Silvan 839a2d3b5b Prevent failure in setup step 69 if cache.objects does not exist (#11673)
# Which Problems Are Solved

If the `cache.objects`-table does not exist setup failes with the
following error:

`ERROR:  relation "cache.objects" does not exist at character 44`

# How the Problems Are Solved

Changed statement to prevent immediately throw an error if the table is
missing.

# Additional Context

- reported in
https://discord.com/channels/927474939156643850/1473769282880934089
- backport to v4
2026-02-24 09:51:22 +00:00
Livio Spring 71fab2e574 feat: allow adding trusted domains in instance setup (#11169)
# Which Problems Are Solved

When running Zitadel behind a reverse proxy and especially when the API
and the login UI don't run on the same domain, Zitadel needs to be
configured to trust the corresponding domains and use them in public
responses, like email links and more.
This can be done by adding a trusted domain. However it's currently only
possible through the API and not in the instance setup process.

# How the Problems Are Solved

Added a possibility to configure multiple trusted domains in the first
instance setup process.

# Additional Changes

None

# Additional Context

- closes #11153
2026-02-23 06:06:33 +00:00
RamonandCopilot b23346f6ca chore: consistent naming for organization domain (#11356)
# Which Problems Are Solved

As part of the consistent naming effort, this PR focuses on
"Organization domain".

# How the Problems Are Solved

- All terms referring referring to Organization Domains where changed to
be Organization Domain

# Additional Changes

None

# Additional Context

- closes [#11283](https://github.com/zitadel/zitadel/issues/11283)

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-18 14:46:21 +00:00
Wim Van Laer 699ed17918 chore: rename configuration to settings (#11568)
# Which Problems Are Solved

naming inconsistencies: Configuration <-> Settings

# How the Problems Are Solved


# Additional Changes


# Additional Context

- Closes #11282
2026-02-18 14:25:42 +01:00
Tim Möhlmann f3b578f19f fix(log): correct log stream in setup (#11623) 2026-02-18 06:32:03 +00:00
c02d812b6c fix(slog): masking of grouped attributes (#11606)
# Which Problems Are Solved

Group attributes weren't masked if their corresponding key was
configured. For example setting `data` as a masked key entry in the
runtime config would still print unmasked event data. This was because
ReplaceAttr does not receive group attributes, only the flattened
attributes with group information.

# How the Problems Are Solved

Check the current groups stack in the replacer. When a configured key is
found in the group, mask the current attribute. This means that the
attribute structure is preserved and all sub-keys of a masked group are
still printed.

# Additional Changes

- Use a binary search for key matching. As we are now comparing slice
against slice, a binary search should make the process a little faster
if many keys are configured.

# Additional Context

- Follow-up for PR #11435

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Co-authored-by: muhlemmer <5411563+muhlemmer@users.noreply.github.com>
2026-02-18 06:00:13 +00:00
2a2d5392a3 fix: correctly send links to login v2 in email notifications (#10711)
# Which Problems Are Solved

There were still some emails (passkey registration and domain claimed)
sent with links pointing to login v1 even when the login v2 was enabled
for the instance.
Also while looking into the issue, it was discovered that some links
pointing to login V2 were not correctly generated.


# How the Problems Are Solved

- Added default paths for passkey registration and domain claimed
notifications
- Fixed the existing paths to properly handle concatenation (resp. use
`url.ResolveReference`)
  - Change their go types (from string) to `*url.URL` 
  - Added a mapstructure hook for string to url
- Removed unnecessary `InstanceSetupFeatures` and corresponding
conversions
- Refactored the methods on the `login.DefaultPaths` struct and added an
interface to the `Commands` to only need to pass a single config (and
not every method)
- Added an `OriginURL` method to the `DomainCtx` to prevent going from
url to string and back
- Added the use of the templates in case of enabled login v2 for passkey
registration and domain claimed)

# Additional Changes

None

# Additional Context

closes #10643

---------

Co-authored-by: Max Peintner <max@caos.ch>
Co-authored-by: Livio Spring <livio.a@gmail.com>
Co-authored-by: Livio Spring <livio@zitadel.com>
Co-authored-by: Max Peintner <peintnerm@gmail.com>
Co-authored-by: Gayathri Vijayan <66356931+grvijayan@users.noreply.github.com>
2026-02-13 13:06:56 +00:00
Marco A. 382aec2d61 chore: Service Account Naming Consistency (#11557)
# Which Problems Are Solved

Inconsistent naming of service account, found in the following
variations:

  - Machine User
  - machine user
  - Service User
  - Machine Account
  - Technical Account
  - User: Type Machine

# How the Problems Are Solved

Attentive search and replace.

Localizations have been translated using Copilot

# Additional Changes

Some unused methods have been removed from the Go code.

# Additional Context

- Closes #11285
2026-02-13 12:31:43 +01:00
Marco A.andWim Van Laer 1f0d54a978 chore(docs): User (Human) naming consistency (#11512)
# Which Problems Are Solved

Naming inconsistencies - User (Human)

# How the Problems Are Solved

Most of the occurrences have not been changed. `User (Human)` was mostly
changed when talking about code objects and where I felt it was
necessary to distinguish them from machine users.

When both human and machine user occurrences were found, the machine
user has been changed to service account (see
https://github.com/zitadel/zitadel/issues/11285)

# Additional Context

- Closes #11284

---------

Co-authored-by: Wim Van Laer <wim+github@zitadel.com>
2026-02-06 09:46:24 +00:00
Livio Spring ce30a5a98e feat(oidc): move back-channel logout from beta to GA (#11493)
# Which Problems Are Solved

This PR marks the OIDC Back-Channel Logout as general available (GA).

# How the Problems Are Solved

- API:
- deprecated the feature toggle (to prevent breaking changes) in v2beta
and v2 API
  - removed all related event logic and updated the projection iteration
  - removed the toggle usage for back-channel logout
  - removed related translations
- Management Console:
  - removed the feature toggle and its translations
- Docs:
  - removed all beta labels and feature toggle notes

# Additional Changes

none

# Additional Context

- closes #11277 
- requires backport to v4.x
2026-02-05 10:51:09 +00:00
Silvan be6590c33c fix(setup): ensure step 69 runs without issues (#11503)
## Which problems are solved

https://github.com/zitadel/zitadel/pull/11484 introduced a regression
that causes the setup process to fail on existing Zitadel deployments.
This prevents users from upgrading to recent versions without
encountering setup failures.

## How the problems are solved

Setup step 69 has been corrected to properly handle existing deployment
configurations and prevent setup failures during initialization and
upgrades.

## Additional Context

introduced by
[7a41fe968b](https://github.com/zitadel/zitadel/commit/7a41fe968b9fcb69b378336740b74e5448c1ff81)

### Testing

- [x] Verified setup succeeds on fresh deployments
- [x] Verified setup succeeds on existing deployments
- [x] Verified migration from PostgreSQL 17 to 18 after running setup of
this version
2026-02-04 10:55:46 +00:00
Tim MöhlmannandSilvan 11dbb1b277 feat(logging): add streams (#11435)
# Which Problems Are Solved

Streams allow differentiating logs produced by different components of
Zitadel.

# How the Problems Are Solved

The `backend/v3/instrumentation/logging` package now exposes convenience
function for setting and getting a logger from the context. As well as
high-level functions to emit log records at various levels. When
constructing a new logger a "stream" needs to be specified:

- **runtime**: General runtime logs, such as startup and shutdown
messages. Default for logs that do not belong to the other categories.
- **request**: Logs for incoming API and HTTP requests.
- **event_handler**: Logs for event handling in projections.
- **queue**: Logs for the job queue processing.
- **event_pusher**: Logs for event pushing to the database. Disabled by
default, contains sensitive information.

Each line from the returned logger contains a `stream` field as well as
a `version` field with the current Zitadel version.

## Runtime config

Streams can be enabled by passing an array of stream names in the
runtime config. Because some log streams may contain sensitive data
(especially events), it is now also possible to mask values by their
key.

# Additional Changes

- Wrap `slogctx` in the `logging` package. (Except API error converter
packages, because of import cycle)
- Add some docs to `logging` package so other devs understand how to add
logging to Zitadel
- Add `logging.OnError` and `logging.WithError` helper functions with
`Panic()` and `Fatal()` methods, to preserve current calls in the `cmd`
packages.
- Add instance context extractor.
- Only output request details in the request info log. Request ID
remains propagated through context.
- Moved middleware functionality into protocol specific packages. 
- Removed setting of URI to context in metric middleware. There were ony
setters and no getters. (Unused value)
- Reuse a single statusWriter in the middleware package for middlewares
that need to know the response status.

# Additional Context

- Closes #11333
- Closes #11331 
- Partly #11330

---------

Co-authored-by: Silvan <27845747+adlerhurst@users.noreply.github.com>
2026-02-04 11:51:43 +01:00
SilvanandCopilot 7a41fe968b fix(setup): ensure PostgreSQL 18 compatibility (#11484)
# Which Problems Are Solved

When starting Zitadel with Postgres version 18, setup fails with the
following error:

`level=error msg="migration failed" caller=".../cmd/setup/setup.go:373"
code=0A000 detail= error="ERROR: partitioned tables cannot be unlogged
(SQLSTATE 0A000)" hint= message="partitioned tables cannot be unlogged"
name=34_add_cache_schema severity=ERROR`

# How the Problems Are Solved

- Modify setup step 34 to ensure compatibility with PostgreSQL 18 by
changing the creation of the partitioned tables to`LOGGED` tables but
keep the partitions `UNLOGGED`.
- Added an additional setup step which alters the table persistence of
the partitioned tables to `LOGGED`.

# Additional Changes

- Bumped Postgres compatibility to version 18 in docs.
- Ensure default partitions for cache tables

## Additional Context

- closes https://github.com/zitadel/zitadel/issues/10712
- backport to v4
- migration from PostgreSQL version 17 to 18 was verified using
`pg_dumpall` and restoring the created backup file
- and new setups using PostgreSQL version 18 directly

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-03 16:23:24 +00:00
Mridang Agarwalla 6363ee0d0a feat: enable cross-app distributed tracing for v2 APIs (#11453) 2026-02-03 13:36:29 +01:00
Livio Spring d51d615e97 feat(oidc): use worker queue for OIDC Back-Channel Logout notifications (#11441)
# Which Problems Are Solved

The current back-channel logout notification still used a single
projection handler for handling terminating sessions and notifying the
necessary clients. This can lead to back pressure and delayed
notifications.

# How the Problems Are Solved

The handler now only creates a job in the (river) worker queue to handle
the notification.
The worker will then search for all necessary clients to be informed and
create a job for each.
These jobs will then be picked up by the (same) worker(s) again, which
will create, sign and send the logout token to the client. If one
notification fails, this one job will be retired, but others can still
be processed in parallel and ore not affected.
Each successful job will still create an event on the session stating
the successful notification of the client.
The existing `OIDC.DefaultBackChannelLogoutLifetime` configuration has
been deprecated in favor of making it part of the new
`OIDC.BackChannelLogout` config.
 
# Additional Changes

None

# Additional Context

- feature check is still executed and will be removed seperately for
easier review: https://github.com/zitadel/zitadel/issues/11277
- closes https://github.com/zitadel/zitadel/issues/9279
- requires backport to v4.x
2026-02-03 10:47:48 +00:00
Marco A. 96bbd6a65c chore: management console naming inconsistency (#11390)
# Which Problems Are Solved

The following terms have all been renamed to management console:

- Customer Portal (when used to mean the console)
- Console
- Admin Console

# How the Problems Are Solved

- Search & Replace smartly
- Use Copilot for translation files

Changes done to: backend + frontend codebase, docs, translations and
protobufs (descriptions only)

# Additional Context

- Partially Closes #11279
2026-01-26 14:52:15 +00:00
Tim MöhlmannandCopilot 34799389b7 feat(logging): gcp error reporting (#11355)
# Which Problems Are Solved

Zitadel Errors (`zerrors` package) are logged by default. This change
add more verbose error reporting through log output, including
compatibility with GCP error reporting if enabled.

# How the Problems Are Solved

Errors can now carry their "report location" and a stack trace. "Report
location" is enabled by default, while stack-traces are disabled. Our
new [`zitadel/sloggcp`](https://github.com/zitadel/sloggcp) package
provides the GCP error reporting handler for `slog`.

The following section displays the different formats for the same error
(User not found). Newlines and formatting of output is applied for
readability and is not part of the actual log output.

<details>

<summary>Default text output</summary>

```
time=2026-01-15T14:27:20.625+01:00
level=WARN
source=/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/gerrors/zitadel_errors.go:57
msg="User could not be found (COMMAND-bd4ir1mblj)"
domain.instance_host=localhost:8080
domain.protocol=http
protocol=connect
service=zitadel.user.v2.UserService
http_method=POST
path=/zitadel.user.v2.UserService/DeleteUser
request_id=d5keme0ednc50mm74bfg
duration=66.761569ms
TraceID=319374baa56acc2dbd1d0179f1bbec9f
SpanID=541621b0abea7ff1
err.kind=NotFound
err.message="User could not be found"
err.id=COMMAND-bd4ir1mblj
err.reportLocation.filePath=/home/tim/Repositories/zitadel/zitadel/internal/command/user_v2.go
err.reportLocation.lineNumber=140
err.reportLocation.functionName=github.com/zitadel/zitadel/internal/command.(*Commands).RemoveUserV2
```

</details>

<details>

<summary>Default JSON output</summary>

```json
{
  "time": "2026-01-15T14:30:59.280497754+01:00",
  "level": "WARN",
  "source": {
    "function": "github.com/zitadel/zitadel/internal/api/grpc/gerrors.ZITADELToConnectError",
    "file": "/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/gerrors/zitadel_errors.go",
    "line": 57
  },
  "msg": "User could not be found (COMMAND-bd4ir1mblj)",
  "domain": { "instance_host": "localhost:8080", "protocol": "http" },
  "protocol": "connect",
  "service": "zitadel.user.v2.UserService",
  "http_method": "POST",
  "path": "/zitadel.user.v2.UserService/DeleteUser",
  "request_id": "d5keo4oednc6klmo3vbg",
  "duration": 54229055,
  "TraceID": "daa21183bd8da5da56969b0ea45f4388",
  "SpanID": "70da728e3086c104",
  "err": {
    "kind": "NotFound",
    "message": "User could not be found",
    "id": "COMMAND-bd4ir1mblj",
    "reportLocation": {
      "filePath": "/home/tim/Repositories/zitadel/zitadel/internal/command/user_v2.go",
      "lineNumber": 140,
      "functionName": "github.com/zitadel/zitadel/internal/command.(*Commands).RemoveUserV2"
    }
  }
}
```

</details>

When stack trace is enabled, using a standard logger,
it is logged under the `stackTrace` key.

<details>

<summary>Standard JSON format with stack trace</summary>

```json
{
  "time": "2026-01-15T14:36:56.522692368+01:00",
  "level": "WARN",
  "source": {
    "function": "github.com/zitadel/zitadel/internal/api/grpc/gerrors.ZITADELToConnectError",
    "file": "/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/gerrors/zitadel_errors.go",
    "line": 57
  },
  "msg": "User could not be found (COMMAND-bd4ir1mblj)",
  "domain": { "instance_host": "localhost:8080", "protocol": "http" },
  "protocol": "connect",
  "service": "zitadel.user.v2.UserService",
  "http_method": "POST",
  "path": "/zitadel.user.v2.UserService/DeleteUser",
  "request_id": "d5kequ0ednc7lphvgfn0",
  "duration": 57820793,
  "TraceID": "1b05d2b01306f2a64feb1a24b65a89c5",
  "SpanID": "c5ff35a27a917764",
  "err": {
    "kind": "NotFound",
    "message": "User could not be found",
    "id": "COMMAND-bd4ir1mblj",
    "reportLocation": {
      "filePath": "/home/tim/Repositories/zitadel/zitadel/internal/command/user_v2.go",
      "lineNumber": 140,
      "functionName": "github.com/zitadel/zitadel/internal/command.(*Commands).RemoveUserV2"
    },
    "stackTrace": "goroutine 673 [running]:\nruntime/debug.Stack()\n\t/usr/lib/go/src/runtime/debug/stack.go:26 +0x8e\ngithub.com/zitadel/zitadel/internal/zerrors.newZitadelError(0x5, {0x0, 0x0}, {0x6af7719, 0x12}, {0x6aff994, 0x14})\n\t/home/tim/Repositories/zitadel/zitadel/internal/zerrors/zerror.go:139 +0x1df\ngithub.com/zitadel/zitadel/internal/zerrors.ThrowNotFound({0x0, 0x0}, {0x6af7719, 0x12}, {0x6aff994, 0x14})\n\t/home/tim/Repositories/zitadel/zitadel/internal/zerrors/not_found.go:6 +0x70\ngithub.com/zitadel/zitadel/internal/command.(*Commands).RemoveUserV2(0xc0035a4008, {0xa3c1410, 0xc003ded5c0}, {0xc0029693a0, 0x3}, {0x0, 0x0}, {0xd550a60, 0x0, 0x0}, ...)\n\t/home/tim/Repositories/zitadel/zitadel/internal/command/user_v2.go:140 +0x711\ngithub.com/zitadel/zitadel/internal/api/grpc/user/v2.(*Server).DeleteUser(0xc00272d080, {0xa3c1410, 0xc003ded5c0}, 0xc002ebf180)\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/user/v2/user.go:191 +0x3f1\nconnectrpc.com/connect.NewUnaryHandler[...].func1({0xa405e70, 0xc002ebf180})\n\t/home/tim/go/pkg/mod/connectrpc.com/connect@v1.19.1/handler.go:51 +0x17e\nconnectrpc.com/connect.unaryThunk.func1({0xa3c1410, 0xc003ded5c0}, {0xa405e70, 0xc002ebf180})\n\t/home/tim/go/pkg/mod/connectrpc.com/connect@v1.19.1/interceptor.go:120 +0xd9\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.ActivityInterceptor.func1.1({0xa3c1410, 0xc003ded5c0}, {0xa405e70, 0xc002ebf180})\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/activity_interceptor.go:20 +0x1b1\nconnectrpc.com/connect.unaryThunk.func1({0xa3c1410, 0xc003ded4d0}, {0xa405e70, 0xc002ebf180})\n\t/home/tim/go/pkg/mod/connectrpc.com/connect@v1.19.1/interceptor.go:120 +0xd9\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.ServiceHandler.func1.1({0xa3c1410, 0xc003ded4d0}, {0xa405e70, 0xc002ebf180})\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/service_interceptor.go:22 +0x283\nconnectrpc.com/connect.unaryThunk.func1({0xa3c1410, 0xc003ded4d0}, {0xa405e70, 0xc002ebf180})\n\t/home/tim/go/pkg/mod/connectrpc.com/connect@v1.19.1/interceptor.go:120 +0xd9\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.validate({0xa3c1410, 0xc003ded4d0}, {0xa405e70, 0xc002ebf180}, 0xc0031bff60)\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/validation_interceptor.go:35 +0x1bd\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.ValidationHandler.func1.1({0xa3c1410, 0xc003ded4d0}, {0xa405e70, 0xc002ebf180})\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/validation_interceptor.go:15 +0x85\nconnectrpc.com/connect.unaryThunk.func1({0xa3c1410, 0xc003ded4d0}, {0xa405e70, 0xc002ebf180})\n\t/home/tim/go/pkg/mod/connectrpc.com/connect@v1.19.1/interceptor.go:120 +0xd9\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.ExecutionHandler.func1.1({0xa3c1410, 0xc003ded4d0}, {0xa405e70, 0xc002ebf180})\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/execution_interceptor.go:39 +0x376\nconnectrpc.com/connect.unaryThunk.func1({0xa3c1410, 0xc003ded4d0}, {0xa405e70, 0xc002ebf180})\n\t/home/tim/go/pkg/mod/connectrpc.com/connect@v1.19.1/interceptor.go:120 +0xd9\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.QuotaExhaustedInterceptor.func1.1({0xa3c1410, 0xc003ded4d0}, {0xa405e70, 0xc002ebf180})\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/quota_interceptor.go:25 +0x42d\nconnectrpc.com/connect.unaryThunk.func1({0xa3c1410, 0xc003ded4d0}, {0xa405e70, 0xc002ebf180})\n\t/home/tim/go/pkg/mod/connectrpc.com/connect@v1.19.1/interceptor.go:120 +0xd9\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.TranslationHandler.func1.1({0xa3c1410, 0xc003ded4d0}, {0xa405e70, 0xc002ebf180})\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/translation_interceptor.go:18 +0x94\nconnectrpc.com/connect.unaryThunk.func1({0xa3c1410, 0xc003ded4d0}, {0xa405e70, 0xc002ebf180})\n\t/home/tim/go/pkg/mod/connectrpc.com/connect@v1.19.1/interceptor.go:120 +0xd9\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.authorize({0xa3c1410, 0xc003dec000}, {0xa405e70, 0xc002ebf180}, 0xc00321c040, {0xa3f0a70, 0xc0004ebc70}, {{0xc000d2fc80, 0x9, 0x9}}, ...)\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/auth_interceptor.go:42 +0x894\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.AuthorizationInterceptor.func1.1({0xa3c1410, 0xc003dec000}, {0xa405e70, 0xc002ebf180})\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/auth_interceptor.go:17 +0x151\nconnectrpc.com/connect.unaryThunk.func1({0xa3c1410, 0xc003dec000}, {0xa405e70, 0xc002ebf180})\n\t/home/tim/go/pkg/mod/connectrpc.com/connect@v1.19.1/interceptor.go:120 +0xd9\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.LimitsInterceptor.func1.1({0xa3c1410, 0xc003dec000}, {0xa405e70, 0xc002ebf180})\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/limits_interceptor.go:31 +0x3b7\nconnectrpc.com/connect.unaryThunk.func1({0xa3c1410, 0xc003dec000}, {0xa405e70, 0xc002ebf180})\n\t/home/tim/go/pkg/mod/connectrpc.com/connect@v1.19.1/interceptor.go:120 +0xd9\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.toConnectError({0xa3c1410, 0xc003dec000}, {0xa405e70, 0xc002ebf180}, 0xc00321c060)\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/error_interceptor.go:21 +0x8a\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.ErrorHandler.func1.1({0xa3c1410, 0xc003dec000}, {0xa405e70, 0xc002ebf180})\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/error_interceptor.go:15 +0x85\nconnectrpc.com/connect.unaryThunk.func1({0xa3c1410, 0xc003dec000}, {0xa405e70, 0xc002ebf180})\n\t/home/tim/go/pkg/mod/connectrpc.com/connect@v1.19.1/interceptor.go:120 +0xd9\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.AccessStorageInterceptor.func1.1({0xa3c1410, 0xc003dec000}, {0xa405e70, 0xc002ebf180})\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/access_interceptor.go:21 +0x814\nconnectrpc.com/connect.unaryThunk.func1({0xa3c1410, 0xc003dec000}, {0xa405e70, 0xc002ebf180})\n\t/home/tim/go/pkg/mod/connectrpc.com/connect@v1.19.1/interceptor.go:120 +0xd9\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.addInstanceByRequestedHost({0xa3c1410, 0xc003db1110}, {0xa405e70, 0xc002ebf180}, 0xc00321c090, {0xa3a63e8, 0xc0002f4a00}, 0xc002dee9b0, {0xc0012106a0, 0x9})\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/instance_interceptor.go:107 +0xbc3\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.setInstance({0xa3c1410, 0xc003db10e0}, {0xa405e70, 0xc002ebf180}, 0xc00321c090, {0xa3a63e8, 0xc0002f4a00}, {0xc0012106a0, 0x9}, 0xc002dee9b0, ...)\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/instance_interceptor.go:61 +0xb05\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.InstanceInterceptor.func1.1({0xa3c1410, 0xc003db10e0}, {0xa405e70, 0xc002ebf180})\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/instance_interceptor.go:23 +0x147\nconnectrpc.com/connect.unaryThunk.func1({0xa3c1410, 0xc003db10e0}, {0xa405e70, 0xc002ebf180})\n\t/home/tim/go/pkg/mod/connectrpc.com/connect@v1.19.1/interceptor.go:120 +0xd9\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.NoCacheInterceptor.func1.1({0xa3c1410, 0xc003db10e0}, {0xa405e70, 0xc002ebf180})\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/cache_interceptor.go:21 +0x351\nconnectrpc.com/connect.unaryThunk.func1({0xa3c1410, 0xc003db10e0}, {0xa405e70, 0xc002ebf180})\n\t/home/tim/go/pkg/mod/connectrpc.com/connect@v1.19.1/interceptor.go:120 +0xd9\ngithub.com/zitadel/zitadel/backend/v3/instrumentation/logging.NewConnectInterceptor.func1({0xa3c1410, 0xc003db10e0}, {0xa405e70, 0xc002ebf180})\n\t/home/tim/Repositories/zitadel/zitadel/backend/v3/instrumentation/logging/connect_handler.go:26 +0x27b\nconnectrpc.com/connect.unaryThunk.func1({0xa3c1410, 0xc003db1050}, {0xa405e70, 0xc002ebf180})\n\t/home/tim/go/pkg/mod/connectrpc.com/connect@v1.19.1/interceptor.go:120 +0xd9\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.RegisterMetrics({0xa3c1410, 0xc003db1050}, {0xa405e70, 0xc002ebf180}, 0xc00321c0d0, {0xcf754f8, 0x3, 0x3}, {0xc0008b8840, 0x4, ...})\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/metrics_interceptor.go:47 +0x3f3\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.MetricsHandler.func1.1({0xa3c1410, 0xc003db1050}, {0xa405e70, 0xc002ebf180})\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/metrics_interceptor.go:31 +0x111\nconnectrpc.com/connect.unaryThunk.func1({0xa3c1410, 0xc003db1050}, {0xa405e70, 0xc002ebf180})\n\t/home/tim/go/pkg/mod/connectrpc.com/connect@v1.19.1/interceptor.go:120 +0xd9\nconnectrpc.com/otelconnect.(*Interceptor).WrapUnary.func1({0xa3c1410, 0xc003db1050}, {0xa405e70, 0xc002ebf180})\n\t/home/tim/go/pkg/mod/connectrpc.com/otelconnect@v0.8.0/interceptor.go:145 +0x16d7\nconnectrpc.com/connect.unaryThunk.func1({0xa3c1410, 0xc003db0ff0}, {0xa405e70, 0xc002ebf180})\n\t/home/tim/go/pkg/mod/connectrpc.com/connect@v1.19.1/interceptor.go:120 +0xd9\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.CallDurationHandler.func1.1({0xa3c1410, 0xc003db0ff0}, {0xa405e70, 0xc002ebf180})\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/call_interceptor.go:15 +0xab\nconnectrpc.com/connect.NewUnaryHandler[...].func2({0x7f11083fe700, 0xc000503a40})\n\t/home/tim/go/pkg/mod/connectrpc.com/connect@v1.19.1/handler.go:78 +0x4e2\nconnectrpc.com/connect.(*Handler).ServeHTTP(0xc0004dd420, {0xa3bb420, 0xc0006b22a0}, 0xc002e3b2c0)\n\t/home/tim/go/pkg/mod/connectrpc.com/connect@v1.19.1/handler.go:333 +0x870\ngithub.com/zitadel/zitadel/pkg/grpc/user/v2/userconnect.NewUserServiceHandler.func1({0xa3bb420, 0xc0006b22a0}, 0xc002e3b2c0)\n\t/home/tim/Repositories/zitadel/zitadel/pkg/grpc/user/v2/userconnect/user_service.connect.go:1951 +0x8e5\nnet/http.HandlerFunc.ServeHTTP(0xc002d7a000, {0xa3bb420, 0xc0006b22a0}, 0xc002e3b2c0)\n\t/usr/lib/go/src/net/http/server.go:2322 +0x33\ngithub.com/rs/cors.(*Cors).Handler.func1({0xa3bb420, 0xc0006b22a0}, 0xc002e3b2c0)\n\t/home/tim/go/pkg/mod/github.com/rs/cors@v1.11.1/cors.go:289 +0x2fd\nnet/http.HandlerFunc.ServeHTTP(0xc000191aa0, {0xa3bb420, 0xc0006b22a0}, 0xc002e3b2c0)\n\t/usr/lib/go/src/net/http/server.go:2322 +0x33\ngithub.com/zitadel/zitadel/internal/api/http/middleware.RobotsTagHandler.func1({0xa3bb420, 0xc0006b22a0}, 0xc002e3b2c0)\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/http/middleware/robots_tag_interceptor.go:12 +0x88\nnet/http.HandlerFunc.ServeHTTP(0xc002e14798, {0xa3bb420, 0xc0006b22a0}, 0xc002e3b2c0)\n\t/usr/lib/go/src/net/http/server.go:2322 +0x33\ngithub.com/zitadel/zitadel/internal/api/http/middleware.WithOrigin.func1.1({0xa3bb420, 0xc0006b22a0}, 0xc002e3b180)\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/http/middleware/origin_interceptor.go:27 +0x42a\nnet/http.HandlerFunc.ServeHTTP(0xc003d89a40, {0xa3bb420, 0xc0006b22a0}, 0xc002e3b180)\n\t/usr/lib/go/src/net/http/server.go:2322 +0x33\ngithub.com/gorilla/mux.(*Router).ServeHTTP(0xc002dfa840, {0xa3bb420, 0xc0006b22a0}, 0xc002e3b180)\n\t/home/tim/go/pkg/mod/github.com/gorilla/mux@v1.8.1/mux.go:212 +0x2bb\ngolang.org/x/net/http2.(*serverConn).runHandler(0xc003e143c0, 0xc0006b22a0, 0xc0002f4000, 0xc002d9ae88)\n\t/home/tim/go/pkg/mod/golang.org/x/net@v0.47.0/http2/server.go:2424 +0x1b9\ncreated by golang.org/x/net/http2.(*serverConn).scheduleHandler in goroutine 607\n\t/home/tim/go/pkg/mod/golang.org/x/net@v0.47.0/http2/server.go:2359 +0x1df\n"
  }
}
```

</details>

When the GCP error reporting handler is enabled with stack trace, as per
[formatting
requirements](https://docs.cloud.google.com/error-reporting/docs/formatting-error-messages):
- A `@type` field is provided
- Some fields are renamed according to Google Cloud Logging specs.
(severity, message etc)
- The stack strace is appended to the `message` field.
- The report location is moved into the top-level `reportLocation`
field.

The original error is still logged in the `error` field.
This is not part of the error reporting API but may still be useful for
logging.

<details>

<summary>GCP error report with stack trace</summary>

```json
{
  "@type": "type.googleapis.com/google.devtools.clouderrorreporting.v1beta1.ReportedErrorEvent",
  "SpanID": "4fdeae9817ade4f5",
  "TraceID": "78e31366d4c36e0718a2dcb6ca73d2f3",
  "domain": { "instance_host": "localhost:8080", "protocol": "http" },
  "duration": "82.464896ms",
  "error": {
    "id": "COMMAND-bd4ir1mblj",
    "kind": "NotFound",
    "message": "User could not be found"
  },
  "http_method": "POST",
  "logging.googleapis.com/sourceLocation": {
    "function": "github.com/zitadel/zitadel/internal/api/grpc/gerrors.ZITADELToConnectError",
    "file": "/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/gerrors/zitadel_errors.go",
    "line": 57
  },
  "message": "ID=COMMAND-bd4ir1mblj Message=User could not be found\ngoroutine 1165 [running]:\nruntime/debug.Stack()\n\t/usr/lib/go/src/runtime/debug/stack.go:26 +0x8e\ngithub.com/zitadel/zitadel/internal/zerrors.newZitadelError(0x5, {0x0, 0x0}, {0x6af7719, 0x12}, {0x6aff994, 0x14})\n\t/home/tim/Repositories/zitadel/zitadel/internal/zerrors/zerror.go:139 +0x1df\ngithub.com/zitadel/zitadel/internal/zerrors.ThrowNotFound({0x0, 0x0}, {0x6af7719, 0x12}, {0x6aff994, 0x14})\n\t/home/tim/Repositories/zitadel/zitadel/internal/zerrors/not_found.go:6 +0x70\ngithub.com/zitadel/zitadel/internal/command.(*Commands).RemoveUserV2(0xc000498588, {0xa3c1450, 0xc002cba540}, {0xc003030910, 0xb}, {0x0, 0x0}, {0xd550a80, 0x0, 0x0}, ...)\n\t/home/tim/Repositories/zitadel/zitadel/internal/command/user_v2.go:140 +0x711\ngithub.com/zitadel/zitadel/internal/api/grpc/user/v2.(*Server).DeleteUser(0xc003101500, {0xa3c1450, 0xc002cba540}, 0xc00216ec80)\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/user/v2/user.go:191 +0x3f1\nconnectrpc.com/connect.NewUnaryHandler[...].func1({0xa405eb0, 0xc00216ec80})\n\t/home/tim/go/pkg/mod/connectrpc.com/connect@v1.19.1/handler.go:51 +0x17e\nconnectrpc.com/connect.unaryThunk.func1({0xa3c1450, 0xc002cba540}, {0xa405eb0, 0xc00216ec80})\n\t/home/tim/go/pkg/mod/connectrpc.com/connect@v1.19.1/interceptor.go:120 +0xd9\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.ActivityInterceptor.func1.1({0xa3c1450, 0xc002cba540}, {0xa405eb0, 0xc00216ec80})\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/activity_interceptor.go:20 +0x1b1\nconnectrpc.com/connect.unaryThunk.func1({0xa3c1450, 0xc002cba330}, {0xa405eb0, 0xc00216ec80})\n\t/home/tim/go/pkg/mod/connectrpc.com/connect@v1.19.1/interceptor.go:120 +0xd9\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.ServiceHandler.func1.1({0xa3c1450, 0xc002cba330}, {0xa405eb0, 0xc00216ec80})\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/service_interceptor.go:22 +0x283\nconnectrpc.com/connect.unaryThunk.func1({0xa3c1450, 0xc002cba330}, {0xa405eb0, 0xc00216ec80})\n\t/home/tim/go/pkg/mod/connectrpc.com/connect@v1.19.1/interceptor.go:120 +0xd9\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.validate({0xa3c1450, 0xc002cba330}, {0xa405eb0, 0xc00216ec80}, 0xc003033180)\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/validation_interceptor.go:35 +0x1bd\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.ValidationHandler.func1.1({0xa3c1450, 0xc002cba330}, {0xa405eb0, 0xc00216ec80})\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/validation_interceptor.go:15 +0x85\nconnectrpc.com/connect.unaryThunk.func1({0xa3c1450, 0xc002cba330}, {0xa405eb0, 0xc00216ec80})\n\t/home/tim/go/pkg/mod/connectrpc.com/connect@v1.19.1/interceptor.go:120 +0xd9\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.ExecutionHandler.func1.1({0xa3c1450, 0xc002cba330}, {0xa405eb0, 0xc00216ec80})\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/execution_interceptor.go:39 +0x376\nconnectrpc.com/connect.unaryThunk.func1({0xa3c1450, 0xc002cba330}, {0xa405eb0, 0xc00216ec80})\n\t/home/tim/go/pkg/mod/connectrpc.com/connect@v1.19.1/interceptor.go:120 +0xd9\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.QuotaExhaustedInterceptor.func1.1({0xa3c1450, 0xc002cba330}, {0xa405eb0, 0xc00216ec80})\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/quota_interceptor.go:25 +0x42d\nconnectrpc.com/connect.unaryThunk.func1({0xa3c1450, 0xc002cba330}, {0xa405eb0, 0xc00216ec80})\n\t/home/tim/go/pkg/mod/connectrpc.com/connect@v1.19.1/interceptor.go:120 +0xd9\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.TranslationHandler.func1.1({0xa3c1450, 0xc002cba330}, {0xa405eb0, 0xc00216ec80})\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/translation_interceptor.go:18 +0x94\nconnectrpc.com/connect.unaryThunk.func1({0xa3c1450, 0xc002cba330}, {0xa405eb0, 0xc00216ec80})\n\t/home/tim/go/pkg/mod/connectrpc.com/connect@v1.19.1/interceptor.go:120 +0xd9\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.authorize({0xa3c1450, 0xc0015827b0}, {0xa405eb0, 0xc00216ec80}, 0xc0030331e0, {0xa3f0ab0, 0xc000203f10}, {{0xc000865980, 0x9, 0x9}}, ...)\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/auth_interceptor.go:42 +0x894\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.AuthorizationInterceptor.func1.1({0xa3c1450, 0xc0015827b0}, {0xa405eb0, 0xc00216ec80})\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/auth_interceptor.go:17 +0x151\nconnectrpc.com/connect.unaryThunk.func1({0xa3c1450, 0xc0015827b0}, {0xa405eb0, 0xc00216ec80})\n\t/home/tim/go/pkg/mod/connectrpc.com/connect@v1.19.1/interceptor.go:120 +0xd9\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.LimitsInterceptor.func1.1({0xa3c1450, 0xc0015827b0}, {0xa405eb0, 0xc00216ec80})\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/limits_interceptor.go:31 +0x3b7\nconnectrpc.com/connect.unaryThunk.func1({0xa3c1450, 0xc0015827b0}, {0xa405eb0, 0xc00216ec80})\n\t/home/tim/go/pkg/mod/connectrpc.com/connect@v1.19.1/interceptor.go:120 +0xd9\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.toConnectError({0xa3c1450, 0xc0015827b0}, {0xa405eb0, 0xc00216ec80}, 0xc003033200)\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/error_interceptor.go:21 +0x8a\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.ErrorHandler.func1.1({0xa3c1450, 0xc0015827b0}, {0xa405eb0, 0xc00216ec80})\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/error_interceptor.go:15 +0x85\nconnectrpc.com/connect.unaryThunk.func1({0xa3c1450, 0xc0015827b0}, {0xa405eb0, 0xc00216ec80})\n\t/home/tim/go/pkg/mod/connectrpc.com/connect@v1.19.1/interceptor.go:120 +0xd9\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.AccessStorageInterceptor.func1.1({0xa3c1450, 0xc0015827b0}, {0xa405eb0, 0xc00216ec80})\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/access_interceptor.go:21 +0x814\nconnectrpc.com/connect.unaryThunk.func1({0xa3c1450, 0xc0015827b0}, {0xa405eb0, 0xc00216ec80})\n\t/home/tim/go/pkg/mod/connectrpc.com/connect@v1.19.1/interceptor.go:120 +0xd9\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.addInstanceByRequestedHost({0xa3c1450, 0xc001636630}, {0xa405eb0, 0xc00216ec80}, 0xc003033230, {0xa3a6428, 0xc00069f040}, 0xc002076910, {0xc002cef510, 0x9})\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/instance_interceptor.go:107 +0xbc3\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.setInstance({0xa3c1450, 0xc0016365a0}, {0xa405eb0, 0xc00216ec80}, 0xc003033230, {0xa3a6428, 0xc00069f040}, {0xc002cef510, 0x9}, 0xc002076910, ...)\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/instance_interceptor.go:61 +0xb05\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.InstanceInterceptor.func1.1({0xa3c1450, 0xc0016365a0}, {0xa405eb0, 0xc00216ec80})\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/instance_interceptor.go:23 +0x147\nconnectrpc.com/connect.unaryThunk.func1({0xa3c1450, 0xc0016365a0}, {0xa405eb0, 0xc00216ec80})\n\t/home/tim/go/pkg/mod/connectrpc.com/connect@v1.19.1/interceptor.go:120 +0xd9\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.NoCacheInterceptor.func1.1({0xa3c1450, 0xc0016365a0}, {0xa405eb0, 0xc00216ec80})\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/cache_interceptor.go:21 +0x351\nconnectrpc.com/connect.unaryThunk.func1({0xa3c1450, 0xc0016365a0}, {0xa405eb0, 0xc00216ec80})\n\t/home/tim/go/pkg/mod/connectrpc.com/connect@v1.19.1/interceptor.go:120 +0xd9\ngithub.com/zitadel/zitadel/backend/v3/instrumentation/logging.NewConnectInterceptor.func1({0xa3c1450, 0xc0016365a0}, {0xa405eb0, 0xc00216ec80})\n\t/home/tim/Repositories/zitadel/zitadel/backend/v3/instrumentation/logging/connect_handler.go:26 +0x27b\nconnectrpc.com/connect.unaryThunk.func1({0xa3c1450, 0xc0016364e0}, {0xa405eb0, 0xc00216ec80})\n\t/home/tim/go/pkg/mod/connectrpc.com/connect@v1.19.1/interceptor.go:120 +0xd9\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.RegisterMetrics({0xa3c1450, 0xc0016364e0}, {0xa405eb0, 0xc00216ec80}, 0xc003033270, {0xcf754f8, 0x3, 0x3}, {0xc000530f60, 0x4, ...})\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/metrics_interceptor.go:47 +0x3f3\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.MetricsHandler.func1.1({0xa3c1450, 0xc0016364e0}, {0xa405eb0, 0xc00216ec80})\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/metrics_interceptor.go:31 +0x111\nconnectrpc.com/connect.unaryThunk.func1({0xa3c1450, 0xc0016364e0}, {0xa405eb0, 0xc00216ec80})\n\t/home/tim/go/pkg/mod/connectrpc.com/connect@v1.19.1/interceptor.go:120 +0xd9\nconnectrpc.com/otelconnect.(*Interceptor).WrapUnary.func1({0xa3c1450, 0xc0016364e0}, {0xa405eb0, 0xc00216ec80})\n\t/home/tim/go/pkg/mod/connectrpc.com/otelconnect@v0.8.0/interceptor.go:145 +0x16d7\nconnectrpc.com/connect.unaryThunk.func1({0xa3c1450, 0xc001636450}, {0xa405eb0, 0xc00216ec80})\n\t/home/tim/go/pkg/mod/connectrpc.com/connect@v1.19.1/interceptor.go:120 +0xd9\ngithub.com/zitadel/zitadel/internal/api/grpc/server/connect_middleware.CallDurationHandler.func1.1({0xa3c1450, 0xc001636450}, {0xa405eb0, 0xc00216ec80})\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/server/connect_middleware/call_interceptor.go:15 +0xab\nconnectrpc.com/connect.NewUnaryHandler[...].func2({0x7fb6100a8e60, 0xc002fa5680})\n\t/home/tim/go/pkg/mod/connectrpc.com/connect@v1.19.1/handler.go:78 +0x4e2\nconnectrpc.com/connect.(*Handler).ServeHTTP(0xc0040768c0, {0xa3bb460, 0xc0025bf360}, 0xc0024c23c0)\n\t/home/tim/go/pkg/mod/connectrpc.com/connect@v1.19.1/handler.go:333 +0x870\ngithub.com/zitadel/zitadel/pkg/grpc/user/v2/userconnect.NewUserServiceHandler.func1({0xa3bb460, 0xc0025bf360}, 0xc0024c23c0)\n\t/home/tim/Repositories/zitadel/zitadel/pkg/grpc/user/v2/userconnect/user_service.connect.go:1951 +0x8e5\nnet/http.HandlerFunc.ServeHTTP(0xc00314a200, {0xa3bb460, 0xc0025bf360}, 0xc0024c23c0)\n\t/usr/lib/go/src/net/http/server.go:2322 +0x33\ngithub.com/rs/cors.(*Cors).Handler.func1({0xa3bb460, 0xc0025bf360}, 0xc0024c23c0)\n\t/home/tim/go/pkg/mod/github.com/rs/cors@v1.11.1/cors.go:289 +0x2fd\nnet/http.HandlerFunc.ServeHTTP(0xc0022cbdc0, {0xa3bb460, 0xc0025bf360}, 0xc0024c23c0)\n\t/usr/lib/go/src/net/http/server.go:2322 +0x33\ngithub.com/zitadel/zitadel/internal/api/http/middleware.RobotsTagHandler.func1({0xa3bb460, 0xc0025bf360}, 0xc0024c23c0)\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/http/middleware/robots_tag_interceptor.go:12 +0x88\nnet/http.HandlerFunc.ServeHTTP(0xc001c8fae8, {0xa3bb460, 0xc0025bf360}, 0xc0024c23c0)\n\t/usr/lib/go/src/net/http/server.go:2322 +0x33\ngithub.com/zitadel/zitadel/internal/api/http/middleware.WithOrigin.func1.1({0xa3bb460, 0xc0025bf360}, 0xc0024c2140)\n\t/home/tim/Repositories/zitadel/zitadel/internal/api/http/middleware/origin_interceptor.go:27 +0x42a\nnet/http.HandlerFunc.ServeHTTP(0xc00048e540, {0xa3bb460, 0xc0025bf360}, 0xc0024c2140)\n\t/usr/lib/go/src/net/http/server.go:2322 +0x33\ngithub.com/gorilla/mux.(*Router).ServeHTTP(0xc00207c540, {0xa3bb460, 0xc0025bf360}, 0xc0024c2140)\n\t/home/tim/go/pkg/mod/github.com/gorilla/mux@v1.8.1/mux.go:212 +0x2bb\ngolang.org/x/net/http2.(*serverConn).runHandler(0xc0025545a0, 0xc0025bf360, 0xc0023fd180, 0xc001703488)\n\t/home/tim/go/pkg/mod/golang.org/x/net@v0.47.0/http2/server.go:2424 +0x1b9\ncreated by golang.org/x/net/http2.(*serverConn).scheduleHandler in goroutine 620\n\t/home/tim/go/pkg/mod/golang.org/x/net@v0.47.0/http2/server.go:2359 +0x1df\n",
  "path": "/zitadel.user.v2.UserService/DeleteUser",
  "protocol": "connect",
  "reportLocation": {
    "filePath": "/home/tim/Repositories/zitadel/zitadel/internal/command/user_v2.go",
    "lineNumber": 140,
    "functionName": "github.com/zitadel/zitadel/internal/command.(*Commands).RemoveUserV2"
  },
  "request_id": "d5kgsu8edncaon6puvag",
  "service": "zitadel.user.v2.UserService",
  "severity": "WARNING",
  "time": "2026-01-15T16:57:45.341000592+01:00"
}
```

</details>

When the GCP error reporting handler is enabled without stack trace,
the `message` field contains the error string, also as per formatting
requirements.

<details>

<summary>GCP error report without stack trace</summary>

```json
{
  "@type": "type.googleapis.com/google.devtools.clouderrorreporting.v1beta1.ReportedErrorEvent",
  "SpanID": "2b4157f874eddbf4",
  "TraceID": "c34f7404fc0d021e1868ccc687bf2995",
  "domain": { "instance_host": "localhost:8080", "protocol": "http" },
  "duration": "52.061597ms",
  "error": {
    "id": "COMMAND-bd4ir1mblj",
    "kind": "NotFound",
    "message": "User could not be found"
  },
  "http_method": "POST",
  "logging.googleapis.com/sourceLocation": {
    "function": "github.com/zitadel/zitadel/internal/api/grpc/gerrors.ZITADELToConnectError",
    "file": "/home/tim/Repositories/zitadel/zitadel/internal/api/grpc/gerrors/zitadel_errors.go",
    "line": 57
  },
  "message": "ID=COMMAND-bd4ir1mblj Message=User could not be found",
  "path": "/zitadel.user.v2.UserService/DeleteUser",
  "protocol": "connect",
  "reportLocation": {
    "filePath": "/home/tim/Repositories/zitadel/zitadel/internal/command/user_v2.go",
    "lineNumber": 140,
    "functionName": "github.com/zitadel/zitadel/internal/command.(*Commands).RemoveUserV2"
  },
  "request_id": "d5kf5tgedncaiajvls9g",
  "service": "zitadel.user.v2.UserService",
  "severity": "WARNING",
  "time": "2026-01-15T15:00:22.910899273+01:00"
}
```

</details>

# Additional Changes

- Private constructor for zitadel errors so that the stack-depth is
consistent for different ways of error creation. (Throw and Create
variants)
- Request logs are now always at info level.
- Bump Go version 1.25, required for `sloggcp`

# Additional Context

- closes #11329

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-01-16 14:24:06 +00:00
Tim Möhlmann eb22b58756 feat(telemetry): improved instrumentation for observability (#11159)
# Which Problems Are Solved

Zitadel did not provide easy correlation between errors, logs, traces
and metrics. The configuration for those instrumentations was also not
consistent, with some supporting different exporters then others.
Implementation and parsing of config was also spaghettified over
multiple packages, with awkward parsing and inconsistent naming of
options.

# How the Problems Are Solved

All telemetry is now merged under the name "instrumentation". Why?
1. We thought it was a good idea in the past to call the milestone
exporter `Telemtry` in the runtime config. Calling this `TelemetryV2`
looks weird.
2. Not everything is a meter and not everything is sent (tele...). 
3. It's also
[defined](https://opentelemetry.io/docs/concepts/instrumentation/) as
such by the OTEL documentation.

## New features

- Adds structured, context based logging with trace-ID awareness
- Static log fields are added to the context, such as service and
request path
- Static log fields are injected in each logline emitted by the
application
- Structured logs can also be send to an otel exporter
- Structured logs can be printed to StdErr in text and JSON format
- Error sinks make sure every error is logged at the correct level:
- Warnings for client side errors (HTTP 400 range, Invalid request etc)
  - Error for server side errors (Internal server errors)
- Metrics can now also be send to a OTEL collector. (previously they
could only be scraped from `/debug/metrics` with prometheus)

## Exporters

This change adds all the exporters supported by OTEL upstream and some
google specific exporters for our cloud deployment.

- StdOut / StdErr: all instrumentations
- OTEL gRPC / HTTP: all instrumentations
- Google: all instrumentations except logging
- Prometheus (pull-based): only metrics

The exception is profiling, which only supports the google exporting due
to lack of support by OTEL upstream.

## Configuration and structure 

- All instrumentation is moved into the new `backend/v3/instrumentation`
package. It reuses configuration types, so both code and runtime
configuration are easier to understand.
- The `internal/telemetry` packages are removed.
- Instrumentation is started with a single function and a proper
shutdown function is now provided.
- Legacy configuration is still parsed from the runtime config, as long
as the new configuration is disabled. This allows backporting this
feature to v4 without breaking existing configurations.

# Additional Changes

- Devcontainer: set `$PATH` variable so installed go binaries can be run
individually, without NX.
- NX: install GCI tool to fix imports

# Additional Context

- Closes https://github.com/zitadel/zitadel/issues/8408
- Closes https://github.com/zitadel/zitadel/issues/6664
- Backport to v4
2026-01-12 05:51:39 +00:00
Tim Möhlmann 686de99967 fix(cmd/build): populate date variable when not set through ldflags (#11316)
# Which Problems Are Solved

When building zitadel through `go build` or using an IDE debugging tool,
ldflags are not passed by dedault. In the past this was taken care of by
the `make compile` command, which got replaced by NX. The `nx
@zitadel/api:build` command does not set the ldflags either. Only the
`pack-platform` command does.

Because the ldflags aren't passed, the `date` and other variables remain
empty in the `cmd/build` package. During init of the package, if the
`version` is empty it defaults to the `date` variable. When the `date`
field was empty, the parsed `dateTime` would default to `time.Now()`,
but the `date` variable remained empty, resulting in a empty version.

A consistently empty version string prevents upgrades of projections on
dev systems, as the version from the last run is used.

# How the Problems Are Solved

If date failed to parse and `dateTime` is set to Now, use the formated
string of `dateTime` as `date`.

# Additional Context

- Blocks https://github.com/zitadel/zitadel/pull/11239
2026-01-08 07:22:36 +01:00
Livio SpringandGayathri Vijayan 5f34d1af8f chore: forward port organization v2 API changes (#11207)
# Which Problems Are Solved

Recent changes to the v2beta and v2 API of the organization service were
directly merged to the `next` branch and release on v4.x, since the
ongoing move to the relation table, blocked it from being merged into
main.

# How the Problems Are Solved

This PR ports the following changes into main:
- fix(api): correct permission check in organization v2beta service:
https://github.com/zitadel/zitadel/commit/8dcfff97ed52a8b9fc77ecb1f972744f42cff3ed
- fix(actions v1): return org metadata again
(https://github.com/zitadel/zitadel/pull/11040)
- feat(api): move organization api
(https://github.com/zitadel/zitadel/pull/11045)

# Additional Changes

None

# Additional Context

relates to #10772

---------

Co-authored-by: Gayathri Vijayan <66356931+grvijayan@users.noreply.github.com>
2026-01-05 14:14:01 +00:00
791d0587aa feat(action v2): add JWT and JWE payload type options (#11196)
# Which Problems Are Solved

The payload in actions V2 is currently sent as JSON to the target
endpoint. It might get exposed to intermediary infrastructure or logging
systems.
For these scenarios there needs to be an application-layer encryption,
where the provider of the endpoint can define an key to be used for the
encryption.

# How the Problems Are Solved

- Added an additional option to the target to specify the payload type:
`JSON` (current and default), `JWT`, `JWE` (api and console)
- added endpoints to upload and manage public keys (to be used for
encryption) for a target
- updated all action v2 executions (interceptors, oidc, saml, ...) to
provide the `GetActiveSigningWebKey` from queries
- implemented jwt and jwe in the exections incl. refactoring of code and
tests
- changes to the authn_keys table:
  - added a `fingerprint` column
  - dropped not null constraint on expiration
- moved the `GetSignerOnce` into its own package to prevent circular
dependencies

# Additional Changes

None

# Additional Context

closes #11061

---------

Co-authored-by: Marco A. <marco@zitadel.com>
Co-authored-by: conblem <mail@conblem.me>
Co-authored-by: Silvan <27845747+adlerhurst@users.noreply.github.com>
2025-12-29 13:35:53 +00:00
Rajat SinghandRajat Singh 0bbc95dd1d docs: update PublicKeyCacheMaxAge and DefaultBackChannelLogoutLifetime description (#11229)
<!--
Please inform yourself about the contribution guidelines on submitting a
PR here:
https://github.com/zitadel/zitadel/blob/main/CONTRIBUTING.md#submit-a-pull-request-pr.
Take note of how PR/commit titles should be written and replace the
template texts in the sections below. Don't remove any of the sections.
It is important that the commit history clearly shows what is changed
and why.
Important: By submitting a contribution you agree to the terms from our
Licensing Policy as described here:
https://github.com/zitadel/zitadel/blob/main/LICENSING.md#community-contributions.
-->

# Which Problems Are Solved

Update the description for `PublicKeyCacheMaxAge` and
`DefaultBackChannelLogoutLifetime`

Fixes https://github.com/zitadel/zitadel/issues/10739

Co-authored-by: Rajat Singh <rajat@zitadel.com>
2025-12-24 16:04:26 -03:00