@decocms/start 3.0.0 → 4.0.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.
@@ -315,7 +315,7 @@ See: `references/search.md`
315
315
  5. **`tsconfig.json` mirrors `vite.config.ts`** -- only `"~/*": ["./src/*"]` in paths
316
316
  6. **Signals don't auto-subscribe in React** -- reading `signal.value` in render creates NO subscription; use `useStore(signal.store)` from `@tanstack/react-store`
317
317
  7. **Commerce loaders need request context** -- `resolve.ts` must pass URL/path to PLP/PDP loaders
318
- 8. **`wrangler.jsonc` main must be a custom worker-entry** -- TanStack Start ignores `export default` in `server.ts`. The `main` field lives in the **central** `decocms/deco-start/deploy/wrangler-template.jsonc` (D6); site repos do not commit `wrangler.jsonc`.
318
+ 8. **`wrangler.jsonc` main must be a custom worker-entry** -- TanStack Start ignores `export default` in `server.ts`. The `main` field lives in the site's per-site `wrangler.jsonc`.
319
319
  9. **Copy components faithfully, never rewrite** -- `cp` the original, then only change mechanical things (class→className, imports). NEVER regenerate or "improve" — AI-rewritten components are the #1 source of visual regressions
320
320
  10. **Tailwind v4 logical property hazard** -- mixed `px-*` + `pl-*/pr-*` on the same element breaks the cascade
321
321
  11. **oklch CSS variables need triplets, not hex** -- `oklch(var(--x))` must store variables as oklch triplets
@@ -37,7 +37,7 @@ TanStack Start's Cloudflare adapter **completely ignores** the `export default`
37
37
 
38
38
  **Diagnosis**: Search the built `dist/server/worker-entry-*.js` bundle for your custom code (e.g., `X-Cache`, `caches.open`, `_cache/purge`). If absent, TanStack stripped it.
39
39
 
40
- **Fix**: Create a **separate** `src/worker-entry.ts` file that wraps TanStack Start's built handler. Wrangler is told to use this file via `main: "./src/worker-entry.ts"` in the **canonical wrangler template** at `decocms/deco-start/deploy/wrangler-template.jsonc` (D6) — sites do not configure this themselves.
40
+ **Fix**: Create a **separate** `src/worker-entry.ts` file that wraps TanStack Start's built handler. Wrangler is told to use this file via `main: "./src/worker-entry.ts"` in the site's `wrangler.jsonc`.
41
41
 
42
42
  ```typescript
43
43
  // src/worker-entry.ts
@@ -59,7 +59,7 @@ export default createDecoWorkerEntry(serverEntry, {
59
59
 
60
60
  The `main` field is set centrally so a future migration of the entry path
61
61
  applies to every site at once (single PR to the template). There is no
62
- per-site override file (D6.2 Pure C); if a single site truly needs a different
62
+ per-site override file; if a single site truly needs a different
63
63
  entry path, change the template (and accept that all sites get it) or add a
64
64
  substitution token like `$WORKER_ENTRY_PATH` and feed it from a per-site env.
65
65
 
@@ -141,7 +141,7 @@ After deploying a new build to Cloudflare Workers, the edge cache may still serv
141
141
  `npx wrangler secret put` manually per-site — the central workflow keeps
142
142
  GitHub and Cloudflare in sync.
143
143
  2. Call the purge endpoint: `POST /_cache/purge` with `Authorization: Bearer <token>` and body `{"paths":["/"]}`
144
- 3. The right place to automate this is the **central** `deco-start/.github/workflows/deploy.yml` (D6) so every site picks it up at once. Do not add site-local deploy.yml steps; site repos hold only ~5-line caller workflows.
144
+ 3. Currently this lives in each storefront's per-site `deploy.yml` (D6 centralization was reverted; D6.3 Workers Builds replacement is in flight).
145
145
 
146
146
 
147
147
  ## 44. Runtime Module Import Kills Lazy-Loaded Sections
@@ -212,137 +212,34 @@ Or in project `.npmrc` with an env var (for CI):
212
212
  **Tradeoff with `github:` syntax**: No semver resolution — `npm update` is meaningless. Pin to a tag for stability: `github:decocms/deco-start#v0.14.2`. Without a tag, you get HEAD of the default branch.
213
213
 
214
214
 
