@zitadel/cli 1.0.0-alpha.22 → 1.0.0-alpha.24

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.
Files changed (66) hide show
  1. package/README.md +2112 -233
  2. package/SKILLS.md +263 -84
  3. package/dist/groups-CMB0fMzE.mjs +22 -0
  4. package/dist/groups-CMB0fMzE.mjs.map +1 -0
  5. package/dist/index.mjs +15745 -0
  6. package/dist/index.mjs.map +1 -0
  7. package/dist/lib/oclif/help.mjs +67 -0
  8. package/dist/lib/oclif/help.mjs.map +1 -0
  9. package/oclif.manifest.json +4063 -227
  10. package/package.json +44 -9
  11. package/dist/box-B_mwKole.mjs +0 -35
  12. package/dist/box-B_mwKole.mjs.map +0 -1
  13. package/dist/branding-DvoH8e0D.mjs +0 -79
  14. package/dist/branding-DvoH8e0D.mjs.map +0 -1
  15. package/dist/claim-state-B6ZSKmhW.mjs +0 -125
  16. package/dist/claim-state-B6ZSKmhW.mjs.map +0 -1
  17. package/dist/commands/apply.mjs +0 -81
  18. package/dist/commands/apply.mjs.map +0 -1
  19. package/dist/commands/branding/eject.mjs +0 -104
  20. package/dist/commands/branding/eject.mjs.map +0 -1
  21. package/dist/commands/claim.mjs +0 -445
  22. package/dist/commands/claim.mjs.map +0 -1
  23. package/dist/commands/doctor.mjs +0 -1138
  24. package/dist/commands/doctor.mjs.map +0 -1
  25. package/dist/commands/eject.mjs +0 -157
  26. package/dist/commands/eject.mjs.map +0 -1
  27. package/dist/commands/logs.mjs +0 -58
  28. package/dist/commands/logs.mjs.map +0 -1
  29. package/dist/commands/plan.mjs +0 -65
  30. package/dist/commands/plan.mjs.map +0 -1
  31. package/dist/commands/reset.mjs +0 -79
  32. package/dist/commands/reset.mjs.map +0 -1
  33. package/dist/commands/schemas/list.mjs +0 -146
  34. package/dist/commands/schemas/list.mjs.map +0 -1
  35. package/dist/commands/setup.mjs +0 -1309
  36. package/dist/commands/setup.mjs.map +0 -1
  37. package/dist/commands/start.mjs +0 -288
  38. package/dist/commands/start.mjs.map +0 -1
  39. package/dist/commands/status.mjs +0 -151
  40. package/dist/commands/status.mjs.map +0 -1
  41. package/dist/commands/stop.mjs +0 -105
  42. package/dist/commands/stop.mjs.map +0 -1
  43. package/dist/designs-Ckz18Dpo.mjs +0 -38
  44. package/dist/designs-Ckz18Dpo.mjs.map +0 -1
  45. package/dist/docker-B1MjOMFo.mjs +0 -436
  46. package/dist/docker-B1MjOMFo.mjs.map +0 -1
  47. package/dist/environment-fibXmzM8.mjs +0 -17
  48. package/dist/environment-fibXmzM8.mjs.map +0 -1
  49. package/dist/journey-guidance-C9MxJNTg.mjs +0 -40
  50. package/dist/journey-guidance-C9MxJNTg.mjs.map +0 -1
  51. package/dist/oclif-JlsN1lfO.mjs +0 -1769
  52. package/dist/oclif-JlsN1lfO.mjs.map +0 -1
  53. package/dist/orca-CPMHzmg3.mjs +0 -3917
  54. package/dist/orca-CPMHzmg3.mjs.map +0 -1
  55. package/dist/package-manager-CDUsoGBy.mjs +0 -194
  56. package/dist/package-manager-CDUsoGBy.mjs.map +0 -1
  57. package/dist/ports-hidBnSW-.mjs +0 -116
  58. package/dist/ports-hidBnSW-.mjs.map +0 -1
  59. package/dist/processes-Cv19mJ2I.mjs +0 -120
  60. package/dist/processes-Cv19mJ2I.mjs.map +0 -1
  61. package/dist/project-Ba0nriNm.mjs +0 -140
  62. package/dist/project-Ba0nriNm.mjs.map +0 -1
  63. package/dist/sync-ChlLQ4vZ.mjs +0 -1633
  64. package/dist/sync-ChlLQ4vZ.mjs.map +0 -1
  65. package/dist/user-schema-C2mC-JTN.mjs +0 -91
  66. package/dist/user-schema-C2mC-JTN.mjs.map +0 -1
package/SKILLS.md CHANGED
@@ -23,7 +23,8 @@ parse the result rather than scraping human output.
23
23
  - The CLI sends anonymous usage telemetry by default. For automated/agent runs
