This repo deploys two Workers:
request-botrequest-bot-backend
The frontend Worker binds to the backend Worker, so deploy the backend first.
- Cloudflare account
- Wrangler authenticated locally
- Node 22+
- npm
Check Wrangler auth:
npx wrangler whoamiCreate these once per environment:
- one D1 database
- one KV namespace
- one Queue
Create them with Wrangler:
npx wrangler d1 create request_bot
npx wrangler kv namespace create SESSION_KV
npx wrangler queues create twitch-reply-queueKeep the returned D1 database ID and KV namespace ID.
The committed wrangler.jsonc and wrangler.aux.jsonc stay as templates with placeholder IDs. Real deploy configs are generated into gitignored .generated.
Copy the deploy template:
cp .env.deploy.example .env.deployFill in:
APP_URLSENTRY_ENVIRONMENTSENTRY_TRACES_SAMPLE_RATEwhen neededCLOUDFLARE_D1_DATABASE_IDCLOUDFLARE_SESSION_KV_IDTWITCH_CLIENT_IDTWITCH_EXTENSION_CLIENT_IDTWITCH_CLIENT_SECRETTWITCH_TOKEN_ENCRYPTION_SECRETINTERNAL_API_SECRETTWITCH_EVENTSUB_SECRETTWITCH_EXTENSION_SECRETSESSION_SECRETADMIN_TWITCH_USER_IDSTWITCH_BOT_USERNAMETWITCH_SCOPESVITE_TWITCH_EXTENSION_API_BASE_URLwhen you build the standalone Twitch panel artifact
Use the deployed public app URL for APP_URL and for VITE_TWITCH_EXTENSION_API_BASE_URL when the panel talks back to that same deployed app.
Set frontend Worker secrets:
echo "<TWITCH_CLIENT_ID>" | npx wrangler secret put TWITCH_CLIENT_ID --config wrangler.jsonc
echo "<TWITCH_CLIENT_SECRET>" | npx wrangler secret put TWITCH_CLIENT_SECRET --config wrangler.jsonc
echo "<TWITCH_TOKEN_ENCRYPTION_SECRET>" | npx wrangler secret put TWITCH_TOKEN_ENCRYPTION_SECRET --config wrangler.jsonc
echo "<INTERNAL_API_SECRET>" | npx wrangler secret put INTERNAL_API_SECRET --config wrangler.jsonc
echo "<TWITCH_EVENTSUB_SECRET>" | npx wrangler secret put TWITCH_EVENTSUB_SECRET --config wrangler.jsonc
echo "<TWITCH_EXTENSION_SECRET>" | npx wrangler secret put TWITCH_EXTENSION_SECRET --config wrangler.jsonc
echo "<SESSION_SECRET>" | npx wrangler secret put SESSION_SECRET --config wrangler.jsonc
echo "<ADMIN_TWITCH_USER_IDS>" | npx wrangler secret put ADMIN_TWITCH_USER_IDS --config wrangler.jsonc
echo "<SENTRY_DSN>" | npx wrangler secret put SENTRY_DSN --config wrangler.jsoncSet backend Worker secrets:
echo "<TWITCH_CLIENT_ID>" | npx wrangler secret put TWITCH_CLIENT_ID --config wrangler.aux.jsonc
echo "<TWITCH_CLIENT_SECRET>" | npx wrangler secret put TWITCH_CLIENT_SECRET --config wrangler.aux.jsonc
echo "<TWITCH_TOKEN_ENCRYPTION_SECRET>" | npx wrangler secret put TWITCH_TOKEN_ENCRYPTION_SECRET --config wrangler.aux.jsonc
echo "<INTERNAL_API_SECRET>" | npx wrangler secret put INTERNAL_API_SECRET --config wrangler.aux.jsonc
echo "<TWITCH_EVENTSUB_SECRET>" | npx wrangler secret put TWITCH_EVENTSUB_SECRET --config wrangler.aux.jsonc
echo "<TWITCH_EXTENSION_SECRET>" | npx wrangler secret put TWITCH_EXTENSION_SECRET --config wrangler.aux.jsonc
echo "<SENTRY_DSN>" | npx wrangler secret put SENTRY_DSN --config wrangler.aux.jsoncSet TWITCH_EXTENSION_CLIENT_ID in .env.deploy or GitHub repository variables so the generated frontend and backend Worker configs can include it.
Initialize the remote D1 database:
npm run db:bootstrap:remoteApply only migrations when the seed is already in place:
npm run db:migrate:remoteRemote-affecting scripts are blocked outside CI by default. Use ALLOW_REMOTE_OPERATIONS=1 only for deliberate operator-driven maintenance.
Preferred deploy path:
npm run deployThat flow:
- builds the app
- generates deploy configs in
.generated/ - deploys the backend Worker
- deploys the frontend Worker
The app works on the frontend Worker workers.dev URL or on a custom domain.
Use the same final app URL in all of these places:
.env.deployAPP_URL- GitHub Actions
APP_URLsecret - Twitch redirect URIs
VITE_TWITCH_EXTENSION_API_BASE_URLfor the standalone panel artifact
Twitch redirect URIs:
https://your-app-host/auth/twitch/callbackhttps://your-app-host/auth/twitch/bot/callback
If you use a custom domain, attach it to the frontend Worker in Cloudflare Workers & Pages -> request-bot -> Settings -> Domains & Routes.
The repo includes:
- CI on pull requests
- preview deploys on pull requests
- production deploys on push to
main
Required GitHub repository secrets:
CLOUDFLARE_API_TOKENCLOUDFLARE_ACCOUNT_IDCLOUDFLARE_D1_DATABASE_IDCLOUDFLARE_SESSION_KV_IDAPP_URL
Required GitHub repository variables:
TWITCH_BOT_USERNAMETWITCH_EXTENSION_CLIENT_IDTWITCH_SCOPESSENTRY_ENVIRONMENT
Optional:
CLOUDFLARE_WORKERS_SUBDOMAIN
Production deploys apply remote D1 migrations and then deploy the backend and frontend Workers.
Preview deploys create isolated Worker names, but they do not provision dedicated D1, KV, or Queue resources and they do not register cron triggers or queue consumers.
Build and package the standalone panel artifact locally when you need a fresh Twitch upload:
npm run build:extension:packageWhen the panel talks to a deployed app origin, set the API base URL in the same shell before building:
VITE_TWITCH_EXTENSION_API_BASE_URL=https://your-app-host npm run build:extension:packageThe zip lands under:
output/twitch-extension/request-bot-panel-YYYYMMDD-HHmmss.zip
The zip contains the contents of dist/twitch-extension/panel directly, so index.html, assets, and backgrounds sit at the archive root.
For the full hosted-test rollout checklist, use docs/twitch-panel-extension-beta-rollout-checklist.md.