@xano/cli 1.2.0 → 1.3.1-beta.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.
Files changed (192) hide show
  1. package/README.md +381 -4
  2. package/dist/base-command.d.ts +54 -36
  3. package/dist/base-command.js +246 -169
  4. package/dist/commands/auth/index.d.ts +1 -1
  5. package/dist/commands/auth/index.js +7 -7
  6. package/dist/commands/branch/create/index.d.ts +1 -1
  7. package/dist/commands/branch/create/index.js +1 -1
  8. package/dist/commands/branch/set_live/index.d.ts +22 -0
  9. package/dist/commands/branch/set_live/index.js +60 -1
  10. package/dist/commands/ephemeral/create/index.d.ts +18 -0
  11. package/dist/commands/ephemeral/create/index.js +103 -0
  12. package/dist/commands/ephemeral/delete/index.d.ts +18 -0
  13. package/dist/commands/ephemeral/delete/index.js +99 -0
  14. package/dist/commands/ephemeral/edit/index.d.ts +18 -0
  15. package/dist/commands/ephemeral/edit/index.js +109 -0
  16. package/dist/commands/ephemeral/get/index.d.ts +21 -0
  17. package/dist/commands/ephemeral/get/index.js +125 -0
  18. package/dist/commands/ephemeral/impersonate/index.d.ts +20 -0
  19. package/dist/commands/ephemeral/impersonate/index.js +120 -0
  20. package/dist/commands/ephemeral/list/index.d.ts +19 -0
  21. package/dist/commands/ephemeral/list/index.js +123 -0
  22. package/dist/commands/ephemeral/pull/index.d.ts +19 -0
  23. package/dist/commands/ephemeral/pull/index.js +163 -0
  24. package/dist/commands/ephemeral/push/index.d.ts +35 -0
  25. package/dist/commands/ephemeral/push/index.js +228 -0
  26. package/dist/commands/ephemeral/static_host/build/delete/index.d.ts +20 -0
  27. package/dist/commands/ephemeral/static_host/build/delete/index.js +121 -0
  28. package/dist/commands/ephemeral/static_host/build/get/index.d.ts +18 -0
  29. package/dist/commands/ephemeral/static_host/build/get/index.js +137 -0
  30. package/dist/commands/ephemeral/static_host/build/list/index.d.ts +18 -0
  31. package/dist/commands/ephemeral/static_host/build/list/index.js +137 -0
  32. package/dist/commands/ephemeral/static_host/build/pull/index.d.ts +53 -0
  33. package/dist/commands/ephemeral/static_host/build/pull/index.js +308 -0
  34. package/dist/commands/ephemeral/static_host/build/push/index.d.ts +24 -0
  35. package/dist/commands/ephemeral/static_host/build/push/index.js +250 -0
  36. package/dist/commands/ephemeral/static_host/create/index.d.ts +18 -0
  37. package/dist/commands/ephemeral/static_host/create/index.js +92 -0
  38. package/dist/commands/ephemeral/static_host/deploy/index.d.ts +19 -0
  39. package/dist/commands/ephemeral/static_host/deploy/index.js +112 -0
  40. package/dist/commands/ephemeral/static_host/edit/index.d.ts +24 -0
  41. package/dist/commands/ephemeral/static_host/edit/index.js +158 -0
  42. package/dist/commands/ephemeral/static_host/get/index.d.ts +19 -0
  43. package/dist/commands/ephemeral/static_host/get/index.js +101 -0
  44. package/dist/commands/ephemeral/static_host/list/index.d.ts +17 -0
  45. package/dist/commands/ephemeral/static_host/list/index.js +132 -0
  46. package/dist/commands/flatten/index.d.ts +34 -0
  47. package/dist/commands/flatten/index.js +140 -0
  48. package/dist/commands/function/create/index.d.ts +15 -0
  49. package/dist/commands/function/create/index.js +41 -1
  50. package/dist/commands/function/edit/index.d.ts +15 -0
  51. package/dist/commands/function/edit/index.js +66 -32
  52. package/dist/commands/function/get/index.js +2 -3
  53. package/dist/commands/function/run/index.d.ts +1 -0
  54. package/dist/commands/function/run/index.js +19 -6
  55. package/dist/commands/policy/catalogue/index.d.ts +15 -0
  56. package/dist/commands/policy/catalogue/index.js +29 -0
  57. package/dist/commands/policy/create/index.d.ts +18 -0
  58. package/dist/commands/policy/create/index.js +39 -0
  59. package/dist/commands/policy/delete/index.d.ts +19 -0
  60. package/dist/commands/policy/delete/index.js +61 -0
  61. package/dist/commands/policy/evaluate/index.d.ts +32 -0
  62. package/dist/commands/policy/evaluate/index.js +71 -0
  63. package/dist/commands/policy/list/index.d.ts +14 -0
  64. package/dist/commands/policy/list/index.js +21 -0
  65. package/dist/commands/policy/parse/index.d.ts +19 -0
  66. package/dist/commands/policy/parse/index.js +18 -0
  67. package/dist/commands/policy/publish/index.d.ts +20 -0
  68. package/dist/commands/policy/publish/index.js +38 -0
  69. package/dist/commands/policy/runs/index.d.ts +54 -0
  70. package/dist/commands/policy/runs/index.js +273 -0
  71. package/dist/commands/policy/status/index.d.ts +21 -0
  72. package/dist/commands/policy/status/index.js +54 -0
  73. package/dist/commands/profile/create/index.d.ts +1 -1
  74. package/dist/commands/profile/create/index.js +7 -7
  75. package/dist/commands/profile/me/index.js +1 -1
  76. package/dist/commands/profile/use/index.js +0 -4
  77. package/dist/commands/profile/wizard/index.d.ts +1 -1
  78. package/dist/commands/profile/wizard/index.js +12 -10
  79. package/dist/commands/profile/workspace/set/index.js +1 -1
  80. package/dist/commands/release/create/index.js +7 -0
  81. package/dist/commands/release/deploy/index.d.ts +28 -0
  82. package/dist/commands/release/deploy/index.js +90 -6
  83. package/dist/commands/release/edit/index.js +2 -2
  84. package/dist/commands/release/export/index.js +1 -2
  85. package/dist/commands/release/import/index.js +6 -2
  86. package/dist/commands/release/list/index.d.ts +7 -0
  87. package/dist/commands/release/list/index.js +54 -10
  88. package/dist/commands/release/pull/index.d.ts +0 -6
  89. package/dist/commands/release/pull/index.js +13 -99
  90. package/dist/commands/release/push/index.js +21 -3
  91. package/dist/commands/sandbox/env/get_all/index.js +1 -1
  92. package/dist/commands/sandbox/env/set_all/index.js +1 -1
  93. package/dist/commands/sandbox/get/index.js +1 -1
  94. package/dist/commands/sandbox/license/get/index.js +1 -1
  95. package/dist/commands/sandbox/license/set/index.js +1 -1
  96. package/dist/commands/sandbox/pull/index.d.ts +0 -1
  97. package/dist/commands/sandbox/pull/index.js +13 -81
  98. package/dist/commands/sandbox/push/index.d.ts +1 -1
  99. package/dist/commands/sandbox/push/index.js +11 -6
  100. package/dist/commands/skills/pull/index.d.ts +30 -0
  101. package/dist/commands/skills/pull/index.js +118 -0
  102. package/dist/commands/static_host/build/create/index.d.ts +1 -1
  103. package/dist/commands/static_host/build/create/index.js +2 -2
  104. package/dist/commands/static_host/build/pull/index.js +1 -1
  105. package/dist/commands/static_host/build/push/index.js +1 -1
  106. package/dist/commands/static_host/edit/index.js +1 -1
  107. package/dist/commands/tenant/backup/export/index.js +1 -2
  108. package/dist/commands/tenant/backup/import/index.js +1 -1
  109. package/dist/commands/tenant/cluster/create/index.js +1 -1
  110. package/dist/commands/tenant/cluster/license/get/index.js +1 -1
  111. package/dist/commands/tenant/cluster/license/set/index.js +1 -1
  112. package/dist/commands/tenant/create/index.d.ts +4 -1
  113. package/dist/commands/tenant/create/index.js +26 -5
  114. package/dist/commands/tenant/deploy_platform/index.js +1 -1
  115. package/dist/commands/tenant/deploy_release/index.d.ts +40 -0
  116. package/dist/commands/tenant/deploy_release/index.js +186 -4
  117. package/dist/commands/tenant/edit/index.d.ts +2 -0
  118. package/dist/commands/tenant/edit/index.js +46 -11
  119. package/dist/commands/tenant/env/get_all/index.js +1 -1
  120. package/dist/commands/tenant/env/set_all/index.js +1 -1
  121. package/dist/commands/tenant/impersonate/index.d.ts +1 -1
  122. package/dist/commands/tenant/impersonate/index.js +14 -14
  123. package/dist/commands/tenant/license/get/index.js +1 -1
  124. package/dist/commands/tenant/license/set/index.js +1 -1
  125. package/dist/commands/tenant/pull/index.d.ts +0 -6
  126. package/dist/commands/tenant/pull/index.js +14 -100
  127. package/dist/commands/tenant_deploy_request/bypass/index.d.ts +31 -0
  128. package/dist/commands/tenant_deploy_request/bypass/index.js +109 -0
  129. package/dist/commands/tenant_deploy_request/create/index.d.ts +21 -0
  130. package/dist/commands/tenant_deploy_request/create/index.js +108 -0
  131. package/dist/commands/tenant_deploy_request/edit/index.d.ts +22 -0
  132. package/dist/commands/tenant_deploy_request/edit/index.js +103 -0
  133. package/dist/commands/tenant_deploy_request/get/index.d.ts +25 -0
  134. package/dist/commands/tenant_deploy_request/get/index.js +116 -0
  135. package/dist/commands/tenant_deploy_request/list/index.d.ts +20 -0
  136. package/dist/commands/tenant_deploy_request/list/index.js +122 -0
  137. package/dist/commands/tenant_deploy_request/revision/index.d.ts +21 -0
  138. package/dist/commands/tenant_deploy_request/revision/index.js +83 -0
  139. package/dist/commands/tenant_deploy_request/set_status/index.d.ts +38 -0
  140. package/dist/commands/tenant_deploy_request/set_status/index.js +159 -0
  141. package/dist/commands/workspace/git/pull/index.d.ts +4 -4
  142. package/dist/commands/workspace/git/pull/index.js +158 -97
  143. package/dist/commands/workspace/pull/index.d.ts +9 -5
  144. package/dist/commands/workspace/pull/index.js +83 -106
  145. package/dist/commands/workspace/push/index.d.ts +19 -0
  146. package/dist/commands/workspace/push/index.js +124 -5
  147. package/dist/help.js +4 -2
  148. package/dist/policy-command.d.ts +90 -0
  149. package/dist/policy-command.js +94 -0
  150. package/dist/update-check.d.ts +1 -1
  151. package/dist/update-check.js +1 -1
  152. package/dist/utils/document-parser.d.ts +126 -0
  153. package/dist/utils/document-parser.js +332 -4
  154. package/dist/utils/flatten.d.ts +36 -0
  155. package/dist/utils/flatten.js +68 -0
  156. package/dist/utils/knowledge-sync.d.ts +1 -0
  157. package/dist/utils/knowledge-sync.js +11 -6
  158. package/dist/utils/microservice-wait.d.ts +53 -0
  159. package/dist/utils/microservice-wait.js +84 -0
  160. package/dist/utils/multidoc-push.d.ts +143 -2
  161. package/dist/utils/multidoc-push.js +479 -40
  162. package/dist/utils/paging.d.ts +2 -1
  163. package/dist/utils/paging.js +0 -1
  164. package/dist/utils/policy/catalogue.d.ts +7 -0
  165. package/dist/utils/policy/catalogue.js +72 -0
  166. package/dist/utils/policy/errors.d.ts +12 -0
  167. package/dist/utils/policy/errors.js +61 -0
  168. package/dist/utils/policy/feedback.d.ts +68 -0
  169. package/dist/utils/policy/feedback.js +165 -0
  170. package/dist/utils/policy/findings.d.ts +23 -0
  171. package/dist/utils/policy/findings.js +65 -0
  172. package/dist/utils/policy/gate.d.ts +161 -0
  173. package/dist/utils/policy/gate.js +201 -0
  174. package/dist/utils/policy/goal.d.ts +46 -0
  175. package/dist/utils/policy/goal.js +95 -0
  176. package/dist/utils/policy/permission.d.ts +23 -0
  177. package/dist/utils/policy/permission.js +84 -0
  178. package/dist/utils/policy/push-gate.d.ts +5 -0
  179. package/dist/utils/policy/push-gate.js +29 -0
  180. package/dist/utils/policy/release.d.ts +85 -0
  181. package/dist/utils/policy/release.js +115 -0
  182. package/dist/utils/policy/request.d.ts +55 -0
  183. package/dist/utils/policy/request.js +115 -0
  184. package/dist/utils/policy/runs.d.ts +30 -0
  185. package/dist/utils/policy/runs.js +101 -0
  186. package/dist/utils/policy/status.d.ts +47 -0
  187. package/dist/utils/policy/status.js +117 -0
  188. package/dist/utils/policy/types.d.ts +185 -0
  189. package/dist/utils/policy/types.js +1 -0
  190. package/dist/utils/reference-checker.js +14 -3
  191. package/oclif.manifest.json +8538 -3445
  192. package/package.json +16 -1
