diff --git a/docs/licensing-spec.md b/docs/licensing-spec.md index bd35421..273f6cc 100644 --- a/docs/licensing-spec.md +++ b/docs/licensing-spec.md @@ -8,7 +8,9 @@ 1. **Продуктовый ключ** — секрет покупателя, известен только ему и серверу. Обменивается на **лицензионный токен** через `POST /v1/activate` (онлайн-активация). В поле «Указать ключ» приложение принимает **продуктовый ключ** `TTRPG-…` или устаревший `DND-…` (клиент сам вызывает `POST /v1/activate` с `deviceId`) или уже готовый **токен** (две части base64url через одну точку). Новые ключи на сервере — только `TTRPG-…` (см. репозиторий лицензий). 2. **Лицензионный токен** — публичная полезная нагрузка (`sub`, `pid`, `iat`, `exp`, `did`) + подпись **Ed25519**. Клиент хранит только токен и **публичный** ключ (вшит в приложение); подделать валидный токен без приватного ключа сервера невозможно. -3. **Срок** — поле `exp` (unix секунды). Клиент отклоняет истёкший токен без сети. +3. **Срок** — поле `exp` (unix секунды) в выданном токене. Клиент отклоняет истёкший токен без сети. На сервере два формата продуктового ключа: + - **fixed** — в записи ключа задан `expiresAtSec`; при активации `exp` копируется из него (старый формат). + - **period** — задан `validDays`; при **первой** активации лицензии (`sub`) сервер фиксирует `activatedAtSec` и выставляет `exp = activatedAtSec + validDays×86400` для всех устройств. Клиенту тип ключа неизвестен — только итоговый `exp` в токене. 4. **Устройства** — поле `did` в токене: при активации сервер привязывает токен к `deviceId` клиента и ведёт учёт списка устройств на `sub` в `data.json` (`maxDevices`). 5. **Отзыв** — сервер помечает `sub` в `revokedSubs`. Клиент при наличии `DND_LICENSE_STATUS_URL` запрашивает `GET /v1/status?sub=…`; при `revoked: true` лицензия считается недействительной **без обновления** приложения. Офлайн до истечения `exp` отозванный токен формально криптографически валиден — это осознанный компромисс; при необходимости сокращайте срок жизни токена или добавляйте принудительную онлайн-проверку перед критичными действиями.