Deployment
askdare runs wherever Next.js runs on Node.js. It needs no database: conversations live in OpenAI, and your app only stores signed tokens in the visitor's browser. These are the steps for Vercel and Heroku. Before the chat is public, also work through the going-public checklist.
What every deployment needs
| Variable | Notes |
|---|---|
OPENAI_API_KEY | A key from the OpenAI project for this environment. Use separate projects for development and production. |
CHAT_SECRET | A new secret per environment: openssl rand -base64 32. Don't reuse the one from .env.local. |
ASKDARE_MODEL | If your config reads the model from it, as the init template does. |
IDENTITY_SECRET | Only for signed-in visitors. Shared with your site's backend, different from CHAT_SECRET. |
KB_VECTOR_STORE_ID | Only with a knowledge base. Vector stores belong to one OpenAI project, so run kb sync with the production key to get the production store. |
Set them in your host's settings, never in files you commit.
Vercel
-
Import the repository in Vercel. The defaults for Next.js are right.
-
Add the variables above under Settings → Environment Variables, for Production and Preview.
next buildfails withoutASKDARE_MODEL, so every environment that builds the app needs it.vercel env pull .env.localcopies the development values to your machine. -
Share the rate limits. Vercel runs many instances at once, and the default limits are kept per instance. Create a Redis database at Upstash (also available in the Vercel Marketplace) and use the add-on:
npm install @boldare/askdare-upstash// app/api/chat/[...path]/route.ts import { upstashStore } from '@boldare/askdare-upstash'; export const { GET, POST, DELETE } = createChatHandler({ bots, limits: { store: upstashStore() }, // UPSTASH_REDIS_REST_URL and _TOKEN, or KV_REST_API_URL and _TOKEN }); -
Keep
export const maxDuration = 300in the route file. That is the longest a streamed answer may take; each bot'slimits.turnTimeoutMs(120 s by default) must stay below it. -
Deploy, then check the live app:
npx @boldare/askdare doctor --url https://your-app.vercel.app --env-file .env.production.localdoctorsigns its request withCHAT_SECRET, so the file must hold the production secret. Keep it out of git;initalready ignores.env*.local.
Requests to Vercel functions can be at most 4.5 MB, so keep attachments.maxBytes at its default of 4 MB or lower.
Heroku
-
Create the app and set the Node.js version in
package.json:{ "engines": { "node": "24.x" } } -
Add a
Procfile. The longer keep-alive outlasts Heroku's router, so connections aren't cut mid-answer:web: next start --port $PORT --keepAliveTimeout 95000 -
Set the variables above under Settings → Config Vars in the dashboard. Typing the key into
heroku config:setwould leave it in your shell history. -
One dyno works with the default limits. With more dynos, share the limits with
@boldare/askdare-upstash, as for Vercel. -
Deploy, then check it:
npx @boldare/askdare doctor --url https://your-app.herokuapp.com --env-file .env.production.local
Heroku closes a request that sends nothing for 30 seconds at first, and 55 seconds after that. askdare sends the first byte at once and a heartbeat every 15 seconds, so slow answers survive.
Behind another proxy or CDN
- Don't buffer
text/event-streamresponses; askdare sendsX-Accel-Buffering: nofor nginx. - Each proxy adds itself to
X-Forwarded-For. With more than the host's own proxy in front of the app, setlimits.trustProxyincreateChatHandlerto the number of proxies, or per-IP limits count the proxy instead of the visitor. - Leave
/api/chatout ofproxy.ts, ofheaders()innext.config, and of any CDN rule that setsX-Frame-Options, a Content Security Policy or cookies.doctorreports each of these.