215
- ## 46. Central Deploy / Wrangler Config (D6.2)
216
-
217
- **Severity**: HIGH — site repos must NOT commit `wrangler.jsonc`, must NOT hold
218
- Cloudflare credentials, and must NOT have per-site deploy logic. Doing so
219
- reintroduces drift and breaks the trust model.
220
-
221
- Per [D6.2](../../../../.cursor/rules/migration-tooling-policy.mdc), all
222
- storefronts deploy via reusable workflows shipped from
223
- `decocms/deco-start/.github/workflows/{deploy,preview,sync-secrets,regen-blocks}.yml@v3`.
224
- There is no per-site registry: worker name == storefront repo basename by
225
- convention. The canonical `wrangler.jsonc` lives at
226
- `decocms/deco-start/deploy/wrangler-template.jsonc`; the build script
227
- substitutes `$WORKER_NAME` / `$WORKER_UNDERSCORE` tokens at deploy time and
228
- writes a generated `wrangler.jsonc` in the runner. Site repos gitignore the
229
- file.
230
-
231
- ### What goes in the site repo
232
-
233
- Four caller workflow stubs that mint a `decocms-deployer` GitHub App
234
- installation token and call `gh workflow run` on `decocms/deco-start@v3`:
235
-
236
- ```yaml
237
- # .github/workflows/deploy.yml
238
- name: Deploy
239
- on:
240
- push:
241
- branches: [main]
242
- permissions:
243
- contents: read
244
- jobs:
245
- trigger:
246
- runs-on: ubuntu-latest
247
- steps:
248
- - uses: actions/create-github-app-token@v1
249
- id: app-token
250
- with:
251
- app-id: ${{ secrets.DECOCMS_DEPLOYER_APP_ID }}
252
- private-key: ${{ secrets.DECOCMS_DEPLOYER_APP_PRIVATE_KEY }}
253
- owner: decocms
254
- repositories: deco-start
255
- - env:
256
- GH_TOKEN: ${{ steps.app-token.outputs.token }}
257
- run: |
258
- gh workflow run deploy.yml \
259
- --repo decocms/deco-start \
260
- --ref v3 \
261
- -f site_owner=${GITHUB_REPOSITORY%%/*} \
262
- -f site_name=${GITHUB_REPOSITORY##*/}
263
- ```
215
+ ## 46. Deploy / Wrangler Config (interim, D6.3 in flight)
264
216
 
265
- (Plus equivalent `preview.yml`, `regen-blocks.yml`, `sync-secrets.yml`
266
- see `scripts/migrate/templates/github-workflows.ts` for the canonical text.)
267
- The migration script generates these for new sites.
268
-
269
- ### App + secrets setup (one-time, per org)
270
-
271
- 1. Create a `decocms-deployer` GitHub App with permissions
272
- `Actions: Write`, `Contents: Read`, `Pull requests: Write` (the last
273
- only needed if you want preview URLs commented on PRs),
274
- `Metadata: Read`. Install it on `decocms/deco-start` and on each
275
- storefront repo (or whole-org install on `deco-sites`).
276
- 2. Store the App ID and private key as `deco-sites` org-level secrets
277
- `DECOCMS_DEPLOYER_APP_ID` and `DECOCMS_DEPLOYER_APP_PRIVATE_KEY` so every
278
- storefront workflow can mint installation tokens without per-repo setup.
279
- 3. Store `CLOUDFLARE_API_TOKEN` and `CLOUDFLARE_ACCOUNT_ID` as plain repo
280
- secrets in `decocms/deco-start` (NOT in any storefront).
281
- 4. For each storefront that has runtime secrets: create a GitHub Environment
282
- in `decocms/deco-start` named `<storefront-repo-basename>-secrets`, add
283
- the `SECRET_*` values there, and configure protection rules to grant the
284
- site team self-service access to that environment only.
285
-
286
- ### Local dev
287
-
288
- Site repos add three package.json hooks so vite picks up the generated
289
- `wrangler.jsonc`:
290
-
291
- ```jsonc
292
- "scripts": {
293
- "gen:wrangler": "deco-wrangler gen",
294
- "predev": "deco-wrangler gen",
295
- "prebuild": "deco-wrangler gen",
296
- "types": "deco-wrangler types",
297
- "deploy": "echo 'Production deploys are managed by .github/workflows/deploy.yml on push to main. For an emergency manual deploy run: npx deco-wrangler deploy'; exit 1"
298
- }
299
- ```
217
+ **Status (2026-05-07)**: D6.2's centralized App-mediated dispatch was
218
+ **reverted** in favour of Cloudflare Workers Builds owning the deploy
219
+ pipeline per-worker. The Workers Builds onboarding plan is being
220
+ designed in a follow-up PR. Until it lands, this section describes the
221
+ **interim state**: each storefront retains its own per-site inline
222
+ `deploy.yml` workflow (the original pre-D6 setup), with its own
223
+ `CLOUDFLARE_API_TOKEN` and `CLOUDFLARE_ACCOUNT_ID` repo secrets.
224
+
225
+ Site repos **do** commit a per-site `wrangler.jsonc` during the interim
226
+ period. The `deco-wrangler` CLI no longer ships from `@decocms/start`.
227
+
228
+ ### What changes when Workers Builds onboarding ships
229
+
230
+ When the D6.3 replacement lands, expect:
231
+
232
+ - Per-storefront CF Builds connection (one dashboard click per worker).
233
+ - Per-site `.github/workflows/deploy.yml` removed; CF Builds takes over
234
+ on push.
235
+ - `wrangler.jsonc` continues to live in the site repo, but a `deco-build`
236
+ CLI in `@decocms/start` regenerates the bindings (KV, R2, etc.) from a
237
+ central template at build time so customers can't add bindings to
238
+ other tenants' resources.
239
+ - `name` field in `wrangler.jsonc` is enforced by CF (verified against
240
+ `baggagio-tanstack` 2026-05-07 a malicious `name` value is ignored
241
+ and CF auto-opens a PR to fix it).
300
242
 