24
24
  that should stay silent, disable it with `--no-telemetry` (per invocation) or
25
25
  `ZITADEL_TELEMETRY=0` / `DO_NOT_TRACK=1` (per environment); this also skips the
26
- small end-of-command network flush.
26
+ small end-of-command network flush and drops the `ci/` and `host/` tokens from
27
+ the CLI's HTTP `User-Agent`.
27
28
  - See `README.md` (its commands section is generated from the CLI's own
28
29
  metadata) or run `zitadel <command> --help` for the full per-command flag list.
29
30
 
@@ -56,10 +57,10 @@ Each invocation prints one JSON object:
56
57
  see what setup created versus merged into (your `package.json` is an
57
58
  `update`). `data.files_written` remains the flat list — deduplicated file
58
59
  paths only, covering both scaffolded and `.zitadel/` resource files.
59
- - `setup` also emits `data.design`: the starter login design it ejected and
60
- published as branding revision 1, or `null` when the built-in template was
61
- kept (no `.zitadel/branding/` files exist in that case). Use it to verify
62
- the requested `--design` took effect without diffing the repo.
60
+ - `setup` never writes `.zitadel/branding/` or publishes a branding
61
+ revision: the login renders the maintained `<zitadel-login>` component.
62
+ Taking ownership of the widget template is the separate, opt-in
63
+ `branding eject` command.
63
64
  - `E_LOCAL_SERVER_NOT_RUNNING`: start the local runtime with
64
65
  `npx @zitadel/cli@alpha start`, then retry with `--server local`.
65
66
  - `E_NOT_FOUND`: an HTTP 404 from the target server. With the platform's
@@ -71,6 +72,29 @@ Each invocation prints one JSON object:
71
72
  Stop that process, run `npx @zitadel/cli@alpha stop --all` for host-wide
72
73
  CLI-managed local runtimes, or choose another `start --port`.
73
74
 
75
+ In human mode the output follows the terminal: a TTY gets the project and
76
+ server lines and an aligned table, while a piped or redirected run (or
77
+ `--plain`) gets one tab-separated record per line and nothing else. `--json`
78
+ is unaffected and remains the contract for agents.
79
+
80
+ Text the server returns is escaped before it is printed, in `--json` as in
81
+ human mode, so a value someone stored cannot drive the reader's terminal.
82
+ Control, format and bidi characters in `data` values and keys and in an
83
+ error's `message` appear as visible `\xNN`, `\uNNNN` or `\u{NNNNN}` text;
84
+ newlines and tabs are kept, and a key's backslashes are doubled so two keys
85
+ never merge. A value that contained such a character is therefore not the
86
+ stored value byte for byte: do not send it back in an update as if it were.
87
+ When you need the exact stored value, call the platform API directly with the
88
+ project secret; the CLI only ever shows the escaped form.
89
+ `plan`, `apply` and `setup` are the exception for `.zitadel/` files: they
90
+ write the server's bodies back verbatim and escape only what they print.
91
+
92
+ A property whose name reads as a credential (`password`, `client_secret`,
93
+ `api_key`, …) is refused anywhere on the command line — as an `--attributes`
94
+ entry and inside an inline `--data` body alike, since argv is visible to other
95
+ processes and kept in shell history. Send such bodies with `--file <path>` or
96
+ `--file -` (stdin), which never pass through argv.
97
+
74
98
  Capture stdout and stderr separately when scripting. Some terminals and agent
75
99
  UIs display both streams together, but the machine contract is one parseable
76
100
  JSON object on stdout; installer, audit, and package-manager progress belongs
@@ -80,14 +104,103 @@ Exit codes mirror the error class (3 = validation, 4 = network or not-found,
80
104
  5 = conflict, 1 = auth, 2 = not-implemented). An unknown command is handled by
81
105
  the CLI's help layer, not the envelope.
82
106
 
