Закрыть открытые вопросы: ADR-015…022, бриф айдентики

Регистрация открытая (015), контакты и приглашения по нику с
закреплением ключа и блокировкой (016), лимиты (017), идентификация
устройства через случайный deviceId в IndexedDB и серверный fan-out
(018), отказ от серверного перца (019), смена пароля не в v1 (020),
интерфейс ru/en (021), айдентика «Скобы» по turn 2 канваса (022).

Архитектура, модель угроз и README обновлены. В открытых вопросах
остались два: моногарнитура с кириллицей (Fragment Mono её не
содержит) и удаление аккаунта.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R2pCJkWyG2aYu48u1Vp1A1
This commit is contained in:
2026-08-20 22:42:20 +03:00
co-authored by Claude Fable 5
parent b9bf4ea04e
commit a898347005
15 changed files with 290 additions and 14 deletions
+22
View File
@@ -0,0 +1,22 @@
# ADR-018: Устройство — случайный идентификатор в IndexedDB
Закрывает открытый вопрос из [ADR-008](008-server-relay.md).
## Контекст
Очередь недоставленных сообщений — per-device (ADR-008), подписка на пуши — тоже per-device (ADR-011). Серверу нужно отличать устройства одного аккаунта. Стабильного идентификатора браузера не существует, и это правильно; его надо завести самим.
## Решение
- При первом запуске клиент генерирует 128-битный случайный `deviceId` (`crypto.getRandomValues`) и хранит его в IndexedDB рядом с историей. Одно хранилище — один жизненный цикл: стёрли данные сайта — исчезли и история, и идентичность устройства. Новый запуск — новое устройство.
- При входе клиент передаёт `deviceId`. Сервер создаёт запись устройства с ключом (аккаунт, `deviceId`) и очередь к нему. Сессионная cookie привязана к устройству.
- `deviceId` — не секрет, а адрес очереди. Аутентифицирует сессия. Коллизии между аккаунтами невозможны: ключ составной.
- Доставка — серверный fan-out: сообщение раскладывается в очереди всех устройств всех участников, кроме отправившего. Отправитель не знает и не должен знать устройств адресата: все устройства одного аккаунта владеют одним приватным ключом (ADR-006), шифротекст один для всех.
- Устройство без связи 30 дней удаляется вместе с очередью и push-подпиской — тот же срок, что TTL очереди (ADR-008). На следующем запуске клиент регистрируется как новое устройство; локальная история при этом цела.
- Клиент при регистрации устройства передаёт короткую метку из user-agent («Firefox · Linux»). Список устройств с метками и датой последней связи виден в настройках; любое можно удалить — очередь и сессия уничтожаются.
## Следствия
- Сообщение, отправленное с телефона, появляется и на ноутбуке — как новое, через его очередь. Это не синхронизация истории: то, что было до появления устройства, на него не приедет.
- Один браузер в двух профилях или в режиме инкогнито — разные устройства. Инкогнито плодит устройства при каждом запуске; их чистит 30-дневный срок и лимит в 10 устройств (ADR-017).
- Потеря `deviceId` (чистка storage) эквивалентна потере устройства: недоставленное в старую очередь пропадает по TTL. `navigator.storage.persist()` (ADR-009) снижает риск.