---
title: "Configuration"
description: "Every deploy setting, Worker binding and secret, and the deploy token permissions."
---

## Deploy-time settings (`alchemy.run.ts`)

Read from the environment of `alchemy deploy`, usually `.env.local` (gitignored) through `--env-file`.

| Variable | Default | Purpose |
| --- | --- | --- |
| `CLOUDFLARE_API_TOKEN` | — (required) | Deploy token, see permissions below |
| `CLOUDFLARE_ACCOUNT_ID` | — (required) | Target account |
| `EMITKIT_DOMAIN` | `emitkit.com` | Zone that serves every hostname |
| `EMITKIT_SITE_HOST` | `$EMITKIT_DOMAIN` | Marketing hostname |
| `EMITKIT_APP_HOST` | `app.$EMITKIT_DOMAIN` | PWA + auth + RPC hostname |
| `EMITKIT_API_HOST` | `api.$EMITKIT_DOMAIN` | Public API hostname |
| `EMITKIT_REDIRECT_HOSTS` | `www.$EMITKIT_DOMAIN` | Comma-separated hosts that 301 to the site; empty for none |
| `EMITKIT_DOCS_HOST` | `docs.$EMITKIT_DOMAIN` | Old docs hostname: attached to the Worker and 301'd to `<site>/docs/<same path>`; empty for none |
| `EMITKIT_MAIL_DOMAIN` | `mail.$EMITKIT_DOMAIN` | Email Sending subdomain (DKIM/SPF records are created automatically) |
| `EMITKIT_EMAIL_FROM` | `noreply@$EMITKIT_MAIL_DOMAIN` | Sender of auth and invitation email |
| `EMITKIT_WORKER_NAME` | `emitkit` | Worker, D1 (`<name>-db`) and queue (`<name>-events`) names |
| `EMITKIT_AE_DATASET` | `emitkit_events` | Analytics Engine dataset |
| `EMITKIT_ALLOW_SIGNUP` | `true` | `false` disables self-service sign-up (invitations still work) |
| `EMITKIT_VAPID_SUBJECT` | `mailto:hi@$EMITKIT_DOMAIN` | Contact for push services |
| `EMITKIT_PARSEW_KEY` | empty | Publishable Parsew key (`pk_…`) for brand icons (projects by their website, users by their email domain, links). It is public: the browser puts it in image URLs, so restrict it to the app origin in the Parsew dashboard. Empty shows initials and dots instead. Locally, set `PARSEW_PUBLISHABLE_KEY` in `.dev.vars`. |

### Build-time settings (`pnpm build`)

| Variable | Default | Purpose |
| --- | --- | --- |
| `DOCS_SITE_URL` | `https://emitkit.com` | Origin used for the docs' canonical URLs, sitemap and Open Graph images (`docs/blume.config.ts`). Set it to your site origin when self-hosting. The docs are always served at `/docs` on the site host. |

### Deploy token permissions

Create a custom account API token scoped to the account and the zone:

- Account: Workers Scripts Edit, D1 Edit, Queues Edit, Workers AI Read, Email Sending Edit, Account API Tokens Edit, Account Settings Read, Secrets Store Edit.
- Zone: Zone Read, DNS Edit, Workers Routes Edit, SSL and Certificates Edit, Single Redirect Edit.

"Account API Tokens Edit" lets the stack mint the Worker's read-only Analytics Engine token (`<name>-analytics-read`). "Secrets Store Edit" lets Alchemy reach its state store, whose bearer token lives in the account Secrets Store.

## Worker variables and secrets

The stack sets these on the Worker. You don't set them yourself.

| Binding / variable | Kind | Source |
| --- | --- | --- |
| `DB` | D1 | `Cloudflare.D1.Database`, migrations from `./migrations` |
| `EVENTS_DATASET` | Analytics Engine | `Cloudflare.AnalyticsEngine.Dataset` |
| `EVENTS_QUEUE` | Queue producer (+ consumer) | `Cloudflare.Queues.Queue` |
| `EMAIL` | `send_email` | restricted to `EMITKIT_EMAIL_FROM` |
| `AI` | Workers AI | optional, emoji suggestions |
| `ASSETS` | static assets | `./dist/client` |
| `APP_URL`, `API_URL`, `SITE_URL` | text | from the hosts above |
| `EVENT_STORE` | text | `analytics-engine` in production |
| `ALLOW_SIGNUP`, `EMAIL_FROM`, `VAPID_SUBJECT`, `AE_DATASET`, `CF_ACCOUNT_ID` | text | from settings |
| `VAPID_PUBLIC_KEY` | text | derived from the generated VAPID private key |
| `BETTER_AUTH_SECRET` | secret | `Alchemy.Random` (32 bytes), kept in Alchemy state |
| `VAPID_PRIVATE_KEY` | secret | `Alchemy.Random` (P-256 scalar), kept in Alchemy state |
| `CF_ANALYTICS_TOKEN` | secret | account token minted by the stack (Account Analytics Read) |

The Worker validates its configuration at startup (`src/server/platform/config.ts`). A missing or malformed value fails every request with a logged `ConfigError`.

## Local development (`wrangler.jsonc` + `.dev.vars`)

`wrangler.jsonc` declares the same bindings for `@cloudflare/vite-plugin` with `EVENT_STORE=d1` and `*.localhost:5173` URLs. Local secrets go in the gitignored `.dev.vars`:

```
BETTER_AUTH_SECRET=<at least 32 random characters>
VAPID_PRIVATE_KEY=<base64url P-256 private scalar>
```

`VAPID_PUBLIC_KEY` in `wrangler.jsonc` must be the matching public key. [Development](/self-hosting/development) shows how to generate a pair.