package/README.md CHANGED
@@ -46,16 +46,16 @@ The Metadata API pages unevenly, so `list` commands differ in what they expose.
46
46
  | Commands | Flags | Footer |
47
47
  |---|---|---|
48
48
  | `function list`, `unit_test list`, `workflow_test list` (plus `sandbox` / `tenant` variants) | `--page`, `--per_page` | `Page 2 · 50 shown · next: --page 3` |
49
- | `static_host list`, `static_host build list` | `--page` only | `Page 2 · 100 shown · 340 total` |
49
+ | `static_host list`, `static_host build list` (plus `ephemeral` variants) | `--page` only | `Page 2 · 100 shown · 340 total` |
50
50
  | `branch list`, `workspace list`, `tenant snapshot list`, `sandbox env list`, `tenant env list` | none | `12 branches` |
51
- | `tenant list`, `release list`, `platform list`, `tenant cluster list` | none | none |
51
+ | `tenant list`, `release list`, `platform list`, `tenant cluster list`, `ephemeral list` | none | none |
52
52
  | `tenant backup list` | `--page` only (pre-existing) | none |
53
53
 
54
54
  Notes:
55
55
 
56
56
  - **`--per_page` is only offered where the endpoint accepts it.** Static-host endpoints hardcode 100 items per page server-side, so those commands take `--page` alone. `xano static_host list --per_page 10` previously parsed but did nothing; it is now rejected rather than silently ignored.
