agents/skills/cf-workers-env-local/SKILL.md
Alchemy + env files — repo-root `.env.local` (dev), `.env.staging` (staging / PR preview deploys), `.env.production` (prod / CI), optional per-package `.env.local`, and package-local Alchemy apps. Use when adding secrets or non-secret vars or debugging missing env in local dev. Never use a plain `.env` file. `.env.example` is human documentation only — no script reads it for all keys.
npx skillsauth add firtoz/cf-multiworker-starter-kit cf-workers-env-localInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
alchemy-cli --stage local dev from its package.json, using alchemy.app → alchemy dev --app <id>; see Alchemy Turborepo).bun run typegen / bun run typecheck relate to infra (alchemy.run.ts) vs deploy-time secrets (.env.*)..env.local + .env.staging + .env.production vs optional per-package .env.local.After cloning or generating from the template, follow README Quick start:
bun run quickstart — install if needed, .env.local regeneratable keys, then bun run devbun run setup:account (optional, once per machine) — CLOUDFLARE_ACCOUNT_ID, CLOUDFLARE_API_TOKEN, ALCHEMY_STATE_TOKEN in cloudflare-alchemy/account.env so bun run setup:staging / setup:prod and github:sync:* do not require repeating those keys in every repo’s .env.staging / .env.production (admin stack merges the account file when building the GitHub payload).bun run onboard:staging — gh, Cloudflare credentials in .env.staging or the account file, setup:staging --yes, github:sync:stagingbun run onboard:prod — production dotfile flow (or account file for shared keys), github:sync:prod, repo variable AUTO_PRODUCTION_PRCreate Cloudflare API tokens only in the dashboard (docs/github-admin.md); this repo does not mint tokens via scripts or OAuth.
.env.example (repo root, optional apps/web/.env.example) — Human documentation only for most keys. Root bun run dev and package dev scripts read the real .env.local; Alchemy docs cover Secrets and State.
Real env — Put dev values in .env.local (gitignored).
CLOUDFLARE_ACCOUNT_ID, CLOUDFLARE_API_TOKEN, ALCHEMY_STATE_TOKEN for every local repo on one Cloudflare account: default paths in .env.example; override with CLOUDFLARE_ALCHEMY_ACCOUNT_ENV. bun run setup:account edits it. Fills gaps only — does not override shell or stage dotfiles. GitHub Actions never reads this file (use Environment secrets / variables or organization secrets to share values across repos in CI)..env.staging — staging + PR preview deploys (STAGE=staging or STAGE=pr-<n>)..env.production — production (STAGE=prod)..env file.POSTHOG_* — optional analytics; same idea as optional WEB_* (unset = dark, or remove scaffolding — root README).ALCHEMY_PASSWORD, ALCHEMY_STATE_TOKEN, and CI — Three different concepts; ALCHEMY_PASSWORD ≠ ALCHEMY_STATE_TOKEN.
ALCHEMY_PASSWORD — encrypts Alchemy-managed secrets stored in deploy state (encryption password). Use one stable value per logical stage (e.g. all STAGE=staging deploys share staging’s password; prod uses prod’s password). Not required to match staging and prod across each other.ALCHEMY_STATE_TOKEN — bearer token Alchemy sends to authenticate against the alchemy-state-service Worker (default Cloudflare state store). Per Alchemy, "this token must be the same for all deployments on your Cloudflare account" — interpret that broadly for this repo:
staging, pr-<n>, prod, GitHub (staging / production / staging-fork Environment secrets, etc.), laptops running deploy/destroy with non-local STAGE, and any other repository or workload that uses the same account + default alchemy-state-service. Different values → [CloudflareStateStore] The token is invalid / 401.ALCHEMY_PASSWORD: you do not get a distinct state token “per environment” or “per project.” Same Cloudflare account and default state Worker ⇒ same secret everywhere you pass ALCHEMY_STATE_TOKEN.STAGE=local alchemy dev uses filesystem .alchemy/ — no Cloudflare store, so no ALCHEMY_STATE_TOKEN for that path unless you explicitly run deploy with a non-local stage from the same machine (then match the account token).github:sync:staging / github:sync:prod (trusted machine) so CI matches dotfiles — CI only reads GitHub‑supplied env.CLOUDFLARE_ACCOUNT_ID) ⇒ separate accounts can each have their own token; still one token per account covering all envs/repos that deploy to that account with the shared store.scriptName other than Alchemy’s default for CloudflareStateStore: separate Workers can theoretically use different bearer secrets; this template ships the default (packages/alchemy-utils/src/alchemy-cloud-state-store.ts → alchemy-state-service), so assume single account token.alchemy("…") app id + stage, not separate state-store Workers for each stage. For any STAGE other than local, apps use alchemyCiCloudStateStoreOptions(stage) with the shared Worker; stack rows are keyed inside that store (Cloudflare state store). Every deploy package lists state-hub as a devDependency so Turbo ^deploy:* runs the hub before other deploys (single creator → avoids 10065 … already in use on the state DO).bun run setup / setup:staging / setup:prod walks both ALCHEMY_PASSWORD and ALCHEMY_STATE_TOKEN in the browser. github:sync / github:sync:* pushes them to GitHub Environments with the other deploy secrets (defaults: gh auth token / gh repo view).github:env:* updates only RepositoryEnvironment deployment protection from config/github.policy.ts (see stacks/github-repository-environment-from-env.ts); it does not upload secrets or Environment variables. Stage dotfile may still be merged for local process env.Infra source of truth — Package-local alchemy.run.ts files. Changing bindings means updating the relevant package app. env.d.ts files use the exported package worker resource's Env.
Turbo + stage files
bun run dev — filtered Turbo dev: web + worker packages run alchemy-cli --stage local dev (Alchemy Turborepo).deploy:* / destroy:* — stage-specific graphs; alchemy-cli --stage … sets STAGE, loads repo dotfiles + account.env, and passes --app from each package’s package.json → alchemy.app (backs onto ALCHEMY_APP_IDS in worker-peer-scripts.ts).dotenv-cli — bunx dotenv-cli -v STAGE=… -e .env.staging|.env.production -- …. Locally, the stage file loads when present; in CI, missing repo dotfiles → values come from workflow env: entries such as ${{ vars.MY_VAR }} / ${{ secrets.MY_SECRET }}.github:sync:* creates/updates Environment secrets and variables, but every CI job that needs a value during deploy:* / destroy:* must explicitly list it in the workflow step or job env: block (for example .github/workflows/main-push.yml, prod-deploy.yml, and pr-deploy.yml).turbo run … must also be declared in root turbo.json globalEnv (or task/package env config), otherwise the workflow shell may have them while the package deploy task does not.alchemy.run.ts, not env files.Per-package .env.local — Optional; include in Turbo inputs where a package’s tasks need it (e.g. chatroom-do). Never substitute .env.example for real values.
Same account for both values
CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID must point at the same Cloudflare account.CLOUDFLARE_ACCOUNT_ID. The Cloudflare state store uses that account’s workers.dev subdomain.[CloudflareStateStore] RPC 404 with text/html, wrong-account deploys, subdomain mismatch.How to stay sane
.env.staging / .env.production, GitHub Environment fields, and local dotfiles consistent per stage.GitHub Actions
CLOUDFLARE_ACCOUNT_ID → Environment variable (plaintext), not a Secret.CLOUDFLARE_API_TOKEN → Secret.github:sync / github:sync:* upserts the account id as a variable.PR previews
staging.staging-fork remains in policy/sync for legacy or future use..env.example # documentation only
.env.local # gitignored dev — loaded by root `bun run dev` / package dev scripts
.env.staging # gitignored — staging + PR preview deploy inputs (`STAGE=staging` or `pr-<n>`)
.env.production # gitignored prod / CI secrets as needed (`STAGE=prod`)
stacks/admin.ts # local-only admin stack — GitHub Environment secrets + deploy enablement var
packages/alchemy-utils/ # `PRODUCT_PREFIX`, `ALCHEMY_APP_IDS`, `alchemy-cli` bin, `requireAlchemyPassword`, deployment-stage
packages/state-hub/ # `alchemy.run.ts` — provisions shared CloudflareStateStore for non-local stages (`ALCHEMY_APP_IDS.stateHub`)
apps/web/
alchemy.run.ts # web Alchemy app
env.d.ts # Alchemy-derived Env (see multiworker-workflow / cf-web-alchemy-bindings)
env.requirements.ts when setup / github:sync:* should know about it (for web keys: apps/web/env.requirements.ts)..env.example so contributors know which keys exist.alchemy.run.ts binding or secret wiring.env: blocks. Environment variables synced by github:sync:* are only available in Actions when referenced as ${{ vars.KEY }} / ${{ secrets.KEY }}.turbo.json globalEnv (or a narrower task env config) so Turbo passes it to package scripts.alchemy.run.ts reads a required value at module scope, mirror it on PR preview destroy jobs too; destroy loads the same Alchemy entrypoint.bun run github:sync:staging / github:sync:prod so GitHub Environments receive the value.bun run typegen from the repo root.alchemy.run.ts bindings or routes, run bun run typegen and bun run typecheck from the repo root (same Turbo task names everywhere; staged dotfiles do not drive React Router typegen).multiworker-workflow — typegen cadence, deploy, checklist.multiworker-gotchas — stack-specific gotchas.project-init — renaming resources after forking the template.alchemy dev / deploy, alchemy login.development
Repo-root commands, typegen and typecheck cadence, lint, deploy, adding packages with bun, and Alchemy app layout. Use at the start of a task, before PR, or when choosing turbo/typegen commands.
development
Fork and template gotchas (env import, routes, typegen, forms, D1, Turbo, HMR, new DO packages). Use when working on apps/web or durable-objects, or when behavior diverges from this stack’s conventions.
testing
Turborepo task configuration patterns for monorepo management. Use when configuring turbo.json tasks, setting up task dependencies, managing cache inputs/outputs, or working with cross-package dependencies in the monorepo.
development
React Router v7 routing patterns and environment variable configuration. Use whenever you touch React Router–related code (routes, links, params, loaders, actions, route config, or env in route context).