@zitadel/cli 0.1.0 → 0.2.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 +2102 -836
- package/dist/index.mjs +8674 -9700
- package/dist/index.mjs.map +1 -1
- package/dist/lib/oclif/help.mjs +66 -20
- package/dist/lib/oclif/help.mjs.map +1 -1
- package/dist/skills-hint-CBoak9HL.mjs +4348 -0
- package/dist/skills-hint-CBoak9HL.mjs.map +1 -0
- package/oclif.manifest.json +4022 -1230
- package/package.json +49 -18
- package/skills/zitadel-cli/SKILL.md +151 -55
- package/skills/zitadel-cli/references/driving-login-ui.md +35 -15
- package/dist/skills-hint-BSspFzhr.mjs +0 -173
- package/dist/skills-hint-BSspFzhr.mjs.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zitadel/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Agent-friendly Zitadel CLI",
|
|
5
5
|
"homepage": "https://github.com/zitadel/nextgen/tree/main/apps/cli#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -49,31 +49,31 @@
|
|
|
49
49
|
],
|
|
50
50
|
"topics": {
|
|
51
51
|
"users": {
|
|
52
|
-
"description": "
|
|
52
|
+
"description": "Create and manage users."
|
|
53
53
|
},
|
|
54
54
|
"teams": {
|
|
55
|
-
"description": "
|
|
55
|
+
"description": "Create and manage teams."
|
|
56
56
|
},
|
|
57
57
|
"sessions": {
|
|
58
|
-
"description": "
|
|
58
|
+
"description": "View and revoke sessions."
|
|
59
59
|
},
|
|
60
60
|
"events": {
|
|
61
|
-
"description": "
|
|
61
|
+
"description": "View events."
|
|
62
62
|
},
|
|
63
63
|
"grants": {
|
|
64
|
-
"description": "Manage
|
|
64
|
+
"description": "Manage access grants."
|
|
65
65
|
},
|
|
66
66
|
"projects": {
|
|
67
|
-
"description": "
|
|
67
|
+
"description": "View and update projects."
|
|
68
68
|
},
|
|
69
69
|
"schemas": {
|
|
70
|
-
"description": "
|
|
70
|
+
"description": "View user schemas."
|
|
71
71
|
},
|
|
72
72
|
"branding": {
|
|
73
|
-
"description": "
|
|
73
|
+
"description": "Manage the login branding."
|
|
74
74
|
},
|
|
75
75
|
"auth-method": {
|
|
76
|
-
"description": "
|
|
76
|
+
"description": "Configure how users sign in."
|
|
77
77
|
},
|
|
78
78
|
"auth-method:password": {
|
|
79
79
|
"description": "Turn password sign-in on or off."
|
|
@@ -83,6 +83,36 @@
|
|
|
83
83
|
},
|
|
84
84
|
"auth-method:sso": {
|
|
85
85
|
"description": "Add or remove identity providers."
|
|
86
|
+
},
|
|
87
|
+
"origin": {
|
|
88
|
+
"description": "Manage the project's allowed origins."
|
|
89
|
+
},
|
|
90
|
+
"release": {
|
|
91
|
+
"description": "Build and manage releases."
|
|
92
|
+
},
|
|
93
|
+
"deployment": {
|
|
94
|
+
"description": "Deploy releases and roll them back."
|
|
95
|
+
},
|
|
96
|
+
"preview": {
|
|
97
|
+
"description": "Deploy and manage pull-request preview URLs."
|
|
98
|
+
},
|
|
99
|
+
"variable": {
|
|
100
|
+
"description": "Manage project variables."
|
|
101
|
+
},
|
|
102
|
+
"idps": {
|
|
103
|
+
"description": "Manage identity provider connections."
|
|
104
|
+
},
|
|
105
|
+
"flow-definitions": {
|
|
106
|
+
"description": "View login flows."
|
|
107
|
+
},
|
|
108
|
+
"policies": {
|
|
109
|
+
"description": "View policies."
|
|
110
|
+
},
|
|
111
|
+
"env": {
|
|
112
|
+
"description": "Show and bind environments."
|
|
113
|
+
},
|
|
114
|
+
"api": {
|
|
115
|
+
"description": "Call any operation of the Zitadel API."
|
|
86
116
|
}
|
|
87
117
|
}
|
|
88
118
|
},
|
|
@@ -97,20 +127,20 @@
|
|
|
97
127
|
"@oclif/plugin-search": "^1.2.50",
|
|
98
128
|
"@oclif/plugin-version": "^2.2.46",
|
|
99
129
|
"@oclif/plugin-which": "^3.2.55",
|
|
100
|
-
"@zitadel/api": "0.
|
|
101
|
-
"@zitadel/config": "0.
|
|
102
|
-
"@zitadel/server": "0.
|
|
130
|
+
"@zitadel/api": "0.2.0",
|
|
131
|
+
"@zitadel/config": "0.2.0",
|
|
132
|
+
"@zitadel/server": "0.2.0",
|
|
103
133
|
"ajv": "^8.20.0",
|
|
104
134
|
"consola": "^3.4.2",
|
|
105
135
|
"magicast": "^0.5.3",
|
|
106
136
|
"mixpanel": "^0.18.1",
|
|
137
|
+
"picocolors": "^1.1.1",
|
|
138
|
+
"rosie-skills": "^0.8.5",
|
|
107
139
|
"safe-stable-stringify": "^2.5.0",
|
|
108
140
|
"semver": "^7.7.3",
|
|
109
141
|
"undici": "^7.29.1",
|
|
110
142
|
"wrap-ansi": "^7.0.0",
|
|
111
|
-
"zod": "^4.3.6"
|
|
112
|
-
"picocolors": "^1.1.1",
|
|
113
|
-
"rosie-skills": "^0.8.5"
|
|
143
|
+
"zod": "^4.3.6"
|
|
114
144
|
},
|
|
115
145
|
"devDependencies": {
|
|
116
146
|
"@types/node": "^24.19.1",
|
|
@@ -118,13 +148,14 @@
|
|
|
118
148
|
"@types/wrap-ansi": "^3.0.0",
|
|
119
149
|
"@vitest/coverage-v8": "^4.1.11",
|
|
120
150
|
"@zitadel/api-mock": "0.0.0",
|
|
121
|
-
"@zitadel/sdk-next": "0.
|
|
151
|
+
"@zitadel/sdk-next": "0.2.0",
|
|
122
152
|
"msw": "^2.14.6",
|
|
123
153
|
"oclif": "^4.17.46",
|
|
124
154
|
"tsdown": "^0.23.0",
|
|
125
155
|
"typescript": "^6.0.3",
|
|
126
156
|
"vite": "^8.3.1",
|
|
127
|
-
"vitest": "^4.1.11"
|
|
157
|
+
"vitest": "^4.1.11",
|
|
158
|
+
"yaml": "^2.9.0"
|
|
128
159
|
},
|
|
129
160
|
"scripts": {
|
|
130
161
|
"build": "tsdown",
|
|
@@ -5,7 +5,7 @@ description: >-
|
|
|
5
5
|
agent-friendly `zitadel` CLI. Use when the user wants to add login,
|
|
6
6
|
registration, or session handling, create a Zitadel project, scaffold auth
|
|
7
7
|
for a Next.js, Nuxt, SvelteKit, SolidStart, Qwik Router, TanStack Start, React,
|
|
8
|
-
Vue, Angular, Solid, Svelte, or Qwik app, or
|
|
8
|
+
Vue, Angular, Solid, Svelte, or Qwik app, or build and deploy Zitadel config
|
|
9
9
|
changes from repo state.
|
|
10
10
|
---
|
|
11
11
|
|
|
@@ -67,11 +67,44 @@ upgrade, so upgrade the app and rerun rather than retrying.
|
|
|
67
67
|
drops records or fields, so you end up reasoning from partial data. It is
|
|
68
68
|
finite — read it whole, or narrow it *at the source*: use a `list` verb's
|
|
69
69
|
`filter_fields` and pagination to fetch fewer records, or pipe to `jq` to pull
|
|
70
|
-
just what you need (e.g. `… resources --json | jq '.data.
|
|
70
|
+
just what you need (e.g. `… resources --json | jq '.data.items[].topic'`).
|
|
71
71
|
`--help` output is bounded too — read it in full rather than `head`-ing it, or
|
|
72
72
|
you will miss flags further down.
|
|
73
73
|
- **Working directory.** Add `--cwd <path>` when operating outside the current
|
|
74
74
|
working directory.
|
|
75
|
+
- **Server and project.** A command that talks to a project reads
|
|
76
|
+
`ZITADEL_URL`, `ZITADEL_PROJECT_ID` and `ZITADEL_PROJECT_SECRET`, each from the
|
|
77
|
+
first source that has it: `--server` (the URL only), `--env-file <path>`, the
|
|
78
|
+
process environment, `.env.<env>.local`, `.env.local`, `.env.<env>`, `.env`,
|
|
79
|
+
then `.zitadel/secret`. Only the default `development` environment reads
|
|
80
|
+
`.env.local` and `.zitadel/secret` (setup's project); `--env-file` reads that
|
|
81
|
+
file and the process environment only.
|
|
82
|
+
`<env>` is `--env`, else `ZITADEL_ENV`, else a hosting platform's own signal
|
|
83
|
+
(`VERCEL_ENV`, or Netlify's `CONTEXT` inside a Netlify build), else
|
|
84
|
+
`development`. A missing value fails with `E_VALIDATION` naming the variable
|
|
85
|
+
and the files it looked in; there is no cloud default outside `setup`, and
|
|
86
|
+
the server is never read from `zitadel.json`. `zitadel env` shows what resolved and where
|
|
87
|
+
each value came from; `zitadel env add <name>` binds another environment to a
|
|
88
|
+
project. The project it creates has never had a release, so the environment
|
|
89
|
+
serves the server's own defaults until something is deployed to it: run the
|
|
90
|
+
`deploy` the result's `next_commands` names, against the file it wrote
|
|
91
|
+
(`--env-file .env.<name>.local`). A person at a terminal is asked whether to
|
|
92
|
+
deploy there and then; your runs never are, so they never deploy — run that
|
|
93
|
+
`deploy` yourself. `data.deployment` carries the `release_id`,
|
|
94
|
+
`content_hash`, `deployment_id` and `origins` when a person accepted, and is
|
|
95
|
+
null otherwise — a failed deploy is a warning, never a failed `env add`.
|
|
96
|
+
- **Origins.** `zitadel origin add <url> --kind primary|preview`, `origin list`
|
|
97
|
+
and `origin rm <url>` manage the URLs a project serves: a `primary` origin is
|
|
98
|
+
where the app is served, a `preview` pattern (it may hold a `*`) is where
|
|
99
|
+
pull-request builds run. They need the project secret, not the preview token.
|
|
100
|
+
An empty list admits every origin, so removing the last one opens the project
|
|
101
|
+
up; `origin rm` asks first and needs `--force` when non-interactive. Removing
|
|
102
|
+
a preview pattern leaves its live previews served until they expire.
|
|
103
|
+
`deploy` lands only on primary origins and fails with `E_VALIDATION` without
|
|
104
|
+
one; its `next_commands` name the step that gives the project one. `setup`
|
|
105
|
+
0.1.0 registered the app's URL (`ZITADEL_ISSUER`) as a `preview` origin,
|
|
106
|
+
which `origin add --kind primary` refuses with `E_CONFLICT`: `doctor` warns
|
|
107
|
+
(`issuer-origin`) and `doctor --fix` makes it primary.
|
|
75
108
|
- **Telemetry.** The CLI sends anonymous usage telemetry by default, and
|
|
76
109
|
agent-driven runs are exactly the ones worth learning from — leave it on.
|
|
77
110
|
Don't add `--no-telemetry` to commands. An opt-out exists for users who need
|
|
@@ -122,36 +155,40 @@ upgrade, so upgrade the app and rerun rather than retrying.
|
|
|
122
155
|
`${{ NAME }}` reference to a project variable is not a secret and may stay on
|
|
123
156
|
argv (a connection's `client_secret` takes nothing else); write it in single
|
|
124
157
|
quotes, since inside double quotes the shell reads `${` as its own expansion.
|
|
125
|
-
- **Streams.** Capture stdout and stderr separately when scripting
|
|
126
|
-
|
|
127
|
-
|
|
158
|
+
- **Streams.** Capture stdout and stderr separately when scripting, into files
|
|
159
|
+
outside the app directory: `setup` scaffolds a new app only into an empty
|
|
160
|
+
directory. The machine contract is one parseable JSON object on stdout;
|
|
161
|
+
installer, audit, and package-manager progress belongs on stderr.
|
|
128
162
|
|
|
129
163
|
## Discovering commands
|
|
130
164
|
|
|
131
165
|
The CLI ships its own command list and per-command flags, always correct for the
|
|
132
166
|
installed version. **Do not guess a command name.** Before running any command you
|
|
133
|
-
are not certain of,
|
|
167
|
+
are not certain of, look it up first and use the exact name the CLI prints:
|
|
134
168
|
command and resource names — including whether a resource is singular or plural
|
|
135
|
-
and the exact verb — are whatever
|
|
136
|
-
wrong guess costs a failed call and a round of recovery; one cheap
|
|
137
|
-
front avoids it. `zitadel --help`
|
|
138
|
-
|
|
169
|
+
and the exact verb — are whatever the CLI shows, never what you would assume. A
|
|
170
|
+
wrong guess costs a failed call and a round of recovery; one cheap lookup up
|
|
171
|
+
front avoids it. `zitadel --help` lists each family of commands (`users`,
|
|
172
|
+
`release`) as one row, so it stays short; `zitadel commands` is the flat list of
|
|
173
|
+
every command, verbs included, and `zitadel <family> --help` lists one family's
|
|
174
|
+
verbs. Treat the CLI's own output as authoritative over anything you remember:
|
|
139
175
|
|
|
140
176
|
```sh
|
|
141
|
-
npx @zitadel/cli@latest --help # every command, grouped by purpose
|
|
177
|
+
npx @zitadel/cli@latest --help # every command family, grouped by purpose
|
|
142
178
|
npx @zitadel/cli@latest commands # flat list of all commands
|
|
143
179
|
npx @zitadel/cli@latest <command> --help # a command's flags, which are required, and examples
|
|
144
180
|
npx @zitadel/cli@latest resources --json # the resource surface in one call (see below); contacts no server
|
|
145
181
|
npx @zitadel/cli@latest api ls --json # every API operation, its parameters, body and paging; contacts no server
|
|
146
182
|
```
|
|
147
183
|
|
|
148
|
-
`--help` groups the
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
memory, since they change as the CLI gains commands.
|
|
184
|
+
`--help` groups the commands by what you are doing: getting started, local
|
|
185
|
+
development, users and teams, authentication, configuration and releases, and
|
|
186
|
+
diagnostics and utilities. A family such as `users` is one row, and
|
|
187
|
+
`zitadel users --help` lists its verbs. Read the groups from `--help` rather than
|
|
188
|
+
from memory, since they change as the CLI gains commands.
|
|
153
189
|
|
|
154
|
-
Use `zitadel --help` to find *which* command to run
|
|
190
|
+
Use `zitadel --help` to find *which* family or command to run, and
|
|
191
|
+
`zitadel commands` for the exact verb. Use `resources --json` when
|
|
155
192
|
you need a resource's *fields*: it reports the whole runtime-resource surface at
|
|
156
193
|
once — every resource, its verbs, each verb's `create_fields` / `update_fields`
|
|
157
194
|
(flag name, kind, `required`, any closed value set), its `filter_fields` /
|
|
@@ -181,19 +218,24 @@ Each invocation prints one JSON object:
|
|
|
181
218
|
success: a warning usually means a follow-up step is needed.
|
|
182
219
|
- A `list` verb emits `data: { items, count, next_page_token }`; when a page
|
|
183
220
|
remains, `data.next_commands` carries the exact command for the next page.
|
|
184
|
-
- `
|
|
185
|
-
(
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
221
|
+
- `deploy` emits `data.content_hash` (the release digest), `data.origins`
|
|
222
|
+
(the origins it went live on) and `data.resources` (how many it pinned). A
|
|
223
|
+
re-run on unchanged config returns the same digest. Use it to confirm what
|
|
224
|
+
went live.
|
|
225
|
+
- `preview` is `deploy` for a pull-request build: it deploys the release to the
|
|
226
|
+
build's preview URLs (from the platform, or `--origin`) for `--ttl` and emits
|
|
227
|
+
`data.previews` plus `data.release_env` (the `NEXT_PUBLIC_ZITADEL_RELEASE`
|
|
228
|
+
line to pin). It uses `ZITADEL_PROJECT_SECRET` until the server accepts the narrower preview token (#1575), never the other credential, and
|
|
229
|
+
outside a preview or development build it is a no-op — even with an explicit
|
|
230
|
+
`--origin`, which selects targets but never authorizes a preview deploy. A CI
|
|
231
|
+
build other than Vercel or Netlify must set `ZITADEL_ENV=preview`, or it is
|
|
232
|
+
skipped too. `preview list` and `preview rm <url>` read and end live previews.
|
|
233
|
+
- `deployment rollback` emits `data.release_id` (the release live again),
|
|
234
|
+
`data.origins` and `data.pointers`, the `{kind, handle, revision_id}`
|
|
235
|
+
revisions that release pins; the `schema` one is what new registrations use.
|
|
236
|
+
Users keep their schema revision and attributes: a rollback does not migrate
|
|
237
|
+
them back. Without `data.pointers`, a warning says the release could not be
|
|
238
|
+
read.
|
|
197
239
|
- A write verb's `--dry-run` reports what it would send without calling the
|
|
198
240
|
platform, so you can preview a mutation before making it.
|
|
199
241
|
- `api` emits the response body as `data`, exactly as the API reference
|
|
@@ -208,11 +250,12 @@ Common error codes point at their own fix in `next_commands`:
|
|
|
208
250
|
`details` often lists the missing or available values.
|
|
209
251
|
- `E_CREDENTIAL_MISSING`: a connection's `client_id` or `client_secret` names a
|
|
210
252
|
project variable that does not exist; `details.variable` is the name. Publish it
|
|
211
|
-
with `
|
|
253
|
+
with `variable set NAME` (add `--secret` for the secret), then retry.
|
|
212
254
|
- `E_LOCAL_SERVER_NOT_RUNNING`: start the local runtime, then retry with
|
|
213
255
|
`--server local`.
|
|
214
|
-
- `E_NOT_FOUND`: an HTTP 404 — a missing resource,
|
|
215
|
-
|
|
256
|
+
- `E_NOT_FOUND`: an HTTP 404 — a missing resource, an operation the server's
|
|
257
|
+
version does not have (`details.body.code` is `req.not_found`), or a
|
|
258
|
+
`--server` that is not a Zitadel platform API. Follow `next_commands`.
|
|
216
259
|
- `E_PORT_IN_USE`: the requested local runtime port already has a listener; stop
|
|
217
260
|
that process or choose another port.
|
|
218
261
|
|
|
@@ -230,8 +273,9 @@ Exit codes mirror the error class (3 = validation, 4 = network or not-found,
|
|
|
230
273
|
5 = conflict, 1 = auth, 2 = not-implemented, 130 = cancelled). `E_NETWORK`
|
|
231
274
|
covers a server that answered 5xx and one that never answered — refused,
|
|
232
275
|
unresolvable, or silent past the 30-second request deadline. Ctrl-C exits
|
|
233
|
-
130: a command waiting on the server
|
|
234
|
-
any other command exits within a second, possibly
|
|
276
|
+
130: a command waiting on the server, or on an answer at a prompt, stops at
|
|
277
|
+
once with `E_CANCELLED`, and any other command exits within a second, possibly
|
|
278
|
+
without an envelope. In human mode a cancel prints no error block. An
|
|
235
279
|
unknown command is handled by the CLI's help layer, not the envelope.
|
|
236
280
|
|
|
237
281
|
## Clarify before you configure
|
|
@@ -276,7 +320,7 @@ like. Read the current sets from `setup --help`, but know the traps:
|
|
|
276
320
|
enabled**; `passkey-first` leads with a passkey. Neither one gives you
|
|
277
321
|
password-only. If the developer does not want passkeys, turn them off
|
|
278
322
|
yourself: set `x-auth-methods.passkey.enabled` to `false` in
|
|
279
|
-
`.zitadel/schemas/*.json`, then `
|
|
323
|
+
`.zitadel/schemas/*.json`, then `deploy`.
|
|
280
324
|
|
|
281
325
|
Translate the developer's answers into the right axis values, then **verify
|
|
282
326
|
against what actually deployed**: read the live schema back
|
|
@@ -291,13 +335,12 @@ invocation) or were explicitly told to just set it up.
|
|
|
291
335
|
## Answering the user
|
|
292
336
|
|
|
293
337
|
Report what changed, in the user's terms — not how you drove the CLI. The
|
|
294
|
-
flags, `--json`, the
|
|
338
|
+
flags, `--json`, the deploy flow, exit codes, and the number of
|
|
295
339
|
commands you ran are your internal business; the user asked for an outcome, not
|
|
296
340
|
a transcript. Say what they got.
|
|
297
341
|
|
|
298
342
|
- Good: "Added an optional phone number to your user schema and deployed it."
|
|
299
|
-
- Avoid: "I ran `
|
|
300
|
-
both returned ok."
|
|
343
|
+
- Avoid: "I ran `deploy` with `--non-interactive --json` and it returned ok."
|
|
301
344
|
|
|
302
345
|
Quote a command or flag only when the user asks *how* something works, or when
|
|
303
346
|
they need to run something themselves. Otherwise keep the mechanics out of the
|
|
@@ -314,8 +357,7 @@ npx @zitadel/cli@latest doctor --non-interactive --json
|
|
|
314
357
|
npx @zitadel/cli@latest start --non-interactive --json
|
|
315
358
|
npx @zitadel/cli@latest setup --framework next --server local --non-interactive --json
|
|
316
359
|
npx @zitadel/cli@latest doctor --non-interactive --json
|
|
317
|
-
npx @zitadel/cli@latest
|
|
318
|
-
npx @zitadel/cli@latest apply --non-interactive --json
|
|
360
|
+
npx @zitadel/cli@latest deploy -m "<why this release changes>" --non-interactive --json
|
|
319
361
|
```
|
|
320
362
|
|
|
321
363
|
`setup` scaffolds into an empty directory, or patches an existing app project
|
|
@@ -329,6 +371,11 @@ terminal `setup` cannot ask which framework to scaffold, so pass `--framework`;
|
|
|
329
371
|
the error lists the choices in `details.frameworks`. `setup --dry-run` creates
|
|
330
372
|
no app: where it would scaffold one, it previews the project, and
|
|
331
373
|
`data.files_written` is empty, because those files depend on the generated app.
|
|
374
|
+
`setup` also writes and publishes the password policy
|
|
375
|
+
(`.zitadel/policies/auth.factor.password.set.json`) with a minimum length of
|
|
376
|
+
15, NIST's minimum for password-only sign-in and the wizard's default. Pass
|
|
377
|
+
`--password-min-length <n>` only when the user asks for another value (`0` sets
|
|
378
|
+
none), and give test users passwords at least that long.
|
|
332
379
|
When setup fails after scaffolding a new app, before its default schema and
|
|
333
380
|
flow are uploaded, it removes the app again and puts back what the directory
|
|
334
381
|
held, so rerunning the same `setup` starts over.
|
|
@@ -347,15 +394,18 @@ replaces a file at a path setup writes, but not the app's root component or
|
|
|
347
394
|
request boundary at another path.
|
|
348
395
|
|
|
349
396
|
The app may also live in a subdirectory of the one `start` ran in: `setup`,
|
|
350
|
-
`console`, `--server local` and `doctor` read `.zitadel/local/` from the working
|
|
397
|
+
`console`, `logs`, `--server local` and `doctor` read `.zitadel/local/` from the working
|
|
351
398
|
directory or the nearest parent that has it (up to the home directory), so
|
|
352
399
|
`mkdir my-app && cd my-app` after `start` works too. `doctor` checks that
|
|
353
400
|
server and its port, but runs its project checks in the working directory, so run
|
|
354
401
|
it from the app. When that server is unhealthy it suggests `start` and
|
|
355
402
|
`reset --force` with `--cwd` set to the directory that holds it (when that path needs
|
|
356
403
|
shell quoting the commands are left out, the hint names the directory, and
|
|
357
|
-
`doctor --json` returns the argument lists in `details.suggested_args` to run as they are).
|
|
358
|
-
|
|
404
|
+
`doctor --json` returns the argument lists in `details.suggested_args` to run as they are). A
|
|
405
|
+
local server is healthy once its `/readyz` answers, which takes its database
|
|
406
|
+
answering too: `start` waits for that, and the commands that read a server
|
|
407
|
+
treat one whose database does not answer as unhealthy. `stop`,
|
|
408
|
+
`reset` and `status` read only the working directory's
|
|
359
409
|
`.zitadel/local/`, so run them where `start` ran. On a local
|
|
360
410
|
server that hosts the platform project, `setup` attaches the project to the
|
|
361
411
|
local admin's team and ends `data.next_commands` with `console`; when it finds
|
|
@@ -363,10 +413,14 @@ no local admin for that server on the way up, it says so in the envelope's
|
|
|
363
413
|
`warnings` and the project stays unattached, so the local console does not
|
|
364
414
|
list it.
|
|
365
415
|
|
|
366
|
-
|
|
416
|
+
For a project set up against a hosted server, `doctor` checks no local
|
|
417
|
+
runtime, unless a local server was started in its directory or a parent, or
|
|
418
|
+
`--port`, `--runtime` or `--image` asks about one.
|
|
419
|
+
|
|
420
|
+
When `setup --sso` or `auth-method sso enable` adds a social sign-in provider, the user
|
|
367
421
|
must register the connection's redirect URI with the provider themselves: give
|
|
368
422
|
them `data.sso[].callback_uri` from `setup`, or `data.callback_uri` from
|
|
369
|
-
`sso enable`, verbatim. Each connection has its own URI, so a URI from another
|
|
423
|
+
`auth-method sso enable`, verbatim. Each connection has its own URI, so a URI from another
|
|
370
424
|
provider or an older CLI version does not work.
|
|
371
425
|
|
|
372
426
|
After `setup`, follow `data.next_commands` to start the app. Prove the generated
|
|
@@ -374,9 +428,18 @@ auth flow in a visible browser by registering a unique user, logging out, loggin
|
|
|
374
428
|
back in with the same email/password, and ending on the signed-in profile page.
|
|
375
429
|
Do not treat a rendered login or registration form as completion.
|
|
376
430
|
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
431
|
+
When a step against the local server fails without saying why (the login says a
|
|
432
|
+
sign-in provider "is not available right now", or a request returns 500), the
|
|
433
|
+
server logged the cause. Read it with `logs --level warn --json`: `data.logs`
|
|
434
|
+
holds the warnings and errors, and `--tail` counts only those lines. `logs
|
|
435
|
+
--follow` streams the log for a person at a terminal, with warnings yellow and
|
|
436
|
+
errors red; it refuses `--json`. `start`, and `doctor` when it finds the server
|
|
437
|
+
running, name it in `data.next_actions`.
|
|
438
|
+
|
|
439
|
+
To move a set-up app to another dev port (setup's `warnings` say the port is
|
|
440
|
+
already in use, the dev server exits with `EADDRINUSE`, or another app needs
|
|
441
|
+
the port), run `setup --dev-port <port>` in the app; never change the port in
|
|
442
|
+
one file by hand. In a directory with a
|
|
380
443
|
`zitadel.json`, `setup` returns `status: "skipped"` with `reason:
|
|
381
444
|
"already-initialized"`, unless `--dev-port` is given: then it moves the app.
|
|
382
445
|
It allows the new origin on the server first, and a refusal changes nothing.
|
|
@@ -399,6 +462,13 @@ than one:
|
|
|
399
462
|
stdin when non-interactive. Any other OpenID Connect provider is
|
|
400
463
|
`--provider custom` with `--slug`, `--display-name`, `--issuer` and optionally
|
|
401
464
|
`--scopes` (default `openid profile email`).
|
|
465
|
+
In a terminal, a custom provider's settings you leave out are asked for, after
|
|
466
|
+
the checks that need no answer; a connection that already has that slug
|
|
467
|
+
supplies its own. A scripted, `--json`, `--dry-run` or `--non-interactive`
|
|
468
|
+
run must pass all three and gets an error naming the missing ones. After
|
|
469
|
+
writing a new connection the text output prints the claim mappings and names
|
|
470
|
+
the file to edit them in; `--json` does not carry them, so read the
|
|
471
|
+
connection file.
|
|
402
472
|
An issuer on `http://localhost:<port>` (a test provider on this machine)
|
|
403
473
|
works against a server `zitadel start` runs as a process: `start` sets
|
|
404
474
|
`NEXTGEN_HTTPCLIENT_ALLOW_LIST=localhost,127.0.0.0/8,::1/128` unless
|
|
@@ -410,7 +480,11 @@ than one:
|
|
|
410
480
|
|
|
411
481
|
The commands change only local files, apart from
|
|
412
482
|
`auth-method sso enable`, which also stores the provider's credentials on the
|
|
413
|
-
project.
|
|
483
|
+
project. A provider added by `sso enable` is configured locally, not live: its
|
|
484
|
+
`data.title` says so, `data.next_actions` says to deploy, and `data.next_commands`
|
|
485
|
+
holds `deploy --dry-run` then `deploy`. Only run those once the provider's OAuth
|
|
486
|
+
application exists. `data.title` claims the credentials are stored on the project
|
|
487
|
+
only when they reached it. When they refuse, they write and publish nothing, on a dry run too.
|
|
414
488
|
They refuse to disable a method a login flow still asks for, to change a method
|
|
415
489
|
while a login flow has errors that prevent checking it, to enable password on a
|
|
416
490
|
schema without `x-identifier`, to edit a value that is not the shape they edit,
|
|
@@ -423,15 +497,37 @@ and warns that no way to sign in would be left. A method that is enabled but not
|
|
|
423
497
|
offered by any active flow is reported in `warnings` (and in `data.not_offered`
|
|
424
498
|
for password and passkey). Every command reports `data.changed`, the
|
|
425
499
|
project-relative files it wrote (on a dry run, the files it would write), empty
|
|
426
|
-
when nothing changed. Follow `data.next_commands` (`
|
|
427
|
-
`
|
|
500
|
+
when nothing changed. Follow `data.next_commands` (`deploy --dry-run`, `deploy`, and
|
|
501
|
+
`variable set` when a credential did not reach the project) to publish the
|
|
428
502
|
change. When a path or schema name would need shell quoting, `next_commands` is
|
|
429
503
|
empty and `data.next_args` (or `details.suggested_args` on a refusal) holds the
|
|
430
504
|
same commands as argument lists.
|
|
431
505
|
|
|
432
506
|
Repo config is authoritative: edit `zitadel.json` or files under `.zitadel/`,
|
|
433
|
-
then re-run `
|
|
434
|
-
and the config-sync details.
|
|
507
|
+
then re-run `deploy`. See the reference below for driving the login UI
|
|
508
|
+
and the config-sync details. The editor schemas in `.zitadel/meta/` are
|
|
509
|
+
generated by the CLI, not config: never edit them. `doctor` warns when one is
|
|
510
|
+
missing or outdated (after an upgrade that changed a dialect), and
|
|
511
|
+
`doctor --fix` writes the current ones, unless the project pins a newer
|
|
512
|
+
`@zitadel/*` version (run that version's `doctor --fix`) or a symlink is on
|
|
513
|
+
the path; `deploy --dry-run` validates without them either way.
|
|
514
|
+
|
|
515
|
+
## Claiming a project
|
|
516
|
+
|
|
517
|
+
A project on a hosted server stays temporary until a person claims it, within
|
|
518
|
+
14 days of `setup`. Claiming signs that person in with their own Zitadel
|
|
519
|
+
account in a browser, so it is their step: hand them the link, and never open
|
|
520
|
+
it yourself. Plain `claim` waits for the browser step and prints nothing on
|
|
521
|
+
stdout until it ends, so run `claim --no-wait --non-interactive --json`
|
|
522
|
+
instead. It returns at once with `data.claim_url`, the link to give the person
|
|
523
|
+
verbatim, and `data.expires_at`, when it stops working (about 10 minutes).
|
|
524
|
+
Then run the `claim --challenge-id …` command in `data.next_commands`: it
|
|
525
|
+
waits for the person to finish, returns `data.dashboard_url`, and records the
|
|
526
|
+
owning team in `.zitadel/secret`. When the link expires, or `--challenge-id`
|
|
527
|
+
names one the platform does not know (`E_NOT_FOUND`), `next_commands` starts a
|
|
528
|
+
new `--no-wait` link. A project that already has a team is `skipped` with
|
|
529
|
+
`reason: "already-claimed"`; one past its 14 days fails with `E_VALIDATION`
|
|
530
|
+
and no `next_commands`, since a new link cannot reopen it.
|
|
435
531
|
|
|
436
532
|
## Reference files
|
|
437
533
|
|
|
@@ -439,4 +535,4 @@ Load on demand:
|
|
|
439
535
|
|
|
440
536
|
- [`references/driving-login-ui.md`](references/driving-login-ui.md) — driving the
|
|
441
537
|
`<zitadel-login>` component in a browser, and the authoritative repo-config
|
|
442
|
-
workflow (schemas, flows, branding).
|
|
538
|
+
workflow (schemas, flows, branding, policies).
|
|
@@ -45,7 +45,8 @@ exercises fresh-app setup plus registration, logout, and login across the
|
|
|
45
45
|
supported frameworks.
|
|
46
46
|
|
|
47
47
|
Repo config is authoritative: edit `zitadel.json` or files under `.zitadel/`,
|
|
48
|
-
then re-run `
|
|
48
|
+
then re-run `deploy` (or `deploy --dry-run` to validate and preview the diff
|
|
49
|
+
without publishing). Schema and flow files are synced from
|
|
49
50
|
`.zitadel/schemas/*.json` and `.zitadel/flows/*.json`. Login templates
|
|
50
51
|
(branding) are synced from `.zitadel/branding/`: a single `branding.json`
|
|
51
52
|
descriptor (layout, asset URLs) plus a sibling `login.liquid` LiquidJS
|
|
@@ -53,9 +54,10 @@ template referenced as `"liquid_template": { "$file": "./login.liquid" }`. Scaff
|
|
|
53
54
|
`branding eject` command (`--design centered|minimal`,
|
|
54
55
|
interactive picker on a TTY); setup never scaffolds them. Branding is
|
|
55
56
|
revisioned and immutable: every edit — including a `.liquid`-only edit —
|
|
56
|
-
|
|
57
|
-
the newest
|
|
58
|
-
validator (`
|
|
57
|
+
is published as part of the next release when you `deploy`, and the login
|
|
58
|
+
serves the newest release. `deploy` validates templates with the authoritative
|
|
59
|
+
LiquidJS validator (and `deploy --dry-run` runs the same validation without
|
|
60
|
+
publishing; `E_VALIDATION` lists rule ids such as `no-script-tag` and
|
|
59
61
|
`mandatory-gates`; every template must keep a `{% mandatory_gates %}` tag,
|
|
60
62
|
where the login adds any required field and the primary action the template
|
|
61
63
|
left out). Asset URLs must be absolute `https://`; during development, images
|
|
@@ -67,17 +69,35 @@ directory. Keep exactly one descriptor in
|
|
|
67
69
|
`.zitadel/branding/` — extra `*.json` files there fail the scan.
|
|
68
70
|
Server-provisioned defaults remain a fallback for non-CLI project
|
|
69
71
|
creation, but CLI-created projects are authored from local files first. Flows are
|
|
70
|
-
revisioned like branding: an edit
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
collects needs that step's `fields` updated, and `
|
|
75
|
-
|
|
76
|
-
revision; an unpinned login serves the newest active unscoped
|
|
77
|
-
purpose in the project, whatever its flow's name. Older
|
|
78
|
-
so an `audience` added after a flow was
|
|
79
|
-
earlier revision out of the running. Removing a
|
|
80
|
-
flow
|
|
72
|
+
revisioned like branding: an edit is published as a new immutable flow revision
|
|
73
|
+
in the next `deploy`. A flow names its schema by `objectType` in `user_schema`
|
|
74
|
+
and runs against the schema's newest revision, so a schema revise needs no
|
|
75
|
+
change to `user_schema`; one that removes or renames a property a step
|
|
76
|
+
collects needs that step's `fields` updated, and `deploy` checks each flow
|
|
77
|
+
against the schema it names. A login pinned by `flow-name` serves that flow's
|
|
78
|
+
newest active revision; an unpinned login serves the newest active unscoped
|
|
79
|
+
revision for its purpose in the project, whatever its flow's name. Older
|
|
80
|
+
revisions stay active, so an `audience` added after a flow was deployed
|
|
81
|
+
without one doesn't take that earlier revision out of the running. Removing a
|
|
82
|
+
flow file does not retire the
|
|
83
|
+
flow. Operation policies live in `.zitadel/policies/`: one `<operation>.json`
|
|
84
|
+
per guarded operation, named after its `operation`, holding
|
|
85
|
+
`{"$schema": "../meta/policy.json", "operation": "auth.factor.password.set",
|
|
86
|
+
"config": {"min_length": 15}}`. `deploy` checks each file against
|
|
87
|
+
`.zitadel/meta/policy.json` (known operation, only the template's settings,
|
|
88
|
+
within their bounds) and its name before anything is sent; settings that
|
|
89
|
+
contradict each other (a `min_length` above `max_length`) are refused by the
|
|
90
|
+
server's release build with `rel.invalid`, the policy's entry carrying
|
|
91
|
+
`pol.invalid`. A changed file publishes a new revision of the same policy,
|
|
92
|
+
which applies the next time a password is set. A setting left out is off, and
|
|
93
|
+
`"config": {}` switches every rule off. A sign-in or sign-up uses the policy
|
|
94
|
+
the deployed release carries, so deploying without the file switches it off
|
|
95
|
+
there; a password set through the API, or before the first deploy, uses the
|
|
96
|
+
newest revision, which only `"config": {}` switches off. `policies list` and
|
|
97
|
+
`policies get <id|operation>` read them back. `setup` writes and publishes
|
|
98
|
+
the password policy with a `min_length` of 15; pass `--password-min-length <n>`
|
|
99
|
+
only when the user asks for another value (`0` sets none). Managed files carry
|
|
100
|
+
a marker comment; `eject` removes only
|
|
81
101
|
files that still carry it, preserving anything the user replaced. For app-local
|
|
82
102
|
development, `--server local` resolves through `.zitadel/local/runtime.json` and
|
|
83
103
|
requires a healthy
|