57
57
  - **All paged commands default `--per_page` to 50.** The test-list commands previously requested 10000 internally so nothing was ever cut off; now that the footer and the JSON envelope both report position, a page that stops at 50 is visible rather than silent. Raise it (up to 10000) when you want everything in one call.
58
- - **The last group has no CLI paging.** Those endpoints page server-side but return a plain array with no page or total metadata, so the CLI cannot tell you where you are. Rather than show a page number with no context — or infer "more available" from a full page, which is wrong exactly when the result count is a multiple of the page size — these commands are left as-is. **They return at most 25–50 items** (50 for tenants, 25 for releases, platforms, and tenant backups). If you have more than that, query the Metadata API directly until those endpoints return paging metadata.
58
+ - **The last group has no CLI paging.** Those endpoints page server-side but return a plain array with no page or total metadata, so the CLI cannot tell you where you are. Rather than show a page number with no context — or infer "more available" from a full page, which is wrong exactly when the result count is a multiple of the page size — these commands are left as-is. **They return at most 25–50 items** (50 for tenants and ephemerals, 25 for releases, platforms, and tenant backups). If you have more than that, query the Metadata API directly until those endpoints return paging metadata.
59
59
  - **`--output json` returns an envelope, not a bare array.** Every list command emits `{items, ...}` using the Metadata API's own field names — `curPage`, `nextPage`, `prevPage`, `itemsTotal` — each included only when the endpoint actually reports it. `perPage` is the one field the CLI adds, since the API never echoes it back and it is otherwise invisible at its default. This is a **breaking change** for scripts that parsed the previous bare array — read `.items` instead.
60
60
 
61
61
  ```jsonc
@@ -170,6 +170,104 @@ xano profile delete myprofile
170
170
  xano profile delete myprofile --force
171
171
  ```
172
172
 
