Release notes
What changed in each version of the zitadel CLI, newest first, from its changelog. --version prints the version you run: npx @zitadel/cli@latest --version.
0.3.0
Minor changes
-
zitadel policies testruns the cases in.zitadel/policies/<operation>.test.jsonagainst your policy. Each case gives the operation's input (for the password policy, the password itself), optionally the facts the operation would look up (self_service,history_matches), and the decision it expects:allowed, and optionally the errorcodeand exactly theviolations. Zitadel evaluates every case as a real password set would, so a case also covers how the password is counted. By default the cases run against the policy file beside them;--release <id>runs them against the policy a built release pins, before you deploy it. A failing case fails the command withE_VALIDATIONand lists what was expected and what Zitadel decided.deploynever carries test files, editors validate them against the new.zitadel/meta/policy-test.json, and the policies README thatsetupwrites shows an example. (#2353) -
The usage analytics events the CLI sends to Vercel Web Analytics now carry one property of the command's own for
setup(framework),claim(claim_outcome),doctor(failed_checks),api(operation) andstart(runtime). On a failed run it takes the place ofnon_interactive. The kind of data sent does not change: fixed names only, never a path, id or message. (#2428) -
zitadel deploy --dry-run --jsonnow reports what the deploy would change, so a script or an agent can review it before deploying:data.difflists each changed file under.zitadel/with its status and its part of the diff the terminal shows, against the release your primary origins serve.zitadel deploy --jsonreports the same diff for what it deployed. (#2288) -
POST /users/querycan find the users still on an older revision of a schema. The newschema_object_typefilter field matches the object type every revision of a schema shares, withequals,not_equalsandcontains, andschemanow acceptsnot_equals, soschema_object_typeequalsa type together withschemanot_equalsits newest revision lists the users left to migrate.zitadel users list --filteracceptsschema_object_type. (#2129) -
setupnow commits the project it scaffolds, initialising a repository first when there is none, so a release records the commit it was built from and a laterdeploycan show a real diff. Because the commit covers the project directory, setup refuses a repository that already has uncommitted work, untracked files included; only the.zitadel/local/and.gitignorethatstartleaves are exempt. It also points at thedeploythat pins.zitadel/into a release.--no-gitopts out of the commit. (#2321)
Patch changes
-
Commands that
zitadel setup,start,doctorandenv addsuggest after a--cwdrun now work when pasted into the same shell:--env-filepaths, the install and dev-server commands,setup --server localandconsolename the project directory. A relative--env-fileis now read from the current directory, where Node itself reads it, instead of from--cwd. (#2416) -
zitadel doctornow says when it checks a local server started in a parent folder, in the runtime check and when that server is unhealthy. The unhealthy message names the parent, says thatreset --forcedeletes the parent's local data, and suggests a plainstartto give the current folder a server of its own. (#2424) -
zitadel setup --cwd <dir>creates<dir>when it does not exist yet, instead of failing with a rawENOENTerror, and removes it again if setup does not finish. A--cwdthat is a file now fails with a clear message. (#2416) -
Updated dependencies:
@zitadel/server@0.3.0,@zitadel/config@0.3.0,@zitadel/api@0.3.0.
0.2.0
Minor changes
-
The CLI's usage analytics events now carry
surface: cli, which names the Zitadel component that sent them, so they can be read beside the events of other Zitadel components. Nothing else they carry changes. (#2292) -
zitadel claim --no-waitprints the claim link and exits, withclaim_url,challenge_idandexpires_atunder--json, so a script or coding agent can hand the link to a person;zitadel claim --challenge-id <id>then waits for that link to be used and records the claim. (#2402) -
Running
zitadelalone now lists the commands under six headings named for what you are doing, one row per family such asusersorrelease.zitadel users --helplists its operations, every command's help ends by pointing at the overview, andzitadel commandsstill prints the flat list. (#2348) -
zitadel auth-method sso enable --provider customnow asks at a terminal for the connection's name, its sign-in button's name, the issuer URL and the scopes you leave out, instead of failing with a list of flags; scripts still need the flags (docs). A new custom connection also prints its claim mappings and the file to edit them in. (#2367) -
The CLI's onboarding output now says what to do next:
zitadel startpoints tozitadel setupinstead of a console link,setupspells out every choice and shows the sign-in configuration before writing it, andauth-method sso enablesays todeploythe provider, then test sign-in. The note about Zitadel's agent skills shows once per machine. (#2323)Breaking:
zitadel start --jsonno longer hasdata.console, and itsnext_actionsandnext_commandsno longer mention the console; a script that reads them gets nothing there. Runzitadel console --jsonfordata.sign_in_url. -
zitadel origin add <pattern> --kind primary|preview,origin listandorigin rmmanage the origins a project serves, with the project secret (docs). A primary origin is an exact URL, and a preview wildcard on a shared host needs a literal label of your own. (#1650) -
zitadel deploynow carries operation policies from.zitadel/policies/, one file per operation, checked against the new.zitadel/meta/policy.jsonschema before anything is sent;zitadel policies listandpolicies getread them back (docs). Sign-ins and sign-ups use the policy the deployed release carries, so deploying without a file switches that policy off. (#2270) -
Preview a branch's release on its pull-request URLs with
zitadel preview. Run inside a preview build, it reads the preview URLs from the hosting platform (Vercel or Netlify; pass Cloudflare Pages URLs with--origin) or from--origin, builds the same releasedeploydoes, validates it without touching your live sign-in providers, and makes it live on those URLs for a time-to-live (--ttl, between 60s and 365d; omitted, the project's default applies). Its last line isNEXT_PUBLIC_ZITADEL_RELEASE=sha256:...for the build to pin. Outside a preview or development build it does nothing — even with an explicit--origin— because production is deployed byzitadel deployafter merge, and--origin primaryor a primary origin's own URL is refused. On a CI platform other than Vercel or Netlify, setZITADEL_ENV=previewin the pull-request build: a CI build that names no environment is skipped too. (#1652)zitadel preview listshows the project's live previews, andzitadel preview rm <url>ends one early. All three authenticate withZITADEL_PROJECT_SECRETfor now, because the server does not accept the narrower preview token yet (#1575); they never fall back to the other credential.preview listandpreview rmfail, naming the variable, when it is not set;zitadel previewinstead warns and still exits successfully unless--strictis passed. -
zitadel pullrebuilds.zitadel/from what a release pins — the live release by default, a release id (rel_…) or a deployment id (dep_…) — so the nextdeploybuilds the same release (docs). It restores schemas and flow definitions, and warns for branding and identity provider connections, which it cannot read back yet. (#1407) -
zitadel startno longer reports a login URL, because the server no longer serves a login page: sign in through the login pagezitadel setupadds to your app. (#2307)Breaking:
zitadel start --jsonno longer hasdata.urls.login; a script that reads it gets nothing there. -
The CLI now sends its anonymous usage events to Vercel Web Analytics on the Zitadel cloud (
preview.zitadel.com), with at most eight of their properties, and sends nothing to Mixpanel for now. The existing opt-outs (--no-telemetry,DO_NOT_TRACK=1,ZITADEL_TELEMETRY=0) turn them off, and runs in CI send nothing. (#2253) -
zitadel logsnow shows the local server's warnings in yellow and errors in red, and--level warnshows only those, so the reason a sign-in failed is easy to find.zitadel startandzitadel doctorsay to keepzitadel logs --followopen, andzitadel logsworks from an app inside the directoryzitadel startran in. (#2245) -
zitadel deployreplacesplanandapply: it builds.zitadel/, identity provider connections included, into one immutable release, shows its git diff against the live one, and makes it live on the project's primary origins (guide). Newreleaseanddeploymentcommands, and their@zitadel/apioperations, create, list, deploy, roll back and revoke releases directly; a rollback lists the revisions it restores. (#1651)Breaking:
planandapplyare removed. Usedeploy --dry-runwhere you ranplan, anddeploywhere you ranapply. The generatedreleases listandreleases getare nowrelease listandrelease get, besiderelease createandrelease revoke. Add the origins your project serves withorigin addbefore the first deploy —deployneeds at least one primary origin. A server refusal because of the state it holds (an HTTP 409) now fails every command withE_CONFLICT(exit 5) rather thanE_VALIDATION(exit 3), and a target the project does not hold as an origin fails withE_VALIDATIONrather thanE_AUTH.Upgrading a project set up with 0.1.0: its app URL is registered as a preview origin only, so its first
deployfails with "The project has no primary origin to deploy to". Runzitadel doctor --fixonce to make that URL primary, aszitadel setupnow does;zitadel doctorwarns about such a project, anddeploysuggests the fix. To do it by hand, find the URL withzitadel origin list, remove it withzitadel origin rm <url>, add it back withzitadel origin add <url> --kind primary, then deploy. Adding it again without removing it fails, because the URL is already stored as a preview origin. -
Commands that talk to a project now resolve its server, project and credential per environment, so one checkout can serve development, staging and production;
zitadel envshows what resolved and from where,env listlists the environments andenv addbinds one (docs). Values come from flags, the process environment,.env.<name>.local,.env.local,.env.<name>and.env; onlydevelopmentreads.env.local. (#1649)Breaking: the server no longer comes from
ZITADEL_API_BASE, theserverfield ofzitadel.json, or a cloud default. SetZITADEL_URLin an env file, or pass--server; onlysetupstill defaults to Zitadel Cloud.zitadel.jsonno longer gets anenvironmentsblock,environments listandenvironments getare gone, and-enow means--env. -
Variables now belong to the project, under a new
variablegroup, and hold a primary value, which primary deployments freeze, and a preview value, which previews freeze, with no fallback between them (docs). A plainvariable setwrites both, a secret only the primary, and--previewworks with the preview value alone;variable resolveshows what a live deployment runs.Breaking: the
variablescommands and the--project-levelflag are removed. Drop--project-level, and usevariable set NAME,variable get NAME,variable list,variable rm NAMEin place of the oldvariables set/get/list/delete. -
zitadel setupnow writes and publishes a password policy for every new project,.zitadel/policies/auth.factor.password.set.json, with a minimum of 15 characters, NIST's minimum without a second factor, and a README on its settings. The wizard offers 15, 8 or none;--password-min-length <n>sets another,0none. Change it in the file and publish it withzitadel deploy. (#2279)Breaking: a scripted
zitadel setupnow publishes a 15-character minimum. If your scripts or tests register users with passwords shorter than 15 characters, use longer ones, or pass--password-min-lengthwith the length you need. -
The
src/hooks.server.tsthatzitadel setup --framework sveltekitwrites now readsZITADEL_URL,ZITADEL_PROJECT_SECRETandZITADEL_PROJECT_IDfrom SvelteKit's$app/env/private, declared insrc/env.ts, sonpm run checkpasses without@types/node, and a missing setting fails with an error naming it instead of proxying to a default server. (#2383)Breaking: setup refuses SvelteKit 2 apps, which have no
$app/env/private, andzitadel doctorandzitadel ejectno longer recognize a SvelteKit 2 app set up by an earlier release.@zitadel/sdk-sveltekitnow peers on@sveltejs/kit3 only. Upgrade to SvelteKit 3 first.
Patch changes
-
The
AGENTS.mdsectionzitadel setupwrites now says the generated pages and request boundary are the app's to change, aszitadel doctortreats them, and points at the documented--zl-*tokens and::part()names for theming; the CLI's agent skill says to capture output outside the app directory. (#2289) -
Every question the CLI asks now reads as a plain question, such as "What is the client ID of your Google OAuth app?" instead of "Client ID", and the client secret question says which project variable will hold it. (#2366)
-
zitadel doctorno longer checks a local Zitadel runtime for a project set up against a hosted server, so another app on port 8080 no longer fails it withE_PORT_IN_USE. (#2368) -
The
AGENTS.mdsectionzitadel setupwrites now says a page heading shown in place of the login's own has to follow itszitadel-flow-stepevent, since the login moves between sign-in and sign-up by itself, instead of suggesting a fixed heading that read "Sign in" during a sign-up. (#2368) -
zitadel setupcreates React, Vue, Solid, Svelte and Qwik apps again: it pinscreate-vite9.3.1, since 9.3.2 was published without its templates and setup then failed with "Could not detect a supported app framework". (#2386) -
When the server has no endpoint for a command, the CLI now says that the server and the CLI are different versions instead of reporting a missing resource, and still tells apart a
--serverthat is not a Zitadel API. (#2299) -
zitadel setupasks fewer, clearer questions: your app's port only when that port is taken, and the server with the local one listed even when stopped. An empty client ID skips that sign-in provider and prints thezitadel auth-method sso enablecommand that adds it later, and Ctrl-C at any prompt exits with code 130 and no error. (#2371) -
Commands the CLI suggests running next now keep the run's
--env,--env-fileor--server, so following one targets the same project instead of the default one. (#1649) -
zitadel startnow waits until the local server's database answers, not only its process, andzitadel doctor,zitadel statusandzitadel setupcount a local server as healthy only then, asking its/readyzinstead of/healthz. (#2308) -
zitadel setup --use-case consumerand--use-case businessnow make a user's given and family name their display name (x-display), so a signed-in session reads "Ada Lovelace" instead of only the email, ingetSession()and<zitadel-session>. (#2368)Upgrading a project set up earlier: add
"x-display": ["givenName", "familyName"]to its user schema and runzitadel deploy; users who sign up after that show their name. -
zitadel doctornow warns when the editor schemas in.zitadel/meta/are missing or outdated, which leaves a file's$schemapointing at nothing or at old rules, anddoctor --fixwrites the current ones;branding ejectwrites.zitadel/meta/branding.jsonthe same way. (#2326) -
<zitadel-login>now says why it switched between sign-in and sign-up by itself: an email without an account becomes a sign-up, with a note to fix a typo in the email field, and one with an account a sign-in; an identity provider sign-in without an account gets the same note. Flow API steps carrypurposeand, after a switch,notice. (#2373)Upgrading a page or design from an earlier release: a page that shows its own heading in place of the widget's should follow
step.purposefrom thezitadel-flow-stepevent, not step names, and a design ejected earlier shows the notice once its template rendersmessages, as the designszitadel branding ejectcopies now do. -
Adding a route to
protectedRoutesalone now requires a sign-in on it:zitadel setupwrites a Next.jsproxy.ts(middleware.tson Next.js 15) whosematchercovers every route but Next.js's build files; before, a route missing from thematcherstayed public without a warning. (#2372)Upgrading an app set up earlier: it keeps its
matcher; replace it with["/((?!_next/static|_next/image|favicon.ico).*)"], or add each protected route to it. -
Updated dependencies:
@zitadel/server@0.2.0,@zitadel/api@0.2.0,@zitadel/config@0.2.0.
0.1.0
Minor changes
-
Add
zitadel api, which calls any operation of the Zitadel API with your project's server and secret, including the ones no command covers yet. Name an operation by itsoperationId(zitadel api queryUsers) or its path (zitadel api /users/query), set query parameters and body fields with-F name=value, typed by the operation's schema, pass a whole body with--input, follow every page with--paginate, and preview the request as cURL with--dry-run.zitadel api lslists the operations, and--jsonreturns the response body asdata.@zitadel/apiexports the operations as data from@zitadel/api/generated/operations: each one's method, path, parameters, authentication and paging. (#2187) -
Releases now publish to npm's
latesttag as0.xpreview versions, starting at0.1.0, instead of1.0.0-alpha.Nunderalpha: run the CLI asnpx @zitadel/cli@latestand install the SDKs with@latest. The1.0.0-alpha.Nversions are deprecated on npm. Until 1.0.0, a breaking change ships in a minor version, and its release note marks it Breaking:.zitadel setuppins the@zitadel/*packages it adds, and the commands it suggests, to the CLI's exact version on every release, andzitadel start --runtime dockerruns the server image of that version. (#2215)
Patch changes
-
zitadel setupnow patches an existing SvelteKit, SolidStart, Qwik City or TanStack Start app instead of failing on it. The four patchers wrote the app shell and landing page (SvelteKit's+layout.svelteand+page.svelte, the other three'sroutes/index.tsx) on every run, so setup stopped withE_CONFLICTon any app that already had them and--forceoverwrote the user's own root layout and homepage. Like the Next and Nuxt patchers, they now write those files only when setup created the app skeleton itself and report them as conditionally scaffolded, so a pre-existing app keeps its shell and landing page while still getting the auth boundary and the login, register and profile routes. (#2190) -
Add
@zitadel/sdk-next/components, a"use client"entry that re-exports the ReactZitadelLogin,ZitadelLogoutandZitadelSessionwrappers, soprojectandlocalesreach the element as properties after server-rendered hydration on React 19 and on React 18, where the raw elements lose them or write[object Object]attributes. The Next.js pageszitadel setupscaffolds now render the wrappers. (#2177) -
Updated dependencies:
@zitadel/api@0.1.0,@zitadel/server@0.1.0,@zitadel/config@0.1.0.
1.0.0-alpha.28
Minor changes
-
zitadel setup --dev-port <port>now moves an app that's already set up to another port instead of skipping it. It allows the new origin on the server, then updates the dev script or dev-server config,ZITADEL_ISSUERin.env.localandzitadel.jsontogether, so an app whose port is taken (EADDRINUSE) signs in on the new port after one command. The README and AGENTS.md sections setup writes name this command. (#2157) -
The dev proxies
zitadel setupwrites for React, Vue, Angular, Solid, Svelte and Qwik apps now keep the app's session in a cookie named after its project,__nextgen_session_<project id>, as the server SDKs do. Onlocalhost, signing in to such an app no longer signs you out of the Console ofzitadel start, and signing in to the Console no longer signs you out of the app. The project comes fromZITADEL_PROJECT_IDin.env.local; without it the proxy passes cookies through as before. (#2071)For an app set up with an earlier CLI, run
zitadel doctor --fix: it adds this to the dev proxy the CLI wrote, andzitadel doctorwarns until then. (Runningzitadel setupagain leaves an app that is already set up alone.) A dev proxy changed by hand since is left as it is, andzitadel doctorsays the hooks must be added by hand. A dev session from before the change is not carried over, so sign in to the app once more.
Patch changes
-
Built-in schemas are now named under
https://zitadel.com/schemas/v1, for examplehttps://zitadel.com/schemas/v1/user-schema.json, instead ofhttps://nextgen.com/api/schemas, a domain Zitadel does not own. The base is an identifier, not an address: the server never fetches a URL in either namespace. Existing projects need no change: a built-in schema's id resolves under either base everywhere the API reads one, includingGET /schemas/{id}, a flow definition'suser_schema, a release's schema pointer, the users list'sschemafilter, and bootstrap user documents. A server still configured with the previous base accepts the new ids too. Projects created byzitadel setupfrom now on reference the new URLs, so pair this CLI with a server from this release or later. (#2056) -
zitadel setupwithout--server, and the setup wizard's Zitadel Cloud choice, now target the hosted preview athttps://preview.zitadel.cominstead ofhttps://api.zitadel.cloud. A project whosezitadel.jsonnames a server keeps using that server. (#2056) -
A server
zitadel startruns can now sign users in through an identity provider on this machine, such as a test provider onhttp://localhost:9400.startpassesNEXTGEN_HTTPCLIENT_ALLOW_LIST=localhost,127.0.0.0/8,::1/128to the server unless.env.local,.envor the shell sets it; set it empty to turn it off.auth-method sso enableno longer warns about a localhost issuer on such a server, and on a--runtime dockerserver it warns that the container cannot reach this machine's localhost. (#2144) -
zitadel planshows an edit inside a list as the lines that changed. A flow's steps and a step's actions are matched byname, so adding a field to one step prints that one line under the step, with the untouched steps summarized as hidden, instead of the wholestepslist removed and added again. Lists of values such asfieldsmark the items added or removed. (#2153)zitadel applyandzitadel setupno longer rewrite your.zitadel/files with what the server stored: after applying a connection,.zitadel/idps/*.jsonno longer gainspkce_enabled,token_endpoint_auth_methodandid_token_mapping. A field you leave out reads as the server's default, soplanreports no change after anapply. The one fileapplystill rewrites, and lists infiles_updated, is a flow whoseuser_schemapins a schema revision id the apply superseded. -
A Next.js app created by
zitadel setup --framework nextno longer logsCould not validate `instant`errors innext devon every request to/. The home page setup writes still redirects to/login, and now opts out of Next.js's instant-navigation check, which Cache Components (on by default in new Next.js apps) runs on every page. The@zitadel/sdk-nextREADME'sauth()examples now render the component that callsauth()inside<Suspense>and import it from@zitadel/sdk-next/server, so they build with Cache Components on. (#2145) -
The
/login,/registerand/profilepages thatzitadel setupwrites for a new Next.js app paint the sign-in page's dark background on the first paint, so they no longer show a white page before the sign-in form loads. (#2148) -
Editors no longer flag
zitadel.jsonfor an unreachable schema.zitadel setupnow writes the schema ofzitadel.jsonto.zitadel/meta/project.json, next to the flow and user-schema dialects, and points the file's$schemaat it, so editors validate the project configuration offline instead of trying to loadhttps://schemas.zitadel.com/v2/project.schema.json, which never existed. In a project set up earlier, delete the$schemaline fromzitadel.jsonto silence the warning; only editors read it. (#2151) -
The Qwik meta-framework SDK is
@zitadel/sdk-qwik-router, named after Qwik Router, which Qwik 2 renamed from Qwik City; it was never published as@zitadel/sdk-qwik-city. The CLI calls the frameworkqwik-routertoo (zitadel setup --framework qwik-router), and its docs page moved to/docs/sdks/qwik-router. (#2077) -
The server SDKs now count a session as signed in only when it belongs to the app's own project. A session of another project on the same server, such as a second app on the same host or the console of a local
zitadel start, reads as signed out inprotectedRoutes,auth()andgetAuth(), and its cookie is left in place for the app it belongs to. (#2076)The project comes from the new
projectIdmiddleware option, elseZITADEL_PROJECT_ID, else the framework's public project id variable (NEXT_PUBLIC_,VITE_orPUBLIC_ZITADEL_PROJECT_ID).zitadel setupalready writes these to.env.local. A deployment that sets none of them must setZITADEL_PROJECT_ID, or nobody is signed in. With@zitadel/sdk-nuxt, the public project id (NUXT_PUBLIC_ZITADEL_PROJECT_ID) decides unlessnextgen.projectIdnames one, so a built server follows it at runtime. A JWT session token must name the project in itsaudclaim.auth()in@zitadel/sdk-nextalso takes the project ofconfigureZitadel(); withcreateProxy(config), pass the same project toauth()when it lives only in that handle.GET /sessions/metakes an optionalproject_idand answers a session of another project with403 sess.project_mismatch. The SDKs send it, and so dogetSession()in@zitadel/sdk-next,<zitadel-session>,<zitadel-logout>and the console.getSession()takes the project from itsprojectIdoption,configureZitadel()orNEXT_PUBLIC_ZITADEL_PROJECT_ID, and without one reads as signed out. Signing out with<zitadel-logout>or<zitadel-session>no longer ends another project's session. TheAGENTS.mdguidance thatzitadel setupwrites now tells agents to read the session this way.A Bearer token that is not this app's no longer decides what happens to the session cookie that came with it: the cookie is judged, and cleared only when the backend refuses it, on its own.
In
@zitadel/api,getMySession()now takes its query parameters first: callgetMySession(undefined, init)where you calledgetMySession(init). In@zitadel/sdk-core,checkOpaqueSessionToken()andvalidateOpaqueSessionToken()take the project id as a fourth argument, andresolveSession()is the one implementation of these rules behind every server SDK's middleware.The
@zitadel/sdk-nextmiddleware also no longer lets a session that has not finished signing in throughprotectedRoutes. -
Updated dependencies:
@zitadel/server@1.0.0-alpha.28,@zitadel/api@1.0.0-alpha.28,@zitadel/config@1.0.0-alpha.28.
1.0.0-alpha.27
Patch changes
-
zitadel setupnow creates new Next.js apps on Next.js 16.4.2 instead of 16.2.4. Next.js 16.2.4 has known advisories, among them a critical remote code execution and several ways around Middleware / Proxy, the layernextgenMiddlewareprotects routes in. An app created by an earlierzitadel setupkeeps the version it was created with: upgrade it withnpm install next@^16.4.2. (#2054) -
zitadel setupno longer leaves a half-set-up app behind in a directory it creates a new app in. It creates the app only once it has every answer, so cancelling a question leaves the directory as it was. When a later step fails, such as an unreachable server, a failed upload of the default schema, or Ctrl-C, setup removes the app again and puts back what the directory held. The next run then sets up a new app, with the/→/loginredirect, instead of treating the leftover skeleton as an app that existed before. In an empty directory,--dry-runno longer runs the framework's generator (such ascreate-next-app): it previews the project and writes nothing. (#2053) -
zitadel stopandzitadel resetno longer fail withspawn docker ENOENTon a machine without Docker. The default runtime does not need Docker, and where Docker is not installed there is no container to remove, so both now stop or reset the directory's local server as usual.zitadel startlikewise no longer fails when it moves a directory that used--runtime dockerback to the default runtime after Docker was uninstalled. (#2058) -
Updated dependencies:
@zitadel/server@1.0.0-alpha.27,@zitadel/api@1.0.0-alpha.27,@zitadel/config@1.0.0-alpha.27.
1.0.0-alpha.26
Minor changes
-
Add the
zitadel auth-methodcommands, one per sign-in method:auth-method password enable|disable,auth-method passkey enable|disable, andauth-method sso enable|disable --provider <name>.auth-method sso enabletakes--provider google, or--provider customwith--slug,--display-name,--issuerand optional--scopesfor any other OpenID Connect provider.zitadel auth-method sso disableis new: it removes a provider from the schema and the login flows, and keeps its connection and credentials; a custom provider is named by its slug, as in--provider acme-sso.zitadel sso enableis removed; runzitadel auth-method sso enableinstead, with the same flags. The commands change only local files, apart fromauth-method sso enable, which also stores the provider's credentials on the project;planandapplypublish the schema and flow changes. Before writing or publishing anything, they refuse: (#1492)- disabling a method that a login flow still asks for;
- changing a method while a flow has errors that prevent checking it;
- disabling the schema's last way to sign in, unless you confirm in a terminal or pass
--force(meant for a schema whose users are only managed through the API); - enabling password on a schema without
x-identifier; - editing a value that is not the shape these commands edit, or a schema that points at an external URL.
The console's empty identity-provider list now tells you to run
zitadel auth-method sso enable. -
Add SvelteKit, TanStack Start, SolidStart and Qwik City meta-framework SDKs, mirroring sdk-next and sdk-nuxt. Each ships a server integration that proxies
/__nextgen/*to the auth backend (attaching the project secret to thePOST /sessions/exchangehandoff only, and forwarding the identity-provider callback's redirect onto the app), verifies the session JWT via JWKS with the Web Crypto API, and redirects unauthenticated requests on protected routes, plus agetAuthhelper. The SvelteKit, SolidStart and Qwik City SDKs are server-only; their pages render the login, logout and session widgets with the matching framework component SDK (@zitadel/sdk-svelte,@zitadel/sdk-solid,@zitadel/sdk-qwik). The server glue uses each framework's native primitive: a SvelteKithandlehook (createNextgenHandle), a SolidStartonRequestmiddleware (createNextgenMiddleware), a Qwik CityRequestHandler(createNextgenOnRequest), and a TanStack Start request middleware (createNextgenRequestMiddleware, with the framework-agnostichandleNextgenRequestcore exported too). All JWT verification and proxy logic is shared from@zitadel/sdk-core. (#373)Like the Nuxt SDK, these SDKs sign in only a session that names a user. The backend sets the session cookie before a sign-in verifies any factor, so an anonymous session reads as signed out, and its cookie is kept so its sign-in can still complete. The signed-in session carries the user's
identifier,identifierPropertyanddisplay, and a dead session cookie is cleared on every unauthenticated response.protectedRoutesguards full page loads, while theZitadelSessioncard covers client-side navigation; each README shows how to guard a page on the server before it renders: a+page.server.tsload in SvelteKit, a preloaded server query in SolidStart, arouteLoader$in Qwik City and abeforeLoadserver function in TanStack Start.@zitadel/sdk-core/middlewareexportsvalidateOpaqueSessionToken, the backend session check these SDKs share, andrequiresProjectSecret, the rule that only thePOST /sessions/exchangehandoff carries the project secret.@zitadel/sdk-tanstack-startalso ships the widgets, so a TanStack Start app installs no other Zitadel package. Its/reactentry exports@zitadel/sdk-react'sZitadelLogin,ZitadelLogoutandZitadelSessioncomponents (every event callback,childrenslots and the signed-in session card),businessLocales, and every type@zitadel/sdk-reactexports, such asZitadelFlowCompleteDetailfor typing a callback. Because TanStack Start server-renders every route, the components render nothing on the server and during hydration and mount right after with every prop in place, so a widget never starts a flow with its defaults. The/cliententry exportsconfigureZitadel,getApiandgetZitadelConfig. The README'ssrc/start.tsexample registerscreateCsrfMiddlewareahead of auth, because adding that file turns off TanStack Start's automatic CSRF protection for server functions.sdk-sveltekit peers
@sveltejs/kit^2.0.0 || ^3.0.0andsvelte^5.0.0, the Svelte range@zitadel/sdk-sveltesupports. sdk-solid-start peerssolid-js^1.8.0, the range@zitadel/sdk-solidsupports.sdk-qwik-city targets Qwik 2: it peers
@qwik.dev/coreand@qwik.dev/router(^2.0.0-beta.45), like@zitadel/sdk-qwik, whose widgets its pages render. A Qwik 1 City app (@builder.io/qwik-city) must migrate to@qwik.dev/*first. It readsZITADEL_URLandZITADEL_PROJECT_SECRETper request from Qwik City'sev.env, which carries.env.local, falling back toprocess.env.The CLI now detects, scaffolds, and integrates all four meta-frameworks end to end. New detectors recognise SvelteKit (
@sveltejs/kit), SolidStart (@solidjs/start), Qwik City on Qwik 2 (@qwik.dev/router) and TanStack Start (@tanstack/react-start) and run before their base-SPA detectors so a meta-framework app is no longer mistaken for a bare SPA (the React detector now also excludes TanStack Start). Each gains a scaffolder and a patcher that wires the framework-native server entrypoint (src/hooks.server.ts;src/middleware.tsregistered via thesolidStart()plugin invite.config.ts;src/routes/plugin@nextgen.ts; and asrc/start.tsrequest middleware respectively), the landing/login/register/profile routes rendering the widgets from the framework component SDK (from@zitadel/sdk-tanstack-start/reactfor TanStack Start), the public project-id env var, and the matching@zitadel/sdk-*dependency. SvelteKit pages read the project id fromVITE_ZITADEL_PROJECT_ID, which setup seeds in.env.local. The generated/profilepage renders the "signed in as" session card with sign-out, andsetup --use-case businesswires thebusinessLocaleswork-email copy into the login and register pages. Setup pins the app'sdevscript to the port it registers as the project's origin, with--strictPort, and refuses a SolidStart 1 app up front with the reason.doctortreats the auth entrypoint, and SolidStart'svite.configmiddleware registration, as managed infrastructure and fails when either is missing. -
Add a custom OpenID Connect provider to the CLI.
zitadel sso enable --provider customtakes a connection name (--slug), a sign-in button name (--display-name), an--issuerand optional--scopes(openidrequired, defaulting toopenid profile email), and writes.zitadel/idps/<slug>.json, enables it on the user schema and adds it to every login flow, as it does for Google. The connection is identified by its slug rather than its issuer, so a Project can hold several connections to one issuer; a name already used for a connection to a different issuer is refused.zitadel setupoffers the same as "another OpenID Connect provider", with--sso customand--sso-slug,--sso-display-name,--sso-issuerand--sso-scopesfor scripted runs. The connection maps the standard OpenID Connect claims the schema defines and trustsemailonly when the provider reportsemail_verified; other mappings are made by editing the file. Adding a second provider afterapplyno longer reports the conflict and registration steps as hand-edited because the platform filled in their emptygatesandsso_providers.@zitadel/config/idpno longer exportsISSUER_REQUIREMENTorisSupportedIssuer; useparseIssuer, which returns the issuer or why it is refused.idpProviderandIDP_PROVIDERSnow cover only the catalog's own providers,IdpProvider.templateisstring | undefinedbecause a custom connection names no template, andselectionSlugis gone in favour ofproviderOf(selection).slug. (#1698) -
The resource commands now take every field of a body as a flag, nested ones included: a nested field's flag is its path joined with
-.zitadel idps createtakes--slug,--display-name,--oidc-issuer,--oidc-client-id,--oidc-client-secretand--oidc-scopesinstead of only a JSON document.zitadel grants createnames its principal with--user-identifieror--team-name, andzitadel projects updatesets the password hashing method with--password-hash-algorithmand--password-hash-params-*. A boolean takes--no-<flag>forfalse, and a list is a repeated flag.--set <path>=<value>(or<path>:=<json>) sets any field by its dot path, and=reads the value as the type the field has, so--set oidc.pkce_enabled=falseis a boolean.zitadel idps create --fileand--datatake the connection document itself, the same shape as.zitadel/idps/<slug>.json; a body still wrapped inidpis accepted too. (#1865)- A field flag over
--fileor--datanow changes only the key it names:--attributes givenName=Adaon top of a file no longer drops the file's other attributes. - A
${{ NAME }}variable reference is no longer refused as a credential, sozitadel idps create --dataworks. Booleans, enum values, declared numbers and endpoint URLs such astoken_endpointare no longer refused or redacted either. Free-form text in a field named like a credential is still refused, now also from the field flags and--set; write references in single quotes so the shell leaves${{alone. --dry-runshows those values instead of«redacted», so thebodyit prints can be saved and sent later with--file.zitadel resources --jsonlists every nested field with its dot path, flag, kind and whether it is required.
- A field flag over
-
setupnow pins every framework's Zitadel SDK at the CLI's own version through one rule, as it already did for Next.js, instead of the floatingalphatag. The app stays on the SDK that matches the files the CLI wrote, anddoctorwarns when the CLI and the pinned SDK drift apart. (#2009) -
zitadel planandzitadel applynow check the SSO configuration across files before anything is sent, and stop with one error that names the file, the steps and the name at fault, and what to change. It reports what the plan touches and the files those reference, so an older mistake in an unrelated file does not stop an edit. It catches a step that offers a provider its user schema does not list, or when SSO is off; a schema that lists a provider with no connection file; a step that offers providers but does not route every outcome (auser_already_existsroute that carries an action does not count); a claim mapping that fills nothing the schema defines; a mapping orverified_claimskey that names a property no schema has (a top-level name, not a dotted path); and averified_claimskey with noclaim_mappingrow. Two connection files with the same slug or the same project variable stopplanandapplytoo, as part of that check. A connection that cannot create users needs onlysso_authenticatedrouted and no claim mapping.@zitadel/config/validateis now one validation library:validateFlowDefinitionfor a single flow and its schema, and the newvalidateProjectfor the whole working tree, with every rule named once in a typed registry.FlowValidationRuleIdis nowFlowRuleId; the old name stays as a deprecated alias. (#1703)Connection files are held to the platform too, and these checks are not turned off by
ZITADEL_SKIP_FLOW_VALIDATION, which switches off flow validation and the cross-file check above. Aclient_idorclient_secretthat names a project variable must find it: a missing one fails withE_CREDENTIAL_MISSING, a client secret that is not a secret variable, a client id that is a secret one, or one variable for both is refused. Editing theslug,protocol,oidc.issuer, the OAuth2 endpoints orsubject_claimof a connection that was already applied (an absent OIDCsubject_claimreads assub) is refused with the field and file named.planstops when the project's variables or an edited connection's stored copy cannot be read, instead of skipping the checks.A removed connection file is reported apart from the changes, because the platform cannot delete it yet:
plan --jsonandapply --jsoncarryunsupported_deletions(a count, with the files inunsupported_deletion_filesonapply), the headline no longer says "no changes", andapplyno longer says everything is in sync. A renamed connection file is planned and reported as a move (moveinchanges, withmoved_from), shown as a diff against the stored connection. -
Project create/get/query now return typed
origins(pattern+kind) instead ofpreview_origins. NestedPOST /projects/{id}/originsand.../origins/removeare available (server returns 501 until the origins service ships). (#1677) -
Environments are gone.
GET /environments,GET /environments/{name}and theenvironment.createdevent are removed, the CLI dropszitadel environments listandzitadel environments get, and projects are created without the defaultdev,stagingandprodenvironments. Variables belong to the project alone:environment_nameon the variable operations answersenv.not_found. The deployment operations answer 501 until deployments target origins. (#1643) -
Show a one-time banner, at the top of
zitadelandzitadel setup, when AI coding agents (Claude Code, Cursor, Gemini CLI, and others) are detected, telling the developer to runnpx skills add zitadel/nextgen --full-depthto give them the Zitadel skills. The CLI installs nothing itself. The banner is skipped in CI, with--jsonor--non-interactive, and whenNO_SKILLS_PROMPTis set. (#1687)
Patch changes
-
The
zitadel-cliagent skill now points agents at the sign-out button that apps generated byzitadel setupactually render,zitadel-session-logoutinside<zitadel-session>, instead of<zitadel-logout>'s.signout-btn, which those apps don't render, and describestypography.font_url, which it said could not be set yet. It also says wherenext_commandssits in the JSON envelope, and tells agents to always pass@alpha, because barenpx @zitadel/cliruns an early0.0.0build. The@zitadel/componentsREADME now lists the sign-out hooks of<zitadel-session>and says how to reach the sign-out button of<zitadel-logout>, which has none. (#1730) -
setupnow checks the running Node against theengines.nodeof the framework CLI before it creates a new app, for every framework, with one shared check that each scaffolder configures. The range is asked of npm for the exact CLI that setup runs, gives up after 5 seconds and then uses a built-in range, so an unreachable registry no longer holdssetupup. For Angular it refuses a Node the Angular CLI would refuse, with a message that names the Node versions Angular needs (for Angular 22,^22.22.3 || ^24.15.0 || >=26.0.0, which the Angular CLI applies one major at a time, so only 22.x, 24.x and 26.x at or above those versions are accepted and Node 27 is refused), and only warns on Node 23 and 25, which the Angular CLI runs on with a warning; previously the refusal surfaced only as a generic "exited with status 3" error. Next.js, Nuxt, React, Vue, Solid, Svelte and Qwik never refuse, because their CLIs carry on or only warn on an unsupported Node, butsetupnow adds a warning to the envelope'swarningswhen the Node is outside the range. (#1927) -
setupmoves a dependency fromdevDependenciesintodependenciesinstead of listing it in both. A package the app already had indevDependencieswas added a second time, and some toolchains refuse a package listed twice. A runtime dependency is never demoted: when the app already lists a package independenciesand a dev add arrives, it stays where it is, andpeerDependenciesandoptionalDependenciesentries are left alone. (#1917) -
Every framework detector now declares its minimum version and one shared check enforces it, so
setupanddoctorrefuse Nuxt below 4, Vue below 3, Angular below 17, Solid below 1.6 and Qwik below 2.0.0-beta.45 (the first release the SDK accepts) up front, as they already did for Next.js, React and Svelte. The SDK peer ranges already refused these at install time; the CLI now says so at setup with an upgrade hint. Angular below 17 now fails withE_UNSUPPORTED_PROJECT_SHAPEinstead ofE_VALIDATION, and every framework is also judged by its installed version, not only Next.js. (#1954) -
- A Svelte app on Svelte 4 is refused up front with the reason, instead of failing later on an uninstallable Svelte 5 component library. (#373)
- The setup summary names every framework with its version ("Angular 20.1.0 · TypeScript · npm") instead of the raw id; Angular, Solid and Qwik apps showed no version at all.
-
zitadel setupno longer writes the server URL and the project id into a Nuxt app'snuxt.config.tsas fixed fallbacks, so the app followsZITADEL_URLandNUXT_PUBLIC_ZITADEL_PROJECT_IDin.env.local, as Next.js and Vite apps do. It also stops writing the unusedruntimeConfig.zitadelUrlandruntimeConfig.public.nextgenProxyPath. An app set up earlier keeps its config: removenextgen.urlandruntimeConfig.public.zitadelProjectIdfrom itsnuxt.config.tsto follow.env.localtoo. (#1759) -
The Nuxt scaffold no longer adds
@zitadel/api,@zitadel/componentsand@zitadel/design-tokenstobuild.transpileinnuxt.config.ts. The app depends only on@zitadel/sdk-nuxt, which owns those packages, and builds and renders the login page without the entries. Apps scaffolded earlier keep their entries, which are harmless, anddoctorandejectstill recognise their config as wired. (#2020) -
The Nuxt scaffold now depends only on
@zitadel/sdk-nuxt, never on@zitadel/components. Its pages import nothing from@zitadel/*, and the business copy overlay comes fromuseNuxtApp().$zitadelBusinessLocales, which the SDK module provides. Setup no longer writesplugins/zitadel-components.client.tsorplugins/auth.server.ts, because the module loads the widgets and seeds the auth state itself.ejectstill removes both from apps an older setup wrote, anddoctor --fixdrops their records from the scaffold manifest once they are deleted. This needs the SDK release that makes the module load the elements. (#2015) -
zitadel setupandzitadel sso enableshow each connection's own redirect URI,{issuer}/__nextgen/idp/{slug}/callback, for examplehttp://localhost:3000/__nextgen/idp/google/callback. The shared/__nextgen/idp/callbackthe previous release printed no longer works: rerunzitadel sso enable --provider googleto see the new URI, which it now prints for an existing connection too, and register it with the provider.setup --jsonreports it ascallback_urion eachssoentry, assso enable --jsonalready did. (#1633) -
zitadel setupandzitadel doctornow require Next.js 15.2.3 or later. Next.js 15.0.0 to 15.2.2 skip middleware for a request that sends anx-middleware-subrequestheader (CVE-2025-29927), so on those versions anyone could reach a page thatprotectedRoutesguards. A version that can only resolve below 15.2.3, such as15.2.2or~15.1.0, is refused withE_UNSUPPORTED_PROJECT_SHAPE, and so is an app whose installed Next.js is older than 15.2.3 even when its range, such as^15.0.0, allows a fixed release. Upgrade Next.js and reinstall, then rerun the command. (#1954) -
setupanddoctorrefuse a Svelte 4 app up front, saying it needs Svelte 5 or newer, instead of failing later on a Svelte 5 component library that cannot be installed. (#1913) -
zitadel idps create --helpnow suggests--file ./identity-provider-connection.json, an example you can run as written: the file name it showed before had spaces in it, so a shell split it into three arguments. The help forzitadel users create,users updateandusers deletenow says "a user" instead of "an user". (#1866) -
The setup summary names every framework with its version, such as "Angular 20.1.0 · TypeScript · npm". Angular, Solid and Qwik apps showed the raw framework id and no version. (#1910)
-
Every question the CLI asks now goes through one input function, so a missing value, a missing
--forceand a cancelled prompt read the same in every command. A command that cannot ask now says "No <value> supplied" with a hint naming the flag to pass or the stdin to pipe, a destructive command without--forcesays it cannot proceed without it, and Ctrl-C at any prompt reports "Cancelled by user" instead of naming the command. Cancelling the confirmation ofvariables deleteor a resourcedeletenow stops with that error instead of reporting a skipped delete, andejectnow asks for confirmation in a terminal when--forceis not given. (#1766) -
Offer the sign-in provider on the password step. Someone who signed up with a provider has no password, and typing their email instead of clicking the button sent them to a password step with only a password field and no way out.
zitadel sso enableandzitadel setupnow add the enabled providers to that step too, and rerunningsso enableadds them to an existing project's unedited password step. A password step edited by hand is left alone and reported, like the other generated steps. (#1701) -
setupanddoctornow refuse a Vue app below 3.2, matching the Vue SDK's peer range, so a project on Vue 3.0 or 3.1 gets the upgrade hint instead of an SDK that fails at runtime. (#1948) -
The
zitadel planwarning about a flow definition becoming the project default for<zitadel-login>now gives advice that keeps the current default: give a new flow definition anaudiencebefore its first apply and select it byflowName, or pinflow-nameon the pages that should keep the current project default. It used to suggest settingflow-nameon the pages that should use the new one, which leaves it the project default on every other page. (#1756) -
doctorrun in an app inside the directoryzitadel startran in now checks that local server — the one setup attached the app to — instead of the default port 8080. It read local runtime state from the current directory only, so it reported "No existing runtime metadata", counted the app's own server as another runtime, and failed withE_PORT_IN_USEwhenever anything else held port 8080. It now finds the nearest started server on the way up, like setup andconsole, and--portdefaults to that server's port. When that server is unhealthy, or its record is malformed, the suggestedstartandreset --forcenow carry--cwdfor the directory that holds it, instead of acting on the app directory. When that directory needs shell quoting, as a Windows path with a space does, the commands are left out and the exact arguments are indetails.suggested_args. (#1914) -
doctornow checks thatpackage.jsondeclares the SDK packages the app actually imports, such as@zitadel/sdk-next, or@zitadel/sdk-sveltekitplus@zitadel/sdk-svelte. Before, any@zitadel/*package passed, so an app that kept only the CLI as a dev dependency was reported healthy. The check names the missing package anddoctor --fixre-adds it. The setup docs and the CLI reference now list all twelve--frameworkids and the framework versions setup supports. (#2010) -
A flow definition names its user schema by
objectTypeand runs against the project's newest revision of it.POST /flow_definitionsstill accepts a schema id and stores theobjectTypeit names, so reading a definition back returns theobjectType. (#1683)The default login flows name
human-user, andgetDefaultLoginFlowno longer takesuserSchemaUrl.zitadel sso enablefinds a flow that names its schema byobjectType, and the console links a flow to the revision it runs against. -
Give each directory its own local server and port, for the binary and the Docker runtime alike.
zitadel startnow keeps the port a directory already had, and otherwise takes the first free port from 8080, so two projects no longer compete for 8080. It checks that a port is free by trying to use it, not by parsinglsof, and it refuses an explicit--portthat is taken instead of reporting another folder's server as its own.zitadel console,zitadel setupand the other commands only use the server named in the directory's own.zitadel/local/runtime.json, and no longer fall back to whatever answers on port 8080; when a directory has none, the error says to runzitadel startor to name a server with--server.zitadel startruns the server binary directly instead of through a wrapper, andzitadel stopnow also stops a server whose recorded process has already died, and says plainly when a directory has no server to stop.@zitadel/servernow exports@zitadel/server/binary, which finds the server binary for a platform, so the CLI and the wrapper share one list of platforms. (#1864) -
zitadel grants create --jsonfor a user named byuser.identifiernow includesdata, an empty object, in its JSON envelope. The server accepts that request with202 Acceptedand no body, and the envelope used to leavedataout. Runzitadel grants listto see whether a grant was written. (#1756) -
Identity provider connections are OIDC only. The
oauth2protocol is removed from the connection schema and the API, together with thesupplementary_fetchstrategy and the"$supplementary_fetch"value inverified_claims. The engine never served either, so such a connection showed a sign-in button that could not work. (#1809)Breaking for stored data: the server's next start deletes every
oauth2connection stored by an earlier release, with its revisions and identity links.POST /idpsnow refuses, at create and at revise, an OIDC connection the engine could not start a sign-in with: anoidcblock that sets an endpoint without the others fails with400 idp.endpoints_partial, and the error details list the missing endpoints. Set no endpoint and let discovery fill them, or setauthorization_endpoint,token_endpoint,jwks_uri, anduserinfo_endpoint(userinfo_endpointmay be left out whenid_token_mappingis on). Nothing is stored, and a revise leaves the current revision unchanged.zitadel planreports the same problem before anything is applied, andzitadel applynames the missing endpoints when the server refuses one. -
A Nuxt app set up by the CLI no longer logs two warnings in dev. The SDK plugin stops reading its private runtime config in the browser, which Nuxt reported as NUXT_E1003, and the scaffolded home page now has a template, so Vue stops warning that it has nothing to render. (#2029)
-
Update dependencies that have published security advisories. The server picks up the HTTP/2 and gRPC denial-of-service fixes, the OpenTelemetry exporter fixes (the log exporter's TLS settings, endpoint URLs in logs) and the
golang.org/x/cryptoSSH fixes.@zitadel/clinow requiresundici7.29.1 or later and@zitadel/componentsDOMPurify 3.4.16 or later, and the optional@faker-js/fakerpeer of@zitadel/apimoves to^10.5.0. (#1845) -
zitadel setupnow scaffolds a new app into the directoryzitadel startjust ran in. The local runtime and the.gitignoreline thatstartwrites no longer make the directory count as non-empty, sostartand thensetup --server localwork in a new directory without--force. (#1741) -
zitadel setupnow checks whether patching your app would overwrite one of your files, such as an existing app'ssrc/App.tsx, before it creates a project. It stops withE_CONFLICTwithout creating a project or writingzitadel.json, so you can move the file aside and rerun, or rerun with--forceto let setup replace it. Before, setup created the project and wrotezitadel.jsonfirst, and every rerun then reported the directory as already set up. (#1730) -
zitadel setupnow stops withE_CONFLICT, before it creates a project, in existing apps it used to leave broken while reporting success: an app that mounts its root component from a file other than the one setup writes, such assrc/App.jsxor Angular 17–19'ssrc/app/app.component.ts; a Next.js app whose ownmiddleware.tswould sit beside theproxy.tssetup writes for Next 16, which Next refuses to run; and a--frameworkother than the app's, where--forceused to scaffold a new app over it. The error's hint says what to change, since--forcecan't fix these. Setup also checks the schema, flow and connection files it writes to.zitadel/before it creates the project, so a conflict there no longer leaves a project behind whose id and secret a rerun kept in.env.local. (#1757) -
zitadel setupnow puts.env.localback the way it was before the run when it fails after creating the project, for example because the default user schema or login flow did not upload. Before, the file kept the failed run's project id and secret, and since setup only adds the keys.env.localis missing, even with--force, the app set up by a rerun still talked to that abandoned project, which has no user schema or login flow. (#1761) -
The sign-in picker in
zitadel setupno longer says thepassword-firstpreset offers a passkey during registration. Its login flow has no passkey step: users sign up and sign in with email and password. (#1730) -
React, Vue, Solid, Svelte, Qwik and Angular apps set up with
zitadel setup --server https://…can now sign in through their dev proxy. The generated/__nextgenproxy sent the app's ownlocalhosthost name on to the server, and a remote https server refuses that connection; the proxy now addresses the server by its own name and passes the app's origin on inX-Forwarded-HostandX-Forwarded-Proto. In an app set up with an earlier version,zitadel doctorflags the dev proxy andzitadel doctor --fixupdates it in place. (#1721) -
zitadel auth-method sso enableno longer reports thesso-conflictstep as edited by hand when it is what an earlier run wrote for different sign-in methods. Enabling a method such as passkey after the step was first generated now brings the step up to date with the methods enabled today; a step that was genuinely edited is still left alone and reported. (#1843) -
zitadel startnow restarts a local server that an earlier CLI release left running, instead of reusing it, and keeps its data. `zitadel setup --server (#1763) -
Variables take a
kindquery parameter,primaryorpreview, in place ofenvironment_name, and reads return each name's value per kind. A write withoutkindstores a plain value for both kinds, and a secret forprimaryonly, removing the name'spreviewvalue.GET /deployments/{deployment_id}/variablesreturns the values a deployment froze. Until the server stores kinds,kind=previewand the deployment read answer501. (#1739) -
zitadel variables set --helpno longer says that a reference to a whole field keeps a number variable a number. The server resolves${{ NAME }}references only in an OIDC identity provider connection'sclient_idandclient_secrettoday, and both take a string. (#1756) -
Updated dependencies:
@zitadel/server@1.0.0-alpha.26,@zitadel/api@1.0.0-alpha.26,@zitadel/config@1.0.0-alpha.26.
1.0.0-alpha.25
Minor changes
-
Distribute the CLI's agent guidance as an installable Agent Skill. Any skills-compatible coding agent — Claude Code, Cursor, Copilot, Codex, and more — can now add it with
npx skills add zitadel/nextgen --full-depth, giving the agent guided, always-current instructions for scaffolding and managing Zitadel auth with thezitadelCLI. The skill also ships inside the@zitadel/clipackage. (#1406) -
zitadel setupno longer limits onboarding to one social provider. The onboarding question is a multi-select rather than one choice, and declining is an empty selection rather than a "not now" option beside the providers. Each chosen provider is asked for its own client id and secret, gets its own connection file, and has its slug added to the schema and the login flow — which already carriedsso_providersas a list. The summary and the--jsonpayload report one entry per provider, so a publish that did not land names the provider it belongs to.--ssostill takes a single provider: a scripted run pipes one secret on stdin, andsso enableadds the rest. The catalog ships one provider today, so this changes what the CLI supports rather than what a developer can pick right now — a second catalog entry is what makes the multi-select visible. (#1366) -
Add social sign-in to the CLI.
zitadel sso enable --provider googleconfigures an identity provider on an existing Project: it says what to register with the vendor and at which redirect URI, takes the client id and secret, writes.zitadel/idps/<slug>.json, enablesssoon the user schema, and adds the provider button plus the steps and routes a provider round trip needs to every login flow that runs against that schema. The client secret is never a flag — it is prompted for, or piped in on stdin for a scripted run — and never written to disk: only its${{ NAME }}reference reaches the connection file, and the value is published to the Project as an encrypted variable, which is where the engine resolves it from.zitadel setupasks the same question during onboarding, so a Project can start with Google rather than adding it afterwards, with--ssoand--sso-client-idfor scripted runs. Everything so far is local: publishing the connection needs identity provider support on the server, which is not in this release, soapplycannot upload a connection file yet and deleting one is not supported either. Enable the provider once that support ships. (#1281) -
@zitadel/sdk-qwiknow targets Qwik 2: it declares a peer dependency on@qwik.dev/core(^2.0.0-beta.45) in place of@builder.io/qwik, so your app must be on Qwik 2 to use it. Qwik 2 runs on Vite 8. The widgets stay reactive to their props, mirroring the other framework SDKs. (#1358)The CLI's Qwik support follows suit.
zitadel setupnow scaffolds and detects Qwik 2 apps: a new app is created on@qwik.dev/coreand Vite 8 (no Vite 7 pin), and a Qwik 1 project is no longer auto-detected, since it cannot use the Qwik 2 SDK. -
setupnow decides where to scaffold with a plain rule instead of a maintained allowlist. A directory with a detected framework is integrated in place; an empty directory gets a fresh app; a non-empty directory with no framework stops and asks for--force.setup --forcescaffolds a fresh app into a non-empty directory, moving the existing files aside and restoring everything the scaffold does not create. This removes the treadmill of allowlisting each tool's metadata (.gitignore,.zitadel,.claude, …) and lets you install the agent skill into a project directory and then scaffold there with an explicit--force. (#1406) -
zitadel setupandzitadel sso enablewrite the renamed SSO outcomes into the login flow:sso_authenticatedandsso_user_not_found.user_already_existskeeps its name and is shared with the typed collision. On a flow an earlier version wrote,zitadel sso enablerenamescallbackandidentity_unknownto the new keys, so enabling another provider keeps the flow valid. If one of those old keys declarespurposeoraction, which the new keys refuse, the command stops with a validation error naming the step and the key, so you can remove them first. If a step the command edits already has an action named like an outcome it writes there (sso_authenticated,sso_user_not_foundoruser_already_exists), whether or not the flow still uses the old keys, the command stops with a validation error naming the step and the action, so you can rename that action first. (#1371)
Patch changes
-
Human output can now be turned colourless with
--no-color, and theNO_COLORandFORCE_COLORenvironment variables are honoured. This covers both the step logging and the highlighted commands, paths and ids, so piped and CI runs read cleanly;--colorforces colour on. (#1383) -
A platform request that gets no response is now reported reliably and can be cancelled. (#1456)
@zitadel/api: the client throws a typedNetworkError(reasonunreachableortimeout) when a request gets no response, alongsideApiErrorfor a failing status.createZitadelClienttakes an optionalsignalandtimeoutMs; an aborted request rejects with the signal's reason.@zitadel/cli: a refused, unresolvable or silent server reportsE_NETWORK(exit 4) on every command, the same as a server answering 5xx; before, a failure worded differently by the fetch runtime fell through toE_VALIDATION(exit 3). Each request has a 30-second deadline, and Ctrl-C while one is waiting cancels it with the newE_CANCELLED(exit 130) instead of leaving the command hung behind a spinner.
-
Every product command now accepts
-vas the short form of--verbose, matching the convention agents already expect from curl, ssh, and wget. The long--verboseis unchanged, and the root--versionkeeps no short form, so nothing collides. (#1383) -
Show CLI warnings in both terminal and JSON output.
--jsonresults fromclaim,start,sso enable,setup,planandapplynow include warnings that were only printed to the terminal before. (#1495) -
Make the local journey work when the app lives inside the directory
zitadel startran in, and say how to continue after setup. (#1429)zitadel setupandzitadel consolenow find the local admin in the working directory or the nearest parent that has one. When a local server has no local admin on that path, setup warns that the project is not attached to a team and that the local console will not list it, and says which directory to run from. The closing box from setup says to open a second terminal for further commands, and listszitadel consoleonce the project is owned by the local admin. The scaffolded README says the same about the second terminal.In the console, the project's Admins section tells an owner that they have admin access as a member of the project's owning team, and an empty list reads "No additional admins have been added."
-
State-changing requests authenticated by the Console session cookie are now protected against cross-site request forgery. A cross-site browser request is refused with
403 auth.csrf_invalid, and every such write except sign-out and thePOST …/queryreads must also send the session's token (from the newGET /sessions/me/csrf) in theX-Zitadel-CSRFheader; the code is listed in each affected operation's default error responses. Requests made with a project secret are unaffected. The generated client adds the header automatically oncesetApiCsrfTokenis set, and only to requests for the origin the token was issued for (the page's own by default). On a refusal it askssetApiCsrfRejectionHandlerfor a fresh token and retries once with it, so a write survives the session being renewed in another tab; an app returns no token when someone else is signed in now, and the write is then not retried.zitadel setupsends it when it claims a project for the local admin. (#1309) -
Updated dependencies:
@zitadel/api@1.0.0-alpha.25,@zitadel/config@1.0.0-alpha.25,@zitadel/server@1.0.0-alpha.25.
1.0.0-alpha.24
Minor changes
-
Breaking: remove
--environment(-e) fromplan,applyand the resource commands (users,teams,sessions,events,grants,idps,projects,schemas,environments,releases,flow-definitions,branding). It accepted onlydevelopment,previeworproduction— none of them a name the platform uses — and never reached the platform: its one effect was to read a server URL fromenvironments.<name>.serverinzitadel.json, a key nothing writes. Passing it now fails as an unknown flag; drop it from scripts.--server,ZITADEL_API_BASEand the top-levelserverinzitadel.jsonstill choose the server, andenvironments list/environments getstill read the platform's environments. (#1286)The CLI therefore has no per-environment flag at all until the platform's environments settle, which is also why the new
variablescommands address the project level only. Their own--environment/--envnever shipped. -
Setup no longer applies a login template, and the template catalog keeps only widget structure. (#1297)
zitadel setupdrops the "How should the login look?" question and the--designflag; passing--designnow fails withE_VALIDATIONand a hint pointing at app-side theming andbranding eject. It embeds the maintained login component (a starting page for a new app, a drop-in for an existing one), writes nothing under.zitadel/branding/, and publishes no branding revision. The JSON envelope dropsdata.design, the summary drops the "Login design" row, and the next actions point at theming the component from your app, withbranding ejectas the opt-in for owning its template.zitadel branding eject --designnow offerscentered(the default card) andminimal(the same form without card chrome).split,split-rightandheroare removed: they were page layout around the same card, which belongs in your application, and asking for one fails withE_VALIDATIONand a hint saying so.BRANDING_DESIGNSin@zitadel/configshrinks accordingly.- Revisions already published from
split,split-rightorherokeep rendering; the login still ships their chrome. The API reference forBranding.layoutno longer lists the retired designs.
Patch changes
-
The CLI documentation now explains the local admin
admin@zitadel.localhostthatzitadel startcreates by default, the one-time console sign-in link it prints, thezitadel consolecommand that prints a fresh link, and that on the default local runtimezitadel setupattaches the project to that admin's team, sozitadel claimreports it as already owned. (#1296) -
Encode path parameters in the generated API client, so any id the API accepts can be fetched. Schema ids are the case that hit today: a schema's id is its
$id, usually a URL such ashttps://nextgen.com/api/schemas/default-human-user.json. Sent raw, the//collapsed on a redirect and the request 404'd, sozitadel schemas get <id>failed for every URL id and the console's user list silently dropped its schema columns.zitadel schemas getalso stops guessing what an id looks like: it used to read anything withoutsch_or://as an object type, which sent ids likeurn:example:humandown the wrong path. It now asks the server, and falls back to the object-type lookup only on a 404. Setup reconciliation andapply/planschema fetches used to encode the id themselves to work around the same bug; that is gone, so the client encodes exactly once rather than sending%25for every delimiter. (#1288) -
Correct the
grantscommand reference. The README still documented--principal-typeand--principal-idongrants create, andprincipal_type/principal_idas filter fields ongrants list, which the user and team locators replaced:grants createtakes--relationas its only required field flag and names the principal through--data/--file, andgrants listfilters onuser_idandteam_id. (#1292)grants create --helpalso no longer suggestsgrants create --relation viewer. A grant needs exactly one ofuserorteam, which are nested objects no flag can carry, so that run was never a complete body. Any write command whose body needs a nested object or an array now offers only its--dataand--fileexamples. -
Updated dependencies:
@zitadel/server@1.0.0-alpha.24,@zitadel/config@1.0.0-alpha.24,@zitadel/api@1.0.0-alpha.24.
1.0.0-alpha.23
Minor changes
-
Add resource commands to the CLI.
users,teams,sessions,events,grants, andprojectseach gain the operations their API supports — users the full set, teams the same withdeactivatein place ofdelete, sessions list, get and revoke, grants list, get, create and delete, projects list, get and update, and events list and get.idpsgainslist,getandcreate, andschemas,environments,releases,flow-definitionsandbrandinggainlistandgetonly: they are authored as files and shipped through a release, so the CLI reads them and never writes them. Every list shares one--filter field=operation:valuegrammar regardless of whether the endpoint takes a query body or query parameters, with each field declaring the operations it accepts so an unsupported one is refused locally rather than as a server error. Cursor pagination,--fieldsto choose columns, per-field flags on writes, local schema validation,--dry-run, and a--forceguard on destructive verbs are the same everywhere.schemas listandflow-definitions listshow the current revision of each rather than the full history, with--filter revisions=allfor the history, andschemas getandflow-definitions getaccept the object type or flow name as well as a revision id.zitadel resourcesreports the whole surface in one call. (#1210)Breaking:
schemas listis now generated from the same registry as every other list. Its--jsonenvelope changes from{ object_type, revisions, count }to the standard{ items, count, next_page_token };--object-typebecomes an optional--filter object_type=<value>rather than a required flag; and the interactive revision picker is gone, sinceschemas get <id>reads one revision. A bareschemas liststill returns the complete revision history. -
A branding descriptor now points at its login template with
"liquid_template": { "$file": "./login.liquid" }instead of a separateliquid_template_filekey. The CLI replaces any$filereference with the file's content before publishing and writes the published value back into the file afterwards.branding ejectandsetup --designscaffold the new form, and a descriptor that still carriesliquid_template_filefailszitadel planwith a hint showing the replacement. (#1224) -
The project's env files now configure the local server. (#1187)
zitadel startreads.env.local, then.env, and hands everyNEXTGEN_*variable to the server it launches: through the process environment on the binary backend and as bare--env NAMEon Docker. No value ever reaches the command line,.zitadel/local/runtime.json, logs, or--jsonoutput; only the names are recorded, asenv.injected, andzitadel statusreports them. The address, data directory and public base the CLI sets itself always win. A running runtime is not updated in place, so after changing a value runzitadel stopandzitadel start.setupnow writes a comment at the top of the scaffolded.env.exampleand.env.localsaying so. -
Group the root help by purpose.
zitadel,zitadel --help, andzitadel helpnow list commands under "Project commands", "Local server commands", and "Configuration commands" in journey order, with oclif's own utilities under "Additional commands", followed by flags, examples, and where to learn more, in the layout ofgh --help. Nested commands such asschemas listandbranding ejectare listed inline; theuninstallalias andhelpare not. Per-command help and--jsonoutput are unchanged. (#1249) -
Identify the CLI in the
User-Agentof its HTTP requests — Zitadel API calls, local-server health checks and branding asset probes — so server logs and session records can tell CLI traffic and CLI versions apart. Requests used to carry Node's barenode; they now sendzitadel-cli/<version> node/<version> <os>/<release> arch/<arch>, followed byci/<provider>when running in CI andhost/<name>when running under a detected host such as Claude Code, Cursor or VS Code, e.g.zitadel-cli/1.0.0 node/v24.12.0 darwin/24.6.0 arch/arm64 host/claude_code. Theci/andhost/tokens are left out when telemetry is opted out with--no-telemetry,DO_NOT_TRACKorZITADEL_TELEMETRY=0. (#1274) -
Manage per-environment variables and secrets from the CLI with
zitadel variables list|get|set|delete. Each command names its owner with--environment(alias--env) or--project-level, and a non-interactive run that names neither is refused rather than defaulted. (#1259) -
The branding and flow definition editor schemas now flag what the server rejects: asset URLs that carry
user:password@,typography.font_urlwithouttypography.font_family, a terminal step that also collects or acts, a step that does nothing,sso_providerswithout acallbacktransition, and a transition that sets bothpurposeandaction. The API client's Zod schemas reject credentials in branding asset URLs, andzitadel planmeasures asset URL length in bytes, as the server does. (#1224) -
Flow definitions sync as revisions. An edited flow file plans as a
reviseandapplypublishes a new immutable revision instead of updating in place. A schema revise re-publishes the flows pinned to it with the newuser_schemain the same run. Removing a flow file no longer deletes the flow on the platform;applyfails withE_NOT_IMPLEMENTEDinstead. (#1116) -
Social login gets its configuration contracts.
zitadel setupnow copies two more dialect files into.zitadel/meta/:idp-connection.json, the schema for a provider connection file, andsso-auth-method.json, the shape of thessoslot in a user schema. A user schema withsso.enabled: truemust now list the connection slugs its users may sign in with undersso.providers, and a disabled slot must not carry the list. In a flow definition,identity_unknownis a reserved transition outcome that switches a login flow to register when a provider returns an unknown user. (#1156) -
zitadel startnow boots the local server with the platform project and a local admin, so you exist on your own server without signing up, and prints a one-time link that signs you in to the console. The newzitadel consolecommand mints a fresh link whenever you need one; the console honours such a link only when it is served from loopback. A localzitadel setupattaches the new project to the admin's team, sozitadel claimreports it as already owned. Booting the platform project also makes it the server's default project, so the console and the hosted login now point atproj_platformrather than at the first project you create; users seeded into another project cannot sign in to them. A harness that wants a bare single-project server setsNEXTGEN_PLATFORM_BOOTSTRAP_PROJECT=falseand gets neither the platform project nor the admin — including one built on@zitadel/testing, whosewithZitadelboots the server by runningzitadel startand so inherits the new default. Users imported with--user-filenow register unique attributes from their user schema'sx-uniqueannotations, so a user whose schema identifies users by email can sign in. (#1225)
Patch changes
-
Escape terminal control characters in everything the server returns. A value anyone can write — a user's name, a branding field, a variable — could carry ESC or OSC sequences that cleared the screen, set the window title, wrote the clipboard or disguised a link when another person ran
listorget. Every response body and server error message now has its C0 and C1 control characters, DEL, and format and bidi controls shown as\xNNor\uNNNN; newlines and tabs are kept.--jsonoutput carries the same escaped text. (#1273) -
Polish the claiming journey copy: the claim nudges in
setup,status, anddoctornow name the claim deadline and the data-loss stake, theclaimcommand's help text, pre-browser message, and success output speak in claim/permanence terms, and the team id moved out of the human success output (it stays in the JSON envelope and.zitadel/secret). The setup summary's INSTALLED section now reports the SDK package the scaffold actually added and recognizes every supported framework's artifacts instead of always claiming@zitadel/sdk-nextwith Next.js file paths. (#1194) -
Updated dependencies:
@zitadel/server@1.0.0-alpha.23,@zitadel/api@1.0.0-alpha.23,@zitadel/config@1.0.0-alpha.23.
1.0.0-alpha.22
Minor changes
- Clearer claim journey output: commands now sit on their own styled line in the setup box, separated from the prose so they can be copied without surrounding words, the end-of-setup notice states the concrete deadline (date, time, and zone) until which the temporary project can be claimed, terminal boxes wrap to the window width instead of breaking their frame below 80 columns, and
zitadel claimexplains a closed 14-day claim window instead of suggesting a futile retry.zitadel setupnow also nudges against a local server when that server hosts the platform plane (probed via its runtime document), where the claim can actually complete; the offline surfaces (status,doctor) keep their cloud-only nudges. (#1147)
Patch changes
- Updated dependencies:
@zitadel/server@1.0.0-alpha.22,@zitadel/config@1.0.0-alpha.22,@zitadel/api@1.0.0-alpha.22.
1.0.0-alpha.21
Patch changes
-
zitadel claimagainst a CLI-launched local server (--server local) now opens the claim page on the local server itself instead of a remote default: the CLI passes the server's public base when it starts the binary or docker runtime. When a manually started loopback server still advertises a remote claim page,claimwarns and names theNEXTGEN_SERVER_PUBLIC_BASEsetting to fix it. (#1127) -
Session responses identify their user through a resolved user ref derived from the user schema's own
x-identifier/x-displaydesignations (ADR 058), replacing the convention-resolved flatname/emailfields this supersedes (see the earlierGET /sessions/meidentity changeset). Rendering follows one chain everywhere:display, falling back toidentifier, thenuser_id. (#1085)@zitadel/server:GET /sessions/me, session get, and query sessions embeduser({user_id, identifier, identifier_property, display}), the list path hydrated with one batch resolution per page — listed sessions now carry user identity at all. The conventional attribute-name resolver (name/givenName+familyName/email) is removed.@zitadel/api: the regenerated client types the newuserref component.@zitadel/components:<zitadel-session>/<zitadel-logout>render from the ref; thezitadel-signoutdetail is now{display, identifier}; logout templates substitute{{display}}/{{identifier}}(the old{{name}}/{{email}}tokens keep filling as aliases).@zitadel/sdk-core(and every SPA SDK via the shared contract):NextgenSession/ClientSessionbecome `{userId, identifier,
-
Updated dependencies:
@zitadel/server@1.0.0-alpha.21,@zitadel/api@1.0.0-alpha.21,@zitadel/config@1.0.0-alpha.21.
1.0.0-alpha.20
Patch changes
-
Scaffolded Nuxt projects no longer list
@zitadel/ui-reactinbuild.transpile. The package is gone, so writing it into a user'snuxt.config.tsleft them with a config entry pointing at a dependency they do not have. (#913) -
GET /schemasandGET /schemas/{id}now return each schema as an{id, schema, metadata}envelope carrying the full customer-authored document, andGET /schemaswraps its rows in a{schemas: [...]}object. The two read endpoints share one representation; the resourceidandmetadata.created_atare server-owned and can no longer collide with keys in the document. Clients that read the bare document fromGET /schemas/{id}or the bare{id, created_at}array fromGET /schemasmust unwrap the envelope. (#930) -
The console's schema directory now pages through
GET /schemaswith aLoad morebutton, andzitadel schemas listwalks every page so the printed revision history stays complete. (#947) -
Updated dependencies:
@zitadel/server@1.0.0-alpha.20,@zitadel/config@1.0.0-alpha.20,@zitadel/api@1.0.0-alpha.20.
0.1.0-alpha.19
Patch changes
-
A branding asset URL that is well-formed but unreachable no longer fails silently.
logo_url/hero_urlcleared every gate — the CLI's shape check and the server's save gate — published a revision, and then rendered as a 0×0<img>: no plan output, no apply output, no console error. (#886)Three changes close that hole:
planandapplyprobe each asset URL (HEAD, 2.5s budget, in parallel) and emit a non-blocking warning when it is unreachable, returns a non-2xx status, or answers with something that is not an image. Advisory by design — the machine planning is not necessarily the machine rendering the login page — so it never fails a run. SetZITADEL_SKIP_ASSET_PROBEto turn it off (offline, air-gapped CI, a CDN that only resolves from production) andZITADEL_ASSET_PROBE_TIMEOUT_MSto retune the per-URL budget. Only public HTTPS destinations are contacted and redirects are re-validated; loopback/private/internal targets remain inconclusive rather than becoming network requests from the machine running the plan.- The login UI hides an asset that fails to load and restores either the split
designs' decorative placeholder or the shipped design's authored no-logo
content, so a broken asset degrades to the same result as no asset instead
of a blank pane or missing compact brand. Templates could not do this
themselves: they are DOMPurify-sanitised and inline
onerroris stripped. - Branding revisions can now carry plan warnings at all; previously only create/update actions could, and branding is revisioned.
Two readability fixes ride along. A branding
planno longer dumps the whole inlined Liquid template as one escaped line: an unchanged multi-line field renders as(<n> lines, sha256:…)and a changed one as a real line diff. And the branding dialect file scaffolded into.zitadel/meta/now spells its command mentions the way the generated app can run them (npx @zitadel/cli@<version> apply), matching the READMEs — the barezitadel applyin the editor tooltip named a command that does not exist there. -
Scaffolded
.zitadel/**READMEs now show runnablenpx @zitadel/cli@<version> …commands instead of the barezitadelcommand, which does not exist inside a generated app. The branding dialect now explains thatlayoutis the degrade preset (centered/split), not the design name — switch designs withbranding eject --design. The branding README shows exactly wherelogo_url/hero_urlgo (and that custom fonts aren't configurable there yet), and the setup summary surfaces the.zitadel/customization entry points (user schema, login flow, login template) and pairs the chosen design's wizard label with its slug (e.g. "Split (reversed)" →split-right) so you can confirm the selection applied. (#872) -
Released Zitadel server binaries now report the published package version instead of a build timestamp, and no longer log a missing-metadata warning at startup. Source builds report the revision they were built from, and locally built Docker images identify themselves as source builds rather than claiming the published version. (#889)
-
The package documentation now matches what the packages actually do. The Next and Nuxt guides drop the removed
api-baseattribute in favor ofconfigureZitadel()and theprojectproperty; the Nuxt guide documents the Nuxt module (whatzitadel setupwires) with its real options and theuseAuth()/useZitadelProject()composables, alongside the hand-rolled middleware path with its full option set.@zitadel/sdk-coreand@zitadel/apigain real documentation of their entry points,@zitadel/configgains a package README, and the SPA guides document theZitadelSessioncard and point local no-proxy experiments at the local runtime's actual default port (8080). The flow-editing guide copied into.zitadel/flows/no longer suggests cross-flowswitch/pivottransitions, which the runtime does not execute yet, and API examples use the real prefixed ID format (proj_…,team_…) instead of a retired naming scheme. (#856) -
Drop
positionfromx-auth-methodsentries;enabledis now the only key. The user schema declares which authentication methods a user type supports. Presentation concerns such as the order methods are offered in belong to the flow engine, which takes them from the order of a step's actions in the flow definition. (#822)An auth-method entry now sets
additionalProperties: false, matching the enclosingx-auth-methodsobject, which already rejects unknown method keys. A schema that still carriespositionfails validation instead of being accepted with the field ignored. -
The
dependency-versiondoctor warning now repairs with the project's own package manager and an exact-save flag (npm install --save-exact/pnpm add --save-exact/yarn add --exact/bun add --exact) instead of always suggesting a barenpm install, which would have switched managers on pnpm/yarn/bun projects and rewritten the exact pin as a caret range the check deliberately ignores. The repair command is also emitted as a structurednext_commandsentry, matching the agent contract's prefer-structured guidance. Widget-posture scaffolds now describetheme="auto"accurately: it follows the OSprefers-color-scheme, not the host app's own theme, and the generated comments and guidance say how to pintheme="light"/"dark"for apps that fix their scheme. (#825) -
The Zitadel server container now starts as the non-root user it ships with. It previously created a data directory next to its own entrypoint before reading configuration, which is not writable by that user, so the container exited before serving — and setting a data directory via environment or config file did not avoid it. This also fixes
zitadel start --runtime docker, which failed the same way. (#908) -
Clarify embedded login design guidance. The generated AGENTS.md gains a "theming the widgets from your app" section (the
--zl-*token bridge through the shadow DOM, thesuppress-headerknob, and how starter designs collapse by container width), and the Next session-state paragraph explains extending the request-boundarymatcherfor server-rendered header chrome. The--designflag help and the wizard hints now state what split-family designs show at card width (compact brand mark:logo_url, elsehero_url;herofalls back to editable text), setup emits a warning when a widget-posture app pickssplit/split-right, and the scaffolded widget-posture pages mentionsuppress-headernext to the variant/theme comment. (#863) -
zitadel setupnow pins the dev-server port in the scaffoldeddevscript for Next and Nuxt, so the app serves the port setup registered as the project's allowed origin. Previously a barenext dev/nuxt devignored that port and defaulted to 3000 — and Next silently moved to 3001 when 3000 was busy — so login rendered but the first submit failed withorigin "http://localhost:3000" is not allowed for this project. The other frameworks already pinned the port in their own dev-server config (Vite'sserver.port+strictPort, Angular'sserve.options.port) and are unchanged. An explicit port also turns a busy port into a loudEADDRINUSEinstead of a silent move to a rejected origin. (#885)doctorverifies that dev script against the port recorded as the development issuer, so a script moved to another port is reported as an unapplied config edit anddoctor --fixrestores the registered port.ejectnow listspackage.jsonamong the edits it cannot auto-revert.A login that cannot start — a rejected origin being the most common cause — now reports the failure inside the login card instead of leaving a bare line of text on an otherwise empty page.
-
zitadel setupno longer claims a split design's brand pane is collapsed in your app. The pane follows the login's own container width, so the warning now describes when it actually collapses — a card-width embed, or any phone-width viewport — and why settinglogo_urlorhero_urlin.zitadel/branding/branding.jsonmatters there. Full-page setups get the warning too: without one of those assets, asplitorsplit-rightlogin loses its branding entirely at narrow widths. (#887) -
Updated dependencies:
@zitadel/server@0.1.0-alpha.19,@zitadel/config@0.1.0-alpha.19,@zitadel/api@0.1.0-alpha.19.
0.1.0-alpha.18
Minor changes
-
The framework SDK packages (react, vue, angular, svelte, solid, qwik, nuxt) now re-export the
businessLocalescopy overlay from@zitadel/components, so an app that only declares its framework SDK as a direct dependency can wire the work-email wording without reaching into@zitadel/components(which strict package managers would not resolve).zitadel setup --use-case businessuses this to wire the overlay into every framework's generated auth pages — previously only Next scaffolds got the business copy; the SPA scaffolds pass a plainlocalesprop to the wrapper components, anddoctor --fixregenerates the same markup from the recorded use case. (#711) -
Add
zitadel claim, which attaches a project to a team so it becomes permanent. The command mints a short-lived link with the project secret, opens it in a browser, and blocks until the developer finishes signing in there, then recordsclaimed_atandteam_idin.zitadel/secret. (#754)Nothing about the project changes: the issuer, users, passkeys, and applications keep working, and the project secret is not rotated. Running it again once the project belongs to a team is a clean
status: "skipped"withreason: "already-claimed", whether that is known locally or learned from the platform, so agents can retry safely. Links last 10 minutes; once one lapses the command exitsE_VALIDATIONand points at a fresh run.--dry-runstops before anything is minted and reportsstatus: "skipped",reason: "dry-run". There is nothing to preview, because a claim is decided in a browser rather than by anything the CLI computes.The link is always printed before any browser opens, so headless machines, SSH sessions, and
--no-openneed no special handling. Where a browser can be launched,BROWSER, macOS, Windows, WSL, and the usual Linux openers (xdg-open,gio open,x-www-browser,sensible-browser,gnome-open,kde-open) are all handled.--timeout <seconds>stops waiting sooner than the link's own expiry. -
Report whether a project is attached to a team in
setup,status, anddoctor, so the temporary nature of a fresh project is visible without having to knowzitadel claimexists. (#776)setupcloses with an ownership line and points atclaim,statuscarriesdata.project.claim(detached, orattachedwith the owningteam_idandclaimed_at), anddoctorgrows aclaimcheck. All three readclaimed_at/team_idfrom.zitadel/secret, whichzitadel claimalready writes, so nothing here costs a platform call and everything keeps working offline.A project with no team is a warning, never a failure: it works exactly like one with a team, so
doctorstill exits 0, and--fixdeliberately does nothing because claiming needs a human in a browser. The messaging frames unattached projects as temporary without promising deletion, since nothing deletes them today.Nudges appear only for projects whose
serverinzitadel.jsonis the Zitadel cloud. Local and self-hosted projects have no team to attach to, sozitadel setup --server localstays quiet about it. -
doctornow verifies the scaffolded app files. Setup records a scaffold manifest (per-file content hash and ownership class) in.zitadel/state.json; the newmanaged-filescheck fails when an infrastructure file (request boundary,custom-elements.d.ts) is missing, warns on a missing generated page, and classifies edited or user-adopted files without failing them.doctor --fixrestores missing managed files and — across all checks, including the dependency repair — never replaces an existing scaffolded app file, edited or not; additive repairs (gitignore entries, env keys, the SDK dependency) stay idempotent. The check also verifies the managed config wirings (the Vite/Nuxt dev-proxy merges, Angular'sangular.jsonproxy and auth routes) through the patchers' own idempotent transforms — a detached or missing wiring config fails, an unverifiable one warns, and--fixre-applies it. Boundary migrations converge safely: a pristine leftovermiddleware.tsfrom a Next 15→16 upgrade is swapped forproxy.tsby--fix, while an edited one becomes an explicit conflict instead of creating both. Apps scaffolded by older CLI versions are checked against template-derived expectations untildoctor --fixmaterializes their manifest (setupskips initialized projects). (#685) -
The CLI now enforces its supported framework floors — Next.js 15 and newer, React 18 and newer.
setuprefuses a below-floor app before making any change, anddoctorreports one the same way: both emit an explicitE_UNSUPPORTED_PROJECT_SHAPEerror naming the floor together with an upgrade hint. Only version ranges that provably cannot resolve to a supported release are rejected — protocol specs (file:,workspace:), dist-tags (latest), and ranges that admit a supported version all pass.@zitadel/sdk-nextnow declares the matching peer rangenext >=15. (#713) -
The framework SDK wrappers now expose the widgets' surface contract:
ZitadelLoginandZitadelSessionacceptvariant(widget|page) andtheme(light|dark|auto), andZitadelLogoutacceptstheme, across the React, Vue, Angular, Svelte, Qwik, and Solid wrappers. Thelocalesprop additionally accepts partial dictionaries, so presets likebusinessLocalesare directly assignable. Apps scaffolded byzitadel setuppinvariant="page"on the generated/profilepage's<zitadel-session>(keeping it full-page under the new widget-first default) and reference the SDK-shipped React JSX declarations instead of carrying a hand-maintained copy. (#686) -
Give the embedding app a supported way to read session state for its own chrome (header navigation, account menus) — previously the widgets read
GET /sessions/meinternally but the host page had no documented path to the same answer and kept rendering signed-out CTAs beside a live session. (#717)@zitadel/sdk-nextships a new@zitadel/sdk-next/sessionentry withgetSession(): a client-side read of the same-origin{proxyPath}/sessions/me(the exact read<zitadel-session>performs). Works on any page — unlike server-sideauth()it does not require the route to be covered by the middlewarematcher— and returns the client-safeClientAuthResult(userId/email/name, no token). 401, the backend's JSON 404, and anonymous sessions map to signed-out; other failures — including a framework's HTML 404 from a misrouted proxy — throw instead of silently rendering signed-out.- The client-safe auth shapes (
ClientSession,ClientAuthState,ClientAuthResult) move to@zitadel/sdk-coreas the single source;@zitadel/sdk-nuxtre-exports them unchanged, so itsuseAuth()and sdk-next'sgetSession()now return the identical shape. - CLI scaffold guidance (
AGENTS.mdmanaged section) and the generated profile pages now name each framework's session read:getSession()on Next, the auto-importeduseAuth()composable on Nuxt, and the raw/__nextgen/sessions/meread for the SPA frameworks.
-
The setup wizard now asks how the login should look: keep the preselected built-in template (writes nothing), or pick one of the five starter designs (centered, split, split-right, hero, minimal) to eject it into
.zitadel/branding/and publish it as branding revision 1 during setup.--designanswers the question non-interactively, as before. The chosen design is reported in the summary box, the JSON envelope (data.design,nullfor built-in), and the setup retry hint, and setup's next actions now point at the branding workflow — edit/plan/apply when a design was ejected,branding ejectwhen not. (#750) -
Tenant-customizable login templates land end to end (ADR 040): eject a design, edit real Liquid,
plan/applypublishes it, and the login renders it. (#563)@zitadel/server: new Branding API (POST /branding,GET /branding,GET /branding/{id}) storing immutable per-project branding revisions with a lexical template gate (size, encoding,<script>/<style>, inline handlers,javascript:URLs,| raw). Flow responses now resolve the latest revision per project instead of the hardcoded default.@zitadel/api: generated client and zod schemas for the Branding API.@zitadel/config: the authoritative LiquidJS template validator (@zitadel/config/template), thebranding.jsonconfig dialect meta-schema, and the ejectable design catalog (centered,split,split-right,minimal) withgetDefaultBrandingConfig.@zitadel/components: split/minimal layout chrome for the design catalog; the{% mandatory_gates %}tag name is now single-sourced from@zitadel/config/template.@zitadel/cli:.zitadel/branding/becomes a synced resource — abranding.jsondescriptor plus a siblinglogin.liquidthe CLI inlines on upload.zitadel branding eject [--design <name>]scaffolds it,zitadel setup --design <name>does so at setup and publishes revision 1, andplan/applyvalidate templates with the authoritative validator and publish edits as new revisions.
-
zitadel setup --use-case businessnow wires the SDK'sbusinessLocalesoverlay into the generated Next login/register pages, restoring work-email wording on top of the widgets' neutral built-in copy (assigned via a ref so it holds on React 18, anddoctor --fixregenerates the same markup from the recorded use case). The generated profile pages leave page chrome to the session card'svariant="page"surface instead of hardcoding viewport height and background, every scaffolded page names thevariant="widget"embedding alternative in a comment, and the scaffolded AGENTS.md guidance points at the widgets' variant/theme knobs and the SDK-shipped JSX types. (#707)
Patch changes
-
Session-state reads now bypass caches and only canonical Zitadel 401/404 error responses are treated as signed out, including expired or superseded session cookies. The browser-only
getSessionhelper and its options type now live on the dedicated@zitadel/sdk-next/sessionentry instead of the package root. Framework proxies attach the project secret only to the exactPOST /sessions/exchangehandoff operation, so browser-reachable public and management paths no longer receive an infrastructure-supplied operator credential. After upgrading the CLI, runzitadel doctor --fixto migrate the legacy managed Vite and Angular proxy hooks. Doctor warns when an unrecognized proxy may still over-forward the project secret; custom proxy implementations remain user-owned and must be reviewed manually. (#719) -
Add the
herolanding design, a mobile compact brand header for split-family designs, split layout knobs (--zl-split-columns,--zl-split-align,--zl-split-brand-mobile), and a warn-once console signal for missing text keys. (#603) -
stopnow reaps the local server's embedded Postgres, andstartself-heals a Postgres orphaned by an earlier unclean exit. The server binary starts Postgres throughpg_ctl, which daemonizes the postmaster into its own session — so the process-group signalstopsends never reached it, and a crash or SIGKILL could leave it running and holding the data-directory lock. The nextstartthen failed withE_NETWORK("exited before becoming healthy") and a `pg_ctl: (#547) -
zitadel setup --renderernow only advertises implemented renderers:--helplistsreactand calls out the plannedweb-componentrenderer as not yet available instead of offering it as an equal option. Passing--renderer web-componentexplicitly now fails at flag parsing — before any remote project is created — rather than mid-setup. (#681) -
Scaffolded auth pages now derive their embedding posture from how setup met the app (ADR 044). Fresh scaffolds keep the widgets' full-page chrome; a pre-existing Next or Nuxt app gets
variant="widget"cards withtheme="auto"in a layout-neutral wrapper, so the login no longer paints token-colored chrome underneath the host app's own header and theme. Nuxt setup also stops writingapp.vue/pages/index.vueinto pre-existing apps — the shell and homepage stay user-owned, mirroring Next. The chosen posture is recorded in the scaffold manifest anddoctor --fixrestores it; manifests without a record restore full-page. A newdependency-versiondoctor check warns (with the exact install command) when an exactly-pinned@zitadel/*dependency trails the CLI's own train, so a newer CLI's guidance can't silently reference SDK entry points the app's pinned SDK does not ship yet. (#824) -
setupno longer reorders yourpackage.json: the dependency splice preserves the file's key order, indentation, line endings, and trailing newline (only the touched dependency map is name-sorted, as package managers write it); the Angulardev-script merge behaves the same. Setup's file reporting is also cleaned up —files_writtennow lists deduplicated file paths only (directories and double-counted env merges are gone), and a newdata.filescarries one typed row per artifact ({path, kind, action}) so scripts can tell what setup created versus merged into. (#688) -
zitadel setupnow asks "Who will sign in to your app?" and scaffolds the matching schema fields:minimal(email only),consumer(email, given and family name), orbusiness(adds acompanyNameattribute).minimalis the default, so the no-flag scaffold now collects email only — a deliberate slim-down from today's output: given/family name move toconsumer/business, anddateOfBirthis no longer scaffolded by any use case. The default schema and login-flow templates (embedded as the server-side fallback for projects created without the CLI) are slimmed to the same email-only baseline, so the no-CLI default and theminimaluse case now agree; the per-field bodies forgivenName/familyName/companyNamemove into the config field catalog the CLI composes from. This is a second axis alongside the sign-in preset (#448): the use case owns the schema field set, the sign-in preset owns the flow, and the login flow's register step is derived from the chosen fields instead of a hard-coded list — so the two compose instead of multiplying into a bundle per pair. The question is asked before the sign-in preset; non-interactive and scripted runs use--use-case(defaults tominimal, never blocks); the choice is recorded inzitadel.jsonfor guidance/status only, never behavior.businessis a field set only for now —companyNameis a plain user attribute with no org/team model behind it yet. Every (use case × sign-in preset) pair is hygiene-tested against the flow validator. The unused, divergentbuildUserSchema/fieldPresethelpers are removed in favor of a single source of field defaults. (#551) -
Flip
<zitadel-login>to widget-first: the defaultvariant="widget"is content-sized, transparent through every layer, injects no default font into the host document, and never steals focus on load — the embedding app owns the page. Dedicated login routes (hosted shell, scaffolded pages) opt into the previous full-page behavior withvariant="page". Split-family responsive chrome now keys off the widget's own width via container queries (baseline 2023 browsers), the hero design ships neutral placeholder copy instead of fabricated claims, and split tenants with only ahero_urlkeep a compact banner fallback on narrow widths. (#603) -
Updated dependencies:
@zitadel/server@0.1.0-alpha.18,@zitadel/api@0.1.0-alpha.18,@zitadel/config@0.1.0-alpha.18.
0.1.0-alpha.17
Patch changes
-
Fixes from alpha.16 community feedback: (#544)
- Custom schema fields now render a readable label. A property with no
catalog entry (e.g.
department,dateOfBirth) falls back to a humanised name ("Department", "Date of birth") on the form instead of leaking the raw<step>.field.<name>text key. A catalogued key still wins, so localised labels are unaffected. - The scaffolded
.zitadel/flows/README.mdno longer contains the "Presets" section twice. - The
warn/default-flow-swapplan warning now leads with the impact in plain language: the new flow becomes the default for its purposes, and every page that does not explicitly setflow-nameon<zitadel-login>will start rendering it — scope it viaaudienceor pinflow-nameto opt out. - The flip-table validation error (login/register entry step missing its
user_not_found/user_already_existstransition) now explains who gets stuck where: someone without an account would be stuck at sign-in instead of being routed to registration, and vice versa. Plan, apply, and the server report the same wording.
- Custom schema fields now render a readable label. A property with no
catalog entry (e.g.
-
plan --jsonandapply --jsonnow enumerate what they touch: adata.changesarray with one{kind, action, file, id?, previous_id?}row per resource (action ∈ create/update/revision/delete). Plan rows preview the pending sync; apply rows report what happened, carrying the resulting platform ids (created ids, newly published revision ids), so agents can verify an edit did what they intended without re-applying.applyalso gainsnext_actions/next_commands("changes are live" + a versionedplanfollow-up), andschemas listemitscreated_atin snake_case like every other envelope field. Counters andfiles_updated(local write-backs only) are unchanged. (#543) -
The interactive
setupwizard now detects a running local Zitadel server the same waystartanddoctordo — via the runtime metadata written byzitadel startplus a/healthzprobe on the default port — and preselects it in the server choice. Previously it scanned localhost ports for an OIDC discovery document the server does not serve, so it always reported "No local OIDC servers found" even with a healthy server running. (#546) -
Updated dependencies:
@zitadel/config@0.1.0-alpha.17,@zitadel/server@0.1.0-alpha.17,@zitadel/api@0.1.0-alpha.17.
0.1.0-alpha.16
Patch changes
-
Select a flow definition by name.
<zitadel-login>gains aflow-nameattribute (flowNameprop on every framework wrapper) that sendsflow_definition_nameon flow start, so a project with several synced flows can run a specific one instead of the audience-resolved default. An unknown name or a purpose mismatch surfaces as a clear startup error naming the attribute. Audience selection itself is now honored and deterministic: hinted app beats hinted team beats the newest unscoped flow, and a flow scoped to an app/team no longer captures the project default. The flows README and plan/apply docs explain how to add and select a second flow. (#514)Because newest-unscoped-wins means a new flow can silently take over the default login,
planwarns on any create of an active, unscoped flow in a project that already has flows (warn/default-flow-swap, a non-blocking# warning:line and a--jsonwarnings entry) — scope the flow viaaudienceor pinflow-namein the widget to opt out. The offline dialect gains the committedauth-methods/auth-methodmeta-schema copies thatuser-schema.jsonreferences, so editors resolve the full dialect without network access. -
Disabling passkey in the user schema (
x-auth-methods.passkey.enabled: false) is now enforced for flows. A flow step declaring apasskeyorpasskey_registeraction against a schema that does not enable passkey fails validation at plan time (and on the server at apply time) with `step "…": action "…" offers passkey but "passkey" is not an enabled (#515) -
The passkey origin-allowlist rejection now names the allowed origins (e.g.
origin "http://127.0.0.1:3000" is not allowed for this project (allowed: http://localhost:3000)), and<zitadel-login>surfaces the server's error message instead of a generic "returned 400".@zitadel/apiexports the newapiErrorMessagehelper for extracting the server error envelope from anApiError. (#497) -
planandapplynow validate flow definitions against the same rules the server enforces — before any mutation. A flow missing an invariant (e.g. a login entry step withoutuser_not_found -> registerwhileregisteris a wired purpose) fails at plan time with the server's exact wording instead of half-applied after the schema already revised. Errors aggregate across flows (--jsoncarries structureddetails.issues); product guidance surfaces as non-blocking# warning:lines in the plan. The validator ships as@zitadel/config/validate. Escape hatch: setZITADEL_SKIP_FLOW_VALIDATIONto skip the pre-flight if it ever disagrees with your server version. (#499) -
Remove the scaffold landing chooser ("Sign in, create an account, or open your profile") from every framework template. Fresh apps now redirect
/to/login, setup next steps tell users to open/login, and Next.js auth pages no longer duplicate login/register links the widget already provides in-flow. (#517) -
Scaffolded projects now explain their own next step.
zitadel setupwrites anAGENTS.mdguidance section for AI agents and an "Authentication (Zitadel)" section into the app README (marker-fenced — existing content is never clobbered), copies the flow/schema dialect meta-schemas into.zitadel/meta/, and scaffolds flow files with"$schema": "../meta/flow-definition.json"so editors validate and autocomplete flow edits offline. The$schemapointer is local-only: sync ignores it and write-back preserves it.ZitadelLoginwrappers gain typedlocales/langprops for labelling custom flow steps (see the new "Customize copy" docs page). (#502)zitadel ejectremoves what setup wrote: the marker-fenced guidance section is stripped fromREADME.md/AGENTS.md(content outside the markers is untouched), and a file is deleted only when nothing but the scaffold-created header would remain — no stale golden path survives pointing at deleted.zitadel/files.Every SDK wrapper now forwards
locales/langto the widget (previously only React did; Solid/Qwik/Svelte accepted and discarded them, Vue/Angular did not expose them). The flow dialect meta-schema (@zitadel/serverembeds it;@zitadel/configships the committed copy) marks a transition'sactionas nullable, matching the OpenAPI contract — editors no longer flag"action": null. -
zitadel setupnow asks "How should users sign in?" and scaffolds the matching schema+flow preset:password-first(today's default) orpasskey-first(a one-tap passkey on the login entry step with an email → password fallback path, passkey-primary registration, and email kept required so the fallback always works). Non-interactive and scripted runs use--preset; the choice is recorded inzitadel.json. Presets are named bundles under@zitadel/config(the mechanism behind app-type selection, #448) and are hygiene-tested: every bundle must pass the flow validator and resolve every text key in every builtin locale. (#500) -
Setup failure guidance now reconstructs the full invocation: retry hints and
next_commandscarry--preset,--renderer,--dev-port, and--non-interactivealongside--framework, so following the printed command verbatim reproduces the requested scaffold instead of silently falling back to defaults. HTTP 404s map to the newE_NOT_FOUNDerror code with exit code 4 (previouslyE_VALIDATION/exit 3 — update scripts that branch on it); a 404 without the platform's error envelope also names the URL and asks whether the target is a Zitadel platform API. Passkey-first scaffolds add a note to AGENTS.md telling agents to verify the login loop via the email/password fallback or a CDP WebAuthn virtual authenticator, since automated browsers cannot complete passkey ceremonies. (#524) -
Setup and status guidance now tracks where you are in the journey. The
zitadel setupterminal box ends on the verify mission (install, start, register → sign out → sign in) plus a single breadcrumb tozitadel statusand the README's Zitadel section, instead of listing customize/publish steps before the first login. The--jsonenvelope keeps the completenext_actions/next_commandsfor agents.zitadel statusasks the platform whether the project has users yet: none → verify-login guidance, some → the customize (.zitadel/schemas/, .zitadel/flows/) and plan/apply publish steps; when the server is unreachable it keeps the lifecycle-only output.next_commandsis staged in lockstep: before the first proven login it offersplanand withholdsapply. (#516)The server implements
GET /users(previously generated-but-unimplemented, returning 500): bearer-scoped to the token's project — the exact call shape of the status probe — returning attribute-hydrated users with a stable creation-orderedoffset/limitwindow (spec defaults limit 20, max 100). The staged status therefore works against a real runtime, not only the api-mock. -
Updated dependencies:
@zitadel/server@0.1.0-alpha.16,@zitadel/config@0.1.0-alpha.16,@zitadel/api@0.1.0-alpha.16.
0.1.0-alpha.15
Patch changes
-
applynow re-pins flows to a freshly published schema revision in the same run: the CLI rewritesuser_schemain every local flow file pinned to the superseded revision (lockfile-style, announced byplanand reported in the output) and the flow update carries the new id — editing a schema and using the new field in a flow no longer fails validation or needs a second apply. Interrupted runs recover via apreviousIdmarker in.zitadel/state.json. (#485) -
Make
plandiffs trustworthy and keep local config in lockstep with live state.@zitadel/config/normalizeis the shared canonical-form normalizer (drops the server's emptyaudienceecho and spelled-outx-*meta-schema property defaults); the sync engine hashes and diffs in normalized form (with a legacy-hash fallback so existing state files don't read as edits), and setup/apply write the server's canonical body back to the local file — reported in human and--jsonoutput — so a one-field edit renders as a one-field diff and applying can no longer silently strip live settings. The api-mock now mirrors the server's unconditionalaudienceecho. (#485) -
Surface the customize loop after setup: the "Zitadel is ready" next steps now point at the editable
.zitadel/schemas/and.zitadel/flows/files and theplan/applycommands, and the scaffolded READMEs are restructured workflow-first (mental model → example → making changes → common changes). (#485) -
Updated dependencies:
@zitadel/config@0.1.0-alpha.15,@zitadel/server@0.1.0-alpha.15,@zitadel/api@0.1.0-alpha.15.
0.1.0-alpha.14
Minor changes
-
Scaffold editable schema and flow config from shared local defaults, add project default seeding control, and seed sync state so plan is idempotent immediately after setup. (#341)
-
Add a post-sign-in
<zitadel-session>"signed in as" card: a dedicated element exposed through every SPA SDK and re-exported from sdk-next. CLI scaffolds now render it as the post-sign-in/profilepage (with a Logout action) across all frameworks. Identity is read fromGET /sessions/me, preferringnamethenemailthenuser_id. (#443)<zitadel-logout>now sources its identity from the samegetMySessionoperation instead of the__nextgen_displaycookie, so both signed-in surfaces work against the real backend. Both components route theirgetMySession/revokeMySessioncalls through the sharedapi-clientwrappers that enforcecredentials: "include".
Patch changes
-
Route published CLI telemetry to the production Mixpanel project during npm publish. (#469)
-
Map CLI telemetry events to Mixpanel's event country property so country appears correctly in analytics. (#436)
-
Verify public npm provenance after repository publication. (#474)
-
Updated dependencies:
@zitadel/server@0.1.0-alpha.14,@zitadel/api@0.1.0-alpha.14,@zitadel/config@0.1.0-alpha.14.
0.1.0-alpha.13
Patch changes
-
Drop the
x-passworduser-property annotation. The flow engine sources the password challenge from the reservedx-auth-methods#passwordfield name combined withx-auth-methods.password.enabledat the schema root (introduced in #400);x-passwordis no longer read by any code path. Removed from theuser-property.jsonmeta-schema and the CLI's generatedpasswordpreset; comments and docs updated to match. (#411) -
Updated dependencies:
@zitadel/server@0.1.0-alpha.13,@zitadel/api@0.1.0-alpha.13.
0.1.0-alpha.12
Minor changes
- Add opt-out anonymous usage telemetry. Disable with
--no-telemetry,DO_NOT_TRACK=1, orZITADEL_TELEMETRY=0. (#392)
Patch changes
-
Scaffolded app pages now enforce the dark surface the Zitadel widgets are designed for (
color-scheme: dark,#0f0f11), instead of following the OS light/dark setting — across every framework template (next,react,vue,angular,nuxt,solid,svelte,qwik). This fixes the inconsistency where the<zitadel-logout>avatar (and other non-widget chrome, e.g. the/profileview) rendered on a white background while<zitadel-login>enforced its own dark surface. (#337)Removed misleading field hints from the login component locales (
en,de,it): the password "include a symbol and number" hint (onlyminLengthis enforced server-side) and theYYYY-MM-DDdate-of-birth hint (native<input type="date">localizes its own format and submits ISO). A dynamic, validation-rule-driven hint is tracked in #251. -
Set the now-required
kindon the default flow's step actions: submit actions usesubmit, the register/login routing actions usenavigate. Without it, the generated flow fails validation against the updated flow-definition schema. (#372) -
Align the CLI flow-definition sync update with the PUT contract: send the
{ flow_definition }body envelope and the requiredproject_idquery parameter so updates to existing flow definitions are accepted by the generated server. (#354) -
Updated dependencies:
@zitadel/api@0.1.0-alpha.12,@zitadel/server@0.1.0-alpha.12.
0.1.0-alpha.11
Patch changes
-
CLI scaffolds now write the project service-key secret to
.env.localasZITADEL_PROJECT_SECRET, and the React/Vue/Angular dev proxies plus the Next.js and Nuxt server middlewares send it as the bearer on every proxied request instead of synthesisingsk_<project_id>from the public project id. The secret stays server-side:.env.localis gitignored, Vite only exposesVITE_-prefixed vars to the client, Next.js auto-loads.env.localintoprocess.envserver-side, and the Nuxt module readsprocess.env.ZITADEL_PROJECT_SECRETin itssetup()and pushes it into Nuxt's server-onlyruntimeConfig.nextgen.projectSecret(overridable at deploy time viaNUXT_NEXTGEN_PROJECT_SECRET). (#310)Also drops the unused
onExchangeResponsehook fromNextgenMiddlewareOptions(no callers anywhere; alpha so no external usage to break). -
Updated dependencies:
@zitadel/server@0.1.0-alpha.11,@zitadel/api@0.1.0-alpha.11.
0.1.0-alpha.10
Patch changes
-
Improve fresh-app CLI recovery guidance and align agent automation hook docs with the rendered login controls. (#328)
-
Updated dependencies:
@zitadel/server@0.1.0-alpha.10,@zitadel/api@0.1.0-alpha.10.
0.1.0-alpha.9
Patch changes
-
Report local port conflicts clearly, sweep managed local runtime orphans with
stop --all, and explain non-empty setup targets. (#325) -
Updated dependencies:
@zitadel/server@0.1.0-alpha.9,@zitadel/api@0.1.0-alpha.9.
0.1.0-alpha.8
Patch changes
- Updated dependencies:
@zitadel/server@0.1.0-alpha.8,@zitadel/api@0.1.0-alpha.8.
0.1.0-alpha.7
Patch changes
- Updated dependencies:
@zitadel/server@0.1.0-alpha.7,@zitadel/api@0.1.0-alpha.7.
0.1.0-alpha.6
Patch changes
-
Step
fieldsandactionsare now ordered[{ name, ... }]arrays on the wire (ADR 021). Templates iterate them in authorial order; the orchestrator buildsfields_by_name/actions_by_nameviews for keyed lookups. The private@zitadel/api-mockworkspace follows the same wire shape for tests.gatesstays a name-keyed object for now. (#270) -
Improve local bootstrap guidance for Docker prerequisites, generated auth pages, and agent browser proof. (#303)
-
Updated dependencies:
@zitadel/server@0.1.0-alpha.6,@zitadel/api@0.1.0-alpha.6.
0.1.0-alpha.5
Minor changes
-
Add
setup --framework react|vue|angular|nuxtsupport to the CLI. Each framework scaffolds its auth entry/pages and wires/__nextgen/*calls to the backend with ask_<project_id>bearer attached: React and Vue get a dev proxy magicast-merged into the Vite config (vite.config.*) that reads the project id fromZITADEL_PROJECT_ID; Angular gets aproxy.conf.cjswired intoangular.jsonthat reads it fromzitadel.json; and Nuxt registers the@zitadel/sdk-nuxtmodule in the Nuxt config (nuxt.config.*), which adds the proxy via server middleware. A--dev-portflag sets the scaffolded dev-server port. (#257) -
Make the generated Next.js auth app easier for agents and developers to prove end-to-end registration, logout, and login in a visible browser. (#299)
Patch changes
-
Allow fresh app scaffolding after
zitadel startcreates local runtime ignore files, and load Nuxt runtime config through the Nuxt virtual imports module. (#295) -
Updated dependencies:
@zitadel/api@0.1.0-alpha.5.
0.1.0-alpha.4
Patch changes
-
Harden local setup guidance, Next 16 scaffolding, and login form automation. (#279)
-
Updated dependencies:
@zitadel/api@0.1.0-alpha.4.
0.1.0-alpha.3
Patch changes
-
Allow same-directory setup after starting the local Zitadel runtime. (#272)
-
Updated dependencies:
@zitadel/api@0.1.0-alpha.3.
0.1.0-alpha.2
Patch changes
-
Derive alpha local runtime images from the installed CLI version, pin generated SDK dependencies to the same alpha train, and emit exact-version follow-up commands for reproducible tester reports. (#265)
-
Make doctor local-runtime checks advisory for cloud setup, harden fresh Next.js scaffolding, auto-install setup dependencies, normalize public follow-up commands, and avoid assuming Next.js in local-runtime setup guidance. (#255)
-
Updated dependencies:
@zitadel/api@0.1.0-alpha.2.
0.1.0-alpha.1
Minor changes
- Add top-level
zitadelcommands for managing a Docker-backed local Zitadel runtime. (#245)
Patch changes
- Scaffold prerelease SDK dependencies on the same npm dist-tag as the CLI. (#228)
0.1.0-alpha.0
Minor changes
-
Drop unused auth methods from the
zitadel setupprompt and consolidate flow domain logic intoapps/cli/src/lib/flows/. The setup prompt previously offeredpasskey,password, andtotpas a multiselect, buttotpis not a valid key underx-auth-methodsper the OAS spec (onlypassword|passkey|magic_link|sso|otpare allowed withadditionalProperties: false), so any user schema written withtotpselected failed validation. The Go flow engine only wirespasswordandidentifierchallenges today;passkeyhas a defined JSON shape but no runtime handler yet. (#158)Breaking change for non-interactive callers. The
--auth-methodsflag (CSV) has been renamed to--auth-method(single value); allowed values arepasskey(default) orpassword. Agents and scripts that previously passed--auth-methods passwordmust update to--auth-method password.Internally, the flow_definition shape (Zod schema, types, build, read/write, text-key extraction) now lives behind a single
apps/cli/src/lib/flows/module exported through one barrel. The sync layer remains shape-agnostic and treats flow payloads as opaque bytes. A follow-up PR will introduceapps/cli/src/lib/user-schema/mirroring the same layout. -
Remove the pre-claim / claim lifecycle from the CLI and api-mock. The
zitadel claimandzitadel claim statuscommands, theClaimClientinterface, theInitClaim*/ClaimStatus*schemas, theclaimed_at/team_idfields on.zitadel/secret, theE_CLAIM_REQUIREDandE_PLATFORM_HANDOFFerror codes, the production-claim gates inapplyanddeploy connect, and the api-mock'sclaim/init/claim/statushandlers andcompleteMockClaim()export are all gone. The SDK'sresolveZitadelRuntimeproduction-issuer error message no longer references the removedzitadel claimcommand./projects/{id}/claim/initand/projects/{id}/claim/statusare not in the OpenAPI spec and have no backend; the surface only worked against the mock. (#150) -
zitadel setupnow scaffolds amiddleware.tsat the project root that wires upnextgenMiddlewarefrom@zitadel/sdk-next. The middleware forwards/__nextgen/*same-origin toNEXTGEN_ISSUER_URL(the auth backend) and gates/profilebehind a JWT check. (#157)The file uses the
middleware.ts+function middleware()convention because Next 15 only recognises that form; Next 16 accepts it too (theproxy.tsrename is deprecated-but-backwards-compatible). Using the universal form means one template works on every supported Next major.Scaffolded pages now use
api-base="/__nextgen"instead of pointing at the backend URL directly, so no CORS configuration is needed and the backend URL never reaches the browser bundle..env.localno longer writesNEXT_PUBLIC_ZITADEL_API_BASE; it writesNEXTGEN_ISSUER_URL(the same value, server-side only).doctor --fixre-appliesmiddleware.tsif missing. -
zitadel setupno longer scaffolds or uploads the user schema and flow definition — the Zitadel server now provisions these defaults when a project is created. Setup no longer writes.zitadel/schemas/user.jsonor.zitadel/flows/default.json, runs no sync step at the end, and the--no-applyflag (which only gated that sync) has been removed. The sync engine and the hiddenapply/plancommands remain in place for a future pull-based workflow. (#208)Behavior change for non-interactive callers.
zitadel setup --no-applyis no longer a valid flag and will error; remove it from scripts and agents.Scaffolded Next.js login/register/profile pages now configure the SDK via
configureZitadel(...)and pass the resulting project handle to the<zitadel-login>/<zitadel-logout>web components through theprojectprop, instead of the removedapi-base/project-idattributes. To support an app that declares only@zitadel/sdk-nextas a direct dependency,@zitadel/sdk-next/clientnow re-exportsconfigureZitadelandgetApi. -
Rename the public packages to the
@zitadelscope and publish them to npm via changesets with GitHub OIDC trusted publishing. This is the first@zitadel/*-scoped release line, cut as analphaprerelease. (#209)
Patch changes
Was this helpful?