---
title: "Cloudflare"
description: "Redeploy and operate Chickpea on your own Cloudflare account with the guarded deploy wrapper."
---

> Documentation Index
> Fetch the complete documentation index at: https://docs.chickpea.co/llms.txt
> Use this file to discover all available pages before exploring further.

# Cloudflare

`npm run deploy` is Chickpea's guarded Cloudflare deploy: it builds the Worker, checks the generated artifact, preserves the deployed database, applies migrations, uploads, and confirms readiness. Where the Deploy to Cloudflare button runs that same wrapper for your first deployment, this page is what you run afterwards from a clone. It covers the checks the wrapper enforces, the two deployment profiles, the bindings and the cron a deployment carries, the secrets you add after, and what updating does to state.

## Prerequisites

- **A Cloudflare account on Workers Paid.** The current bundle's compressed upload exceeds the Free plan's 3 MiB Worker limit. The coding sandbox needs Workers Paid as well.
- **A clone of the repository at the release you intend to run,** with Node 24.x, minimum 24.20.0, selected with `nvm install && nvm use` and `npm ci` already done.
- **A Cloudflare credential that may list Worker secrets.** The wrapper reads them before it changes anything and stops with `The deploy credential must allow secret listing before Chickpea can deploy safely.`

The repository's `.nvmrc` selects Node 24.20.0 for Cloudflare builds. An explicit
`NODE_VERSION` build override should match that pin. These settings choose the
build tool, while the Worker runs in workerd with its existing compatibility date
and flags. Node updates do not replace workerd or real Slack acceptance.
See [Cloudflare's build image documentation](https://developers.cloudflare.com/workers/ci-cd/builds/build-image/).

## Redeploy from a clone

1. **Check out the release and install its lockfile**

```sh
git checkout <release-tag>
npm ci
```
2. **Run the guarded deploy**

```sh
npm run deploy
```

   For the optional coding sandbox, run `npm run deploy:sandbox` instead. The wrapper builds the Cloudflare artifact first. Pass `--skip-build` only when you have already run `npm run build` and want that exact artifact, and `--preflight-only` to run every check and deploy nothing.
3. **Watch the progress lines**

   A healthy redeploy prints, in order, `Preserving the deployed AUTH_DB database...`, `Applying reviewed Better Auth migrations to AUTH_DB...`, `Verified the exact fresh Better Auth schema in AUTH_DB...`, then wrangler's own output, then `Waiting for the current Worker and Slack gateway version...`. A first deploy provisions the database instead of preserving it.

   Any check that fails prints one line naming what was missing or unsafe and exits without uploading.
4. **Copy the new setup link**

   Every deploy ends with a block headed `PRIVATE SETUP LINK`. The URL is `/admin/setup#setup=<capability>` on the deployed origin and expires 24 hours after the deploy. Each deploy mints a fresh capability and rotates the stored digest, so links printed by earlier deploys stop working.

   When the deploy prints no `workers.dev` URL, the block is headed `PRIVATE SETUP PATH` and carries the path and fragment alone. Open it on the origin your deployment answers on.

## Verify

The wrapper verifies readiness for you. It posts to `/internal/deployment/ready` on the deployed origin with the version it uploaded, retries every two seconds for up to six minutes, and prints `Verified current-version deployment readiness.` on a 204. If it never sees one, it exits non-zero with `Worker uploaded, but current-version readiness was not confirmed. The Slack gateway may still be serving older code.`

A successful upload is not acceptance. Record the account, Worker name, deployed version, traffic allocation, bindings, and source commit, then confirm a signed-in Admin and a real Slack request routed through that deployment.

## What the guarded deploy checks

A bare `wrangler deploy` runs none of the following. Deploy through the wrapper.

- **The command line.** `--config`, `-c`, and `--name` are refused, because Chickpea has to inspect and mutate the same Worker it deploys.
- **The Slack manifest.** `slack-app-manifest.json` must declare `features.agent_view`, and may not declare `assistant_view` alongside it.
- **Worker identity.** The generated Worker name must match the `name` in `wrangler.jsonc`, and the artifact must have a real generated entry.
- **`AUTH_DB` identity.** Exactly one D1 binding named `AUTH_DB`, its `migrations_dir` ending in `migrations/better-auth`, and the same database ID the live Worker is already serving. A generated ID that differs stops the deploy rather than replacing customer auth data.
- **Durable Object history.** The deleted and renamed class set must be exactly the retired beta Flue and `AuthGuard` classes. `TagStateStore`, `Sandbox`, and `ContainerProxy` may never be marked deleted or renamed.
- **The scheduler.** Exactly one `* * * * *` Cron Trigger, the `TAG_STATE` binding, the two schedule agent bindings, and composed heartbeat and maintenance handlers.
- **Platform settings.** Workers Traces enabled, `compatibility_date` at or above `2026-04-01`, the `global_fetch_strictly_public` compatibility flag, and the `CF_VERSION_METADATA` binding.
- **Profile match.** The generated artifact's deployment profile must be the one you asked for.

Migrations run before the upload, against the live database. They are forward only and idempotent, so a failed deploy resumes from D1's migration ledger when you rerun the command. Never attempt a schema rollback.

## Deployment profiles

Two profiles exist. `CHICKPEA_DEPLOY_PROFILE` selects between them, and the npm scripts set it for you.

| Profile | Build | Deploy | What it adds |
|---|---|---|---|
| `core` (default) | `npm run build` | `npm run deploy` | Nothing beyond the base Worker |
| `sandbox` | `npm run build:sandbox` | `npm run deploy:sandbox` | A `SANDBOX` Durable Object binding and one container application |

The sandbox container is built from the repository's `Dockerfile` at instance type `standard-1` with `max_instances` 25. The overlay applies to the core config only, so a sandbox artifact is always core plus those two additions.

On a Deploy to Cloudflare deployment you have no local shell for the redeploy. Request installation in **Settings → Coding sandbox**, then add the non-secret build variable `CHICKPEA_DEPLOY_PROFILE` with value `sandbox` under **Cloudflare dashboard → Workers & Pages → your Worker → Settings → Builds → Variables** and retry the deployment. The dormant `v3` `Sandbox` migration is present in both profiles; it is not proof the tier is installed. Only the live binding is.

## What a deployment binds

| Binding | Kind | What it holds |
|---|---|---|
| `ASSETS` | Static Assets | The public product images in `assets/` |
| `AI` | Workers AI | The keyless model provider |
| `TAG_STATE` | Durable Object | Agents, assignments, thread snapshots, claims, the thread registry, operator settings |
| `SLACK_GATEWAY_SESSION` | Durable Object | The shared Slack gateway's ingress session |
| `FLUE_CHICKPEA_SLACK_V2_AGENT` | Durable Object | The Slack turn agent |
| `FLUE_CHICKPEA_ROUTINE_INTENT_V2_AGENT` | Durable Object | Schedule intent |
| `FLUE_CHICKPEA_ROUTINE_EXECUTION_V2_AGENT` | Durable Object | Schedule execution |
| `AUTH_DB` | D1 | Better Auth sessions and provider accounts |
| `CF_VERSION_METADATA` | Version metadata | The running Worker version, used by the readiness check |
| `SANDBOX` | Durable Object | The coding sandbox, on the `sandbox` profile only |

There is no KV namespace and no R2 bucket. The public images ship inside the same deployment through Static Assets, so no second Worker and no bucket is needed.

## The one-minute cron

The deployment declares one Cron Trigger, `* * * * *`, and it drives two handlers on every tick.

- **The schedule heartbeat** admits and runs schedule occurrences that are due, then drains pause notices.
- **Work maintenance** maintains the work ledger, repairs pending OAuth continuation resumes, and wakes the shared gateway session. The gateway wake runs even when the rest of maintenance fails, because it is the ingress lifeline for the shared Slack lane.

## On Node

Node runs no scheduler and no coding sandbox. Both are Cloudflare only.

## Set secrets after deploy

The Deploy to Cloudflare form asks for no Chickpea secret. Every active key in `.dev.vars.example` renders there as a required prompt, so provider keys stay commented out and are added afterwards.

```sh
npx wrangler secret put ANTHROPIC_API_KEY
npx wrangler secret put OPENAI_API_KEY
npx wrangler secret put OPENROUTER_API_KEY
```

The same keys can live in **Settings → Keys and models** instead. For a deployment-owned Composio key, set `COMPOSIO_API_KEY` as a secret and `CHICKPEA_COMPOSIO_CONFIGURATION_MODE=deployment` as a plain Worker variable. To turn telemetry off, add `DO_NOT_TRACK` or `CHICKPEA_DISABLE_TELEMETRY` as a dashboard variable; to label a non-production deployment, add `CHICKPEA_TELEMETRY_ENVIRONMENT=test`.

Two secrets belong to the wrapper. It mints `CHICKPEA_AUTH_SECRET` and the first credential encryption key (`CHICKPEA_CREDENTIAL_KEY_CURRENT_ID` set to `key_v1`, plus the matching `CHICKPEA_CREDENTIAL_KEY_KEY_V1` slot) on a deployment that has none, and preserves them on every deploy after. Removing one of the credential values without the other stops the next deploy: restore both before deploying again.

## The public origin

By default the deployment answers on its `workers.dev` URL, and on Cloudflare the request Host is the public origin. If you serve it somewhere else, set `SLACK_TAG_PUBLIC_URL` to that origin. It is an explicit operator pin that wins outright, and no request header can override it.

## Updates and state

Updates are manual. The Deploy to Cloudflare button clones this repository rather than forking it, so a new release reaches your deployment when you redeploy it.

> **Never reset a production database to make an upgrade pass**
>
> Earlier experimental schemas may be incompatible with a release. That is not permission to delete a production database. Do not edit already-applied migration SQL either; its digest is part of the auth database contract. Add a forward migration instead.

Cloudflare keeps app and runtime state in Durable Objects as well as auth data in D1. Backing up or restoring `AUTH_DB` alone does not back up or restore the whole application, and Chickpea does not currently provide a complete cross-store snapshot and restore tool. Keep credential secrets recoverable, and follow the specific release's migration and recovery procedure. Stop if it requires a recovery capability you have not established.

## Next steps

- [Deploy and operate](/operate): the Node target, upgrades, and configuration.
- [Get started on Cloudflare](/start/get-started-cloudflare): the first deployment, from the Deploy button to a reply in Slack.
- [How Chickpea works](/start/how-it-works): what the Worker, the Durable Objects, and D1 are holding.

Source: https://docs.chickpea.co/operate/cloudflare/index.mdx
