void 0.20.3 → 0.21.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 (294) hide show
  1. package/README.md +2 -2
  2. package/dist/agents-gni4eqBu.mjs +111 -0
  3. package/dist/auth-3sGB0zJ1.mjs +22 -0
  4. package/dist/{auth-DPl6kck4.mjs → auth-B9_D6ygs.mjs} +6 -241
  5. package/dist/auth-CAw7zHqj.mjs +2 -0
  6. package/dist/auth-client-BWBT8HLp.mjs +6 -0
  7. package/dist/auth-client-Bawdj38n.d.mts +7 -0
  8. package/dist/auth-client-react-7GFffu8R.d.mts +7 -0
  9. package/dist/auth-client-react-CMLwT2xI.mjs +6 -0
  10. package/dist/auth-client-solid-DyTFL4cq.d.mts +7 -0
  11. package/dist/auth-client-solid-UoreJWZG.mjs +6 -0
  12. package/dist/auth-client-svelte-Cv_WvrRk.mjs +6 -0
  13. package/dist/auth-client-svelte-zetW0KIH.d.mts +7 -0
  14. package/dist/auth-client-vue-CFk7Xbv3.mjs +6 -0
  15. package/dist/auth-client-vue-bChShbQP.d.mts +7 -0
  16. package/dist/{auth-cmd-CzwquNiP.mjs → auth-cmd-MkBG2u1f.mjs} +5 -5
  17. package/dist/{auth-link-ElDTgF7j.mjs → auth-link-Devmv96E.mjs} +5 -5
  18. package/dist/{better-auth-shared-DSCeohOK.d.mts → better-auth-shared-BoVwA5Vm.d.mts} +1 -6
  19. package/dist/{better-auth-shared-CYw1T3k4.mjs → better-auth-shared-syGYYmnY.mjs} +1 -1
  20. package/dist/{build-cmd-BpJe6boP.mjs → build-cmd-DsBGwtfs.mjs} +6 -4
  21. package/dist/{cache-D98YTqeE.mjs → cache-BMw8eMyF.mjs} +6 -4
  22. package/dist/{cancel-deploy-CPbEQLMG.mjs → cancel-deploy-B3_s9NIN.mjs} +5 -3
  23. package/dist/cf-access-rAZxzMkL.mjs +181 -0
  24. package/dist/cf-build-output-CTdlo6Rt.mjs +520 -0
  25. package/dist/cli/cf-compat.d.mts +1 -0
  26. package/dist/cli/cf-compat.mjs +934 -0
  27. package/dist/cli/cli.mjs +60 -38
  28. package/dist/cli/env-schema-probe.mjs +3 -3
  29. package/dist/client-DAAivdid.mjs +2 -0
  30. package/dist/{client-BQBrZoCX.mjs → client-PdAJ-F0t.mjs} +6 -12
  31. package/dist/{cloudflare-auth-6M5llVPC.mjs → cloudflare-auth-BCmTc1X_.mjs} +13 -44
  32. package/dist/{cloudflare-cmd-4RPGN3KB.mjs → cloudflare-cmd-D6PrKn7o.mjs} +4 -7
  33. package/dist/{cloudflare-connect-t1UU5svD.mjs → cloudflare-connect-DloREDlc.mjs} +6 -6
  34. package/dist/cloudflare-operations-BHNJVDbN.mjs +2 -0
  35. package/dist/{cloudflare-operations-BzWnlC1_.mjs → cloudflare-operations-CPEdHYhM.mjs} +37 -39
  36. package/dist/cloudflare-user-output-Q-k0QoNQ.mjs +20 -0
  37. package/dist/collect-DAUItMDS.mjs +2 -0
  38. package/dist/collect-DXHNWHcT.mjs +48 -0
  39. package/dist/config--T87TD8T.mjs +2 -0
  40. package/dist/{config-NOG_U1aK.mjs → config-BSe70f4T.mjs} +4 -6
  41. package/dist/config-DDIFxQYx.mjs +2 -0
  42. package/dist/{config-uNGuFsI2.mjs → config-Dxr6cTXn.mjs} +22 -37
  43. package/dist/config-DywxBLQC.d.mts +185 -0
  44. package/dist/config-entry.d.mts +6 -0
  45. package/dist/config-entry.mjs +7 -0
  46. package/dist/config-uP7ZDtVZ.mjs +21 -0
  47. package/dist/config-write-y3CBI_ux.mjs +60 -0
  48. package/dist/{connect-WCQZ_u3m.mjs → connect-BvC9kolB.mjs} +8 -6
  49. package/dist/{create-project-D0oXA090.mjs → create-project-C9lQhRzj.mjs} +8 -23
  50. package/dist/create-project-CI-17RVJ.mjs +2 -0
  51. package/dist/database-provider-Cx525bwX.mjs +6 -0
  52. package/dist/database-provider.mjs +1 -5
  53. package/dist/{db-DJ-9qs3S.mjs → db-R7IQgOv5.mjs} +48 -44
  54. package/dist/{delete-BZ4-WaGm.mjs → delete-CghI_NXn.mjs} +6 -4
  55. package/dist/deploy-CYPymbtG.mjs +2 -0
  56. package/dist/{deploy-BAhhcg5q.mjs → deploy-H968Lo4T.mjs} +751 -190
  57. package/dist/discover-BBzDZe_o.mjs +2 -0
  58. package/dist/{discover-xvfrgJeo.mjs → discover-C4O6YxVS.mjs} +3 -8
  59. package/dist/{dist-BrsS7cai.mjs → dist-4WkAWhWx.mjs} +1 -15
  60. package/dist/{dist-m40_XgNh.mjs → dist-Bn8Kodjp.mjs} +1 -1
  61. package/dist/dist-C5fND3R0.mjs +2 -0
  62. package/dist/dist-D7-nEOXi.mjs +2 -0
  63. package/dist/{output-B0cfNSx5.mjs → dist-Dn6nn2IU.mjs} +3 -926
  64. package/dist/{domain-BxAyhxXN.mjs → domain-CITt8lC-.mjs} +7 -5
  65. package/dist/{route-types-Da-DpyUp.mjs → drizzle-Beb2Am5S.mjs} +1 -280
  66. package/dist/{email-Ce6SQq-i.mjs → email-C5kaZXzJ.mjs} +2 -2
  67. package/dist/{email-Bj7Cvdwp.mjs → email-_8VmyX7V.mjs} +14 -12
  68. package/dist/{entry-DU3oDoQ3.mjs → entry-DdRFGK0y.mjs} +2 -2
  69. package/dist/{env-DP_EErve.mjs → env-DV4r3nHz.mjs} +9 -7
  70. package/dist/{env-DBKmK4vc.mjs → env-DX_v-Q-v.mjs} +1 -1
  71. package/dist/env-Yz4HcvHs.mjs +78 -0
  72. package/dist/env-helpers--wFmQ_5Q.mjs +136 -0
  73. package/dist/{env-types-BNPhro-M.mjs → env-types-CWDtqHgw.mjs} +6 -2
  74. package/dist/{env-validation-CF6KvTRf.mjs → env-validation-BsFEXps5.mjs} +19 -28
  75. package/dist/env-validation-DIDGM7h4.mjs +2 -0
  76. package/dist/fetch-CXDChK7B.mjs +18 -0
  77. package/dist/fetch-_SeGZao9.d.mts +57 -0
  78. package/dist/fetch-stream-AOByI7Ki.mjs +81 -0
  79. package/dist/fetch-stream-Bjf0hoZb.d.mts +49 -0
  80. package/dist/gen-BBiIZw6g.mjs +2 -0
  81. package/dist/{gen-B_wPnVTK.mjs → gen-CFOEEc-t.mjs} +13 -11
  82. package/dist/{generate-RTK8_kK1.mjs → generate-C0VY6RVf.mjs} +2 -2
  83. package/dist/{github-cmd-C-z_xRrQ.mjs → github-cmd-jYjha2LO.mjs} +6 -4
  84. package/dist/{handler-D1hLsObx.d.mts → handler-BXJTXd02.d.mts} +6 -1
  85. package/dist/handler-DghKr6dU.mjs +150 -0
  86. package/dist/head-D_QRR5Yd.mjs +112 -0
  87. package/dist/head-client-DzmJGN4C.mjs +90 -0
  88. package/dist/{headers-BAHwgHdW.mjs → headers-B_HBMgi0.mjs} +2 -2
  89. package/dist/{help-CwOX-zmI.mjs → help-C46mnlUS.mjs} +17 -12
  90. package/dist/help-CS_nAsWu.mjs +2 -0
  91. package/dist/index.d.mts +2 -20
  92. package/dist/index.mjs +116 -60
  93. package/dist/{init-BGktCXgA.mjs → init-CSXmvxKu.mjs} +49 -50
  94. package/dist/{link-D2kbqhWb.mjs → link-DxMOALXk.mjs} +7 -5
  95. package/dist/{list-CvkK_G7k.mjs → list-0wQs0oYg.mjs} +7 -5
  96. package/dist/live-CB1y5IuC.mjs +411 -0
  97. package/dist/live-CKiJilLr.d.mts +105 -0
  98. package/dist/local-d1-DzykTWY8.mjs +104 -0
  99. package/dist/{login-WIjNc77c.mjs → login-CKk5NX4d.mjs} +6 -11
  100. package/dist/login-Ch9cgWRF.mjs +2 -0
  101. package/dist/{logs-dLUFCapG.mjs → logs-hxWBSoej.mjs} +6 -4
  102. package/dist/migrate-CLty4mZR.mjs +2 -0
  103. package/dist/migrate-DcW8F2W8.mjs +285 -0
  104. package/dist/migration-handler-DM4clYj5.d.mts +51 -0
  105. package/dist/{neon-DHwd2zvC.mjs → neon-n74ta1Pr.mjs} +1 -1
  106. package/dist/{node-Ez5KW5rn.mjs → node-Cupyf7-s.mjs} +6 -6
  107. package/dist/{operator-auth-B3e08unv.mjs → operator-auth-BkVgJqv-.mjs} +2 -2
  108. package/dist/{operator-client-LUZnlnYk.mjs → operator-client-A0iex2yi.mjs} +2 -1
  109. package/dist/{operator-cmd-CKJ7xIRs.mjs → operator-cmd-BEUYhaHB.mjs} +6 -6
  110. package/dist/output-urU86XeT.mjs +146 -0
  111. package/dist/{package-json-CPoWX79C.mjs → package-json-iCbMg5XF.mjs} +1 -1
  112. package/dist/pages/client.d.mts +5 -2
  113. package/dist/pages/client.mjs +5 -3
  114. package/dist/pages/head-client.mjs +1 -89
  115. package/dist/pages/head.mjs +1 -111
  116. package/dist/pages/index.d.mts +2 -3
  117. package/dist/pages/index.mjs +7 -7
  118. package/dist/pages/islands-plugin.mjs +2 -2
  119. package/dist/pages/prefetch.d.mts +2 -30
  120. package/dist/pages/prefetch.mjs +1 -89
  121. package/dist/pages/protocol.d.mts +2 -2
  122. package/dist/pages/protocol.mjs +3 -3
  123. package/dist/pages/serialize.d.mts +2 -9
  124. package/dist/pages/serialize.mjs +1 -13
  125. package/dist/plan-BEZ8VJW0.mjs +256 -0
  126. package/dist/plan-DpuOr14e.mjs +2 -0
  127. package/dist/{platform-auth-config-CdVWRRJr.mjs → platform-auth-config-Df6yVw-e.mjs} +6 -6
  128. package/dist/{platform-auth-protection-Drl0qhrn.mjs → platform-auth-protection-Jb0yftay.mjs} +7 -5
  129. package/dist/{platform-auth-recovery-CmKEWpDo.mjs → platform-auth-recovery-DmRyIfW0.mjs} +6 -5
  130. package/dist/platform-cmd-C9Vt7m-d.mjs +2 -0
  131. package/dist/{platform-cmd-5q_k56xS.mjs → platform-cmd-DRxCOTwy.mjs} +8 -5
  132. package/dist/{platform-domain-4GiDlcqx.mjs → platform-domain-IjiXgrXJ.mjs} +4 -3
  133. package/dist/{platform-lifecycle-R9xAxvtG.mjs → platform-lifecycle-BquAaU-5.mjs} +208 -37
  134. package/dist/platform-lifecycle-CuJIZNvA.mjs +2 -0
  135. package/dist/{platform-management-BfWsXHEW.mjs → platform-management-CCQKGsq4.mjs} +8 -5
  136. package/dist/platform-management-Cgyed0WY.mjs +2 -0
  137. package/dist/{platform-recovery-CbK-I1FB.mjs → platform-recovery-DS8ih1SW.mjs} +3 -3
  138. package/dist/platform-registry-BJbgZLS1.mjs +431 -0
  139. package/dist/{plugin-inference-BDRfZngg.mjs → plugin-inference-DsvtJLll.mjs} +4 -4
  140. package/dist/prefetch-Bsc_Pb6c.mjs +90 -0
  141. package/dist/prefetch-Ce6la4EI.d.mts +31 -0
  142. package/dist/{prepare-blNRQvQl.mjs → prepare-CaUxODOU.mjs} +3 -2
  143. package/dist/prepare-D4CkM3_v.mjs +2 -0
  144. package/dist/{prepare-CtDJjoOj.mjs → prepare-pcWcxSCh.mjs} +13 -11
  145. package/dist/prerender-render-Cf_WDE9W.mjs +111 -0
  146. package/dist/prerender-render.mjs +1 -110
  147. package/dist/{preset-lAy0B0BQ.mjs → preset-Dowh9tTt.mjs} +16 -118
  148. package/dist/project-BEBFDFLz.mjs +2 -0
  149. package/dist/project-CWNIPoXc.mjs +209 -0
  150. package/dist/{project-cmd-CTdmnzvc.mjs → project-cmd-2lN--YAX.mjs} +18 -16
  151. package/dist/{project-paths-SK8nMHPp.mjs → project-paths-CKQ-Q5JS.mjs} +47 -14
  152. package/dist/project-slug-23TpquG4.mjs +8 -0
  153. package/dist/project-slug-DofjTFd-.mjs +2 -0
  154. package/dist/{project-team-CGxsQe3_.mjs → project-team-Dapn9HZn.mjs} +6 -4
  155. package/dist/{project-token-Cirx7uwZ.mjs → project-token-C90v9SJK.mjs} +6 -4
  156. package/dist/{project-tsconfig-Ql2XsSQp.mjs → project-tsconfig-CwfqUnVp.mjs} +2 -2
  157. package/dist/{protocol-C-pqYJjE.d.mts → protocol-ZH3jP4a7.d.mts} +1 -1
  158. package/dist/providers-BNKRacMr.d.mts +7 -0
  159. package/dist/provision-CNgEBkVA.mjs +3 -0
  160. package/dist/{provision-CSJOjjQk.mjs → provision-Cck2m3jJ.mjs} +11 -25
  161. package/dist/queues-BWKt1Xo4.d.mts +7 -0
  162. package/dist/{requests-4Nq59hOr.mjs → requests-CzX46I0i.mjs} +5 -3
  163. package/dist/resolve-project-BTotl8Nn.mjs +2 -0
  164. package/dist/{resolve-project--Vxawf7z.mjs → resolve-project-Xvis70DG.mjs} +2 -8
  165. package/dist/response-Tn7rU0MV.mjs +30 -0
  166. package/dist/{rollback-Dr7u0Ljx.mjs → rollback-BEeyc73H.mjs} +6 -4
  167. package/dist/{rolldown-runtime-rQ84J-ij.mjs → rolldown-runtime-DXIUcv95.mjs} +1 -10
  168. package/dist/route-types-Id82-veQ.mjs +280 -0
  169. package/dist/{local-d1-D2I6Ox5F.mjs → runner-BGVsGgkb.mjs} +6 -114
  170. package/dist/runner-CQs_cDSG.mjs +2 -0
  171. package/dist/runner-mysql-1o47achK.mjs +2 -0
  172. package/dist/{runner-mysql-7BPUNGmL.mjs → runner-mysql-BhwMk2Bm.mjs} +2 -9
  173. package/dist/{runner-pg-BkEza-dX.mjs → runner-pg-CJ_JeGF6.mjs} +2 -9
  174. package/dist/runner-pg-D7z01mQS.mjs +2 -0
  175. package/dist/runtime/ai.mjs +1 -1
  176. package/dist/runtime/auth-client-react.d.mts +2 -6
  177. package/dist/runtime/auth-client-react.mjs +1 -5
  178. package/dist/runtime/auth-client-solid.d.mts +2 -6
  179. package/dist/runtime/auth-client-solid.mjs +1 -5
  180. package/dist/runtime/auth-client-svelte.d.mts +2 -6
  181. package/dist/runtime/auth-client-svelte.mjs +1 -5
  182. package/dist/runtime/auth-client-vue.d.mts +2 -6
  183. package/dist/runtime/auth-client-vue.mjs +1 -5
  184. package/dist/runtime/auth-client.d.mts +2 -6
  185. package/dist/runtime/auth-client.mjs +1 -5
  186. package/dist/runtime/auth.mjs +1 -21
  187. package/dist/runtime/better-auth-mysql.d.mts +1 -1
  188. package/dist/runtime/better-auth-mysql.mjs +1 -1
  189. package/dist/runtime/better-auth-pg.d.mts +1 -1
  190. package/dist/runtime/better-auth-pg.mjs +1 -1
  191. package/dist/runtime/better-auth.d.mts +1 -1
  192. package/dist/runtime/better-auth.mjs +1 -1
  193. package/dist/runtime/client-react.d.mts +3 -3
  194. package/dist/runtime/client-react.mjs +3 -3
  195. package/dist/runtime/client-solid.d.mts +3 -3
  196. package/dist/runtime/client-solid.mjs +3 -3
  197. package/dist/runtime/client-svelte.d.mts +3 -3
  198. package/dist/runtime/client-svelte.mjs +3 -3
  199. package/dist/runtime/client-vue.d.mts +3 -3
  200. package/dist/runtime/client-vue.mjs +3 -3
  201. package/dist/runtime/client.d.mts +3 -3
  202. package/dist/runtime/client.mjs +3 -3
  203. package/dist/runtime/db.mjs +1 -1
  204. package/dist/runtime/durable.mjs +1 -1
  205. package/dist/runtime/email/testing.mjs +1 -1
  206. package/dist/runtime/env-helpers.mjs +1 -135
  207. package/dist/runtime/env-public-client.mjs +1 -1
  208. package/dist/runtime/env-public.mjs +2 -2
  209. package/dist/runtime/env.mjs +1 -77
  210. package/dist/runtime/fetch-stream.d.mts +2 -49
  211. package/dist/runtime/fetch-stream.mjs +2 -80
  212. package/dist/runtime/fetch.d.mts +2 -57
  213. package/dist/runtime/fetch.mjs +2 -17
  214. package/dist/runtime/handler.d.mts +1 -1
  215. package/dist/runtime/handler.mjs +1 -149
  216. package/dist/runtime/kv.mjs +1 -1
  217. package/dist/runtime/live-client.d.mts +1 -1
  218. package/dist/runtime/live-server.mjs +2 -2
  219. package/dist/runtime/live.d.mts +2 -104
  220. package/dist/runtime/live.mjs +1 -410
  221. package/dist/runtime/migration-handler-mysql.d.mts +1 -1
  222. package/dist/runtime/migration-handler-pg.d.mts +1 -1
  223. package/dist/runtime/migration-handler.d.mts +2 -50
  224. package/dist/runtime/queues.d.mts +2 -6
  225. package/dist/runtime/queues.mjs +1 -1
  226. package/dist/runtime/response.mjs +1 -29
  227. package/dist/runtime/sandbox.mjs +1 -1
  228. package/dist/runtime/sse.mjs +1 -171
  229. package/dist/runtime/storage.mjs +1 -1
  230. package/dist/runtime/validator.d.mts +1 -1
  231. package/dist/runtime/validator.mjs +1 -71
  232. package/dist/runtime/ws-server.d.mts +2 -2
  233. package/dist/runtime/ws-server.mjs +2 -2
  234. package/dist/runtime/ws.d.mts +2 -121
  235. package/dist/{scan-CpK-57ug.mjs → scan-DJbooZm2.mjs} +3 -3
  236. package/dist/{scan-BMH4rzlv.mjs → scan-DdDvRCU1.mjs} +8 -26
  237. package/dist/{secret-AcPi-FoA.mjs → secret-wnTel5Yw.mjs} +8 -6
  238. package/dist/serialize-BPvnNQuA.mjs +14 -0
  239. package/dist/serialize-CfSwWfF2.d.mts +10 -0
  240. package/dist/{skills-C0RvGjeE.mjs → skills-B-690E7h.mjs} +3 -2
  241. package/dist/sse-BaC1jXko.mjs +172 -0
  242. package/dist/{subcommand-prompt-Bmyn5Rlc.mjs → subcommand-prompt-Gj3VzLIh.mjs} +2 -1
  243. package/dist/sveltekit.d.mts +2 -1
  244. package/dist/sveltekit.mjs +3 -2
  245. package/dist/validate-DqJ33oHj.mjs +2 -0
  246. package/dist/validate-qNhV00PD.mjs +180 -0
  247. package/dist/validator-BTOu0fB0.mjs +72 -0
  248. package/dist/{wrangler--imS8n0d.mjs → wrangler-D01qs6VB.mjs} +259 -71
  249. package/dist/ws-BwcqizuH.d.mts +122 -0
  250. package/dist/{yarn-pnp-DxSInkzL.mjs → yarn-pnp-CVEc3gE7.mjs} +1 -1
  251. package/package.json +19 -8
  252. package/skills/void/SKILL.md +6 -4
  253. package/skills/void/docs/guide/ai.md +3 -3
  254. package/skills/void/docs/guide/app-types.md +12 -11
  255. package/skills/void/docs/guide/auth.md +2 -2
  256. package/skills/void/docs/guide/database/d1.md +1 -1
  257. package/skills/void/docs/guide/database/mysql.md +1 -1
  258. package/skills/void/docs/guide/database/postgresql.md +3 -3
  259. package/skills/void/docs/guide/deployment.md +2 -2
  260. package/skills/void/docs/guide/durable-state.md +2 -2
  261. package/skills/void/docs/guide/edge/headers.md +3 -3
  262. package/skills/void/docs/guide/edge/prerendering.md +1 -1
  263. package/skills/void/docs/guide/edge/redirects.md +4 -4
  264. package/skills/void/docs/guide/edge/revalidation.md +6 -6
  265. package/skills/void/docs/guide/edge/rewrites.md +28 -27
  266. package/skills/void/docs/guide/edge/static-assets.md +1 -1
  267. package/skills/void/docs/guide/email.md +15 -15
  268. package/skills/void/docs/guide/env-migration.md +1 -1
  269. package/skills/void/docs/guide/index.md +1 -1
  270. package/skills/void/docs/guide/pages-routing/head.md +1 -1
  271. package/skills/void/docs/guide/platform/administration/access.md +13 -5
  272. package/skills/void/docs/guide/platform/administration/projects.md +3 -1
  273. package/skills/void/docs/guide/platform/installation/ci.md +3 -0
  274. package/skills/void/docs/guide/platform/installation/credentials.md +15 -5
  275. package/skills/void/docs/guide/platform/installation/prerequisites.md +2 -0
  276. package/skills/void/docs/guide/platform/installation/setup.md +1 -1
  277. package/skills/void/docs/guide/remote-dev.md +2 -2
  278. package/skills/void/docs/guide/sandboxes.md +1 -1
  279. package/skills/void/docs/guide/ssg.md +1 -1
  280. package/skills/void/docs/guide/websockets.md +1 -1
  281. package/skills/void/docs/integrations/cloudflare.md +86 -84
  282. package/skills/void/docs/integrations/frameworks/analog.md +14 -9
  283. package/skills/void/docs/integrations/frameworks/astro.md +14 -10
  284. package/skills/void/docs/integrations/frameworks/nuxt.md +14 -9
  285. package/skills/void/docs/integrations/frameworks/overview.md +35 -29
  286. package/skills/void/docs/integrations/frameworks/react-router.md +1 -1
  287. package/skills/void/docs/integrations/frameworks/sveltekit.md +33 -30
  288. package/skills/void/docs/integrations/frameworks/tanstack-start.md +1 -1
  289. package/skills/void/docs/integrations/nodejs-bun-deno.md +5 -5
  290. package/skills/void/docs/reference/api.md +21 -4
  291. package/skills/void/docs/reference/cli.md +32 -23
  292. package/skills/void/docs/reference/config.md +56 -33
  293. package/skills/void/docs/reference/structure.md +2 -2
  294. package/dist/validate-tBBN_dXH.mjs +0 -505