107
+ ## Resource commands
108
+
109
+ Runtime resources — users, teams, sessions, events, grants, projects — have a
110
+ uniform `zitadel <resource> <verb>` surface built from one registry
111
+ (`src/commands/resources.ts`); the conventions are documented in
112
+ `docs/design/cli/resource-commands.md`. Config resources (schemas, flows,
113
+ branding) stay on `plan` / `apply`; the resource commands never write them.
114
+
115
+ | Resource | Verbs |
116
+ | ---------- | -------------------------------------- |
117
+ | `users` | list, get, create, update, delete |
118
+ | `teams` | list, get, create, update, deactivate |
119
+ | `sessions` | list, get, revoke |
120
+ | `events` | list, get |
121
+ | `grants` | list, get, create, delete |
122
+ | `projects` | list, get, update |
123
+
124
+ Every verb reads the project credential from `.zitadel/secret` and injects
125
+ `project_id` itself; there is no project flag.
126
+
127
+ `zitadel resources --json` reports the whole surface in one call: every
128
+ resource, its verbs, its `filter_fields` / `sort_fields`, and the fields of its writes,
129
+ reported per verb as `create_fields` and `update_fields` (flag name, kind,
130
+ `required`, and any closed value set), and `delete_outcome`, the property a
131
+ delete's envelope carries beside `id`. Prefer it
132
+ over reading `--help` per command. It contacts no server.
133
+
134
+ - `list` emits `data: { items, count, next_page_token }`, and when a page
135
+ remains, `data.next_commands` carries the exact command for the next page —
136
+ the cursor repeated alongside the same `--limit`, `--sort` and `--filter`,
137
+ since a token is only valid with the query that issued it. Prefer it over
138
+ rebuilding the invocation yourself. One page by default
139
+ (`--limit`, `--page-token`); `--all` drains every page and sets
140
+ `next_page_token` to `null`. Query-backed lists take `--filter
141
+ field=operation:value` (repeatable, AND-combined; the operation defaults to
142
+ `equals`) and `--sort field:asc|desc`; the accepted fields are listed in
143
+ `--help` and an unknown one fails with `E_VALIDATION` before any request.
144
+ `events list` takes its filters as named flags (`--category`, `--actor-id`,
145
+ `--created-after`, …).
146
+ - `--fields id,attributes.email` chooses which columns the human rendering
147
+ shows (dot-paths allowed); an unknown path fails before any request, with the
148
+ available ones in `details.available`; a user's schema-defined `attributes`
149
+ accept any key. `--json` always carries the whole resource regardless.
150
+ - `get <id>` emits the resource as `data`. Its human rendering lays the record
151
+ out field by field on a terminal and prints the whole object when piped;
152
+ `--json` is unchanged either way.
153
+ - `create` / `update <id>` take the body either as one flag per schema field
154
+ (`--name`, `--relation`, …; run `<resource> create --help` for the
155
+ list, where required fields are marked `(required)`) or as a whole JSON
156
+ object via `--data '<json>'` / `--file <path>` (`--file -` reads stdin). A
157
+ field flag overrides the same key in `--data`. A user's schema-defined
158
+ `attributes` are set with the repeatable `--attributes`, where `key=value` is
159
+ always a string and `key:=value` parses the value as JSON — use `:=` for a
160
+ field the user schema types as a number, boolean, null, array, or object
161
+ (`--attributes age:=42`), and `=` to keep a numeric-looking identifier a
162
+ string (`--attributes postcode=02139`). A missing
163
+ required field fails with `E_VALIDATION`, naming the flags in `message` and
164
+ listing their wire names in `details.missing`. The body is validated against
165
+ the API schema locally, and the server's resource is emitted as `data`. A create adds
166
+ `data.next_commands` pointing at the matching `get`. `--dry-run` emits
167
+ `{ dry_run: true, verb, topic, body }` without calling the platform.
168
+ - The destructive verb (`delete`, `revoke`, `deactivate`) requires `--force` in non-interactive mode (declared per command, so its help says what it permits)
169
+ (the error's `next_commands` carries the exact retry) and report what the API
170
+ did: `{ id, deleted: true }` for users and grants, `{ id, revoked: true }` for
171
+ sessions, and `{ id, deactivated: true }` for teams, whose DELETE deactivates
172
+ the team and leaves it readable (ADR 024). Read the property that accompanies
173
+ `id` rather than assuming `deleted`.
174
+
175
+ ```sh
176
+ npx @zitadel/cli@alpha users list --filter status=active --sort created_at:desc --non-interactive --json
177
+ npx @zitadel/cli@alpha users create --schema sch_… --attributes email=a@b.c --non-interactive --json
178
+ npx @zitadel/cli@alpha sessions revoke sess_… --force --non-interactive --json
179
+ ```
180
+
83
181
  ## Commands
84
182
 
183
+ The groups below mirror the ones `zitadel --help` prints.
184
+
185
+ ### Project commands
186
+
85
187
  - `setup` — create a Zitadel project and scaffold local auth (routes,
86
188
  middleware, `.zitadel/**`, env templates). Setup writes the versioned local
87
189
  default user schema and login flow into
88
190
  `.zitadel/schemas/default-human-user.json` and
89
191
  `.zitadel/flows/default-login.json`, uploads them through the schema and flow
90
- APIs, then seeds `.zitadel/state.json` so `plan` is immediately empty. Agents
192
+ APIs, then seeds `.zitadel/state.json` so `plan` is immediately empty.
193
+ Against a local server that hosts the platform project and has a local admin
194
+ (the default `start` configuration), setup also attaches the project to the
195
+ local admin's team: it writes `team_id` and `claimed_at` into
196
+ `.zitadel/secret` and prints
197
+ `Project owned by admin@zitadel.localhost (team ...)`, so a later `claim`
198
+ returns `status: "skipped"` with `reason: "already-claimed"`. The step is
199
+ best-effort. When the attempt fails on a platform-hosting runtime, setup
200
+ warns and the normal claim nudge applies. When `start` opted out of the
201
+ platform bootstrap there is no local admin and no claim surface, so setup
202
+ skips the step silently, emits no nudge, and the project has no owning
203
+ team. Agents
91
204
  must pass `--framework` when scaffolding into a fresh directory; interactive
92
205
  humans can omit it and choose from the prompt. Supported floors: Next.js 15+
93
206
  and React 18+ — `setup` and `doctor` fail with `E_UNSUPPORTED_PROJECT_SHAPE`
@@ -115,12 +228,9 @@ the CLI's help layer, not the envelope.
115
228
  email only; `consumer` adds given and family name; `business` also adds a
116
229
  `companyName` attribute and overlays work-email copy on the generated auth
117
230
  pages via the SDK's `businessLocales`; asked before `--preset` and recorded
118
- in `zitadel.json`), `--design centered|split|split-right|hero|minimal`
119
- (starter login design: ejects the design's template into
120
- `.zitadel/branding/` and publishes it as branding revision 1 during setup;
121
- the interactive wizard asks this as its final question with the built-in
122
- template preselected — omit the flag in non-interactive runs to keep the
123
- built-in template and no branding files), `--skip-install`.
231
+ in `zitadel.json`), `--skip-install`. Setup does not ask about or apply a
232
+ login design: it writes no `.zitadel/branding/` files and publishes no
233
+ branding revision (`branding eject` is the opt-in for that).
124
234
  On Next and Nuxt, the scaffolded auth/profile pages derive their embedding
