- archive local-fs-storage-backend, sqlite-metadata-backend, storage-orphan-reconciliation, metadata-managed-jwt-secret, default-local-backends - sync specs: storage-backends, metadata-backends, storage-reconciliation, app-secrets; update photos/share-links - defaults reflected in specs: SQLite and local FS
77 lines
5.0 KiB
Markdown
77 lines
5.0 KiB
Markdown
# Design
|
|
|
|
## Context
|
|
|
|
См. `proposal.md` — Why. Текущее состояние:
|
|
|
|
- `JWT_SECRET` обязателен в env (`config.Load`), используется в `auth.NewTokenManager`
|
|
(HS256-подпись access-токенов) и как fallback для `URL_SECRET` в `storage.Open`.
|
|
- Refresh-токены не подписываются секретом (случайные, хранятся хэшем).
|
|
- Есть два бэкенда метаданных (`postgres`, `sqlite`) за интерфейсом `Repository` и
|
|
фабрикой `store.NewPostgres/NewSQLite`; миграции раздельные (pg-набор и `migrations/sqlite`).
|
|
- Порядок старта в `cmd/server`: миграции → метаданные → storage → app.
|
|
|
|
## Goals / Non-Goals
|
|
|
|
**Goals:**
|
|
- Хранить секрет приложения в слое метаданных; генерировать при первом использовании.
|
|
- Убрать обязательный `JWT_SECRET` из окружения.
|
|
- Единый стабильный секрет на инсталляцию; корректное поведение при конкурентном старте.
|
|
|
|
**Non-Goals:**
|
|
- Шифрование секрета в БД (БД доверенная).
|
|
- UI/команда ротации секрета.
|
|
- Миграция существующих env-секретов в БД.
|
|
- Изменение формата токенов или наблюдаемого поведения аутентификации для клиента.
|
|
|
|
## Decisions
|
|
|
|
### D1. Таблица `app_settings`
|
|
`app_settings(key TEXT PRIMARY KEY, value TEXT NOT NULL, created_at, updated_at)` в обоих
|
|
бэкендах. Ключ `jwt_secret`. Отдельные миграции: PostgreSQL `000010_app_settings`,
|
|
SQLite `000002_app_settings` (консолидированная 000001 уже применена на существующих БД).
|
|
|
|
### D2. Репозиторий `internal/settings`
|
|
Интерфейс `Repository { Get(ctx,key) (value string, ok bool, err error); Set(ctx,key,value) error; Ensure(ctx,key, generate func() (string,error)) (string, error) }`.
|
|
Реализации pg и sqlite. `Ensure`: `SELECT` → если есть, вернуть; иначе сгенерировать,
|
|
`INSERT ... ON CONFLICT(key) DO NOTHING`, затем `SELECT` (единый секрет при гонке).
|
|
Добавляется в `store.Repos`.
|
|
|
|
### D3. Генерация секрета
|
|
`crypto/rand` 32 байта → base64url (без padding). Значение хранится как строка; в
|
|
TokenManager и Signer передаётся как есть.
|
|
|
|
### D4. Порядок старта и проводка
|
|
`cmd/server`: миграции → метаданные → `secret := repos.Settings.Ensure("jwt_secret", generate)`
|
|
→ `tm := auth.NewTokenManager(secret, accessTTL, refreshTTL)` → `storage.Open(ctx, cfg, secret)`
|
|
(где `URL_SECRET`, если задан, имеет приоритет, иначе используется `secret`) → `app.New`.
|
|
`app.New` перестаёт строить TokenManager из конфига и принимает готовый `*auth.TokenManager`
|
|
(или секрет).
|
|
|
|
### D5. Конфиг
|
|
Поле `Auth.JWTSecret` и проверка обязательности удаляются; `JWT_ACCESS_TTL`/`JWT_REFRESH_TTL`
|
|
остаются. `URL_SECRET` остаётся необязательным. `storage.Open` получает секрет параметром
|
|
вместо чтения из конфига.
|
|
|
|
## Risks / Trade-offs
|
|
|
|
- [Смена источника секрета у существующих деплоев] → access-токены инвалидируются, но
|
|
refresh-токены остаются валидными (не подписаны секретом), клиент молча обновляет access;
|
|
в худшем случае — перелогин. Отмечаем в доках.
|
|
- [Конкурентный первый старт на общей БД] → `INSERT ... ON CONFLICT DO NOTHING` + повторный
|
|
`SELECT` (D2).
|
|
- [Секрет в открытом виде в БД] → БД доверенная; шифрование — вне рамок.
|
|
- [Тихая смена поведения] → при `URL_SECRET` не задан подпись URL зависит от секрета из БД;
|
|
явный `URL_SECRET` снимает зависимость.
|
|
|
|
## Migration Plan
|
|
|
|
1. Добавить таблицу и репозиторий секретов; сид отсутствует — генерируется при старте.
|
|
2. Переключить `cmd/server`/`app`/`storage` на секрет из метаданных; убрать `JWT_SECRET`.
|
|
3. Обновить доки и `.env.example`.
|
|
4. Откат: вернуть чтение `JWT_SECRET` из env (код), данные не меняются.
|
|
|
|
## Open Questions
|
|
|
|
- Нужна ли операция ротации секрета (сброс значения и инвалидация access-токенов).
|