301
- `deco-wrangler` is a `bin` shipped from `@decocms/start` that materializes the
302
- canonical config from the central template (worker name inferred from git
303
- remote / package.json), then either exits (`gen` mode) or execs the real
304
- `wrangler` with that config in cwd.
305
-
306
- ### Trust model
307
-
308
- - **Authorization gate**: the deploy can only start if the
309
- `decocms-deployer` App is installed on the target storefront repo. The
310
- central workflow's App-token mint step fails with a clear error otherwise.
311
- - **Worker naming is convention-based and not customer-controlled.** A
312
- customer with push access to their own storefront cannot rename the worker
313
- their deploy lands on (the central workflow uses `inputs.site_name` as the
314
- worker name; modifying their stub to pass a different `site_name` would
315
- also require the App to be installed on that other repo).
316
- - **Cloudflare credentials never leave `decocms/deco-start`.** The central
317
- workflow runs in deco-start's context, so the env-var resolves natively
318
- from repo secrets.
319
- - **Force-rollback is impossible for production.** The central deploy
320
- workflow ignores any caller-supplied sha and always resolves the
321
- storefront's current default-branch HEAD itself.
322
- - **Per-site runtime secrets** (`SECRET_*`) live in deco-start environments,
323
- not storefront repos. The storefront caller stub for `sync-secrets.yml`
324
- triggers a workflow that binds to the matching env and runs
325
- `wrangler secret put`. The storefront repo holds zero secrets beyond the
326
- `decocms-deployer` App credentials.
327
-
328
- ### Common mistakes (do not do these)
329
-
330
- - **Committing `wrangler.jsonc` to a site repo.** Generated only;
331
- always gitignored. If you see it tracked, the site missed migration.
332
- - **Adding a site-local `deploy.yml` step** (e.g. cache purge after deploy).
333
- Add it to `deco-start/.github/workflows/deploy.yml` instead so every site
334
- picks it up at once.
335
- - **Adding `CLOUDFLARE_*` to a storefront repo's secrets.** They never
336
- belong there. The central workflow runs in deco-start's context.
337
- - **Adding `SECRET_*` to a storefront repo's secrets.** They live in the
338
- matching `<site_name>-secrets` environment in deco-start.
339
- - **Hard-coding `account_id` in a site's wrangler config.** It comes from
340
- `CLOUDFLARE_ACCOUNT_ID` (in CI; `wrangler login` locally) — keeping
341
- it out of JSON is the one-way protection against accidentally deploying
342
- to the wrong account.
343
- - **Trying to override the worker name** for one site. There is no
344
- per-site override mechanism in D6.2. If the worker MUST be named
345
- differently from the repo, the right answer is either to rename the
346
- repo to match (cleanest) or to do a CF-side migration (deploy a new
347
- worker with the repo-name and re-attach routes). This trade-off was
348
- taken intentionally in exchange for the registry-free architecture.
243
+ Until then, do NOT scaffold caller stubs that reference
244
+ `decocms/deco-start/.github/workflows/*.yml@vN` those workflows are
245
+ gone.
@@ -12,11 +12,8 @@
12
12
  "generate:blocks": "tsx node_modules/@decocms/start/scripts/generate-blocks.ts",
13
13
  "generate:invoke": "tsx node_modules/@decocms/start/scripts/generate-invoke.ts",
14
14
  "generate:schema": "tsx node_modules/@decocms/start/scripts/generate-schema.ts --site storefront",
