Deploying NVX Stack Builder to Cloudflare Workers
On this page
- English
- 1. Requirements
- 2. Local development
- 3. Configuration
- 4. Deploy (workers.dev only)
- 5. Rollback
- 6. Later: binding agents-sdk.space (NOT done; plan only)
- 7. Token permissions
- 8. Files added/changed for Cloudflare
- ภาษาไทย
- 1. สิ่งที่ต้องมี
- 2. รันบนเครื่อง
- 3. ตัวแปรและ secret
- 4. Deploy (workers.dev เท่านั้น)
- 5. ย้อนเวอร์ชัน (rollback)
- 6. ผูกโดเมน agents-sdk.space ภายหลัง (ยังไม่ได้ทำ)
- 7. สิทธิ์ของ token ที่ต้องใช้
- 8. ไฟล์ที่เพิ่มหรือแก้
Production (2026-10-11, v0.6.0): live at https://devstack.bid (apex). It is a Workers Custom Domain of Worker
nvx-stack-builderin account2d92bd5b25768fa9093d6adc0a8887fc;www.devstack.bidis also attached and returns 308 to the apex.
- v0.6.0 deployed 2026-10-11 11:18 ICT (in-app docs at
/docs,/llms.txt, DevStack signature). Version7ddd9404-a31f-4e31-8460-82b3b90c7d0e. Then the D1 bindingDB(databasenvx-db) was added the same day; that build is now current:450890b7-5432-4795-afbc-1c7ebd241bff. Rollback targets:7ddd9404-a31f-4e31-8460-82b3b90c7d0e(v0.6.0), then671b6b3b-1074-40cb-84b0-0df2681b69ce(v0.5.1), thend369f2b6-863a-4e44-8f9e-b5551e18361a(v0.5.0) and107ab78e-2cdd-4159-95e7-e8fcb89130aa.- workers.dev fallback: https://nvx-stack-builder.examplessdk.workers.dev. The account's subdomain was renamed from
space6toexamplessdkon 2026-10-11, so the oldspace6URL no longer resolves.- HSTS is now
max-age=31536000, withoutincludeSubDomains/preload.- Details, created records and token permissions: operations/custom-domain.md.
v0.5.0 (2026-10-11), history: first deployed at https://nvx-stack-builder.space6.workers.dev; that URL was retired by the subdomain rename (Worker
nvx-stack-builder, account2d92bd5b25768fa9093d6adc0a8887fc"Examplessdk@gmail.com's Account", workers.dev subdomainspace6, no routes or custom domains). Deployed 2026-10-11 08:47 ICT from commitf995d24(released as tagv0.5.0). Verified live:
- all pages 200 (including the 10 template pages);
/templates/unknownand/definitely-missingreturn a real 404- security headers on pages, and
Cache-Controlon/brand/*- manifest, favicon, icons and the OG image are served
- no horizontal scroll at 1440px or 390px, in Thai or English; no console errors
- the mascot animates and goes static under reduced motion
/api/plan: 415/413/400; a Thai prompt returnssource: "openai"(gpt-6.1-sol); after 20–21 POSTs/min it returns 429 withRetry-After: 60v0.5.0 versions:
d369f2b6-863a-4e44-8f9e-b5551e18361a(code +OPENAI_API_KEYsecret, the rollback target before v0.5.1);107ab78e-2cdd-4159-95e7-e8fcb89130aa(same code before the secret was set, so the AI agent falls back to the offline planner). That account has no earlier versions. The only other Worker in the account,odd-violet-5e66, was not touched.Previous test deployment (unchanged, still live): https://nvx-stack-builder.agen-sdk-work.workers.dev (account
f70d35188a3c56b9781538c73a86e04e, v0.3.0, version0fb9ecd4-d788-468c-8843-559545c357e3, earlier9b9b678a-f0b4-4727-96d7-e5f81fc00391). It is left untouched on purpose and still serves the v0.3.0 design.
English#
1. Requirements#
- Node.js 22+ for Wrangler 4 (
wranglerrefuses Node 20). Next.js itself runs on Node 20.9+. - A Cloudflare account and an API token (see Token permissions).
npm install. It runspostinstall→scripts/patch-opennext.mjs, which patches@opennextjs/cloudflareso it also loads Next 16.4'spreview-props.json. The patch is idempotent and safe to re-run.
2. Local development#
npm install
cp .dev.vars.example .dev.vars # put OPENAI_API_KEY=... (never commit; gitignored)
npm run dev # Next.js dev server, http://localhost:3000
npm run preview # build for Workers + run in workerd, http://localhost:8787npm run dev also uses .dev.vars and the Cloudflare bindings, through initOpenNextCloudflareForDev().
Checks: npm run lint && npm run typecheck && npm test && npm run cf:build.
3. Configuration#
| Name | Kind | Where | Default |
|---|---|---|---|
OPENAI_API_KEY |
secret | wrangler secret put / .dev.vars |
none → offline planner |
OPENAI_MODEL |
var | wrangler.jsonc vars |
gpt-6.1-sol |
OPENAI_MAX_OUTPUT_TOKENS |
var | wrangler.jsonc |
6000 (hard cap 16000) |
OPENAI_TIMEOUT_MS |
var | wrangler.jsonc |
75000 in wrangler, 60000 in code |
NVX_PLAN_LIMITER |
rate-limit binding | wrangler.jsonc ratelimits (namespace 4712) |
20 req / 60 s per IP, all POSTs |
NVX_PLAN_AI_LIMITER |
rate-limit binding | wrangler.jsonc ratelimits (namespace 4711) |
5 req / 60 s per IP, shared server key only |
PLAN_LIMIT_PER_MIN, PLAN_AI_LIMIT_PER_MIN |
var | only used by the in-memory fallback (non-Workers hosts) | 20 / 5 |
Rate-limit bindings are declared in config only. They are not a separate dashboard resource, and they are created when the Worker is deployed. Counters are per Cloudflare location and eventually consistent, so very fast bursts may let a few extra requests through, which is fine for abuse control. Clients are keyed by IP, and IPv6 clients by their /64 prefix.
Set the secret via stdin so it never appears in shell history or logs:
printenv OPENAI_API_KEY | npx wrangler secret put OPENAI_API_KEYUsers can still paste their own OpenAI key in the UI. It travels only in the x-openai-key header of that single request, isn't rate-limited by the AI limiter, and is never stored or logged.
4. Deploy (workers.dev only)#
export CLOUDFLARE_API_TOKEN=... # only the token string, nothing else
export CLOUDFLARE_ACCOUNT_ID=2d92bd5b25768fa9093d6adc0a8887fc # production account (devstack.bid, examplessdk.workers.dev)
# previous test account (v0.3.0, agen-sdk-work.workers.dev): f70d35188a3c56b9781538c73a86e04e
# 1) make sure the worker name is free (should say it does not exist)
npx wrangler deployments list --name nvx-stack-builder
# If it exists and is not ours: change "name" (and the WORKER_SELF_REFERENCE
# service) in wrangler.jsonc to "nvx-stack-builder-staging".
# 2) build + deploy (creates the Worker on first deploy)
npm run deploy # = opennextjs-cloudflare build && opennextjs-cloudflare deploy
# 3) secret (after the worker exists)
printenv OPENAI_API_KEY | npx wrangler secret put OPENAI_API_KEY
# 4) verify
curl -sI https://nvx-stack-builder.<subdomain>.workers.dev/ | grep -iE 'strict-transport|content-security'
curl -s https://nvx-stack-builder.<subdomain>.workers.dev/api/plan # {"serverKeyConfigured":true,...}wrangler.jsonc sets workers_dev: true, preview_urls: false and no routes, so the deploy never touches any zone, DNS or custom domain.
5. Rollback#
npx wrangler deployments list --name nvx-stack-builder
npx wrangler rollback <version-id> --name nvx-stack-builder -m "reason"
# or stop serving the test worker entirely:
npx wrangler delete --name nvx-stack-builder # only this workerSecrets and vars belong to versions. A rollback restores the previous version's code and bindings.
6. Later: binding agents-sdk.space (NOT done; plan only)#
Current state: zone agents-sdk.space (dfb531c381f8e6dd75082a6fe964f71b) has a proxied apex AAAA 100:: placeholder record. That pattern is used for Worker routes, and the existing Worker agents-sdk-space most likely serves the apex through a route or custom domain.
Steps, when the owner approves:
- Decide: replace
agents-sdk-spaceon the apex, or use a subdomain such asstack.agents-sdk.space(no conflict, recommended for a first rollout). - Token: add Zone › Zone: Read, Zone › Workers Routes: Edit and Zone › DNS: Edit for that zone, and include the account
9d56…34d1. - If you take over the apex, first remove the apex route or custom domain from
agents-sdk-space(Workers → agents-sdk-space → Settings → Domains & Routes). Delete theAAAA 100::record only if no route still needs it. A custom domain creates its own DNS record and conflicts with an existing record for the same name. - Add to
wrangler.jsonc:then"routes": [{ "pattern": "stack.agents-sdk.space", "custom_domain": true }]npm run deploy, or use Dashboard → Worker → Domains & Routes → Add Custom Domain. - HSTS: since v0.5.1 the app sends
Strict-Transport-Security: max-age=31536000(noincludeSubDomains), because it now runs on the devstack.bid apex. The older valuemax-age=31536000; includeSubDomainswould have applied as described next. On the apex this applies to all subdomains, so make sure all of them serve HTTPS. Don't addpreloaduntil you're sure. - Optionally set
workers_dev: falseonce the domain works.
7. Token permissions#
Each test account uses its own account-scoped token. The v0.3.0 token (restless-heart-ac34) covers only f70d…e04e. The v0.5.0 deploy used a separate token scoped to 2d92…87fc, passed only as CLOUDFLARE_API_TOKEN to wrangler and never printed or stored. Its /user/tokens/verify returns non-JSON, which is normal for account tokens: check them with /accounts/<id>/tokens/verify or wrangler whoami. To deploy to yet another account:
Minimum for the test deploy (account-scoped token, resources must include account 9d56815c97f330febf9d6a0bb7f234d1):
- Account › Workers Scripts: Edit (upload, secrets, versions, rollback, rate-limit bindings)
- Account › Account Settings: Read (whoami, workers.dev subdomain)
- Optional: Account › Workers Observability: Edit, Workers Tail: Read
8. Files added/changed for Cloudflare#
wrangler.jsonc, open-next.config.ts, cloudflare-env.d.ts, .dev.vars.example, public/_headers, scripts/patch-opennext.mjs, next.config.ts (headers, 404 rewrite, dev bindings), src/lib/server/{env,http,rate-limit}.ts, src/lib/security-headers.ts, src/data/template-ids.ts, src/app/api/plan/route.ts, src/lib/ai/openai.ts, tests/production.test.ts, vitest.config.mts, package.json, .gitignore.
ภาษาไทย#
Production (11 ต.ค. 2026, v0.6.0): เสิร์ฟที่ https://devstack.bid (apex) ผ่าน Workers Custom Domain ของ Worker
nvx-stack-builderและwww.devstack.bidredirect 308 ไป apex เวอร์ชันปัจจุบัน7ddd9404-a31f-4e31-8460-82b3b90c7d0e(v0.6.0, deploy 11:18 ICT; ก่อนหน้า671b6b3b-1074-40cb-84b0-0df2681b69cev0.5.1 ใช้ rollback ได้ แล้วd369f2b6-863a-4e44-8f9e-b5551e18361a) workers.dev สำรองที่ https://nvx-stack-builder.examplessdk.workers.dev (บัญชีเปลี่ยน subdomain จากspace6เป็นexamplessdk) HSTS เหลือmax-age=31536000ไม่มีincludeSubDomainsรายละเอียดดู operations/custom-domain.mdสถานะ v0.5.0 (11 ต.ค. 2026): deploy ครั้งแรกที่ https://nvx-stack-builder.space6.workers.dev (URL นี้ใช้ไม่ได้แล้ว) (Worker
nvx-stack-builderบัญชี2d92bd5b25768fa9093d6adc0a8887fc"Examplessdk@gmail.com's Account" subdomainspace6ใช้ workers.dev เท่านั้น) deploy เมื่อ 11 ต.ค. 2026 08:47 (ICT) จาก commitf995d24(tagv0.5.0) ตรวจบนเว็บจริงผ่านทุกข้อ: ทุกหน้าได้ 200, 404 จริง, security headers, manifest/favicon/OG image, ไม่มี horizontal scroll ทั้งไทยและอังกฤษ, prompt ภาษาไทยได้source: "openai", rate limit ตอบ 429 พร้อมRetry-After: 60เวอร์ชันสำหรับ rollback:d369f2b6-863a-4e44-8f9e-b5551e18361a(ปัจจุบัน) และ107ab78e-2cdd-4159-95e7-e8fcb89130aa(โค้ดเดียวกันแต่ยังไม่ตั้ง secret) Worker เดิมodd-violet-5e66ในบัญชีนั้นไม่ถูกแตะdeploy ทดสอบเดิม (v0.3.0) ที่ https://nvx-stack-builder.agen-sdk-work.workers.dev บัญชี
f70d35188a3c56b9781538c73a86e04eยังอยู่และไม่ถูกแก้ไข (เวอร์ชัน0fb9ecd4-d788-468c-8843-559545c357e3)
1. สิ่งที่ต้องมี#
- Node.js 22 ขึ้นไป สำหรับ Wrangler 4
- บัญชี Cloudflare และ API token ที่มีสิทธิ์ตามหัวข้อ 7
npm installจะรันscripts/patch-opennext.mjsให้อัตโนมัติ เพื่อแก้ adapter ให้รองรับpreview-props.jsonของ Next 16.4
2. รันบนเครื่อง#
npm install
cp .dev.vars.example .dev.vars # ใส่ OPENAI_API_KEY (ห้าม commit)
npm run dev # http://localhost:3000
npm run preview # รันใน Workers runtime ที่ http://localhost:87873. ตัวแปรและ secret#
OPENAI_API_KEYเป็น secret ตั้งผ่าน stdin เท่านั้น:printenv OPENAI_API_KEY | npx wrangler secret put OPENAI_API_KEYOPENAI_MODEL(ค่าเริ่มต้นgpt-6.1-sol),OPENAI_MAX_OUTPUT_TOKENS(6000, สูงสุด 16000),OPENAI_TIMEOUT_MSตั้งในvarsของwrangler.jsonc- Rate limit:
NVX_PLAN_LIMITER20 ครั้งต่อนาทีต่อ IP สำหรับทุก POST และNVX_PLAN_AI_LIMITER5 ครั้งต่อนาทีต่อ IP เฉพาะเมื่อใช้ key ของเซิร์ฟเวอร์ binding ทั้งสองถูกสร้างพร้อมการ deploy - key ที่ผู้ใช้ใส่เองถูกใช้แค่ในคำขอนั้น ไม่ถูกเก็บและไม่ถูกบันทึก log
4. Deploy (workers.dev เท่านั้น)#
export CLOUDFLARE_API_TOKEN=... # ใส่เฉพาะตัว token
export CLOUDFLARE_ACCOUNT_ID=2d92bd5b25768fa9093d6adc0a8887fc # บัญชี production (devstack.bid) · บัญชีเดิม v0.3.0: f70d35188a3c56b9781538c73a86e04e
npx wrangler deployments list --name nvx-stack-builder # ตรวจว่าชื่อยังว่าง ถ้าไม่ว่างให้ใช้ nvx-stack-builder-staging
npm run deploy
printenv OPENAI_API_KEY | npx wrangler secret put OPENAI_API_KEYไฟล์ config ไม่มี routes จึงไม่แตะ zone, DNS หรือ custom domain ใด ๆ
5. ย้อนเวอร์ชัน (rollback)#
npx wrangler deployments list --name nvx-stack-builder
npx wrangler rollback <version-id> --name nvx-stack-builder6. ผูกโดเมน agents-sdk.space ภายหลัง (ยังไม่ได้ทำ)#
- เลือกว่าจะใช้ apex แทน Worker
agents-sdk-spaceเดิม หรือใช้ subdomain เช่นstack.agents-sdk.space(แนะนำ เพราะไม่ชนกับของเดิม) - เพิ่มสิทธิ์ token: Zone Read, Workers Routes Edit, DNS Edit บน zone
dfb531c381f8e6dd75082a6fe964f71b - ถ้าจะใช้ apex ต้องถอด route หรือ custom domain ของ
agents-sdk-spaceก่อน และพิจารณาลบ recordAAAA 100::(proxied) ที่เป็น placeholder ของ route เดิม - เพิ่ม
"routes": [{ "pattern": "stack.agents-sdk.space", "custom_domain": true }]แล้วnpm run deploy - HSTS: ตั้งแต่ v0.5.1 ไม่ส่ง
includeSubDomainsเพราะแอปอยู่บน apex ถ้าจะเพิ่มกลับ ทุก subdomain ต้องรองรับ HTTPS ก่อน
7. สิทธิ์ของ token ที่ต้องใช้#
token ต้องมีบัญชี 9d56815c97f330febf9d6a0bb7f234d1 อยู่ใน resources และมีสิทธิ์ Account › Workers Scripts: Edit กับ Account › Account Settings: Read (ไม่บังคับ: Workers Observability Edit, Workers Tail Read)
8. ไฟล์ที่เพิ่มหรือแก้#
ดูรายการในหัวข้อ 8 ของภาษาอังกฤษด้านบน
Source: docs/DEPLOY.md · /docs/deploy.md