docs(security): warn that APP_SECRET must never change after setup
APP_SECRET does double duty: it signs JWTs and derives the AES-256-GCM key that encrypts stored AI-provider credentials. Rotating it makes every saved AI API key undecryptable and invalidates existing sessions. Document this footgun where operators set the value (RT-30 from the red-team report). - .env.example: dual-role warning block above APP_SECRET - README.md / README.ru.md: warning callout in the upgrade section Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -12,6 +12,11 @@ PORT=3000
|
|||||||
# (The /mcp limiter keeps a global per-email key as an IP-independent backstop,
|
# (The /mcp limiter keeps a global per-email key as an IP-independent backstop,
|
||||||
# but the per-IP and per-IP+email keys rely on a trustworthy X-Forwarded-For.)
|
# but the per-IP and per-IP+email keys rely on a trustworthy X-Forwarded-For.)
|
||||||
|
|
||||||
|
# APP_SECRET has a DUAL role: it signs JWTs AND derives the AES-256-GCM key that
|
||||||
|
# encrypts stored AI-provider credentials (API keys) at rest. CONSEQUENCE: if you
|
||||||
|
# change APP_SECRET after setup, every stored AI API key becomes undecryptable —
|
||||||
|
# you must re-enter them in AI settings — and all existing sessions/JWTs are
|
||||||
|
# invalidated. Choose it ONCE, keep it stable, and back it up alongside your DB.
|
||||||
# minimum of 32 characters. Generate one with: openssl rand -hex 32
|
# minimum of 32 characters. Generate one with: openssl rand -hex 32
|
||||||
APP_SECRET=REPLACE_WITH_LONG_SECRET
|
APP_SECRET=REPLACE_WITH_LONG_SECRET
|
||||||
|
|
||||||
|
|||||||
@@ -158,6 +158,11 @@ the existing data directory is reused as-is:
|
|||||||
start the new migrations apply on top of your existing schema (`CREATE EXTENSION vector` plus the
|
start the new migrations apply on top of your existing schema (`CREATE EXTENSION vector` plus the
|
||||||
`page_embeddings` and AI tables); watch the logs for `Migration "..." executed successfully`.
|
`page_embeddings` and AI tables); watch the logs for `Migration "..." executed successfully`.
|
||||||
|
|
||||||
|
> ⚠️ **Never change `APP_SECRET` after setup.** It does double duty: it signs JWTs *and* derives the
|
||||||
|
> AES-256-GCM key that encrypts stored AI-provider credentials (API keys). Rotating it makes every
|
||||||
|
> saved AI API key undecryptable (you'd have to re-enter them in AI settings) and invalidates all
|
||||||
|
> existing sessions. Pick it once, keep it stable, and back it up together with your database.
|
||||||
|
|
||||||
### Notes
|
### Notes
|
||||||
|
|
||||||
- **Back up first.** Take a `pg_dump` before swapping — migrations apply in place, and the
|
- **Back up first.** Take a `pg_dump` before swapping — migrations apply in place, and the
|
||||||
|
|||||||
@@ -159,6 +159,12 @@ dump/restore, существующий каталог данных переис
|
|||||||
новые миграции применяются поверх вашей схемы (`CREATE EXTENSION vector` плюс таблицы
|
новые миграции применяются поверх вашей схемы (`CREATE EXTENSION vector` плюс таблицы
|
||||||
`page_embeddings` и AI-таблицы); следите в логах за строками `Migration "..." executed successfully`.
|
`page_embeddings` и AI-таблицы); следите в логах за строками `Migration "..." executed successfully`.
|
||||||
|
|
||||||
|
> ⚠️ **Никогда не меняйте `APP_SECRET` после установки.** Он выполняет двойную роль: подписывает JWT
|
||||||
|
> *и* служит материалом для ключа AES-256-GCM, которым шифруются сохранённые ключи AI-провайдеров
|
||||||
|
> (API-ключи). Смена секрета сделает все сохранённые AI-ключи нерасшифровываемыми (придётся вводить
|
||||||
|
> их заново в настройках AI) и инвалидирует все текущие сессии. Задайте его один раз, держите
|
||||||
|
> неизменным и бэкапьте вместе с базой данных.
|
||||||
|
|
||||||
|
|
||||||
## Возможности
|
## Возможности
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user