173
+ ### Policies
174
+
175
+ Policies combine a description with deterministic check rules; the platform parses, validates and evaluates
176
+ them. `xano policy <command> --help` gives each command's flags and output, and the Xano developer MCP documents
177
+ the rest: the `policies` topic of `xano_xanoscript_docs` (the language) and the `policy` topic of `xano_cli_docs`.
178
+
179
+ ```bash
180
+ xano policy catalogue --check object.auth_required # The checks a rule can use (all of them without --check)
181
+ xano policy create --goal endpoints_need_login -b dev # Create an independent policy from a platform goal
182
+ xano policy create --goal no_pii_in_responses --key DATA-020 --param 'fields=["email","ssn"]'
183
+ xano policy list # Policies on the branch
184
+ xano policy parse policies/AUTH-001.xs # Validate and print canonical XanoScript (or --file, --stdin)
185
+ xano policy publish policies/AUTH-001.xs -m "Why" # Create or update by key; -m labels the Version History entry
186
+ xano policy evaluate # Evaluate the branch now
187
+ xano policy evaluate --summary # The same, answering the run summary and its first 50 findings
188
+ xano policy status --fail-on-findings # The latest run, without evaluating (CI)
189
+ xano policy runs 1674 --run-detail # One run and its first 100 findings; without an id, the retained runs
190
+ xano policy runs 1674 --offset 100 --limit 100 # The next page of that run's findings (at most 500 a page)
191
+ xano policy runs 1674 --all -o json # Every finding, read page by page
192
+ xano policy runs 1674 --blocking --policy AUTH-001 # Only some findings: --blocking/--advisory, --policy, --rule,
193
+ # --severity, --kind, --object type:id, --tag (repeatable), --search
194
+ xano policy runs --release v1.2 # A release's check (stored when it was cut), paged and filtered the same way
195
+ xano policy runs --release v1.2 --recheck # Check the release again from its archive, store the run, then read it
196
+ xano policy runs --release-id 12 # Read an imported release by ID without a name lookup
197
+ xano policy delete TMP-001 # Remove a policy; its Version History is kept
198
+ ```
199
+
200
+ `policy create --goal <id>` reads the instance's goals from `policy catalogue -o json`, copies a goal's
201
+ sparse settings, validates them natively and creates an Active, Advisory policy. Required goal settings must
202
+ be filled before it saves. Repeat `--param 'N.path=JSON'` for overrides: rule numbers start at 1; the `N.`
203
+ prefix is optional for a single-rule goal. Nested paths work, for example `--param '1.when.statement="db.edit"'`.
204
+ Values are JSON, including quoted strings, arrays, booleans and numbers. The goal key gets a free numeric
205
+ suffix if already taken (case-insensitive); an explicitly taken `--key` is refused. It never updates an
206
+ existing policy. `-m/--message` labels its Version History entry; `-o json` returns the saved policy and
207
+ native rule warnings go to stderr. No continuing link to the goal is stored.
208
+
209
+ A push to the live branch is refused before commit when mandatory policies block it. The gate judges the branch by the policies it will have after the push, so the pushed policy files count, and a push that weakens a mandatory policy of the live branch (demotes, deactivates or deletes it, or loosens one of its rules) needs `workspace:policy` update and is otherwise refused before anything is imported. A 403 `policy_gate` exits **2**, says nothing was imported, and lists the blocking findings and introduced/changed counts. With `-o json` it prints `{imported: false, refused: <payload>}`. `--policy-override "reason"` sends an audited override, requiring `workspace:policy` update permission. This gate requires transactions: remove `--no-transaction` if the server refuses it (exit 1).
210
+
211
+ Every policy command takes `-w/--workspace`, `-b/--branch` (the profile's branch by default; `-b ''` is live)
212
+ and `-o/--output summary|json`. `workspace pull` and `workspace push` carry policies as `policies/<KEY>.xs`.
213
+ Policies use `active = true|false` before `enforcement`; authored `lifecycle` is refused. The list shows
214
+ `Active, Mandatory`, `Active, Advisory` or `Inactive`. Inactive policies are saved directly and skipped by
215
+ branch checks; `policy status` reports `inactive`. An inactive policy may have no rules.
216
+ `release push` carries them too, for a credential holding `workspace:policy` create and update: a release ships its
217
+ policies like its code, and a tenant deploy lands them (see Releases). Tier1 tenant, sandbox and ephemeral pushes
218
+ carry them as well; a remote tenant push, or a tenant created before policies, leaves them out and prints the
219
+ platform's notice.
220
+ While the instance's Policies feature is off, `workspace pull` exports no policy (and keeps local policy files)
221
+ and `workspace push` leaves its policy files out, printing the platform's notice instead of a policy count.
222
+
223
+ A run can hold tens of thousands of findings, so the CLI reads them a page at a time. `policy runs <id>` reads
224
+ the run's summary and one page of its findings; a page that is not every finding says `(one page)` and how to read
225
+ the next. `-o json` is the summary with `findings`, the page's findings as an array, and
226
+ `findings_page: {total, offset, limit, next_offset}`. `--all` reads every page (500 a request, saying so on stderr
227
+ when there are more) and returns every matching finding in `findings`. `policy status` reads the latest run's
228
+ summary, so its `-o json` `run` carries each policy's finding count rather than the findings. The `policy_check` a
229
+ `workspace push` prints lists the first 100 findings; when there are more it prints their counts and
230
+ `Listed: the first 100 of N; \`xano policy runs <run_id>\` has them all.` `policy evaluate --summary` does the same
231
+ with the first 50, and `policy evaluate` without it still answers the whole run.
232
+
233
+ `policy evaluate --policy AUTH-001` tries that one policy alone, even if inactive. It never stores or audits
234
+ the trial, replaces the latest run or gates anything. The output names each rule's clear objects (up to five
235
+ names, with the rest counted). Combine it with `--summary`, `--run-detail` or `-o json`; an inactive trial
236
+ does not exit 2. A blank key is refused before any request, and an unknown key gets the platform's 404.
237
+
238
+ `policy runs --release <name>` reads a release's check: the run stored when the release was cut, evaluated on what
239
+ the release ships against the policies it ships. It takes every finding flag a run read takes, `-o json` has the same
240
+ shape (plus the release's `shipped` policies), and a release with no check says so (`{"run": null}` under `-o json`).
241
+ `--recheck` checks the release again from its archive and stores the new run, so a read-only credential is refused.
242
+ `--release-id <id>` reads the same report directly by ID and also supports `--recheck`; it is exclusive with
243
+ `--release` and `--branch`.
244
+
245
+ **Exit codes** of `policy evaluate`, `workspace push` and `policy status --fail-on-findings`: `2` when a finding
246
+ blocks (an active, mandatory policy failed), whatever else happened; `1` when a request or the import failed, or
247
+ when `status --fail-on-findings` finds stale, missing or errored evidence; otherwise `0`. `tenant deploy_release`,
248
+ `tenant_deploy_request set_status` and `tenant_deploy_request bypass` exit `2` when the tenant deploy policy gate
249
+ refuses the deploy (and `tenant deploy_release --check` when it would), `1` for any other failure, otherwise `0`.
250
+ `release deploy --set_live` and `branch set_live` exit `2` when the set-live policy gate refuses to set the branch live, and `1` for any other failure; `branch set_live --policy-override "<reason>"` sets it live past the gate with an audited reason, as `release deploy` does. `function create` and `function edit` exit `2` when the live-branch publish gate refuses the save (see Functions), and `1` for any other failure. A blank `--policy-override` is refused before any request. A `workspace push`, `branch set_live`, `release deploy --set_live` or `policy delete` refused with `policy_weakening_permission_required` exits `1`, like the other permission refusals: it is a missing permission, not a blocking finding.
251
+
252
+ Each route needs the `workspace:policy` permission at the request's level. A refusal's `payload.code` names the
253
+ gate and the CLI prints its remedy: `policy_feature_disabled` (Policies are off on the instance),
254
+ `policy_permission_required` (an instance admin grants your role the level) or `policy_scope_required` (create a
255
+ Metadata API token that has the scope). A push to the live branch, a set live or a merge is judged by the policies it
256
+ leaves, so one that weakens a policy active and mandatory on the branch (demotes, deactivates or deletes it, or
257
+ loosens one of its rules; for a set live, a live mandatory policy the branch lacks counts as deleted) needs
258
+ `workspace:policy` update, and without it is refused before anything is written with
259
+ `policy_weakening_permission_required`, naming the policies. `--policy-override` does not lift it: the CLI prints the
260
+ platform's sentence and how to go on (a push can leave the policy files out with `-e "policies/*"`), and a push's
261
+ `--dry-run` is refused the same way. `policy delete` of an active, mandatory policy needs update too, not only delete,
262
+ and is refused with the same code otherwise. A set live whose check could not read the policies fails closed: only
263
+ update proceeds, and the refusal says so (`unavailable`). A rule naming a check the instance does not have (`policy_unknown_check`)
264
+ points at `xano policy catalogue`. `policy delete` sends the `updated_at` it listed, so a policy changed since is
265
+ refused (`policy_stale`) rather than deleted; it asks first, and without `--force` in a non-interactive shell it
266
+ exits 1. A create past the plan's per-branch policy cap (lower plans cap the policies on each branch; Pro and above are unlimited) is refused with
267
+ `policy_plan_limit`, naming the plan, and a `workspace push` whose new policies would not fit imports nothing.
268
+ `policy parse` and `policy publish` print rule warnings (a part of a rule that would do nothing or allow too much on
269
+ that branch) on stderr; `-o json` keeps stdout the JSON.
270
+
173
271
  ### Workspaces
174
272
 
175
273
  ```bash
@@ -197,6 +295,7 @@ xano workspace delete -w <workspace_id> --force
197
295
  xano workspace pull
198
296
  xano workspace pull -d ./my-workspace # Specify output directory
199
297
  xano workspace pull -b dev # Specific branch
298
+ xano workspace pull -b '' # The live branch, whatever the profile's branch
200
299
  xano workspace pull --env --records # Include env vars and table records
201
300
  xano workspace pull --draft # Include draft changes
202
301
 
@@ -204,6 +303,7 @@ xano workspace pull --draft # Include draft changes
204
303
  xano workspace push
205
304
  xano workspace push -d ./my-workspace # Push from a specific directory
206
305
  xano workspace push -b dev
306
+ xano workspace push -b '' # The live branch, whatever the profile's branch
207
307
  xano workspace push --sync # Full push — send all files, not just changed ones
208
308
  xano workspace push --sync --delete # Full push + delete remote objects not included
209
309
  xano workspace push --dry-run # Preview changes without pushing
@@ -216,6 +316,9 @@ xano workspace push --force # Skip preview and conf
216
316
  xano workspace push -i "function/*" # Push only matching files
217
317
  xano workspace push -e "table/*" # Push all files except tables
218
318
  xano workspace push -i "function/*" -e "**/test*" # Include functions, exclude tests
319
+ xano workspace push -o json # One JSON document: the preview (with --dry-run) or the import result with policy_check
320
+ xano workspace push -m "Tightened the auth policies" # Label the Version History entry of each policy this push changes; other objects get no message
321
+ xano workspace push --policy-override "Approved exception" # Proceed past a blocking live-branch gate with an audited reason
219
322
 
220
323
  # Pull from a git repository to local files (defaults to current directory)
221
324
  xano workspace git pull -r https://github.com/owner/repo
@@ -229,6 +332,51 @@ xano workspace git pull -r https://github.com/owner/private-repo -t ghp_xxx
229
332
  xano workspace git pull -r https://github.com/owner/repo --path subdir
230
333
  ```
231
334
 
335
+ **One workspace document per tree.** A push directory must contain at most one
336
+ `workspace/*.xs` document. The server applies the first workspace document it
337
+ receives to the workspace being pushed into and silently discards the rest,
338
+ without checking that it describes that workspace — so a second one renames the
339
+ destination to a foreign name while leaving its content untouched. `workspace push`
340
+ and `sandbox push` refuse a tree carrying more than one, naming the offending
341
+ files; there is no `--force` override, because only you know which workspace the
342
+ tree is meant to be.
343
+
344
+ Trees pick up a second one easily: `pull` names the file after the workspace
345
+ itself (`workspace/{name}.xs`), so pulling a different workspace into the same
346
+ directory *adds* a file rather than overwriting, and renaming a workspace leaves
347
+ the old-name file behind. Delete the stale ones, keeping the single document that
348
+ matches your target workspace.
349
+
350
+ ### Flatten
351
+
352
+ A hand-authored `.xs` file can hold several documents joined by `---`. That is
353
+ convenient to edit but unsupported by `push`: the partial-diff filter and GUID
354
+ writeback both parse only the first document in a file, so such a bundle
355
+ silently pushes nothing (partial mode) or corrupts GUIDs (full mode). `push`
356
+ refuses these files and offers to flatten them.
357
+
358
+ `xano flatten` performs that split on demand, producing the same
359
+ one-document-per-file tree `pull` would have written. It is purely local — no
360
+ profile, token, or network — and deletes the original bundle once the split
361
+ succeeds, unless you pass `--keep-source`.
362
+
363
+ ```bash
364
+ # Split a bundle in place into per-document files (removes the original)
365
+ xano flatten secret/pdf-micro/multidoc.xs
366
+
367
+ # Write the split files somewhere else instead of alongside the bundle
368
+ xano flatten ./bundle.xs -o ./workspace
369
+
370
+ # Preview the resulting layout without writing anything
371
+ xano flatten secret/pdf-micro/multidoc.xs --dry-run
372
+
373
+ # Flatten every multi-doc .xs under a directory, keeping the originals
374
+ xano flatten ./dir-of-bundles --keep-source
375
+
376
+ # Overwrite existing destination files instead of erroring
377
+ xano flatten ./bundle.xs --force
378
+ ```
379
+
232
380
  ### Knowledge
233
381
 
234
382
  Knowledge items are user-authored docs and skills (e.g. `CLAUDE.md`, `AGENTS.md`, runbooks)
@@ -285,6 +433,7 @@ xano branch edit <branch_label> --color "#ff0000"
285
433
  # Set live branch
286
434
  xano branch set_live <branch_label>
287
435
  xano branch set_live <branch_label> --force
436
+ xano branch set_live <branch_label> --policy-override "Rollback approved" # Past a blocking set-live policy gate (audited)
288
437
 
289
438
  # Delete a branch
290
439
  xano branch delete <branch_label>
@@ -309,6 +458,7 @@ xano function get <function_id> --include_draft # Include draft version
309
458
  xano function create -f function.xs
310
459
  xano function create -f function.xs --edit # Open in $EDITOR before creating
311
460
  cat function.xs | xano function create --stdin
461
+ xano function create -f function.xs --policy-override "Exception approved" # Past a blocking publish policy gate (audited)
312
462
 
313
463
  # Edit a function
314
464
  xano function edit <function_id> # Opens in $EDITOR
@@ -316,6 +466,7 @@ xano function edit <function_id> -f new.xs # Update from file
316
466
  xano function edit <function_id> -f new.xs --edit # Open in $EDITOR before updating
317
467
  cat function.xs | xano function edit <function_id> --stdin # Update from stdin
318
468
  xano function edit <function_id> --no-publish # Edit without publishing
469
+ xano function edit <function_id> -f new.xs --policy-override "Exception approved" # Past a blocking publish policy gate (audited)
319
470
 
320
471
  # Run (execute) a function by name
321
472
  xano function run <name> # Prompts for declared inputs (on a TTY)
@@ -323,8 +474,14 @@ xano function run <name> --data email=jo@x.com --data age:=30 --data active:=tru
323
474
  xano function run <name> --json @payload.json --data env=staging # base payload + override
324
475
  echo '{"email":"jo@x.com"}' | xano function run <name> --stdin -o json | jq .result
325
476
  xano function run <name> --branch dev --logs # run on a branch, show execution logs
477
+ xano function run <name> --datasource test # run against the 'test' data source
326
478
  ```
327
479
 
480
+ `function create` and `function edit` save a function on the live branch through the publish policy gate. When
481
+ blocking findings refuse the save, nothing is saved: the command prints the verdict and the platform's message and
482
+ exits `2` (any other failure exits `1`), and `-o json` prints `{created|updated: false, message, policy_gate}`.
483
+ `--policy-override "<reason>"` saves past the gate with an audited reason (requires `workspace:policy` update).
484
+
328
485
  Input flexibility for `function run` (assembled into one JSON `input` object):
329
486
 
330
487
  | Form | Meaning |
@@ -335,6 +492,11 @@ Input flexibility for `function run` (assembled into one JSON `input` object):
335
492
  | `--json '<inline>'` / `--json @file.json` / `--json -` | a base JSON object (stdin with `-`) |
336
493
  | `--stdin` | read the JSON object from stdin (same as `--json -`) |
337
494
 
495
+ `--datasource <label>` runs the function against a non-live data source by sending the
496
+ `X-Data-Source` header, so table reads and writes hit that data source's tables. Omit it
497
+ to run against `live`; an unknown label is rejected by the server with
498
+ `Invalid data source.`
499
+
338
500
  Merge order is JSON base first, then `--data` overrides. Missing required inputs are
339
501
  prompted for on an interactive terminal; in a non-TTY (CI) context the command fails
340
502
  listing them. Output defaults to the raw `result` (`-o json`); the exit code is non-zero
@@ -370,12 +532,27 @@ xano knowledge get "deploy-runbook" -w 40 --output json
370
532
  xano knowledge get "deploy-runbook" -w 40 --file checklist.md
371
533
  ```
372
534
 
535
+ ### Agent skills
536
+
537
+ ```bash
538
+ xano skills pull # Writes ./.claude/skills/xano-policies/SKILL.md
539
+ xano skills pull -d ./my-project # Install into another project directory
540
+ xano skills pull -b dev -o json # Another branch; print the result as JSON
541
+ ```
542
+
543
+ `xano skills pull` writes the `xano-policies` skill the instance generates from its check catalogue to
544
+ `<directory>/.claude/skills/xano-policies/SKILL.md`, replacing that file without a backup; pull it again after an
545
+ instance upgrade. A copy installed anywhere else is not touched, so remove it. The route needs the
546
+ `workspace:policy` read permission. The CLI writes the file's frontmatter (`name` and `description`) itself and drops
547
+ any frontmatter the served content opens with, so a workspace knowledge record that replaces the platform skill cannot
548
+ set agent settings such as `allowed-tools` or `hooks`. It refuses to write through a symbolic link at that path.
549
+
373
550
  ### Releases
374
551
 
375
552
  All release commands use **release names** (e.g., `v1.0`), not IDs.
376
553
 
377
554
  ```bash
378
- # List releases
555
+ # List releases, each tagged with its stored policy check when Policies is enabled and readable (see Policies)
379
556
  xano release list
380
557
 
381
558
  # Get release details
@@ -416,8 +593,54 @@ xano release deploy "v1.0"
416
593
  xano release deploy "v1.0" --force
417
594
  xano release deploy "v1.0" --branch "restore-v1" --no-set_live
418
595
  xano release deploy "v1.0" -w 40 -o json --force
596
+
597
+ # Set it live past a blocking set-live policy gate with an audited reason (needs workspace:policy update)
598
+ xano release deploy "v1.0" --branch "rollback-v1" --set_live --policy-override "Rollback approved; tracked in JIRA-12"
599
+
600
+ # Read a release's policy check, or check it again (see Policies)
601
+ xano policy runs --release v1.0
602
+ xano policy runs --release v1.0 --recheck
419
603
  ```
420
604
 
605
+ A release carries its branch's policies like its code (`release push` sends `policies/*.xs` with the rest), and a
606
+ tenant deploy lands them on the tenant. A release built by `release push` carries those files only for a credential
607
+ holding `workspace:policy` create and update; for anyone else it carries the workspace's live-branch policies instead,
608
+ and when the files differ from them the push prints the platform's notice naming them (exit `0`). `release import` of
609
+ an archive that carries policies needs the same levels: without them nothing is stored, and the command fails with
610
+ the platform's message and the remedy for your role or token.
611
+
612
+ Cutting a release (`release create`, `release push`, `release import`) stores its policy check, and the summary
613
+ prints it: `Policy check: fail (run 1712), 2 blocking, 1 advisory findings`, or
614
+ `Policy check disabled|skipped|error: <why>` when none was recorded. `-o json` keeps it as `policy_run`
615
+ (`{status, run_id, run_status, blocking, advisory, message?}`, or `null` for a credential without `workspace:policy`
616
+ read). A cut is never refused for its findings: its blocking findings are what gates each tenant deploy (see Tenant
617
+ deployments).
618
+
619
+ `release list` tags each release with its stored check, worded as Studio's Policies column: `[policies: 2 blocking,
620
+ 1 advisory]`, `[policies: 11 advisory]`, `[policies: passed]`, `[policies: none shipped]` (the release carries no
621
+ policies), `[policies: could not check]` (a rule could not run) or `[policies: not checked]` (no stored check:
622
+ deploying the release checks it, and so does `policy runs --release <name> --recheck`), and ends with
623
+ `Policy check details: xano policy runs --release <name>`. The tag is the release's own check, its code against the
624
+ policies it ships, and it is also what the tenant deploy gate judges: a release with blocking findings is held on
625
+ every standard or run tenant (`tenant deploy_release --check`). The list reads `release/policy_check` once, stores nothing, and
626
+ prints as it did before when that read is refused (Policies off, no `workspace:policy` read) or fails. `-o json`
627
+ adds `policy_check` to each item (`{run_id, status, finished_at, counts {findings, blocking, advisory, errors},
628
+ policies}`, or `null` for a release with no stored check) and leaves it out when the checks could not be read.
629
+ Tier1 tenant, ephemeral and sandbox pushes carry policy files too when Policies is enabled and the tenant has
630
+ a policy table. Remote tenant pushes still leave them out; remote release deploys carry them.
631
+
632
+ `release deploy --set_live` lands the release as a new branch, then sets it live through the same policy gate as
633
+ `branch set_live`: the branch is checked against its own policies, which live will have once it is set live, and
634
+ blocking findings it introduces compared with the live branch, or leaves on objects it changes, refuse it. A branch
635
+ that weakens a mandatory policy of the live branch needs `workspace:policy` update (a release that lands no policy is
636
+ judged by the live branch's policies instead, and nothing is checked for weakening): without it set live is refused
637
+ with `policy_weakening_permission_required`, the branch stays, and the command exits `1`, naming the branch to set
638
+ live or delete. A gate refusal keeps the branch, prints the verdict, says `The branch was
639
+ created; set live was refused` and exits `2`; `-o json` prints `{"branch_created": true, "set_live": false, "branch",
640
+ "message", "policy_gate"}`. Set it live from Studio's Branches panel with a reason, or delete the branch and deploy
641
+ again with `--policy-override "<reason>"`, which sets it live past the gate and is audited. A policy of the release
642
+ that the new branch could not take (its key is held by another policy) is named in a warning.
643
+
421
644
  ### Platforms
422
645
 
423
646
  ```bash
