From 7ac7a87516f642f45771320df2701c8a2957aa43 Mon Sep 17 00:00:00 2001 From: Madraka <8898640+Madraka@users.noreply.github.com> Date: Sat, 14 Nov 2026 10:14:41 +0300 Subject: [PATCH 1/2] fix(release): require workers-scripts.admin in the release manifest (#225) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Installing a release creates the site and admin Workers. Since Cloudflare's granular Worker roles, Workers Editor (workers-scripts.edit) cannot create a Worker; a grant built from the manifest's requiredScopes got a 403 on the new site Worker's asset upload. The standard manifest now names Workers Admin. - scripts/release-bundle/standard.ts: workers-scripts.edit -> .admin, with a test that keeps it so. - release-manifest fixtures follow (schema unchanged: ScopeId is a pattern). - RFC-0046 §3: the scopes rule for consumers. Manifests up to v0.9.0 keep .edit (immutable releases); a consumer treats .admin as covering .edit and asks for .admin when it builds a grant from an older manifest. Closes #225 Co-Authored-By: Claude Opus 5.5 Signed-off-by: Madraka <8898640+Madraka@users.noreply.github.com> --- docs/rfcs/0046-release-bundles.md | 8 ++++++++ packages/cli/test/release-bundle.test.ts | 6 ++++++ .../invalid/release-manifest/asset-too-large.json | 2 +- .../release-manifest/cli-site-id-too-short.json | 2 +- .../invalid/release-manifest/missing-feature-flag.json | 2 +- .../invalid/release-manifest/schema-version-2.json | 2 +- .../release-manifest/signature-name-unknown.json | 2 +- .../invalid/release-manifest/unknown-capability.json | 2 +- .../invalid/release-manifest/unknown-property.json | 2 +- .../valid/release-manifest/cloudhal-0.6.3.json | 2 +- .../valid/release-manifest/starter-bundle.json | 2 +- .../valid/release-manifest/statement-signed.json | 2 +- scripts/release-bundle/standard.ts | 10 +++++++++- 13 files changed, 33 insertions(+), 11 deletions(-) diff --git a/docs/rfcs/0046-release-bundles.md b/docs/rfcs/0046-release-bundles.md index 71266634..5e7ae4f8 100644 --- a/docs/rfcs/0046-release-bundles.md +++ b/docs/rfcs/0046-release-bundles.md @@ -103,6 +103,14 @@ Root `release-manifest.schema.json` (`$id` `https://cloudhal.dev/schemas/v1/rele It holds `version`, `channel`, `securityRelease`, `commit`, `createdAt`, `contracts`, `levels` (`schedules`, `gates`, …), `migrations` (`first`, `last`, `count`, `names`, `compatibleWithPrevious`), `durableObjects` (`bindings`, `lifecycleChange`), `runtime` (compatibility date and flags), `toolchain`, `theme`, `skills`, `workers.site` and `workers.admin` (`mainModule`, `modules[]` with `path`/`type`/`bytes`/`sha256`, `assets` with `count`/`bytes`/`files[]` with a precomputed `assetHash`, inline `headers`), `bindingTemplates`, `requiredScopes`/`optionalScopes`, `requiredCapabilities`/`optionalCapabilities`, `starter`, `features` (a declared map of booleans: the named flags are required and unknown flags are allowed, so an older deployer ignores a new flag), `planVersion` and `signing`. +Scopes (amended 2026-11-14, #225): + +- `requiredScopes` and `optionalScopes` name Cloudflare OAuth scope ids. A deployer asks for every required scope and may ask for optional ones. +- Installing a release creates the site and admin Workers. Since Cloudflare's granular Worker roles (changelog 2026-09-15), only Workers Admin (`workers-scripts.admin`) can create a Worker. Workers Editor (`workers-scripts.edit`) can only change existing ones. Bundles from the next release on therefore require `workers-scripts.admin`. +- Manifests up to and including `v0.9.0` name `workers-scripts.edit`. Releases are immutable, so these manifests keep it. +- A consumer that compares a grant with `requiredScopes` treats `workers-scripts.admin` as covering `workers-scripts.edit`. A consumer that builds a grant from an older manifest asks for `workers-scripts.admin` in its place, because a grant with only `workers-scripts.edit` cannot install a release: the asset upload for a new Worker answers 403. +- This is the only implied scope. Every other id is compared exactly. + Provenance fields: - `commit` is the release commit, which is also the commit the tag names. diff --git a/packages/cli/test/release-bundle.test.ts b/packages/cli/test/release-bundle.test.ts index 6392518a..0d4f4413 100644 --- a/packages/cli/test/release-bundle.test.ts +++ b/packages/cli/test/release-bundle.test.ts @@ -254,4 +254,10 @@ describe("standard manifest facts follow the CLI", () => { expect([...standard.ADMIN_SECRETS]).toEqual([...SECRET_KEYS]); expect([...standard.SITE_SECRETS].sort()).toEqual([...SITE_SECRET_KEYS].sort()); }); + + it("asks for Workers Admin: an installer creates Workers, which Workers Editor cannot (#225)", () => { + const scopes = standard.STANDARD_MANIFEST_FIELDS.requiredScopes as readonly string[]; + expect(scopes).toContain("workers-scripts.admin"); + expect(scopes).not.toContain("workers-scripts.edit"); + }); }); diff --git a/packages/core-contracts/fixtures/invalid/release-manifest/asset-too-large.json b/packages/core-contracts/fixtures/invalid/release-manifest/asset-too-large.json index f3a1fda1..8293f439 100644 --- a/packages/core-contracts/fixtures/invalid/release-manifest/asset-too-large.json +++ b/packages/core-contracts/fixtures/invalid/release-manifest/asset-too-large.json @@ -163,7 +163,7 @@ }, "requiredScopes": [ "account-settings.read", - "workers-scripts.edit", + "workers-scripts.admin", "d1.write", "workers-r2.write" ], diff --git a/packages/core-contracts/fixtures/invalid/release-manifest/cli-site-id-too-short.json b/packages/core-contracts/fixtures/invalid/release-manifest/cli-site-id-too-short.json index 616afda9..696763de 100644 --- a/packages/core-contracts/fixtures/invalid/release-manifest/cli-site-id-too-short.json +++ b/packages/core-contracts/fixtures/invalid/release-manifest/cli-site-id-too-short.json @@ -163,7 +163,7 @@ }, "requiredScopes": [ "account-settings.read", - "workers-scripts.edit", + "workers-scripts.admin", "d1.write", "workers-r2.write" ], diff --git a/packages/core-contracts/fixtures/invalid/release-manifest/missing-feature-flag.json b/packages/core-contracts/fixtures/invalid/release-manifest/missing-feature-flag.json index 8fb427e6..f16a19f8 100644 --- a/packages/core-contracts/fixtures/invalid/release-manifest/missing-feature-flag.json +++ b/packages/core-contracts/fixtures/invalid/release-manifest/missing-feature-flag.json @@ -163,7 +163,7 @@ }, "requiredScopes": [ "account-settings.read", - "workers-scripts.edit", + "workers-scripts.admin", "d1.write", "workers-r2.write" ], diff --git a/packages/core-contracts/fixtures/invalid/release-manifest/schema-version-2.json b/packages/core-contracts/fixtures/invalid/release-manifest/schema-version-2.json index d0b22d2c..62548480 100644 --- a/packages/core-contracts/fixtures/invalid/release-manifest/schema-version-2.json +++ b/packages/core-contracts/fixtures/invalid/release-manifest/schema-version-2.json @@ -163,7 +163,7 @@ }, "requiredScopes": [ "account-settings.read", - "workers-scripts.edit", + "workers-scripts.admin", "d1.write", "workers-r2.write" ], diff --git a/packages/core-contracts/fixtures/invalid/release-manifest/signature-name-unknown.json b/packages/core-contracts/fixtures/invalid/release-manifest/signature-name-unknown.json index a50ae7dd..0494f97e 100644 --- a/packages/core-contracts/fixtures/invalid/release-manifest/signature-name-unknown.json +++ b/packages/core-contracts/fixtures/invalid/release-manifest/signature-name-unknown.json @@ -163,7 +163,7 @@ }, "requiredScopes": [ "account-settings.read", - "workers-scripts.edit", + "workers-scripts.admin", "d1.write", "workers-r2.write" ], diff --git a/packages/core-contracts/fixtures/invalid/release-manifest/unknown-capability.json b/packages/core-contracts/fixtures/invalid/release-manifest/unknown-capability.json index 1b9f184e..74ab0caa 100644 --- a/packages/core-contracts/fixtures/invalid/release-manifest/unknown-capability.json +++ b/packages/core-contracts/fixtures/invalid/release-manifest/unknown-capability.json @@ -163,7 +163,7 @@ }, "requiredScopes": [ "account-settings.read", - "workers-scripts.edit", + "workers-scripts.admin", "d1.write", "workers-r2.write" ], diff --git a/packages/core-contracts/fixtures/invalid/release-manifest/unknown-property.json b/packages/core-contracts/fixtures/invalid/release-manifest/unknown-property.json index 72c4ea41..79542b97 100644 --- a/packages/core-contracts/fixtures/invalid/release-manifest/unknown-property.json +++ b/packages/core-contracts/fixtures/invalid/release-manifest/unknown-property.json @@ -163,7 +163,7 @@ }, "requiredScopes": [ "account-settings.read", - "workers-scripts.edit", + "workers-scripts.admin", "d1.write", "workers-r2.write" ], diff --git a/packages/core-contracts/fixtures/valid/release-manifest/cloudhal-0.6.3.json b/packages/core-contracts/fixtures/valid/release-manifest/cloudhal-0.6.3.json index ab338a99..903b19f1 100644 --- a/packages/core-contracts/fixtures/valid/release-manifest/cloudhal-0.6.3.json +++ b/packages/core-contracts/fixtures/valid/release-manifest/cloudhal-0.6.3.json @@ -161,7 +161,7 @@ }, "requiredScopes": [ "account-settings.read", - "workers-scripts.edit", + "workers-scripts.admin", "d1.write", "workers-r2.write" ], diff --git a/packages/core-contracts/fixtures/valid/release-manifest/starter-bundle.json b/packages/core-contracts/fixtures/valid/release-manifest/starter-bundle.json index 0e1613a6..69135f3d 100644 --- a/packages/core-contracts/fixtures/valid/release-manifest/starter-bundle.json +++ b/packages/core-contracts/fixtures/valid/release-manifest/starter-bundle.json @@ -161,7 +161,7 @@ }, "requiredScopes": [ "account-settings.read", - "workers-scripts.edit", + "workers-scripts.admin", "d1.write", "workers-r2.write" ], diff --git a/packages/core-contracts/fixtures/valid/release-manifest/statement-signed.json b/packages/core-contracts/fixtures/valid/release-manifest/statement-signed.json index 74f8ef67..2515922d 100644 --- a/packages/core-contracts/fixtures/valid/release-manifest/statement-signed.json +++ b/packages/core-contracts/fixtures/valid/release-manifest/statement-signed.json @@ -161,7 +161,7 @@ }, "requiredScopes": [ "account-settings.read", - "workers-scripts.edit", + "workers-scripts.admin", "d1.write", "workers-r2.write" ], diff --git a/scripts/release-bundle/standard.ts b/scripts/release-bundle/standard.ts index ba1a41b8..7438a38b 100644 --- a/scripts/release-bundle/standard.ts +++ b/scripts/release-bundle/standard.ts @@ -91,7 +91,15 @@ export const STANDARD_MANIFEST_FIELDS: Readonly> = { cron: ADMIN_CRON, }, }, - requiredScopes: ["account-settings.read", "workers-scripts.edit", "d1.write", "workers-r2.write"], + // Workers Admin, not Workers Editor (`workers-scripts.edit`): installing creates the site and + // admin Workers, and an Editor cannot create Workers (#225). Consumers treat `.admin` as covering + // the `.edit` that manifests up to v0.9.0 name (RFC-0046 §2). + requiredScopes: [ + "account-settings.read", + "workers-scripts.admin", + "d1.write", + "workers-r2.write", + ], optionalScopes: ["account-analytics.read", "workers-routes.write", "dns.write"], requiredCapabilities: ["account", "workers", "d1", "r2"], optionalCapabilities: ["analytics", "domains"], From 70f973a7551ca5cf842865a59129aecfa1512254 Mon Sep 17 00:00:00 2001 From: Madraka <8898640+Madraka@users.noreply.github.com> Date: Sat, 14 Nov 2026 10:45:40 +0300 Subject: [PATCH 2/2] =?UTF-8?q?docs(release):=20cite=20RFC-0046=20=C2=A73?= =?UTF-8?q?=20for=20the=20scopes=20rule=20(PR=20#229=20review)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 Signed-off-by: Madraka <8898640+Madraka@users.noreply.github.com> --- scripts/release-bundle/standard.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/release-bundle/standard.ts b/scripts/release-bundle/standard.ts index 7438a38b..a3bb1c71 100644 --- a/scripts/release-bundle/standard.ts +++ b/scripts/release-bundle/standard.ts @@ -93,7 +93,7 @@ export const STANDARD_MANIFEST_FIELDS: Readonly> = { }, // Workers Admin, not Workers Editor (`workers-scripts.edit`): installing creates the site and // admin Workers, and an Editor cannot create Workers (#225). Consumers treat `.admin` as covering - // the `.edit` that manifests up to v0.9.0 name (RFC-0046 §2). + // the `.edit` that manifests up to v0.9.0 name (RFC-0046 §3). requiredScopes: [ "account-settings.read", "workers-scripts.admin",