Этап 5: история — экспорт и импорт .bare, устройства, место, пагинация
Экспорт: ключ архива из секрета аккаунта через HKDF, заголовок ровно 65 байт (magic, версия, соль, 32 сырых байта отпечатка владельца, iv) и он же целиком AAD шифротекста. Ника владельца в файле нет. Импорт сверяет отпечаток до расшифровки, сливает идемпотентно по id, а записи peers берёт только для ников, которых в локальном TOFU ещё нет: архивом доверие к ключу не перебить. Настройки: устройства с датой и пометкой «это устройство», «занято N МБ», кнопка «экспортировать» в подтверждении выхода и в подтверждении входа под другим ником — долг этапа 1 и обещание ADR-029 закрыты. Лента: страницы по 50 с подгрузкой вверх без прыжка прокрутки; новая страница вставляется, а не пересобирает ленту. ADR-050: импорт не перезаписывает лежащую запись — у своей есть состояние отправки, которого в архиве нет. ADR-054: архив — недоверенный ввод. Ревью собрало архивы с ts вне диапазона Date, мусорным lastId, ником с bidi-переопределением и roomId с обходом пути: каждый из них навсегда ломал ленту или счётчик. Теперь форма ника, roomId, id, ts и автора проверяется, а lastId из файла не читается вовсе. ADR-051, 052, 053: тексты и кнопки, устройства и место, страницы ленты без виртуализации. Приёмка: формат сверен побайтно на модулях, скачанных с боевого сервера, — смещения заголовка, отпечаток сырыми байтами, новые соль и iv на каждый экспорт, подмена любого байта заголовка ломает расшифровку, чужой секрет не открывает, семь видов битых файлов отвергнуты. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015DbCjVfTFq4ZFG8juD45YJ
This commit is contained in:
+49
@@ -165,6 +165,17 @@ input[type="password"] {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
/* три кнопки подтверждения в один ряд помещаются не всегда: переносятся
|
||||
и делят место поровну (ADR-051) */
|
||||
|
||||
.row--wrap {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.row--wrap .button {
|
||||
flex: 1 1 120px;
|
||||
}
|
||||
|
||||
/* подписи и строки состояния: акцент mark — только ошибка */
|
||||
|
||||
.hint {
|
||||
@@ -488,6 +499,44 @@ input[type="password"] {
|
||||
color: var(--mute);
|
||||
}
|
||||
|
||||
/* устройства — docs/ui.md, «Настройки». Строка: восемь символов
|
||||
идентификатора, дата появления и «удалить»; у своей строки вместо
|
||||
кнопки пометка (ADR-052) */
|
||||
|
||||
.devices {
|
||||
margin: 0 0 12px;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.device {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
min-height: 28px;
|
||||
font-size: 13px;
|
||||
color: var(--text2);
|
||||
}
|
||||
|
||||
.device__id {
|
||||
flex: none;
|
||||
}
|
||||
|
||||
.device .tag {
|
||||
color: var(--stone);
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.device .tag:last-child {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.device .link {
|
||||
margin-left: auto;
|
||||
color: var(--mute);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* баннер установки — docs/ui.md, «Баннер установки (iOS)».
|
||||
Цель нажатия у крестика — 44 px, отрицательные поля не дают ей
|
||||
растянуть сам баннер */
|
||||
|
||||
Reference in New Issue
Block a user