@@ -555,9 +778,14 @@ xano tenant get <tenant_name>
555
778
  xano tenant create "My Tenant"
556
779
  xano tenant create "My Tenant" -d "Description" --type tier2 --cluster_id 1 --platform_id 5
557
780
  xano tenant create "My Tenant" --type tier2 --cluster_id 1 --license ./license.yaml
781
+ xano tenant create "Production" --required_reviewers 1 --allow_deploy_bypass
782
+ xano tenant create "Staging" --required_reviewers 0 --allow_quick_deploy
558
783
 
559
784
  # Edit a tenant
560
785
  xano tenant edit <tenant_name> --display "New Name" -d "New description"
786
+ xano tenant edit <tenant_name> --required_reviewers 1
787
+ xano tenant edit <tenant_name> --required_reviewers 0
788
+ xano tenant edit <tenant_name> --allow_quick_deploy
561
789
 
562
790
  # Delete a tenant (confirmation required)
563
791
  xano tenant delete <tenant_name>
@@ -604,10 +832,101 @@ xano tenant deploy_platform <tenant_name> --platform_id 5
604
832
  # Deploy a release by name
605
833
  xano tenant deploy_release <tenant_name> --release v1.0
606
834
 
835
+ # Preview the release's policy gate on the tenant without deploying (exit 2 when it would refuse)
836
+ xano tenant deploy_release <tenant_name> --release v1.0 --check
837
+ xano tenant deploy_release <tenant_name> --release v1.0 --check -o json
838
+
839
+ # Deploy past a blocking policy gate with an audited reason (needs workspace:policy update)
840
+ xano tenant deploy_release <tenant_name> --release v1.0 --override-reason "Accepted; fix tracked in JIRA-12"
841
+
607
842
  # Deploy with a license override file (deploy_platform only)
