@zitadel/cli 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -0
- package/dist/index.mjs +1450 -1047
- package/dist/index.mjs.map +1 -1
- package/dist/lib/oclif/help.mjs +3 -2
- package/dist/lib/oclif/help.mjs.map +1 -1
- package/dist/skills/zitadel/SKILL.md +111 -0
- package/dist/skills/zitadel-cli/SKILL.md +635 -0
- package/dist/skills/zitadel-cli/references/driving-login-ui.md +124 -0
- package/dist/skills/zitadel-components/SKILL.md +152 -0
- package/dist/skills/zitadel-sdk-angular/SKILL.md +149 -0
- package/dist/skills/zitadel-sdk-next/SKILL.md +174 -0
- package/dist/skills/zitadel-sdk-nuxt/SKILL.md +149 -0
- package/dist/skills/zitadel-sdk-qwik/SKILL.md +136 -0
- package/dist/skills/zitadel-sdk-qwik-router/SKILL.md +126 -0
- package/dist/skills/zitadel-sdk-react/SKILL.md +149 -0
- package/dist/skills/zitadel-sdk-solid/SKILL.md +144 -0
- package/dist/skills/zitadel-sdk-solid-start/SKILL.md +149 -0
- package/dist/skills/zitadel-sdk-svelte/SKILL.md +137 -0
- package/dist/skills/zitadel-sdk-sveltekit/SKILL.md +147 -0
- package/dist/skills/zitadel-sdk-tanstack-start/SKILL.md +150 -0
- package/dist/skills/zitadel-sdk-vue/SKILL.md +147 -0
- package/dist/{skills-hint-N1H5-ny1.mjs → skills-hint-C8oe8cCo.mjs} +1446 -231
- package/dist/skills-hint-C8oe8cCo.mjs.map +1 -0
- package/oclif.manifest.json +102 -3
- package/package.json +8 -5
- package/skills/zitadel-cli/SKILL.md +62 -9
- package/dist/skills-hint-N1H5-ny1.mjs.map +0 -1
|
@@ -0,0 +1,635 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: zitadel-cli
|
|
3
|
+
description: >-
|
|
4
|
+
Set up and manage Zitadel authentication in a local project with the
|
|
5
|
+
agent-friendly `zitadel` CLI. Use when the user wants to add login,
|
|
6
|
+
registration, or session handling, create a Zitadel project, scaffold auth
|
|
7
|
+
for a Next.js, Nuxt, SvelteKit, SolidStart, Qwik Router, TanStack Start, React,
|
|
8
|
+
Vue, Angular, Solid, Svelte, or Qwik app, or build and deploy Zitadel config
|
|
9
|
+
changes from repo state.
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Zitadel CLI
|
|
13
|
+
|
|
14
|
+
The `zitadel` CLI integrates Zitadel auth into an existing project and keeps the
|
|
15
|
+
local config (`zitadel.json` and `.zitadel/**`) as the source of truth. Every
|
|
16
|
+
command emits a JSON envelope, so you should drive it non-interactively and
|
|
17
|
+
parse the result rather than scraping human output.
|
|
18
|
+
|
|
19
|
+
This skill teaches how to *drive* the CLI — how to invoke it, its output modes,
|
|
20
|
+
and its envelope. It deliberately does **not** enumerate the commands. The CLI
|
|
21
|
+
is self-describing, and this skill and the installed CLI version move
|
|
22
|
+
independently: a user may install the skill once and upgrade the CLI later, so
|
|
23
|
+
any command or flag list hardcoded here would drift out of lockstep and mislead.
|
|
24
|
+
Ask the CLI for its current surface (see [Discovering commands](#discovering-commands))
|
|
25
|
+
and treat what it prints as authoritative over anything you remember.
|
|
26
|
+
|
|
27
|
+
## Installing and invoking
|
|
28
|
+
|
|
29
|
+
Run the CLI with `npx`, which fetches and caches it on demand — there is nothing
|
|
30
|
+
to install first:
|
|
31
|
+
|
|
32
|
+
```sh
|
|
33
|
+
npx @zitadel/cli@latest <command> --non-interactive --json
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Use an exact version (`npx @zitadel/cli@0.1.0`) in agent scripts and bug
|
|
37
|
+
reports for reproducibility. The CLI is a preview: until 1.0.0 a minor version
|
|
38
|
+
can break, and its release note says so with **Breaking:**.
|
|
39
|
+
The CLI requires Node.js 24+. When `setup` creates a new app it first checks the running Node.js against the `engines.node` of the framework CLI it is about to run (`@angular/cli`, `nuxi`, `create-vite`, `create-next-app`). Only the Angular CLI refuses a Node it does not accept, and that range can be newer than 24.0: if `setup` refuses with `E_VALIDATION` naming the Node versions Angular accepts, switch to one of them and run it again. The Angular CLI checks each major on its own, so only 22.22.3+ on 22, 24.15.0+ on 24 and 26.x are accepted (23 and 25 run with a warning), and Node 27 or later is refused even though the published range reads `>=26.0.0`. For the other frameworks the CLI carries on, so `setup` adds a warning to the envelope's `warnings` and continues.
|
|
40
|
+
|
|
41
|
+
The app's framework must be at or above a minimum version: Next.js 15.2.3,
|
|
42
|
+
React 18, Vue 3.2, Nuxt 4, Angular 17, Solid 1.6, Svelte 5, Qwik 2.0.0-beta.45. Newer major
|
|
43
|
+
versions are not refused. `setup` and `doctor` refuse an app below its minimum,
|
|
44
|
+
by declared range or installed release, with `E_UNSUPPORTED_PROJECT_SHAPE`
|
|
45
|
+
(exit 3); `setup` refuses before it scaffolds or patches anything, while
|
|
46
|
+
`doctor --fix` can still repair unrelated files, so do not assume a floor error
|
|
47
|
+
left the project untouched. The message names the minimum and the hint says to
|
|
48
|
+
upgrade, so upgrade the app and rerun rather than retrying.
|
|
49
|
+
|
|
50
|
+
The CLI ships this skill and the other Zitadel skills. `skills add` puts the
|
|
51
|
+
ones of the version that runs it into the project's `.agents/skills/`, and into
|
|
52
|
+
`.claude/skills/` when Claude Code is installed or the project has a `.claude/`
|
|
53
|
+
folder, replacing older copies. After moving the app to a newer CLI, run it
|
|
54
|
+
with that version so the skills describe it. It talks to no server and asks
|
|
55
|
+
nothing; `data.skills` and `data.directories` say what it added where.
|
|
56
|
+
|
|
57
|
+
## Using the CLI
|
|
58
|
+
|
|
59
|
+
- **Drive it non-interactively.** Always pass `--non-interactive --json` (short:
|
|
60
|
+
`-n`). The JSON object on stdout is the machine contract — parse it rather than
|
|
61
|
+
scraping human output. `--non-interactive` also disables every prompt, so a
|
|
62
|
+
command that would otherwise ask for an answer fails fast with `E_VALIDATION`
|
|
63
|
+
naming the flag to pass instead of blocking. Supply each answer as a flag; run
|
|
64
|
+
the command's `--help` to see which flags exist and which are required.
|
|
65
|
+
- **Output modes.** `--json` is the agent contract and is unaffected by the
|
|
66
|
+
terminal — color and width never apply to it, so you do **not** need
|
|
67
|
+
`--no-color` or other cosmetic flags when you parse JSON; don't sprinkle them
|
|
68
|
+
on every call. Human mode adapts to the terminal: a TTY gets aligned tables,
|
|
69
|
+
while a piped or redirected run (or `--plain`) gets one tab-separated record
|
|
70
|
+
per line and nothing else. If you ever read human output, set `NO_COLOR=1`
|
|
71
|
+
once in the environment rather than repeating `--no-color` per command.
|
|
72
|
+
- **Don't truncate `--json` with `head`/`tail`.** The envelope is one bounded
|
|
73
|
+
object; slicing it with `head -c`/`head -n` yields invalid JSON and silently
|
|
74
|
+
drops records or fields, so you end up reasoning from partial data. It is
|
|
75
|
+
finite — read it whole, or narrow it *at the source*: use a `list` verb's
|
|
76
|
+
`filter_fields` and pagination to fetch fewer records, or pipe to `jq` to pull
|
|
77
|
+
just what you need (e.g. `… resources --json | jq '.data.items[].topic'`).
|
|
78
|
+
`--help` output is bounded too — read it in full rather than `head`-ing it, or
|
|
79
|
+
you will miss flags further down.
|
|
80
|
+
- **Working directory.** Add `--cwd <path>` when operating outside the current
|
|
81
|
+
working directory. A relative `--env-file` path is still read from the
|
|
82
|
+
current directory, not from `--cwd` (`--cwd app --env-file app/.env.ci`):
|
|
83
|
+
Node reads `--env-file` itself before the CLI starts and exits 9 with
|
|
84
|
+
`node: <path>: not found`, and no envelope, when the file is not there.
|
|
85
|
+
- **Server and project.** A command that talks to a project reads
|
|
86
|
+
`ZITADEL_URL`, `ZITADEL_PROJECT_ID` and `ZITADEL_PROJECT_SECRET`, each from the
|
|
87
|
+
first source that has it: `--server` (the URL only), `--env-file <path>`, the
|
|
88
|
+
process environment, `.env.<env>.local`, `.env.local`, `.env.<env>`, `.env`,
|
|
89
|
+
then `.zitadel/secret`. Only the default `development` environment reads
|
|
90
|
+
`.env.local` and `.zitadel/secret` (setup's project); `--env-file` reads that
|
|
91
|
+
file and the process environment only.
|
|
92
|
+
`<env>` is `--env`, else `ZITADEL_ENV`, else a hosting platform's own signal
|
|
93
|
+
(`VERCEL_ENV`, or Netlify's `CONTEXT` inside a Netlify build), else
|
|
94
|
+
`development`. A missing value fails with `E_VALIDATION` naming the variable
|
|
95
|
+
and the files it looked in; there is no cloud default outside `setup`, and
|
|
96
|
+
the server is never read from `zitadel.json`. `zitadel env` shows what resolved and where
|
|
97
|
+
each value came from; `zitadel env add <name>` binds another environment to a
|
|
98
|
+
project. The project it creates has never had a release, so the environment
|
|
99
|
+
serves the server's own defaults until something is deployed to it: run the
|
|
100
|
+
`deploy` the result's `next_commands` names, against the file it wrote
|
|
101
|
+
(`--env-file .env.<name>.local`). A person at a terminal is asked whether to
|
|
102
|
+
deploy there and then; your runs never are, so they never deploy — run that
|
|
103
|
+
`deploy` yourself. `data.deployment` carries the `release_id`,
|
|
104
|
+
`content_hash`, `deployment_id` and `origins` when a person accepted, and is
|
|
105
|
+
null otherwise — a failed deploy is a warning, never a failed `env add`.
|
|
106
|
+
`ZITADEL_URL` and `--server` are the server's origin: the API lives under
|
|
107
|
+
`/api/v1` there, which the CLI adds (and drops from a URL that already ends
|
|
108
|
+
in it).
|
|
109
|
+
- **Origins.** `zitadel origin add <url> --kind primary|preview`, `origin list`
|
|
110
|
+
and `origin rm <url>` manage the URLs a project serves: a `primary` origin is
|
|
111
|
+
where the app is served, a `preview` pattern (it may hold a `*`) is where
|
|
112
|
+
pull-request builds run. They need the project secret, not the preview token.
|
|
113
|
+
An empty list admits every origin, so removing the last one opens the project
|
|
114
|
+
up; `origin rm` asks first and needs `--force` when non-interactive. Removing
|
|
115
|
+
a preview pattern leaves its live previews served until they expire.
|
|
116
|
+
`deploy` lands only on primary origins and fails with `E_VALIDATION` without
|
|
117
|
+
one; its `next_commands` name the step that gives the project one. `setup`
|
|
118
|
+
0.1.0 registered the app's URL (`ZITADEL_ISSUER`) as a `preview` origin,
|
|
119
|
+
which `origin add --kind primary` refuses with `E_CONFLICT`: `doctor` warns
|
|
120
|
+
(`issuer-origin`) and `doctor --fix` makes it primary.
|
|
121
|
+
- **Telemetry.** The CLI sends anonymous usage telemetry by default, and
|
|
122
|
+
agent-driven runs are exactly the ones worth learning from — leave it on.
|
|
123
|
+
Don't add `--no-telemetry` to commands. An opt-out exists for users who need
|
|
124
|
+
it (`ZITADEL_TELEMETRY=0`, or the standard `DO_NOT_TRACK`, in their
|
|
125
|
+
environment), but that is the user's environment choice, not a flag to attach
|
|
126
|
+
to every call.
|
|
127
|
+
- **Flag conventions.** Flags follow what a model already expects from curl, ssh
|
|
128
|
+
and wget: `--help`/`-h`, `--version`, `-v`/`--verbose`, kebab-case long flags,
|
|
129
|
+
both `--flag value` and `--flag=value`, and `--no-telemetry`-style negation.
|
|
130
|
+
Two deliberate deviations worth knowing: `-n` is `--non-interactive` (not
|
|
131
|
+
`--dry-run`, which is long-only), and machine output is `--json` (not
|
|
132
|
+
`--output json`). `api` takes `-X` for the method, as curl does, and `-F` for
|
|
133
|
+
a typed field, as `gh api` and `vercel api` do (not curl's form field).
|
|
134
|
+
- **Writing a resource's body.** A `create` or `update` takes every field of
|
|
135
|
+
its body as a flag, nested ones too: a nested field's flag is its dot path
|
|
136
|
+
joined with `-` (`oidc.client_id` is `--oidc-client-id`), a list is a
|
|
137
|
+
repeated flag, a boolean takes `--no-<flag>` for `false`, and an open record
|
|
138
|
+
(`attributes`) takes repeated `key=value` entries. `--set <path>=<value>`
|
|
139
|
+
(or `<path>:=<json>` for JSON) reaches any field by its dot path, and
|
|
140
|
+
`--data` / `--file` take the whole document. They combine: the document
|
|
141
|
+
first, then the field flags, then each `--set`, with objects merging key by
|
|
142
|
+
key. `resources --json` lists each write's fields with their dot path
|
|
143
|
+
(`name`), `flag` (`null` when only `--set` reaches it), `kind` and
|
|
144
|
+
`required`; `--dry-run --json` returns the body that would be sent. For a
|
|
145
|
+
deeply nested body, writing the document and passing it with `--file -` is
|
|
146
|
+
often simplest.
|
|
147
|
+
- **Calling an operation no command covers.** `zitadel api <operation>` calls
|
|
148
|
+
any operation of the API with the resolved server and `.zitadel/secret`.
|
|
149
|
+
Name it by its `operationId` (`queryUsers`, as the API reference and
|
|
150
|
+
`@zitadel/api` spell it) or by its path as the spec has it (`/users/query`,
|
|
151
|
+
which the server serves at `/api/v1/users/query`; a path copied with the
|
|
152
|
+
prefix works too, and `-X` picks the method where a path has several). Path parameters follow as arguments, in
|
|
153
|
+
path order; a `project_id` left out is the project's own. `-F name=value`
|
|
154
|
+
(or `name:=json`) sets a query parameter when the operation declares one by
|
|
155
|
+
that name, else a dot path into the body, typed by the operation's schema;
|
|
156
|
+
a list parameter takes one `-F` per value. `--input <path>` or `--input -`
|
|
157
|
+
takes the whole body, under the `-F` fields. `--paginate` follows
|
|
158
|
+
`next_page_token` to the last page. `--dry-run` sends nothing and returns
|
|
159
|
+
the request. An operation only an end user's session or the login flow's
|
|
160
|
+
cookie authenticates is refused with `E_VALIDATION`. Prefer a resource
|
|
161
|
+
command where one exists: `api ls --json` names it in each operation's
|
|
162
|
+
`command`.
|
|
163
|
+
- **Never put secrets on the command line.** A property whose name reads as a
|
|
164
|
+
credential (`password`, `client_secret`, `api_key`, …) is refused anywhere on
|
|
165
|
+
argv when it holds free-form text — as a field flag, a `--set` or
|
|
166
|
+
`--attributes` entry, and inside an inline `--data` body alike, since argv is
|
|
167
|
+
visible to other processes and kept in shell history. Send such bodies with
|
|
168
|
+
`--file <path>` or `--file -` (stdin), which never pass through argv. A
|
|
169
|
+
`${{ NAME }}` reference to a project variable is not a secret and may stay on
|
|
170
|
+
argv (a connection's `client_secret` takes nothing else); write it in single
|
|
171
|
+
quotes, since inside double quotes the shell reads `${` as its own expansion.
|
|
172
|
+
- **Streams.** Capture stdout and stderr separately when scripting, into files
|
|
173
|
+
outside the app directory: `setup` scaffolds a new app only into an empty
|
|
174
|
+
directory. The machine contract is one parseable JSON object on stdout;
|
|
175
|
+
installer, audit, and package-manager progress belongs on stderr.
|
|
176
|
+
|
|
177
|
+
## Discovering commands
|
|
178
|
+
|
|
179
|
+
The CLI ships its own command list and per-command flags, always correct for the
|
|
180
|
+
installed version. **Do not guess a command name.** Before running any command you
|
|
181
|
+
are not certain of, look it up first and use the exact name the CLI prints:
|
|
182
|
+
command and resource names — including whether a resource is singular or plural
|
|
183
|
+
and the exact verb — are whatever the CLI shows, never what you would assume. A
|
|
184
|
+
wrong guess costs a failed call and a round of recovery; one cheap lookup up
|
|
185
|
+
front avoids it. `zitadel --help` lists each family of commands (`users`,
|
|
186
|
+
`release`) as one row, so it stays short; `zitadel commands` is the flat list of
|
|
187
|
+
every command, verbs included, and `zitadel <family> --help` lists one family's
|
|
188
|
+
verbs. Treat the CLI's own output as authoritative over anything you remember:
|
|
189
|
+
|
|
190
|
+
```sh
|
|
191
|
+
npx @zitadel/cli@latest --help # every command family, grouped by purpose
|
|
192
|
+
npx @zitadel/cli@latest commands # flat list of all commands
|
|
193
|
+
npx @zitadel/cli@latest <command> --help # a command's flags, which are required, and examples
|
|
194
|
+
npx @zitadel/cli@latest resources --json # the resource surface in one call (see below); contacts no server
|
|
195
|
+
npx @zitadel/cli@latest api ls --json # every API operation, its parameters, body and paging; contacts no server
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
`--help` groups the commands by what you are doing: getting started, local
|
|
199
|
+
development, users and teams, authentication, configuration and releases, and
|
|
200
|
+
diagnostics and utilities. A family such as `users` is one row, and
|
|
201
|
+
`zitadel users --help` lists its verbs. Read the groups from `--help` rather than
|
|
202
|
+
from memory, since they change as the CLI gains commands.
|
|
203
|
+
|
|
204
|
+
Use `zitadel --help` to find *which* family or command to run, and
|
|
205
|
+
`zitadel commands` for the exact verb. Use `resources --json` when
|
|
206
|
+
you need a resource's *fields*: it reports the whole runtime-resource surface at
|
|
207
|
+
once — every resource, its verbs, each verb's `create_fields` / `update_fields`
|
|
208
|
+
(flag name, kind, `required`, any closed value set), its `filter_fields` /
|
|
209
|
+
`sort_fields`, and the `delete_outcome` a destructive verb returns — so prefer it
|
|
210
|
+
over reading `<resource> --help` one at a time. A `list` verb's envelope carries
|
|
211
|
+
`data.count`, the number of items it returned. A list returns one page unless
|
|
212
|
+
you pass `--all`, so a "how many …?" question is one `list --all --json` call:
|
|
213
|
+
read the count, do not page and tally.
|
|
214
|
+
|
|
215
|
+
To find the users still on an older revision of a schema, filter on the
|
|
216
|
+
schema's object type and exclude the newest revision's URL:
|
|
217
|
+
`users list --filter schema_object_type=equals:<type> --filter
|
|
218
|
+
schema=not_equals:<newest-url> --all --json`.
|
|
219
|
+
|
|
220
|
+
## Reading the envelope
|
|
221
|
+
|
|
222
|
+
Each invocation prints one JSON object:
|
|
223
|
+
|
|
224
|
+
- `status`: `ok` | `skipped` | `error`.
|
|
225
|
+
- `cli_version`, `command`, `source`: always present.
|
|
226
|
+
- On success: `data` with the command-specific payload.
|
|
227
|
+
- On a no-op: `reason` (e.g. `already-initialized`, `already-claimed`,
|
|
228
|
+
`nothing-to-eject`).
|
|
229
|
+
- On failure: `code` (e.g. `E_VALIDATION`, `E_NETWORK`, `E_NOT_FOUND`,
|
|
230
|
+
`E_CONFLICT`) and `message`, usually with a `hint`, and `details` when there
|
|
231
|
+
is structured context.
|
|
232
|
+
- `next_commands`: the suggested follow-ups, inside `data` on success and at
|
|
233
|
+
the top level on a no-op or failure. Prefer these over free-text hints.
|
|
234
|
+
- `warnings`: things that went wrong without stopping the command, such as a
|
|
235
|
+
value that was not published. Always present on success (often empty), and
|
|
236
|
+
present on a no-op or failure when there are any. Read them before you report
|
|
237
|
+
success: a warning usually means a follow-up step is needed.
|
|
238
|
+
- A `list` verb emits `data: { items, count, next_page_token }`; when a page
|
|
239
|
+
remains, `data.next_commands` carries the exact command for the next page.
|
|
240
|
+
- `deploy` emits `data.content_hash` (the release digest), `data.origins`
|
|
241
|
+
(the origins it went live on) and `data.resources` (how many it pinned). A
|
|
242
|
+
re-run on unchanged config returns the same digest. Use it to confirm what
|
|
243
|
+
went live.
|
|
244
|
+
- `preview` is `deploy` for a pull-request build: it deploys the release to the
|
|
245
|
+
build's preview URLs (from the platform, or `--origin`) for `--ttl` and emits
|
|
246
|
+
`data.previews` plus `data.release_env` (the `NEXT_PUBLIC_ZITADEL_RELEASE`
|
|
247
|
+
line to pin). It uses `ZITADEL_PROJECT_SECRET` until the server accepts the narrower preview token (#1575), never the other credential, and
|
|
248
|
+
outside a preview or development build it is a no-op — even with an explicit
|
|
249
|
+
`--origin`, which selects targets but never authorizes a preview deploy. A CI
|
|
250
|
+
build other than Vercel or Netlify must set `ZITADEL_ENV=preview`, or it is
|
|
251
|
+
skipped too. `preview list` and `preview rm <url>` read and end live previews.
|
|
252
|
+
- `deploy` and `preview` record `-m` on the release and the deployment. Omitted,
|
|
253
|
+
they use a GitHub pull request's title, Vercel's commit message, or a clean
|
|
254
|
+
worktree's commit subject; with none, a non-interactive `deploy` fails. A
|
|
255
|
+
blank `-m` is refused. A release keeps the message of
|
|
256
|
+
the run that first built its content, usually the pull request's `preview`.
|
|
257
|
+
`deployment rollback -m` records why on the rollback.
|
|
258
|
+
- `deployment get <id>` emits the deployment as the API serves it; on a
|
|
259
|
+
terminal it also shows the variables the deployment froze, secrets masked.
|
|
260
|
+
Read them as JSON with `variable resolve`.
|
|
261
|
+
- `deployment rollback` emits `data.release_id` (the release live again),
|
|
262
|
+
`data.origins` and `data.pointers`, the `{kind, handle, revision_id}`
|
|
263
|
+
revisions that release pins; the `schema` one is what new registrations use.
|
|
264
|
+
Users keep their schema revision and attributes: a rollback does not migrate
|
|
265
|
+
them back. Without `data.pointers`, a warning says the release could not be
|
|
266
|
+
read.
|
|
267
|
+
- `deploy --dry-run` and `deploy` emit `data.diff`, the git diff the terminal
|
|
268
|
+
shows, against the live release's commit (`diff.against`): `diff.files` holds
|
|
269
|
+
one `{path, status, previous_path?, patch}` per changed file (`status` is
|
|
270
|
+
`added`, `modified`, `deleted`, `renamed` or `copied`; `patch` is its unified
|
|
271
|
+
diff), including rapid same-size edits and untracked configuration files.
|
|
272
|
+
Review it before you deploy. `files` is `[]` when nothing changed and
|
|
273
|
+
`null` when there is nothing to diff against, with `diff.note` saying why.
|
|
274
|
+
- A write verb's `--dry-run` reports what it would send without calling the
|
|
275
|
+
platform, so you can preview a mutation before making it.
|
|
276
|
+
- `api` emits the response body as `data`, exactly as the API reference
|
|
277
|
+
documents it (`null` for a response without one); with `--paginate`, the
|
|
278
|
+
page's list holds every page's items. `api --dry-run` emits the request
|
|
279
|
+
instead: `{operation, method, url, body?}`. A refusal from the server is the
|
|
280
|
+
error envelope, with its status and body in `details`.
|
|
281
|
+
|
|
282
|
+
Common error codes point at their own fix in `next_commands`:
|
|
283
|
+
|
|
284
|
+
- `E_VALIDATION`: the body or flags are wrong; `message` names what, and
|
|
285
|
+
`details` often lists the missing or available values.
|
|
286
|
+
- `E_CREDENTIAL_MISSING`: a connection's `client_id` or `client_secret` names a
|
|
287
|
+
project variable that does not exist; `details.variable` is the name. Publish it
|
|
288
|
+
with `variable set NAME` (add `--secret` for the secret), then retry.
|
|
289
|
+
- `E_LOCAL_SERVER_NOT_RUNNING`: start the local runtime, then retry with
|
|
290
|
+
`--server local`.
|
|
291
|
+
- `E_NOT_FOUND`: an HTTP 404 — a missing resource, an operation the server's
|
|
292
|
+
version does not have (`details.body.code` is `req.not_found`; a server
|
|
293
|
+
from before the API moved under `/api/v1` answers every call so), or a
|
|
294
|
+
`--server` that is not a Zitadel platform API. Follow `next_commands`.
|
|
295
|
+
- `E_PORT_IN_USE`: the requested local runtime port already has a listener; stop
|
|
296
|
+
that process or choose another port.
|
|
297
|
+
|
|
298
|
+
In human mode the output follows the terminal (TTY table, or tab-separated lines
|
|
299
|
+
when piped or `--plain`); `--json` is unaffected and remains the contract. A
|
|
300
|
+
human run lists its next steps under `Next:` after its result (`setup` shows
|
|
301
|
+
them in its summary box instead); where stdout is data a script reads (a piped
|
|
302
|
+
or `--plain` list, a resource's created or updated JSON) they go to stderr, so
|
|
303
|
+
stdout holds only that data.
|
|
304
|
+
|
|
305
|
+
Text the server returns is escaped before it is printed, in `--json` as in human
|
|
306
|
+
mode, so a stored value cannot drive the reader's terminal. Control, format and
|
|
307
|
+
bidi characters appear as visible `\xNN`, `\uNNNN` or `\u{NNNNN}` text, so a
|
|
308
|
+
value that contained one is not the stored value byte for byte — do not send it
|
|
309
|
+
back in an update as if it were. When you need the exact value, call the platform
|
|
310
|
+
API directly with the project secret.
|
|
311
|
+
|
|
312
|
+
Exit codes mirror the error class (3 = validation, 4 = network or not-found,
|
|
313
|
+
5 = conflict, 1 = auth, 2 = not-implemented, 130 = cancelled). `E_NETWORK`
|
|
314
|
+
covers a server that answered 5xx and one that never answered — refused,
|
|
315
|
+
unresolvable, or silent past the 30-second request deadline. Ctrl-C exits
|
|
316
|
+
130: a command waiting on the server, or on an answer at a prompt, stops at
|
|
317
|
+
once with `E_CANCELLED`, and any other command exits within a second, possibly
|
|
318
|
+
without an envelope. In human mode a cancel prints no error block. An
|
|
319
|
+
unknown command is handled by the CLI's help layer, not the envelope.
|
|
320
|
+
|
|
321
|
+
## Clarify before you configure
|
|
322
|
+
|
|
323
|
+
Driving the CLI non-interactively does not mean deciding *for* the user. When
|
|
324
|
+
the user asks to add authentication but has not said *how*, don't silently pick
|
|
325
|
+
defaults — ask them first, in one short batched question, then run the CLI
|
|
326
|
+
non-interactively with their answers. The two things sit on different sides of
|
|
327
|
+
the conversation: you clarify with the human, then drive the CLI headlessly.
|
|
328
|
+
|
|
329
|
+
Ask about:
|
|
330
|
+
|
|
331
|
+
- **Sign-in methods** — password (the usual default), passkeys, and single
|
|
332
|
+
sign-on. If they want SSO, which providers? A provider the CLI does not list
|
|
333
|
+
by name is still possible: run `zitadel auth-method sso enable --help` to see
|
|
334
|
+
whether the installed CLI takes one, and which settings it needs, and ask for
|
|
335
|
+
those too.
|
|
336
|
+
- **Profile fields** — which attributes sign-up collects. The starting field
|
|
337
|
+
set depends on the use-case you pick (see *Presets and use-cases* below) —
|
|
338
|
+
the default (`minimal`) is the login identifier only, **not** names. Confirm
|
|
339
|
+
which fields they want (first/last name, phone, username, display name) and
|
|
340
|
+
choose the use-case or edit the schema accordingly.
|
|
341
|
+
|
|
342
|
+
Offer only what the installed CLI actually supports: read the real sign-in
|
|
343
|
+
presets, profile fields, and SSO options from the CLI first (`setup --help`,
|
|
344
|
+
`resources --json`) and present those, not a list you remember — the options
|
|
345
|
+
move with the CLI version.
|
|
346
|
+
|
|
347
|
+
## Presets and use-cases — map the answers, don't trust the names
|
|
348
|
+
|
|
349
|
+
`setup` has two independent axes, and their names do **not** mean what they look
|
|
350
|
+
like. Read the current sets from `setup --help`, but know the traps:
|
|
351
|
+
|
|
352
|
+
- **Use-case = which profile fields the schema collects.** `minimal` is the
|
|
353
|
+
login identifier **only** — it does *not* include names. First + last name is
|
|
354
|
+
`--use-case consumer` (adds `givenName` + `familyName`); add a company with
|
|
355
|
+
`--use-case business`. Choosing `minimal` because the developer said "keep it
|
|
356
|
+
minimal" leaves an identifier-only schema that ignores the names they asked
|
|
357
|
+
for.
|
|
358
|
+
- **Preset = how they sign in (the login flow + auth methods).**
|
|
359
|
+
`password-first` makes password the primary step **but still leaves passkeys
|
|
360
|
+
enabled**; `passkey-first` leads with a passkey. Neither one gives you
|
|
361
|
+
password-only. If the developer does not want passkeys, turn them off
|
|
362
|
+
yourself: set `x-auth-methods.passkey.enabled` to `false` in
|
|
363
|
+
`.zitadel/schemas/*.json`, then `deploy`.
|
|
364
|
+
|
|
365
|
+
Translate the developer's answers into the right axis values, then **verify
|
|
366
|
+
against what actually deployed**: read the live schema back
|
|
367
|
+
(`schemas get <objectType> --json`) and confirm the fields and auth methods
|
|
368
|
+
match what they asked for — don't assume a preset name did it.
|
|
369
|
+
|
|
370
|
+
Skip the questions and proceed with the defaults above — stating the assumptions
|
|
371
|
+
you made so the user can correct them — in exactly two cases: the user already
|
|
372
|
+
told you what they want, or you are running non-interactively (a one-shot/headless
|
|
373
|
+
invocation) or were explicitly told to just set it up.
|
|
374
|
+
|
|
375
|
+
## Answering the user
|
|
376
|
+
|
|
377
|
+
Report what changed, in the user's terms — not how you drove the CLI. The
|
|
378
|
+
flags, `--json`, the deploy flow, exit codes, and the number of
|
|
379
|
+
commands you ran are your internal business; the user asked for an outcome, not
|
|
380
|
+
a transcript. Say what they got.
|
|
381
|
+
|
|
382
|
+
- Good: "Added an optional phone number to your user schema and deployed it."
|
|
383
|
+
- Avoid: "I ran `deploy` with `--non-interactive --json` and it returned ok."
|
|
384
|
+
|
|
385
|
+
Quote a command or flag only when the user asks *how* something works, or when
|
|
386
|
+
they need to run something themselves. Otherwise keep the mechanics out of the
|
|
387
|
+
answer.
|
|
388
|
+
|
|
389
|
+
## Golden path
|
|
390
|
+
|
|
391
|
+
The high-level workflow is stable even as individual flags evolve — run each
|
|
392
|
+
command's `--help` for its current flags (for example, `setup --help` lists the
|
|
393
|
+
framework, sign-in preset, and profile-field options):
|
|
394
|
+
|
|
395
|
+
```sh
|
|
396
|
+
npx @zitadel/cli@latest doctor --non-interactive --json
|
|
397
|
+
npx @zitadel/cli@latest start --non-interactive --json
|
|
398
|
+
npx @zitadel/cli@latest setup --framework next --server local --non-interactive --json
|
|
399
|
+
npx @zitadel/cli@latest doctor --non-interactive --json
|
|
400
|
+
npx @zitadel/cli@latest deploy -m "<why this release changes>" --non-interactive --json
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
`setup` commits the project it scaffolds: `git init` first when the directory
|
|
404
|
+
is not yet a repository, then one commit, `chore: scaffold the project with
|
|
405
|
+
zitadel setup`. A release records the commit it was built from and `deploy`
|
|
406
|
+
diffs `.zitadel/` against it, so without this the first release carries no
|
|
407
|
+
commit and the next deploy reports no diff.
|
|
408
|
+
|
|
409
|
+
Then setup deploys `.zitadel/` to the app's origin, so the sign-in it asks you
|
|
410
|
+
to verify is served the release it built. `data.deployment` carries the
|
|
411
|
+
`release_id`, `content_hash`, `deployment_id` and `origins`, and is null on
|
|
412
|
+
`--dry-run` or when the deploy failed. A failed deploy is a warning, never a
|
|
413
|
+
failed setup, and `data.next_commands` then leads with the `deploy` that fixes
|
|
414
|
+
it.
|
|
415
|
+
|
|
416
|
+
Setup refuses with `E_CONFLICT` when the repository already has uncommitted
|
|
417
|
+
work, untracked files included, which `details.untracked` lists separately
|
|
418
|
+
from `details.modified`. The commit covers the project directory, so it can
|
|
419
|
+
only tell its own files from yours when the tree starts clean. It never
|
|
420
|
+
commits `.env*` files (other than `.env.example`), `.zitadel/secret` or
|
|
421
|
+
`.zitadel/local/`, whatever their case. Commit or stash
|
|
422
|
+
first, then rerun. Only what `start` leaves is exempt: `.zitadel/local/` and
|
|
423
|
+
the `.gitignore` line that ignores it. A connection under `.zitadel/idps/` is
|
|
424
|
+
yours and is not exempt. During a merge, rebase, cherry-pick, revert or bisect,
|
|
425
|
+
or on a detached HEAD, setup refuses with `E_CONFLICT` before it writes
|
|
426
|
+
anything, naming what stops the commit.
|
|
427
|
+
|
|
428
|
+
Setup deploys nothing, and it does not need to: a project with no release
|
|
429
|
+
resolves the newest revisions, which are the ones setup just uploaded, so
|
|
430
|
+
sign-in already runs on `.zitadel/`. Deploying pins them into a release, which
|
|
431
|
+
is what the server will require once that fallback goes (#1616). The publish
|
|
432
|
+
step says so and names the command. `data.git` reports what setup did:
|
|
433
|
+
`initialized` when it ran `git init`, even if the commit then failed, and the
|
|
434
|
+
`commit`, or null. `--no-git` skips both the refusal
|
|
435
|
+
and the commit, and nothing prompts, so this is the same interactively and in
|
|
436
|
+
CI.
|
|
437
|
+
|
|
438
|
+
`setup` scaffolds into an empty directory, or patches an existing app project
|
|
439
|
+
where it detects a framework. What `start` leaves in a directory —
|
|
440
|
+
`.zitadel/local/` and the `.gitignore` line that ignores it — still counts as
|
|
441
|
+
empty, so the golden path runs `setup` right after `start` in the same
|
|
442
|
+
directory. A `--cwd` that does not exist yet counts as empty too: setup creates
|
|
443
|
+
it with the app, and removes it again when setup fails before the app is kept.
|
|
444
|
+
A `--cwd` that is a file fails with `E_VALIDATION`. Any other non-empty
|
|
445
|
+
directory stops setup with a hint to pass `--force`. Pass it only for a
|
|
446
|
+
directory whose contents may be replaced: files
|
|
447
|
+
the new app also has, such as `package.json`, are overwritten. Without a
|
|
448
|
+
terminal `setup` cannot ask which framework to scaffold, so pass `--framework`;
|
|
449
|
+
the error lists the choices in `details.frameworks`. `setup --dry-run` creates
|
|
450
|
+
no app: where it would scaffold one, it previews the project, and
|
|
451
|
+
`data.files_written` is empty, because those files depend on the generated app.
|
|
452
|
+
`setup` also writes and publishes the password policy
|
|
453
|
+
(`.zitadel/policies/auth.factor.password.set.json`) with a minimum length of
|
|
454
|
+
15, NIST's minimum for password-only sign-in and the wizard's default. Pass
|
|
455
|
+
`--password-min-length <n>` only when the user asks for another value (`0` sets
|
|
456
|
+
none), and give test users passwords at least that long.
|
|
457
|
+
When setup fails after scaffolding a new app, before its default schema and
|
|
458
|
+
flow are uploaded, it removes the app again and puts back what the directory
|
|
459
|
+
held, so rerunning the same `setup` starts over.
|
|
460
|
+
|
|
461
|
+
`start` reuses a server already running in the directory only when it is the
|
|
462
|
+
release this CLI ships; one an earlier release started is restarted, and its
|
|
463
|
+
data stays. `setup --server local` refuses a binary-runtime server another
|
|
464
|
+
release started with `E_CONFLICT` before it creates anything: that server's API
|
|
465
|
+
is its own release's. Its `next_commands` run `start`, then `setup` again.
|
|
466
|
+
|
|
467
|
+
In an existing app, `--framework` has to name the framework setup detects
|
|
468
|
+
there; another one stops setup with `E_CONFLICT` instead of offering
|
|
469
|
+
`--force`. Setup reports its conflicts with the app before it creates a
|
|
470
|
+
project, and the error's `hint` says whether `--force` resolves one: it
|
|
471
|
+
replaces a file at a path setup writes, but not the app's root component or
|
|
472
|
+
request boundary at another path.
|
|
473
|
+
|
|
474
|
+
The app may also live in a subdirectory of the one `start` ran in: `setup`,
|
|
475
|
+
`console`, `logs`, `--server local` and `doctor` read `.zitadel/local/` from the working
|
|
476
|
+
directory or the nearest parent that has it (up to the home directory), so
|
|
477
|
+
`mkdir my-app && cd my-app` after `start` works too. `doctor` checks that
|
|
478
|
+
server and its port, but runs its project checks in the working directory, so run
|
|
479
|
+
it from the app. When that server is unhealthy it suggests `start` and
|
|
480
|
+
`reset --force` with `--cwd` set to the directory that holds it (when that path needs
|
|
481
|
+
shell quoting the commands are left out, the hint names the directory, and
|
|
482
|
+
`doctor --json` returns the argument lists in `details.suggested_args` to run as they are).
|
|
483
|
+
The hint says the server is the parent's and that `reset --force` deletes the data in that
|
|
484
|
+
directory, and it adds a plain `start`, which gives the working directory a server of its own
|
|
485
|
+
because `start` does not look in parents; a healthy parent server is reported in the `runtime`
|
|
486
|
+
check as started in the parent folder. A
|
|
487
|
+
local server is healthy once its `/readyz` answers, which takes its database
|
|
488
|
+
answering too: `start` waits for that, and the commands that read a server
|
|
489
|
+
treat one whose database does not answer as unhealthy. `stop`,
|
|
490
|
+
`reset` and `status` read only the working directory's
|
|
491
|
+
`.zitadel/local/`, so run them where `start` ran. On a local
|
|
492
|
+
server that hosts the platform project, `setup` attaches the project to the
|
|
493
|
+
local admin's team and ends `data.next_commands` with `console`; when it finds
|
|
494
|
+
no local admin for that server on the way up, it says so in the envelope's
|
|
495
|
+
`warnings` and the project stays unattached, so the local console does not
|
|
496
|
+
list it.
|
|
497
|
+
|
|
498
|
+
For a project set up against a hosted server, `doctor` checks no local
|
|
499
|
+
runtime, unless a local server was started in its directory or a parent, or
|
|
500
|
+
`--port`, `--runtime` or `--image` asks about one.
|
|
501
|
+
|
|
502
|
+
When `setup --sso` or `auth-method sso enable` adds a social sign-in provider, the user
|
|
503
|
+
must register the connection's redirect URI with the provider themselves: give
|
|
504
|
+
them `data.sso[].callback_uri` from `setup`, or `data.callback_uri` from
|
|
505
|
+
`auth-method sso enable`, verbatim. Each connection has its own URI, so a URI from another
|
|
506
|
+
provider or an older CLI version does not work.
|
|
507
|
+
|
|
508
|
+
After `setup`, follow `data.next_commands` to start the app. Prove the generated
|
|
509
|
+
auth flow in a visible browser by registering a unique user, logging out, logging
|
|
510
|
+
back in with the same email/password, and ending on the signed-in profile page.
|
|
511
|
+
Do not treat a rendered login or registration form as completion.
|
|
512
|
+
|
|
513
|
+
When a step against the local server fails without saying why (the login says a
|
|
514
|
+
sign-in provider "is not available right now", or a request returns 500), the
|
|
515
|
+
server logged the cause. Read it with `logs --level warn --json`: `data.logs`
|
|
516
|
+
holds the warnings and errors, and `--tail` counts only those lines. `logs
|
|
517
|
+
--follow` streams the log for a person at a terminal, with warnings yellow and
|
|
518
|
+
errors red; it refuses `--json`. `start`, and `doctor` when it finds the server
|
|
519
|
+
running, name it in `data.next_actions`.
|
|
520
|
+
|
|
521
|
+
To move a set-up app to another dev port (setup's `warnings` say the port is
|
|
522
|
+
already in use, the dev server exits with `EADDRINUSE`, or another app needs
|
|
523
|
+
the port), run `setup --dev-port <port>` in the app; never change the port in
|
|
524
|
+
one file by hand. In a directory with a
|
|
525
|
+
`zitadel.json`, `setup` returns `status: "skipped"` with `reason:
|
|
526
|
+
"already-initialized"`, unless `--dev-port` is given: then it moves the app.
|
|
527
|
+
It allows the new origin on the server first, and a refusal changes nothing.
|
|
528
|
+
It then rewrites the dev script or dev-server config, `ZITADEL_ISSUER` in
|
|
529
|
+
`.env.local`, `zitadel.json`, `.zitadel/secret` and the README/AGENTS.md
|
|
530
|
+
guidance, and removes the old origin. `data.previous_issuer` and `data.issuer`
|
|
531
|
+
name both origins, and `data.moved` is `false` when the app was already on that
|
|
532
|
+
port. A social sign-in provider still redirects to the old port: `warnings`
|
|
533
|
+
names the redirect URI to add at each provider. Restart the dev server
|
|
534
|
+
afterwards.
|
|
535
|
+
|
|
536
|
+
To change how users sign in after setup, use the `auth-method` commands. Each
|
|
537
|
+
sign-in method has its own command, with `--schema` when the project has more
|
|
538
|
+
than one:
|
|
539
|
+
|
|
540
|
+
- `auth-method password enable` and `auth-method password disable`
|
|
541
|
+
- `auth-method passkey enable` and `auth-method passkey disable`
|
|
542
|
+
- `auth-method sso enable --provider google` adds a provider. It prompts for
|
|
543
|
+
the client id and secret, or takes `--client-id` with the secret piped on
|
|
544
|
+
stdin when non-interactive. Any other OpenID Connect provider is
|
|
545
|
+
`--provider custom` with `--slug`, `--display-name`, `--issuer` and optionally
|
|
546
|
+
`--scopes` (default `openid profile email`).
|
|
547
|
+
In a terminal, a custom provider's settings you leave out are asked for, after
|
|
548
|
+
the checks that need no answer; a connection that already has that slug
|
|
549
|
+
supplies its own. A scripted, `--json`, `--dry-run` or `--non-interactive`
|
|
550
|
+
run must pass all three and gets an error naming the missing ones. After
|
|
551
|
+
writing a new connection the text output prints the claim mappings and names
|
|
552
|
+
the file to edit them in; `--json` does not carry them, so read the
|
|
553
|
+
connection file.
|
|
554
|
+
An issuer on `http://localhost:<port>` (a test provider on this machine)
|
|
555
|
+
works against a server `zitadel start` runs as a process: `start` sets
|
|
556
|
+
`NEXTGEN_HTTPCLIENT_ALLOW_LIST=localhost,127.0.0.0/8,::1/128` unless
|
|
557
|
+
`.env.local`, `.env` or the shell sets it, and setting it empty turns it off.
|
|
558
|
+
Under `--runtime docker` localhost is the container, so the provider is not
|
|
559
|
+
reachable. Otherwise `sso enable` warns in `warnings` that sign-in will fail.
|
|
560
|
+
`auth-method sso disable --provider google` removes a provider; a custom one
|
|
561
|
+
is named by its slug, as in `auth-method sso disable --provider acme-sso`.
|
|
562
|
+
|
|
563
|
+
The commands change only local files, apart from
|
|
564
|
+
`auth-method sso enable`, which also stores the provider's credentials on the
|
|
565
|
+
project. A provider added by `sso enable` is configured locally, not live: its
|
|
566
|
+
`data.title` says so, `data.next_actions` says to deploy, and `data.next_commands`
|
|
567
|
+
holds `deploy --dry-run` then `deploy`. Only run those once the provider's OAuth
|
|
568
|
+
application exists. `data.title` claims the credentials are stored on the project
|
|
569
|
+
only when they reached it. When they refuse, they write and publish nothing, on a dry run too.
|
|
570
|
+
They refuse to disable a method a login flow still asks for, to change a method
|
|
571
|
+
while a login flow has errors that prevent checking it, to enable password on a
|
|
572
|
+
schema without `x-identifier`, to edit a value that is not the shape they edit,
|
|
573
|
+
and to edit a schema that points at an external URL. Disabling the schema's last
|
|
574
|
+
way to sign in needs `--force` when non-interactive; only pass it for a schema
|
|
575
|
+
whose users are managed through the API, and ask the user first. That refusal's
|
|
576
|
+
`details.suggested_args` lists the `--force` re-run first, then the methods that
|
|
577
|
+
could be enabled instead. A dry run of the change is not refused: it previews it
|
|
578
|
+
and warns that no way to sign in would be left. A method that is enabled but not
|
|
579
|
+
offered by any active flow is reported in `warnings` (and in `data.not_offered`
|
|
580
|
+
for password and passkey). Every command reports `data.changed`, the
|
|
581
|
+
project-relative files it wrote (on a dry run, the files it would write), empty
|
|
582
|
+
when nothing changed. Follow `data.next_commands` (`deploy --dry-run`, `deploy`, and
|
|
583
|
+
`variable set` when a credential did not reach the project) to publish the
|
|
584
|
+
change. When a path or schema name would need shell quoting, `next_commands` is
|
|
585
|
+
empty and `data.next_args` (or `details.suggested_args` on a refusal) holds the
|
|
586
|
+
same commands as argument lists.
|
|
587
|
+
|
|
588
|
+
Repo config is authoritative: edit `zitadel.json` or files under `.zitadel/`,
|
|
589
|
+
then re-run `deploy`. See the reference below for driving the login UI
|
|
590
|
+
and the config-sync details. The editor schemas in `.zitadel/meta/` are
|
|
591
|
+
generated by the CLI, not config: never edit them. `doctor` warns when one is
|
|
592
|
+
missing or outdated (after an upgrade that changed a dialect), and
|
|
593
|
+
`doctor --fix` writes the current ones, unless the project pins a newer
|
|
594
|
+
`@zitadel/*` version (run that version's `doctor --fix`) or a symlink is on
|
|
595
|
+
the path; `deploy --dry-run` validates without them either way.
|
|
596
|
+
`deploy`, `preview` and `release create`, `--dry-run` included, check the
|
|
597
|
+
shape of `.zitadel/` locally before anything is written, and fail with
|
|
598
|
+
`E_VALIDATION` listing each problem by field and by the schema's or flow's
|
|
599
|
+
handle (a policy by its file). The server still applies its own release rules
|
|
600
|
+
when the build is submitted, such as handles being unique, so a passing dry run
|
|
601
|
+
does not promise the release is accepted.
|
|
602
|
+
|
|
603
|
+
An SPA's dev proxy (`vite.config.*`, Angular's `proxy.conf.cjs`) keeps the
|
|
604
|
+
app's session in a cookie of its own project, `zitadel_session_<project id>`
|
|
605
|
+
on `http://localhost` and `__Host-zitadel_session_<project id>` over https.
|
|
606
|
+
`doctor` fails (`managed-files`, `outdated managed config wiring`) on the
|
|
607
|
+
session-cookie hooks a CLI wrote before these names: the server no longer
|
|
608
|
+
reads the cookies they keep. `doctor --fix` replaces hooks that are as the CLI
|
|
609
|
+
wrote them; ones changed by hand must be updated by hand, which the failure
|
|
610
|
+
says. Browsers signed in before the change sign in again.
|
|
611
|
+
|
|
612
|
+
## Claiming a project
|
|
613
|
+
|
|
614
|
+
A project on a hosted server stays temporary until a person claims it, within
|
|
615
|
+
14 days of `setup`. Claiming signs that person in with their own Zitadel
|
|
616
|
+
account in a browser, so it is their step: hand them the link, and never open
|
|
617
|
+
it yourself. Plain `claim` waits for the browser step and prints nothing on
|
|
618
|
+
stdout until it ends, so run `claim --no-wait --non-interactive --json`
|
|
619
|
+
instead. It returns at once with `data.claim_url`, the link to give the person
|
|
620
|
+
verbatim, and `data.expires_at`, when it stops working (about 10 minutes).
|
|
621
|
+
Then run the `claim --challenge-id …` command in `data.next_commands`: it
|
|
622
|
+
waits for the person to finish, returns `data.dashboard_url`, and records the
|
|
623
|
+
owning team in `.zitadel/secret`. When the link expires, or `--challenge-id`
|
|
624
|
+
names one the platform does not know (`E_NOT_FOUND`), `next_commands` starts a
|
|
625
|
+
new `--no-wait` link. A project that already has a team is `skipped` with
|
|
626
|
+
`reason: "already-claimed"`; one past its 14 days fails with `E_VALIDATION`
|
|
627
|
+
and no `next_commands`, since a new link cannot reopen it.
|
|
628
|
+
|
|
629
|
+
## Reference files
|
|
630
|
+
|
|
631
|
+
Load on demand:
|
|
632
|
+
|
|
633
|
+
- [`references/driving-login-ui.md`](references/driving-login-ui.md) — driving the
|
|
634
|
+
`<zitadel-login>` component in a browser, and the authoritative repo-config
|
|
635
|
+
workflow (schemas, flows, branding, policies and their tests).
|