---
title: "Recovery"
description: "Repair a deployment that can no longer authenticate the Slack app and workspace it is already bound to."
---

> 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.

# Recovery

**Recovery** is a hidden repair path that replaces the stored Slack credentials of the app and workspace your deployment is already bound to. Where setup connects Slack for the first time and Sign in with Slack proves who a person is, recovery restores only the deployment's ability to authenticate, and it signs nobody in. This page covers when to use it, the token that opens it, the read-only preflight, the 15-minute repair session, the `recovery_only` health gate, and removing the token afterwards.

## When to use recovery

Use it when the deployment still points at the right Slack app but can no longer authenticate as it.

- **The stored Slack secrets no longer work.** A rotated client secret or signing secret, or a bot token that was revoked in Slack.
- **The deployment hostname changed.** The app's two OAuth redirects and its Events request URL still point at the old origin.
- **The credential-encryption key is gone.** Chickpea cannot decrypt the active revision and has closed itself into `recovery_only`.

Do not use recovery when the Slack app itself was deleted, when its identity realm is irrecoverable, or when every active Owner lost their Slack identity. Those cases need an explicit destructive fresh deployment and state reset, followed by setup again.

## What recovery cannot do

- **It never signs anyone in.** Recovery cannot create an Owner, change a role, bind a different app or workspace, or issue a Chickpea session.
- **It is scoped to the unchanged app and workspace.** A different app ID or a different team ID is refused outright.
- **It cannot adopt a replacement Slack app.** The candidate must match the app ID, team ID, and manifest fingerprint recorded on the active revision.
- **It cannot widen the manifest.** URL repair updates only the exact OAuth and Events URLs, and refuses any other contract difference.
- **It cannot promote an unproven credential.** The staged revision stays inactive until Slack OAuth validation and a revision-bound signed Events proof both pass.

## The recovery token

`CHICKPEA_RECOVERY_TOKEN` is an optional deployment capability, separate from `CHICKPEA_AUTH_SECRET` and from the credential keyring. It is 32 bytes of random material, accepted in exactly three encodings: 64 hexadecimal characters, 43 base64url characters, or 43 standard base64 characters plus `=`. Anything else is rejected before a session is considered.

The token is dormant by default. Left unset, the route does not exist: `/admin/recovery` returns 404 when no valid token is configured, and also when the deployment has no connected workspace installation to repair. The deploy wrapper never creates one for you.

One token opens exactly one session, ever. A second use of the same value is refused, including through another supported encoding, and only one recovery session may be live at a time. If a session fails or expires, mint a new token value before trying again.

Generate a value with 32 bytes of randomness:

```sh
openssl rand -base64 32 | tr '+/' '-_' | tr -d '='
```

| Action | On Cloudflare | On Node |
|---|---|---|
| Set the token | `npx wrangler secret put CHICKPEA_RECOVERY_TOKEN` | Set `CHICKPEA_RECOVERY_TOKEN` in the runtime environment file, then restart |
| Remove the token | `npx wrangler secret delete CHICKPEA_RECOVERY_TOKEN` | Clear the variable, then restart |

Keep the value out of chat, logs, screenshots, tickets, and shell history. Both commands above prompt for the value rather than reading it from the command line.

## Prerequisites

- **The Slack app still exists.** Record its non-secret app ID and the installed workspace team ID.
- **A usable current credential-encryption key.** If the prior root was lost, provision a new versioned key slot rather than reusing the lost key ID with different material. On Cloudflare that is `CHICKPEA_CREDENTIAL_KEY_CURRENT_ID` plus a matching `CHICKPEA_CREDENTIAL_KEY_<ID>`. On Node the keyring is a `0600` JSON file beside the state database, at `CHICKPEA_CREDENTIAL_KEYRING_PATH` when set.
- **The app's client ID, client secret, and signing secret,** ready to paste once.
- **A fresh Slack app configuration token, only if the hostname changed.** It begins `xoxe.` and is used in that one request, never stored.
- **One browser you keep open.** The repair session lasts 15 minutes and is bound to the browser that started it.

## Preflight

Run the read-only preflight first. It validates the token encoding, prints the hidden URL, and changes nothing.

```sh
CHICKPEA_RECOVERY_TOKEN='<32-byte value>' \
  npm run auth:recover -- --url https://your-chickpea.example
```

On a Node deployment, add the state database to confirm which app, workspace, credential revision, and health gate the deployment expects.

```sh
CHICKPEA_RECOVERY_TOKEN='<32-byte value>' \
  npm run auth:recover -- --url https://your-chickpea.example --state-db /path/to/tag-state.db
```