125
235
  posture from the app: a fresh scaffold (setup created the skeleton) pins
126
236
  `variant="page"` full-page chrome, while a pre-existing app embeds
@@ -139,68 +249,9 @@ the CLI's help layer, not the envelope.
139
249
  link color `--zl-link`); the `suppress-header` attribute
140
250
  (wrapper prop `suppressHeader`) visually hides the widget's own heading
141
251
  block when the page already carries one, keeping it in the accessibility
142
- tree. Split-family designs collapse their brand pane by container width —
143
- at card width they show the compact brand mark (`logo_url`, else
144
- `hero_url`, from `.zitadel/branding/branding.json`; `hero` falls back to
145
- editable text), and setup warns when a widget-posture app picks `split`
146
- or `split-right`.
147
- - `plan` — validate config and preview the sync diff without mutating anything.
148
- - `apply` — validate and upload repo config to the platform.
149
- - `plan` and `apply --dry-run` also emit `data.warnings`: non-blocking
150
- findings as `{path, rule, message}`, the same text the human plan prints as
151
- `# warning:` lines and `apply` prints through stderr. They never fail a run.
152
- Two families exist today: flow-definition rules (`warn/…`, mirrored from the
153
- server's validator) and branding asset reachability. `warn/asset-unreachable`
154
- and `warn/asset-content-type` come from a bounded HEAD probe of
155
- `logo_url` / `hero_url` — a URL that is well-formed but dead passes every
156
- gate and then renders as a 0×0 image with nothing in the console, so the
157
- probe is the only place it can be caught. It is advisory by design: the
158
- machine planning is not necessarily the machine that renders the login page.
159
- The probe only contacts public HTTPS destinations and re-checks every
160
- redirect; loopback/private/internal targets stay inconclusive instead of
161
- turning repo config into a network request from the planning host.
162
- Set `ZITADEL_SKIP_ASSET_PROBE` to turn it off (offline, air-gapped CI, or a
163
- CDN that only resolves from production) and `ZITADEL_ASSET_PROBE_TIMEOUT_MS`
164
- to retune the per-URL budget (default 2500).
165
- - In the human-readable plan, a multi-line field (branding's inlined
166
- `liquid_template`) renders as `(<n> lines, sha256:…)` when it is created or
167
- unchanged, and as a changed-line diff when it moved — not as one escaped
168
- line. Read the file itself for full content.
169
- - `schemas list` — inspect the revision history of a user-schema, filtered by
170
- `--object-type` (e.g. `human-user`). Non-interactive/`--json` prints one row
171
- per revision (newest first); interactive adds a picker that fetches and
172
- pretty-prints the selected revision body.
173
- - `doctor` — verify generated app files and local state once `zitadel.json`
174
- exists. The `managed-files` check compares the scaffolded app files against
175
- the manifest setup recorded in `.zitadel/state.json`: a missing
176
- infrastructure file (the request boundary, `custom-elements.d.ts`) fails,
177
- a missing generated page warns, and files you edited (marker kept) or
178
- replaced (marker removed) pass as `edited`/`adopted`. It also verifies the
179
- managed config wirings (Vite/Nuxt proxy merges, Angular's `angular.json`
180
- proxy and auth routes) through the patchers' idempotent transforms — a
181
- detached or missing wiring config fails, an unverifiable one warns, and
182
- `--fix` re-applies it. The Next/Nuxt `dev` script is verified the same way,
183
- against the port recorded as the development issuer rather than the port the
184
- script names today: a script moved off that port reports as an unapplied
185
- config edit (a warning — a `dev` script is not the only way to choose a
186
- port), and `--fix` restores the registered one. Boundary migrations converge: a pristine leftover
187
- `middleware.ts` from a Next 15→16 upgrade is swapped for `proxy.ts`, while
188
- an edited one is reported as a conflict instead of creating both (Next
189
- rejects the pair). The default local
190
- runtime is the `@zitadel/server` npm binary; Docker checks apply only when
191
- using `--runtime docker` or `--image`. `--fix` restores missing managed
192
- files and never replaces an existing scaffolded app file; additive repairs
193
- (missing `.gitignore` entries, `.env.example` keys) still append to their
194
- targets, and the SDK dependency is re-added only when absent — an existing
195
- version pin is never rewritten. The `dependency-version` check warns when
196
- an exactly-pinned `@zitadel/*` dependency does not match the CLI's own
197
- version (the packages release as one train, and a floating
198
- `npx @zitadel/cli@alpha` can run ahead of the app's pins); ranges,
199
- dist-tags, and `file:`/`workspace:` specifiers express a deliberate choice
200
- and are not compared. The repair — an exact-pin install command for the
201
- project's detected package manager — is emitted in `data.next_commands`
202
- and quoted in the warning message.
203
- - `claim` — attach the project to a team so it becomes permanent. Mints a
252
+ tree. Page layout around the widget (split screens, hero panes) is the
253
+ app's own code, not a Zitadel template.
254
+ - `claim` — claim the project for a team to make it permanent. Mints a
204
255
  short-lived link, opens it in a browser, and blocks until the developer
205
256
  finishes signing in there, then records `claimed_at` and `team_id` in
206
257
  `.zitadel/secret`. Nothing about the project changes: the issuer, users,
@@ -214,7 +265,8 @@ the CLI's help layer, not the envelope.
214
265
  possible within 14 days of project creation: past that the platform answers
215
266
  `410 proj.claim_window_expired`, the command exits `E_VALIDATION`, and a
216
267
  fresh link does **not** help — only a fresh `setup` yields a claimable
217
- project (the old one keeps working, it just can't be attached anymore).
268
+ project (the old one can no longer be claimed; it stays temporary and its
269
+ data may be lost).
218
270
  `--dry-run` stops before
219
271
  anything is minted and reports `status: "skipped"`, `reason: "dry-run"` —
220
272
  there is nothing to preview, because a claim is decided in a browser.
@@ -241,9 +293,41 @@ the CLI's help layer, not the envelope.
241
293
  it probes the runtime document and nudges only when the server hosts the
242
294
  platform plane (`platform.bootstrap_project`), where the claim can actually
243
295
  complete.
244
- - `status` — summarize the local runtime and project state.
296
+ - `doctor` — verify generated app files and local state once `zitadel.json`
297
+ exists. The `managed-files` check compares the scaffolded app files against
298
+ the manifest setup recorded in `.zitadel/state.json`: a missing
299
+ infrastructure file (the request boundary, `custom-elements.d.ts`) fails,
300
+ a missing generated page warns, and files you edited (marker kept) or
301
+ replaced (marker removed) pass as `edited`/`adopted`. It also verifies the
302
+ managed config wirings (Vite/Nuxt proxy merges, Angular's `angular.json`
303
+ proxy and auth routes) through the patchers' idempotent transforms — a
304
+ detached or missing wiring config fails, an unverifiable one warns, and
305
+ `--fix` re-applies it. The Next/Nuxt `dev` script is verified the same way,
306
+ against the port recorded as the development issuer rather than the port the
307
+ script names today: a script moved off that port reports as an unapplied
308
+ config edit (a warning — a `dev` script is not the only way to choose a
309
+ port), and `--fix` restores the registered one. Boundary migrations converge: a pristine leftover
310
+ `middleware.ts` from a Next 15→16 upgrade is swapped for `proxy.ts`, while
311
+ an edited one is reported as a conflict instead of creating both (Next
312
+ rejects the pair). The default local
313
+ runtime is the `@zitadel/server` npm binary; Docker checks apply only when
314
+ using `--runtime docker` or `--image`. `--fix` restores missing managed
315
+ files and never replaces an existing scaffolded app file; additive repairs
316
+ (missing `.gitignore` entries, `.env.example` keys) still append to their
317
+ targets, and the SDK dependency is re-added only when absent — an existing
318
+ version pin is never rewritten. The `dependency-version` check warns when
319
+ an exactly-pinned `@zitadel/*` dependency does not match the CLI's own
320
+ version (the packages release as one train, and a floating
321
+ `npx @zitadel/cli@alpha` can run ahead of the app's pins); ranges,
322
+ dist-tags, and `file:`/`workspace:` specifiers express a deliberate choice
323
+ and are not compared. The repair — an exact-pin install command for the
324
+ project's detected package manager — is emitted in `data.next_commands`
325
+ and quoted in the warning message.
245
326
  - `eject` (alias `uninstall`) — remove managed files and local Zitadel state;
246
327
  requires `--force` when non-interactive.
328
+
329
+ ### Local server commands
330
+
247
331
  - `start` — start the managed local Zitadel server and persist runtime metadata
248
332
  under `.zitadel/local/runtime.json`. The binary runtime defaults to SQLite
249
333
  under `.zitadel/local/nextgen-data/`. Runtime metadata reports the published
@@ -251,11 +335,43 @@ the CLI's help layer, not the envelope.
251
335
  `dev+<short-commit>` source build it launched. That label names the revision
252
336
  the binary was built from, which after a Moon cache hit can be an earlier
253
337
  commit whose server sources are byte-identical. Use `--runtime docker` or
254
- `--image` for the Docker backend.
338
+ `--image` for the Docker backend. The project's env files configure the
339
+ local server: every `NEXTGEN_*` variable in `.env.local` and `.env` (the
340
+ former wins; empty values are skipped) is handed to the runtime through its
341
+ environment only (bare `--env NAME` on Docker), so no value reaches `argv`,
342
+ logs, `runtime.json`, or `--json`. The address, data dir and public base the
343
+ CLI sets itself always win. `data.runtime.env` and `runtime.json` carry
344
+ `injected`, the list of names. A running runtime is not updated in place:
345
+ after changing a value run `stop` then `start`. An unreadable env file fails
346
+ `start` with `E_VALIDATION` before any runtime is stopped. `setup` writes a
347
+ comment saying so at the top of the scaffolded `.env.example` and
348
+ `.env.local`.
349
+ The server boots with the platform project
350
+ and a local admin, so the developer exists on their own server without
351
+ signing up: the admin signs in as `admin@zitadel.localhost`, and a
352
+ generated password is kept in `.zitadel/local/admin.json` (gitignored with
353
+ the rest of `.zitadel/local/`) and never printed. `start` prints a one-time
354
+ console sign-in link and reports it as `data.console.sign_in_url` with
355
+ `data.console.signed_in_as`; if no link can be minted (for example a data
356
+ directory from before the local admin existed), `data.console.error` and
357
+ `data.console.hint` say why, `start` still succeeds, and `zitadel console`
358
+ drops out of `next_commands`. Setting `NEXTGEN_PLATFORM_BOOTSTRAP_PROJECT=false`,
359
+ in the shell or in `.env.local` / `.env`, opts out of both the platform project
360
+ and the local admin, for harnesses that
361
+ want a bare single-project server; `data.console` is then absent.
362
+ - `console` — print (and, interactively, open) a fresh one-time sign-in link
363
+ for the local console as the local admin: `data.sign_in_url`,
364
+ `data.signed_in_as`, `data.browser_opened`. Each link works once; run the
365
+ command again for a new one. The console honours the link only when it is
366
+ served from loopback, since the token signs in whoever opens it. Fails with `E_VALIDATION` when `start` never
367
+ created a local admin in this directory. Flags: `--no-open`.
255
368
  - `stop` — stop the managed runtime while preserving
256
369
  `.zitadel/local/nextgen-data`. Use `stop --all` to sweep all discovered
257
370
  host-wide CLI-managed local runtime processes, including healthy runtimes
258
371
  from other local projects; it does not kill arbitrary `/healthz` listeners.
372
+ - `status` — summarize the local runtime and project state.
373
+ `data.server.runtime.env.injected` repeats the variable names recorded at
374
+ the last `start` (empty for a runtime started before this field existed).
259
375
  - `logs` — print managed runtime logs; `--follow` streams in human mode.
260
376
  - `reset` — stop/remove the managed runtime and delete local runtime data;
261
377
  requires `--force` when non-interactive.
@@ -264,6 +380,66 @@ Alpha releases are fixed product package trains. `npx @zitadel/cli@alpha start`
264
380
  uses the matching `@zitadel/server` package by default. `zitadel start --runtime
265
381
  docker --image <ref>` remains the explicit image override for debugging.
266
382
 
383
+ ### Configuration commands
384
+
385
+ - `plan` — validate config and preview the sync diff without mutating anything.
386
+ - `apply` — validate and upload repo config to the platform.
387
+ - `plan` and `apply --dry-run` also emit `data.warnings`: non-blocking
388
+ findings as `{path, rule, message}`, the same text the human plan prints as
389
+ `# warning:` lines and `apply` prints through stderr. They never fail a run.
390
+ Two families exist today: flow-definition rules (`warn/…`, mirrored from the
391
+ server's validator) and branding asset reachability. `warn/asset-unreachable`
392
+ and `warn/asset-content-type` come from a bounded HEAD probe of
393
+ `logo_url` / `hero_url` — a URL that is well-formed but dead passes every
394
+ gate and then renders as a 0×0 image with nothing in the console, so the
395
+ probe is the only place it can be caught. It is advisory by design: the
396
+ machine planning is not necessarily the machine that renders the login page.
397
+ The probe only contacts public HTTPS destinations and re-checks every
398
+ redirect; loopback/private/internal targets stay inconclusive instead of
399
+ turning repo config into a network request from the planning host.
400
+ Set `ZITADEL_SKIP_ASSET_PROBE` to turn it off (offline, air-gapped CI, or a
401
+ CDN that only resolves from production) and `ZITADEL_ASSET_PROBE_TIMEOUT_MS`
402
+ to retune the per-URL budget (default 2500).
403
+ - In the human-readable plan, a multi-line field (branding's inlined
404
+ `liquid_template`) renders as `(<n> lines, sha256:…)` when it is created or
405
+ unchanged, and as a changed-line diff when it moved — not as one escaped
406
+ line. Read the file itself for full content.
407
+ - No command takes `--environment` (`-e`, `--env`). `plan`, `apply` and the
408
+ resource commands work on the project's resources and never selected an
409
+ environment; `variables` names its owner with `--project-level`. The flag
410
+ returns when the platform's environments settle, and `deploy` (ADR 035) is
411
+ what will put config onto one.
412
+ - `schemas list` — inspect the revision history of a user-schema, filtered by
413
+ `--object-type` (e.g. `human-user`). Non-interactive/`--json` prints one row
414
+ per revision (newest first); interactive adds a picker that fetches and
415
+ pretty-prints the selected revision body.
416
+ - `branding eject` — take ownership of the login template: scaffold
417
+ `.zitadel/branding/` (a `branding.json` descriptor plus the `login.liquid`
418
+ template) from a shipped design, `--design centered|minimal` (the default card, or
419
+ the same form without card chrome) or an interactive picker on a TTY. `plan`/`apply` then publish every edit as
420
+ a new branding revision.
421
+ - `variables list|get|set|delete` — manage the variables and secrets a
422
+ configuration document references as `${{ NAME }}`. Every command addresses
423
+ one owner, and `--project-level` is the only one the CLI can name today, so it
424
+ is **required**: a run without it fails with `E_VALIDATION`, on a terminal as
425
+ in a script. The platform also keeps variables per environment, but the CLI
426
+ cannot address those until the platform's environments settle; `--environment`
427
+ returns then, and every command written today keeps its meaning because the
428
+ owner was named rather than assumed. Owners do not inherit from one another —
429
+ a value entered at the project level is **not** seen by an environment
430
+ (ADR 062 §4). `set` takes its value from a prompt or from stdin and never from
431
+ a flag, so a credential never reaches `argv`; `--secret` stores it encrypted,
432
+ after which it can be replaced but never read back (`list` reports it as held,
433
+ and `--json` omits the value key entirely). `set --as number|boolean` stores a
434
+ JSON number or boolean instead of a string, so a whole-field `${{ NAME }}`
435
+ reference resolves to that type; it is refused with `--secret`, and an integer
436
+ too large to store exactly is refused rather than rounded. Output follows the
437
+ resource commands: on a pipe, `list` prints tab-separated `name`/`value` rows
438
+ (a secret's value is `(secret)`) and `get` prints the whole record as JSON,
439
+ which carries no `value` key for a secret. There is no `pull` and no bulk
440
+ import. `set` and `delete` honour `--dry-run` and make no change; `delete`
441
+ needs `--force` when non-interactive.
442
+
267
443
  ## Golden path
268
444
 
269
445
  ```sh
@@ -332,10 +508,9 @@ then re-run `plan` and `apply`. Schema and flow files are synced from
332
508
  `.zitadel/schemas/*.json` and `.zitadel/flows/*.json`. Login templates
333
509
  (branding) are synced from `.zitadel/branding/`: a single `branding.json`
334
510
  descriptor (layout, asset URLs) plus a sibling `login.liquid` LiquidJS
335
- template referenced via `liquid_template_file`. Scaffold them with the
336
- `branding eject` command (`--design centered|split|split-right|hero|minimal`,
337
- interactive picker on a TTY) or at project creation with
338
- `setup --design <name>`, which also publishes revision 1. Branding is
511
+ template referenced as `"liquid_template": { "$file": "./login.liquid" }`. Scaffold them with the
512
+ `branding eject` command (`--design centered|minimal`,
513
+ interactive picker on a TTY); setup never scaffolds them. Branding is
339
514
  revisioned and immutable: every edit — including a `.liquid`-only edit —
340
515
  plans as a `revise` and `apply` publishes a new revision; the login serves
341
516
  the newest one. `plan` validates templates with the authoritative LiquidJS
@@ -345,9 +520,13 @@ validator (`E_VALIDATION` lists rule ids such as `no-script-tag` and
345
520
  must be absolute `https://`. Keep exactly one descriptor in
346
521
  `.zitadel/branding/` — extra `*.json` files there fail the scan.
347
522
  Server-provisioned defaults remain a fallback for non-CLI project
348
- creation, but CLI-created projects are authored from local files first. Flow create, read, list,
349
- update, and delete are available, while the server enforces lifecycle rules
350
- such as draft-only edits. Managed files carry a marker comment; `eject` removes only
523
+ creation, but CLI-created projects are authored from local files first. Flows are
524
+ revisioned like branding: an edit plans as a `revise` and `apply` publishes a new
525
+ immutable flow revision; a schema revise re-publishes the flows pinned to it with
526
+ the new `user_schema` in the same run. A login pinned by `flow-name` serves that
527
+ flow's newest revision; an unpinned login serves the newest active unscoped flow
528
+ in the project, whatever its name. Removing a flow file does not retire the
529
+ flow. Managed files carry a marker comment; `eject` removes only
351
530
  files that still carry it, preserving anything the user replaced. For app-local
352
531
  development, `--server local` resolves through `.zitadel/local/runtime.json` and
353
532
  requires a healthy
@@ -0,0 +1,22 @@
1
+ //#region src/lib/oclif/groups.ts
2
+ /**
3
+ * The groups the root help lists commands under, declared in print order: the
4
+ * journey runs from setting up a project, to running the local server, to
5
+ * managing its configuration, to acting on the resources inside it. Every command declares its own group
6
+ * (`static group` on the class) so nothing has to be registered centrally; a
7
+ * command without one lands in the trailing {@link ADDITIONAL_COMMANDS_GROUP}
8
+ * with oclif's own utilities.
9
+ */
10
+ const CommandGroups = {
11
+ project: "Project commands",
12
+ localServer: "Local server commands",
13
+ configuration: "Configuration commands",
14
+ resources: "Resource commands"
15
+ };
16
+ const COMMAND_GROUPS = Object.values(CommandGroups);
17
+ /** Where ungrouped commands (oclif's `autocomplete`, `search`, …) are listed. */
18
+ const ADDITIONAL_COMMANDS_GROUP = "Additional commands";
19
+ //#endregion
20
+ export { COMMAND_GROUPS as n, CommandGroups as r, ADDITIONAL_COMMANDS_GROUP as t };
21
+
22
+ //# sourceMappingURL=groups-CMB0fMzE.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"groups-CMB0fMzE.mjs","names":[],"sources":["../src/lib/oclif/groups.ts"],"sourcesContent":["/**\n * The groups the root help lists commands under, declared in print order: the\n * journey runs from setting up a project, to running the local server, to\n * managing its configuration, to acting on the resources inside it. Every command declares its own group\n * (`static group` on the class) so nothing has to be registered centrally; a\n * command without one lands in the trailing {@link ADDITIONAL_COMMANDS_GROUP}\n * with oclif's own utilities.\n */\nexport const CommandGroups = {\n project: \"Project commands\",\n localServer: \"Local server commands\",\n configuration: \"Configuration commands\",\n resources: \"Resource commands\",\n} as const;\n\nexport type CommandGroup = (typeof CommandGroups)[keyof typeof CommandGroups];\n\nexport const COMMAND_GROUPS: readonly CommandGroup[] = Object.values(CommandGroups);\n\n/** Where ungrouped commands (oclif's `autocomplete`, `search`, …) are listed. */\nexport const ADDITIONAL_COMMANDS_GROUP = \"Additional commands\";\n"],"mappings":";;;;;;;;;AAQA,MAAa,gBAAgB;CAC3B,SAAS;CACT,aAAa;CACb,eAAe;CACf,WAAW;CACZ;AAID,MAAa,iBAA0C,OAAO,OAAO,cAAc;;AAGnF,MAAa,4BAA4B"}