void 0.21.0 → 0.21.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -2
- package/dist/{auth-cmd-MkBG2u1f.mjs → account-cmd-D8OC-U7y.mjs} +8 -8
- package/dist/{auth-link-Devmv96E.mjs → auth-link-BYFeMJAp.mjs} +3 -3
- package/dist/auth-router-OHZNmrb_.mjs +74 -0
- package/dist/{build-cmd-DsBGwtfs.mjs → build-cmd-Bft7EY51.mjs} +2 -2
- package/dist/{cache-BMw8eMyF.mjs → cache-CKV1Gz2w.mjs} +2 -2
- package/dist/{cancel-deploy-B3_s9NIN.mjs → cancel-deploy-BAKmkX4f.mjs} +2 -2
- package/dist/cli/cf-compat.mjs +13 -3
- package/dist/cli/cli.mjs +112 -60
- package/dist/{connect-BvC9kolB.mjs → connect-DLx1pRYk.mjs} +3 -3
- package/dist/{create-project-C9lQhRzj.mjs → create-project-BZBl_gQ0.mjs} +1 -1
- package/dist/{create-project-CI-17RVJ.mjs → create-project-JV9-6ICF.mjs} +1 -1
- package/dist/{db-R7IQgOv5.mjs → db-B98_UV4H.mjs} +8 -8
- package/dist/{delete-CghI_NXn.mjs → delete-BEEyqlwd.mjs} +2 -2
- package/dist/{deploy-H968Lo4T.mjs → deploy-Bsymb0_c.mjs} +85 -48
- package/dist/{deploy-CYPymbtG.mjs → deploy-CsjbRNyv.mjs} +1 -1
- package/dist/{domain-CITt8lC-.mjs → domain-Av2AkLQP.mjs} +2 -2
- package/dist/{email-_8VmyX7V.mjs → email-CUqa6TtH.mjs} +3 -3
- package/dist/{env-DV4r3nHz.mjs → env-D4RIGEzz.mjs} +2 -2
- package/dist/gen-B87rlalt.mjs +2 -0
- package/dist/{gen-CFOEEc-t.mjs → gen-pg-Ojg8Y.mjs} +1 -1
- package/dist/{github-cmd-jYjha2LO.mjs → github-cmd-BEK4VprE.mjs} +4 -4
- package/dist/{help-C46mnlUS.mjs → help-iQOt7WHk.mjs} +40 -9
- package/dist/help-r_vrpH_d.mjs +2 -0
- package/dist/index.mjs +5 -5
- package/dist/{init-CSXmvxKu.mjs → init-D5niUPAz.mjs} +5 -5
- package/dist/{link-DxMOALXk.mjs → link-3lGrfXzd.mjs} +3 -3
- package/dist/{list-0wQs0oYg.mjs → list-CVjGr_C6.mjs} +2 -2
- package/dist/{login-CKk5NX4d.mjs → login-BgQJzxAl.mjs} +3 -3
- package/dist/login-Bz0zEPoy.mjs +2 -0
- package/dist/{logs-hxWBSoej.mjs → logs-DbzD-QDf.mjs} +2 -2
- package/dist/{migrate-DcW8F2W8.mjs → migrate-BV8qHiCb.mjs} +1 -1
- package/dist/migrate-BXY3B3m7.mjs +2 -0
- package/dist/{operator-cmd-BEUYhaHB.mjs → operator-cmd-CR6Lxzt0.mjs} +3 -3
- package/dist/{output-urU86XeT.mjs → output-CCH48AMM.mjs} +4 -4
- package/dist/{platform-auth-config-Df6yVw-e.mjs → platform-auth-config-De7BqRJm.mjs} +2 -2
- package/dist/{platform-auth-protection-Jb0yftay.mjs → platform-auth-protection-Dcat-6zu.mjs} +2 -2
- package/dist/{platform-auth-recovery-DmRyIfW0.mjs → platform-auth-recovery-C9gsM640.mjs} +2 -2
- package/dist/{platform-cmd-C9Vt7m-d.mjs → platform-cmd-Cl9Uhzru.mjs} +1 -1
- package/dist/{platform-cmd-DRxCOTwy.mjs → platform-cmd-CudokNIg.mjs} +4 -4
- package/dist/{platform-domain-IjiXgrXJ.mjs → platform-domain-Ckpd2kXw.mjs} +2 -2
- package/dist/{platform-lifecycle-BquAaU-5.mjs → platform-lifecycle-DVzM2T4j.mjs} +8 -8
- package/dist/{platform-lifecycle-CuJIZNvA.mjs → platform-lifecycle-DlpiGcjr.mjs} +1 -1
- package/dist/{platform-management-Cgyed0WY.mjs → platform-management-44Bxd3YC.mjs} +1 -1
- package/dist/{platform-management-CCQKGsq4.mjs → platform-management-ibuS1as0.mjs} +2 -2
- package/dist/{platform-recovery-DS8ih1SW.mjs → platform-recovery-D_KmvdSk.mjs} +2 -2
- package/dist/{prepare-CaUxODOU.mjs → prepare-_T-Haxrr.mjs} +1 -1
- package/dist/{project-cmd-2lN--YAX.mjs → project-cmd-BuF9doku.mjs} +14 -14
- package/dist/{project-team-Dapn9HZn.mjs → project-team-B1Tia8vd.mjs} +3 -3
- package/dist/{project-token-C90v9SJK.mjs → project-token-Bwxjf4y6.mjs} +1 -1
- package/dist/{requests-CzX46I0i.mjs → requests-CphRF98D.mjs} +2 -2
- package/dist/{rollback-BEeyc73H.mjs → rollback-BYHJV2yD.mjs} +2 -2
- package/dist/{secret-wnTel5Yw.mjs → secret-Rt9c4Ttw.mjs} +2 -2
- package/dist/{subcommand-prompt-Gj3VzLIh.mjs → subcommand-prompt-BuGYkAkC.mjs} +1 -1
- package/package.json +7 -7
- package/skills/migrate-vite-cloudflare-to-void/SKILL.md +1 -1
- package/skills/void/SKILL.md +4 -2
- package/skills/void/docs/guide/deployment.md +6 -13
- package/skills/void/docs/guide/email.md +53 -59
- package/skills/void/docs/guide/index.md +15 -41
- package/skills/void/docs/guide/platform/administration/access.md +1 -1
- package/skills/void/docs/guide/project-collaboration.md +1 -1
- package/skills/void/docs/guide/quickstart.md +11 -78
- package/skills/void/docs/index.md +17 -17
- package/skills/void/docs/integrations/cloudflare.md +16 -23
- package/skills/void/docs/reference/cli.md +38 -17
- package/skills/void/docs/reference/config.md +7 -7
- package/dist/gen-BBiIZw6g.mjs +0 -2
- package/dist/help-CS_nAsWu.mjs +0 -2
- package/dist/login-Ch9cgWRF.mjs +0 -2
- package/dist/migrate-CLty4mZR.mjs +0 -2
|
@@ -4,15 +4,13 @@ outline: deep
|
|
|
4
4
|
|
|
5
5
|
# Deployment
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
`void deploy` builds your app, provisions its resources, applies migrations, and deploys it to your own Cloudflare account or a Void platform run by your team.
|
|
8
8
|
|
|
9
|
-
Choose Cloudflare
|
|
9
|
+
Choose Cloudflare for your own account or Void for a shared team platform. You can [install a Void platform](./self-hosted-platform.md) in your team's Cloudflare account.
|
|
10
10
|
|
|
11
|
-
##
|
|
11
|
+
## Deployment Targets
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
Void platform provides that deployment service to a team using the operator's
|
|
15
|
-
account. Both use the same application APIs, with the following differences:
|
|
13
|
+
Both targets use the same application APIs. Their deployment features differ:
|
|
16
14
|
|
|
17
15
|
| Feature | Direct Cloudflare | Core self-hosted platform |
|
|
18
16
|
| ------------------------------------------------ | -------------------------------- | --------------------------------------------- |
|
|
@@ -28,14 +26,9 @@ account. Both use the same application APIs, with the following differences:
|
|
|
28
26
|
| Generated GitHub deployment workflow | Supported | Use your CI with a scoped developer token |
|
|
29
27
|
| User dashboard and managed GitHub builds | Not required | Not included in a standard installation |
|
|
30
28
|
|
|
31
|
-
|
|
32
|
-
quotas. Installing a team platform requires Workers for Platforms and the
|
|
33
|
-
[documented infrastructure](./platform/installation/domains.md#cloudflare-footprint).
|
|
34
|
-
Application rollback never reverses database migrations.
|
|
29
|
+
Native apps can run on Workers Free within its quotas. A team platform requires Workers for Platforms and the [documented infrastructure](./platform/installation/domains.md#cloudflare-footprint). Rollback does not reverse database migrations.
|
|
35
30
|
|
|
36
|
-
|
|
37
|
-
Framework-owned Worker output uses its framework's routing and asset policy;
|
|
38
|
-
unsupported Void routing rules are rejected before direct deployment.
|
|
31
|
+
Native Void apps and static sites use Void routing rules. Framework deployments use their framework's routing and asset policies; direct deploy rejects unsupported Void rules.
|
|
39
32
|
|
|
40
33
|
Use matching CLI and framework adapter versions. For a team platform, the available features depend on its installed version and configuration. Ask your administrator if a feature is unavailable or an upgrade is required.
|
|
41
34
|
|
|
@@ -30,24 +30,18 @@ if (!result.ok) {
|
|
|
30
30
|
}
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
unconditionally throws on the second — and the second is the one a new project
|
|
35
|
-
hits first, because a recipient you have not verified yet fails per-recipient.
|
|
33
|
+
Check both failure shapes: `result.error` reports a request failure, while `result.deliveries` reports failures for individual recipients. An unverified recipient fails in `deliveries`.
|
|
36
34
|
|
|
37
35
|
## Setup
|
|
38
36
|
|
|
39
|
-
On a platform with email enabled, your
|
|
40
|
-
`void deploy`. Ask your administrator for the platform's shared mail domain. A
|
|
41
|
-
self-hosted administrator [enables email during installation or upgrade](/guide/platform/installation/credentials#runtime-token-permissions); installations without it do not offer platform email. Void Cloud uses `mail.void.cloud`.
|
|
37
|
+
On a platform with email enabled, deploy without additional email configuration. Ask your administrator for its shared mail domain. Administrators [enable email during installation or upgrade](/guide/platform/installation/credentials#runtime-token-permissions); Void Cloud uses `mail.void.cloud`.
|
|
42
38
|
|
|
43
39
|
Each project on an email-enabled platform has:
|
|
44
40
|
|
|
45
|
-
- **
|
|
46
|
-
- **
|
|
47
|
-
platform `send_email` binding. Your worker never gets one, so there is nothing to configure.
|
|
48
|
-
- **Your own address as a recipient** — the email on your Void account is registered as a recipient when the project is created. It is verified at once when Cloudflare already holds it verified for the platform (you clicked its link for an earlier project of yours); otherwise Cloudflare mails it a verification link, and until you click that link and run `void email destinations` — the listing is what records the click — a send to yourself comes back `ok: false` with a per-recipient `UNVERIFIED_DESTINATION` in `result.deliveries`.
|
|
41
|
+
- **Default sender:** `<your-slug>+noreply@<mail-domain>`. Older projects with slugs longer than 56 characters must pass `from` explicitly.
|
|
42
|
+
- **Your account email as a recipient:** it is registered when the project is created. If Cloudflare has not verified it for the platform, follow the emailed verification link and run `void email destinations` before sending to it.
|
|
49
43
|
|
|
50
|
-
|
|
44
|
+
The shared sender can send only to verified recipients. Verify your own address or [add another recipient](#adding-recipients) before sending.
|
|
51
45
|
|
|
52
46
|
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
47
|
|
|
@@ -68,9 +62,9 @@ void email destinations
|
|
|
68
62
|
The project owner's account email is added automatically when the project is created on an email-enabled platform, so it skips `void email allow` — not the verification. See [Setup](#setup) for when it is verified at once and when there is a link to click.
|
|
69
63
|
|
|
70
64
|
::: warning When this is the right fit
|
|
71
|
-
The shared sender
|
|
65
|
+
The shared sender suits team alerts, project notifications, and replies to inbound mail.
|
|
72
66
|
|
|
73
|
-
For
|
|
67
|
+
For mail to arbitrary users, [register your own domain](#your-own-domain-on-the-platform), use [Workers Paid on your own Cloudflare account](#your-own-cloudflare-account), or call another email provider from your handler.
|
|
74
68
|
:::
|
|
75
69
|
|
|
76
70
|
## Your own domain on the platform
|
|
@@ -120,7 +114,7 @@ On an administrator-managed platform, `add` prints the administrator command for
|
|
|
120
114
|
| `attachments` | `Attachment[]` | See [Attachments](#attachments). |
|
|
121
115
|
| `idempotencyKey` | `string` | Optional on the Void Platform: 1–128 printable, non-space ASCII characters. Reuse for retries of the same send. |
|
|
122
116
|
|
|
123
|
-
|
|
117
|
+
You can send to at most 50 recipients across `to`, `cc`, and `bcc`. Addresses must have an ASCII local part of at most 64 characters and a dotted domain; the whole address can be at most 254 characters. Quoted local parts, `user@localhost`, and malformed dots are rejected. Use punycode for internationalized domains. Void returns `INVALID_TO` for an invalid `to`, `INVALID_FROM` for `from`, and `MIME_ERROR` for `cc`, `bcc`, `replyTo`, or invalid custom header names.
|
|
124
118
|
|
|
125
119
|
`Address` accepts either a string (`"hello@acme.dev"` or `"Name <hello@acme.dev>"`) or an object (`{ email, name? }`). Display names with non-ASCII characters are RFC 2047 encoded automatically.
|
|
126
120
|
|
|
@@ -164,7 +158,7 @@ await sendEmail({
|
|
|
164
158
|
});
|
|
165
159
|
```
|
|
166
160
|
|
|
167
|
-
`contentType`
|
|
161
|
+
Void infers `contentType` from the filename if you omit it. An explicit value must be a valid media type. `contentId` names the image referenced by `cid:<id>` in HTML; use an ID such as `logo` or `logo@acme.dev`. The encoded message is limited to 5 MiB and custom headers to 16 KiB. Invalid attachment fields or oversized messages return `MIME_ERROR`.
|
|
168
162
|
|
|
169
163
|
## Result and errors
|
|
170
164
|
|
|
@@ -207,7 +201,7 @@ For 30 days, repeating a key with the same payload returns its recorded outcome
|
|
|
207
201
|
| `OUTCOME_UNKNOWN` | The send may have reached the provider; do not blindly resend. |
|
|
208
202
|
| `UPSTREAM_ERROR` | The provider or platform refused the request. |
|
|
209
203
|
|
|
210
|
-
The default platform allowance is 200 recipient submissions per UTC
|
|
204
|
+
The default platform allowance is 200 recipient submissions per UTC month and 10 per rolling minute. Reserved and started submissions count toward the limits; a started attempt stays charged even if it fails or its outcome is unknown. Administrators can change these limits.
|
|
211
205
|
|
|
212
206
|
`void email usage` shows monthly recipient attempts, inbound receipts, and the remaining allowance. `void email logs --limit 50` shows up to 100 recent metadata entries retained for 30 days: operation, recipient, direction, state, provider reference, and error code. It does not store subjects, bodies, or attachments. Receiving a message and sending a reply are separate events.
|
|
213
207
|
|
|
@@ -233,13 +227,11 @@ curl -X DELETE http://localhost:5173/__void/inbox \
|
|
|
233
227
|
-H "x-void-dev-trigger: <printed-token>"
|
|
234
228
|
```
|
|
235
229
|
|
|
236
|
-
The
|
|
230
|
+
The inbox keeps the most recent 100 messages through HMR and clears on a full server restart.
|
|
237
231
|
|
|
238
|
-
|
|
232
|
+
The dev inbox works in native Void apps, TanStack Start, and React Router. It is unavailable in SvelteKit, Nuxt, Analog, and Astro; sends from those apps return `BINDING_MISSING`. Use `createEmailTestHarness` from `void/email/testing` to capture sends in tests. If a configured inbox cannot be reached, `sendEmail` returns `UPSTREAM_ERROR` without sending.
|
|
239
233
|
|
|
240
|
-
|
|
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 `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.
|
|
234
|
+
`sendInDev: true` skips the inbox for one call. It returns `BINDING_MISSING` in `void dev` because no live send transport is bound. Deploy the app to test real delivery.
|
|
243
235
|
|
|
244
236
|
```ts
|
|
245
237
|
const result = await sendEmail({
|
|
@@ -362,7 +354,7 @@ export default defineEmail(async (message, env, ctx, info) => {
|
|
|
362
354
|
|
|
363
355
|
`replyEmail` builds the reply MIME with `Subject: Re: <original>` (no double-prefix), `In-Reply-To: <Message-ID>`, and a continued `References` chain, then dispatches through the message's `reply()`. Defaults: `to = message.from`, `subject = "Re: <original>"`.
|
|
364
356
|
|
|
365
|
-
|
|
357
|
+
Void uses the inbound message's `Message-ID` and `References` when they fit in reply headers. It drops unusable values and falls back to a bare `Re:` for a subject it cannot safely reuse. Pass `subject` to set it explicitly.
|
|
366
358
|
|
|
367
359
|
On the platform, shared-domain replies default to
|
|
368
360
|
`<slug>+noreply@<mail domain>`. For mail received at a registered custom domain,
|
|
@@ -474,27 +466,27 @@ The harness uses the same precedence rules as the production dispatcher. Reserve
|
|
|
474
466
|
|
|
475
467
|
## Your own Cloudflare account
|
|
476
468
|
|
|
477
|
-
|
|
469
|
+
For direct Cloudflare deployment, set a sender address on a zone you own. Void checks the zone and asks before changing its email settings.
|
|
470
|
+
|
|
471
|
+
### Setup
|
|
478
472
|
|
|
479
|
-
|
|
473
|
+
1. Set the default sender and mail domain in `void.config.ts`:
|
|
480
474
|
|
|
481
|
-
|
|
475
|
+
```ts
|
|
476
|
+
import { defineConfig } from 'void/config';
|
|
482
477
|
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
"from": "Acme <support@mail.acme.com>"
|
|
487
|
-
}
|
|
488
|
-
}
|
|
478
|
+
export default defineConfig({
|
|
479
|
+
email: { from: 'Acme <support@mail.acme.com>' },
|
|
480
|
+
});
|
|
489
481
|
```
|
|
490
482
|
|
|
491
|
-
The host (`mail.acme.com`) must be a zone in
|
|
483
|
+
The host (`mail.acme.com`) must be a zone in your selected Cloudflare account or a subdomain of one. If you omit `email.from`, Void deploys without email and tells you to set it. It does not choose a zone for you.
|
|
492
484
|
|
|
493
|
-
2.
|
|
485
|
+
2. Run `void cloudflare login`, or set `CLOUDFLARE_API_TOKEN` with **Email Routing Edit** and **Email Sending Edit** permissions in addition to deploy permissions. If an older browser session lacks email scopes, log out and sign in again. Void names missing token permissions in its checklist. Global API Keys are not supported for email setup.
|
|
494
486
|
|
|
495
487
|
### The first deploy
|
|
496
488
|
|
|
497
|
-
Before
|
|
489
|
+
Before building, Void checks the zone, DNS, routing rules, and sending status. It shows a checklist of the changes it would make:
|
|
498
490
|
|
|
499
491
|
```
|
|
500
492
|
Email in use email/support+[ticket].ts · email/_default.ts · sendEmail() in 2 files
|
|
@@ -518,7 +510,7 @@ Before any of your project code runs, the deploy **reads** — the session, the
|
|
|
518
510
|
◆ Set up email on mail.acme.com? ● Yes / ○ No
|
|
519
511
|
```
|
|
520
512
|
|
|
521
|
-
|
|
513
|
+
Accepting the prompt applies the missing setup, builds and deploys your app, then shows its email address map:
|
|
522
514
|
|
|
523
515
|
```
|
|
524
516
|
✔ deployed acme-support
|
|
@@ -526,13 +518,13 @@ Enter (Yes is the default) applies only the `+` rows that are not ready, then th
|
|
|
526
518
|
outbound sendEmail() from support@mail.acme.com
|
|
527
519
|
```
|
|
528
520
|
|
|
529
|
-
|
|
521
|
+
Later deploys skip the prompt when setup is ready. Declining before any setup is saved deploys without email.
|
|
530
522
|
|
|
531
523
|
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
524
|
|
|
533
|
-
|
|
525
|
+
Void updates the configured addresses when handlers change and keeps the default sender in sync with `email.from`. `void email setup` records the setup; the next deploy creates routing rules and may mark them as `(rule created by this deploy)` in the address map.
|
|
534
526
|
|
|
535
|
-
|
|
527
|
+
If Email Routing is off on the apex, Void does not enable it while setting up a subdomain; doing so could replace the apex's live mail records. The checklist directs you to **Email → Settings → Subdomains** in the Cloudflare dashboard. Deploy does not add addresses until the subdomain is ready.
|
|
536
528
|
|
|
537
529
|
### Subdomain or apex
|
|
538
530
|
|
|
@@ -548,24 +540,28 @@ The apex path (`email.from` on `acme.com` itself) is allowed with the same one E
|
|
|
548
540
|
|
|
549
541
|
### What Void records in `void.lock.json`
|
|
550
542
|
|
|
543
|
+
The relevant fields appear under `resolved`:
|
|
544
|
+
|
|
551
545
|
```jsonc
|
|
552
546
|
{
|
|
553
|
-
"
|
|
554
|
-
|
|
555
|
-
|
|
547
|
+
"resolved": {
|
|
548
|
+
"send_email": [{ "name": "SEND_EMAIL" }],
|
|
549
|
+
"vars": { "__VOID_EMAIL_FROM": "Acme <support@mail.acme.com>" },
|
|
550
|
+
"addresses": ["support@mail.acme.com"],
|
|
551
|
+
},
|
|
556
552
|
}
|
|
557
553
|
```
|
|
558
554
|
|
|
559
|
-
- **`send_email`** — the binding `sendEmail()
|
|
560
|
-
- **`__VOID_EMAIL_FROM`** —
|
|
561
|
-
- **`addresses`** — derived from your `email/`
|
|
555
|
+
- **`send_email`** — the binding used by `sendEmail()`. An existing binding under another name must be renamed before Void can use it.
|
|
556
|
+
- **`__VOID_EMAIL_FROM`** — the default sender from `email.from`.
|
|
557
|
+
- **`addresses`** — addresses derived from your `email/` handlers. Deploy creates the corresponding Email Routing rules for this Worker.
|
|
562
558
|
|
|
563
|
-
|
|
559
|
+
Void derives `addresses` from your handlers on each deploy. If routing is not ready, it withholds the array and explains why. It also protects rules it does not own:
|
|
564
560
|
|
|
565
|
-
- An address
|
|
566
|
-
- If the array
|
|
561
|
+
- An address routed to another Worker or a forwarding rule is excluded and reported; Void leaves that rule alone.
|
|
562
|
+
- If the array contains addresses Void did not derive, deploy skips email setup and lists them. Remove them, or manage `addresses` yourself and leave `email.from` unset.
|
|
567
563
|
|
|
568
|
-
|
|
564
|
+
If you delete a handler, the next deploy reports its stale address and skips email setup until you set the desired `cloudflare.addresses` in `void.config.ts`. Removing a routing rule requires confirmation in a terminal and fails in CI without it. If you remove all email use, deploy keeps the existing setup and warns about remaining routes. To detach it, remove `addresses`, `send_email`, and `vars.__VOID_EMAIL_FROM` from `resolved` in `void.lock.json`, remove any authored overrides in `void.config.ts`, then delete the routing rules in Cloudflare.
|
|
569
565
|
|
|
570
566
|
How handlers become addresses, with `email.from` on `mail.acme.com` under the zone `acme.com`:
|
|
571
567
|
|
|
@@ -581,9 +577,9 @@ Inside the worker, a message reaches your handlers exactly as addressed — `sup
|
|
|
581
577
|
|
|
582
578
|
### Sending
|
|
583
579
|
|
|
584
|
-
`sendEmail()` uses the
|
|
580
|
+
`sendEmail()` uses the Worker's `SEND_EMAIL` binding. Cloudflare's limits apply, with `QUOTA_EXCEEDED` on failure. The `void email usage`, `logs`, `destinations`, `allow`, and `disallow` commands are for Void platforms.
|
|
585
581
|
|
|
586
|
-
|
|
582
|
+
Email setup covers both inbound and outbound mail for the domain, even if your app uses only one. Cloudflare meters outbound messages; `replyEmail` uses Email Routing and does not need Email Sending onboarding.
|
|
587
583
|
|
|
588
584
|
Who you can send to depends on your Workers plan. Onboarding the mail domain for Email Sending is what allows **arbitrary recipients**, and it needs **Workers Paid** — billing is dashboard-only, so Void cannot do that for you. On Workers Free the onboarding row fails and the deploy prints:
|
|
589
585
|
|
|
@@ -593,35 +589,33 @@ Workers Paid needed for arbitrary recipients. Inbound works; sendEmail() to veri
|
|
|
593
589
|
|
|
594
590
|
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
591
|
|
|
596
|
-
|
|
592
|
+
Void remembers when Email Sending onboarding was refused. Later deploys keep inbound mail and sends to verified destinations working; this state also satisfies `--require-email`. Void does not retry onboarding automatically. After upgrading to Workers Paid, run `void email setup --platform cloudflare` to enable arbitrary recipients. `void email status --platform cloudflare` shows whether sending is still limited to verified destinations.
|
|
597
593
|
|
|
598
594
|
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
595
|
|
|
600
596
|
### CI
|
|
601
597
|
|
|
602
|
-
|
|
598
|
+
In CI or when input or output is redirected, Void cannot prompt. If email is not ready, it prints the checklist and:
|
|
603
599
|
|
|
604
600
|
```
|
|
605
601
|
deploy: email on mail.acme.com is not set up, and this shell cannot ask.
|
|
606
602
|
Run `void email setup --platform cloudflare` once locally, commit void.lock.json, then redeploy — deploying without email.
|
|
607
603
|
```
|
|
608
604
|
|
|
609
|
-
then deploys
|
|
605
|
+
Void then deploys without email. Pass `--require-email` to fail instead. If setup has recorded the subdomain addresses but its MX records are not visible yet, deploy stops until DNS can be verified. Check progress with `void email status --platform cloudflare`.
|
|
610
606
|
|
|
611
|
-
|
|
607
|
+
For CI, run `void email setup --platform cloudflare` once locally and commit `void.lock.json`. Then run `void deploy --platform cloudflare --require-email` in CI. After DNS is ready, later deploys need no prompt. Workers Free can still send to verified destinations; see [Sending](#sending).
|
|
612
608
|
|
|
613
609
|
### If the subdomain step is refused
|
|
614
610
|
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
When either call is refused, or the token cannot be read, the deploy does not fail. It prints what Cloudflare answered and the one-time dashboard step:
|
|
611
|
+
If Cloudflare refuses subdomain routing or subaddressing, Void prints the response and the dashboard step to complete:
|
|
618
612
|
|
|
619
613
|
```
|
|
620
614
|
✘ routing mail.acme.com: Cloudflare answered 403 …
|
|
621
615
|
Cloudflare dashboard → your zone → Email → Settings → Subdomains → add the subdomain, then redeploy
|
|
622
616
|
```
|
|
623
617
|
|
|
624
|
-
|
|
618
|
+
Void withholds the addresses until routing is ready. Complete the dashboard step and deploy again.
|
|
625
619
|
|
|
626
620
|
### What stays manual
|
|
627
621
|
|
|
@@ -631,4 +625,4 @@ and **withholds `addresses` for that run** — no routing rule is created, so no
|
|
|
631
625
|
4. Workers Paid, if you need `sendEmail()` to arbitrary recipients.
|
|
632
626
|
5. A verification click when a handler `forward()`s to a new destination.
|
|
633
627
|
6. The Subdomains form in the dashboard, only if the subdomain step above is refused.
|
|
634
|
-
7.
|
|
628
|
+
7. Confirmation when a deploy would delete a routing rule.
|
|
@@ -4,52 +4,32 @@ outline: deep
|
|
|
4
4
|
|
|
5
5
|
# What is Void?
|
|
6
6
|
|
|
7
|
-
Void
|
|
7
|
+
Void adds server routes, typed data access, and deployment to Vite apps. Native Void apps run on Cloudflare Workers. You can also use Void with a [supported framework](../integrations/frameworks/overview).
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Start with the [Quickstart](./quickstart) to create an app, run it locally, and deploy it.
|
|
10
10
|
|
|
11
11
|
The CLI and build tools require Node.js 24.21.0 or later.
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
npm install -D vite void
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
```ts
|
|
18
|
-
import { defineConfig } from 'vite';
|
|
19
|
-
import { voidPlugin } from 'void';
|
|
20
|
-
|
|
21
|
-
export default defineConfig({
|
|
22
|
-
plugins: [voidPlugin()],
|
|
23
|
-
});
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
```sh
|
|
27
|
-
void init # choose your framework and deployment target
|
|
28
|
-
void deploy
|
|
29
|
-
```
|
|
13
|
+
## Resources from your code
|
|
30
14
|
|
|
31
|
-
|
|
15
|
+
Import `db` from `void/db`, and Void provides a local database and provisions one for deployment. The same pattern applies to KV, object storage, queues, and AI. Configure resources explicitly when you need to use existing infrastructure.
|
|
32
16
|
|
|
33
|
-
Your
|
|
17
|
+
Your Drizzle schema defines database types. Route handlers infer return types, and the [typed fetch client](./typed-fetch.md) checks calls from the frontend. Changing a field shows you which callers need to change.
|
|
34
18
|
|
|
35
|
-
|
|
19
|
+
Void connects these parts of your app:
|
|
36
20
|
|
|
37
|
-
|
|
21
|
+
- [Server routes](./server-routing.md) and [pages](./pages-routing/overview.md) for your app's backend and UI
|
|
22
|
+
- [Database](./database.md), [KV](./kv.md), and [storage](./storage.md) that work locally and in production
|
|
23
|
+
- [Standard Schema](https://standardschema.dev/) validation for runtime values and TypeScript types
|
|
24
|
+
- `void deploy` to build, apply migrations, provision resources, and deploy
|
|
38
25
|
|
|
39
|
-
|
|
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
|
-
- **Local Cloudflare development:** native Void apps run server code in `workerd`, with local database, KV, and storage.
|
|
42
|
-
- **Deploy that understands the app:** `void deploy` reads your migrations, provisions the resources you actually use, and ships the result to the edge.
|
|
26
|
+
## Deployment targets
|
|
43
27
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
Void deploys to [Cloudflare Workers](https://developers.cloudflare.com/workers/). You can use your own Cloudflare account or connect to a Void platform managed by your team. Both use the same SDK and CLI.
|
|
28
|
+
Deploy to [your own Cloudflare account](../integrations/cloudflare.md#deploy-to-your-own-cloudflare-account) or to a [Void platform](./self-hosted-platform.md) run by your team. Both use the same SDK and CLI.
|
|
47
29
|
|
|
48
30
|
[Static assets](./edge/static-assets) are cached at the edge. [Prerendering](./edge/prerendering) builds pages ahead of time, while [incremental revalidation](./edge/revalidation) caches pages rendered on demand. Database, storage, secrets, and deployment commands are available through Void.
|
|
49
31
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
## How it works
|
|
32
|
+
## How resource detection works
|
|
53
33
|
|
|
54
34
|
```
|
|
55
35
|
vite.config.ts → voidPlugin() (works with any Vite app)
|
|
@@ -58,17 +38,11 @@ import { kv } → KV namespace (auto-provisioned)
|
|
|
58
38
|
import { storage } → R2 bucket (auto-provisioned)
|
|
59
39
|
import { ai } → Workers AI inference (metered)
|
|
60
40
|
db/schema.ts → Drizzle schema (source of truth for DB types)
|
|
61
|
-
db/migrations/*.sql → Applied to
|
|
41
|
+
db/migrations/*.sql → Applied to the selected database on deploy
|
|
62
42
|
void deploy → Deploy to the saved Cloudflare or Void target
|
|
63
43
|
```
|
|
64
44
|
|
|
65
|
-
The plugin
|
|
66
|
-
|
|
67
|
-
Void also works with existing frameworks. [TanStack Start](/integrations/frameworks/tanstack-start), [React Router](/integrations/frameworks/react-router), [SvelteKit](/integrations/frameworks/sveltekit), [Nuxt](/integrations/frameworks/nuxt), and [Astro](/integrations/frameworks/astro) can all deploy with the same `voidPlugin()`.
|
|
68
|
-
|
|
69
|
-
If you are building a full-stack app without a meta-framework, Void also gives you [file-based server routing](./server-routing) with method exports, dynamic params, middleware, and validation. It also includes [pages routing](./pages-routing/overview) for server-rendered UI, SPA navigation, co-located data loading, and typed forms across React, Vue, Svelte, and Solid.
|
|
70
|
-
|
|
71
|
-
Void detects your [app type](./app-types) and chooses the appropriate build and deployment flow.
|
|
45
|
+
The Vite plugin detects supported imports and adds the corresponding bindings. Void also detects your [app type](./app-types) to choose the build and deployment flow. Use `void.config.ts` when you need to control either choice.
|
|
72
46
|
|
|
73
47
|
## Next steps
|
|
74
48
|
|
|
@@ -107,7 +107,7 @@ Cloudflare applications for deliberate cleanup. Disabling an Access login method
|
|
|
107
107
|
does not remove the gate.
|
|
108
108
|
|
|
109
109
|
Users can add another enabled login to their existing account with
|
|
110
|
-
`void
|
|
110
|
+
`void account link <connection-id>` or **Account** in the optional
|
|
111
111
|
dashboard. Sign in again first if prompted, then authenticate with the additional
|
|
112
112
|
provider and confirm the identity shown. Matching email addresses alone do not
|
|
113
113
|
link accounts.
|
|
@@ -77,7 +77,7 @@ and run `void project link`.
|
|
|
77
77
|
|
|
78
78
|
If the invitation is missing, check the platform and signed-in account. To
|
|
79
79
|
switch accounts, set `VOID_API_URL` to the invitation's platform URL, unset
|
|
80
|
-
`VOID_TOKEN` if present, then run `void
|
|
80
|
+
`VOID_TOKEN` if present, then run `void account logout` and `void account login` using
|
|
81
81
|
the invited email address. For an expired or revoked invitation, ask a project
|
|
82
82
|
administrator to invite you again.
|
|
83
83
|
|
|
@@ -4,15 +4,11 @@ outline: deep
|
|
|
4
4
|
|
|
5
5
|
# Quickstart
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
Use Node.js 24.21.0 or later. New projects pin the SDK's tested Workers
|
|
10
|
-
compatibility date, so the bundled local runtime can start them. An existing
|
|
11
|
-
compatibility date in your project is preserved.
|
|
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.
|
|
12
8
|
|
|
13
9
|
## Start in an Empty Directory
|
|
14
10
|
|
|
15
|
-
Install Void in
|
|
11
|
+
Install Void in an empty project directory:
|
|
16
12
|
|
|
17
13
|
::: code-group
|
|
18
14
|
|
|
@@ -34,17 +30,7 @@ bun add -D void
|
|
|
34
30
|
|
|
35
31
|
:::
|
|
36
32
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
With pnpm, you can also start with `pnpm create void my-app`; the scaffolder sets
|
|
40
|
-
up the required native build permissions before installing Void. If a manual
|
|
41
|
-
installation reports blocked build scripts, approve `esbuild`, `sharp`, and
|
|
42
|
-
`workerd` with `pnpm approve-builds`. Set `better-sqlite3: false` in
|
|
43
|
-
`pnpm-workspace.yaml`'s `allowBuilds`: Void uses version 13's bundled binaries,
|
|
44
|
-
so it does not need a native rebuild.
|
|
45
|
-
|
|
46
|
-
The setup install updates the pnpm lockfile to match the generated dependencies,
|
|
47
|
-
including when setup runs in CI. Later builds can use `pnpm install --frozen-lockfile`.
|
|
33
|
+
Run setup:
|
|
48
34
|
|
|
49
35
|
::: code-group
|
|
50
36
|
|
|
@@ -66,58 +52,25 @@ bunx void init
|
|
|
66
52
|
|
|
67
53
|
:::
|
|
68
54
|
|
|
69
|
-
Void asks you to choose Vite+ or
|
|
70
|
-
|
|
71
|
-
Setup also asks where you want to deploy. Choose Cloudflare to use your own account, or Void to connect to your team's platform. You can skip this and decide later.
|
|
72
|
-
|
|
73
|
-
<details>
|
|
74
|
-
<summary style="cursor:pointer">
|
|
75
|
-
💡 <b>Notes on <code>void</code> binary usage</b>
|
|
76
|
-
</summary>
|
|
77
|
-
|
|
78
|
-
The docs use `void` for brevity. Because it's installed in your project, run it through your package manager outside package scripts: `npx void`, `pnpm void`, `yarn void`, or `bunx void`.
|
|
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.
|
|
79
56
|
|
|
80
|
-
|
|
57
|
+
With pnpm, you can start with `pnpm create void my-app`. It configures native build permissions before installing Void.
|
|
81
58
|
|
|
82
|
-
|
|
83
|
-
Install `void` locally so the CLI and your app use the same version.
|
|
84
|
-
:::
|
|
59
|
+
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. Setup updates the pnpm lockfile, including in CI. Later installs can use `pnpm install --frozen-lockfile`.
|
|
85
60
|
|
|
86
|
-
|
|
61
|
+
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.
|
|
87
62
|
|
|
88
63
|
## Using with Coding Agents
|
|
89
64
|
|
|
90
|
-
`void init` detects your coding agent and
|
|
91
|
-
|
|
92
|
-
If auto-detection fails, `void init` asks you to choose from a short list (Claude, Cursor, Codex, Gemini CLI, Generic).
|
|
93
|
-
|
|
94
|
-
In agents that support it, use the `/void` skill to load the relevant guidance, then describe the app you want to build. See [Coding Agents](../integrations/agents) for setup details.
|
|
65
|
+
`void init` detects your coding agent and installs its instructions and skills. If detection fails, choose an agent when prompted. 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.
|
|
95
66
|
|
|
96
67
|
## Meta Frameworks
|
|
97
68
|
|
|
98
|
-
|
|
69
|
+
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.
|
|
99
70
|
|
|
100
71
|
## Adding to an Existing Vite App
|
|
101
72
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
```sh [npm]
|
|
105
|
-
npm install -D void
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
```sh [pnpm]
|
|
109
|
-
pnpm add -D void
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
```sh [yarn]
|
|
113
|
-
yarn add -D void
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
```sh [bun]
|
|
117
|
-
bun add -D void
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
:::
|
|
73
|
+
Install Void using the package manager command [above](#start-in-an-empty-directory).
|
|
121
74
|
|
|
122
75
|
Enable the plugin in `vite.config.ts`:
|
|
123
76
|
|
|
@@ -130,27 +83,7 @@ export default defineConfig({
|
|
|
130
83
|
});
|
|
131
84
|
```
|
|
132
85
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
::: code-group
|
|
136
|
-
|
|
137
|
-
```sh [npm]
|
|
138
|
-
npx void init
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
```sh [pnpm]
|
|
142
|
-
pnpm void init
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
```sh [yarn]
|
|
146
|
-
yarn void init
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
```sh [bun]
|
|
150
|
-
bunx void init
|
|
151
|
-
```
|
|
152
|
-
|
|
153
|
-
:::
|
|
86
|
+
Then run `void init` with your package manager to configure the remaining project files. Existing compatibility dates are preserved; new projects use Void's tested Workers compatibility date.
|
|
154
87
|
|
|
155
88
|
## Once You Have a Working App
|
|
156
89
|
|