608
843
  xano tenant deploy_platform <tenant_name> --platform_id 5 --license ./license.yaml
609
844
  ```
610
845
 
846
+ If a tenant has `required_reviewers` set above 0, `tenant deploy_release` fails with an
847
+ error naming the `tenant_deploy_request` command to run instead — see below. A tenant
848
+ with `allow_quick_deploy` enabled skips the gate entirely, regardless of
849
+ `required_reviewers`.
850
+
851
+ A release deploy is also gated on policy. The gate judges the release alone, by its own policy check (the one
852
+ stored when it was cut, or checked from its archive on demand): the blocking findings of its active mandatory policies
853
+ refuse the deploy, including findings the tenant's current release already has. It does not compare the release with
854
+ what the tenant runs, so the same release gets the same verdict on every tenant. Ephemeral and sandbox tenants get the
855
+ verdict but are never refused. A release cut before releases carried policies reads `not_carried` and is not gated.
856
+ `--check` prints the verdict (status, the release and its policy run, the number of blocking findings, and the first
857
+ of them) without deploying; `-o json` is the gate's answer as served. A refused deploy prints the same verdict and exits `2`, naming the
858
+ `--override-reason` rerun when your credential may override; under `-o json` it prints
859
+ `{"deployed": false, "message", "policy_gate"}`. `--override-reason` sends `override_policy: true` with the reason,
860
+ and the platform audits it. A successful deploy's summary prints the gate's status when the platform returns one,
861
+ and a warning names any policy of the release the tenant could not take (its key is held by another policy).
862
+
863
+ #### Tenant Deploy Requests
864
+
865
+ When a tenant has `required_reviewers` set above 0 and `allow_quick_deploy` is off (see
866
+ `tenant create`/`tenant edit` above), a release may only be deployed to it through an
867
+ **approved deploy request**, requiring that many distinct reviewer approvals. An author
868
+ with no deploy permission opens a request; a named reviewer who does hold deploy
869
+ permission on the tenant votes to approve it, and once enough reviewers have approved,
870
+ it deploys as part of that action. `allow_deploy_bypass` (set at `tenant create` time
871
+ only) additionally allows an authorized user to skip a specific request's gate via
872
+ `tenant_deploy_request bypass`, audited with a required reason.
873
+
874
+ ```bash
875
+ # List deploy requests in a workspace (results show the target tenant and release)
876
+ xano tenant_deploy_request list
877
+ xano tenant_deploy_request list --tenant prod --status pending
878
+ xano tenant_deploy_request list --tenant prod --release v1.2
879
+ xano tenant_deploy_request list --to-review
880
+
881
+ # Get details of a specific deploy request, with its release's policy check on its tenant
882
+ xano tenant_deploy_request get <id>
883
+
884
+ # Open a deploy request (submits for review immediately unless --draft is passed)
885
+ xano tenant_deploy_request create "Deploy v1.2 to prod" --tenant prod --release v1.2 --reviewers 12,45
886
+ xano tenant_deploy_request create "Deploy v1.2 to prod" --tenant prod --release v1.2 --draft
887
+
888
+ # Edit a deploy request's title, description or reviewers
889
+ xano tenant_deploy_request edit <id> --title "Deploy v1.2.1 to prod"
890
+ xano tenant_deploy_request edit <id> --reviewers 12,45,67
891
+
892
+ # Change status: draft, submit, approve, request_changes, close, or reopen
893
+ # (approve records one reviewer's vote; once enough votes are in it deploys the
894
+ # release as part of that same call)
895
+ xano tenant_deploy_request set_status <id> --status submit
896
+ xano tenant_deploy_request set_status <id> --status approve
897
+ xano tenant_deploy_request set_status <id> --status approve --override-policy --reason "Accepted; fix tracked in JIRA-12"
898
+ xano tenant_deploy_request set_status <id> --status request_changes --reason "Needs a migration note"
899
+ xano tenant_deploy_request set_status <id> --status close --reason "Superseded by a newer request"
900
+ xano tenant_deploy_request set_status <id> --status reopen
901
+
902
+ # Link a newer release to a request after changes were requested (author only)
903
+ xano tenant_deploy_request revision <id> --release v1.2.1 --note "Added the missing migration"
904
+
905
+ # Deploy past the approval gate under the deploy-bypass permission (requires the
906
+ # tenant's allow_deploy_bypass to be enabled, and a separately-grantable RBAC
907
+ # permission). Every use is audited.
908
+ xano tenant_deploy_request bypass <id> --reason "Prod incident, reviewer unavailable"
909
+ xano tenant_deploy_request bypass <id> --reason "Prod incident, reviewer unavailable" --override-policy
910
+ ```
911
+
912
+ `tenant_deploy_request get` always names the tenant and release. For requests that are neither approved nor closed,
913
+ it also prints what the policy gate would decide now for the request's release on its
914
+ tenant (under `-o json`, as `policy_gate`). It needs `workspace:policy` read; without it, or with the Policies
915
+ feature off, it prints `Policy checks: not available (<why>)` (`policy_gate: null` and `policy_gate_unavailable`
916
+ under `-o json`) and the command still succeeds. The deploy an approval triggers, and a bypass deploy, are gated like
917
+ `tenant deploy_release`: a refusal prints the verdict and exits `2`. An approval refused this way is still recorded
918
+ and the request stays approved; deploy it with `tenant deploy_release --override-reason`. `--override-policy`
919
+ (with `--reason` as the override reason, and the `workspace:policy` update permission) proceeds past the gate on
920
+ `set_status --status approve` and on `bypass`. Approval bypass and policy override are separate permissions:
921
+ neither implies the other.
922
+
923
+ CI example (e.g. a GitHub Action step):
924
+
925
+ ```bash
926
+ xano tenant_deploy_request create "$PR_TITLE" --tenant prod --release v1.2 \
927
+ --description "$PR_BODY" --reviewers 12,45 -o json
928
+ ```
929
+
611
930
  #### Tenant License
612
931
 
613
932
  ```bash
