void 0.24.0 → 0.25.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 (137) hide show
  1. package/README.md +19 -106
  2. package/dist/{account-cmd-vJhh5lrb.mjs → account-cmd-DcR1ywt8.mjs} +5 -5
  3. package/dist/{auth-CQkSDS50.mjs → auth-7wuZ5ob8.mjs} +3 -3
  4. package/dist/{auth-DX3JURcX.mjs → auth-CdgZ9uag.mjs} +1 -1
  5. package/dist/{auth-link-D75t9wO2.mjs → auth-link-Z6E5DYvM.mjs} +5 -6
  6. package/dist/{auth-router-ClfhL1J7.mjs → auth-router-BdM4avI8.mjs} +7 -7
  7. package/dist/{build-cmd-zMi1iFnS.mjs → build-cmd-CEE7D-hM.mjs} +6 -7
  8. package/dist/{cache-ZtQmyxVV.mjs → cache-TcZnBLtH.mjs} +6 -7
  9. package/dist/{cancel-deploy-Box0kkEV.mjs → cancel-deploy-CYoZmcmR.mjs} +4 -6
  10. package/dist/{cf-access-BGe5sWXF.mjs → cf-access-AHKBzmuu.mjs} +1 -1
  11. package/dist/cf-build-output-ChY6-XWP.mjs +2 -0
  12. package/dist/cli/cli.mjs +45 -45
  13. package/dist/cli/cloudflare-operation-process.mjs +2 -2
  14. package/dist/cli/env-schema-probe.mjs +2 -2
  15. package/dist/client-B4Me-9HE.mjs +2 -0
  16. package/dist/{client-DKAsFA4X.mjs → client-RsPo8wsP.mjs} +103 -25
  17. package/dist/{cloudflare-cmd-CmY8dGkd.mjs → cloudflare-cmd-bhwCxAN6.mjs} +2 -2
  18. package/dist/{cloudflare-connect-IiR3ZieC.mjs → cloudflare-connect-CuhRgk3k.mjs} +2 -2
  19. package/dist/{cloudflare-operations-BCdXSsmo.mjs → cloudflare-operations-BhbK6yTq.mjs} +1 -1
  20. package/dist/{cloudflare-operations-DzH-A966.mjs → cloudflare-operations-CjaV4lZi.mjs} +2 -2
  21. package/dist/{connect-B-usTnlF.mjs → connect-DCvUmPXY.mjs} +12 -9
  22. package/dist/{create-project-FWbf5Olo.mjs → create-project-C734I-Mu.mjs} +1 -1
  23. package/dist/{create-project-CYOOAYcn.mjs → create-project-eAPgYq35.mjs} +6 -6
  24. package/dist/{db-Ck2eZ2iB.mjs → db-C07GEdKF.mjs} +25 -28
  25. package/dist/{delete-sK56qtzZ.mjs → delete-pKMVsrpJ.mjs} +5 -6
  26. package/dist/deploy-BDu1vhSH.mjs +2 -0
  27. package/dist/{deploy-gCVaiJbS.mjs → deploy-DfsAe-T0.mjs} +9212 -8902
  28. package/dist/{dev-inbox-P0u4tM8Y.mjs → dev-inbox-D64ZKUod.mjs} +5 -6
  29. package/dist/{domain-DFfQE8jg.mjs → domain-D3uA9ize.mjs} +8 -8
  30. package/dist/{email-CdnhUHvJ.mjs → email-z3o2cwjH.mjs} +7 -7
  31. package/dist/{env-DmGdHi5i.mjs → env-3Q8-_2IH.mjs} +6 -8
  32. package/dist/{env-validation-zZOCcQVZ.mjs → env-validation-cZBvGWaP.mjs} +1 -1
  33. package/dist/gen-ById9r2j.mjs +2 -0
  34. package/dist/{gen-CshAkukR.mjs → gen-NjwoG9FK.mjs} +2 -4
  35. package/dist/{github-cmd-DftuEvIt.mjs → github-cmd-DquUq-b1.mjs} +7 -16
  36. package/dist/help-DPGBwomV.mjs +2 -0
  37. package/dist/{help-Dotbf6iz.mjs → help-DRZXC4fj.mjs} +3 -3
  38. package/dist/index.d.mts +18 -14
  39. package/dist/index.mjs +2166 -2096
  40. package/dist/{info-B97bTX9N.mjs → info-BmA3_IFZ.mjs} +1 -3
  41. package/dist/{init-BTQEZZGJ.mjs → init-DT7yuuyl.mjs} +16 -18
  42. package/dist/{link-B46sYH0K.mjs → link-CXOlqh2F.mjs} +6 -7
  43. package/dist/{list-BnPrIsU_.mjs → list-eEWVJEPn.mjs} +8 -9
  44. package/dist/{local-d1-2CMnpuW_.mjs → local-d1-Df4XpwhW.mjs} +1 -1
  45. package/dist/login-C98-sw79.mjs +2 -0
  46. package/dist/{login-BuiMyUrx.mjs → login-pWQHYVu2.mjs} +9 -7
  47. package/dist/{logs-CZG9d-ic.mjs → logs-DnxVX7TR.mjs} +7 -8
  48. package/dist/migrate-CEu2X5DB.mjs +2 -0
  49. package/dist/{migrate-q4_njtaW.mjs → migrate-CV1on-yJ.mjs} +2 -4
  50. package/dist/{operator-args-B3BTmSoi.mjs → operator-args-CAVcmwUt.mjs} +1 -1
  51. package/dist/{operator-auth-7erYc6Lt.mjs → operator-auth-CkmTE3rt.mjs} +2 -2
  52. package/dist/{operator-client-CpHjTd0t.mjs → operator-client-LmwjxT6q.mjs} +2 -2
  53. package/dist/{operator-cmd-DGqPJUD3.mjs → operator-cmd-d-IEKgNk.mjs} +6 -6
  54. package/dist/{output-CbM85T4k.mjs → output-BBsUwdyT.mjs} +5 -5
  55. package/dist/pages/client.d.mts +3 -0
  56. package/dist/pages/client.mjs +120 -29
  57. package/dist/{platform-args-3k8X9-H_.mjs → platform-args-Bzf8JPc2.mjs} +1 -1
  58. package/dist/platform-args-Cq6EjQro.mjs +2 -0
  59. package/dist/{platform-auth-config-DDrxUy4m.mjs → platform-auth-config-0V0lA3pF.mjs} +7 -7
  60. package/dist/{platform-auth-protection-BLadnnct.mjs → platform-auth-protection-DFeJ-JkK.mjs} +8 -8
  61. package/dist/{platform-auth-recovery-CmCzX2Fl.mjs → platform-auth-recovery-ICy4muq6.mjs} +7 -7
  62. package/dist/{platform-cmd-pMTRhvYs.mjs → platform-cmd-BPySFOew.mjs} +1 -1
  63. package/dist/{platform-cmd-DMDEc9uS.mjs → platform-cmd-D7vSWMvV.mjs} +37 -27
  64. package/dist/platform-discovery-DDDUvnqp.mjs +27 -0
  65. package/dist/{platform-domain-Di6EYrQl.mjs → platform-domain-CUdY064e.mjs} +4 -4
  66. package/dist/{platform-lifecycle-BFdSi9Qt.mjs → platform-lifecycle-BaUdTRPU.mjs} +1 -1
  67. package/dist/{platform-lifecycle-BBoNzojY.mjs → platform-lifecycle-Dm09JNkn.mjs} +3901 -3651
  68. package/dist/{platform-management-CWOQOWXm.mjs → platform-management-BSEc_ZeG.mjs} +47 -10
  69. package/dist/platform-management-Bp6aoH2C.mjs +2 -0
  70. package/dist/{platform-plans-config-Vl9BP3H4.mjs → platform-plans-config-B0pOVQyW.mjs} +3 -3
  71. package/dist/{platform-recovery-BJnQJEtQ.mjs → platform-recovery-WOZ95Uze.mjs} +3 -3
  72. package/dist/{prepare-CBLT3CHH.mjs → prepare-39EvOhI6.mjs} +1 -3
  73. package/dist/{project-DKG5Ra8p.mjs → project-BkbfDs0V.mjs} +1 -1
  74. package/dist/{project-DsblYifa.mjs → project-IOjBU8R6.mjs} +1 -1
  75. package/dist/{project-cmd-DxnXstAq.mjs → project-cmd-Bs5A392E.mjs} +21 -22
  76. package/dist/{project-team-BmHcaALJ.mjs → project-team-ohAWWaew.mjs} +8 -9
  77. package/dist/{project-token-GionZlBI.mjs → project-token-CP01Jna6.mjs} +6 -9
  78. package/dist/{project-zero-trust-JLCjVQA7.mjs → project-zero-trust-BzkVgx35.mjs} +5 -6
  79. package/dist/{prompts-BNUoTiaf.mjs → prompts-6OSVA7DJ.mjs} +58 -15
  80. package/dist/{prompts-2TGE6oXx.mjs → prompts-ZAuJlyCj.mjs} +1 -1
  81. package/dist/{provision-CVku1JFU.mjs → provision-BEv0k-qz.mjs} +1 -1
  82. package/dist/{provision-CaoxO_U7.mjs → provision-Btj1jppf.mjs} +5 -7
  83. package/dist/{requests-BOjIVAeF.mjs → requests-B3PBuGlX.mjs} +5 -6
  84. package/dist/{rollback-CwR3pLj3.mjs → rollback-DDub9GsO.mjs} +9 -10
  85. package/dist/runtime/email/testing.mjs +1 -1
  86. package/dist/runtime/email.mjs +35 -17
  87. package/dist/runtime/routing.mjs +1 -1
  88. package/dist/{secret-ByWJWBUG.mjs → secret-3c72lY1U.mjs} +15 -16
  89. package/dist/{skills-C1PlclPt.mjs → skills-BPQUGClu.mjs} +1 -1
  90. package/dist/{subcommand-prompt-klFCfcor.mjs → subcommand-prompt-Dhom39bd.mjs} +2 -2
  91. package/dist/sveltekit.d.mts +1 -1
  92. package/dist/sveltekit.mjs +4 -9
  93. package/dist/{wrangler-7K-bW_DL.mjs → wrangler-DhUerFkW.mjs} +10 -15
  94. package/package.json +21 -21
  95. package/sandbox.Dockerfile +1 -1
  96. package/skills/void/docs/guide/ai.md +24 -86
  97. package/skills/void/docs/guide/auth.md +0 -2
  98. package/skills/void/docs/guide/database/d1.md +7 -7
  99. package/skills/void/docs/guide/deployment.md +58 -124
  100. package/skills/void/docs/guide/env-vars.md +1 -5
  101. package/skills/void/docs/guide/index.md +19 -18
  102. package/skills/void/docs/guide/kv.md +0 -2
  103. package/skills/void/docs/guide/pages-routing/actions-and-forms.md +49 -47
  104. package/skills/void/docs/guide/pages-routing/overview.md +2 -2
  105. package/skills/void/docs/guide/platform/administration/email.md +0 -2
  106. package/skills/void/docs/guide/platform/administration/operations.md +11 -22
  107. package/skills/void/docs/guide/platform/administration/plans.md +1 -5
  108. package/skills/void/docs/guide/platform/administration/zero-trust.md +1 -4
  109. package/skills/void/docs/guide/platform/development/schema-ci.md +7 -1
  110. package/skills/void/docs/guide/platform/installation/maintenance.md +30 -10
  111. package/skills/void/docs/guide/platform/installation/prerequisites.md +79 -7
  112. package/skills/void/docs/guide/quickstart.md +37 -80
  113. package/skills/void/docs/guide/sandboxes.md +36 -46
  114. package/skills/void/docs/guide/self-hosted-platform.md +20 -0
  115. package/skills/void/docs/guide/websockets.md +0 -2
  116. package/skills/void/docs/integrations/frameworks/overview.md +2 -2
  117. package/skills/void/docs/integrations/frameworks/sveltekit.md +40 -25
  118. package/skills/void/docs/integrations/nodejs-bun-deno.md +7 -2
  119. package/skills/void/docs/reference/api/resources.md +27 -0
  120. package/skills/void/docs/reference/api.md +1 -1
  121. package/skills/void/docs/reference/cli/deploy.md +1 -1
  122. package/skills/void/docs/reference/cli/platform-operations.md +4 -8
  123. package/skills/void/docs/reference/cli/platform.md +5 -0
  124. package/skills/void/docs/reference/resource-inference.md +11 -8
  125. package/dist/cf-build-output-_0HNWysu.mjs +0 -2
  126. package/dist/client-B9UBI03V.mjs +0 -2
  127. package/dist/deploy-irP5q0-x.mjs +0 -2
  128. package/dist/gen-DvQTWzDU.mjs +0 -2
  129. package/dist/help-mabuIV98.mjs +0 -2
  130. package/dist/login-DIy9Sb-U.mjs +0 -2
  131. package/dist/migrate-BCiJ8w0f.mjs +0 -2
  132. package/dist/platform-args-DdnM6WIf.mjs +0 -2
  133. package/dist/platform-management-DG47jys8.mjs +0 -2
  134. package/skills/void/docs/guide/env-migration.md +0 -109
  135. package/dist/{cf-build-output-BPqOT964.mjs → cf-build-output-U8o1Gl6X.mjs} +1 -1
  136. package/dist/{env-validation-C_8tPdhs.mjs → env-validation-vBSQ8gw5.mjs} +1 -1
  137. package/dist/{platform-registry-DkPPXfp_.mjs → platform-registry-KfNEF84O.mjs} +1 -1
