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

# Rotate secrets

> Rotating AUTH_SECRET signs out every session but leaves personal API tokens working. The database password needs an alter role statement.

Three secrets are worth rotating, and each one behaves differently. Take a backup first in every case.

## Rotate AUTH\_SECRET

`AUTH_SECRET` signs session cookies. Rotating it invalidates every signature, so every browser session is signed out at once.

<Steps>
  <Step title="Generate a new value">
    ```bash theme={null}
    openssl rand -hex 32
    ```
  </Step>

  <Step title="Replace it in the root .env">
    Or in `apps/api/.env` for a local development instance.
  </Step>

  <Step title="Recreate the API">
    ```bash theme={null}
    docker compose up -d --force-recreate api --wait
    ```
  </Step>

  <Step title="Sign in again">
    Every user has to. There is no way to rotate the signing secret without that.
  </Step>
</Steps>

Personal API tokens are unaffected. They are stored as SHA-256 hashes rather than signed with `AUTH_SECRET`, so scripts and the MCP server keep working across a rotation. Revoke an individual token instead when that token is what leaked. See [Personal API tokens](/security/api-tokens).

## Rotate the database password

The role password and the root `.env` must change together, so generate the value once and apply it to both before recreating the services.

```bash theme={null}
set -eu
umask 077
new_password="$(openssl rand -hex 32)"
docker compose stop web api
printf "alter role asobeast with password '%s'\n" "$new_password" |
  docker compose exec -T postgres psql --username asobeast --dbname postgres --set=ON_ERROR_STOP=1
printf 'POSTGRES_PASSWORD=%s\n' "$new_password" > .env
chmod 600 .env
docker compose up -d --force-recreate postgres api web --wait
curl --fail --silent --show-error http://127.0.0.1:3000/api/backend/health
```

Two details make this work. Recreating the PostgreSQL container does not change an existing role password, which is why the `alter role` statement is required. The new value reaches the database over standard input rather than as a command line argument, so it stays out of the process list.

<Warning>
  Writing `.env` with `>` replaces the file. If your `.env` carries more than `POSTGRES_PASSWORD`, including `AUTH_SECRET`, edit it in place or rewrite every line you need. Losing `AUTH_SECRET` here signs everyone out.
</Warning>

If the API fails to start afterwards, the role and `.env` disagree. Re run the block.

## Rotate an OpenAI key

The simplest of the three. Replace `OPENAI_API_KEY` in `apps/api/.env`, recreate the API, then revoke the old key at OpenAI.

```bash theme={null}
docker compose up -d --force-recreate api --wait
```

Nothing is stored that depends on the old key. Cached AI audit results stay valid because they are stored output rather than credentials. See [Enable the optional AI features](/guides/ai-features).

## Related

<CardGroup cols={2}>
  <Card title="Back up PostgreSQL" icon="database-backup" href="/operations/backups">
    Take one before every rotation.
  </Card>

  <Card title="Hosting behind TLS and a reverse proxy" icon="shield" href="/security/hosting">
    The rest of the pre exposure checklist.
  </Card>
</CardGroup>