You should see the hidden URL and, with `--state-db`, the expected installation:

```text
Slack credential recovery preflight passed.
Open: https://your-chickpea.example/admin/recovery
Expected Slack app: <your app ID>
Expected Slack workspace: <your team ID>
Deployment health gate: normal
```

## Repair the connection

> **Promotion replaces the active credential**
>
> The last step atomically promotes the staged revision, fences the runtime credential cache, and consumes the recovery session. The session cannot be reopened with the same token, so finish the Events step in Slack before returning to promote.

1. **Open the hidden route**

   Open the exact `/admin/recovery` URL from the preflight. This route is absent from normal navigation. Enter the deployment recovery token and choose **Start 15-minute repair**.
2. **Enter the app credentials**

   The page names the app ID and team ID it expects. Fill in **Client ID**, **Client secret**, and **Signing secret**. Add the configuration token only if the deployment URLs changed. The fields are write-only and encrypted before durable storage.

   Choose **Encrypt credentials and authorize Slack**. If a configuration token was supplied, Chickpea exports the current manifest, refuses any non-URL contract difference, updates only the exact OAuth and Events URLs, exports again to confirm, and discards the token from request memory.
3. **Continue through Slack bot authorization**

   Slack asks you to authorize the same app in the same workspace. Chickpea requires the unchanged app and workspace, a bot token, all required bot scopes, agreement with `auth.test`, readable directory and channel capabilities, and confidential `client_secret_post`.

   The result is staged inactive. The prior usable revision stays active if anything fails.
4. **Re-verify the Events URL in Slack**

   In Slack app settings, retry and save Event Subscriptions for the same app so Slack sends a fresh signed URL-verification challenge to your deployment.
5. **Promote the repair**

   Return to the recovery page and choose **Verify and promote repair**. Chickpea verifies the challenge against the exact app and workspace using the candidate's signing secret, promotes that revision, and returns a neutral completion page.

## Verify

The page reads **Slack connection repaired** and offers **Continue to Sign in with Slack**. Recovery granted no session of its own, so confirm the repair from the outside:

1. An existing Owner signs in with Slack and Admin loads.
2. A mention in Slack gets a threaded reply from the Agent.

## Remove the token

Delete or replace `CHICKPEA_RECOVERY_TOKEN` as soon as the repair is verified, using the command for your target from the table above. The capability is already consumed, and leaving the value in place leaves one more secret in the deployment.

Keep only safe evidence of the operation: the deployment ID, app ID, team ID, credential revision IDs, the audit correlation ID, the health-gate transition, and the pass or fail result. Never retain the recovery or configuration token, the OAuth code or state, browser cookies, bot, client, or signing secrets, Slack ID or access tokens, or raw signed event bodies.

## When the encryption root is lost

When live ciphertext references a key the deployment no longer has, Chickpea sets its health gate to `recovery_only`. Normal Admin, Better Auth, OIDC, and Slack execution stay closed: in Admin, every path other than `/admin/recovery` and `/auth/slack/recovery/callback` returns 404.

The flow above still applies. It may tombstone the undecryptable workspace-default revision, advance to a new key version, and stage a same-app candidate. Dedicated bot revisions are never reset by this operation, so restore their prior key first. The gate clears only when signed Events proof promotes the replacement.

## If recovery is refused

A refusal leaves the candidate inactive and creates no identity authority. The common causes:

- **A different app, workspace, broader manifest change, missing scope, user token, or mismatched Slack truth.** Recovery repairs one app and one workspace and refuses everything else.
- **A Slack outage, an invalid Slack response, or missing Events proof.** Retry after Slack recovers, with a new token value.
- **A stale revision, a parallel session, a replay, or the 15-minute expiry.** The session ends and the previous credential stays active.
- **Too many attempts.** The page answers `Too many recovery attempts. Try again later.` with a `Retry-After` header. Wait out the interval rather than retrying immediately.

## Next steps

- [Security model](/security/security-model): the guarantees recovery is built to preserve.
- [Data and storage](/security/data-and-storage): where the encrypted Slack credentials and the keyring live.
- [Set up your own Slack app](/slack/set-up-your-own-slack-app): the manifest, scopes, and Events URL that recovery re-verifies.
- [Node](/operate/node): backups and restore, the other way back from a lost state directory.
- [Environment variables](/reference/environment-variables): every variable named on this page, with its target and default.

Source: https://docs.chickpea.co/security/recovery/index.mdx