@@ -49,7 +49,7 @@ Each project on an email-enabled platform has:
49
49
 
50
50
  That's it for configuration. Delivery is gated separately: outbound mail only reaches verified recipients, so `sendEmail({ to: '...', subject: '...', text: '...' })` works on first deploy for your own address once it is verified, and for anyone else after `void email allow` — see [Adding recipients](#adding-recipients).
51
51
 
52
- Deploying to your own Cloudflare account instead (`void deploy --platform cloudflare`) takes one line of `void.json` and one Enter on the first deploy — see [Your own Cloudflare account](#your-own-cloudflare-account).
52
+ Deploying to your own Cloudflare account instead (`void deploy --platform cloudflare`) takes one line of `void.config.ts` and one Enter on the first deploy — see [Your own Cloudflare account](#your-own-cloudflare-account).
53
53
 
54
54
  ## Adding recipients
55
55
 
@@ -126,7 +126,7 @@ At most 50 recipients across `to`, `cc` and `bcc` per call. Each address is chec
126
126
 
127
127
  On the platform the sender is pinned to your project. `from` must be your project's own platform address — `<project-slug>@<mail-domain>` or `<project-slug>+<tag>@<mail-domain>`, optionally with a display name — or any address on a domain registered with `void email domain add` (see [Your own domain on the platform](#your-own-domain-on-the-platform)). For example, if your platform's mail domain is `mail.example.com`, you can use `Acme <acme+noreply@mail.example.com>`. Anything else is rejected with `INVALID_FROM`. Omit `from` and Void fills in `<project-slug>+noreply@<mail-domain>` for you.
128
128
 
129
- On your own Cloudflare account, `from` defaults to `email.from` from `void.json` and must be on a domain your account can send from; Cloudflare rejects any other sender and `sendEmail` reports it as `INVALID_FROM`.
129
+ On your own Cloudflare account, `from` defaults to `email.from` from `void.config.ts` and must be on a domain your account can send from; Cloudflare rejects any other sender and `sendEmail` reports it as `INVALID_FROM`.
130
130
 
131
131
  ## Attachments
132
132
 
@@ -239,7 +239,7 @@ Under the hood, your code runs in workerd (a separate process from the Vite dev
239
239
 
240
240
  The dev inbox is **not** available under a Class B or C framework — SvelteKit, Nuxt, Analog, Astro. Those adapters own their own dev server and worker build, so Void never installs the Cloudflare Vite plugin for them and has nowhere to register the inbox. Declaring an `assets` binding does not help: SvelteKit, Nuxt and Analog do not run your code in a workerd instance fronted by Vite at all, so there is no loopback to bind to. Sends from those apps return `BINDING_MISSING`. Use `createEmailTestHarness` from `void/email/testing` instead, which captures in-process and works everywhere. When the inbox is configured but unreachable, `sendEmail` returns `UPSTREAM_ERROR` — nothing is captured and nothing is sent.
241
241
 
242
- `sendInDev: true` bypasses the dev inbox for a single send. `void dev` binds no send transport at all — the platform's `__VOID_PROXY` service binding is added only on a deployed worker, and the own-account `SEND_EMAIL` binding is stripped under `serve` so miniflare cannot write stray `.eml` files or send real mail (only that entry: a `send_email` binding of your own under another name is left exactly as `wrangler.jsonc` declares it). With the inbox skipped there is nothing left to fall through to, so the call returns `BINDING_MISSING`: it proves the inbox was bypassed, it does not deliver. To verify real delivery, deploy and send from the deployed worker.
242
+ `sendInDev: true` bypasses the dev inbox for a single send. `void dev` binds no send transport at all — the platform's `__VOID_PROXY` service binding is added only on a deployed worker, and the own-account `SEND_EMAIL` binding is stripped under `serve` so miniflare cannot write stray `.eml` files or send real mail (only that entry: a `send_email` binding of your own under another name is left exactly as `cloudflare.send_email` in `void.config.ts` declares it). With the inbox skipped there is nothing left to fall through to, so the call returns `BINDING_MISSING`: it proves the inbox was bypassed, it does not deliver. To verify real delivery, deploy and send from the deployed worker.
243
243
 
244
244
  ```ts
245
245
  const result = await sendEmail({
@@ -408,7 +408,7 @@ deployed `email/` handlers without per-project DNS setup. A
408
408
  handlers once its inbound readiness is `ready`. Use `void email domain status`
409
409
  to check current provider routing and any remaining setup steps.
410
410
 
411
- On your own Cloudflare account there is no shared facility: the deploy derives one Email Routing rule per handler and writes it into `wrangler.jsonc` for you — see [Your own Cloudflare account](#your-own-cloudflare-account).
411
+ On your own Cloudflare account there is no shared facility: the deploy derives one Email Routing rule per handler and records it in `void.lock.json` for you — see [Your own Cloudflare account](#your-own-cloudflare-account).
412
412
 
413
413
  There is no local inbound trigger yet — `void dev` serves the outbound dev inbox only, so test inbound handlers with `createInboundTestHarness` below.
414
414
 
@@ -478,7 +478,7 @@ The harness uses the same precedence rules as the production dispatcher. Reserve
478
478
 
479
479
  ### Two things to type, once
480
480
 
481
- 1. **`email.from` in `void.json`** — the default sender, and the domain Void sets up:
481
+ 1. **`email.from` in `void.config.ts`** — the default sender, and the domain Void sets up:
482
482
 
483
483
  ```json
484
484
  {
@@ -488,17 +488,17 @@ The harness uses the same precedence rules as the production dispatcher. Reserve
488
488
  }
489
489
  ```
490
490
 
491
- The host (`mail.acme.com`) must be a zone in the Cloudflare account your deploy is pinned to, or sit under one. Without `email.from`, a deploy that uses email prints `add "email": { "from": "you@mail.acme.com" } to void.json` and deploys without it. Void does not pick a zone for you — wrangler cannot list them — and it never writes `void.json`.
491
+ The host (`mail.acme.com`) must be a zone in the Cloudflare account your deploy is pinned to, or sit under one. Without `email.from`, a deploy that uses email asks you to set it in `void.config.ts` and deploys without email. Void does not pick a zone for you or write the setting automatically.
492
492
 
493
493
  2. **`void cloudflare login`**, or **`CLOUDFLARE_API_TOKEN`** set to a token with **Email Routing Edit** and **Email Sending Edit** (zone and account) alongside the deploy permissions. A browser session created by older Cloudflare tooling lacks the two email scopes; the deploy tells you to run `void cloudflare logout`, then `void cloudflare login` (one browser Allow) and skips the email step. A token's permissions cannot be listed up front, so a token that lacks one shows up as a row Void could not read (`unknown`), with the permission named. A Global API Key pair (`CLOUDFLARE_API_KEY`) is refused: it has no single bearer for the two calls wrangler has no command for.
494
494
 
495
495
  ### The first deploy
496
496
 
497
- Before any of your project code runs, the deploy **reads** — the session, the zone, public DNS (MX on the mail domain and on the apex, `_dmarc`, `cf-bounce`), Email Routing status, the zone's subaddressing setting, the routing rules on your addresses, Email Sending, and what `wrangler.jsonc` already holds — and prints what it found and what it would change:
497
+ Before any of your project code runs, the deploy **reads** — the session, the zone, public DNS (MX on the mail domain and on the apex, `_dmarc`, `cf-bounce`), Email Routing status, the zone's subaddressing setting, the routing rules on your addresses, Email Sending, and what the resolved Cloudflare config already holds — and prints what it found and what it would change:
498
498
 
499
499
  ```
500
500
  Email in use email/support+[ticket].ts · email/_default.ts · sendEmail() in 2 files
501
- domain mail.acme.com (void.json email.from)
501
+ domain mail.acme.com (void.config.ts email.from)
502
502
  zone acme.com account Acme (f721b8e5…) · session dev@acme.com
503
503
  apex MX aspmx.l.google.com left alone — mail lives on the subdomain
504
504
  routing mail.acme.com not enabled · subaddressing off
@@ -511,7 +511,7 @@ Before any of your project code runs, the deploy **reads** — the session, the
511
511
  + enable Email Routing on mail.acme.com Cloudflare writes and locks 3 MX + 1 SPF record there
512
512
  + turn on subaddressing for acme.com support+anything@ reaches support@
513
513
  + onboard mail.acme.com for Email Sending MX/SPF/DKIM on cf-bounce.mail.acme.com, _dmarc.mail.acme.com (p=reject)
514
- + wrangler.jsonc send_email: [{ name: "SEND_EMAIL" }], addresses: ["support@mail.acme.com"], vars.__VOID_EMAIL_FROM: "noreply@mail.acme.com"
514
+ + Cloudflare config send_email: [{ name: "SEND_EMAIL" }], addresses: ["support@mail.acme.com"], vars.__VOID_EMAIL_FROM: "noreply@mail.acme.com"
515
515
  + routing rule (created on deploy) support@mail.acme.com → acme-support
516
516
  ! email/_default.ts a catch-all exists only on an apex; other @mail.acme.com mail bounces
517
517
 
@@ -530,7 +530,7 @@ On the second deploy every row reads ready: no prompt, no account call, and wran
530
530
 
531
531
  DNS can take a few minutes to become visible to the resolver running the deploy. If `void email setup` has already written the exact `addresses` plan but that resolver still sees no subdomain MX, Void preserves the plan and stops before the build or upload. Run `void email status --platform cloudflare`, then retry the deploy after it sees Cloudflare's MX records.
532
532
 
533
- Two rows live in `wrangler.jsonc` rather than in your account, and a deploy that finds every account row ready reconciles them with a plain file write, no prompt: the `addresses` array is rewritten whenever it is not the current derivation (an entry pruned since, a worker rename, a new handler), and `vars.__VOID_EMAIL_FROM` follows a changed `email.from`. Routing rules are `wrangler deploy`'s own work, so a committed `addresses` entry whose rule does not exist yet — the state right after `void email setup` — still reads ready; the address map marks it `(rule created by this deploy)`.
533
+ Two rows live in the generated Cloudflare config rather than in your account, and a deploy that finds every account row ready reconciles them with a plain file write, no prompt: the `addresses` array is rewritten whenever it is not the current derivation (an entry pruned since, a worker rename, a new handler), and `vars.__VOID_EMAIL_FROM` follows a changed `email.from`. Routing rules are `wrangler deploy`'s own work, so a committed `addresses` entry whose rule does not exist yet — the state right after `void email setup` — still reads ready; the address map marks it `(rule created by this deploy)`.
534
534
 
535
535
  A subdomain is added to a zone that already routes. When Email Routing is **off** on the apex (`Enabled: false`), the subdomain step is not attempted at all — Void never enables routing on the apex from the subdomain path, since that would lock MX records over the apex's live mail — and the checklist prints the dashboard step (`Email → Settings → Subdomains → add mail.acme.com`) instead; `addresses` is withheld until routing on the subdomain reads ready.
536
536
 
@@ -546,7 +546,7 @@ The apex path (`email.from` on `acme.com` itself) is allowed with the same one E
546
546
 
547
547
  **Subaddressing** is a per-zone Cloudflare setting, and it is off by default. Until it is on, a rule for `support@mail.acme.com` does not match `support+T-42@mail.acme.com`. The checklist's `turn on subaddressing for acme.com` row flips it for the whole zone — on the apex and every subdomain — so `email/support+[ticket].ts` works the way it does on the platform.
548
548
 
549
- ### What Void writes into `wrangler.jsonc`
549
+ ### What Void records in `void.lock.json`
550
550
 
551
551
  ```jsonc
552
552
  {
@@ -565,7 +565,7 @@ The apex path (`email.from` on `acme.com` itself) is allowed with the same one E
565
565
  - An address already routed to another worker or to a forwarding rule is **pruned** from the array and reported (`! support@mail.acme.com already routed to …; left alone, not in addresses`). wrangler's plan is never destructive on your account because of something Void derived.
566
566
  - If the array holds entries Void did not derive, the deploy prints which ones and **skips the email step for that deploy** — the deploy itself continues and the array is left untouched. Remove them, or manage `addresses` by hand and leave `email.from` unset.
567
567
 
568
- Deleting a handler leaves its address in `addresses`: the next deploy names it in a `✘` row as an entry Void no longer derives, and skips the email step until you remove that entry from `wrangler.jsonc` by hand (the row says which). Once removed, wrangler's plan drops the rule with its own y/n (default No) in a terminal, an error in CI. Removing the last handler and every `sendEmail()` call leaves the whole setup in `wrangler.jsonc`; the deploy warns which addresses are still routed to a worker with no `email()` export and how to detach them, and deploys as-is.
568
+ Deleting a handler leaves its address in `addresses`: the next deploy names it in a `✘` row as an entry Void no longer derives, and skips the email step until you override `cloudflare.addresses` in `void.config.ts` with the desired list (the row says which). Once removed, Cloudflare's plan drops the rule with its own y/n (default No) in a terminal, an error in CI. Removing the last handler and every `sendEmail()` call leaves the whole setup in `void.lock.json`; the deploy warns which addresses are still routed to a worker with no `email()` export and how to detach them, and deploys as-is. To detach fully, remove `addresses`, `send_email`, and `vars.__VOID_EMAIL_FROM` from `resolved` in `void.lock.json` and remove any authored overrides in `void.config.ts`, then delete the routing rules in Cloudflare.
569
569
 
570
570
  How handlers become addresses, with `email.from` on `mail.acme.com` under the zone `acme.com`:
571
571
 
@@ -593,7 +593,7 @@ Workers Paid needed for arbitrary recipients. Inbound works; sendEmail() to veri
593
593
 
594
594
  Everything else — routing, rules, the binding — still goes through, and the address map ends with `outbound sendEmail() from support@mail.acme.com (verified destinations only)`. Verified destinations are the addresses under **Email Routing → Destination addresses** in your Cloudflare dashboard; a handler that `forward()`s to a new address needs the same verification click.
595
595
 
596
- The refusal is remembered by the `send_email` binding that same run writes into `wrangler.jsonc`: Void writes the binding only after it has attempted the onboarding, so a committed binding next to a domain that is still not onboarded means "tried, refused". Later deploys ask nothing about it, `--require-email` passes, `void email status` reads the domain as set up (its sending row says `not onboarded — verified destinations only` and names the retry), and the address map keeps ending with `(verified destinations only)`. The deploy never retries the onboarding on its own. After upgrading to Workers Paid, run `void email setup --platform cloudflare` once: it asks `Onboard mail.acme.com for Email Sending?` and, on Yes, onboards the domain — from then on the map ends without the marker.
596
+ The refusal is remembered by the `send_email` binding that same run records in `void.lock.json`: Void records the binding only after it has attempted the onboarding, so a committed binding next to a domain that is still not onboarded means "tried, refused". Later deploys ask nothing about it, `--require-email` passes, `void email status` reads the domain as set up (its sending row says `not onboarded — verified destinations only` and names the retry), and the address map keeps ending with `(verified destinations only)`. The deploy never retries the onboarding on its own. After upgrading to Workers Paid, run `void email setup --platform cloudflare` once: it asks `Onboard mail.acme.com for Email Sending?` and, on Yes, onboards the domain — from then on the map ends without the marker.
597
597
 
598
598
  If `_dmarc.mail.acme.com` or `cf-bounce.mail.acme.com` already has a TXT record, sending onboarding is refused — it writes its own `_dmarc` (`p=reject`) and DKIM records and Cloudflare would answer with a conflict. Inbound is unaffected; remove the records or keep sending off.
599
599
 
@@ -603,12 +603,12 @@ The email prompt follows wrangler's own interactivity rule: a CI environment as
603
603
 
604
604
  ```
605
605
  deploy: email on mail.acme.com is not set up, and this shell cannot ask.
606
- Run `void email setup --platform cloudflare` once locally, commit wrangler.jsonc, then redeploy — deploying without email.
606
+ Run `void email setup --platform cloudflare` once locally, commit void.lock.json, then redeploy — deploying without email.
607
607
  ```
608
608
 
609
609
  then deploys **without** email. Pass `--require-email` to fail instead. When setup has already committed the exact subdomain `addresses` plan and only its MX records are not visible yet, every deploy stops and preserves that plan until DNS can be verified. Automatic resource provisioning is not an escape hatch: it creates D1/KV/R2/Queue/Hyperdrive resources, never a mail setup. To read the rows without deploying or being asked anything, run `void email status --platform cloudflare`.
610
610
 
611
- So the CI story is: run `void email setup --platform cloudflare` once on your machine (it runs the same preflight, checklist and prompt as the first deploy, then writes `wrangler.jsonc`, without deploying), commit `wrangler.jsonc`, and let CI run `void deploy --platform cloudflare --require-email`. Once the MX records are visible, the committed binding and `addresses` make every row read ready and nothing is asked — on Workers Free too, where the committed binding is what remembers the refused sending onboarding (see [Sending](#sending)).
611
+ So the CI story is: run `void email setup --platform cloudflare` once on your machine (it runs the same preflight, checklist and prompt as the first deploy, then updates `void.lock.json`, without deploying), commit `void.lock.json`, and let CI run `void deploy --platform cloudflare --require-email`. Once the MX records are visible, the committed binding and `addresses` make every row read ready and nothing is asked — on Workers Free too, where the committed binding is what remembers the refused sending onboarding (see [Sending](#sending)).
612
612
 
613
613
  ### If the subdomain step is refused
614
614
 
@@ -80,7 +80,7 @@ void secret list
80
80
 
81
81
  Remote stores return names only; plaintext values are validated during upload and again at worker startup. If an upload fails or a name is missing, retain the source values and retry before continuing.
82
82
 
83
- For direct Cloudflare deployments, remove the migrated server keys from `vars` in `wrangler.jsonc` after confirming the upload. Void now treats any schema-declared server key in Worker `vars` as an error.
83
+ For direct Cloudflare deployments, remove the migrated server keys from `cloudflare.vars` in `void.config.ts` after confirming the upload. Void now treats any schema-declared server key in Worker `vars` as an error.
84
84
 
85
85
  ## 4. Supply client values at build time
86
86
 
@@ -36,7 +36,7 @@ Types follow your data through the app: from a Drizzle schema to a route handler
36
36
 
37
37
  Void brings these pieces together:
38
38
 
39
- - **Resources from your code:** imports tell Void which supported resources to provision. Use `void.json` or your Cloudflare config when you need to customize them.
39
+ - **Resources from your code:** imports tell Void which supported resources to provision. Use `void.config.ts` and its `cloudflare` field when you need to customize them.
40
40
  - **Types from database to frontend:** your Drizzle schema defines DB types, route handlers infer return types, and the [typed fetch client](./typed-fetch.md) checks calls at the usage site. One [Standard Schema](https://standardschema.dev/) validator can drive both runtime validation and compile-time types.
41
41
  - **Local Cloudflare development:** native Void apps run server code in `workerd`, with local database, KV, and storage.
42
42
  - **Deploy that understands the app:** `void deploy` reads your migrations, provisions the resources you actually use, and ships the result to the edge.
@@ -39,7 +39,7 @@ export const head = defineHead<Props>((c, props) => {
39
39
 
40
40
  ## Config Defaults
41
41
 
42
- Set site-wide head defaults in `void.json`:
42
+ Set site-wide head defaults in `void.config.ts`:
43
43
 
44
44
  ```json
45
45
  {
@@ -112,10 +112,12 @@ dashboard. Sign in again first if prompted, then authenticate with the additiona
112
112
  provider and confirm the identity shown. Matching email addresses alone do not
113
113
  link accounts.
114
114
 
115
- For company installations, choose **Company-approved users** under **Who can
116
- join?** to create accounts automatically for users accepted by your configured
117
- company rules. Individual invitations are not required. Invited/allowlisted
118
- signup remains available when you need to approve people individually.
115
+ In the admin dashboard, open **People → Access** to choose who can join, invite
116
+ people, and manage individual signup grants. Choose **Company-approved or
117
+ individually approved users** to create accounts automatically for people who
118
+ meet your configured company rules while still allowing explicit invitations
119
+ and allowlist grants. Company protection at the platform edge still applies
120
+ before anyone reaches a login page.
119
121
 
120
122
  With invited/allowlisted signup selected, let a teammate join with GitHub by adding their login to the allowlist:
121
123
 
@@ -148,7 +150,13 @@ To invite someone else by email, use:
148
150
  void platform invitation send alex@example.org
149
151
  ```
150
152
 
151
- An invitation grants signup access and sends an email when the platform has email delivery configured. If delivery is unavailable or fails, the result tells you; the person can still join using the platform's URL.
153
+ An invitation grants signup access for that exact email address. The person must
154
+ sign in with a login method that supplies the same verified email address.
155
+ When platform email delivery is configured, Void also sends connection
156
+ instructions. If delivery is unavailable or fails, the grant still works:
157
+ share the platform's `/invite` page or `void connect '<platform URL>'` command
158
+ with the person yourself. The admin dashboard shows the delivery outcome
159
+ separately from the invitation's pending, accepted, or revoked status.
152
160
 
153
161
  Inspect the current access settings and invitations with:
154
162
 
@@ -23,9 +23,11 @@ void platform project show <project-id>
23
23
 
24
24
  Project details include resources, domains, and recent builds and deployments. The [command reference](/reference/cli#operator-commands) also covers suspending and restoring users, deleting projects, and removing accounts.
25
25
 
26
+ In the admin dashboard, open a project to view its team. Search for a platform user and choose a role to add them immediately; an email address is not required. You can also change or remove existing members. Pending invitations created through other project workflows remain visible until accepted or revoked. If email is unavailable for a pending invitation, share its ID so the user can accept it with `void project team accept <invitation-id>`. The owner cannot be removed from the team; transfer ownership first.
27
+
26
28
  ### Transferring Project Ownership
27
29
 
28
- Only an installation administrator can change a project's owner. The new owner must already have an account on this platform. Preview the transfer before applying it:
30
+ Only an installation administrator can change a project's owner. The new owner must already have an account on this platform. In the dashboard, search for the new owner on the project page and preview the transfer before applying it. The CLI accepts a user ID:
29
31
 
30
32
  ```sh
31
33
  void platform project owner <project-id> <new-owner-user-id> --plan
@@ -15,6 +15,9 @@ Inject the following values from protected CI secrets. Do not commit them in a w
15
15
  | `CLOUDFLARE_API_TOKEN` | Management API token |
16
16
  | `VOID_PLATFORM_RUNTIME_CLOUDFLARE_API_TOKEN` | Runtime API token |
17
17
  | `VOID_PLATFORM_DATABASE_URL` | Dedicated PostgreSQL URL |
18
+ | `VOID_PLATFORM_HYPERDRIVE_ID` | Existing externally managed Hyperdrive ID, when applicable |
19
+ | `VOID_PLATFORM_HYPERDRIVE_ORIGIN_HOST` | Origin host of that Hyperdrive; set with its ID and user |
20
+ | `VOID_PLATFORM_HYPERDRIVE_ORIGIN_USER` | Runtime database user of that Hyperdrive; set with its ID and host |
18
21
  | `VOID_PLATFORM_R2_ACCESS_KEY_ID` | R2 Access Key ID |
19
22
  | `VOID_PLATFORM_R2_SECRET_ACCESS_KEY` | R2 Secret Access Key |
20
23
  | `VOID_PLATFORM_JWT_SECRET` | Original JWT signing secret |
@@ -41,15 +41,25 @@ Use the following permissions for the core platform. Cloudflare may label write
41
41
 
42
42
  The management token also needs **Zone Edit** with authority to create zones if you ask Void to create the zone. If it already exists, use the selected zone with Zone Read and DNS Edit. Nested application domains additionally need **SSL and Certificates: Read** on the management token. A custom runtime that enables custom project domains needs **SSL and Certificates: Edit** on the runtime token; the core runtime does not enable that feature.
43
43
 
44
- Enabling email lets administrators [register email domains for projects](/guide/platform/administration/email#registering-email-domains-for-projects) and lets projects register destination addresses through the runtime token. That needs **Email Routing Addresses: Edit** and **Email Sending: Edit** on the account, plus **Zone: Read**, **Zone Settings: Edit** and **Email Routing Rules: Edit** on the zones that will carry mail; the token link preselects them when email is enabled. Email Sending onboarding itself needs Workers Paid on the account.
44
+ Void checks access before provisioning. If it reports a missing permission, update the token's permissions for the selected account or zone and retry.
45
45
 
46
- To enable email, set `VOID_EMAIL_SENDER_DOMAIN` to the shared sender domain and `VOID_EMAIL_SHARED_ZONE_ID` to its Cloudflare zone ID when installing or upgrading. Void records both values for later upgrades and rejects attempts to replace them during an ordinary upgrade. The mail zone can differ from the application zone, but it must belong to the selected platform Cloudflare account; without an explicit mail-zone identity, shared inbound delivery stays unavailable. The dedicated email gateway is deployed in the platform account. Each customer zone uses its own ingress Worker to forward mail to that gateway.
46
+ :::
47
47
 
48
- The installer prepares the shared mail route and verifies inbound readiness before it opens platform traffic. If that setup fails, the installation remains disabled. Correct the reported Cloudflare permission, mail-zone configuration, or routing conflict, then rerun the same install or upgrade command; a fresh install resumes with `void platform install --resume --name <installation-id>`.
48
+ ## Enable Email {#enable-email}
49
49
 
50
- Void checks access before provisioning. If it reports a missing permission, update the token's permissions for the selected account or zone and retry.
50
+ Choose a shared sender domain and its Cloudflare zone ID. The mail zone may differ from the application zone, but it must belong to the platform's Cloudflare account. To enable email on an existing installation, run:
51
51
 
52
- :::
52
+ ```sh
53
+ VOID_EMAIL_SENDER_DOMAIN=mail.example.com \
54
+ VOID_EMAIL_SHARED_ZONE_ID=your-zone-id \
55
+ void platform upgrade your-installation-id
56
+ ```
57
+
58
+ Set the same two variables before `void platform install` to enable email during a new installation. Void records the pair for later upgrades; an ordinary upgrade cannot replace it.
59
+
60
+ Enabling email lets administrators [register email domains for projects](/guide/platform/administration/email#registering-email-domains-for-projects) and lets projects register destination addresses through the runtime token. That needs **Email Routing Addresses: Edit** and **Email Sending: Edit** on the account, plus **Zone: Read**, **Zone Settings: Edit** and **Email Routing Rules: Edit** on the zones that will carry mail. The runtime-token link preselects them when email is enabled. Email Sending onboarding for arbitrary recipients needs Workers Paid.
61
+
62
+ The installer deploys the email gateway, prepares the shared mail route, and verifies inbound readiness before opening platform traffic. If setup fails, correct the reported permission, mail-zone configuration, or routing conflict, then rerun the same install or upgrade command. A fresh install resumes with `void platform install --resume --name <installation-id>`.
53
63
 
54
64
  ## R2 Upload Credentials
55
65
 
@@ -84,3 +84,5 @@ Void does not require a particular database provider. Use an existing PostgreSQL
84
84
  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.
85
85
 
86
86
  Once installation claims the database, continue using that same database for resume and maintenance commands. Uninstall never deletes external PostgreSQL.
87
+
88
+ During interactive installation, choose whether Void creates Hyperdrive or uses one you manage separately. If you choose a separately managed Hyperdrive and none exists, Void gives you the required name and setup instructions, then stops so you can create it. Name it `void-<installation-name>-database`, point it at the same dedicated PostgreSQL database using a runtime user, and disable SQL result caching. See [Cloudflare's Hyperdrive setup guide](https://developers.cloudflare.com/hyperdrive/get-started/), or ask your organization's Hyperdrive administrator to create it. Rerun the installer; Void shows the matching Hyperdrive's database, host, port, and runtime user for confirmation. Paste a database owner connection into the PostgreSQL URL prompt for installation and migrations. Void verifies that Hyperdrive points to the same database and records its identity for later maintenance without taking ownership of it. For unattended installation, supply the existing Hyperdrive ID, origin host, and runtime user through the environment variables in [Install from CI](/guide/platform/installation/ci).
@@ -29,7 +29,7 @@ void platform install
29
29
 
30
30
  :::
31
31
 
32
- Use the same name, account, and domain as the preview, then confirm the installation plan. Void saves a local setup draft and opens the runtime-token page when that token is missing. Paste the token into the masked prompt. As you continue, it opens GitHub and R2 at their respective steps. Each page has a short checklist and a clickable fallback link in the terminal. Values already supplied through the environment or saved setup are reused without opening their pages again.
32
+ After `--plan`, run the install command printed at the end of the preview. Void recalculates the plan and asks you to confirm it; choose the same login methods again if you selected them interactively. Void then saves a local setup draft and opens the runtime-token page when that token is missing. Paste the token into the masked prompt. As you continue, it opens GitHub and R2 at their respective steps. Each page has a short checklist and a clickable fallback link in the terminal. Values already supplied through the environment or saved setup are reused without opening their pages again.
33
33
 
34
34
  ## Choose login methods
35
35
 
@@ -17,7 +17,7 @@ Before enabling remote mode, you need:
17
17
 
18
18
  ## Enabling Remote Mode
19
19
 
20
- ### In `void.json` (persistent)
20
+ ### In `void.config.ts` (persistent)
21
21
 
22
22
  ```json
23
23
  {
@@ -31,7 +31,7 @@ Before enabling remote mode, you need:
31
31
  VOID_REMOTE=1 vite dev
32
32
  ```
33
33
 
34
- `VOID_REMOTE=0` disables remote mode even if `void.json` has `"remote": true`.
34
+ `VOID_REMOTE=0` disables remote mode even if `void.config.ts` has `"remote": true`.
35
35
 
36
36
  ## Supported Bindings
37
37
 
@@ -25,7 +25,7 @@ Importing from `void/sandbox` enables the required Sandbox resources. Native Clo
25
25
 
26
26
  Most apps do not need config. The default binding is `SANDBOX`, the Durable Object class is `Sandbox`, and local development, native Cloudflare deploys, and Void Platform all use the published image matching the installed `@cloudflare/sandbox` version.
27
27
 
28
- Use `void.json` when you need a custom image or container size:
28
+ Use `void.config.ts` when you need a custom image or container size:
29
29
 
30
30
  ```json
31
31
  {
@@ -4,7 +4,7 @@ outline: deep
4
4
 
5
5
  # Static Site Generation (SSG)
6
6
 
7
- Set `output: "static"` in `void.json` to prerender all pages at build time:
7
+ Set `output: "static"` in `void.config.ts` to prerender all pages at build time:
8
8
 
9
9
  ```json
10
10
  {
@@ -196,6 +196,6 @@ Each socket connects to one route instance. Applications that need to switch roo
196
196
 
197
197
  ## Deployment
198
198
 
199
- `void deploy --platform cloudflare` persists the required binding and append-only SQLite class migration in `wrangler.jsonc`, then deploys the generated Worker directly to your account. Commit this migration history and never delete or reorder a step after deployment.
199
+ `void deploy --platform cloudflare` persists the required binding and append-only SQLite class migration in `void.lock.json`, then deploys the generated Worker directly to your account. Commit this migration history and never delete or reorder a step after deployment.
200
200
 
201
201
  `void deploy --platform void` uses the same shared migration planner in the hosted uploader. Existing hosted WebSocket classes created on legacy storage remain there; only genuinely new classes use SQLite.