15
- "gen:wrangler": "deco-wrangler gen",
16
- "predev": "deco-wrangler gen",
17
- "prebuild": "deco-wrangler gen",
18
- "types": "deco-wrangler types",
19
- "deploy": "echo 'Production deploys are managed by .github/workflows/deploy.yml on push to main. See D6.'; exit 1"
15
+ "deploy": "wrangler deploy",
16
+ "types": "wrangler types"
20
17
  },
21
18
  "dependencies": {
22
19
  "@decocms/apps": "^0.20.1",
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: Constitutional decisions for the deco-start migration-tooling effort (D1–D6, priorities, process). Always loaded.
2
+ description: Constitutional decisions for the deco-start migration-tooling effort (D1–D5 + D6.3 revert, priorities, process). Always loaded.
3
3
  alwaysApply: true
4
4
  ---
5
5
 
@@ -14,7 +14,7 @@ summary; always defer to the plan when they conflict.
14
14
  1. **Design for 100 sites, not 3.** When the surface of an abstraction is
15
15
  well-understood, ship it. Don't defer waiting for "more signal" — design
16
16
  for the projection. When the surface is *not* understood, *decide*
17
- explicitly (write a D-record like D1–D6), don't ship fast.
17
+ explicitly (write a D-record like D1–D5), don't ship fast.
18
18
  2. **Strict over flexible.** No support layers, no soft drift, no
19
19
  fork-runtime adapters. Every fork divergence becomes either a PR back to
20
20
  canonical or visible local debt.
@@ -76,51 +76,36 @@ Failure modes get documented in skills, not encoded as escape hatches.
76
76
  **Agent behavior**: when a migration goes sideways, propose deletion +
77
77
  re-run, not in-place repair. Add the failure mode to the skill.
78
78
 
79
- ### D6.2App-mediated dispatch + no per-site registry (signed off 2026-05-07; supersedes D6 + D6.1)
80
- All storefronts deploy via reusable workflows under
81
- `decocms/deco-start/.github/workflows/{deploy,preview,sync-secrets,regen-blocks}.yml@v3`.
82
- Storefront caller stubs mint a short-lived **`decocms-deployer` GitHub App**
83
- installation token (App ID + private key live as `deco-sites` org-level
84
- secrets) and call `gh workflow run deploy.yml --repo decocms/deco-start --ref v3
85
- -f site_owner=… -f site_name=…`. The central workflow runs in
86
- **`decocms/deco-start`'s context**, so `CLOUDFLARE_API_TOKEN` and
87
- `CLOUDFLARE_ACCOUNT_ID` are ordinary repo secrets in `decocms/deco-start`
88
- and never leave it.
89
-
90
- There is **no per-site registry under `deploy/sites/`**. Worker name == storefront
91
- repo basename by convention. Per-worker derived fields use `$WORKER_NAME` /
92
- `$WORKER_UNDERSCORE` substitution tokens in
93
- [`deploy/wrangler-template.jsonc`](../../deploy/wrangler-template.jsonc).
94
- The deploy authorization gate is the App being installed on the storefront
95
- repo if it isn't, the App-token mint inside the central workflow fails and
96
- the deploy never starts. **Customer repos do not commit `wrangler.jsonc`**
97
- it is generated locally by `deco-wrangler gen` (a `bin` shipped from
98
- `@decocms/start`) and gitignored.
99
-
100
- Production deploys are **force-rollback proof**: the central workflow ignores
101
- any caller-supplied `site_sha` and resolves the storefront's current default
102
- branch HEAD itself. The worst a compromised storefront can do across tenants
103
- is trigger a no-op redeploy of another storefront's current main.
104
-
105
- `SECRET_*` runtime secrets live in **`decocms/deco-start`** as well, in
106
- per-storefront GitHub Environments named `<site_name>-secrets`. `sync-secrets.yml`
107
- binds to the matching environment and runs `wrangler secret put`. Site teams
108
- get edit/approve permission on their own environment via Environment
109
- protection rules. **The storefront repo holds zero secrets** beyond the
110
- App credentials (which only grant the ability to trigger workflows on
111
- `decocms/deco-start`).
112
-
113
- `deploy/`, `scripts/deploy/`, and the central workflow files are
114
- CODEOWNERS-protected.
115
-
116
- **Agent behavior**: when adding or migrating a site, do **not** generate a
117
- per-site `wrangler.jsonc`, do **not** generate a `deploy/sites/<repo>.jsonc`
118
- file (that whole directory was removed in D6.2), and do **not** add
119
- `CLOUDFLARE_*` or `SECRET_*` to a storefront repo's secrets. The site repo
120
- gets four ~15-line caller stubs that mint a `decocms-deployer` App token and
121
- fire `workflow_dispatch` at `decocms/deco-start@v3`; the App ID + private key
122
- live as `deco-sites` org-level secrets `DECOCMS_DEPLOYER_APP_ID` and
123
- `DECOCMS_DEPLOYER_APP_PRIVATE_KEY`.
79
+ ### D6.3Revert D6/D6.1/D6.2; Cloudflare Workers Builds owns deploy (signed off 2026-05-07)
80
+ The whole D6 family — centralized GitHub Actions reusable workflows under
81
+ `decocms/deco-start/.github/workflows/{deploy,preview,sync-secrets}.yml`,
82
+ the `decocms-deployer` GitHub App, the per-storefront `<site_name>-secrets`
83
+ GitHub Environments, the central `deploy/wrangler-template.jsonc`, the
84
+ `deco-wrangler` CLI, the `deploy/sites/<repo>.jsonc` registry, and the
85
+ per-site caller stubs has been **reverted**.
86
+
87
+ Why: GitHub Free orgs do not propagate org-level secrets to private repos,
88
+ which forced the App private key to be distributed to every storefront repo
89
+ individually. That key gives the holder the ability to mint installation
90
+ tokens for any installed repo, concentrating blast radius on a credential
91
+ that had to be rotated across N storefronts. The trust model couldn't pay
92
+ for itself at our scale.
93
+
94
+ **Replacement direction (to be elaborated in a follow-up D-record once
95
+ shipped):** Cloudflare Workers Builds owns deploy/preview pipelines per-worker.
96
+ The dashboard repo<->worker connection is the source of truth verified
97
+ empirically against `baggagio-tanstack` 2026-05-07 that a malicious
98
+ `wrangler.jsonc` `name` field is ignored, the deploy lands on the connected
99
+ worker, and CF auto-opens a PR to fix the config. Per-storefront wiring is
100
+ one CF dashboard click per worker.
101
+
102
+ **Agent behavior current interim period**: while D6.3's replacement is
103
+ being designed, do **not** scaffold `decocms/deco-start@vN`-calling caller
104
+ stubs in storefronts and do **not** depend on a `deco-wrangler` CLI from
105
+ `@decocms/start` (it no longer exists). Storefronts retain their existing
106
+ per-site inline `deploy.yml` (with their own `CLOUDFLARE_*` secrets) until
107
+ Workers Builds onboarding lands. Site repos do still commit `wrangler.jsonc`
108
+ during this interim `deco-wrangler gen` is gone.
124
109
 
125
110
  ## Priority order
126
111
 
@@ -155,4 +140,3 @@ order — but only if explicitly identified as urgent.
155
140
  - When reading a section of the plan or a skill, prefer the plan or skill
156
141
  over your memory of past conversations.
157
142
  - Do not run `deco-deploy` or any deploy command from agent flows.
158
- - Do not commit `wrangler.jsonc` to a site repo. See **D6** above.
package/CODEOWNERS CHANGED
@@ -1,14 +1,6 @@
1
1
  # CODEOWNERS for decocms/deco-start
2
2
  #
3
- # `deploy/wrangler-template.jsonc` is the canonical wrangler config every
4
- # storefront inherits. A bad PR here can change every site's runtime config in
5
- # one shot. Only the platform team approves changes.
6
- #
7
- # The central reusable workflows under `.github/workflows/` are in the same
8
- # trust boundary: they decide how every site is built and deployed.
9
- deploy/ @vibe-dex
10
- .github/workflows/deploy.yml @vibe-dex
11
- .github/workflows/preview.yml @vibe-dex
12
- .github/workflows/sync-secrets.yml @vibe-dex
13
- .github/workflows/regen-blocks.yml @vibe-dex
14
- scripts/deploy/ @vibe-dex
3
+ # Workflows under `.github/workflows/` decide how this package is built,
4
+ # released, and what blocks regeneration sites can trigger. Only the
5
+ # platform team approves changes.
6
+ .github/workflows/ @vibe-dex
@@ -120,6 +120,7 @@ this plan.
120
120
  | 2026-05-07 | **D6 — Deploy / preview / secrets pipelines: centralize in `deco-start`** | At 6 sites the "1-minute copy" of `deploy.yml` / `preview.yml` / `wrangler.jsonc` had already produced unintended drift (lebiscuit missing 2 workflows, miess missing `account_id`, casaevideo's `loadtest:tail` worker name out of sync with its wrangler config). All sites now consume reusable workflows from `decocms/deco-start@v2` and a per-site registry under [`deploy/sites/<repo>.jsonc`](./deploy/) deep-merged on top of [`deploy/wrangler-template.jsonc`](./deploy/wrangler-template.jsonc) at deploy time. Customer repos hold only ~5-line caller workflows; `wrangler.jsonc` is generated and gitignored. The repo→worker binding is the trust boundary that prevents one site's commits from misrouting onto another site's worker (the central workflow ignores caller `inputs:` for identity and derives the site name from `${{ github.repository }}`). See [`deploy/README.md`](./deploy/README.md) for the contract. |
121
121
  | 2026-05-07 | **D6.1 — Cloudflare credentials never leave `deco-start`** | Same-day refinement of D6 after the first central deploy on `baggagio-tanstack` failed with `Secret CLOUDFLARE_API_TOKEN is required, but not provided while calling`. The original D6 design used `secrets: inherit` from the storefront stub and required `CLOUDFLARE_*` to live in the `deco-sites` org, which broke the principle that *the only secrets a storefront repo holds are the secrets that go into wrangler secrets, not the ones used to deploy*. First-pass refinement: the central `deploy.yml` / `preview.yml` / `sync-secrets.yml` jobs declared `environment: production` to try to make `${{ secrets.CLOUDFLARE_* }}` resolve from `decocms/deco-start`'s `production` Environment. **Found broken empirically on 2026-05-07** — the deployment registers in the *caller* repo, not the called workflow's repo, so the environment lookup uses the caller's `production` env (auto-created with no secrets). Superseded by D6.2 the same evening. |
122
122
  | 2026-05-07 | **D6.2 — App-mediated dispatch + no per-site registry (supersedes D6 + D6.1)** | After D6.1's `environment:` mechanism was empirically shown not to work cross-repo, the architecture pivoted: a `decocms-deployer` GitHub App is installed on `decocms/deco-start` (`actions:write`) and on each storefront repo (`contents:read`, optionally `pull-requests:write`). The storefront caller stub mints a short-lived App-installation token and calls `gh workflow run deploy.yml --repo decocms/deco-start --ref v3 -f site_owner=… -f site_name=…`. The central workflow runs in `decocms/deco-start`'s context, so `CLOUDFLARE_API_TOKEN` / `CLOUDFLARE_ACCOUNT_ID` are ordinary repo secrets. For runtime `SECRET_*` values, each storefront has a `<site_name>-secrets` GitHub Environment in `decocms/deco-start` (S1 design); `sync-secrets.yml` binds to that environment and pushes to `wrangler secret put`. The per-site registry under `deploy/sites/<repo>.jsonc` was dropped entirely (Pure C): worker name = repo basename by convention; the App being installed on the storefront repo is the deploy authorization gate; rare per-worker derived fields (like AE dataset name) use `$WORKER_*` substitution tokens in the template. Force-rollback is impossible for production deploys because the central workflow ignores caller-supplied `site_sha` and resolves the storefront's current default-branch HEAD itself. See [`deploy/README.md`](./deploy/README.md) for the full trust model. **Operational migrations required by Pure C:** `miess-01-tanstack` repo's worker shifts from `miess-tanstack` to `miess-01-tanstack` (CF-side cutover); `lebiscuit-tanstack` AE dataset shifts from `deco_metrics_lebiscuit` to `deco_metrics_lebiscuit_tanstack` (orphans old data). |
123
+ | 2026-05-07 | **D6.3 — Revert D6/D6.1/D6.2; deploys move to Cloudflare Workers Builds** | The whole D6 family (centralized GitHub Actions reusable workflows + `decocms-deployer` GitHub App + per-storefront GitHub Environments + central `deploy/wrangler-template.jsonc` + `deco-wrangler` CLI + per-site caller stubs) is being **reverted**. Trigger: GitHub Free orgs do not propagate org-level secrets to private repos, which forced the App private key to live as a per-storefront repo secret in every storefront — that key gives the holder the ability to mint installation tokens that can trigger workflows on `decocms/deco-start`, which in turn have the only Cloudflare credentials in the system. Per-repo distribution + rotation of that key across N customer storefronts didn't scale and concentrated blast radius on one credential. **Replacement (chosen, to be detailed in a follow-up D-record once shipped):** [Cloudflare Workers Builds](https://developers.cloudflare.com/workers/ci-cd/builds/) owns the deploy/preview pipelines per-worker. Verified empirically on `baggagio-tanstack` 2026-05-07: a malicious `wrangler.jsonc` `name` field pointing at a different worker (`americanas-tanstack`) is **ignored** by CF Builds — the deploy lands on the connected worker (`baggagio-tanstack`), CF surfaces a warning banner in the dashboard, and CF auto-opens a PR to fix the config (deco-sites/baggagio-tanstack#34). The dashboard repo<->worker connection is the source of truth; the in-repo config is treated as a secondary input. Per-storefront wiring (one CF dashboard click per worker) is acceptable at our scale; revisit when CF's [git-integration enable API](https://github.com/cloudflare/workers-sdk/issues/12058) lands. The `deco-build` CLI (regenerates `wrangler.jsonc` bindings from a central template) and runtime-secrets management remain to be designed in a separate PR. |
123
124
 
124
125
  The full text of the constitutional rule (loaded into every agent
125
126
  session for this repo) lives at
@@ -1678,19 +1679,14 @@ props. One broken section never takes the page down.
1678
1679
  `regen-blocks.yml` and its `wrangler.jsonc` lacked `account_id`,
1679
1680
  lebiscuit's preview workflow swallowed `wrangler` exit codes, and
1680
1681
  casaevideo's `loadtest:tail` referenced a worker name that didn't
1681
- match its `wrangler.jsonc`. **Resolved 2026-05-07 via D6 → D6.1 →
1682
- D6.2:** all workflows + wrangler config are centralized in
1683
- [`deco-start/.github/workflows/`](./.github/workflows/) and
1684
- [`deco-start/deploy/`](./deploy/), and storefront caller stubs use
1685
- a `decocms-deployer` GitHub App to trigger them via `workflow_dispatch`
1686
- with no Cloudflare credentials in the storefront repo. New-site
1687
- onboarding is now: install the `decocms-deployer` App on the new
1688
- storefront repo, drop the ~15-line caller workflows in, push to main.
1689
- No `deploy/sites/<repo>.jsonc` PR is needed — worker name is the
1690
- repo basename by convention. No `wrangler.jsonc` is committed to the
1691
- site repo — `deco-wrangler gen` (a `bin` shipped from `@decocms/start`)
1692
- materializes it from the central template on demand for local dev
1693
- and CI alike.
1682
+ match its `wrangler.jsonc`. **Status 2026-05-07: D6 → D6.1 → D6.2
1683
+ reverted via D6.3** in favour of Cloudflare Workers Builds owning
1684
+ the deploy/preview pipelines per-worker. The replacement is being
1685
+ designed in a follow-up; until it ships, the existing per-site
1686
+ inline `deploy.yml` workflows in each storefront remain the
1687
+ operational deploy path (i.e. the original drift problem comes back
1688
+ on the surface for now, but with full single-credential trust
1689
+ isolation per storefront no shared credentials).
1694
1690
 
1695
1691
  #### Counter-evidence the user-rule asks for
1696
1692
 
package/package.json CHANGED
@@ -1,14 +1,13 @@
1
1
  {
2
2
  "name": "@decocms/start",
3
- "version": "3.0.0",
3
+ "version": "4.0.0",
4
4
  "type": "module",
5
5
  "description": "Deco framework for TanStack Start - CMS bridge, admin protocol, hooks, schema generation",
6
6
  "main": "./src/index.ts",
7
7
  "bin": {
8
8
  "deco-migrate": "./scripts/migrate.ts",
9
9
  "deco-post-cleanup": "./scripts/migrate-post-cleanup.ts",
10
- "deco-htmx-analyze": "./scripts/htmx-analyze.ts",
11
- "deco-wrangler": "./scripts/deploy/wrangler-wrapper.mjs"
10
+ "deco-htmx-analyze": "./scripts/htmx-analyze.ts"
12
11
  },
13
12
  "exports": {
14
13
  ".": "./src/index.ts",
@@ -5,7 +5,6 @@ import { log, logPhase } from "./types";
5
5
  import { generatePackageJson } from "./templates/package-json";
6
6
  import { generateTsconfig } from "./templates/tsconfig";
7
7
  import { generateViteConfig } from "./templates/vite-config";
8
- import { generateGithubWorkflows } from "./templates/github-workflows";
9
8
  import { generateKnipConfig } from "./templates/knip-config";
10
9
  import { generateRoutes } from "./templates/routes";
11
10
  import { generateSetup } from "./templates/setup";
@@ -50,20 +49,21 @@ function writeMultiFile(ctx: MigrationContext, files: Record<string, string>) {
50
49
  export function scaffold(ctx: MigrationContext): void {
51
50
  logPhase("Scaffold");
52
51
 
53
- // Root config files. wrangler.jsonc is INTENTIONALLY not generated --
54
- // per D6.2, the canonical wrangler config lives in decocms/deco-start under
55
- // deploy/wrangler-template.jsonc; the file is materialized locally by
56
- // `deco-wrangler gen` and gitignored. Worker name = storefront repo basename
57
- // by convention; there is no per-site registry.
52
+ // Root config files. wrangler.jsonc is intentionally NOT generated by
53
+ // the migration script -- it's per-site, lives in the site repo, and
54
+ // is wired by hand on first deploy (D6.3 interim state until the
55
+ // Cloudflare Workers Builds onboarding lands).
58
56
  writeFile(ctx, "package.json", generatePackageJson(ctx));
59
57
  writeFile(ctx, "tsconfig.json", generateTsconfig());
60
58
  writeFile(ctx, "vite.config.ts", generateViteConfig(ctx));
61
59
  writeFile(ctx, "knip.config.ts", generateKnipConfig());
62
60
  writeFile(ctx, ".gitignore", generateGitignore());
63
61
 
64
- // Caller workflow stubs that delegate to decocms/deco-start's reusable
65
- // workflows. The customer repo holds no deploy logic of its own.
66
- writeMultiFile(ctx, generateGithubWorkflows());
62
+ // Deploy / preview pipelines are owned by Cloudflare Workers Builds
63
+ // configured per-worker in the CF dashboard (D6.3). The migration
64
+ // does NOT scaffold deploy/preview/sync-secrets workflows in the site
65
+ // repo; the operator wires the repo<->worker connection in the CF
66
+ // dashboard once after the first push.
67
67
  writeFile(ctx, ".prettierrc", JSON.stringify({
68
68
  semi: true,
69
69
  singleQuote: false,
@@ -216,9 +216,6 @@ dist/
216
216
  # Cloudflare Workers
217
217
  .wrangler/
218
218
  .dev.vars
219
- # Generated by \`deco-wrangler\` from @decocms/start's wrangler template.
220
- # Worker name is derived from the git remote / package.json by convention.
221
- wrangler.jsonc
222
219
 
223
220
  # TanStack Router (auto-generated)
224
221
  src/routeTree.gen.ts
@@ -13,12 +13,10 @@ const REQUIRED_FILES = [
13
13
  "package.json",
14
14
  "tsconfig.json",
15
15
  "vite.config.ts",
16
- // wrangler.jsonc is INTENTIONALLY absent -- D6.2: it's generated from
17
- // decocms/deco-start's deploy/wrangler-template.jsonc by `deco-wrangler gen`.
18
- ".github/workflows/deploy.yml",
19
- ".github/workflows/preview.yml",
16
+ // Deploy / preview / sync-secrets pipelines are owned by Cloudflare
17
+ // Workers Builds (D6.3) -- configured in the CF dashboard, not via
18
+ // GitHub workflow files in the site repo.
20
19
  ".github/workflows/regen-blocks.yml",
21
- ".github/workflows/sync-secrets.yml",
22
20
  "knip.config.ts",
23
21
  ".prettierrc",
24
22
  "src/server.ts",
@@ -109,15 +109,12 @@ export function generatePackageJson(ctx: MigrationContext): string {
109
109
  build:
110
110
  "npm run generate:blocks && npm run generate:sections && npm run generate:loaders && npm run generate:schema && npm run generate:invoke && tsr generate && vite build",
111
111
  preview: "vite preview",
112
- // wrangler.jsonc is generated by `deco-wrangler gen` from the central
113
- // registry in @decocms/start (D6). Predev/prebuild ensure it is up to
114
- // date before vite runs the @cloudflare/vite-plugin which reads it.
115
- "gen:wrangler": "deco-wrangler gen",
116
- predev: "deco-wrangler gen",
117
- prebuild: "deco-wrangler gen",
118
- deploy:
119
- "echo 'Production deploys are managed by .github/workflows/deploy.yml on push to main. For an emergency manual deploy run: npx deco-wrangler deploy'; exit 1",
120
- types: "deco-wrangler types",
112
+ // Deploy is owned by Cloudflare Workers Builds (D6.3); the
113
+ // repo<->worker connection is configured per-worker in the CF
114
+ // dashboard. Local devs use plain `wrangler` against the
115
+ // committed wrangler.jsonc.
116
+ deploy: "wrangler deploy",
117
+ types: "wrangler types",
121
118
  typecheck: "tsc --noEmit",
122
119
  format: 'prettier --write "src/**/*.{ts,tsx}"',
123
120
  "format:check": 'prettier --check "src/**/*.{ts,tsx}"',