@boldare/askdare 0.0.0-stage → 0.1.0-rc.1
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.
- package/CHANGELOG.md +26 -0
- package/LICENSE +21 -0
- package/README.md +65 -2
- package/dist/cli.js +1461 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +193 -0
- package/dist/next.d.ts +86 -0
- package/dist/next.js +2864 -0
- package/dist/store.d.ts +413 -0
- package/dist/version.js +641 -0
- package/docs/attachments.md +41 -0
- package/docs/cli.md +227 -0
- package/docs/configuration.md +357 -0
- package/docs/costs.md +181 -0
- package/docs/custom-store.md +259 -0
- package/docs/deployment.md +76 -0
- package/docs/embedding.md +258 -0
- package/docs/getting-started.md +116 -0
- package/docs/going-public.md +81 -0
- package/docs/identity.md +109 -0
- package/docs/knowledge-base.md +65 -0
- package/docs/migrating-from-openai-assistant.md +43 -0
- package/docs/privacy.md +175 -0
- package/docs/protocol.md +174 -0
- package/docs/styling.md +166 -0
- package/docs/support.md +48 -0
- package/docs/texts.md +176 -0
- package/docs/themes.md +172 -0
- package/docs/tools.md +52 -0
- package/docs/troubleshooting.md +345 -0
- package/docs/voice-input.md +31 -0
- package/llms.txt +32 -0
- package/package.json +98 -3
- package/skills/askdare/SKILL.md +36 -0
package/docs/cli.md
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
# Command line
|
|
2
|
+
|
|
3
|
+
askdare comes with a small command-line tool. Run it with `npx` in the folder of your Next.js app:
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npx @boldare/askdare <command> [options]
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
| Command | What it does |
|
|
10
|
+
| --- | --- |
|
|
11
|
+
| [`init`](#init) | Adds askdare to the app: a bot config, the route, `.env.local` and the chat script. |
|
|
12
|
+
| [`doctor`](#doctor) | Checks the project, its environment and the running app. |
|
|
13
|
+
| [`kb sync <folder>`](#kb-sync) | Makes an OpenAI vector store mirror a folder of documents. |
|
|
14
|
+
| [`-v`, `--version`](#version-and-help) | Prints the installed version. |
|
|
15
|
+
| [`-h`, `--help`](#version-and-help) | Prints the usage. |
|
|
16
|
+
|
|
17
|
+
The commands and their options are public API ([ADR-0006](https://github.com/boldare/askdare/blob/main/docs/adr/0006-support-matrix-and-versioning.md)). Their printed output is not, and may change in any release.
|
|
18
|
+
|
|
19
|
+
## init
|
|
20
|
+
|
|
21
|
+
Adds askdare to the Next.js app in the current folder. It never overwrites a file, so it's safe to run again.
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npx @boldare/askdare init
|
|
25
|
+
npx @boldare/askdare init --bot sales --model <model>
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
| Option | Default | What it does |
|
|
29
|
+
| --- | --- | --- |
|
|
30
|
+
| `--bot <id>` | `support` | The bot's ID: 1 to 48 lowercase letters, digits or dashes, starting with a letter or digit. It goes into `askdare.config.ts` and the script tag. |
|
|
31
|
+
| `--model <id>` | none | The OpenAI model to put into `.env.local` as `ASKDARE_MODEL`. Without it, `ASKDARE_MODEL` stays empty for you to fill in. It never replaces a value that's already there. |
|
|
32
|
+
| `-h`, `--help` | | Prints the usage. |
|
|
33
|
+
|
|
34
|
+
The folder needs a `package.json` with `next` among its dependencies, and an `app/` or `src/app/` folder.
|
|
35
|
+
|
|
36
|
+
### What it writes
|
|
37
|
+
|
|
38
|
+
| File | What `init` does | When it leaves the file alone |
|
|
39
|
+
| --- | --- | --- |
|
|
40
|
+
| `askdare.config.ts` | Creates one bot: the ID, `model: process.env.ASKDARE_MODEL ?? ''`, short instructions, an empty `security.allowedOrigins`, `locale: 'auto'`, a greeting and the `aura` theme. It is `askdare.config.js` when the project has no `tsconfig.json`. | An `askdare.config` file already exists (`.ts`, `.tsx`, `.js`, `.jsx` or `.mjs`). |
|
|
41
|
+
| `app/api/chat/[...path]/route.ts` | Creates the catch-all route: `export const maxDuration = 300;` and `createChatHandler({ bots })`. Under `src/app/` if that's where your app is. | A route file is already there. |
|
|
42
|
+
| `.gitignore` | Appends `.env*.local`, so secrets never land in git. | Git already ignores `.env.local` and `.env.production.local`. |
|
|
43
|
+
| `.env.local` | Appends, under `# askdare`, each with a comment: `OPENAI_API_KEY` (empty), `CHAT_SECRET` (32 random bytes, base64url) and `ASKDARE_MODEL` (the `--model` value, or empty). A new file is readable only by you. | Skips each variable that `.env.local` or `.env` already defines. It never changes existing lines. |
|
|
44
|
+
| Root layout, such as `app/layout.tsx` | Adds `import Script from 'next/script';` and, before `</body>`, `<Script src="/api/chat/widget.js?bot=support" strategy="afterInteractive" />` with your bot ID. | The layout already loads `/api/chat/widget.js`. With no layout, or more than one `</body>`, it prints the two lines for you to add by hand. |
|
|
45
|
+
| `AGENTS.md` | Appends a short block that tells AI coding agents to read `node_modules/@boldare/askdare/llms.txt` before changing the chat, and to run `doctor` afterwards. Creates the file if needed. | The block is already there. |
|
|
46
|
+
|
|
47
|
+
Each line of the output starts with `+` for something added or `=` for something kept. Then `init` lists what is left to do:
|
|
48
|
+
|
|
49
|
+
```text
|
|
50
|
+
askdare init
|
|
51
|
+
|
|
52
|
+
+ askdare.config.ts
|
|
53
|
+
+ app/api/chat/[...path]/route.ts
|
|
54
|
+
+ .env.local: OPENAI_API_KEY, CHAT_SECRET (generated), ASKDARE_MODEL
|
|
55
|
+
+ app/layout.tsx: the chat script
|
|
56
|
+
+ AGENTS.md: where AI coding agents find the askdare docs
|
|
57
|
+
|
|
58
|
+
Next:
|
|
59
|
+
1. In .env.local, set OPENAI_API_KEY, ASKDARE_MODEL.
|
|
60
|
+
2. Start the app (npm run dev), then check it: npx @boldare/askdare doctor
|
|
61
|
+
3. To embed the chat on another site, add that site to allowedOrigins and paste:
|
|
62
|
+
<script src="https://<your app>/api/chat/widget.js?bot=support" async></script>
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
The list also tells you to install `@boldare/askdare` and `zod` if they're missing, and to upgrade `zod` when it's older than 4. It uses your package manager, which it detects from the lockfile.
|
|
66
|
+
|
|
67
|
+
## doctor
|
|
68
|
+
|
|
69
|
+
Checks the project, its environment and the running app, and says what to do about each problem. Start the app first, then run `doctor` in a second terminal:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
npx @boldare/askdare doctor
|
|
73
|
+
npx @boldare/askdare doctor --origin https://www.example.com
|
|
74
|
+
npx @boldare/askdare doctor --url https://app.example.com --env-file .env.production.local
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
| Option | Default | What it does |
|
|
78
|
+
| --- | --- | --- |
|
|
79
|
+
| `--url <url>` | `http://localhost:3000` | Where the app runs. It must use `https`; plain `http` only for `localhost`, `127.0.0.1`, `[::1]` and `*.localhost`. |
|
|
80
|
+
| `--base-path <path>` | `/api/chat` | The path of the askdare route, if you mounted it elsewhere (`basePath` in `createChatHandler`). |
|
|
81
|
+
| `--env-file <path>` | `.env.development.local`, `.env.local`, `.env.development` and `.env` | Where to read `CHAT_SECRET` and the other variables. |
|
|
82
|
+
| `--origin <url>` | none | A site that should embed the chat. `doctor` checks it against each bot's `security.allowedOrigins`. Only the origin of the URL counts. |
|
|
83
|
+
| `-h`, `--help` | | Prints the usage. |
|
|
84
|
+
|
|
85
|
+
Without `--env-file`, `doctor` reads the environment the way `next dev` does: variables in your shell win, then the files in the order above. With `--env-file`, it reads only that file, and the file wins over your shell.
|
|
86
|
+
|
|
87
|
+
### What it checks
|
|
88
|
+
|
|
89
|
+
**Project**
|
|
90
|
+
|
|
91
|
+
- Node 22 or later.
|
|
92
|
+
- At least Next.js 16, React 19 and zod 4.1, and `@boldare/askdare`, as installed in `node_modules`.
|
|
93
|
+
- The route file, `app/api/chat/[...path]/route.ts` (it follows `--base-path`): it calls `createChatHandler`, doesn't use the Edge runtime, and has a literal `maxDuration`. A missing `maxDuration` is a warning.
|
|
94
|
+
- A `middleware.ts`: a warning to rename it to `proxy.ts`.
|
|
95
|
+
|
|
96
|
+
**Environment**
|
|
97
|
+
|
|
98
|
+
- `OPENAI_API_KEY` is set, for an app on your machine.
|
|
99
|
+
- `CHAT_SECRET` is set and strong: at least 32 characters, with at least 12 different ones. The same goes for each secret in `CHAT_SECRET_PREVIOUS`.
|
|
100
|
+
- `IDENTITY_SECRET`, when set, is strong and differs from `CHAT_SECRET`.
|
|
101
|
+
- `ASKDARE_MODEL` is set and not empty, for an app on your machine, when `askdare.config` mentions it or the variable exists. Without a model the app fails to start, so set it in every environment of your host too.
|
|
102
|
+
- Every env file it read that holds `OPENAI_API_KEY` or `CHAT_SECRET` is ignored by git.
|
|
103
|
+
|
|
104
|
+
**The running app**
|
|
105
|
+
|
|
106
|
+
`doctor` asks the app at `--url` to test its own setup. The request carries a 2-minute token signed with `CHAT_SECRET`, so only someone who knows the app's secret can run it. Without `CHAT_SECRET`, the live checks are skipped with a warning.
|
|
107
|
+
|
|
108
|
+
- The app is reachable, in development or production. In production, `doctor` warns when the state gallery is on, and when rate limits are kept in memory per instance.
|
|
109
|
+
- It reports when the route redirects (often `proxy.ts`), when there is no askdare route, when the app's `CHAT_SECRET` differs from yours, when the app is switched off with `ASKDARE_DISABLED` or has no `CHAT_SECRET`, and when there were too many runs.
|
|
110
|
+
|
|
111
|
+
**Each bot**, with the key and model of the running app:
|
|
112
|
+
|
|
113
|
+
| Check | Passes when | The key permission it names when it fails |
|
|
114
|
+
| --- | --- | --- |
|
|
115
|
+
| Model | The bot's model is available to the OpenAI project. A key that may not look up models gives a warning instead. | List models: Read |
|
|
116
|
+
| Responses | The key can create a 16-token response with the bot's model. | Responses API: Write |
|
|
117
|
+
| Conversations | The key can create and delete a conversation. | Write access to conversations |
|
|
118
|
+
| Knowledge base | Only with `knowledge`: the vector store exists in the project and hasn't expired. A store without searchable documents, or with documents still indexing or failed, gives a warning. | Vector Stores: Read |
|
|
119
|
+
| Voice input | Only with `voice`: the transcription model is available, and the key can transcribe one second of silence. | Model capabilities: Request |
|
|
120
|
+
| Attachments | Only with `attachments`: the key can upload and delete a small file. | Files: Write |
|
|
121
|
+
| Headers | `widget.js?bot=<id>` answers with JavaScript, and the chat page answers without a redirect, without `X-Frame-Options` and with exactly one `frame-ancestors`. It lists the sites that may embed the chat, and warns when something sets cookies. | |
|
|
122
|
+
| `--origin` | The site is the app itself or is in the bot's `security.allowedOrigins`. | |
|
|
123
|
+
| Timeout | The route's `maxDuration` is at least the bot's `limits.turnTimeoutMs` plus 15 seconds. Otherwise it's a warning. | |
|
|
124
|
+
|
|
125
|
+
`doctor` also says when OpenAI rejects the key, or when the project is out of credit. Each run makes a few small OpenAI requests per bot, and everything it creates is deleted straight away. The app accepts one run at a time, and at most 3 a minute.
|
|
126
|
+
|
|
127
|
+
### Output
|
|
128
|
+
|
|
129
|
+
`✓` is fine, `!` is a warning and `✗` is a problem. A line starting with `→` says what to do:
|
|
130
|
+
|
|
131
|
+
```text
|
|
132
|
+
askdare doctor
|
|
133
|
+
|
|
134
|
+
Project
|
|
135
|
+
✓ Node 24.11.0
|
|
136
|
+
✓ next 16.0.1, react 19.2.0, zod 4.1.12, @boldare/askdare 0.1.0
|
|
137
|
+
✓ app/api/chat/[...path]/route.ts (maxDuration 300 s)
|
|
138
|
+
|
|
139
|
+
Environment (.env.local)
|
|
140
|
+
✓ OPENAI_API_KEY is set.
|
|
141
|
+
✓ CHAT_SECRET is strong.
|
|
142
|
+
|
|
143
|
+
App at http://localhost:3000
|
|
144
|
+
✓ Reachable (development).
|
|
145
|
+
|
|
146
|
+
Bot "support"
|
|
147
|
+
✓ Model <model> is available.
|
|
148
|
+
✓ The key can create responses with <model>.
|
|
149
|
+
✓ The key can create and delete conversations.
|
|
150
|
+
✓ widget.js loads.
|
|
151
|
+
✗ https://www.example.com may not embed the chat: it isn't in the bot's security.allowedOrigins.
|
|
152
|
+
→ Add 'https://www.example.com' to security.allowedOrigins of bot "support" and deploy.
|
|
153
|
+
✓ Sites that may embed the chat: 'self'
|
|
154
|
+
|
|
155
|
+
1 problem, 0 warnings.
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### A deployed app
|
|
159
|
+
|
|
160
|
+
Point `--url` at the deployed app, and give `doctor` that app's `CHAT_SECRET` through `--env-file`:
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
npx @boldare/askdare doctor --url https://app.example.com --env-file .env.production.local
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
For an app that isn't on your machine, `doctor` skips the local `OPENAI_API_KEY` and `ASKDARE_MODEL` checks: the live checks test the deployed app's own key and model. Keep the file out of git; `init` already ignores `.env*.local`. See [Deployment](deployment.md).
|
|
167
|
+
|
|
168
|
+
## kb sync
|
|
169
|
+
|
|
170
|
+
Makes an OpenAI vector store mirror a folder of documents, for the bot's knowledge base. Re-running without changes does nothing.
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
npx @boldare/askdare kb sync knowledge
|
|
174
|
+
npx @boldare/askdare kb sync knowledge --dry-run
|
|
175
|
+
npx @boldare/askdare kb sync knowledge --store vs_… --env-file .env.production.local
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
| Option | Default | What it does |
|
|
179
|
+
| --- | --- | --- |
|
|
180
|
+
| `<folder>` | | The folder to mirror. Exactly one. |
|
|
181
|
+
| `--store <vs_…>` | `KB_VECTOR_STORE_ID` | The vector store to sync. Without either, `kb sync` creates a store, except with `--dry-run`. |
|
|
182
|
+
| `--name <name>` | `askdare knowledge` | The name of a newly created store. |
|
|
183
|
+
| `--env-file <path>` | `.env.local` and `.env` in the current folder, when present | Loads environment variables from this file. Variables in your shell win. |
|
|
184
|
+
| `--dry-run` | off | Shows what would change, without changing anything. |
|
|
185
|
+
| `-h`, `--help` | | Prints the usage of `kb sync`. |
|
|
186
|
+
|
|
187
|
+
It needs `OPENAI_API_KEY`. A store ID must look like `vs_…`.
|
|
188
|
+
|
|
189
|
+
What it changes in the store:
|
|
190
|
+
|
|
191
|
+
- Uploads new and edited files.
|
|
192
|
+
- Updates the title or URL of a file whose content didn't change.
|
|
193
|
+
- Deletes files that are no longer in the folder, and the old copy of each edited file, from the store and from the OpenAI project.
|
|
194
|
+
- Leaves files it didn't upload alone.
|
|
195
|
+
|
|
196
|
+
It reads `.md`, `.mdx`, `.txt`, `.pdf`, `.docx`, `.pptx`, `.html`, `.htm` and `.json`, and skips hidden files and `node_modules`. When it creates a store, it prints the line to add to your environment, `KB_VECTOR_STORE_ID=vs_…`. Titles, URLs and costs are in [Knowledge base](knowledge-base.md).
|
|
197
|
+
|
|
198
|
+
```text
|
|
199
|
+
Loaded .env.local.
|
|
200
|
+
+ pricing.md
|
|
201
|
+
+ guides/handbook.pdf
|
|
202
|
+
- old-plans.md
|
|
203
|
+
Synced: 2 added, 0 retagged, 1 removed, 12 unchanged.
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
A file that fails to upload is listed as `Failed: <path> (<reason>)`, and the rest of the sync goes on.
|
|
207
|
+
|
|
208
|
+
## Version and help
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
npx @boldare/askdare --version
|
|
212
|
+
npx @boldare/askdare --help
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
- `-v` or `--version` prints the installed version of `@boldare/askdare`.
|
|
216
|
+
- `-h`, `--help` or no command prints the usage of every command. `init --help` and `doctor --help` print the same text; `kb sync --help` prints the usage of `kb sync`.
|
|
217
|
+
|
|
218
|
+
## Exit codes
|
|
219
|
+
|
|
220
|
+
| Command | `0` | `1` |
|
|
221
|
+
| --- | --- | --- |
|
|
222
|
+
| `init` | Done. | The folder isn't a Next.js app with the App Router, or the bot ID is invalid. |
|
|
223
|
+
| `doctor` | No problems. Warnings don't count. | At least one `✗` problem. |
|
|
224
|
+
| `kb sync` | Done, or nothing to do. | At least one file failed to upload. |
|
|
225
|
+
| All | | An unknown command or option, or a missing or invalid value, such as a `--url` without `https` or no `OPENAI_API_KEY` for `kb sync`. The usage is printed with the error. Any other error is printed as `askdare: <message>`. |
|
|
226
|
+
|
|
227
|
+
`doctor` exits with `1` while there is a problem, so you can run it in CI after a deploy.
|
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
# Configuration reference
|
|
2
|
+
|
|
3
|
+
You configure askdare in two places:
|
|
4
|
+
|
|
5
|
+
| Where | What | Import |
|
|
6
|
+
| --- | --- | --- |
|
|
7
|
+
| `askdare.config.ts` | One `defineBot({ … })` per bot: its model, instructions, features, limits and look. | `@boldare/askdare` |
|
|
8
|
+
| `app/api/chat/[...path]/route.ts` | One `createChatHandler({ … })` for all bots: secrets, the counter store, the client IP and error reporting. | `@boldare/askdare/next` |
|
|
9
|
+
|
|
10
|
+
`defineBot` checks the whole definition when your app starts. It throws a `TypeError` that lists every problem, for example:
|
|
11
|
+
|
|
12
|
+
```text
|
|
13
|
+
[askdare] Invalid bot "Support": id use 1-48 lowercase letters, digits or dashes.
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
`createChatHandler` checks its own options when the route loads. It reads secrets on the first request, so `next build` never needs them.
|
|
17
|
+
|
|
18
|
+
## Example
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
// askdare.config.ts
|
|
22
|
+
import { defineBot } from '@boldare/askdare';
|
|
23
|
+
import { orderStatus } from './tools';
|
|
24
|
+
|
|
25
|
+
// Other sites that may embed the chat, comma-separated.
|
|
26
|
+
const allowedOrigins = (process.env.ASKDARE_ALLOWED_ORIGINS ?? '')
|
|
27
|
+
.split(',')
|
|
28
|
+
.map((origin) => origin.trim())
|
|
29
|
+
.filter(Boolean);
|
|
30
|
+
|
|
31
|
+
export const bots = [
|
|
32
|
+
defineBot({
|
|
33
|
+
id: 'support',
|
|
34
|
+
model: process.env.ASKDARE_MODEL ?? '',
|
|
35
|
+
instructions: ({ locale }) =>
|
|
36
|
+
`You are the assistant on our website. Answer briefly. Reply in the visitor's language; default to ${locale}.`,
|
|
37
|
+
tools: [orderStatus],
|
|
38
|
+
knowledge: process.env.KB_VECTOR_STORE_ID
|
|
39
|
+
? { vectorStoreId: process.env.KB_VECTOR_STORE_ID }
|
|
40
|
+
: undefined,
|
|
41
|
+
attachments: { pdf: false },
|
|
42
|
+
voice: process.env.ASKDARE_TRANSCRIBE_MODEL
|
|
43
|
+
? { model: process.env.ASKDARE_TRANSCRIBE_MODEL }
|
|
44
|
+
: undefined,
|
|
45
|
+
security: { allowedOrigins },
|
|
46
|
+
limits: { dailyTokens: 500_000, messagesPerVisitorPerMinute: 5 },
|
|
47
|
+
widget: {
|
|
48
|
+
title: 'Support',
|
|
49
|
+
locale: 'auto',
|
|
50
|
+
starters: ['Where is my order?'],
|
|
51
|
+
disclosure: { privacyUrl: '/privacy' },
|
|
52
|
+
theme: 'aura',
|
|
53
|
+
},
|
|
54
|
+
}),
|
|
55
|
+
];
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
// app/api/chat/[...path]/route.ts
|
|
60
|
+
import { createChatHandler } from '@boldare/askdare/next';
|
|
61
|
+
import { upstashStore } from '@boldare/askdare-upstash';
|
|
62
|
+
import { bots } from '@/askdare.config';
|
|
63
|
+
|
|
64
|
+
// Must be a literal in this file; keep it above every bot's limits.turnTimeoutMs.
|
|
65
|
+
export const maxDuration = 300;
|
|
66
|
+
|
|
67
|
+
export const { GET, POST, DELETE } = createChatHandler({
|
|
68
|
+
bots,
|
|
69
|
+
limits: { store: upstashStore(), globalDailyTokens: 1_000_000 },
|
|
70
|
+
});
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## `defineBot`
|
|
74
|
+
|
|
75
|
+
`defineBot(definition: BotDefinition): Bot`. The types of every field are exported from `@boldare/askdare`: `BotDefinition`, `BotContext`, `BotLimits`, `BotSecurity`, `KnowledgeConfig`, `AttachmentConfig`, `VoiceConfig` and `WidgetConfig`.
|
|
76
|
+
|
|
77
|
+
| Field | Type | Default | Rules |
|
|
78
|
+
| --- | --- | --- | --- |
|
|
79
|
+
| `id` | `string` | required | 1–48 lowercase letters, digits or dashes, starting with a letter or digit. Used in the script tag (`widget.js?bot=<id>`) and in routes. Unique within a handler. |
|
|
80
|
+
| `model` | `string` | required | An OpenAI model ID, not empty. askdare never picks a model for you. |
|
|
81
|
+
| `instructions` | `string \| (context: BotContext) => string` | required | A non-empty string, or a function. See [Instructions](#instructions). |
|
|
82
|
+
| `tools` | `Tool[]` | `[]` | Created with `defineTool`. At most 64, with unique names. The name `file_search` is reserved. Keep it under about 20. See [Tools](tools.md). |
|
|
83
|
+
| `pageContext` | `boolean` | `false` | Accepts a page description from `askdare('configure', { context })` and passes it to the model with the visitor's message. Anyone can set it, so it gets no more trust than what visitors type. See [Styling](styling.md#3-page-javascript). |
|
|
84
|
+
| `knowledge` | `KnowledgeConfig` | off | Documents the model searches and cites. See [Knowledge](#knowledge). |
|
|
85
|
+
| `attachments` | `AttachmentConfig` | off | Images and PDFs from visitors. See [Attachments](#attachments). |
|
|
86
|
+
| `voice` | `VoiceConfig` | off | Dictation. See [Voice](#voice). |
|
|
87
|
+
| `security` | `BotSecurity` | own origin only | Sites that may embed the chat. See [Security](#security). |
|
|
88
|
+
| `limits` | `BotLimits` | see [Limits](#limits) | Rates and budgets. |
|
|
89
|
+
| `widget` | `WidgetConfig` | see [Widget](#widget) | Texts, language and look. |
|
|
90
|
+
|
|
91
|
+
### Instructions
|
|
92
|
+
|
|
93
|
+
`instructions` is a string or a function. The function runs for each request and receives a `BotContext`:
|
|
94
|
+
|
|
95
|
+
| Field | Type | Meaning |
|
|
96
|
+
| --- | --- | --- |
|
|
97
|
+
| `locale` | `string` | The validated BCP 47 tag the widget chose. |
|
|
98
|
+
| `identity` | `VisitorIdentity \| undefined` | The signed-in visitor, verified with `IDENTITY_SECRET`. `undefined` for anonymous visitors. See [Signed-in visitors](identity.md). |
|
|
99
|
+
|
|
100
|
+
Put no secrets in instructions. Assume visitors can make the model reveal them.
|
|
101
|
+
|
|
102
|
+
### Knowledge
|
|
103
|
+
|
|
104
|
+
| Field | Type | Default | Rules |
|
|
105
|
+
| --- | --- | --- | --- |
|
|
106
|
+
| `vectorStoreId` | `string` | required | A vector store ID: `vs_` followed by letters, digits, `_` or `-`. Usually `process.env.KB_VECTOR_STORE_ID`. |
|
|
107
|
+
| `maxResults` | `number` | `5` | Whole number, 1–50. Chunks the model reads per search. More costs more input tokens. |
|
|
108
|
+
|
|
109
|
+
Fill the store with `npx @boldare/askdare kb sync`. See [Knowledge base](knowledge-base.md).
|
|
110
|
+
|
|
111
|
+
### Attachments
|
|
112
|
+
|
|
113
|
+
Absent, attachments are off. `attachments: {}` turns them on with the defaults.
|
|
114
|
+
|
|
115
|
+
| Field | Type | Default | Rules |
|
|
116
|
+
| --- | --- | --- | --- |
|
|
117
|
+
| `images` | `boolean` | `true` | PNG, JPEG, WebP and GIF. |
|
|
118
|
+
| `pdf` | `boolean` | `true` | PDF documents. |
|
|
119
|
+
| `maxBytes` | `number` | `4_000_000` | Largest file in bytes, 1,000–20,000,000. The default fits Vercel's 4.5 MB request limit. |
|
|
120
|
+
| `maxPerMessage` | `number` | `3` | Files per message, 1–10. |
|
|
121
|
+
| `maxPdfPages` | `number` | `50` | Longest PDF in pages, 1–500. |
|
|
122
|
+
|
|
123
|
+
`images` and `pdf` can't both be `false`. See [Attachments](attachments.md).
|
|
124
|
+
|
|
125
|
+
### Voice
|
|
126
|
+
|
|
127
|
+
Absent, dictation is off.
|
|
128
|
+
|
|
129
|
+
| Field | Type | Default | Rules |
|
|
130
|
+
| --- | --- | --- | --- |
|
|
131
|
+
| `model` | `string` | required | An OpenAI transcription model, not empty. askdare never picks one. |
|
|
132
|
+
| `maxSeconds` | `number` | `60` | Longest dictation in seconds, 5–300. Recording stops by itself at this point. |
|
|
133
|
+
|
|
134
|
+
See [Voice input](voice-input.md).
|
|
135
|
+
|
|
136
|
+
### Security
|
|
137
|
+
|
|
138
|
+
`security.allowedOrigins` lists the other sites that may embed the chat. The app's own origin is always allowed. The browser enforces the list through the `frame-ancestors` policy of the chat page ([ADR-0004](https://github.com/boldare/askdare/blob/main/docs/adr/0004-threat-model.md)).
|
|
139
|
+
|
|
140
|
+
Each entry must be an origin exactly as a browser writes it:
|
|
141
|
+
|
|
142
|
+
- `https://`, a host name and, only if it isn't the default, a port: `https://www.example.com`, `https://shop.example.com:8443`.
|
|
143
|
+
- `http://` only for `localhost`, `127.0.0.1`, `[::1]` and names that end in `.localhost`.
|
|
144
|
+
- No path, no trailing slash, no query and no wildcard (`*`).
|
|
145
|
+
- No default port (`:443`) and no capital letters.
|
|
146
|
+
- International domain names in their `xn--` form.
|
|
147
|
+
|
|
148
|
+
The list holds at most 50 origins. Duplicates are dropped. A wrong entry fails with "must be an https origin like https://www.example.com (no path or wildcard; http only for localhost)".
|
|
149
|
+
|
|
150
|
+
To check that a site may embed the chat, run `npx @boldare/askdare doctor --origin https://www.example.com`.
|
|
151
|
+
|
|
152
|
+
### Limits
|
|
153
|
+
|
|
154
|
+
Limits bound what visitors can cost ([ADR-0004](https://github.com/boldare/askdare/blob/main/docs/adr/0004-threat-model.md)). Every limit applies per bot, and every value is a whole number.
|
|
155
|
+
|
|
156
|
+
| Limit | Default | What it counts | Allowed |
|
|
157
|
+
| --- | --- | --- | --- |
|
|
158
|
+
| `maxOutputTokens` | `1024` | Tokens in one model response | 16–128,000 |
|
|
159
|
+
| `maxInputChars` | `4000` | Characters in one visitor message | 1–100,000 |
|
|
160
|
+
| `maxSteps` | `5` | Model calls per turn, tool round trips included | 1–20 |
|
|
161
|
+
| `turnTimeoutMs` | `120_000` | Milliseconds a whole turn may take. Keep it below the route's `maxDuration`. | 1,000–900,000 |
|
|
162
|
+
| `maxTurns` | `50` | Turns per conversation, before the visitor must start a new one | 1–1,000 |
|
|
163
|
+
| `messagesPerVisitorPerMinute` | `10` | Messages per visitor per minute | 1–1,000 |
|
|
164
|
+
| `messagesPerIpPerMinute` | `30` | Messages per client IP per minute, across visitors | 1–10,000 |
|
|
165
|
+
| `sessionsPerIpPerHour` | `30` | New sessions per client IP per hour. Renewals by returning visitors count separately. | 1–10,000 |
|
|
166
|
+
| `conversationsPerIpPerHour` | `20` | New conversations per client IP per hour | 1–10,000 |
|
|
167
|
+
| `dailyTokens` | `2_000_000` | Input plus output tokens per UTC day, across all visitors | 1,000 or more |
|
|
168
|
+
| `dailyTokensPerIp` | `200_000` | Input plus output tokens per client IP per UTC day | 100 or more |
|
|
169
|
+
| `uploadsPerVisitorPerHour` | `20` | File uploads per visitor per hour | 1–1,000 |
|
|
170
|
+
| `uploadsPerIpPerHour` | `60` | File uploads per client IP per hour, across visitors | 1–10,000 |
|
|
171
|
+
| `uploadsPerDay` | `500` | File uploads per UTC day, across all visitors | 1–1,000,000 |
|
|
172
|
+
| `transcriptionsPerVisitorPerHour` | `30` | Dictations per visitor per hour | 1–1,000 |
|
|
173
|
+
| `voiceSecondsPerDay` | `3600` | Seconds of speech transcribed per UTC day, across all visitors | 60 or more |
|
|
174
|
+
| `voiceSecondsPerIpPerDay` | `600` | Seconds of speech transcribed per client IP per UTC day | 60 or more |
|
|
175
|
+
|
|
176
|
+
What happens at a limit:
|
|
177
|
+
|
|
178
|
+
- **Rates** answer `rate_limited`, with a `Retry-After` header. So does `maxTurns`, which asks the visitor to start a new conversation.
|
|
179
|
+
- **Token budgets** answer `budget_exceeded` until midnight UTC. Before each model call, askdare reserves an estimate against `dailyTokens`, `dailyTokensPerIp` and the handler's `globalDailyTokens`. The estimate covers the conversation so far, the new message, `maxOutputTokens`, attached files and file search results. A bot with tools reserves the next call before each tool round, and answers without tools when a budget can't hold it. When the turn ends, the real usage replaces the estimate. A turn that fails or is cut off keeps the estimate charged.
|
|
180
|
+
- **Speech budgets** reserve `voice.maxSeconds` per dictation, then charge the duration OpenAI reports. When one is spent, dictation answers `budget_exceeded` until midnight UTC.
|
|
181
|
+
- **`maxInputChars`** rejects a longer message with `invalid_input`.
|
|
182
|
+
|
|
183
|
+
A few limits are fixed:
|
|
184
|
+
|
|
185
|
+
| What | Limit |
|
|
186
|
+
| --- | --- |
|
|
187
|
+
| Session renewals by a returning visitor | 12 per visitor per hour |
|
|
188
|
+
| History reads and deletions | 20 per visitor per minute |
|
|
189
|
+
| Dictations transcribing at once | 2 per client IP and 10 per bot |
|
|
190
|
+
| `npx @boldare/askdare doctor` runs | 3 per minute for the whole app |
|
|
191
|
+
|
|
192
|
+
Per-IP limits depend on reading the client IP correctly. See [`trustProxy`](#trustproxy).
|
|
193
|
+
|
|
194
|
+
### Widget
|
|
195
|
+
|
|
196
|
+
Everything in `widget` is sent to browsers. Never put secrets in it. [Styling](styling.md) covers texts, languages and your own CSS. [Themes](themes.md) covers presets, tokens and variants.
|
|
197
|
+
|
|
198
|
+
| Field | Type | Default | Rules |
|
|
199
|
+
| --- | --- | --- | --- |
|
|
200
|
+
| `title` | `string` | `'Assistant'` | 1–80 characters. Shortcut for `texts['header.title']`. |
|
|
201
|
+
| `subtitle` | `string` | none | 1–120 characters. Shown under the title. |
|
|
202
|
+
| `greeting` | `string` | `'Hi! How can I help?'` | 1–500 characters. Shortcut for `texts['welcome.greeting']`. `**words**` show in bold. |
|
|
203
|
+
| `placeholder` | `string` | `'Type your message…'` | 1–120 characters. Shortcut for `texts['composer.placeholder']`. |
|
|
204
|
+
| `launcherLabel` | `string` | `'Ask us'` | 1–40 characters. Text on the launcher. Shortcut for `texts['launcher.label']`. |
|
|
205
|
+
| `locale` | `string` | `'en'` | `'auto'`, or a BCP 47 tag such as `en` or `pl-PL`. Also the default answer language. `'auto'` uses the language of the embedding page, else the visitor's browser. |
|
|
206
|
+
| `texts` | `Partial<Record<TextKey, string>>` | `{}` | Any widget text by its key, 1–300 characters each. |
|
|
207
|
+
| `disclosure` | `false \| { privacyUrl?: string }` | shown, no link | The note that visitors talk to an AI. `privacyUrl` is an `https://` URL or a path on your app, such as `/privacy`. `false` hides the note. |
|
|
208
|
+
| `starters` | `string[]` | `[]` | Suggested questions on the welcome screen. Up to 4, 1–80 characters each. |
|
|
209
|
+
| `avatar` | `string` | none | A path on your app, such as `/avatar.png`, or a PNG, JPEG, WebP or GIF `data:` URL of up to 90,000 base64 characters. |
|
|
210
|
+
| `theme` | `PresetName \| ThemeDefinition` | `'aura'` | `'halo'`, `'aura'`, `'sage'`, `'care'`, or a theme from `defineTheme`. |
|
|
211
|
+
| `colorScheme` | `'auto' \| 'light' \| 'dark'` | `'auto'` | `'auto'` follows the visitor's system setting. |
|
|
212
|
+
| `css` | `string` | none | Your own CSS, up to 50,000 characters. It loads after the theme. |
|
|
213
|
+
|
|
214
|
+
The text defaults come from the built-in dictionary of the configured language. The table shows the English ones, which `'auto'` also uses. Texts ship in English and Polish.
|
|
215
|
+
|
|
216
|
+
## `createChatHandler`
|
|
217
|
+
|
|
218
|
+
`createChatHandler(options: ChatHandlerOptions): ChatHandlers` returns `GET`, `POST` and `DELETE` for the catch-all route. Its types are exported from `@boldare/askdare/next`: `ChatHandlerOptions`, `ChatHandlerLimitsOptions`, `ChatErrorReport`, `TrustProxy`, `ChatHandlers` and `RouteHandler`.
|
|
219
|
+
|
|
220
|
+
| Option | Type | Default | Notes |
|
|
221
|
+
| --- | --- | --- | --- |
|
|
222
|
+
| `bots` | `readonly Bot[]` | required | At least one bot. IDs must be unique. |
|
|
223
|
+
| `basePath` | `string` | `'/api/chat'` | Path of the catch-all route. One or more segments of letters, digits, `.`, `_`, `~` or `-`, each after a `/`. No trailing slash. |
|
|
224
|
+
| `openaiApiKey` | `string` | `process.env.OPENAI_API_KEY` | The OpenAI API key. |
|
|
225
|
+
| `secret` | `string` | `process.env.CHAT_SECRET` | Encrypts session tokens and attachment handles. At least 32 characters, with at least 12 different ones. Use `openssl rand -base64 32`. |
|
|
226
|
+
| `previousSecrets` | `readonly string[]` | `process.env.CHAT_SECRET_PREVIOUS`, split at commas | Former secrets that still verify tokens during a rotation. Each follows the same rule as `secret`. See [Going public](going-public.md#2-secrets-and-environment). |
|
|
227
|
+
| `identitySecret` | `string` | `process.env.IDENTITY_SECRET` | Checks identity tokens from your site's backend. Same rule as `secret`, and it must differ from it. Without one, identity is off. See [Signed-in visitors](identity.md). |
|
|
228
|
+
| `limits` | `ChatHandlerLimitsOptions` | see [Handler limits](#handler-limits) | The counter store, the client IP and a budget across bots. |
|
|
229
|
+
| `onError` | `(report: ChatErrorReport) => void` | one `console.error` line | Your error reporting. See [`onError`](#onerror). |
|
|
230
|
+
| `gallery` | `boolean` | `false` | Serves the state gallery. See [`gallery`](#gallery). |
|
|
231
|
+
|
|
232
|
+
A wrong `basePath`, a duplicate bot ID or an empty `bots` list throws a `TypeError` when the route loads. A missing key or a missing or weak secret doesn't stop the route from loading. Instead, requests that need it answer `bot_unavailable`, and `onError` receives the reason. In development (`next dev`), the chat itself says what to fix.
|
|
233
|
+
|
|
234
|
+
### Handler limits
|
|
235
|
+
|
|
236
|
+
`limits` in `createChatHandler` applies to every bot of the handler.
|
|
237
|
+
|
|
238
|
+
| Option | Type | Default | Notes |
|
|
239
|
+
| --- | --- | --- | --- |
|
|
240
|
+
| `store` | `RateLimitStore` | in memory, per process | Counter storage for rate limits and budgets. With more than one instance, use a shared store such as `@boldare/askdare-upstash`. See [Custom rate-limit store](custom-store.md). |
|
|
241
|
+
| `trustProxy` | `'auto' \| 'vercel' \| 'heroku' \| number` | `'auto'` | How to read the client IP behind your proxy. |
|
|
242
|
+
| `globalDailyTokens` | `number` | unlimited | Input plus output tokens per UTC day, across all bots of this handler. Reaching it answers `budget_exceeded`. |
|
|
243
|
+
|
|
244
|
+
### `trustProxy`
|
|
245
|
+
|
|
246
|
+
| Value | Client IP taken from |
|
|
247
|
+
| --- | --- |
|
|
248
|
+
| `'auto'` | Same as `'vercel'` when the `VERCEL` environment variable is set. Otherwise one trusted proxy, like `'heroku'`. |
|
|
249
|
+
| `'vercel'` | The `x-real-ip` header, else the first entry of `x-forwarded-for`. Vercel overwrites both. |
|
|
250
|
+
| `'heroku'` | The last entry of `x-forwarded-for`, which Heroku's router appends. Entries to its left may be spoofed. |
|
|
251
|
+
| a number `n` | The `n`-th entry from the right of `x-forwarded-for`, for `n` trusted proxies. Values below 1 count as 1. |
|
|
252
|
+
|
|
253
|
+
- IPv6 addresses count per /64 network.
|
|
254
|
+
- Without a usable address, the request counts as `unknown`, and all such requests share the per-IP limits.
|
|
255
|
+
- Never expose `next start` without a proxy in front. Clients then choose their own IP.
|
|
256
|
+
|
|
257
|
+
See [Deployment](deployment.md#behind-another-proxy-or-cdn) for setups with a CDN.
|
|
258
|
+
|
|
259
|
+
### `onError`
|
|
260
|
+
|
|
261
|
+
`onError` receives server errors (status 500 and up), any error that ends an answer while it streams, and once a day a `budget_exceeded` report when a bot's or the global budget runs out (never a single visitor's). Use it for your monitoring. It never receives message contents or API keys. Don't throw from it.
|
|
262
|
+
|
|
263
|
+
```ts
|
|
264
|
+
import * as Sentry from '@sentry/nextjs';
|
|
265
|
+
|
|
266
|
+
export const { GET, POST, DELETE } = createChatHandler({
|
|
267
|
+
bots,
|
|
268
|
+
onError: ({ code, message, cause }) => {
|
|
269
|
+
Sentry.captureException(cause ?? new Error(message), { tags: { askdare: code } });
|
|
270
|
+
},
|
|
271
|
+
});
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
Without `onError`, askdare logs one `console.error` line with the code, what went wrong and a pointer to the troubleshooting guide.
|
|
275
|
+
|
|
276
|
+
`ChatErrorReport`:
|
|
277
|
+
|
|
278
|
+
| Field | Type | Meaning |
|
|
279
|
+
| --- | --- | --- |
|
|
280
|
+
| `code` | string union | The stable code the widget also receives. See the list below. |
|
|
281
|
+
| `message` | `string` | What went wrong, for you: a missing key, a model the project can't use. Never shown to visitors. |
|
|
282
|
+
| `cause` | `unknown` | The underlying error, with any API key cut down to its prefix. Optional. |
|
|
283
|
+
|
|
284
|
+
`code` is one of `invalid_input`, `unauthorized`, `session_expired`, `not_found`, `rate_limited`, `budget_exceeded`, `bot_unavailable`, `content_blocked` (reserved, not sent yet), `timeout`, `upstream_error` or `internal_error`. The codes at status 500 and up are `internal_error` (500), `upstream_error` (502), `bot_unavailable` (503) and `timeout` (504). [Protocol](protocol.md#error-codes) explains each one.
|
|
285
|
+
|
|
286
|
+
### `gallery`
|
|
287
|
+
|
|
288
|
+
`gallery: true` serves the state gallery. `<basePath>/embed/<bot>?gallery=<state>&preset=<preset>&scheme=<scheme>` then renders a fixed state of the widget without calling the model.
|
|
289
|
+
|
|
290
|
+
| Parameter | Values |
|
|
291
|
+
| --- | --- |
|
|
292
|
+
| `gallery` | `welcome`, `conversation`, `streaming`, `attachments`, `error`, `listening` |
|
|
293
|
+
| `preset` | `halo`, `aura`, `sage`, `care` |
|
|
294
|
+
| `scheme` | `auto`, `light`, `dark` |
|
|
295
|
+
|
|
296
|
+
It is for development and visual tests. Leave it off in production: `npx @boldare/askdare doctor` warns when it is on there.
|
|
297
|
+
|
|
298
|
+
### The route file
|
|
299
|
+
|
|
300
|
+
The route needs one setting outside `createChatHandler`:
|
|
301
|
+
|
|
302
|
+
```ts
|
|
303
|
+
export const maxDuration = 300;
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
It must be a literal in the route file. It is the longest a streamed answer may take, so keep it above every bot's `limits.turnTimeoutMs`.
|
|
307
|
+
|
|
308
|
+
## Environment variables
|
|
309
|
+
|
|
310
|
+
### Read by askdare
|
|
311
|
+
|
|
312
|
+
| Variable | Read by | Meaning |
|
|
313
|
+
| --- | --- | --- |
|
|
314
|
+
| `OPENAI_API_KEY` | `createChatHandler`, `npx @boldare/askdare kb sync`, `npx @boldare/askdare doctor` | The OpenAI API key. Default for `openaiApiKey`. Required. |
|
|
315
|
+
| `CHAT_SECRET` | `createChatHandler`, `npx @boldare/askdare doctor` | Default for `secret`. Required. `doctor` signs its request to your app with it. |
|
|
316
|
+
| `CHAT_SECRET_PREVIOUS` | `createChatHandler`, `npx @boldare/askdare doctor` | Former secrets, comma-separated. Default for `previousSecrets`. |
|
|
317
|
+
| `IDENTITY_SECRET` | `createChatHandler`, `npx @boldare/askdare doctor` | Default for `identitySecret`. Optional: without it, identity is off. |
|
|
318
|
+
| `ASKDARE_DISABLED` | `createChatHandler` | The kill switch. When it is exactly `true`, every bot answers `bot_unavailable`. askdare reads it on every request. |
|
|
319
|
+
| `KB_VECTOR_STORE_ID` | `npx @boldare/askdare kb sync` | The vector store to sync, unless you pass `--store`. |
|
|
320
|
+
| `ASKDARE_MODEL` | `npx @boldare/askdare doctor` | `doctor` reports it when it is missing or empty and your `askdare.config` uses it. The server doesn't read it; your config does. |
|
|
321
|
+
| `VERCEL` | `createChatHandler` | Set by Vercel. With `trustProxy: 'auto'`, askdare then reads the client IP the Vercel way. |
|
|
322
|
+
| `NODE_ENV` | `createChatHandler`, `defineBot` | Set by Next.js. With `production`, a store that isn't shared logs a warning, and theme contrast warnings are off. With `development`, the chat explains setup errors such as a missing key. |
|
|
323
|
+
|
|
324
|
+
The CLI reads `.env.local` and `.env`, or the file you pass with `--env-file`.
|
|
325
|
+
|
|
326
|
+
### Conventions
|
|
327
|
+
|
|
328
|
+
The library doesn't read these. `npx @boldare/askdare init`, the starter and the demo site use them in `askdare.config.ts` and the route.
|
|
329
|
+
|
|
330
|
+
| Variable | Used for | Notes |
|
|
331
|
+
| --- | --- | --- |
|
|
332
|
+
| `ASKDARE_MODEL` | `model: process.env.ASKDARE_MODEL ?? ''` | `init` writes it to `.env.local` (from `--model`). Set it in every environment that builds or runs the app; on Vercel, in Production and Preview. |
|
|
333
|
+
| `ASKDARE_TRANSCRIBE_MODEL` | `voice.model` | |
|
|
334
|
+
| `ASKDARE_ALLOWED_ORIGINS` | `security.allowedOrigins` | Comma-separated, as in the [example](#example). |
|
|
335
|
+
| `KB_VECTOR_STORE_ID` | `knowledge.vectorStoreId` | The first `npx @boldare/askdare kb sync` creates the store and prints its ID. |
|
|
336
|
+
| `ASKDARE_GALLERY` | `gallery: process.env.ASKDARE_GALLERY === '1'` | |
|
|
337
|
+
|
|
338
|
+
### `@boldare/askdare-upstash`
|
|
339
|
+
|
|
340
|
+
| Variable | Notes |
|
|
341
|
+
| --- | --- |
|
|
342
|
+
| `UPSTASH_REDIS_REST_URL` | Default for the `url` option of `upstashStore()`. |
|
|
343
|
+
| `UPSTASH_REDIS_REST_TOKEN` | Default for the `token` option of `upstashStore()`. |
|
|
344
|
+
| `KV_REST_API_URL` | Used when `UPSTASH_REDIS_REST_URL` isn't set. |
|
|
345
|
+
| `KV_REST_API_TOKEN` | Used when `UPSTASH_REDIS_REST_TOKEN` isn't set. |
|
|
346
|
+
|
|
347
|
+
The Vercel Marketplace sets the `UPSTASH_` names. Databases from the older Vercel KV have the `KV_` names. The store reads them on first use, not at import.
|
|
348
|
+
|
|
349
|
+
## Where to go next
|
|
350
|
+
|
|
351
|
+
| To… | Read |
|
|
352
|
+
| --- | --- |
|
|
353
|
+
| Count limits in your own database | [Custom rate-limit store](custom-store.md) |
|
|
354
|
+
| Set secrets and limits before launch | [Going public](going-public.md) |
|
|
355
|
+
| Deploy to Vercel or Heroku | [Deployment](deployment.md) |
|
|
356
|
+
| Change texts, language or CSS | [Styling](styling.md) |
|
|
357
|
+
| Pick or adjust a theme | [Themes](themes.md) |
|