Commit Graph
50 Commits
Author SHA1 Message Date
8ccd5b206f docs: migrate to native Fumadocs search & UI fixes (#12732)
Changes:

- **Search**: Replaced Inkeep with Fumadocs' native built-in search
(createFromSource). Removed @inkeep/cxkit-react and custom search
dialogs.

- **Hydration Fix**: Removed markdown links from ### headings in
egress.mdx (across all versions) to fix the <a inside a> DOM mismatch
error.

- **Console Fix**: Added the missing key prop to <VersionSelector/> in
the layout to resolve the React list warning.

---------

Co-authored-by: Florian Forster <[email protected]>
Co-authored-by: Claude Fable 5.1 <[email protected]>
2026-09-11 10:47:53 -07:00
7e34f04361 docs: fix broken links to renamed repository paths (#12618)
# Which Problems Are Solved

Several links in the docs point at repository paths that no longer
exist, so they 404 on GitHub:

- The `load-test` directory was renamed to `benchmark`. The benchmarks
page still links to `load-test`, `load-test/Makefile` and
`load-test/README.md` (5 occurrences).
- `apps/docs/content/index.mdx` links to `CONTRIBUTING.mdx`, but the
file is `CONTRIBUTING.md`.
- The login-app guide links to `apps/login/src/middleware.ts`, which was
renamed to `apps/login/src/proxy.ts` in the Next.js 16 upgrade (#11629).

# How the Problems Are Solved

- Point the benchmarks page at `benchmark/`, `benchmark/Makefile` and
`benchmark/README.md`. The `#test` anchor still resolves —
`benchmark/README.md` has a `### Test` heading.
- Fix the `CONTRIBUTING.md` extension.
- Point the middleware example at `apps/login/src/proxy.ts`.

Verified by extracting every
`github.com/zitadel/zitadel/{blob,tree}/main/...` link from
`apps/docs/content` and resolving each one against the working tree —
all 14 distinct paths now exist.

# Additional Changes

- Prose in the benchmarks page that names the directory ("the load-test
Makefile", "the load-test readme") now says "benchmark". Generic uses of
"load-test"/"load-testing" as an activity are left as they are.

# Additional Context

- The versioned docs under `apps/docs/content/v4.13`–`v4.16` contain the
same stale links, but those directories are generated at build time from
release tags by `apps/docs/scripts/fetch-remote-content.mjs` and are
gitignored, so they cannot be fixed from `main`. The published pages for
those versions will keep the old links.
- `apps/docs/scripts/check-links.ts` skips anything starting with
`http`, which is why these went unnoticed. Teaching it to resolve
`github.com/zitadel/zitadel/blob/main/<path>` links against the working
tree would catch this class of breakage in CI — not done here to keep
the PR focused.

Co-authored-by: Claude Opus 5 <[email protected]>
2026-09-09 16:06:20 +00:00
8dac4e2b54 docs: add instance administrator hardening guide (#12678)
# Which Problems Are Solved

- Operators lack a single guide for separating instance-level principals
(break-glass, login client) from tenant organizations.
- Login policy and IdP behavior for platform accounts is easy to
conflate with customer org settings when admins share one organization.
- There is no documented procedure to inventory instance administrators
or detect unexpected grants over time.

# How the Problems Are Solved

- Add Hardening instance administrators covering:
- Dedicated platform organization layout and why login, password, and
lockout policies matter for operator accounts.
- Login client (`IAM_LOGIN_CLIENT`) and break-glass (`IAM_OWNER`)
hygiene.
- Periodic review with Internal Permission Service v2
`ListAdministrators` (instance and platform org filters).
- Actions v2 webhooks for `instance.member.*` and `org.member.*` events
(with payload interpretation and alert hints).
- Event API polling alternative for SIEM/batch pipelines
(`instance.member.*` and platform-org `org.member.*`).
- Register the page in the docs sidebar under Roles & Permissions.
- Cross-link from administrators, production checklists, login-client
setup, and service account authentication guides.

# Additional Changes

- Clarify instance vs org IdP availability depends on effective login
policy and linked providers (not automatic for all orgs).
- Note deprecated Admin API `POST /admin/v1/members/_search` vs v2
`ListAdministrators`.

# Additional Context

- Operational hardening guide only (no product authz changes).

---------

Co-authored-by: Cursor <[email protected]>
2026-09-04 15:26:32 +02:00
8de625dc48 docs(oidc): remove stale back-channel logout unimplemented notice (#12660)
<!-- CURSOR_AGENT_PR_BODY_BEGIN -->
# Which Problems Are Solved

- The OIDC logout guide still claimed Back-Channel Logout was not
implemented in ZITADEL, even though the feature is supported and
documented elsewhere.

# How the Problems Are Solved

- Removed the outdated "This is not yet implemented in ZITADEL" callout
from the Back-Channel Logout section of the logout guide.
- Added a short note linking to the dedicated [OIDC Back-Channel
Logout](/guides/integrate/back-channel-logout) guide.
- Left the Front-Channel Logout "not yet implemented" callout unchanged,
since that mechanism is still unsupported.

# Additional Changes

- None.

# Additional Context

- Highlighted in
https://github.com/zitadel/zitadel/issues/8287#issuecomment-5425724614
- Related epic: #8287
<!-- CURSOR_AGENT_PR_BODY_END -->

<div><a
href="https://cursor.com/agents/bc-3be6fda6-ce62-409c-96f0-f7ae9c58914d?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-3be6fda6-ce62-409c-96f0-f7ae9c58914d&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-08-28 15:20:28 -03:00
8e442ed4bc fix(oidc): allow OrgRoleIDScope downscoping in token exchange (#12563)
# Which Problems Are Solved

- Token exchange rejects `urn:zitadel:iam:org:roles🆔{orgID}` when
that scope is not already present on the subject or actor token
(`invalid_scope`).
- A documented least-privilege pattern — exchanging a broad access token
for one whose roles claims are limited to a single organization — has
been broken since the v4.15.3 token-exchange hardening and was not
restored by #12312.
- A naive allow-any-`roles:id` exception would let a previously narrowed
token widen again (switch org filter, or drop the filter and get all
role orgs), including via an actor token's filter on actor-path
exchanges.
- A client-requested bare `urn:zitadel:iam:org:roles🆔` (empty org id)
is not a meaningful filter and should be rejected on exchange.

# How the Problems Are Solved

- Treats `OrgRoleIDScope` as a restriction filter with downscope-only
semantics via `applyOrgRoleIDScopeDownscoping(requested,
ceilingScopes)`:
- union / standard exchange passes the **subject** token scopes as the
ceiling (actor filters ignored)
- impersonation (scopeless `user_id` / `id_token` subject) passes the
**actor** token scopes as the ceiling
- ceiling unfiltered → any requested `roles:id` is allowed (broad →
narrow)
  - ceiling filtered → requested filters must be ⊆ that set
- ceiling filtered but request omits them → inherit the ceiling (omit
must not re-widen)
- Rejects a **client-requested** bare `urn:zitadel:iam:org:roles🆔` in
`validateTokenExchangeScopes` after normalize (before empty-request
fallback). Input tokens may still carry the bare prefix as a degenerate
ceiling; empty-request / omit inherit does not re-trigger the rejection.
- Keeps non-restriction scopes on the existing subject ∪ actor presence
checks.
- Adds unit coverage for downscope, further downscope, widen rejection,
omit-inherit, mixed subject/actor filters, empty-scope access/JWT
subjects, and bare-prefix request/ceiling cases.
- Adds an integration test for broad → `roles:id` filtered roles, plus a
second exchange that rejects widening to another org.
- Documents downscope-only and omit-inherit behavior in the reserved
scopes table and the token exchange guide.

# Additional Changes

- Updates the token-exchange guide Scope section to reflect that
authorization-related reserved scopes must already be present on input
tokens (aligned with the v4.15.3 hardening), rather than suggesting
scope can be freely extended.

# Additional Context

- Closes #12413
- Follow-up for PR #12312
- Related discussion in #11869 (recommended `roles:id` workaround; this
restores that path for token exchange without allowing filter widening)
- Behavior note for release notes: during token exchange, an existing
`urn:zitadel:iam:org:roles🆔*` filter on the subject is inherited when
omitted from the request, so role claims cannot re-widen.

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Livio Spring <[email protected]>
2026-08-28 16:27:56 +02:00
zitadel-knowledge-bot[bot]GitHubzitadel-knowledge-bot[bot] <296006658+zitadel-knowledge-bot[bot]@users.noreply.github.com>Matías RacedoCopilot Autofix powered by AI
27ce10276b docs: create knowledge gap ID 65 (#12646)
Automatically generated PR resolving Knowledge Gap ID 65.

**Thread ID:** manual-1787771291264
**Action:** CREATE
**New File:**
`apps/docs/content/guides/integrate/login-ui/fork-and-deploy-login-app.mdx`

### AI Summary
> My recommendation is to publish a a new Deploy & Operate guide for the
Login App, then link to it from the existing Login App page. The
existing pages address architecture/development, login-client
authorization, and Login V2 adoption separately, but none currently
explains long-term fork ownership and upgrades. (Contribution guide,
login-client setup, adoption guide)

The article should cover:

Full-repository fork as the simplest current recommendation.
The customer’s history-preserving slim-fork approach as an advanced
alternative.
Why copying only apps/login is unsafe while the current npm packages
remain unavailable, linked to #12250.
Release-tag pinning and versioning such as v4.17.0-custom.1.
Development, generation, linting, tests, and Docker builds.
An EKS/Kubernetes Deployment and Service example.
PAT handling through ZITADEL_SERVICE_USER_TOKEN_FILE.
Same-origin versus separate-domain proxy requirements.
Gradual rollout, break-glass access, rollback, and troubleshooting.
A repeatable upstream synchronization and release process.

### Human Reviewer Instructions
> Sample content:
> Maintain and deploy a customized ZITADEL Login UI
> 
> The ZITADEL Login UI is an MIT-licensed Next.js application maintained
in the ZITADEL monorepo. You can customize and deploy it independently,
but your fork becomes an application that your team must build, test,
secure, monitor, and update.
> 
> This guide describes the current workflow for maintaining a customized
Login UI. It is intended for teams that need source-code changes beyond
the branding and text customization available through ZITADEL settings.
> 
> If you only need to change colors, logos, fonts, or translated login
text, first use ZITADEL's supported branding and customization settings.
A source fork creates an ongoing maintenance responsibility.
> 
> Understand the source dependencies
> 
> The Login UI is not currently an isolated project in the repository.
Its relevant dependency chain is:
> 
> apps/login
> └── packages/zitadel-client
>     └── packages/zitadel-proto
>         └── proto
> 
> The root pnpm workspace and Nx configuration also participate in
installing, generating, testing, and building these projects. Copying
only apps/login can therefore fail or silently separate the UI from the
API definitions against which it was developed.
> 
> For the current monorepo workflow, keep the Login UI and these
workspace dependencies on the same upstream ZITADEL revision.
> 
> Choose a fork strategy
> 
> Full repository fork — recommended
> 
> Fork the complete zitadel/zitadel repository and limit your own
changes to apps/login wherever possible. This is the simplest model to
understand and the least likely to miss a new build-time dependency when
upstream changes.
> 
> Use two remotes:
> 
> git clone [email protected]:YOUR_ORGANIZATION/zitadel.git
> cd zitadel
> git remote add upstream https://github.com/zitadel/zitadel.git
> git fetch upstream --tags
> 
> Start customization from a released tag rather than an arbitrary
commit on main:
> 
> git checkout -b custom-login/vX.Y.Z vX.Y.Z
> 
> Replace vX.Y.Z with the ZITADEL release you have selected. A release
tag gives you a reproducible compatibility baseline and a corresponding
changelog.
> 
> History-preserving slim fork — advanced
> 
> If repository size or internal policy makes a full fork impractical,
you can maintain a filtered fork containing only:
> 
> apps/login
> 
> packages/zitadel-client
> 
> packages/zitadel-proto
> 
> proto
> 
> the root pnpm, lockfile, Node, and Nx configuration required by those
projects
> 
> Use git filter-repo identically both when bootstrapping the fork and
during every later synchronization. The list of retained paths and all
filter arguments must remain stable so that freshly filtered upstream
history shares commit ancestry with your fork.
> 
> Important constraints for this model:
> 
> Bootstrap and update with one shared filtering implementation; do not
duplicate the path list in separate scripts.
> 
> Merge the initial import and subsequent update branches with a merge
commit or fast-forward. Do not squash them, because squashing removes
the filtered upstream ancestry needed for future three-way merges.
> 
> Treat any change to the retained path list as a new bootstrap. Adding
or removing paths rewrites the filtered commit IDs.
> 
> Re-run pnpm install after filtering or merging so the lockfile is
reconciled with the retained workspace.
> 
> Review the retained path list whenever upstream changes the Login
build graph.
> 
> This is a valid Git maintenance technique, but it adds machinery that
ZITADEL's normal build does not require. Own and test the filtering
automation as part of your fork.
> 
> Why a standalone apps/login copy is not recommended today
> 
> At the time of writing, current @zitadel/client and @zitadel/proto
releases are not available as version-matched npm dependencies for the
Login UI. The application consumes their workspace sources from the
monorepo. Until version-matched packages are published, retain those
sources rather than replacing the workspace dependencies with old npm
versions. Progress is tracked in zitadel/zitadel#12250.
> 
> Configure a development environment
> 
> The current contribution guide requires Node.js 22.x, Corepack/pnpm,
and Nx. From the repository root:
> 
> corepack enable
> pnpm install
> pnpm nx run-many --target generate
> 
> To develop the Login UI against an existing ZITADEL instance, create
apps/login/.env.dev.local:
> 
> ZITADEL_API_URL=https://your-zitadel-instance.example.com
> ZITADEL_SERVICE_USER_TOKEN=your-login-client-pat
> 
> Do not commit this file or its token. ZITADEL_API_URL must not have a
trailing slash.
> 
> Start the development server:
> 
> pnpm nx run @zitadel/login:dev
> 
> The token must belong to a machine user with the Instance Login Client
role (IAM_LOGIN_CLIENT). See Connect your self-hosted Login UI to
ZITADEL for the supported setup.
> 
> Run the required quality checks
> 
> Run the checks for all three affected projects, not only the Next.js
application:
> 
> pnpm nx run-many \
>   --projects @zitadel/login @zitadel/client @zitadel/proto \
>   --targets lint build test
> 
> If your update includes .proto changes, regenerate the TypeScript
clients before testing:
> 
> pnpm nx run @zitadel/proto:generate
> 
> For meaningful Login UI changes, also perform end-to-end tests against
a staging ZITADEL instance. At minimum, cover the authentication methods
and policies you enable in production:
> 
> Authorization Code Flow with PKCE and callback to the application
> 
> password, passkey, external identity provider, and applicable MFA/OTP
flows
> 
> registration, account selection, password reset, verification, and
logout
> 
> organization-specific policies, branding, and translated text
> 
> expected negative cases such as invalid or expired codes
> 
> desktop and mobile browsers supported by your application
> 
> Build an immutable image
> 
> The repository's documented Docker build flow is:
> 
> pnpm nx run @zitadel/login:build
> docker build -t
registry.example.com/identity/zitadel-login:vX.Y.Z-custom.1 apps/login
> docker push
registry.example.com/identity/zitadel-login:vX.Y.Z-custom.1
> 
> Use an immutable tag that records both the upstream ZITADEL baseline
and your customization revision. For example:
> 
> v4.17.0-custom.1
> 
> Also retain the source commit and image digest in your release record.
Do not deploy a mutable latest tag to production.
> 
> Deploy outside Vercel
> 
> Vercel is not required. The Login UI can run as the Node.js
application produced by the Next.js build or as a container. In
Kubernetes or EKS, run it as a separate Deployment and Service behind
your ingress or load balancer.
> 
> The following fragment shows the important application settings. Adapt
registry authentication, resources, topology, and ingress to your
platform:
> 
> apiVersion: apps/v1
> kind: Deployment
> metadata:
>   name: zitadel-login
> spec:
>   replicas: 2
>   selector:
>     matchLabels:
>       app: zitadel-login
>   template:
>     metadata:
>       labels:
>         app: zitadel-login
>     spec:
>       containers:
>         - name: login
> image: registry.example.com/identity/zitadel-login:vX.Y.Z-custom.1
>           ports:
>             - name: http
>               containerPort: 3000
>           env:
>             - name: ZITADEL_API_URL
>               value: https://zitadel.example.com
>             - name: NEXT_PUBLIC_BASE_PATH
>               value: /ui/v2/login
>             - name: ZITADEL_SERVICE_USER_TOKEN_FILE
>               value: /var/run/secrets/zitadel/login-client.pat
>           volumeMounts:
>             - name: login-client
>               mountPath: /var/run/secrets/zitadel
>               readOnly: true
>           readinessProbe:
>             tcpSocket:
>               port: http
>           livenessProbe:
>             tcpSocket:
>               port: http
>       volumes:
>         - name: login-client
>           secret:
>             secretName: zitadel-login-client
> ---
> apiVersion: v1
> kind: Service
> metadata:
>   name: zitadel-login
> spec:
>   selector:
>     app: zitadel-login
>   ports:
>     - name: http
>       port: 3000
>       targetPort: http
> 
> Store the PAT in a Kubernetes Secret or external secrets manager and
expose it as a read-only file. Restrict access to the Secret and plan
token rotation. Do not bake the PAT into the image.
> 
> Add CPU and memory requests and limits based on load tests from your
own authentication traffic. Use multiple replicas, disruption controls,
and multi-zone scheduling according to your availability requirements.
> 
> Configure routing and domains
> 
> The Login UI contains a server-side component and Next.js middleware
that proxies OIDC-related requests to ZITADEL. Correct proxy routing is
part of the authentication flow, not only static frontend delivery.
> 
> Same public origin
> 
> When the Login UI and ZITADEL share a public origin, route:
> 
> /ui/v2/login to the Login UI service
> 
> all other ZITADEL paths to the ZITADEL API service
> 
> Set NEXT_PUBLIC_BASE_PATH=/ui/v2/login at build and runtime as
required by your packaging. Keep the value consistent with the Login V2
base URI configured in ZITADEL.
> 
> Separate Login domain
> 
> If the Login UI uses a separate public domain such as
login.example.com:
> 
> Serve it over HTTPS.
> 
> Add the Login domain to the instance's Trusted Domains.
> 
> Configure the proxy in front of the Login UI as documented by ZITADEL:
> 
> x-zitadel-public-host: the public Login UI host, for example
login.example.com
> 
> x-zitadel-instance-host: the ZITADEL instance host, for example
auth.example.com
> 
> Test OIDC and SAML callbacks and every configured external identity
provider through the public route.
> 
> Do not assume that setting only ZITADEL_API_URL replaces the required
host-routing behavior for every topology. The Login App architectural
and proxy guidance is the source of truth for these headers.
> 
> Enable Login V2 safely
> 
> For an existing self-hosted ZITADEL installation, follow Adopt Login
V2 on an existing installation.
> 
> Prefer a gradual rollout:
> 
> Deploy the custom Login UI without changing the instance-wide setting.
> 
> Enable the new Login UI for a non-production test application and set
its custom base URL.
> 
> Run the complete authentication test matrix.
> 
> Expand to selected applications.
> 
> Enable it instance-wide only after validation, if that is your
intended topology.
> 
> Keep a break-glass PAT for a machine user with the Instance Owner role
(IAM_OWNER) so that you can revert the Login V2 setting if interactive
login becomes unavailable. Store and audit this credential according to
your incident-access policy.
> 
> Keep the fork synchronized
> 
> Treat upstream synchronization as a normal application dependency
update, not an automatic deployment.
> 
> For a full repository fork:
> 
> git fetch upstream --tags
> git checkout -b update-login-vX.Y.Z
> git merge vX.Y.Z
> 
> Resolve conflicts while preserving the intent of your customizations.
Then install, generate, test, and build again. For a slim fork, fetch a
freshly filtered copy of the same release and merge its history using
the identical filter configuration used during bootstrap.
> 
> Recommended update workflow:
> 
> Select a released ZITADEL version and read its release notes and
migration guidance.
> 
> Merge that release into a dedicated update branch.
> 
> Review changes to apps/login, packages/zitadel-client,
packages/zitadel-proto, proto, and the root build files.
> 
> Reconcile dependencies and generated code.
> 
> Run lint, build, unit, and staging end-to-end tests.
> 
> Build an immutable custom image and scan it using your normal
supply-chain controls.
> 
> Deploy to staging, then canary or gradually roll out to production.
> 
> Retain the previous image digest and configuration for rollback.
> 
> Do not merge upstream continuously into production without
qualification. Login changes can affect cookies, redirects, external IdP
callbacks, API contracts, and authentication-policy behavior.
> 
> Recommended release policy for your fork
> 
> ZITADEL releases are the upstream input; your custom Login UI image is
your own release artifact. A useful version format is:
> 
> <upstream-zitadel-version>-custom.<revision>
> 
> Maintain a short release record containing:
> 
> upstream tag and commit
> 
> custom fork commit
> 
> container image digest
> 
> applicable ZITADEL backend version or tested range
> 
> configuration changes and migrations
> 
> test evidence and known limitations
> 
> Subscribe to ZITADEL releases and security notices. Decide
deliberately which releases to adopt; prioritize security fixes and
changes that affect Login V2 or the APIs it consumes.
> 
> Troubleshooting checklist
> 
> If the Login UI returns an internal error or an authentication flow
redirects incorrectly, verify:
> 
> the PAT is readable and belongs to a user with IAM_LOGIN_CLIENT
> 
> ZITADEL_API_URL is reachable from the Login pod and has no trailing
slash
> 
> the public Login domain is trusted by the ZITADEL instance
> 
> the configured Login V2 base URI exactly matches the public route and
base path
> 
> the ingress sends Login UI traffic to the correct service
> 
> separate-domain deployments set the public-host and instance-host
headers correctly
> 
> the selected Login source, client package, proto package, and ZITADEL
API version were tested together
> 
> external IdP callback URLs reach the expected public endpoint
> 
> the prior known-good image and configuration can still be restored
> 
> Related documentation
> 
> Login App architecture and setup
> 
> Contribute to the Login UI
> 
> Connect your self-hosted Login UI to ZITADEL
> 
> Adopt Login V2 on an existing installation
> 
> ZITADEL releases

### ⚠️ Manual Action Required
> **Sidebar Update Needed:** Because this PR creates a brand new
document, it will not automatically appear in the site's navigation.
Please review the file location and manually update
`apps/docs/lib/sidebar-data.ts` to include a reference to this new page
before merging.

---
🤖 **Need adjustments?**
Leave a comment below and tag **@zitadel-knowledge-bot** with your
requested changes, and I will automatically update the files and push a
new commit!

---------

Co-authored-by: zitadel-knowledge-bot[bot] <296006658+zitadel-knowledge-bot[bot]@users.noreply.github.com>
Co-authored-by: Matías Racedo <[email protected]>
Co-authored-by: Copilot Autofix powered by AI <[email protected]>
2026-08-27 17:13:04 -03:00
c9d747d407 fix(api): prevent cross-instance caching of well-known app link files (#12644)
# Which Problems Are Solved

The `/.well-known/apple-app-site-association` and
`/.well-known/assetlinks.json` responses are instance specific: the body
depends on the requested host. The responses did not declare this, so a
shared cache whose cache key does not include the host could store one
instance's file and serve it on another instance's domain. This could
result in a wrong or empty file being returned intermittently, breaking
native passkey verification.

Dropping the `public` directive (#12634) was not sufficient: depending
on its cache mode, a shared cache may store any response carrying a
`max-age`.

The docs page for native app links also still documented the old
`Cache-Control: public` header and only the single `get_login_creds`
relation (missing `handle_all_urls`, added in #12634).

# How the Problems Are Solved

- Send **`Vary: Host`** on both well-known responses, declaring that the
body depends on the host: a spec-compliant shared cache must store
per-host variants or skip caching. Clients addressing a single host (the
Apple and Google verifiers, browsers) are unaffected and keep honoring
the configured `max-age`.
- Correct the outdated caching reasoning in the code comments.
- Guard the header with unit and integration tests.
- Update the native app links docs page: both Digital Asset Links
relations and the current response headers.

# Additional Changes

None. `WellKnown.AppLinksCacheControlMaxAge` behavior is unchanged (`0`
still results in `no-store`).

# Additional Context

- Follow-up to #12634; relates to #12497.
- Verified with interleaved requests against two instances: responses
are no longer reused across domains, and each domain consistently serves
its own configuration.
- To be cherry-picked to `v4.x` from `main` once merged.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Fable 5 <[email protected]>
2026-08-26 10:57:28 +02:00
Marco A.andGitHub 6fdb74593c docs: remove outdated Actions v2 feature flag references (#12641)
# Which Problems Are Solved

The docs ([Actions v2 concepts
page](https://zitadel.com/docs/concepts/features/actions_v2) and the
Actions testing guides) state that Actions v2 must be enabled via the
"Actions" feature flag. That flag no longer exists — it is `reserved` in
`proto/zitadel/feature/v2/instance.proto` and `system.proto` — and
Actions v2 is available by default. The callout even linked to a
non-existent API field.

# How the Problems Are Solved

- Removed the stale feature-flag callout (and the roadmap teaser
callout) from `concepts/features/actions_v2.mdx`.
- Removed the "Your ZITADEL instance needs to have the actions feature
enabled." prerequisite bullet from the seven
`guides/integrate/actions/testing-*.mdx` guides.

Pure deletions, 17 lines across 8 files.

# Additional Context

The `SetInstanceFeatures` mentions in `hosted-login.mdx` and
`webkeys.mdx` refer to flags that still exist and are untouched.
2026-08-25 16:58:43 +02:00
6127b567e7 feat(oidc): provide actor information in userinfo actions (#12566)
# Which Problems Are Solved

Expose impersonating actor to `preAccessToken` and `preUserinfo`
actions.

# How the Problems Are Solved

Provide the `actor` information already present in the OIDC session
model of JWT token to the relevant actions. Both goja-based actions "v1"
and webhook based execution targets carry the actor information now.
Actor remains null in case of non-impersonated tokens. Actor may contain
nested actors to display a delegation chain of impersonators.

# Additional Changes

- dba6261c8ed0dfd00ffde6193910b1405b17432f: refactor `userinfoFlows` to
reduce complexity, add test coverage and solve a couple of potential
bugs.
- `getClientId` in actions was previously undocumented. Added to
documentation.
- A skill that reproduces manual testing using webhook.site or a local
sink.

# Additional Context

- Closes https://github.com/zitadel/zitadel/issues/12097

---------

Co-authored-by: Claude Sonnet 5 <[email protected]>
Co-authored-by: Copilot Autofix powered by AI <[email protected]>
Co-authored-by: Wim Van Laer <[email protected]>
2026-08-12 07:36:56 +00:00
318bfc36d7 feat: native app links for passkeys (#12580)
# Which Problems Are Solved

- Native iOS/Android passkeys need OS trust files on the ZITADEL domain;
without them, Associated Domains / App Links verification fails.
- Operators could not configure iOS Team ID + Bundle ID or Android
package name + SHA-256 fingerprints on OIDC apps.
- `/.well-known/apple-app-site-association` and
`/.well-known/assetlinks.json` were not served from application config.

# How the Problems Are Solved

- Add iOS/Android app-link fields on OIDC app create/update (Application
API v2 + Management), with validation.
- Persist and project those fields; query active app-link configs
instance-wide.
- Serve AASA (`webcredentials`) and Digital Asset Links
(`get_login_creds`) from well-known paths, with configurable
`Cache-Control` and fingerprint normalization at serve time.
- Console UI to edit the fields, with links to the well-known endpoints.
- Operator docs for configuration, endpoints, caching, and verification.

# Additional Changes

- Document on API fields that well-known responses may be HTTP-cached
and platform verifiers may delay propagation.
- Runtime config: `WellKnown.AppLinksCacheControlMaxAge` (default `5m`;
`0` → `no-store`).

# Additional Context

- Closes #12497
- Implemented and reviewed as stack:
  - #12531 API contract
  - #12532 storage wiring
  - #12536 well-known endpoints
  - #12537 console
  - #12547 docs

---------

Co-authored-by: Cursor <[email protected]>
2026-08-12 08:04:27 +02:00
jmaretteandGitHub 9070b537d2 feat(oidc): add RFC 7592 dynamic client management (#12315)
# Which Problems Are Solved

#12313 lets clients register themselves as OIDC applications (RFC 7591),
but a dynamically registered client has no way to manage itself
afterwards:

- The registration response returns no `registration_access_token` and
no `registration_client_uri`, which RFC 7591 and 7592 clients (including
the MCP SDKs) expect in order to manage their own registration.
- There is no way to read back, update or delete a registration without
an operator acting on the client's behalf through the Console or the
Management API. A client that needs to rotate a redirect URI has to ask
a human.

# How the Problems Are Solved

Implement the [OAuth 2.0 Dynamic Client Registration Management Protocol
(RFC 7592)](https://datatracker.ietf.org/doc/html/rfc7592): `GET`, `PUT`
and `DELETE` on `/oauth/v2/register/{client_id}`, wired through the same
`op.WithSetRouter` hook as the registration endpoint.

## Control plane: the same security settings, no new switch

The management endpoints are gated on the
`DynamicClientRegistrationSettings.enabled` introduced in #12313. No new
setting, no feature flag: a client that may register may manage what it
registered. When registration is disabled the endpoints answer `404`,
exactly as `POST /oauth/v2/register` does.

`allow_unauthenticated` selects the registration mode and does not apply
here, because the registration access token authorizes these endpoints
in both modes.

## Authorization: the registration access token, and nothing else

RFC 7592 §3 defines the credential as the registration access token
issued at registration and bound to a single client, not a user
credential. So a client manages only itself, proving it with the token
it was handed:

| | Registration (`POST`) | Management (`GET` / `PUT` / `DELETE`) |
| --- | --- | --- |
| Credential | Access token, or none in open mode | Registration access
token |
| Permission | `project.app.register_dynamic` in token mode | None |

Neither `project.app.register_dynamic`, which governs who may *create*
clients, nor `project.app.write` applies to the management endpoints.
One authorization path per endpoint is easier to reason about than two,
and there is no user identity to check against in the first place.
Operators keep managing dynamically registered clients through the
Management API and the Console, like any other application; they cannot
obtain a client's registration access token, by design.

Status codes: a missing, malformed or non matching token is `401`; a
client that no longer exists is `404`. The token's `client_id` is
checked against the path before any lookup, so a token bound to another
client is rejected without touching the database.

## The token itself

- **Stored like a client secret.** Only its passwap hash is persisted,
through a new
`project.application.config.oidc.registration_token.changed` event; the
plain token leaves the server exactly once, in the response that issues
it.
- **Bound to its client and organization** through authenticated
encryption with the instance key, the same mechanism as refresh tokens.
The opaque token handed to the client encrypts
`client_id:org_id:secret`, so the endpoint recovers the binding before
checking the secret against the stored hash.
- **Verified in O(1)** against the hash on the projection, folded into
the client lookup the handler already does, with a strongly consistent
fallback to the eventstore so a token that was just rotated is accepted
before it is projected.
- **Rotated on update** (RFC 7592 §4.4) and invalidated implicitly when
the client is deleted. A read does not rotate it. Every update rotates,
including one that changes no metadata, so a client always gets a usable
token back.

## Reusing the existing persistence

A small refactor extracts the shared OIDC application creation and
update logic (`pushOIDCApplication` gains a variadic `extraEvents`, and
`oidcApplicationChangeEvent` is lifted out of `UpdateOIDCApplication`),
so the dynamic commands reuse it without the permission check. The
registration token event is pushed atomically with the application, so a
registered client is never left unmanageable.

# Additional Changes

- Setup step **74** adds the `registration_token` column to the
`apps7_oidc_configs` projection, with a reducer for the new event
modelled on the client secret. Applications are not part of the
relational storage projection, so there is no second write path to keep
in sync.
- The registration response (`POST`) now also carries
`registration_access_token` and `registration_client_uri`.
- Docs: a "Manage a registration" section in the dynamic client
registration guide covering the three operations, the authorization
model, token rotation and the new limitations, plus the management
endpoints on the OIDC endpoints page.
- Unit tests for the command layer (issue, verify, rotate, update,
delete), the projection reducer, and the token binding and response
mapping. An integration test covers the full lifecycle (register, read,
update with rotation, delete, read again with `404`), a token bound to
another client, and the endpoints not being served when registration is
disabled. None of the integration calls carries a user access token,
which pins down that the management endpoints require no permission of
their own.

# Additional Context

- Part of the MCP authorization effort in #9810. Follow-up to #12313
(RFC 7591), which is now merged; this branch has been rebased on `main`
and carries the settings and authorization model agreed there.
- **Open question for the review**: the authorization model above
(registration access token only, no operator path through these
endpoints) is the one I proposed when #12313 was reviewed, but it was
never explicitly confirmed. If you would rather have `project.app.write`
also grant access to a client in the operator's own organization, that
is a contained change in `authorizeClientManagement`.
- Rotation is eventually consistent: right after an update the previous
token may keep working for a brief moment until the rotation is
projected, while the new token works immediately through the eventstore
fallback. This matches how ZITADEL validates access tokens, where
revocation is likewise eventually consistent. It is documented in the
guide and asserted as such in the integration test.
- A database migration is introduced for the new projection column. No
proto change and no new feature flag.
- Verified locally: full build, `go vet` including `-tags integration`,
`golangci-lint` with 0 issues, and the unit suites for every touched
package. As before, I cannot run the integration suite here, so CI
remains the check on it.
2026-08-03 08:37:59 +00:00
jmaretteandGitHub f34cc45eac feat(oidc): add RFC 7591 dynamic client registration (#12313)
# Which Problems Are Solved

ZITADEL cannot currently be used as an OAuth 2.0 Authorization Server by
clients that need to register themselves at runtime:

- There is no [OAuth 2.0 Dynamic Client Registration (RFC
7591)](https://datatracker.ietf.org/doc/html/rfc7591) endpoint, and no
`registration_endpoint` is advertised in the discovery document.
- This blocks [Model Context Protocol
(MCP)](https://modelcontextprotocol.io) clients (Claude Desktop,
claude.ai, Cursor, the MCP SDKs), whose OAuth 2.1 authorization profile
expects an Authorization Server that supports dynamic client
registration. They self-register before any user context exists and only
then start the authorization code + PKCE flow.

# How the Problems Are Solved

## Control plane: instance security settings

No feature flag and no runtime config. A
`DynamicClientRegistrationSettings` message is nested under the instance
`SecuritySettings` in `settings/v2`, next to `embedded_iframe` and
`enable_impersonation`:

| `enabled` | `allow_unauthenticated` | Behaviour |
| --- | --- | --- |
| `false` (default) | — | `POST /oauth/v2/register` returns `404`,
`registration_endpoint` absent from discovery |
| `true` | `false` (default) | Token-gated registration, client homed in
the token's organization |
| `true` | `true` | Open registration for MCP hosts, client homed in the
instance default organization |

Both values are carried on `authz.Instance` and loaded with the
instance, exactly like `EnableImpersonation`, so the discovery document
and the endpoint read them per request without an extra query.
`allow_unauthenticated` implies `enabled` in both implementations, so a
single setting closes the endpoint.

Adding the two columns bumps the projection table to
`projections.security_policies3`, following what the impersonation
setting did for `security_policies2`.

## Authorization: a dedicated permission, not `project.app.write`

- New org-scoped permission **`project.app.register_dynamic`**, granted
by default to `ORG_OWNER`, `IAM_OWNER` and `IAM_ORG_MANAGER` — in both
`InternalAuthZ` and `SystemAuthZ`, mirroring where `project.app.write`
already lives.
- New built-in role **`ORG_DYNAMIC_CLIENT_REGISTRAR`** carrying only
that permission, so a service user can self-register clients without
gaining write access to the organization's existing applications.
- Open mode (`allow_unauthenticated = true`) requires no token and no
permission: that is the MCP self-register path.
- A valid token whose user lacks the permission is answered with **`403
insufficient_scope`** (RFC 6750 §3.1) rather than `401 invalid_token`,
so a caller can tell "your token is bad" from "your token may not do
this". `verifyAccessToken` also reports invalid tokens as permission
denied, so the authorization failure is marked explicitly to keep the
two apart.

The check runs once, at the endpoint, right after the token is verified
and before any state is created, so it also gates the auto-provisioning
of the organization's DCR project. The permission itself lives in the
command layer next to the others
(`Commands.CheckPermissionRegisterDynamicClient`). As the OIDC endpoints
are not behind the authorization interceptor, the caller identity is
derived from the verified token, the way token exchange does for the
impersonating actor.

## Registration itself

- **Registered clients are ordinary OIDC applications.** The endpoint
reuses the existing application-creation events
(`NewApplicationAddedEvent` and `NewOIDCConfigAddedEvent`), so the whole
token, authorization and introspection flow keeps working unchanged.
They are stored in a dedicated, auto-provisioned project named `ZITADEL
DCR` per organization (name lookup), so they do not pollute the
`IAMProject` or other projects.
- Input is validated with the existing `GetOIDCV1Compliance` rules;
`private_key_jwt` and `jwks`/`jwks_uri` are rejected with
`invalid_client_metadata`.
- **Endpoint wiring without forking the library.** `NewServer` calls
`op.RegisterServer` instead of `op.RegisterLegacyServer`, so the new
route can be added through the same `op.WithSetRouter` hook as the
authorize callback. `op.RegisterLegacyServer` appends a middleware after
the caller options, which chi forbids once a route has been registered;
`op.NewIssuerInterceptor` reproduces the issuer middleware it would
otherwise add. The `/oauth/v2` routing prefix is unchanged.

# Additional Changes

- **`application_type` inference for custom-scheme redirects** (last
commit, isolated). `application_type` is an OpenID Connect member; RFC
7591 does not define it, so clients that only implement RFC 7591 omit
it. Native MCP hosts doing so register custom-scheme redirect URIs,
which ZITADEL reserves for native applications, and the OIDC default of
`web` rejected them with `invalid_redirect_uri`. Inference is limited to
custom schemes and to an absent `application_type`, so every request
accepted today keeps its current application type and auth method — an
`http` loopback redirect still yields a web application. Happy to drop
this commit if you would rather keep it out of this PR.
- **Docs**: a Dynamic Client Registration integration guide, a
`registration_endpoint` section on the OpenID Connect endpoints page,
and the new role in the administrators table (plus its description in
the Console translations).

# Additional Context

- Part of #9810. This is **Phase 1 (DCR)**; #12315 adds RFC 7592 and
#12316 adds CIMD, both stacked on this branch and rebased once this
lands.
- Implements the control-plane and authorization model requested in the
review of this PR, itself a refinement of the direction in
https://github.com/zitadel/zitadel/issues/9810#issuecomment-4891176997.
- **Out of scope (deliberately)**: RFC 7592, CIMD, persisting free-form
metadata (`logo_uri`, `contacts`, and similar), and `private_key_jwt` /
`jwks_uri` (rejected cleanly). No new event types.
- Follow-ups explicitly left out, per the review: DCR client lifecycle
and richer audit, consent and custom scopes, JWT / `aud` (RFC 8707), and
RFC 8414 AS metadata (currently absent from both ZITADEL and
`zitadel/oidc`).
2026-07-30 14:43:47 +00:00
6bd46c5fb6 feat(login): idp userAction oneof (#12433)
Closes #11369

# Which Problems Are Solved

During SSO login with an external IdP, Actions v2 lets you manipulate
the `RetrieveIdentityProviderIntent` response to shape the user that
ZITADEL creates or updates. There was an inconsistency between the two
flows:

- **First login (user does not exist):** the response carried an
`addHumanUser` object (mirroring the deprecated `AddHumanUser` API),
which **does** allow setting user `metadata`.
- **Subsequent logins (user exists):** the response carried an
`updateHumanUser` object (mirroring the deprecated `UpdateHumanUser`
API), which does **not** support metadata.

As a result, actions could set metadata when creating a user but not
when updating one. Customers doing SSO attribute mapping had to make a
separate `SetUserMetadata` call on every subsequent login — extra
latency and a non-atomic update. The proto/backend already gained a
non-deprecated `user_action` oneof (`create_user` → `CreateUserRequest`,
`update_user` → `UpdateUserRequest`, both supporting metadata), but the
login app was still reading the deprecated flat fields, so the new
capability was unreachable from the frontend.

# How the Problems Are Solved

Migrate the login app's IDP intent handler to consume the new
`user_action` oneof, with a fallback to the deprecated fields so older
API responses keep working during the transition.

- **`zitadel.ts`** — added `createUser` / `updateUser` client wrappers
calling the non-deprecated `UserService.CreateUser` /
`UserService.UpdateUser` endpoints.
- **`idp-intent.ts`** — added three helpers, each preferring
`user_action` and falling back to `add_human_user` /
`update_human_user`:
- `resolveCreateUser` — flat read view for org resolution,
required-field checks, and registration-form pre-fill.
- `buildCreateUserRequest` — passes the action's `CreateUserRequest`
through and injects the resolved `organizationId`; maps the deprecated
flat payload into the nested shape on fallback.
- `buildUpdateUserRequest` — builds an `UpdateUserRequest` **including
metadata** (the fix); deliberately syncs only
profile/email/phone/metadata (not username) to preserve existing
auto-update behavior and avoid invalidating sessions.
- Rewired all handlers (`handleUserExists`, `handleAutoLinking`,
`handleAutoCreation`, `handleManualCreation`,
`resolveOrganizationForUser`) to use these, and switched auto-create to
read `CreateUserResponse.id`.
- **Tests** — updated mocks/assertions to the new request shapes and
added two cases exercising the `user_action` oneof with metadata (create
+ update). 817/817 login unit tests pass; no new type errors.

# Additional Changes

Updated the Actions v2 guide
`guides/integrate/actions/testing-response-manipulation.mdx` (the
unreleased/`latest` docs) to reflect the new response shape:

- Go handler example now manipulates `resp.GetCreateUser()` /
`resp.GetUpdateUser()` and appends `user.Metadata`, demonstrating
metadata on both flows.
- Both JSON payloads switched from `addHumanUser` to the nested
`createUser` shape (`human.profile`, `human.email`, `human.idpLinks`,
top-level `metadata`).
- Added a Callout explaining first-login → `createUser` vs.
existing-user → `updateUser`, that both support metadata, and that
`addHumanUser`/`updateHumanUser` are deprecated.
- Updated the claim-mapping debugging section to the new
`createUser.human.profile.givenName` path.

Versioned snapshots (`v4.12`/`v4.13`/`v4.14`) were intentionally left
unchanged, as they document releases where the old API was correct.

---------

Co-authored-by: gayathri <[email protected]>
2026-07-16 12:01:07 +02:00
f0a999bccd docs: clarify human PAT limitation and link issue #10915 (#12412)
## Which Problems Are Solved

There is currently a knowledge gap in the documentation where users are
unclear on if or why human accounts cannot have Personal Access Tokens
(PATs), which has led to confusion in community channels like Discord
and GitHub.

## How the Problems Are Solved

Clarified that human PATs are currently not supported in the Personal
Access Token documentation and added a direct link to GitHub Issue
#10915 so users can track and upvote this capability.

## Additional Changes

None

## Additional Context

None

---------

Signed-off-by: Rajat Singh <[email protected]>
Co-authored-by: Rajat Singh <[email protected]>
2026-07-14 14:16:13 +00:00
b4e205c601 docs: add IdP claim mapping troubleshooting to Actions V2 response manipulation guide (#12339)
# Which Problems Are Solved

- The documentation had no guidance for debugging auto-user-creation
failures caused by unexpected or missing claims from an external IdP,
leaving users without a path forward when they see a
`SetHumanProfile.GivenName` validation error during OIDC login.

# How the Problems Are Solved

- Adds a troubleshooting section to the Actions V2 response manipulation
guide explaining how to use the `RetrieveIdentityProviderIntent` webhook
to inspect `rawInformation` and identify claim key mismatches from the
external IdP.

# Additional Changes

- None

Signed-off-by: Rajat Singh <[email protected]>
Co-authored-by: Rajat Singh <[email protected]>
2026-07-01 09:35:06 -03:00
zitadel-knowledge-bot[bot]GitHubzitadel-knowledge-bot[bot] <296006658+zitadel-knowledge-bot[bot]@users.noreply.github.com>fcoppede
9ccafca12b docs: update knowledge gap from thread 1512031509387673610 (#12328)
Automatically generated PR targeting 1 files.

**Thread ID:** 1512031509387673610
**Action:** UPDATE

**AI Summary:**
> The documentation lacks clear explanation of how the 'Use new login
UI' checkbox and 'Custom base URL for the new Login UI' field work
together, including step-by-step configuration and troubleshooting
guidance.

---------

Co-authored-by: zitadel-knowledge-bot[bot] <296006658+zitadel-knowledge-bot[bot]@users.noreply.github.com>
Co-authored-by: fcoppede <[email protected]>
2026-06-24 15:32:38 +00:00
Livio SpringandGitHub 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
f4f43f5248 docs: fix broken GitHub code-reference blocks after fumadocs migration (#12213)
# Which Problems Are Solved

Pages in `apps/docs` that embed source from GitHub via the Docusaurus
convention

````
```js reference

https://github.com/zitadel/actions/blob/main/examples/org_metadata_claim.js
```
````

stopped working after the migration from Docusaurus to fumadocs. The old
`docusaurus-theme-github-codeblock` plugin used to fetch the file and
render it; fumadocs has no support for that meta, so the page rendered
the raw URL as plain code-block text. Visible at
`/docs/apis/actions/code-examples` and 16 other pages.

# How the Problems Are Solved

- Converted every ```` ```<lang> reference\n<URL>\n``` ```` block (46
total across 17 `.mdx` files) to the native fumadocs JSX form:
`<GithubCodeBlock url="<URL>" />`. The existing `<details>`/`<summary>`
collapsibles around blocks are kept — they're an authoring choice, not
part of the rendering bug.
- Updated `apps/docs/components/github-code-block.tsx` to render via
`DynamicCodeBlock` from `fumadocs-ui/components/dynamic-codeblock`
(proper shiki highlighting) instead of raw `CodeBlock` + `Pre` (which
produced unhighlighted output). Also fixed language detection so a URL
hash like `#L10-L20` no longer pollutes the language token.
- Registered `GithubCodeBlock` globally in
`apps/docs/mdx-components.tsx`, matching how every other shared
component (`APIPage`, `Callout`, `Tab/Tabs`, `Step/Steps`, `Admonition`,
`TerminologyUpdate`) is exposed. MDX files no longer need a local
`import`.

# Additional Changes

- Normalized the two MDX files that were already using the JSX form
(`examples/secure-api/python-django.mdx`,
`examples/secure-api/java-spring.mdx`): removed their now-redundant
local `import { GithubCodeBlock }` and rewrote 9 long-form
`<GithubCodeBlock url="..."></GithubCodeBlock>` tags to self-closing for
consistency.

# Additional Context

Verified locally with `pnpm --filter @zitadel/docs dev`:

- `/docs/apis/actions/code-examples` — 20 shiki-highlighted code blocks
rendered inside the `<details>` collapsibles (was 0).
- `/docs/apis/openidoauth/claims` — line-range hashes (`#L9-L11`)
honored.
- `/docs/examples/login/flutter` — mixed languages (xml/dart/html)
detected and highlighted.
- `/docs/guides/integrate/external-audit-log` — edge case of fenced
reference indented inside a numbered list also converted and rendered.

Greps:
- `^[ \t]*\`\`\`[a-zA-Z0-9]+ reference` in `apps/docs/content/**/*.mdx`
→ 0 matches.
- `<GithubCodeBlock url="` in `apps/docs/content/**/*.mdx` → 55 matches.
- `from '@/components/github-code-block'` in
`apps/docs/content/**/*.mdx` → 0 matches.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
2026-05-28 11:02:44 -07:00
Federico CoppedeandGitHub f96a9c54ac docs: clarify exp and iat claim precedence for private key JWTs (#12110)
This PR updates the "Private Key JWT Auth for Service Accounts"
documentation to explicitly clarify the relationship between the exp
(expiration) and iat (issued at) claims.

Previously, the documentation didn't make it clear what happens if a
developer sets an exp claim far into the future. This update clarifies
that while the exp value is strictly enforced, the iat claim takes
precedence if the exp is set to more than 1 hour in the future (i.e.,
ZITADEL will reject the JWT once the iat is older than 1 hour,
regardless of the exp time).

**Changes included:**

Updated the description of the exp claim in the JWT payload section to
highlight the 1-hour iat limit enforcement.
2026-04-27 14:58:50 +02:00
Wim Van LaerandGitHub 4fdc3d6d3b docs: fixed login v1 jwt idp callback (#11989)
# Which Problems Are Solved

The example callback url for JWT idP was wrong
2026-04-10 07:51:01 +00:00
b145f1c85a docs: added documentation for jwt idp regarding callback uris (#11925)
# Which Problems Are Solved

The documentation did not reflect the difference between login v1 and v2
for configuring a JWT-idp

# How the Problems Are Solved

Added a warning in the docs which highlight that difference.

# Additional Changes

- Restructured the docs to reflect a more generic approach.
- Fixed indentation inside the `<Callout type="warning">` block so the
content renders as formatted text instead of a code block.
- Fixed inconsistent acronym casing in the use-case section: "jwt" →
"JWT" and "idp" → "IdP" to match the rest of the page and UI terms.

# Additional Context

- Closes: #11589

<!-- START COPILOT CODING AGENT TIPS -->
---

💡 You can make Copilot smarter by setting up custom instructions,
customizing its development environment and configuring Model Context
Protocol (MCP) servers. Learn more [Copilot coding agent
tips](https://gh.io/copilot-coding-agent-tips) in the docs.

---------

Co-authored-by: Copilot <[email protected]>
Co-authored-by: copilot-swe-agent[bot] <[email protected]>
2026-03-30 10:23:42 +02:00
SilvanandGitHub 4bc899d052 docs: Improve README for local development and standardize tab usage (#11865)
## Problem statement

- `<Tabs>` were not rendered in docs
- Missing steps in README for local dev

## How it was solved

- Enhance the README to better describe local development setup and
clean up formatting.
- Standardize the use of tabs across various components for consistency.

## Additional info

To not cause merge conflicts with
https://github.com/zitadel/zitadel/pull/11729 changes in
`/self-hosting/manage/configure/configure.mdx` were skipped
2026-03-23 14:39:07 +00:00
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 <[email protected]>
2026-03-23 13:32:27 +00:00
279f592d80 docs: tweaking titles for better SEO appeal (#11852)
Just updating some title and descriptions for better SEO discovery

---------

Co-authored-by: Copilot Autofix powered by AI <[email protected]>
2026-03-16 20:03:11 -03:00
34e6a792d2 docs: text customization on Login V2 (#11849)
This PR updates the "Hosted Login UI" documentation to clarify how text
customization works for the new Hosted Login V2.

**Changes:**

1. Adds a warning callout to the "Customization options" section.
2. Clarifies that Login V2 interface texts currently cannot be modified
via the ZITADEL Console UI.
3. Provides the required workaround, directing users to use the Settings
V2 API to patch translation keys using the en.json locale file as a
reference.

---------

Co-authored-by: Copilot Autofix powered by AI <[email protected]>
2026-03-16 17:12:10 +01:00
Fabienne BühlerandGitHub 9a4530bf77 docs: Add note for updated terminology (#11802)
# Which Problems Are Solved

For existing customers it could be hard to understand the new aligned
terms we have defined, if they are already used to the "old term"

# How the Problems Are Solved

Adding notes at the top of docs pages to state clearliy new and old
terms.
2026-03-16 09:10:48 +00:00
cbf05dbe16 fix(ui/auth): display correct Apple sign-in callback URL (#11439)
# Which Problems Are Solved

- Apple Sign-In uses a Form Post method to return the authorization
response, which ZITADEL handles at a specific endpoint ending in /form
for the login v1.
- The Console previously displayed the standard callback URL (without
`/form`), causing confusion and configuration errors..


# How the Problems Are Solved

- **Console**: Updated ProviderNextService to accept a suffix string . 
- **Console**: Updated ProviderAppleComponent to append the suffix to
the urls.


# Additional Changes
- Updated `docs/docs/guides/integrate/identity-providers/apple.mdx` to
explicitly state that the callback URL end with `/form`
- Updated `docs/docs/guides/integrate/login-ui/external-login.mdx` to
clarify the `/form` suffix requirement for Apple Sign-In in the hosted
login documentation.
# Additional Context
- fixes #11438

---------

Co-authored-by: Ramon <[email protected]>
Co-authored-by: Florian Forster <[email protected]>
2026-03-12 10:24:48 +00:00
Florian ForsterandGitHub 2763e93a61 docs: shorten page titles for SEO (#11750)
## What

Shorten the `title` frontmatter on 20 docs pages so that the rendered
`<title>` tag stays within the 70-character SEO limit.

The root layout appends ` | ZITADEL Docs` (15 chars) to every page
title, meaning the raw frontmatter `title` must be ≤ 55 characters. A
site scan flagged all 20 pages for the "Title too long" warning (titles
ranged from 71 to 92 characters in the final rendered tag).

## Why

Search engines may truncate or ignore `<title>` tags longer than 70
characters, hurting click-through rates and keyword relevance in search
results.

## How

Each title was rewritten to:
- Fit within 55 characters (leaving room for ` | ZITADEL Docs`)
- Front-load the primary search keyword (product name, protocol, or
feature)
- Include "ZITADEL" where users realistically search for `{feature}
ZITADEL`
- Avoid generic filler words ("Configure", "Set up", "Support for")

All 20 files already have a `sidebar_label` field that preserves the
original descriptive title for sidebar navigation — no navigation
changes result from this PR.

## Pages updated

| Page | Before (chars) | After (chars) |
|---|---|---|
| concepts/features/passkeys | 82 | 64 |
| guides/integrate/identity-providers/openldap | 72 | 58 |
| guides/solution-scenarios/b2b | 78 | 59 |
| guides/integrate/services/cloudflare-oidc | 86 | 54 |
| guides/integrate/login/hosted-login | 71 | 55 |
| guides/integrate/service-accounts/private-key-jwt | 76 | 56 |
| guides/integrate/service-accounts/client-credentials | 78 | 60 |
| guides/integrate/services/google-cloud | 92 | 57 |
| guides/solution-scenarios/saas | 88 | 65 |
| guides/migrate/sources/auth0 | 73 | 65 |
| guides/integrate/login-ui/oidc-standard | 81 | 55 |
| guides/integrate/login-ui/username-password | 73 | 59 |
| guides/integrate/login-ui/device-auth | 78 | 60 |
| guides/integrate/identity-providers/linkedin_oauth | 72 | 58 |
| guides/integrate/identity-providers/pingfederate-saml | 74 | 61 |
| guides/integrate/service-accounts/personal-access-token | 82 | 62 |
| legal/service-description/cloud-service-description | 76 | 61 |
| guides/integrate/login/login-users | 83 | 55 |
| guides/integrate/identity-providers/migrate | 74 | 65 |
| guides/manage/console/projects-overview | 71 | 62 |

All final `<title>` values are between 54 and 66 characters (well under
the 70-char limit).
2026-03-04 11:14:11 +00:00
Vitor Bari BucciantiandGitHub a3d0df1f4c docs: small fixes across docs (#11687)
# Which Problems Are Solved

This PR fixes a couple of minor issues in the docs:
- Duplicated sentences
- Obsolete names
- Wrong header hierarchy 

# How the Problems Are Solved

Solved by rewriting/fixing the mdx files.

# Additional Changes

none

# Additional Context

none
2026-02-27 11:44:35 +01:00
Florian ForsterandGitHub 2c260a1f10 docs: add description frontmatter to content pages for SEO (#11654) 2026-02-21 07:44:42 -08:00
9b2507832a docs: update outdated screenshots (#11630)
# Which Problems Are Solved

Update outdated screenshots in the docs. 

# How the Problems Are Solved

By replacing the outdated ones with current screenshots

# Additional Changes
Replace the usage of "authorization" with "role assignments" in docs
where it was previously missed.

# Additional Context
- Follow-up for PR https://github.com/zitadel/zitadel/pull/11588
- there are some more docs to be updated, which will be done in a
following PR

---------

Co-authored-by: Copilot <[email protected]>
2026-02-20 12:33:49 +01:00
545e2665f3 docs: fix code block styling by migrating to native fumadocs-ui components (#11646)
# Which Problems Are Solved

- Docs code blocks that display imported files (YAML configs, Go
examples) render as unstyled plain text with no syntax highlighting,
unlike fenced code blocks which go through `rehypeCode`/Shiki

# How the Problems Are Solved

- Deleted `components/docusaurus/code-block.tsx` entirely — no custom
wrapper is needed anymore
- Replaced all usages with `DynamicCodeBlock` from
`fumadocs-ui/components/dynamic-codeblock` directly in MDX files; it
handles SSR pre-rendering through the same Shiki pipeline as all other
code blocks
- Raw file content (YAML, Go) is imported as strings via webpack's
existing `asset/source` rule already configured in `next.config.mjs` —
no new infrastructure required
- In `_proxy_guide_tls_mode.mdx`, the `Commands` JSX component was
refactored from concatenated JSX array children to a single template
literal string, which is what `DynamicCodeBlock` (and any string-based
highlighter) requires
- All changes applied to both non-versioned docs and versioned snapshots
(v4.10, v4.11)

# Additional Changes

None.

# Additional Context

- Affected MDX files: YAML config files under
`self-hosting/manage/configure/`, reverse-proxy guides,
`self-hosting/deploy/compose.mdx`, and the actions
`testing-request-signature.mdx` guide

<!-- START COPILOT ORIGINAL PROMPT -->



<details>

<summary>Original prompt</summary>

> 
> ----
> 
> *This section details on the original issue you should resolve*
> 
> <issue_title>code block style is weird</issue_title>
> <issue_description>### Preflight Checklist
> 
> - [x] I could not find a solution in the existing issues, docs, nor
discussions
> - [x] I have joined the [ZITADEL chat](https://zitadel.com/chat)
> 
> ### Describe the docs your are missing or that are wrong
> 
> This hurts my eyes:
> 
> <img width="867" height="525" alt="Image"
src="https://github.com/user-attachments/assets/b7b66d92-56e1-4e0a-8dba-3cab448930fe"
/>
> 
> ### Additional Context
> 
> _No response_</issue_description>
> 
> <agent_instructions>the user reported a weirdly styled code block in
the docs page where we used to display yaml files. Investigate the docs
and fix the styling</agent_instructions>
> 
> ## Comments on the Issue (you are @copilot in this section)
> 
> <comments>
> <comment_new><author>@fforootd</author><body>
> haha, ok that is certainly weird, let me look at
that.</body></comment_new>
> </comments>
> 


</details>



<!-- START COPILOT CODING AGENT SUFFIX -->

- Fixes zitadel/zitadel#11645

<!-- START COPILOT CODING AGENT TIPS -->

---------

Co-authored-by: copilot-swe-agent[bot] <[email protected]>
Co-authored-by: fforootd <[email protected]>
Co-authored-by: Florian Forster <[email protected]>
2026-02-20 06:46:32 +05:30
369b468720 chore: consistent naming for object ids (#11604)
<!--
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
As part of the consistent naming effort, this PR focuses on Resource
ID's.

# How the Problems Are Solved
Renamed generic Resource ID to matching Object + ID such as

- Organization ID
- User ID
- Application ID
- Identity Provider ID
- Instance ID
- Project ID

# Additional Changes
None

# Additional Context
- Closes #11306

---------

Co-authored-by: Livio Spring <[email protected]>
2026-02-19 06:21:01 +00:00
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 <[email protected]>
2026-02-18 14:46:21 +00:00
Wim Van LaerandGitHub 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
Florian ForsterandGitHub d17e954f26 docs: update fumadocs dependencies (#11594)
This PR upgrade Fuma docs and alings some code snippets since shiki
3.22.0 does no longer include defaults for `curl`.
2026-02-17 21:41:48 +01:00
93a4ed8857 chore: rename authorization to role assignment (#11588)
# Which Problems Are Solved

As part of the naming consistency initiative, the terms Authorization,
User grants, internal authorization and other similar terms that are
used in the context of assigning project roles to users are replaced by
Role Assignments in the UI, API docs, and documentation.

# How the Problems Are Solved

By replacing the following terms with Role Assignments in API docs,
Console UI, and guides.

- Authorization
- internal authorization
- external authorization
- User Grant
- Roles and Authorizations

By adding a note in the API docs to make it clear that Authorization
within the context of Authorization Service API refers to role
assignments, not to OAuth authorization as it is still used in the APIs.

By updating some screenshots in documentation / guides to show "Role
Assignments" instead of "Authorizations" in the UI.

# Additional Changes

N/A

# Additional Context

- Closes https://github.com/zitadel/zitadel/issues/11289

---------

Co-authored-by: Copilot <[email protected]>
2026-02-17 13:14:57 +01:00
943d9e0967 docs: fix broken docs links and insecure redirects (#11591)
Summary
- audited doc CSVs to replace insecure http links and ensure paths point
to the migrated `apps/docs` structure
- preserved downstream link intent while keeping references aligned with
the `/docs` deployment prefix

---------

Co-authored-by: Copilot <[email protected]>
2026-02-13 11:01:11 -08:00
Marco A.andGitHub 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
Wim Van LaerandGitHub 9b064d8b74 docs: fixed some typos, formatting, grammar (#11540)
# Which Problems Are Solved

- Typos
- Punctuation
- Markdown table formatting
- ...

(A bunch of issues my editor gave)

# How the Problems Are Solved


# Additional Changes


# Additional Context
2026-02-13 07:45:17 +01:00
cc7a6145f1 chore: rename manager/member to administrator (#11542)
# Which Problems Are Solved

As part of the naming consistency initiative, the terms `Manager`,
`Member` and other similar terms that are used in the context of an
administrator are replaced by `Administrator` in the UI and
documentation.

# How the Problems Are Solved

By replacing the following terms with Administrator / Administrator
Roles in docs, Console UI, and guides.

- Manager
- Add Manager
- Add a Manager
- Administrator (used as a person type)
- Membership
- Member grants

# Additional Changes
minor grammatical edits in the docs

# Additional Context
- Closes https://github.com/zitadel/zitadel/issues/11291

---------

Co-authored-by: Marco A. <[email protected]>
2026-02-11 17:43:06 +01:00
7e2c335df9 fix: Login v2 callback url's in console #11053 (#11398)
# Which Problems Are Solved
Correctly display callback URL's in the console for external identity
providers.
<img width="1965" height="527" alt="CleanShot 2026-01-21 at 14 21 56"
src="https://github.com/user-attachments/assets/149fa74d-02d9-4e81-9933-a16eaeac316d"
/>

# How the Problems Are Solved
Changed the template string in the console to match the correct
endpoint.

# Additional Changes

# Additional Context
- Closes #11053

Co-authored-by: Marco A. <[email protected]>
2026-02-11 08:57:18 +00:00
5a2181bcec chore: rename IAM to instance (#11351)
# Which Problems Are Solved

In many cases IAM was used where instance was meant. 

# How the Problems Are Solved

- Rename wrong usage of IAM to instance all over the code base.
- Add an agents.md which explains the terms used in Zitadel's
architecture.

# Additional Context

- closes #11280

---------

Co-authored-by: Copilot <[email protected]>
Co-authored-by: muhlemmer <[email protected]>
Co-authored-by: Copilot <[email protected]>
Co-authored-by: Marco A. <[email protected]>
2026-02-10 20:25:39 +01:00
Liam NevilleandGitHub 67a02a6aeb feat(login): add support for disabling automatic email verification (#11515)
# Which Problems Are Solved

Enterprise email link scanners (used by organizations for security)
pre-fetch URLs in emails to check for malicious content. In Login V2,
email verification codes are automatically redeemed when the
verification page loads. This causes link scanners to inadvertently
consume one-time verification codes before users can click them,
resulting in "code already used" errors.

# How the Problems Are Solved

- Changed the default behaviour so that verification codes are **not**
auto-submitted on page load — users must click a Submit button to redeem
the code
- Added a new environment variable `NEXT_PUBLIC_AUTO_SUBMIT_CODE` that
can be set to `true` to re-enable auto-submission for instances where
link scanners are not a concern
- The env var is named generically so it can be extended to other
auto-submit flows (e.g. Email OTP) in the future

# Additional Changes

None

# Additional Context

Closes #11406
2026-02-10 17:14:27 +01:00
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 <[email protected]>
2026-02-06 09:46:24 +00:00
Livio SpringandGitHub 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
b99271755b chore: renamed one time password to OTP (#11447)
# Which Problems Are Solved

naming consistency for TOTP/OTP

# How the Problems Are Solved


# Additional Changes


# Additional Context

- Closes #11299 
- Closes #11302 
- Closes #11304

---------

Co-authored-by: Marco A. <[email protected]>
Co-authored-by: Florian Forster <[email protected]>
Co-authored-by: Copilot <[email protected]>
2026-02-04 10:20:47 +01:00
Wim Van LaerandGitHub 61d4060df3 chore: naming inconsistency application (#11431) 2026-02-04 07:20:07 +00:00
09cf7cf0ae feat(oauth/oidc): move token exchange from beta to GA (#11475)
# Which Problems Are Solved

This PR marks the OAuth2 Token Exchange 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 interation
  - removed the toggle usage for token exchange
  - removed related translations
- Management Console:
  - removed the feature toggle and its translations
- Docs:
  - removed all beta labels and feature toggle notes

# Additional Changes

- removed unused `Errors.WebKey.FeatureDisabled` translations

# Additional Context

- closes #11114

---------

Co-authored-by: Marco A. <[email protected]>
2026-02-02 09:47:56 +00:00
d8ea041c56 docs: introduce versioned docs and migrate to fuma (#11166)
## Todos for release

- [x] Configure Env in docs project on vercel
- [x] Configure Root Path in the docs project on vercel
- [ ] Remove old CSP https://github.com/zitadel/website/pull/1592

## What we did

This pull request migrates the project documentation from the old
`docs/` directory to the new `apps/docs/` directory, introduces a new
documentation system built with Next.js and Fumadocs, and updates all
relevant references, configuration files, and documentation to reflect
this change. It also adds new configuration and ignore files for the new
documentation app, updates CI and linting to exclude the new docs from
certain checks, and revises the contributing guidelines accordingly.

**Documentation System Migration and New Docs App**

* Migrated all documentation from `docs/` to `apps/docs/`, and updated
all references in `README.md`, `CONTRIBUTING.md`, and other files to
point to the new location.
[[1]](diffhunk://#diff-eca12c0a30e25b4b46522ebf89465a03ba72a03f540796c979137931d8f92055L585-L640)
[[2]](diffhunk://#diff-eca12c0a30e25b4b46522ebf89465a03ba72a03f540796c979137931d8f92055L660-R607)
[[3]](diffhunk://#diff-eca12c0a30e25b4b46522ebf89465a03ba72a03f540796c979137931d8f92055L740-R687)
[[4]](diffhunk://#diff-b335630551682c19a781afebcf4d07bf978fb1f8ac04c6bf87428ed5106870f5L2-R3)
[[5]](diffhunk://#diff-b335630551682c19a781afebcf4d07bf978fb1f8ac04c6bf87428ed5106870f5L30-R30)
* Added a new Next.js/Fumadocs-based documentation app under
`apps/docs/`, including core app files, layouts, routing, search API,
and a comprehensive `README.md` with development and contribution
instructions.
[[1]](diffhunk://#diff-5a1b07344a2c1b4d3f37b23ff1388b62cd9f57dea3c1cd23d8a0412b7602b132R1-R74)
[[2]](diffhunk://#diff-462b9ad1eabbb7d1c29bb9c36e4931eb180fd7190900e6d2babf8f4d66ad1c28R1-R39)
[[3]](diffhunk://#diff-e16ae25660ded787b10ac35dea96d5ecaacf895dae0afc8a9bd4382dc79a8c87R1-R7)
[apps/docs/app/[[...slug]]/layout.tsxR1-R81](diffhunk://#diff-59e08acde4e805b7aeccef1dcf98f1d71dfc550777e6b9402085cee0e9fa4e0aR1-R81),
[apps/docs/app/[[...slug]]/page.tsxR1-R79](diffhunk://#diff-e5df3f80d0fa01e12d63d81f29c57a9d14e846c78fd3beabb2ef768e38fd9580R1-R79),
[[4]](diffhunk://#diff-389b34918e040cacaa87cd7201ffa462cc2b0b716736f537e3d3c660ac69353bR1-R7)
[[5]](diffhunk://#diff-d3b03416d1c457b19f1c27b26f2db741412df841b875c26ccadc36e4522247f4R1-R29)
[[6]](diffhunk://#diff-c8fb8339570a5305809be7c618e14705fd86390dc278e6ce8ba224a7bc8b0c3cR1-R25)

**Configuration and Tooling Updates**

* Updated `.github/workflows/codeql.yml`, `.golangci.yaml`, and
`.github/dependabot.yml` to properly handle the new docs app: excluded
`apps/docs` from certain checks, added npm dependency updates for the
docs app, and excluded generated content.
[[1]](diffhunk://#diff-12783128521e452af0cfac94b99b8d250413c516ec71fe6d97dbea666ff7ba27L8-R14)
[[2]](diffhunk://#diff-9917ddc9f1c3304218f7269265b746d997c5c0615478177b5fceecd33ef47cb5R5-R6)
[[3]](diffhunk://#diff-9917ddc9f1c3304218f7269265b746d997c5c0615478177b5fceecd33ef47cb5R126-R129)
[[4]](diffhunk://#diff-dd4fbda47e51f1e35defb9275a9cd9c212ecde0b870cba89ddaaae65c5f3cd28R89-R106)
* Updated `.devcontainer/devcontainer.json` to use the latest Go 1.25.3
version for consistency.

**Licensing and Miscellaneous**

* Added `apps/docs/` to the list of licensed directories in
`LICENSING.md`.

These changes ensure the documentation is now maintained in a modern,
scalable system and all project tooling is updated to support the new
structure.

---------

Co-authored-by: Federico Coppede <[email protected]>
2026-01-29 08:38:36 -08:00