void 0.20.0 → 0.20.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 +5 -1
- package/dist/{auth-W9WII-mN.mjs → auth-DPl6kck4.mjs} +46 -26
- package/dist/{auth-cmd-CYVhSNFy.mjs → auth-cmd-gniL2fNt.mjs} +5 -4
- package/dist/auth-link-NZdjCmSc.mjs +28 -0
- package/dist/{build-cmd-CkVD1uOh.mjs → build-cmd-sI18tX_O.mjs} +3 -3
- package/dist/{cache-QcUfR-ff.mjs → cache-IHn5MwBC.mjs} +3 -3
- package/dist/{cancel-deploy-DsvWKFTe.mjs → cancel-deploy-C5qTOdLi.mjs} +3 -3
- package/dist/cf-access-DRsQRe6k.mjs +75 -0
- package/dist/cli/cli.mjs +364 -1883
- package/dist/cli/env-schema-probe.mjs +11 -2
- package/dist/{client-CyCHWSO_.mjs → client-dHfSJvAN.mjs} +336 -58
- package/dist/{cloudflare-auth-DRkGe7-s.mjs → cloudflare-auth-6M5llVPC.mjs} +53 -6
- package/dist/{cloudflare-cmd-D3ME2GAe.mjs → cloudflare-cmd-4RPGN3KB.mjs} +2 -2
- package/dist/{cloudflare-connect-BivMefMA.mjs → cloudflare-connect-t1UU5svD.mjs} +2 -2
- package/dist/{cloudflare-operations-CPTpRW6d.mjs → cloudflare-operations-BzWnlC1_.mjs} +1 -1
- package/dist/{config-BQFq7QvD.mjs → config-uNGuFsI2.mjs} +1 -1
- package/dist/{connect-D9Yr-T5f.mjs → connect-Bfk31O_8.mjs} +6 -6
- package/dist/{create-project-DMV-csEm.mjs → create-project-Bk9Z0-Jg.mjs} +6 -5
- package/dist/{db-BxoUWL0F.mjs → db-BkRoptAt.mjs} +75 -48
- package/dist/{delete-iJOqxBz1.mjs → delete-DouASY9P.mjs} +3 -3
- package/dist/{deploy-CcDoeBIf.mjs → deploy-DTaWUS1S.mjs} +137 -128
- package/dist/{dev-inbox-DkgRWLkW.mjs → dev-inbox-P0u4tM8Y.mjs} +1 -1
- package/dist/{domain-gu_iaHmn.mjs → domain-1RhhOVrC.mjs} +4 -4
- package/dist/email-C-lGh51B.mjs +795 -0
- package/dist/{env-D4Emu-M_.mjs → env-DBKmK4vc.mjs} +1 -0
- package/dist/{env-DmU2To0C.mjs → env-DJHsPE7Z.mjs} +5 -5
- package/dist/{env-validation-ENpMy6Ez.mjs → env-validation-CF6KvTRf.mjs} +3 -1
- package/dist/{gen-BKw6qHIg.mjs → gen-B_wPnVTK.mjs} +3 -3
- package/dist/generate-RTK8_kK1.mjs +47 -0
- package/dist/{github-cmd-DJS5Ab-H.mjs → github-cmd-PW7ZnWTp.mjs} +3 -3
- package/dist/{headers-D8QfRX9Y.mjs → headers-BAHwgHdW.mjs} +1 -1
- package/dist/help-CwOX-zmI.mjs +2216 -0
- package/dist/{inbound-BJ70in1n.d.mts → inbound-CH5Mksyy.d.mts} +37 -42
- package/dist/{inbound-CNKxb3FY.mjs → inbound-aVHEUhKo.mjs} +143 -101
- package/dist/index.mjs +69 -30
- package/dist/{init-Dx5cgKqK.mjs → init-BWZ7q5Z4.mjs} +11 -11
- package/dist/{link-D_rm5sRH.mjs → link-Rmvu2Wl_.mjs} +4 -4
- package/dist/{list-M8XShti7.mjs → list-DEE2S6mY.mjs} +4 -4
- package/dist/{local-d1-BE8KBbMy.mjs → local-d1-D2I6Ox5F.mjs} +1 -1
- package/dist/{login-C8-UuLnp.mjs → login-Uvferzmm.mjs} +28 -10
- package/dist/{logs-DwFU7dMW.mjs → logs-27FenuiC.mjs} +4 -4
- package/dist/{mime-BJD7d_qL.mjs → mime-D5Nmdzf7.mjs} +23 -9
- package/dist/{node-BkaXcpAc.mjs → node-Ez5KW5rn.mjs} +3 -3
- package/dist/operator-auth-B3e08unv.mjs +52 -0
- package/dist/operator-client-LUZnlnYk.mjs +82 -0
- package/dist/{operator-cmd-DYWRbWUA.mjs → operator-cmd-CjOTmAYE.mjs} +35 -55
- package/dist/{output-tFQLLj26.mjs → output-B0cfNSx5.mjs} +316 -2
- package/dist/pages/index.mjs +2 -2
- package/dist/platform-auth-config-DrbQXXiW.mjs +368 -0
- package/dist/platform-auth-protection-Bhtvp0B_.mjs +219 -0
- package/dist/platform-auth-recovery-CeOKGVeJ.mjs +310 -0
- package/dist/{platform-cmd-BEOVv1PN.mjs → platform-cmd-BFhieCdV.mjs} +16 -6
- package/dist/{platform-domain-BszUfiS8.mjs → platform-domain-C74PULqV.mjs} +4 -4
- package/dist/{platform-lifecycle-CjSr6xf0.mjs → platform-lifecycle-BwAIgz-t.mjs} +1667 -191
- package/dist/{platform-management-W30iCaTL.mjs → platform-management-COogu_Se.mjs} +33 -7
- package/dist/{platform-recovery-pHHv4ZSg.mjs → platform-recovery-ewqLefp1.mjs} +6 -5
- package/dist/{prepare-CBetXvsN.mjs → prepare-CtDJjoOj.mjs} +2 -2
- package/dist/{prepare-BfJvFUtJ.mjs → prepare-blNRQvQl.mjs} +2 -2
- package/dist/prerender-render.d.mts +11 -0
- package/dist/prerender-render.mjs +111 -0
- package/dist/{project-cmd-DCpk1cDt.mjs → project-cmd-DmZK9Hxf.mjs} +32 -14
- package/dist/project-team-D8jOJMUJ.mjs +130 -0
- package/dist/project-token-DA34bf-C.mjs +75 -0
- package/dist/{provision-Blnstcm2.mjs → provision-CSJOjjQk.mjs} +2 -0
- package/dist/{requests-Dn8Vheh1.mjs → requests-CUExwGQQ.mjs} +3 -3
- package/dist/{rollback-CtlPXEBi.mjs → rollback-CDNGU1gr.mjs} +4 -4
- package/dist/{runner-mysql-CgRFl3s6.mjs → runner-mysql-7BPUNGmL.mjs} +1 -1
- package/dist/{runner-pg-DCkWPsWS.mjs → runner-pg-BkEza-dX.mjs} +1 -1
- package/dist/runtime/ai.mjs +3 -2
- package/dist/runtime/email/testing.d.mts +1 -1
- package/dist/runtime/email/testing.mjs +3 -3
- package/dist/runtime/email-protocol.d.mts +15 -0
- package/dist/runtime/email-protocol.mjs +70 -0
- package/dist/runtime/email.d.mts +2 -2
- package/dist/runtime/email.mjs +189 -96
- package/dist/runtime/remote/index.mjs +5 -3
- package/dist/{secret-BqTxGqki.mjs → secret-Bzzi2e9E.mjs} +5 -5
- package/dist/{skills-Q46GZMO-.mjs → skills-C0RvGjeE.mjs} +1 -1
- package/dist/sqlite-validation-BzKMWnO4.mjs +25 -0
- package/dist/{subcommand-prompt-WfySCQ7S.mjs → subcommand-prompt-Bmyn5Rlc.mjs} +1 -1
- package/dist/{validate-Bihr8WBi.mjs → validate-tBBN_dXH.mjs} +1 -0
- package/package.json +12 -7
- package/skills/void/SKILL.md +35 -4
- package/skills/void/docs/guide/deployment.md +2 -0
- package/skills/void/docs/guide/email.md +121 -98
- package/skills/void/docs/guide/platform-administration.md +214 -2
- package/skills/void/docs/guide/platform-development.md +93 -2
- package/skills/void/docs/guide/project-collaboration.md +94 -0
- package/skills/void/docs/guide/self-hosted-platform.md +184 -30
- package/skills/void/docs/reference/cli.md +294 -15
- package/dist/cf-access-AJ1ehiFR.mjs +0 -42
- package/dist/cf-access-DsSsZUPr.mjs +0 -67
- package/dist/email-r6DHJyAB.mjs +0 -262
|
@@ -29,6 +29,24 @@ void platform auth status
|
|
|
29
29
|
|
|
30
30
|
This shows your account, the session's expiry, and the features available on the platform. To end the session, run `void platform auth logout`.
|
|
31
31
|
|
|
32
|
+
If the platform is protected by Cloudflare Access, `void connect <url>` handles
|
|
33
|
+
the company sign-in before Void login. Interactive Access authentication uses a
|
|
34
|
+
locally installed `cloudflared` and saves its short-lived credential in your
|
|
35
|
+
system keychain for that platform origin. You do not need to copy browser cookies.
|
|
36
|
+
|
|
37
|
+
For CI, Access credentials do not replace Void deployment credentials. A project
|
|
38
|
+
owner creates the latter with `void project token create`; it is independently
|
|
39
|
+
revocable, expires within 90 days, and authorizes only that project's deploy
|
|
40
|
+
workflow. Human and operator tokens cannot be renewed by Access service proof.
|
|
41
|
+
|
|
42
|
+
Store Access credentials in origin-keyed `VOID_ACCESS_CREDENTIALS`. A deploy
|
|
43
|
+
that uses prerendering or remote bindings needs entries for both the exact API
|
|
44
|
+
and proxy HTTPS origins, even when both entries contain the same admitted
|
|
45
|
+
service-token pair. `VOID_ACCESS_ORIGIN` selects only one recipient and therefore
|
|
46
|
+
cannot cover both calls. Keep the JSON value in your secret manager, not in
|
|
47
|
+
application configuration. See [CI deployment setup](./self-hosted-platform.md#connect-and-deploy-an-app)
|
|
48
|
+
for the required shape.
|
|
49
|
+
|
|
32
50
|
## Choosing a Platform
|
|
33
51
|
|
|
34
52
|
If you manage more than one platform, list your connections and choose a default:
|
|
@@ -46,9 +64,64 @@ void platform user list --connection <connection-id>
|
|
|
46
64
|
|
|
47
65
|
Use an ID or URL from the connection list. Administrative commands use this selection even when you run them inside an app with a different deployment destination.
|
|
48
66
|
|
|
67
|
+
## Recovering Administrator Login
|
|
68
|
+
|
|
69
|
+
If no administrator can use the configured identity provider, the installation
|
|
70
|
+
owner can recover an existing administrator with Cloudflare management access,
|
|
71
|
+
direct database access, and the original encrypted recovery credentials:
|
|
72
|
+
|
|
73
|
+
```sh
|
|
74
|
+
void platform config auth recover company --installation <id> --file recovery.json
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
The file identifies the existing account, for example
|
|
78
|
+
`{"administratorUserId":"existing-admin-id"}`. Recovery opens a real login test
|
|
79
|
+
and asks you to confirm the exact identity before restoring access. It does not
|
|
80
|
+
create a new administrator. To use a replacement provider, include its nonsecret
|
|
81
|
+
`configuration` with a new connection ID and supply the secret through
|
|
82
|
+
`--client-secret-env <name>`. To rotate an existing provider's secret, keep its
|
|
83
|
+
connection ID and identity configuration.
|
|
84
|
+
|
|
85
|
+
For `--yes`, also pin `expectedIdentity` with the exact `issuer` and `subject` in
|
|
86
|
+
the file. A successful browser login is still required. Use `--plan` to preview
|
|
87
|
+
the affected administrator and connection before starting recovery.
|
|
88
|
+
|
|
49
89
|
## Giving People Access
|
|
50
90
|
|
|
51
|
-
|
|
91
|
+
Open **Settings** in the administrator UI, or run `void platform config auth`,
|
|
92
|
+
to add, test, enable, or disable login methods. Access protection and login
|
|
93
|
+
methods are separate settings. A provider test shows the authenticated account
|
|
94
|
+
before you explicitly link it or enable the configuration.
|
|
95
|
+
|
|
96
|
+
To change the company gate after installation, use the installing workstation
|
|
97
|
+
with its saved recovery credentials:
|
|
98
|
+
|
|
99
|
+
```sh
|
|
100
|
+
void platform config auth protection show
|
|
101
|
+
void platform config auth protection enable --installation <id>
|
|
102
|
+
void platform config auth protection disable --installation <id>
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Enabling offers application creation or connection to an existing application.
|
|
106
|
+
It checks every API, proxy, and configured dashboard origin and requires a company
|
|
107
|
+
user sign-in. Changing protection ends all current human sessions; sign in again
|
|
108
|
+
afterwards. Before removing a gate used for company signup, select another
|
|
109
|
+
verified company rule or restricted signup. Removing protection retains the
|
|
110
|
+
Cloudflare applications for deliberate cleanup. Disabling an Access login method
|
|
111
|
+
does not remove the gate.
|
|
112
|
+
|
|
113
|
+
Users can add another enabled login to their existing account with
|
|
114
|
+
`void auth link <connection-id>` or **Account** in the optional
|
|
115
|
+
dashboard. Sign in again first if prompted, then authenticate with the additional
|
|
116
|
+
provider and confirm the identity shown. Matching email addresses alone do not
|
|
117
|
+
link accounts.
|
|
118
|
+
|
|
119
|
+
For company installations, choose **Company-approved users** under **Who can
|
|
120
|
+
join?** to create accounts automatically for users accepted by your configured
|
|
121
|
+
company rules. Individual invitations are not required. Invited/allowlisted
|
|
122
|
+
signup remains available when you need to approve people individually.
|
|
123
|
+
|
|
124
|
+
With invited/allowlisted signup selected, let a teammate join with GitHub by adding their login to the allowlist:
|
|
52
125
|
|
|
53
126
|
```sh
|
|
54
127
|
void platform signup allow github teammate
|
|
@@ -65,6 +138,14 @@ void platform signup allow email '*@example.com'
|
|
|
65
138
|
|
|
66
139
|
Email patterns apply across the platform's sign-in providers. Quote a domain pattern so your shell passes the `*` to Void.
|
|
67
140
|
|
|
141
|
+
For an OIDC login without a verified email, allow the identity by its login connection and stable provider subject instead:
|
|
142
|
+
|
|
143
|
+
```sh
|
|
144
|
+
void platform signup allow identity company-sso 'Employee-42'
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Use the connection ID shown by `void platform config auth list` and the exact subject reported by your identity provider. The match is case-sensitive and does not infer an email address or link another account. Any domain or group restrictions configured for that login method still apply, and the account joins as an ordinary user. Remove the grant with `void platform signup disallow identity company-sso 'Employee-42'`.
|
|
148
|
+
|
|
68
149
|
To invite someone else by email, use:
|
|
69
150
|
|
|
70
151
|
```sh
|
|
@@ -83,7 +164,7 @@ void platform invitation list
|
|
|
83
164
|
Invitation history remains available after an involved account is removed. The stored actor ID
|
|
84
165
|
remains visible when that account's login no longer exists.
|
|
85
166
|
|
|
86
|
-
`void platform signup open` allows anyone to sign up. Use `void platform signup restrict` to require an allowlist match again.
|
|
167
|
+
`void platform signup open` allows anyone to sign up. Use `void platform signup restrict` to require an allowlist match again. Disallowing an entry affects future signup; it does not suspend an existing account.
|
|
87
168
|
|
|
88
169
|
## Managing Users and Projects
|
|
89
170
|
|
|
@@ -104,6 +185,19 @@ void platform project show <project-id>
|
|
|
104
185
|
|
|
105
186
|
Project details include resources, domains, and recent builds and deployments. The [command reference](../reference/cli.md#operator-commands) also covers suspending and restoring users, deleting projects, and removing accounts.
|
|
106
187
|
|
|
188
|
+
### Transferring Project Ownership
|
|
189
|
+
|
|
190
|
+
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:
|
|
191
|
+
|
|
192
|
+
```sh
|
|
193
|
+
void platform project owner <project-id> <new-owner-user-id> --plan
|
|
194
|
+
void platform project owner <project-id> <new-owner-user-id> --yes
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
The preview shows both owners, their plans and suspension state, active work that blocks the transfer, and the changes to routing and usage counters. Wait for or cancel any active deploy, rollback, or build before retrying.
|
|
198
|
+
|
|
199
|
+
After the transfer, the former owner becomes a project administrator. Existing project-scoped CI deploy credentials are revoked; create replacements as the new owner. The new owner's plan and limits apply immediately. Usage before the transfer remains charged to the former owner; later usage is charged to the new owner. If an apply reports partial convergence, inspect the project and operator event log before repeating it.
|
|
200
|
+
|
|
107
201
|
New users on a self-hosted installation start with the `custom` profile, which
|
|
108
202
|
does not cap application requests, AI usage, deployment frequency, or retained
|
|
109
203
|
Worker deployments. Named profiles such as `pro` apply the platform's quota and
|
|
@@ -121,6 +215,122 @@ before changing application traffic or deleting resources. If cleanup fails,
|
|
|
121
215
|
access stays revoked and the error describes the partial result. A remaining
|
|
122
216
|
administrator can inspect it and retry cleanup.
|
|
123
217
|
|
|
218
|
+
## Managing Email
|
|
219
|
+
|
|
220
|
+
These commands apply to a platform that enables email. Every project can send from and receive at `<slug>+tag@<mail domain>` on the platform's shared mail domain; the [email guide](./email.md) describes what applications do with that.
|
|
221
|
+
|
|
222
|
+
### Deciding who mail may reach
|
|
223
|
+
|
|
224
|
+
By default, mail from the shared sender reaches only the recipients each project has verified. For a team whose applications mainly mail colleagues, widen that once for the whole installation:
|
|
225
|
+
|
|
226
|
+
```sh
|
|
227
|
+
void platform email policy
|
|
228
|
+
void platform email policy-set domains --domains example.com,corp.example.net
|
|
229
|
+
void platform email policy-set any
|
|
230
|
+
void platform email policy-set verified
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
`domains` lets every project mail any address on the listed domains in addition to its verified recipients; `any` lifts the check. Cloudflare still refuses a recipient it has not verified until your mail domain is onboarded for Email Sending, which needs Workers Paid on the platform's account. Until then such sends come back as `UNVERIFIED_DESTINATION` for that recipient, whatever the policy says. Two things the policy never changes: sends from a project's own custom domain, and mail to any address on the platform's own mail domain — those stay verified-recipient-only, so no project reaches another project's inbox without its consent. A tightening applies to new send admissions as soon as it commits; previously admitted attempts may finish. The same setting is on the admin UI's **Email** page.
|
|
234
|
+
|
|
235
|
+
### Project caps
|
|
236
|
+
|
|
237
|
+
Each project has 200 recipient submissions per UTC calendar month and 10 in a rolling 60-second window by default. An attempt stays charged once it starts, including a failed or uncertain outcome. Raise or lower a project's caps by id or slug:
|
|
238
|
+
|
|
239
|
+
```sh
|
|
240
|
+
void platform email limit hr-portal --monthly 2000 --burst 30
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
The project's page in the admin UI shows the current values and clears an override.
|
|
244
|
+
|
|
245
|
+
### Recovering an interrupted send
|
|
246
|
+
|
|
247
|
+
If a sender stops after beginning a provider call, its unresolved attempt blocks
|
|
248
|
+
removal of the project or sender domain. Inspect the attempt ID and start time:
|
|
249
|
+
|
|
250
|
+
```sh
|
|
251
|
+
void platform email attempts --project hr-portal
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
First confirm that the original execution has ended through your Worker logs or
|
|
255
|
+
incident records. Once the attempt is at least 24 hours old, resolve it with an
|
|
256
|
+
audit reason that contains no recipient address or message content:
|
|
257
|
+
|
|
258
|
+
```sh
|
|
259
|
+
void platform email attempt-resolve <attempt-id> --ended --reason "Original Worker execution ended; provider outcome could not be verified" --plan
|
|
260
|
+
void platform email attempt-resolve <attempt-id> --ended --reason "Original Worker execution ended; provider outcome could not be verified"
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
This records the send as `outcome_unknown` and releases its cleanup fence. The
|
|
264
|
+
attempt remains charged and is never retried. If its 30-day delivery record has
|
|
265
|
+
expired, only its recipient-free fence is removed. Do not resolve an attempt
|
|
266
|
+
while its original execution may still be active.
|
|
267
|
+
|
|
268
|
+
Inspect a project's retained receipt and recipient outcomes with:
|
|
269
|
+
|
|
270
|
+
```sh
|
|
271
|
+
void platform email logs hr-portal --page 1 --limit 50 --json
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
Logs include operation IDs, recorded outcomes, provider references, and error codes.
|
|
275
|
+
After project deletion, use its project ID until the 30-day metadata retention
|
|
276
|
+
period expires. Message bodies, subjects, attachments, and credentials are not logged.
|
|
277
|
+
|
|
278
|
+
### Registering email domains for projects
|
|
279
|
+
|
|
280
|
+
Your users may hold no Cloudflare account. Tell the platform that administrators register email domains, so `void email domain add` prints the command to ask you for instead of opening a token-creation page:
|
|
281
|
+
|
|
282
|
+
```sh
|
|
283
|
+
void platform email settings-set --domains admin
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
Then register a domain for a project. The zone must be in the platform's Cloudflare account; the platform's own credential does the setup, and no Cloudflare credential is stored per domain:
|
|
287
|
+
|
|
288
|
+
```sh
|
|
289
|
+
void platform email domain-add mail.example.com --project hr-portal
|
|
290
|
+
void platform email domain-status mail.example.com
|
|
291
|
+
void platform email domain-rotate-secret mail.example.com
|
|
292
|
+
void platform email domains
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
Name the exact mail domain: a subdomain such as `mail.example.com` when the apex already receives mail, otherwise the apex itself. `domain-status` reports inbound, outbound, and management readiness separately. Follow any required DNS or Cloudflare dashboard step, then use `domain-sync` to reconcile. Use `domain-rotate-secret` when you need to replace the zone ingress credential explicitly. Email Sending onboarding needs Workers Paid; after upgrading, `domain-sync` re-attempts it. For a zone in another Cloudflare account, pipe an API token for that account on standard input:
|
|
296
|
+
|
|
297
|
+
```sh
|
|
298
|
+
void platform email domain-add mail.other.example --project hr-portal --token-stdin --yes < token.txt
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
Project owners see administrator-registered domains in `void email domain list` and `status`; `status` names the `void platform email` command for any step they cannot take themselves, and `sync` and `remove` point them at `domain-sync` and `domain-remove`. A domain an owner registered before you switched to `admin` stays theirs to renew, sync and remove. The runtime token needs the email permissions listed in the [self-hosting guide](./self-hosted-platform.md#runtime-token-permissions) for this to work.
|
|
302
|
+
|
|
303
|
+
A permission refusal leaves setup blocked. After correcting it, run `domain-sync`
|
|
304
|
+
to resume that same setup operation; repeat `domain-rotate-secret` to resume a
|
|
305
|
+
blocked rotation without replacing its staged secret. If project deletion leaves
|
|
306
|
+
a blocked domain cleanup, correct the permission and run `domain-remove <domain>`
|
|
307
|
+
to finish the retained cleanup. If its token has expired or been revoked, provide
|
|
308
|
+
a replacement scoped to the same account and zone:
|
|
309
|
+
|
|
310
|
+
```sh
|
|
311
|
+
void platform email domain-remove mail.other.example --token-stdin --yes < token.txt
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
This recovery is available only after project deletion has begun and no other
|
|
315
|
+
project uses the connection. For a live project, renew its token through
|
|
316
|
+
`domain-add`. A timed-out Cloudflare
|
|
317
|
+
mutation stays stopped because replay may duplicate a provider write. After
|
|
318
|
+
confirming the original execution ended, wait 24 hours, inspect the exact
|
|
319
|
+
routing, Worker, secret, catch-all, or Sending resource named by the preview,
|
|
320
|
+
and use its recovery fence:
|
|
321
|
+
|
|
322
|
+
```sh
|
|
323
|
+
void platform email operation-resolve <operation-id> --ended --outcome <applied|not-applied> --reason "Provider state verified" --plan
|
|
324
|
+
void platform email operation-resolve <operation-id> --ended --outcome <applied|not-applied> --reason "Provider state verified"
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
Run recovery with the same platform version that created the persisted intent.
|
|
328
|
+
`applied` continues without replaying the write; `not-applied` permits that exact
|
|
329
|
+
step to retry. Secret rotation keeps its staged generation, and removal resumes
|
|
330
|
+
from the unresolved cleanup step. Age alone never authorizes a retry.
|
|
331
|
+
|
|
332
|
+
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.
|
|
333
|
+
|
|
124
334
|
## Previewing Changes
|
|
125
335
|
|
|
126
336
|
Commands that change the platform show the affected objects before asking for confirmation. To inspect a change without applying it, add `--plan`:
|
|
@@ -210,3 +420,5 @@ unset VOID_OPERATOR_TOKEN
|
|
|
210
420
|
Disable shell tracing for the exchange and do not write either token to logs or plaintext files. A full API login session expires after 30 days and can be revoked sooner; renew it through the normal authenticated login flow and update the protected CI secret. A job that runs longer than one hour must repeat the exchange while its full API session is still valid. An expired operator token cannot refresh itself, and Void does not issue permanent service tokens for administrator automation.
|
|
211
421
|
|
|
212
422
|
`auth login --token-stdin` performs the same elevation and saves the one-hour operator token in the system keychain for interactive use. See [operator authentication](../reference/cli.md#operator-authentication) for the full command syntax.
|
|
423
|
+
|
|
424
|
+
An email zone has one connection and ingress Worker shared by its exact domain assignments. Removing one assignment preserves resources used by the others. The Email administration page can explicitly rotate that connection secret; ordinary synchronization does not rotate it. Setup and cleanup outcomes include operation IDs, and uncertain provider writes remain recorded until reconciled.
|
|
@@ -83,7 +83,18 @@ CF_ACCESS_APP_URL=https://platform.example.com
|
|
|
83
83
|
SITE_DOMAIN=apps.example.com
|
|
84
84
|
```
|
|
85
85
|
|
|
86
|
-
The helper refreshes an Access session before the dev server starts.
|
|
86
|
+
The helper refreshes an Access session before the dev server starts. Human
|
|
87
|
+
dashboard requests require the user's Access session as well as their Void login;
|
|
88
|
+
a service token does not represent that user. Service-token pairs are for scoped
|
|
89
|
+
machine operations. This is dashboard development configuration; Access
|
|
90
|
+
credentials are separate from platform login credentials.
|
|
91
|
+
|
|
92
|
+
For a deployed dashboard, bind its `API` service to your platform API, configure
|
|
93
|
+
`DASHBOARD_URL` on both the dashboard and API, and include that exact dashboard origin in the
|
|
94
|
+
platform's Access protection application. The dashboard passes the browser's
|
|
95
|
+
company identity to the API using that service binding. Its login page shows
|
|
96
|
+
the platform's currently enabled methods, and **Account** supports adding an
|
|
97
|
+
additional login identity.
|
|
87
98
|
|
|
88
99
|
## Testing Changes
|
|
89
100
|
|
|
@@ -144,6 +155,57 @@ void-dev platform upgrade <installation-id> \
|
|
|
144
155
|
|
|
145
156
|
Then run it without `--plan` to apply the upgrade. Source-built runtimes go through the same artifact, database, ownership, and health checks as released runtimes. The CLI preserves a disabled installation's state and records the source revision in its installation checkpoint.
|
|
146
157
|
|
|
158
|
+
## Optional GitHub Webhook Ingress for Access-Protected APIs
|
|
159
|
+
|
|
160
|
+
The core installer does not deploy the dashboard, GitHub App, build Containers,
|
|
161
|
+
or webhook ingress. If a source-built installation adds those optional services
|
|
162
|
+
and Cloudflare Access protects its API hostname, GitHub cannot deliver directly
|
|
163
|
+
to `/webhooks/github`: GitHub does not present your Access credentials. Do not add
|
|
164
|
+
an Everyone or bypass policy to the API application.
|
|
165
|
+
|
|
166
|
+
The API source package includes an optional, path-isolated Worker for this case.
|
|
167
|
+
It accepts only `POST /github`, validates GitHub's signature over the raw body,
|
|
168
|
+
and forwards one authenticated internal operation over an API service binding.
|
|
169
|
+
The API independently verifies both that internal proof and GitHub's signature
|
|
170
|
+
before running the normal webhook handler. Installations without perimeter
|
|
171
|
+
protection can continue using the API's direct `/webhooks/github` endpoint.
|
|
172
|
+
|
|
173
|
+
To deploy the optional ingress:
|
|
174
|
+
|
|
175
|
+
1. Deploy the source API/build runtime containing the internal operation and its
|
|
176
|
+
build bindings, then finish the separate GitHub App/build-service
|
|
177
|
+
configuration. A core install or upgrade alone does not add managed builds.
|
|
178
|
+
Enable the platform's managed-build capability only when that infrastructure
|
|
179
|
+
is ready.
|
|
180
|
+
2. Edit `platform/packages/api/wrangler.github-webhook-ingress.jsonc`. Give the
|
|
181
|
+
ingress a name unique to the installation and set its `API` service binding to
|
|
182
|
+
the exact installed API Worker name. Keep its public hostname separate from
|
|
183
|
+
every human/API hostname covered by Access.
|
|
184
|
+
3. Deploy it from the repository root:
|
|
185
|
+
|
|
186
|
+
```sh
|
|
187
|
+
vp run --filter @voidcloud/api deploy:github-webhook-ingress --env production
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Use `--env staging` for the staging entries in the same config.
|
|
191
|
+
|
|
192
|
+
4. In the Cloudflare dashboard, add encrypted Worker secrets. Set the GitHub
|
|
193
|
+
App's existing `GITHUB_WEBHOOK_SECRET` on both the API and ingress Workers.
|
|
194
|
+
Generate a separate high-entropy value, such as `openssl rand -base64 32`,
|
|
195
|
+
and set it as `GITHUB_WEBHOOK_INGRESS_SECRET` on both Workers. Do not reuse a
|
|
196
|
+
platform management, JWT, Access, or GitHub webhook credential for that value.
|
|
197
|
+
5. In the GitHub App settings, keep **Content type** set to `application/json`,
|
|
198
|
+
keep the same webhook secret, and change **Webhook URL** to the isolated
|
|
199
|
+
ingress URL ending in `/github`. Use GitHub's test delivery and confirm a 2xx
|
|
200
|
+
response before relying on push builds.
|
|
201
|
+
|
|
202
|
+
The ingress has no login, dashboard, project, operator, proxy, or arbitrary
|
|
203
|
+
forwarding route. It does not make the GitHub integration part of the core
|
|
204
|
+
installer, provision build executors, create a GitHub App, configure Cloudflare
|
|
205
|
+
Access, or manage either Worker's secrets. Its body limit is 25 MiB, based on
|
|
206
|
+
GitHub's [documented 25 MB webhook payload cap](https://docs.github.com/en/webhooks/webhook-events-and-payloads#payload-cap);
|
|
207
|
+
malformed or larger deliveries are rejected before event processing.
|
|
208
|
+
|
|
147
209
|
## Deploying Source Builds from CI {#source-build-ci}
|
|
148
210
|
|
|
149
211
|
Build `@void/platform` from your checkout and pass its runtime directory to `install`, `upgrade`, `repair`, `enable`, or `rollback` with `--runtime`. Custom runtimes get the same integrity, migration, health, and rollback checks as packaged releases.
|
|
@@ -169,7 +231,7 @@ node packages/void/dist/cli/cli.mjs platform upgrade "$VOID_PLATFORM_INSTALLATIO
|
|
|
169
231
|
|
|
170
232
|
Omit the installation selector only if the account has one discoverable installation. Run one deployment per installation at a time, and let it finish before starting the next. Cancelling during migrations or Worker rollout can leave an installation waiting for recovery.
|
|
171
233
|
|
|
172
|
-
Keep the management token, database URL, JWT signing secret, and complete project-encryption keyring in a protected CI environment. Upgrades inherit deployed Worker secrets. The original values are needed when recreating a missing Worker; configuration credentials are also needed when explicitly rotating them.
|
|
234
|
+
Keep the management token, database URL, JWT signing secret, email signing secret for email-enabled installations, and complete project-encryption keyring in a protected CI environment. Upgrades inherit deployed Worker secrets. The original values are needed when recreating a missing Worker; configuration credentials are also needed when explicitly rotating them.
|
|
173
235
|
|
|
174
236
|
:::
|
|
175
237
|
|
|
@@ -232,4 +294,33 @@ Publishing requires both SDK CI and platform CI, including the platform unit and
|
|
|
232
294
|
API integration suites. Release tags also run the Windows SDK checks; a passing
|
|
233
295
|
SDK-only build cannot publish a changed control plane.
|
|
234
296
|
|
|
297
|
+
### Retrying a Release
|
|
298
|
+
|
|
299
|
+
To retry a failed release without moving an existing tag, add `+retry.N` to a
|
|
300
|
+
new Git tag, with `N` starting at `1`. Keep the package versions unchanged:
|
|
301
|
+
|
|
302
|
+
| Git tag | Package version | npm channel |
|
|
303
|
+
| ------------------------ | --------------- | ----------- |
|
|
304
|
+
| `v0.21.0` | `0.21.0` | `latest` |
|
|
305
|
+
| `v0.21.0+retry.1` | `0.21.0` | `latest` |
|
|
306
|
+
| `v0.21.0-beta.1+retry.2` | `0.21.0-beta.1` | `beta` |
|
|
307
|
+
|
|
308
|
+
For example, when the packages are at `0.21.0`, commit the release fix and tag
|
|
309
|
+
that commit:
|
|
310
|
+
|
|
311
|
+
```sh
|
|
312
|
+
git tag -a 'v0.21.0+retry.1' -m 'Retry 0.21.0 publication.'
|
|
313
|
+
git push origin 'refs/tags/v0.21.0+retry.1'
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
The retry suffix belongs only in the Git tag, not in `package.json`. A `-1`
|
|
317
|
+
suffix is a distinct prerelease version, not a retry. Tag and package versions
|
|
318
|
+
are checked before dependency installation and the full CI jobs; retries still
|
|
319
|
+
run the normal release checks.
|
|
320
|
+
|
|
321
|
+
Retries publish only package versions that are still missing from npm. They
|
|
322
|
+
cannot replace an already-published version. If an earlier attempt partially
|
|
323
|
+
published the release and you changed its package contents, bump the version
|
|
324
|
+
instead of combining different contents under the same version.
|
|
325
|
+
|
|
235
326
|
For implementation history, use the design archive at `platform/meta/design-docs/README.md`. Its proposals explain earlier decisions; the source and current guides define the supported behavior.
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
---
|
|
2
|
+
outline: deep
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Project Collaboration
|
|
6
|
+
|
|
7
|
+
A project hosted on a Void platform can have one owner and additional readers, collaborators, and project administrators. Team access is a platform feature; it is not available for projects deployed directly to Cloudflare.
|
|
8
|
+
|
|
9
|
+
## Roles
|
|
10
|
+
|
|
11
|
+
| Role | Access |
|
|
12
|
+
| --------------------- | ------------------------------------------------------------------------------------------------------------------------- |
|
|
13
|
+
| Reader | View the project, usage, resources, deployments, builds, logs, and member roster. Cannot deploy. |
|
|
14
|
+
| Collaborator | Reader access plus deploy, rollback, cancellation, migrations, secrets, database configuration, and deploy prerequisites. |
|
|
15
|
+
| Project administrator | Collaborator access plus domains, email destinations, GitHub configuration, invitations, roles, and member removal. |
|
|
16
|
+
| Owner | Project administrator access plus project deletion. |
|
|
17
|
+
|
|
18
|
+
Only the owner can create, list, renew, or revoke the project's CI deploy
|
|
19
|
+
credentials. Those credentials run unattended deployments as the billing owner
|
|
20
|
+
and remain independent of a member's login session, so collaborators and
|
|
21
|
+
project administrators deploy with their own account instead of minting a
|
|
22
|
+
durable owner credential.
|
|
23
|
+
|
|
24
|
+
The owner's plan, limits, and suspension state govern the project. A member's
|
|
25
|
+
own plan does not change the project's available usage, and acting through a
|
|
26
|
+
collaborator does not bypass an owner suspension. A project administrator is
|
|
27
|
+
not an installation administrator and receives no platform-wide access.
|
|
28
|
+
|
|
29
|
+
## Invite a Registered User
|
|
30
|
+
|
|
31
|
+
An owner or project administrator can invite someone by the email address registered to their existing account on the same platform:
|
|
32
|
+
|
|
33
|
+
```sh
|
|
34
|
+
void project team invite teammate@example.com --role collaborator
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Pass `--project <slug>` to manage a project other than the linked project. An unknown email is rejected. A project invitation neither creates an account nor adds the address to a restricted-signup allowlist.
|
|
38
|
+
|
|
39
|
+
Invitations expire after seven days. If the platform cannot send invitation
|
|
40
|
+
email, the CLI prints the invitation ID and connection and acceptance commands
|
|
41
|
+
to share with the invited user. The invitation also appears when that user runs
|
|
42
|
+
`void project team pending` on the same platform.
|
|
43
|
+
|
|
44
|
+
List the roster and sent invitations with:
|
|
45
|
+
|
|
46
|
+
```sh
|
|
47
|
+
void project team list
|
|
48
|
+
void project team invitations
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Respond to an Invitation
|
|
52
|
+
|
|
53
|
+
Connect to the platform from your invitation, then see invitations addressed to
|
|
54
|
+
your account and respond using the invitation ID:
|
|
55
|
+
|
|
56
|
+
```sh
|
|
57
|
+
void connect https://api.example.com
|
|
58
|
+
void project team pending
|
|
59
|
+
void project team accept <invitation-id>
|
|
60
|
+
# or
|
|
61
|
+
void project team decline <invitation-id>
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
An invitation is bound to the registered account. Forwarding its ID does not let another user accept it.
|
|
65
|
+
|
|
66
|
+
These three commands use the active platform connection selected by
|
|
67
|
+
`void connect`, even inside a directory linked to another project or deployed
|
|
68
|
+
directly to Cloudflare. `VOID_API_URL` takes precedence when set. If you use
|
|
69
|
+
`VOID_TOKEN`, set `VOID_API_URL` to the matching platform; a token without that
|
|
70
|
+
URL selects Void Cloud. Each command shows the platform it contacts.
|
|
71
|
+
|
|
72
|
+
Accepting an invitation grants access without linking your current directory.
|
|
73
|
+
Open the invited application's directory and run `void project link` if it is
|
|
74
|
+
not linked yet. If it is already linked to another project, use a separate
|
|
75
|
+
checkout without `.void/project.json`, connect to the invited platform there,
|
|
76
|
+
and run `void project link`.
|
|
77
|
+
|
|
78
|
+
If the invitation is missing, check the platform and signed-in account. To
|
|
79
|
+
switch accounts, set `VOID_API_URL` to the invitation's platform URL, unset
|
|
80
|
+
`VOID_TOKEN` if present, then run `void auth logout` and `void auth login` using
|
|
81
|
+
the invited email address. For an expired or revoked invitation, ask a project
|
|
82
|
+
administrator to invite you again.
|
|
83
|
+
|
|
84
|
+
## Change or Remove Access
|
|
85
|
+
|
|
86
|
+
Owners and project administrators can change any non-owner member or revoke a pending invitation:
|
|
87
|
+
|
|
88
|
+
```sh
|
|
89
|
+
void project team role <user-id> reader
|
|
90
|
+
void project team remove <user-id>
|
|
91
|
+
void project team revoke <invitation-id>
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
A non-owner member can leave with `void project team leave`. The owner cannot leave; an installation administrator must [transfer ownership](./platform-administration.md#transferring-project-ownership) first.
|