@@ -29,57 +29,12 @@ export const action = defineHandler.withValidator({
29
29
  | Nothing (void) | Re-runs the loader, page re-renders with fresh props. |
30
30
  | `c.redirect('/path')` | Navigates to another page. |
31
31
 
32
- ## Named Actions
33
-
34
- When a page needs multiple mutations, such as updating and deleting a user on the same page, export `actions` (plural) instead of `action`:
35
-
36
- ```ts
37
- // pages/users/[id].server.ts
38
- import { defineHandler } from 'void';
39
- import { db, eq } from 'void/db';
40
- import { users, updateUserSchema } from '@schema';
41
-
42
- export const actions = {
43
- update: defineHandler.withValidator({
44
- body: updateUserSchema,
45
- })(async (c, { body }) => {
46
- await db
47
- .update(users)
48
- .set(body)
49
- .where(eq(users.id, Number(c.req.param('id'))));
50
- }),
51
-
52
- delete: defineHandler(async (c) => {
53
- await db.delete(users).where(eq(users.id, Number(c.req.param('id'))));
54
- }),
55
- };
56
- ```
57
-
58
- Named actions are dispatched via a `?actionName` suffix on the URL (e.g. `/users/42?update`). The client primitives `useForm` and `action()` handle this automatically.
59
-
60
- Use `action` or `actions` in a file, not both.
61
-
62
- You can also define an `actions.default` key for the action that runs when no name is specified (i.e. a bare POST to the page URL):
63
-
64
- ```ts
65
- export const actions = {
66
- default: defineHandler(async (c) => {
67
- // runs on POST /users/edit (no ?suffix)
68
- }),
69
- delete: defineHandler(async (c) => {
70
- // runs on POST /users/edit?delete
71
- }),
72
- };
73
- ```
74
-
75
32
  ## Form
76
33
 
77
- Pair `Form` with `useForm` for automatic error presentation. It preserves controlled input on failure, announces validation and action errors, and displays the message from structured quota responses. Known retry deadlines prevent premature resubmission without locking the fields. It never retries a submission automatically.
34
+ Pair `Form` with `useForm` to submit values and display validation and action errors. Inputs stay editable when a submission fails.
78
35
 
79
36
  Import `Form` from your adapter. Pass `form` and, optionally, `method="put"`, `"patch"`, or `"delete"`; the default is `"post"`. Use `renderError` for a custom action-error renderer (a snippet in Svelte).
80
37
 
81
- On the server, use `limit.response({ message: 'AI is temporarily unavailable. Your draft is still editable.' })` in an AI or Sandbox operation's required `limited` handler. It supplies the status and structured metadata consumed by `Form`.
82
-
83
38
  ## `useForm`
84
39
 
85
40
  `useForm` handles submissions, loading state, and validation errors. URLs, form values, and error fields are typed from your action.
@@ -216,6 +171,49 @@ const form = useForm('/users/:id', { name: '' }, { params: { id: '42' } });
216
171
  <Form form={form} method="put">{/* submits to /users/42 */}</Form>
217
172
  ```
218
173
 
174
+ ## Named Actions
175
+
176
+ When a page needs multiple mutations, such as updating and deleting a user on the same page, export `actions` (plural) instead of `action`:
177
+
178
+ ```ts
179
+ // pages/users/[id].server.ts
180
+ import { defineHandler } from 'void';
181
+ import { db, eq } from 'void/db';
182
+ import { users, updateUserSchema } from '@schema';
183
+
184
+ export const actions = {
185
+ update: defineHandler.withValidator({
186
+ body: updateUserSchema,
187
+ })(async (c, { body }) => {
188
+ await db
189
+ .update(users)
190
+ .set(body)
191
+ .where(eq(users.id, Number(c.req.param('id'))));
192
+ }),
193
+
194
+ delete: defineHandler(async (c) => {
195
+ await db.delete(users).where(eq(users.id, Number(c.req.param('id'))));
196
+ }),
197
+ };
198
+ ```
199
+
200
+ Named actions are dispatched via a `?actionName` suffix on the URL (e.g. `/users/42?update`). The client primitives `useForm` and `action()` handle this automatically.
201
+
202
+ Use `action` or `actions` in a file, not both.
203
+
204
+ You can also define an `actions.default` key for the action that runs when no name is specified (i.e. a bare POST to the page URL):
205
+
206
+ ```ts
207
+ export const actions = {
208
+ default: defineHandler(async (c) => {
209
+ // runs on POST /users/edit (no ?suffix)
210
+ }),
211
+ delete: defineHandler(async (c) => {
212
+ // runs on POST /users/edit?delete
213
+ }),
214
+ };
215
+ ```
216
+
219
217
  ### Named Actions with `useForm`
220
218
 
221
219
  When a page exports named actions, append `?actionName` to the URL:
@@ -258,7 +256,7 @@ Authentication errors (`401`, `403`), server errors (`500`, `502`), and unexpect
258
256
  Actions can throw `ValidationError` for custom validation logic:
259
257
 
260
258
  ```ts
261
- import { defineHandler, ValidationError } from 'void';
259
+ import { defineHandler, ValidationError } from 'void/handler';
262
260
 
263
261
  export const action = defineHandler(async (c) => {
264
262
  const body = await c.req.json();
@@ -269,6 +267,10 @@ export const action = defineHandler(async (c) => {
269
267
  });
270
268
  ```
271
269
 
270
+ ### Usage limits
271
+
272
+ For [AI](../ai.md) and [Sandbox](../sandboxes.md) actions, return `limit.response({ message: 'Try again shortly. Your draft is still editable.' })` from the operation's `limited` handler. `Form` displays the message and waits until a known retry deadline before allowing resubmission. It keeps the fields editable and never retries automatically.
273
+
272
274
  ## Authentication and origin checks
273
275
 
274
276
  Browser submissions must come from the same origin as the page, including its scheme, hostname, and port. Void rejects cross-origin page actions with `403 Forbidden`. Server clients can call actions without browser origin headers; actions still need authentication and authorization for protected data.
@@ -225,9 +225,9 @@ export default function PostPage() {
225
225
 
226
226
  ## Scroll Restoration
227
227
 
228
- Client-side navigation manages scroll position automatically:
228
+ Void manages scroll position for Pages automatically:
229
229
 
230
- - **Back/forward navigation** restores the exact scroll position you were at before navigating away.
230
+ - **Back/forward navigation and reloads** restore the saved scroll position.
231
231
  - **New navigation** scrolls to the top of the page.
232
232
  - **Hash links** (`/docs#api`) scroll to the target element. Same-page hash links (`#section`) skip the server fetch entirely.
233
233
 
@@ -144,5 +144,3 @@ Run recovery with the same platform version that created the persisted intent.
144
144
  `applied` continues without replaying the write; `not-applied` permits that exact
145
145
  step to retry. Secret rotation keeps its staged generation, and removal resumes
146
146
  from the unresolved cleanup step. Age alone never authorizes a retry.
147
-
148
- Register administrator domains only after a platform upgrade has completed, and remove them before rolling the platform back to a version that predates this feature: an earlier runtime cannot use the platform credential for them and does not distinguish administrator-registered domains from owner-registered ones.
@@ -51,28 +51,6 @@ Following waits for the final logs before stopping, including diagnostics writte
51
51
 
52
52
  ## Checking the Platform
53
53
 
54
- ### Migrating older deployment assets
55
-
56
- After upgrading the platform, inspect every page of its asset inventory:
57
-
58
- ```sh
59
- void platform system asset-migrations --page 1 --limit 100
60
- void platform deployment migrate-assets <deployment-id> --plan
61
- void platform deployment migrate-assets <deployment-id> --yes
62
- ```
63
-
64
- Migrate every deployment with a nonzero `needsMigration` count, including retained
65
- rollback targets and stored Worker bundles. Investigate entries marked `invalid`
66
- before continuing. The command verifies each copied asset, preserves originals,
67
- and applies successive batches. If interrupted, inspect the result and resume
68
- with the last successful `nextCursor` using `--cursor`; restarting without a
69
- cursor also verifies and reuses completed copies.
70
-
71
- Run the inventory again across all pages when finished. Existing applications
72
- continue serving during this migration, and the provider asset mirror remains.
73
- Legacy reads remain available while older platform runtimes and cached manifests
74
- can still reference original assets.
75
-
76
54
  ### Health checks
77
55
 
78
56
  Use the overview to see recent activity, or run a health check to test the platform's services and database:
@@ -88,6 +66,17 @@ The dashboard’s **System Status** page refreshes health checks every 30 second
88
66
 
89
67
  Use `void platform upgrade`, `repair`, `disable`, and `enable` to maintain your platform. See [platform maintenance](/guide/platform/installation/maintenance#resume-repair-recover-and-upgrade).
90
68
 
69
+ Upgrades and rollbacks run through the CLI; watching their progress is optional.
70
+ During maintenance, navigating the admin UI automatically redirects to
71
+ `/admin/status`. You can also open that page directly on the API origin. It
72
+ refreshes automatically and shows saved progress and the API's traffic state
73
+ while the API and authentication database remain reachable. If a status refresh
74
+ fails, the last confirmed snapshot is marked as stale. Normal administrator
75
+ changes stay paused during maintenance.
76
+ If the CLI reports an error or a waiting state, follow its recovery instructions
77
+ to resume the operation. See
78
+ [Follow upgrade and rollback progress](/guide/platform/installation/maintenance#follow-upgrade-and-rollback-progress).
79
+
91
80
  For disaster recovery, keep a matching PostgreSQL backup, provider data backups, installation identity and complete project-encryption keyring, and immutable runtime artifacts. Keep traffic disabled while restoring data, run `discover` to reconstruct verified local metadata, and review `repair --plan` before recreating missing installer-owned infrastructure. Neither command restores backed-up data. See [Prepare for disaster recovery](/guide/platform/installation/maintenance#prepare-for-disaster-recovery) for the full sequence.
92
81
 
93
82
  ## Using Scripts
@@ -11,9 +11,7 @@ void platform auth login
11
11
  void platform config plans
12
12
  ```
13
13
 
14
- Choose a plan to inspect or edit it, copy one into a new plan, or choose the default for new accounts. Existing installations keep their plans, limits, and default until you change them. After upgrading the platform to support plan configuration, these changes require no app or platform redeploy.
15
-
16
- Inspection and previews are available immediately after a platform upgrade. Allow 15 minutes before applying changes.
14
+ Choose a plan to inspect or edit it, copy one into a new plan, or choose the default for new accounts. Changes require no app or platform redeploy.
17
15
 
18
16
  ## Use the Dashboard
19
17
 
@@ -90,8 +88,6 @@ Storage figures shown for D1, R2, and KV are informational and cannot be edited
90
88
 
91
89
  The preview shows the affected account count and checks a sample for accounts already above a proposed quota. Lowering a quota can restrict existing apps, including accounts outside that sample. Usage history and billing periods are preserved, so a plan change does not reset usage. Manual administrator suspensions remain in effect.
92
90
 
93
- After the initial platform upgrade, request quota accounting includes unlimited plans too. For an account that was unlimited before that upgrade, its quota counter may not include earlier requests in the current billing period. Historical analytics remain available. When introducing its first cap, allow for the remaining period or wait until its next billing period.
94
-
95
91
  ## Archive or Remove a Plan
96
92
 
97
93
  Archive a plan to stop assigning it to new accounts while preserving its current accounts:
@@ -8,10 +8,7 @@ Project Zero Trust puts Cloudflare Access in front of the hostnames of projects
8
8
 
9
9
  This is separate from the Access login and protection for the platform API. Projects deployed directly to Cloudflare are not affected.
10
10
 
11
- New installations choose app access during `void platform install`. The installer
12
- reuses the platform policy or helps you select separate company rules, and finishes
13
- setup before opening traffic. Existing installations keep their settings when
14
- upgraded; use this page to configure app access if it has not been set up yet.
11
+ Choose app access during `void platform install`, or configure it later using this page. The installer reuses the platform policy or helps you select separate company rules, and finishes setup before opening traffic.
15
12
 
16
13
  If you choose public apps in an account with Default-Deny, Void manages scoped
17
14
  public rules instead. New projects and custom domains remain public automatically.
@@ -14,7 +14,13 @@ vp run --filter @voidcloud/api db:generate
14
14
 
15
15
  Review the generated SQL and migration journal before committing them. Released migrations are append-only. An upgrade applies new migrations while the previous Workers may still serve requests, so schema changes must remain compatible with that code.
16
16
 
17
- `platform/packages/api/drizzle/compatibility.json` records which earlier runtimes have been verified against the new schema. Add an edge only after testing that compatibility. The runtime build checks that the migration files and recorded schema history agree. See `packages/platform/README.md` for the manifest contract.
17
+ `platform/packages/api/drizzle/compatibility.json` records which earlier runtimes have been verified against the new schema. Add an edge only after testing that compatibility. The runtime build checks that the migration files and recorded schema history agree.
18
+
19
+ ## Runtime artifact contract
20
+
21
+ `@void/platform` contains installation manifests, resource naming, discovery contracts, Worker configuration, and deployable bundles under the `./installer` export. Platform source lives in `platform/packages/`.
22
+
23
+ Runtime artifacts include migration SQL, its journal, and a rollback compatibility manifest. Drizzle authoring snapshots remain in the source tree. Migrations are forward-only; compatibility edges in `platform/packages/api/drizzle/compatibility.json` must be tested against the earlier runtime before publication. The build verifies schema lineage and hashes the journal and SQL files.
18
24
 
19
25
  ## CI in a Fork
20
26
 
@@ -42,11 +42,6 @@ secret manager to an object keyed by each exact API/proxy origin, with
42
42
  and stops if the gate still rejects its checks. Account-wide Default-Deny and
43
43
  deployed-application policies remain your responsibility.
44
44
 
45
- If **System Status** reports `proxy HTTP 302 (redirect)` on an older Access-protected
46
- installation, upgrade with `void platform upgrade <id>`. Current installations use
47
- a private connection for the dashboard's proxy health check. Keep the proxy's
48
- Access protection enabled.
49
-
50
45
  ### Expired administrator setup code
51
46
 
52
47
  Resume an unfinished installation with `void platform install --resume --name <id>`.
@@ -68,7 +63,7 @@ Repair recreates missing infrastructure that the installer owns. It does not res
68
63
 
69
64
  ### Upgrade the platform
70
65
 
71
- Keep the platform runtime current when updating the Void CLI. If a developer's CLI reports that project lookup requires an upgrade, upgrade the platform before retrying.
66
+ Keep the platform runtime current when updating the Void CLI.
72
67
 
73
68
  Use the installed CLI's packaged runtime to upgrade:
74
69
 
@@ -92,10 +87,6 @@ manager so they can also restore a missing Worker during repair.
92
87
 
93
88
  Database migrations only move forward. Void checks compatibility before upgrading and tells you if an intermediate release is needed. If deployment fails, it attempts to restore the previous Workers against the compatible database schema. Retrying does not repeat completed migrations.
94
89
 
95
- Upgrades automatically enable managed Sandboxes. The platform runtime token needs Account / Containers: Edit and Account / Cloudchamber: Edit, and the Cloudflare account must use Workers Paid before its first Sandbox application deploy. The upgrade itself does not probe Containers access, so platforms that do not deploy Sandbox applications need no additional plan or permissions.
96
-
97
- When upgrading from a release that used tenant-owned Sandbox containers, the upgrade may first ask you to finish the legacy cleanup. Follow the [`sandbox-drain` instructions](../../../reference/cli/platform-operations.md#operator-system), then rerun the upgrade. Administrator login remains available during that maintenance step.
98
-
99
90
  After a successful upgrade, you can restore a declared-compatible earlier runtime without reversing migrations:
100
91
 
101
92
  ```sh
@@ -107,6 +98,35 @@ Keep the earlier runtime files if you need rollback. If the installed version is
107
98
 
108
99
  :::
109
100
 
101
+ ### Follow upgrade and rollback progress
102
+
103
+ Watching progress is optional: a running upgrade or rollback proceeds through
104
+ the CLI without action on the status page. During maintenance, navigating the
105
+ admin UI automatically redirects to `/admin/status`. You can also open it
106
+ directly on your platform API origin and sign in as an administrator.
107
+
108
+ The read-only page shows the saved operation, source and target versions, and
109
+ the traffic state reported by the API. It refreshes automatically every five
110
+ seconds while visible. If updates fail, it keeps the last confirmed snapshot
111
+ and marks it as out of date. Normal administrator changes remain paused.
112
+
113
+ Sign-in and status remain available while the API and authentication database
114
+ are reachable. An operation completing
115
+ does not confirm every project's health. A disabled installation keeps traffic
116
+ paused after an upgrade or rollback.
117
+
118
+ If an operation fails or waits for DNS or Sandbox drain work, the CLI prints
119
+ the recovery instructions and command needed to resume. Resolve the reported
120
+ problem, then resume with the same runtime files. For a custom rollback, retain
121
+ the source runtime files too. `void platform status <id>` also prints locally saved progress
122
+ before checking the live API; this local checkpoint can be out of date if another
123
+ administrator changed the installation.
124
+
125
+ When application traffic is paused, visitors receive a temporary-unavailability
126
+ page with a **Try again** link. API requests and form submissions still receive
127
+ HTTP 503 responses. Void does not automatically replay requests; visitors who
128
+ submitted a form should check its result before submitting it again.
129
+
110
130
  ### Manage an installation from another machine
111
131
 
112
132
  Use `discover` to restore the administrator's local installation records:
@@ -73,16 +73,88 @@ Use any PostgreSQL provider that accepts connections from your computer and Clou
73
73
  2. Open the provider's connection details and select the primary database. Use a direct connection or a session-mode pooler, not transaction pooling. The connection must work from both your computer and Cloudflare.
74
74
  3. Copy the PostgreSQL connection URL, including the password and SSL settings, into your password manager. Paste only the URL—not a surrounding `psql` command—into Void's `PostgreSQL DATABASE_URL` prompt, as the provider gives it, including SSL settings such as `sslmode=verify-full`. A Hyperdrive that Void creates trusts only public certificate authorities, so if your database uses a private certificate authority, use a separately managed Hyperdrive, as described below.
75
75
 
76
- | Provider | Connection setup |
77
- | ---------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
78
- | [PlanetScale Postgres](https://planetscale.com/docs/postgres/connecting) | Choose **Postgres**, not Vitess/MySQL. Open **Connect**, create role credentials, and use the direct primary connection on port `5432`, not PgBouncer on `6432`. |
79
- | [Neon](https://neon.com/docs/get-started-with-neon/connect-neon) | Open **Connect**, select the database and owner role, and turn **connection pooling off**. |
80
- | [Supabase](https://supabase.com/docs/guides/database/connecting-to-postgres) | Open **Connect** and use the direct connection where IPv6 is available, or the **Session pooler** on port `5432` for IPv4 connectivity. Do not select the transaction pooler on `6543`. Replace the password placeholder with your database password. |
81
-
82
- For a dedicated Supabase project, [disable the Data API](https://supabase.com/docs/guides/api/securing-your-api#disable-the-data-api) so the platform's tables are not exposed through Supabase's auto-generated endpoints. Void uses the PostgreSQL connection, not Supabase API keys.
76
+ | Provider | Connection setup |
77
+ | ------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
78
+ | [PlanetScale Postgres](https://planetscale.com/docs/postgres/connecting) | Choose **Postgres**, not Vitess/MySQL. Open **Connect**, create role credentials, and use the direct primary connection on port `5432`, not PgBouncer on `6432`. |
79
+ | [Neon](https://neon.com/docs/get-started-with-neon/connect-neon) | Open **Connect**, select the database and owner role, and turn **connection pooling off**. |
83
80
 
84
81
  Once installation claims the database, continue using that same database for resume and maintenance commands. Uninstall never deletes external PostgreSQL.
85
82
 
83
+ ### Use a provider CLI
84
+
85
+ You can create the database and retrieve its connection URL from either provider's CLI instead of its dashboard.
86
+
87
+ ::: details PlanetScale Postgres
88
+
89
+ Install the latest [PlanetScale CLI](https://planetscale.com/docs/cli/planetscale-environment-setup). On macOS, use `brew install pscale`; the installation guide also covers Linux and Windows.
90
+
91
+ Sign in and list your organizations and regions:
92
+
93
+ ```sh
94
+ pscale auth login
95
+ pscale org list
96
+ pscale region list --org <organization>
97
+ ```
98
+
99
+ Replace `<organization>` with your organization slug and choose a region. List the available Postgres cluster sizes and their prices for that region:
100
+
101
+ ```sh
102
+ pscale size cluster list --org <organization> --engine postgresql --region <region>
103
+ ```
104
+
105
+ Create a dedicated Postgres database with your chosen cluster size, then create a role with permission to create and migrate Void's tables:
106
+
107
+ ```sh
108
+ pscale database create void-platform \
109
+ --org <organization> \
110
+ --engine postgresql \
111
+ --region <region> \
112
+ --cluster-size <cluster-size> \
113
+ --wait
114
+
115
+ pscale role create void-platform main void_owner \
116
+ --org <organization> \
117
+ --inherited-roles postgres
118
+ ```
119
+
120
+ Save the **DATABASE URL** printed by the role-creation command in your password manager and paste it into Void's `PostgreSQL DATABASE_URL` prompt. Use the direct primary connection on port `5432` with `sslmode=verify-full`. The role's password is shown when it is created, so save it before closing the terminal.
121
+
122
+ See PlanetScale's [database commands](https://planetscale.com/docs/cli/database) and [role commands](https://planetscale.com/docs/cli/role) for more options.
123
+
124
+ :::
125
+
126
+ ::: details Neon
127
+
128
+ Install the latest [Neon CLI](https://neon.com/docs/cli/install) and sign in:
129
+
130
+ ```sh
131
+ pnpm add --global neon@latest
132
+ neon login
133
+ ```
134
+
135
+ Create a dedicated project, database, and owner role. Replace `<region-id>` with one of Neon's [supported regions](https://neon.com/docs/cli/projects#create), such as `aws-us-east-2`:
136
+
137
+ ```sh
138
+ neon projects create --name void-platform \
139
+ --database void_platform \
140
+ --role void_owner \
141
+ --region-id <region-id>
142
+ ```
143
+
144
+ Use the project ID from the output to retrieve a direct primary connection URL with TLS certificate verification:
145
+
146
+ ```sh
147
+ neon connection-string --project-id <project-id> \
148
+ --database-name void_platform \
149
+ --role-name void_owner \
150
+ --endpoint-type read_write \
151
+ --ssl verify-full
152
+ ```
153
+
154
+ Save the printed URL in your password manager and paste it into Void's `PostgreSQL DATABASE_URL` prompt. Keep this connection unpooled. See Neon's [connection-string command](https://neon.com/docs/cli/connection-string) for more options.
155
+
156
+ :::
157
+
86
158
  ### Use an existing Hyperdrive
87
159
 
88
160
  The installer can create Hyperdrive for you or use one you manage separately. For an existing configuration:
@@ -4,65 +4,51 @@ outline: deep
4
4
 
5
5
  # Quickstart
6
6
 
7
- Create a Void app, run it locally, and deploy it. You can also [add Void to an existing Vite app](#adding-to-an-existing-vite-app). Use Node.js 24.21.0 or later.
7
+ Create a Void app, run it locally, and deploy it. You can also [add Void to an existing Vite app](#adding-to-an-existing-vite-app).
8
8
 
9
- ## Start in an Empty Directory
9
+ ## Create an app
10
10
 
11
- Install Void in an empty project directory:
11
+ In an empty directory, install Void and run setup:
12
12
 
13
13
  ::: code-group
14
14
 
15
15
  ```sh [npm]
16
16
  npm install -D void
17
+ npx void init
17
18
  ```
18
19
 
19
20
  ```sh [pnpm]
20
21
  pnpm add -D void
22
+ pnpm void init
21
23
  ```
22
24
 
23
25
  ```sh [yarn]
24
26
  yarn add -D void
27
+ yarn void init
25
28
  ```
26
29
 
27
30
  ```sh [bun]
28
31
  bun add -D void
32
+ bunx void init
29
33
  ```
30
34
 
31
35
  :::
32
36
 
33
- Run setup:
34
-
35
- ::: code-group
36
-
37
- ```sh [npm]
38
- npx void init
39
- ```
37
+ Choose your UI framework, a starter, and a deployment target. Vite+ is the default build tool. D1 is a good first database: it runs locally without a separate database server. Choose Cloudflare to deploy to your own account, or skip deployment setup for now.
40
38
 
41
- ```sh [pnpm]
42
- pnpm void init
43
- ```
39
+ With pnpm, `pnpm create void my-app` also creates the project directory and configures native build permissions for you.
44
40
 
45
- ```sh [yarn]
46
- yarn void init
47
- ```
41
+ ## Run locally
48
42
 
49
- ```sh [bun]
50
- bunx void init
43
+ ```sh
44
+ npm run dev
51
45
  ```
52
46
 
53
- :::
54
-
55
- Void asks you to choose Vite+ or Vite, a UI framework, a starter, and a deployment target. Vite+ is the default. D1 needs no local database server; choose PostgreSQL or MySQL if you use an external database. You can skip deployment setup and decide later.
56
-
57
- With pnpm, you can start with `pnpm create void my-app`. It configures native build permissions before installing Void.
58
-
59
- The examples below use `void` for brevity. Outside package scripts, run the local binary with `npx void`, `pnpm void`, `yarn void`, or `bunx void`. Keep Void installed in the project so the CLI and app use the same version.
47
+ Open `http://localhost:5173`. In a Pages starter, `pages/` contains your UI and companion `.server.ts` files contain its server loaders and actions. Loaders return data to page components as typed props.
60
48
 
61
- ## Run and deploy
49
+ ## Call a server route
62
50
 
63
- ### 1. Edit the generated API route
64
-
65
- Database-backed starters include `routes/api/hello.ts`. You can edit its `GET` handler, or create this file if you started with Static Pages:
51
+ Edit `routes/api/hello.ts`, or create it if your starter doesn't include it:
66
52
 
67
53
  ```ts
68
54
  import { defineHandler } from 'void';
@@ -72,75 +58,46 @@ export const GET = defineHandler(() => {
72
58
  });
73
59
  ```
74
60
 
75
- ### 2. Run locally
76
-
77
- ```sh
78
- npm run dev
79
- ```
61
+ Visit `http://localhost:5173/api/hello` to see the response. From your client code, call it with Void's typed fetch:
80
62
 
81
- Then visit:
63
+ ```ts
64
+ import { fetch } from 'void/client';
82
65
 
83
- - App: `http://localhost:5173`
84
- - API route: `http://localhost:5173/api/hello`
66
+ const { message } = await fetch('/api/hello');
67
+ ```
85
68
 
86
- ### 3. Deploy
69
+ The file path defines the URL, and the handler defines the response type. Change `message` in the handler and TypeScript checks the client against the new shape.
87
70
 
88
- If you chose a deployment target during setup, run:
71
+ Database starters also include `db/schema.ts`. Its Drizzle schema defines your tables and query types; Void runs a local database and provisions one when you deploy.
89
72
 
90
- ```sh
91
- void deploy
92
- ```
73
+ ## Deploy
93
74
 
94
- If you skipped deployment setup, select your Cloudflare account for the first deploy:
75
+ If you chose a target during setup:
95
76
 
96
77
  ```sh
97
- void deploy --platform cloudflare
78
+ npx void deploy
98
79
  ```
99
80
 
100
- Void opens your browser to sign in when needed. To use your team's platform, connect using the API URL from your administrator, then link a project and deploy:
81
+ If you skipped setup, select your Cloudflare account:
101
82
 
102
83
  ```sh
103
- void connect https://platform.example.com
104
- void project link
105
- void deploy
84
+ npx void deploy --platform cloudflare
106
85
  ```
107
86
 
108
- Void builds the app, provisions its resources, applies pending migrations, and prints the deployed URL. If a production secret is missing, [set it](./env-vars.md) and deploy again. Later deploys use the saved target.
109
-
110
- See [Deployment](./deployment.md) for CI setup and rollback.
111
-
112
- ## Using with Coding Agents
113
-
114
- `void init` detects your coding agent and installs its instructions and skills. If no agent is detected, use the bundled docs linked in `AGENTS.md`. In agents that support it, load the `/void` skill and describe the app you want to build. See [Coding Agents](../integrations/agents) for setup details.
87
+ Void opens your browser to sign in when needed, builds the app, provisions resources, applies migrations, and prints the deployed URL. Later deploys use the saved target. See [Deployment](./deployment.md) for team platforms, CI, and rollback.
115
88
 
116
- ## Meta Frameworks
89
+ ## Adding to an existing Vite app
117
90
 
118
- Use Void's [Pages routing](./pages-routing/overview) or keep a framework such as TanStack Start, React Router, or SvelteKit. Follow the [framework guides](../integrations/frameworks/overview) for setup.
91
+ Install Void and run `void init` with your package manager as shown [above](#create-an-app). Setup adds `voidPlugin()` to your Vite config and creates the remaining project files. If the config needs a manual edit, the CLI shows the change to make.
119
92
 
120
- ## Adding to an Existing Vite App
93
+ For a meta framework such as TanStack Start, React Router, or SvelteKit, follow its [integration guide](../integrations/frameworks/overview.md).
121
94
 
122
- Install Void using the package manager command [above](#start-in-an-empty-directory).
123
-
124
- Enable the plugin in `vite.config.ts`:
125
-
126
- ```ts
127
- import { defineConfig } from 'vite';
128
- import { voidPlugin } from 'void';
129
-
130
- export default defineConfig({
131
- plugins: [voidPlugin()],
132
- });
133
- ```
134
-
135
- Then run `void init` with your package manager to configure the remaining project files.
95
+ ## Next steps
136
96
 
137
- ## Troubleshooting pnpm installs
97
+ - [Pages Routing](./pages-routing/overview.md): loaders, layouts, and forms
98
+ - [Server Routing](./server-routing.md): params, middleware, and validation
99
+ - [Database](./database.md): queries and migrations
100
+ - [Environment Variables](./env-vars.md): local values and production secrets
101
+ - [Coding Agents](../integrations/agents.md): use Void's bundled docs and skills
138
102
 
139
103
  If a manual pnpm install reports blocked build scripts, run `pnpm approve-builds` for `esbuild`, `sharp`, and `workerd`. Set `better-sqlite3: false` in `pnpm-workspace.yaml`'s `allowBuilds`; Void uses its bundled binaries.
140
-
141
- ## Next steps
142
-
143
- - To understand what kind of apps are supported: [Supported App Types](./app-types)
144
- - [Server Routing](./server-routing): dynamic params, middleware, and validation
145
- - [Database](./database): queries, migrations, and generated types
146
- - [Type Safety](./type-safety): end-to-end typed fetch client