@@ -694,6 +1013,64 @@ xano tenant cluster license set <cluster_id>
694
1013
  xano tenant cluster license set <cluster_id> --file ./kubeconfig.yaml
695
1014
  ```
696
1015
 
1016
+ ### Ephemeral Tenants
1017
+
1018
+ Manage ephemeral tenants — short-lived, auto-expiring tenants scoped to a workspace (default TTL 1h, max 24h). Unlike a sandbox, an ephemeral tenant requires a workspace.
1019
+
1020
+ ```bash
1021
+ # List ephemeral tenants in the current workspace
1022
+ xano ephemeral list
1023
+ xano ephemeral list -w 5
1024
+
1025
+ # List ephemeral tenants across every workspace you can access
1026
+ xano ephemeral list --global
1027
+
1028
+ # Create an ephemeral tenant (workspace required)
1029
+ xano ephemeral create "PR preview"
1030
+ xano ephemeral create "Demo" --expires-hours 24 -w 5
1031
+ xano ephemeral create "Load test" -d "overnight soak" --expires-hours 24
1032
+
1033
+ # Get / edit / delete
1034
+ xano ephemeral get <tenant_name>
1035
+ xano ephemeral edit <tenant_name> --display "New Name" -d "New description"
1036
+ xano ephemeral delete <tenant_name> --force
1037
+
1038
+ # Pull to / push from local files (multidoc)
1039
+ xano ephemeral pull <tenant_name> -d ./my-ephemeral
1040
+ xano ephemeral push <tenant_name> -d ./my-ephemeral --dry-run # preview first
1041
+ xano ephemeral push <tenant_name> -d ./my-ephemeral
1042
+
1043
+ # Open in the browser (or print the URL)
1044
+ xano ephemeral impersonate <tenant_name>
1045
+ xano ephemeral impersonate <tenant_name> --url-only
1046
+ xano ephemeral impersonate <tenant_name> --guest # read-only session (browse only)
1047
+ ```
1048
+
1049
+ #### Static hosting for an ephemeral tenant
1050
+
1051
+ An ephemeral tenant can host static sites, scoped to that tenant. These commands mirror
1052
+ `xano static_host *` but take the tenant name as the first argument (the tenant's static
1053
+ hosting is isolated inside the tenant's own database).
1054
+
1055
+ ```bash
1056
+ # List / create / inspect a tenant's static hosts
1057
+ xano ephemeral static_host list <tenant_name>
1058
+ xano ephemeral static_host create <tenant_name> --name marketing --description "Marketing site"
1059
+ xano ephemeral static_host get <tenant_name> -H marketing
1060
+ xano ephemeral static_host edit <tenant_name> -H marketing --description "Updated"
1061
+
1062
+ # Builds: push a directory, list, inspect, deploy to an env, pull, delete
1063
+ xano ephemeral static_host build push <tenant_name> -H default -f ./site
1064
+ xano ephemeral static_host build list <tenant_name> -H default
1065
+ xano ephemeral static_host build get <tenant_name> -H default --build_id 52
1066
+ xano ephemeral static_host deploy <tenant_name> -H default --build_id 52 --env prod
1067
+ xano ephemeral static_host build pull <tenant_name> -H default --latest
1068
+ xano ephemeral static_host build delete <tenant_name> -H default --build_id 52
1069
+ ```
1070
+
1071
+ > Static hosting is currently available for **local** tenants. Remote (tier2/tier3)
1072
+ > tenants are not yet supported and will return an error.
1073
+
697
1074
  ### Sandbox
698
1075
 
699
1076
  Manage your sandbox tenant. Each user has a single sandbox tenant that is auto-provisioned on first use.