---
title: Ротация ключей
icon: rotate
---

У Twenty есть два независимых семейства ключей:

* **Ключи подписи JWT** — асимметричные пары ключей ES256 (с тегами `kid`), хранятся в `core."signingKey"` и используются для подписи и проверки access/refresh-токенов.
* **Ключ шифрования данных на диске** — `ENCRYPTION_KEY`, используется для шифрования OAuth-токенов, переменных приложения, закрытых ключей подписи, конфиденциальных значений конфигурации и TOTP-секретов внутри оболочки `enc:v2:`.

`APP_SECRET` — устаревший секрет, сохранённый для обратной совместимости: когда `ENCRYPTION_KEY` не установлен, он используется как резерв для шифрования данных на диске/сеансовых cookie и по-прежнему проверяет ранее выданные HS256 access-токены. Он будет объявлен устаревшим.

## Ключи подписи JWT

Каждый ключ содержит `publicKey` (хранится бессрочно, чтобы можно было проверять ранее выданные токены), зашифрованный `privateKey` (используется только пока ключ является текущим), флаг `isCurrent` (в каждый момент времени ровно одна строка) и необязательное поле `revokedAt`.

### Выполнить ротацию текущего ключа

* **Вручную** — **Settings → Admin Panel → Signing keys → Revoke** в текущей строке. Отзыв стирает его зашифрованный закрытый материал и понижает его; следующий вызов подписи автоматически создает новую пару ключей ES256 как новый текущий ключ. Токены, подписанные любым другим (не отозванным) `kid`, продолжают успешно проходить проверку до истечения срока действия.
* **Enterprise (автоматически)** — ежедневный cron (`'15 3 * * *'` UTC) выпускает новый текущий ключ, как только существующий ключ находится в статусе текущего в течение `SIGNING_KEY_ROTATION_DAYS` (по умолчанию `90`). Предыдущий ключ *не* отзывается, поэтому токены, подписанные им, продолжают успешно проходить проверку. Зарегистрируйте его один раз с помощью `yarn command:prod cron:register:all`.

  <Note>Enterprise-cron и `SIGNING_KEY_ROTATION_DAYS` поставляются начиная с v2.6+.</Note>

### Отозвать ключ (только при утечке / в экстренных случаях)

**Settings → Admin Panel → Signing keys → Revoke** в нетекущей строке. Стирает зашифрованный закрытый материал, устанавливает `revokedAt` и отклоняет все существующие токены, подписанные с этим `kid`.

## Выполнить ротацию `ENCRYPTION_KEY`

<Note>Команда `secret-encryption:rotate`, описанная ниже, поставляется начиная с v2.6+.</Note>

Каждое зашифрованное значение оборачивается как `enc:v2:\<keyId>:\<payload>`, где `\<keyId>` — это 8-символьный шестнадцатеричный префикс, полученный из исходного ключа. Ротация выполняется онлайн и может быть возобновлена.

1. **Сгенерировать новый ключ**: `openssl rand -base64 32`.

2. **Настройте оба ключа параллельно** в `.env`, затем перезапустите:
   ```ini
   ENCRYPTION_KEY=NEW_VALUE
   FALLBACK_ENCRYPTION_KEY=OLD_VALUE
   ```
   Новые записи используют новый ключ, существующие строки по-прежнему расшифровываются через резервный ключ.

3. **Повторно зашифровать существующие строки**:

   ```bash
   docker exec -it {server_container} yarn command:prod secret-encryption:rotate
   ```

   Команда обходит шесть разделов (`connected-account-tokens`, `application-variable`, `application-registration-variable`, `signing-key-private-keys`, `sensitive-config-storage`, `totp-secrets`). SQL-фильтр пропускает строки, которые уже находятся на новом `\<keyId>`, поэтому команда идемпотентна: прерывайте и запускайте повторно по мере необходимости. Завершает работу с ненулевым кодом, если какая-либо строка завершилась с ошибкой — запустите повторно, чтобы повторить попытку.

   | Флаг                                     | Описание                                                           |
   | ---------------------------------------- | ------------------------------------------------------------------ |
   | `-s, --site \<site>`    | Ограничить одним сайтом.                                           |
   | `-b, --batch-size \<n>` | Строк в партии (по умолчанию `200`, максимум `5000`).              |
   | `-d, --dry-run`                          | Расшифровать + повторно зашифровать в памяти, пропустить `UPDATE`. |

4. **Удалите резервный ключ** после того, как `--dry-run` покажет ноль оставшихся строк: удалите `FALLBACK_ENCRYPTION_KEY` и перезапустите.

## Устаревшая поддержка `APP_SECRET`

Более старые инстансы, в которых никогда не был установлен `ENCRYPTION_KEY`, используют `APP_SECRET` как ключ шифрования данных на диске (и как секрет для сеансовых cookie, производный от него). Этот путь сохранен для обратной совместимости, но **устарел** — установите отдельный `ENCRYPTION_KEY` и выполните описанную выше процедуру ротации, чтобы с него мигрировать. Сам `APP_SECRET` продолжает использоваться для проверки устаревших HS256 access-токенов.
