# Luma — техническое задание для Web, Desktop и Android Версия: 1.0. Дата: 14 октября 2026 года. Статус: целевой стандарт разработки. Связанный документ: [UI Kit](uikit_clear.md). ## Содержание - [1. Назначение документа](#1-назначение-документа) - [2. Продукт и пользователи](#2-продукт-и-пользователи) - [3. Платформы и объём](#3-платформы-и-объём) - [4. Общие требования к реализации](#4-общие-требования-к-реализации) - [5. Предметная модель](#5-предметная-модель) - [6. Аккаунты и вход](#6-аккаунты-и-вход) - [7. Соединение и аутентификация](#7-соединение-и-аутентификация) - [8. Навигация и основные экраны](#8-навигация-и-основные-экраны) - [9. Контакты, presence и аватары](#9-контакты-presence-и-аватары) - [10. Текстовые сообщения и доставка](#10-текстовые-сообщения-и-доставка) - [11. Действия над сообщениями](#11-действия-над-сообщениями) - [12. Реакции и typing](#12-реакции-и-typing) - [13. Группы MUC](#13-группы-muc) - [14. Шифрование и устройства](#14-шифрование-и-устройства) - [15. Файлы и вложения](#15-файлы-и-вложения) - [16. Просмотр и сохранение медиа](#16-просмотр-и-сохранение-медиа) - [17. Запись аудио, видео и геопозиция](#17-запись-аудио-видео-и-геопозиция) - [18. Локальная история и MAM](#18-локальная-история-и-mam) - [19. Звонки](#19-звонки) - [20. Уведомления, push и фоновая работа](#20-уведомления-push-и-фоновая-работа) - [21. Локальная защита и хранение](#21-локальная-защита-и-хранение) - [22. Приватность и безопасность](#22-приватность-и-безопасность) - [23. Совместимость протоколов](#23-совместимость-протоколов) - [24. Нефункциональные требования](#24-нефункциональные-требования) - [25. Критерии приёмки](#25-критерии-приёмки) - [26. Состав результата разработки](#26-состав-результата-разработки) - [27. Паспорт конкретного приложения](#27-паспорт-конкретного-приложения) ## 1. Назначение документа Разработать семейство XMPP-приложений Luma с общими пользовательскими сценариями, совместимыми сообщениями и единым визуальным языком для Web, Desktop и Android. Документ является самостоятельным техническим заданием. Он определяет результат, поведение и критерии приёмки без привязки к языку программирования, UI framework, структуре исходников и конкретной библиотеке XMPP. «Обязательно» означает требование базового продукта. «При наличии возможности» означает управляемую зависимость от разрешений, оборудования, сервера или платформы, а не необязательную реализацию обработки отказа. Даже при отсутствии функции приложение обязано корректно объяснить ограничение и сохранить доступность остальных сценариев. Требования с идентификаторами используются в тестах и задачах. Выбранные технологии, поддерживаемые версии ОС/браузеров и инфраструктурные адреса фиксируются отдельно в паспорте конкретного приложения до его выпуска. ## 2. Продукт и пользователи Luma — мессенджер для пользователей существующих XMPP-аккаунтов. Пользователь входит по JID и паролю, выбирает совместимый сервер, общается лично и в группах, отправляет файлы и звонит другим пользователям. Основные ценности: - независимость от одного XMPP-провайдера; - совместимость с другими XMPP-клиентами; - конфиденциальность содержимого через OMEMO; - локальная история и восстановление с сервера; - одинаковая семантика сообщений и действий на разных устройствах; - понятные ошибки и отсутствие молчаливого отключения защиты. Интерфейс первого выпуска — русский. Вся пользовательская строка должна допускать последующее добавление локализаций. Идентификаторы протокола и адреса пользователей не переводятся. ## 3. Платформы и объём | Платформа | Форма | Обязательный сценарий | | --- | --- | --- | | Web | Приложение в браузере, адаптивное для телефона и компьютера | Работа в активной сессии, локальная история, сообщения, медиа и звонки при доступных разрешениях | | Desktop | Устанавливаемое приложение для утверждённого набора настольных ОС | Оконный интерфейс, системные уведомления, работа при скрытом окне в пределах жизненного цикла процесса | | Android | Устанавливаемое мобильное приложение | Сенсорный интерфейс, системный lifecycle, уведомления и входящие вызовы через поддержанный фоновый механизм | ### 3.1. Базовый объём - несколько сохранённых аккаунтов, один активный XMPP-контекст; - вход/выход/переключение, настройки подключения; - контакты, presence, личные чаты и чат с собой; - MUC: создание, вход, приглашение, выход, локальное удаление; - текст, replies, reactions, correction, retraction и локальное удаление; - множественный выбор и пересылка; - OMEMO legacy и OMEMO 2, доверие устройствам; - файлы, фото, видео, музыка, voice, video notes и геоточки; - локальная история, MAM, Carbons, receipts и read state; - аудио-/видеозвонки один на один и история звонков; - аватары; - настройка уведомлений, app lock и биометрия при доступности; - серверная диагностика; - push и фоновые входящие в пределах утверждённой платформенной интеграции. ### 3.2. За пределами базового объёма Apple Watch и другие companion-устройства, регистрация новых XMPP-аккаунтов, восстановление серверного пароля, групповые звонки, каналы/MIX, боты, платежи, истории, live location, полнотекстовый поиск всех сообщений, обязательные одновременные соединения всех сохранённых аккаунтов. Развёртывание XMPP-провайдера не является функцией пользовательского клиента. Инфраструктура push/relay, если нужна для заявленной доставки, входит в состав решения отдельным компонентом и должна быть поставлена либо явно подключена к утверждённому внешнему сервису. ## 4. Общие требования к реализации **GEN-001.** Одинаковые действия на разных платформах должны иметь одинаковый смысл: «Отправлено», «Доставлено», «Прочитано», локальное удаление и отзыв нельзя интерпретировать по-разному. **GEN-002.** UI, предметная логика, протоколы, хранение и системные интеграции разделяются устойчивыми интерфейсами. UI не владеет криптографической сессией или активным медиасоединением. **GEN-003.** Криптография, обработка больших файлов, индексирование и архивная загрузка не должны монопольно занимать поток отображения интерфейса. **GEN-004.** Все долгие операции имеют состояние выполнения, завершения, ошибки и отмены там, где отмена безопасна. Ошибка не оставляет интерфейс навсегда заблокированным. **GEN-005.** Недоступная возможность сервера отключает соответствующую функцию с объяснением, сохраняя остальные функции. Поддержка, объявленная сервером, не отображается как уже настроенная интеграция приложения. **GEN-006.** Приложение не обещает гарантированную доставку, прочтение, удаление чужих копий, работу закрытого процесса или конфиденциальность метаданных, если механизм этого не обеспечивает. **GEN-007.** Исходные документы, тесты и поставляемая версия приложения должны соответствовать друг другу. Релиз имеет номер версии и журнал изменений. ## 5. Предметная модель Ниже приведён логический контракт. Физическая схема БД и способы сериализации выбираются реализацией. ### 5.1. Аккаунт | Атрибут | Содержание | | --- | --- | | accountId | Нормализованный bare JID, устойчивый ключ локального контекста | | jid | Адрес пользователя | | displayName | Отображаемое имя | | deviceResource | Уникальный для установки ресурс соединения | | connectionSettings | Endpoint/host/port, транспортный режим, настройки discovery | | encryptionDefault | По умолчанию включён | | typingEnabled | По умолчанию включён | | notificationSettings | Разрешение/предпочтения, показ preview | | credentialReference | Ссылка на защищённое хранилище, не пароль в общедоступном объекте | Пароли, refresh tokens и приватные ключи не входят в открытый экспорт настроек. ### 5.2. Диалог Идентичность диалога — accountId + conversationJid, а не один JID без контекста аккаунта. Атрибуты: JID, direct/group, имя, avatar reference, lastActivity, lastMessagePreview, unreadCount, presence, optional pinned flag, encryptionOverride, group nickname, joined, autojoin, occupantCount и invitation metadata. Значение joined после перезапуска определяется реальным соединением, а не только записью в БД. Режим шифрования: наследовать аккаунт / всегда включено / выключено. ### 5.3. Сообщение | Группа | Атрибуты | | --- | --- | | Идентичность | accountId, conversationId, clientId, optional originId, server stanzaId, archive UID | | Автор | sender JID, optional nickname/display name, incoming/outgoing | | Содержимое | kind, body, timestamp | | Доставка | sending / sent / delivered / read / failed | | Защита | encrypted / plaintext / decryptionFailed, optional device/fingerprint reference | | Медиа | remote URI, filename, MIME, byte count, duration, local asset reference | | Изменения | editedAt, retractedAt | | Reply | targetId, author JID, preview | | Пересылка | Атрибуция источника, без ложного сохранения исходного авторства нового сообщения | | Реакции | Автор → текущий набор emoji и timestamp | | Звонок | Направление, audio/video, исход, начало, duration | Kinds: text, attachment, photo, video, audio, voice, videoNote, location, system. Исторический звонок — системная запись с типизированными metadata. ### 5.4. Служебные сущности - archive checkpoint: account/room archive key, opaque cursor, timestamp; - local deletion suppression: account + conversation + идентификаторы удалённого сообщения; - device: owner JID, id, protocol support, identity fingerprint, active/trust; - attachment draft: staged asset, kind, filename, MIME, size, duration, thumbnail; - call session: id, endpoint full JID, direction, media, phase, tracks/control state; - pending operation: тип, target, автор, timestamp и ограниченный срок/объём удержания. ## 6. Аккаунты и вход **ACC-001.** Входная форма содержит JID, пароль, показать/скрыть пароль и дополнительные настройки. Пустой JID/пароль не принимаются. **ACC-002.** JID валидируется по выбранному XMPP address preparation; пробелы по краям ввода обрабатываются, resource не смешивается с bare account identity. Пароль не обрезается молча. **ACC-003.** Native-клиенты поддерживают discovery и ручной host/port с STARTTLS/direct TLS. Стандартные порты: 5222 для STARTTLS, 5223 для direct TLS. Порт проверяется как число 1…65535; нечисловой ввод показывает ошибку, а не молча превращается в default. **ACC-004.** Web-клиент использует защищённый WebSocket XMPP endpoint. Форма допускает ввод/выбор WSS endpoint и не обещает прямое использование произвольного TCP host/port браузером. BOSH не обязателен; если включён отдельно, допускается только HTTPS. **ACC-005.** Endpoint discovery не должен незаметно отправлять пароль произвольному стороннему шлюзу. Пользователь видит домен выбранного endpoint; для неизвестной конфигурации требуется явный выбор сервера. **ACC-006.** На время входа показывается progress и блокируется повторная отправка той же формы. После успешного входа открываются локальная история и основной интерфейс. Ошибка оставляет возможность исправить credentials. **ACC-007.** Можно сохранить несколько аккаунтов. Добавление аккаунта делает его активным после успешного входа. Одновременно обязательно только одно активное соединение; неактивные аккаунты не представляются как постоянно online. **ACC-008.** Переключение закрывает/отделяет старый контекст и открывает данные нового. Завершившаяся поздно операция прошлого аккаунта не может записать результат в новый аккаунт или отправить сообщение от его имени. **ACC-009.** При доступных credentials запуск восстанавливает активный аккаунт и локальную историю, затем соединение. Если secrets недоступны/истекли, приложение запрашивает повторный вход без потери сохранённой истории. **ACC-010.** Для каждого устройства создаётся устойчивый уникальный ресурс; одновременный запуск разных устройств одного аккаунта не должен создавать конфликт одинаковых default resources. ### 6.1. Выход и удаление данных **ACC-011.** «Выйти» прекращает соединение, unregister/disable применимую push-привязку, удаляет сохранённые credentials текущего аккаунта и очищает его чувствительное session state. Локальную историю можно сохранить. **ACC-012.** Отдельный вариант «Выйти и удалить локальные данные» удаляет историю, suppression, drafts, медиа cache и данные аккаунта в локальном профиле. Удаление OMEMO identity — явно обозначенный вариант со предупреждением о потере доступа к старым encrypted сообщениям; без согласия identity не уничтожается как побочный эффект обычного выхода. **ACC-013.** Удаление локальных данных не называется secure erase и не обещает удаление server archives, exports, backup или чужих копий. **ACC-014.** Перед уходом из аккаунта незавершённые записи/загрузки имеют определённое поведение: отмена либо сохранение безопасного локального результата; нет продолжения отправки от случайного другого аккаунта. ## 7. Соединение и аутентификация **NET-001.** Подключение защищено TLS; проверяются certificate chain и ожидаемое имя сервиса/endpoint согласно используемому транспорту. Insecure HTTP/WS и обход ошибок сертификата не включены в пользовательский стандарт. **NET-002.** Нативный транспорт поддерживает DNS discovery и ручные параметры. В Web используется WSS endpoint, утверждённый для выбранного сервера. **NET-003.** Аутентификация выбирает наиболее сильный совместимый объявленный механизм. Приоритет SCRAM-PLUS с доступным channel binding, затем SCRAM-SHA-512/256/1, затем разрешённый защищённым транспортом fallback. **NET-004.** PLUS не имитируется при отсутствии данных реального TLS-канала. В частности, реализации, не имеющие доступа к exporter/binding, выбирают совместимый non-PLUS механизм и корректно отражают это в диагностике. **NET-005.** Проверяются nonce, server proof/signature, параметры challenge и применимые downgrade-protection данные. Для limits на challenge/iterations определяется защитная конфигурация, предотвращающая блокировку клиента недоверенным вводом. **NET-006.** Состояния: отключено, подключение, подключено, восстановление, ошибка/требуется вход. Причина ошибки локализована. Нельзя показывать «Подключено» только по наличию сохранённого аккаунта. **NET-007.** Поддерживаются stream management, carbons, ping и client-state indication при поддержке транспорта/сервера. **NET-008.** Повторное соединение использует ограниченный backoff с jitter, прекращается при явном logout и не создаёт параллельных конкурирующих клиентов одного контекста. Возврат в foreground инициирует проверку/восстановление. **NET-009.** Смена сети, sleep/wake, закрытие вкладки и потеря credentials обрабатываются без повреждения локального состояния. Возобновление stream не должно повторно создавать bubbles уже известных сообщений. ## 8. Навигация и основные экраны **NAV-001.** Основные разделы: Контакты, Звонки, Чаты, Настройки. Начальный раздел — Чаты. Compact/wide варианты заданы UI Kit. **NAV-002.** Поиск расположен над соответствующим списком. Поиск чатов — по имени/JID/аккаунту, контактов — по имени/JID, звонков — по собеседнику. Это фильтр списков; полнотекстовый поиск истории отдельно не обещается. **NAV-003.** Чаты сортируются pinned-first при наличии pinned-функции, затем по последней активности, затем по имени для стабильности. Изменение unread не должно произвольно менять порядок при одинаковой активности. **NAV-004.** В списке видны avatar, имя, preview, время, unread и применимый status. Аккаунт отображается при необходимости различать одинаковые JID. **NAV-005.** Пустые данные и отсутствие совпадений поиска имеют разные объяснения. Создание чата/комнаты остаётся доступным в empty state. **NAV-006.** Back возвращает в предыдущий логический контекст. На широком экране смена selection не должна ломать sidebar; клавиатурная навигация обязательна для Web/Desktop. ## 9. Контакты, presence и аватары **CON-001.** Контакты синхронизируются через roster: initial state и последующие изменения. Удаление контакта из roster не удаляет историю. **CON-002.** Новый direct chat открывается по валидному JID с optional name и выбором добавления в roster. Отсутствие roster membership не запрещает сообщение само по себе. **CON-003.** Presence показывает реальное известное состояние; нет объявления online только из-за cached avatar/history. **CON-004.** MUC-комнаты показываются отдельно от людей. Список сохранённых комнат не выдаётся за полный серверный список членства. **CON-005.** Аватары публикуются/получаются через совместимые XMPP PEP mechanisms; legacy fallback допустим. Новый avatar нормализуется до разумного размера, baseline 512 px по длинной стороне. **CON-006.** При отсутствии изображения используется стабильный цвет и инициалы, для группы — group symbol. Обновление cache не требует перезапуска клиента. **CON-007.** Пользователю не обещается OMEMO для аватаров; доступ определяется серверными правами. ## 10. Текстовые сообщения и доставка **MSG-001.** Пустое после trim сообщение не отправляется. Каждая новая отправка имеет устойчивый уникальный client/origin id и зафиксированный аккаунт/получателя. **MSG-002.** До сетевого результата создаётся optimistic запись sending. Success transport send → sent, receipt → delivered, displayed → read. Ошибка → failed с объяснением. **MSG-003.** Sent означает передачу по клиентскому протокольному пути, delivered — полученный receipt, read — подтверждённый marker. UI не выводит read только из времени или online собеседника. **MSG-004.** Объединение live, Carbons и MAM не создаёт duplicate. Проверяются контекст аккаунта/чата и origin/stanza/client identities; archive UID не используется глобально для разных архивов. **MSG-005.** Доставка повышается монотонно sending → sent → delivered → read. Failed не «исправляется» молча архивной копией; явная повторная отправка — отдельное действие с контролем дубликатов. **MSG-006.** Порядок ленты — timestamp и устойчивый id при равных датах. Архивная вставка сверху сохраняет viewport. **MSG-007.** Служебные receipts, reactions, markers и пустые protocol messages не становятся обычными bubbles. Реальная ошибка дешифрования отображается как отдельное безопасное состояние. **MSG-008.** Чат с собой поддерживает текст/медиа и sync. Успешное собственное сообщение допускается помечать read по определённому self-chat правилу. **MSG-009.** У исходящего failed text доступно явное «Повторить», если payload сохранён. Повтор не должен незаметно менять encryption policy уже подготовленного сообщения; изменение режима требует нового явного решения. ### 10.1. Непрочитанные **READ-001.** Новое входящее увеличивает локальный unread, если чат не просматривается активно. Архивный replay не раздувает счётчик повторно. **READ-002.** При активном просмотре last-visible/latest incoming direct message отправляется displayed по протоколу; одно и то же состояние не рассылается без необходимости. **READ-003.** Outgoing direct messages помечаются markable. Для MUC приложение не рассылает direct displayed markers в комнату как универсальный механизм прочтения всеми участниками. **READ-004.** Поддерживается own-device sync read state через совместимое служебное расширение. Маркер, пришедший раньше сообщения, применяется после получения target. Пользователь не видит служебную запись в self-chat. **READ-005.** Read marker — metadata; нельзя заявлять его E2EE только по encrypted body исходного сообщения. ## 11. Действия над сообщениями ### 11.1. Доступность | Действие | Требование | | --- | --- | | Ответить | Не удалённое, не служебное; в MUC нужен server room stanza id | | Переслать | Не удалённое и не служебное | | Копировать | Есть пользовательский текст/подпись, не tombstone | | Редактировать | Собственный отправленный direct text | | Удалить у всех | Собственное отправленное direct сообщение, поддерживаемый retraction | | Удалить у меня | Локальная запись текущего аккаунта | | Реакция | Доступное пользовательское сообщение с устойчивым reference id | | Сохранить | Доступное/получаемое медиа по явному действию | | Повторить | Failed собственная отправка, payload ещё доступен | Для group edit/retraction базовый UI не обещает поддержку. Состояние sending ограничивает действия, требующие завершённой идентичности. ### 11.2. Редактирование **EDIT-001.** Используется совместимая message correction. Оригинал сохраняет свою идентичность и положение; заменяется body, устанавливается editedAt. **EDIT-002.** Применение проверяет чат, автора, допустимый тип, security context и timestamp. Сторонний отправитель не может исправить чужое сообщение. **EDIT-003.** Correction до оригинала удерживается в ограниченной pending очереди. Более старая correction не заменяет новую. **EDIT-004.** Шифрование исправления соответствует исходному сообщению; не допускается plaintext раскрытие исправляемого encrypted body из-за текущего глобального переключателя. ### 11.3. Ответы **REPLY-001.** Поддерживаются стандартная reply reference и fallback для совместимых старых клиентов. Unicode offsets считаются согласно протоколу, без смешения байтов и символов. **REPLY-002.** Отображается компактный preview источника, а fallback quote не дублируется в основном body. **REPLY-003.** Нажатие открывает источник/ветку, если reference известен. При недоступном оригинале показывается preview и объяснение без crash. **REPLY-004.** Legacy `>` quote допускается отображать без ложного target id. MUC replies ссылаются на room-assigned stanza id и корректный occupant identity. **REPLY-005.** Reference нельзя применить к другому чату с совпавшим id. Визуальная ветка — группировка reply, не отдельная серверная комната. ### 11.4. Пересылка **FWD-001.** Можно выбрать одно/несколько сообщений и переслать в существующий чат либо на валидный JID. Порядок: timestamp, устойчивый id. **FWD-002.** Создаются новые сообщения с новой identity и понятной атрибуцией. Пересылка не подделывает исходного sender. **FWD-003.** Media скачивается/расшифровывается и заново загружается для нового recipient и его encryption policy. Старый encrypted URI/key не переиспользуется автоматически. **FWD-004.** При частичной ошибке показывается количество успешно пересланного; уже доставленные элементы не откатываются и не повторяются молча. ### 11.5. Удаление **DEL-001.** «Удалить у меня» удаляет local row и сохраняет suppression всех известных identities, чтобы MAM/live echo не восстановили удалённое сообщение. **DEL-002.** «Удалить у всех» отправляет retraction, проверяет авторство при применении и заменяет target на tombstone. Очищаются body/media/reply/reaction sensitive payload и связанные доступные cache. **DEL-003.** Retract до оригинала удерживается pending. Generic fallback не повторяет удаляемое содержимое. **DEL-004.** UI явно сообщает разницу локального удаления и отзыва. Ни одно не обещает стирание screenshots, backups, чужих exports или неподдерживающего клиента. **DEL-005.** Массовое действие фильтрует/объясняет недоступные элементы; подтверждение показывает область действия и число сообщений. ## 12. Реакции и typing **REACT-001.** Реакции совместимы с XMPP reactions: target id и полный актуальный набор автора; пустой набор снимает реакцию. **REACT-002.** Emoji валидируются, дубликаты удаляются. Один reaction value — один grapheme, ограниченный безопасной длиной; baseline до 64 UTF-8 bytes. **REACT-003.** Показываются emoji, count и own state. Повторный tap переключает свой вклад. Более старое событие не отменяет новое. **REACT-004.** Direct авторы сравниваются по подготовленному bare JID, group — по корректной occupant identity. Нельзя объединять разных участников из-за неверной нормализации resource. **REACT-005.** Реакции не шифруются автоматически только из-за OMEMO текста. Базовая совместимая stanza является видимой серверу metadata; отдельное encrypted extension требует самостоятельного согласования и interoperability tests. **TYPE-001.** Typing имеет active/composing/paused/inactive/gone. Baseline пауза после 5 с, удалённый composing истекает через 8 с без обновления. **TYPE-002.** Настройка typing per-account; выключение прекращает отправку/отображение и очищает pending indicators. Для direct учитывается поддержка peer. **TYPE-003.** В группе показываются имена для одного/двух участников, затем обобщённый статус. Переход между чатами/уход из foreground не оставляет вечное «печатает». ## 13. Группы MUC **MUC-001.** Пользователь создаёт/открывает room JID, задаёт nickname и optional name; для защищённых комнат предусмотрен запрос password при требовании сервера. **MUC-002.** Поддерживаются invitations, accept/join, occupant count, autojoin и информация о комнате. Invitees валидируются; некорректные адреса явно обозначаются. **MUC-003.** Новая encrypted-friendly комната конфигурируется persistent, members-only, nonanonymous и с доступными участникам affiliation lists, если сервер даёт эти права. **MUC-004.** Для encrypted send проверяются real JID всех необходимых online/offline recipients через occupants и member/admin/owner lists. Недоступный/неполный набор получателей блокирует безопасную отправку. **MUC-005.** Скрытые JID, отсутствие устройств или невозможность построить session не вызывают fallback к plaintext. Ошибка объясняет, что может изменить владелец/участник. **MUC-006.** Own echo дополняет room stanza identity даже при повторном encrypted payload. Replies/reactions становятся доступны после устойчивой серверной identity. **MUC-007.** «Выйти» сохраняет локальный чат/историю и снимает joined. «Удалить чат» удаляет локальную историю/сохранённую комнату после выхода; это не уничтожение комнаты сервера. **MUC-008.** После локального удаления поздние события текущей room session не создают чат заново без явного основания. Повторное приглашение/вход имеют определённую семантику. ## 14. Шифрование и устройства **ENC-001.** OMEMO включён по умолчанию для аккаунта. Per-chat override: наследовать / включено / выключено. UI показывает итоговый режим до отправки. **ENC-002.** Ошибка encryption никогда не приводит к автоматической plaintext отправке. Plaintext возможен только при явно выбранной политике. **ENC-003.** Поддерживаются legacy OMEMO и OMEMO 2 с совместимыми wire formats, device lists/bundles и согласованной lifecycle политикой identity/session. **ENC-004.** Для direct предпочтителен OMEMO 2 при готовности и поддержке peer; иначе совместимый legacy. Для group OMEMO 2 используется только при поддержке всех recipients; legacy остаётся encrypted fallback. **ENC-005.** Encrypt-to-self охватывает собственные другие устройства, чтобы Carbons/MAM могли быть прочитаны. Active own device не включается как чужой recipient с некорректной обязательной self-session. **ENC-006.** Private identity, prekeys и sessions сохраняются устойчиво и раздельно по аккаунтам. Обновление приложения не создаёт новую identity без необходимости. **ENC-007.** TOFU и verified различаются. Первое доверие отмечается как автоматическое; changed identity переводится в undecided и требует понятного решения. Compromised key не используется как trusted. **ENC-008.** Пользователь видит список устройств, protocol support, fingerprint, active state и trust, может отметить проверенный fingerprint и снять verification. **ENC-009.** OMEMO encryption/decryption выполняются в клиентском endpoint. Web gateway может передавать encrypted stanza, но не получает password/crypto state для скрытой серверной расшифровки. Применяются проверенные криптографические реализации и совместимые test vectors. Собственный новый алгоритм, замена примитивов или трактовка общего session storage как доказанной interoperability не допускаются. **ENC-010.** Зашифрованный text не означает шифрование JID, времени, stanza ids, replies metadata, reactions, typing, avatars или custom sync. Матрица раскрываемых metadata является частью security документации каждого релиза. **ENC-011.** Криптографический формат OMEMO 2 реализуется по утверждённой совместимой редакции с SCE и необходимыми payload/key/MAC rules; он не заменяется media AES-GCM только из-за удобства библиотеки. ## 15. Файлы и вложения **FILE-001.** Поддерживаются файл, фото, видео, музыка, voice и videoNote. Тип определяется безопасно по metadata/MIME/доступному анализу; расширение не даёт права автоматически выполнять содержимое. **FILE-002.** Можно выбрать до 20 элементов за пакет, просмотреть, удалить отдельные и добавить общую подпись. Перевышение лимита объясняется; элементы не теряются молча. **FILE-003.** Источник копируется/удерживается в staged draft с достаточным сроком доступа. Не предполагается, что временный URL picker остаётся доступным после его закрытия. **FILE-004.** При encrypted policy blob шифруется до upload случайным file key; ключ передаётся внутри защищённого сообщения, не upload service. Для plaintext это явно обозначено. **FILE-005.** Используется HTTP File Upload discovery/slot. Выбирается подходящий component по реально загружаемому размеру. Ограничение сервера и памяти отображается до бессмысленного повторения операции. **FILE-006.** Upload/download допускаются по HTTPS. Проверка распространяется на redirect chain; переход к небезопасной схеме не допускается. HTTP headers/credentials slot не отправляются произвольному другому origin после redirect. **FILE-007.** Сохраняется исходное безопасное расширение filename. Encrypted media URI использует совместимую aesgcm scheme/key fragment, а не вымышленное обязательное расширение файла. **FILE-008.** Body media-сообщения остаётся распознаваемой ссылкой; plaintext дополнительно передаёт совместимый OOB. Подпись пакета — отдельный text/reply к первому подходящему вложению. **FILE-009.** Пакет отправляется с контролем порядка. Successful drafts удаляются из pending, failed остаются для retry; caption отправляется после успешных вложений. Ошибка caption не откатывает уже отправленные файлы. **FILE-010.** Retry сохраняет account/recipient/message identity и encryption context. Сбой после PUT, но до XMPP send может оставить orphan blob; это не обозначается как гарантированно удалённый файл. **FILE-011.** Загрузки поддерживают progress по доступным данным, cancellation и bounded retry. Недостаток диска/памяти обрабатывается как ошибка без повреждения остальных сообщений. **FILE-012.** В Web endpoint обязан поддерживать необходимые browser access/CORS политики. Proxy не получает file key или decrypted blob для обхода ограничений незаметно для пользователя. ### 15.1. Совместимые transport metadata Созданные Luma voice/videoNote сохраняют распознаваемое имя при загрузке: | Тип | Формат transport filename | | --- | --- | | Voice | `luma-voice--` | | VideoNote | `luma-videonote--` | | Другой файл | Безопасное исходное имя с расширением | Duration — положительное конечное значение, округлённое до миллисекунд. При отсутствии валидной duration имя не должно содержать неверную/отрицательную длительность. Получатель распознаёт специальные prefixes, обычный MIME/расширение и прежние совместимые encrypted URI. Transport filename и размер доступны upload service; они не являются секретными metadata. `byteCount` сообщения относится к исходному файлу; лимит slot сравнивается с размером реально загружаемого encrypted/plain blob. ### 15.2. Baseline сетевых параметров | Параметр | Значение по умолчанию | | --- | --- | | Upload request timeout | 120 с | | Upload total resource timeout | 300 с | | Automatic attempts | 2 суммарно | | Задержка перед второй попыткой | 600 мс, допустим небольшой jitter | | Retryable HTTP | 408, 429 с учётом Retry-After, 5xx | | Неповторяемые без изменения условий | Insecure URL, invalid input/key, неподходящий размер, постоянный permission error | Повтор discovery/slot/transport ограничен. Таймауты конфигурируемы и не являются обещанием скорости сети. ## 16. Просмотр и сохранение медиа **MEDIA-001.** Photo/video имеют inline preview с сохранением aspect ratio без неожиданного crop. Loading/error/placeholder различаются. **MEDIA-002.** Фото открывается в viewer с zoom/pan. Видео имеет play/pause/seek/fullscreen и доступные системные controls. Voice/audio — inline player, duration, seek, progress; voice speeds 1×/1.5×/2×. **MEDIA-003.** В пределах приложения активный media playback согласован: начало другого audio/video не создаёт неконтролируемое одновременное звучание. Системный audio focus обрабатывается по платформе. **MEDIA-004.** VideoNote показан кругом, воспроизводится inline и увеличивается при playback. Изменение размера не вызывает потерю позиции чтения. **MEDIA-005.** Preview cache не скачивает один файл многократно из-за повторного появления row. Decrypted cache ограничивается размером/сроком и очищается согласно privacy/account lifecycle. **MEDIA-006.** Экспорт/скачивание/копирование выполняются явно. Пользователь выбирает доступное платформенное место сохранения; отмена не показывает success. **MEDIA-007.** Viewer закрывается доступной кнопкой/keyboard action и gesture по UI Kit. Zoomed pan не конфликтует с dismiss. **MEDIA-008.** Неподдерживаемый codec/file открывается безопасным fallback/download с объяснением; не имитируется успешное воспроизведение. ## 17. Запись аудио, видео и геопозиция **REC-001.** Разрешения microphone/camera запрашиваются по действию пользователя. Отказ не ломает текстовый composer. **REC-002.** Voice: start, elapsed, cancel, finish; запись короче 0.4 с не отправляется. Длительность голоса не ограничивается автоматически 60 секундами: ограничения файла/сервера действуют отдельно. **REC-003.** VideoNote: до 60 с, корректная ориентация, переключение камеры при доступном hardware. Минимальный валидный media asset — 0.8 с; early stop может дождаться достаточного capture, baseline 1.2 с. **REC-004.** Отпускание/отмена до асинхронного старта capture не оставляет скрытую запись. Finalization имеет timeout и завершает UI в определённое состояние. **REC-005.** При interruption пригодный partial asset может быть предложен для восстановления/предпросмотра, но не отправляется автоматически как подтверждённое действие. **REC-006.** На touch поддерживаются hold/slide cancel/lock. На mouse/keyboard всегда есть явные start/stop/cancel; жест не является единственным способом записи. **GEO-001.** Отправка текущей или выбранной точки доступна через меню вложения. Разрешение location запрашивается только в этом сценарии. **GEO-002.** При denied/отсутствии GPS доступен ручной выбор координат на карте либо ввод точки. Live tracking в scope не входит. **GEO-003.** Передаётся совместимый geo URI: finite latitude −90…90, longitude −180…180, optional nonnegative uncertainty. UI не выдаёт число знаков после запятой за точность датчика. **GEO-004.** Карта/tiles provider и передача ему coordinates отражены в privacy конфигурации решения. OMEMO чата не скрывает запрос карты от её провайдера. ## 18. Локальная история и MAM **HIST-001.** История хранится локально с изоляцией аккаунтов. Существующие сообщения доступны при временном отсутствии сети, если устройство/профиль допускает persistent storage. **HIST-002.** На первом входе загружается ограниченное недавнее окно, baseline 40 сообщений. Полная многолетняя история не блокирует запуск; старое догружается по запросу/прокрутке. **HIST-003.** Account archive и каждая MUC имеют собственные checkpoint. Cursor непрозрачен, не вычисляется из local id и не переносится между архивами. **HIST-004.** Incremental запрос использует stored cursor. При его истечении допустим однократный timestamp fallback с overlap 60 с. Цикл cursor↔timestamp запрещён. **HIST-005.** Проверяются query id, допустимый источник, порядок завершения и размер страницы. Чужой результат/overflow не считается успешной историей. **HIST-006.** Missing/repeating cursor у incomplete page прекращает проход с recoverable error. Не допускаются вечный spinner и пропуск остатка как якобы complete. **HIST-007.** Архивные mutations применяются согласованными пачками. Durable checkpoint продвигается только после успешной локальной транзакции соответствующих данных. Ошибка диска сохраняет предыдущую точку восстановления. **HIST-008.** Backward pagination берёт следующий before из server RSM first даже если страница содержала только reactions/retractions/дубликаты. **HIST-009.** Pending correction/retraction/reaction/read marker применяются после target. Очереди ограничены и очищаются по политике; overflow не создаёт crash. **HIST-010.** Расшифрование и большие batches уступают время UI. Background/media capture/upload могут временно снижать архивную активность; возобновление не теряет checkpoint. **HIST-011.** Автоматические retries ограничены; после исчерпания показывается recoverable state и доступно повторить. Незавершённый проход не объявляется «История полностью загружена». **HIST-012.** Локальная миграция выполняется транзакционно; исходный формат удаляется после успешной записи. Upgrade не должен уничтожать единственную durable копию данных. ### 18.1. Baseline ограничений | Параметр | Значение | | --- | --- | | MAM page/bootstrap | 40 / 40 | | Максимум buffered stanza одной query | 96 | | Decode slice | 8, с уступкой UI между срезами | | Ориентир порции применения | До 2 страниц или 2 с перед yield/commit | | Query/apply timeout | 20 / 20 с | | Interactive backward query timeout | 15 с | | Межстраничная пауза | 100 мс | | Page retry | 1 дополнительный | | Automatic catch-up recovery | До 3 повторов, пауза 20 с | | Pending corrections/retractions | До 100 каждого вида | | Pending reactions | До 200 | Порция работы не ограничивает всю историю двумя страницами: после yield допустимо продолжение. Реализация может уменьшать slice по нагрузке и увеличивать configurable server-compatible limits после измерений, но не отменять контроль объёма. ## 19. Звонки **CALL-001.** Поддерживаются аудио-/видеозвонки один на один, один активный звонок на клиентский контекст. Второй входящий/исходящий получает определённый busy/reject сценарий. **CALL-002.** До запуска запрашиваются microphone/camera permissions. Есть incoming accept/decline, connecting, connected, elapsed и завершение. **CALL-003.** Доступны mute, camera enable/disable, switch camera при нескольких камерах и speaker/audio output выбор в пределах платформенных возможностей. **CALL-004.** Используется совместимый Jingle/JMI signaling и WebRTC media с ICE/DTLS-SRTP. Endpoint выбирается как подходящий full JID; последующие сообщения согласованы с тем же ресурсом. **CALL-005.** SDP media sections сохраняют порядок offer. ICE candidates удерживаются до готового remote description, затем передаются/применяются без вечного ожидания bare/full JID. **CALL-006.** STUN/TURN обнаруживаются через extdisco или утверждённую конфигурацию. Проверяются scheme, host/port, transport, expiration и credentials. Невалидная запись не приводит к crash медиадвижка. **CALL-007.** TURN relay необходим для утверждённой тестовой матрицы сложных сетей. Отсутствие relay показывается диагностически; STUN не описывается как гарантия соединения за любым NAT. **CALL-008.** Внешний fallback STUN допускается только как задокументированный конфигурационный выбор. Сервис-получатель сетевых адресов отражается в privacy документации. **CALL-009.** Смена сети/краткая ICE failure имеют ограниченное recovery окно. После него звонок завершается корректно, освобождает tracks/camera/microphone и снимает active state. **CALL-010.** Закрытие экрана звонка не может оставить скрыто включённый microphone/camera вопреки показанному состоянию. Background behavior задаётся интеграцией платформы, не визуальным состоянием окна. ### 19.1. История **CALL-011.** Каждый звонок создаёт одну history card: направление, media type, начало, duration и outcome. Outcomes: completed, declined, missed, cancelled, unanswered, failed, answeredElsewhere. Если media было connected, последующая transport error не переименовывает состоявшийся разговор в неудачную попытку; duration сохраняется. Классификация до connected: отказ стороны → declined; собственная отмена → cancelled; timeout incoming ringing → missed; timeout outgoing ringing → unanswered; сбой negotiation → failed; подтверждённый ответ другим собственным устройством → answeredElsewhere. **CALL-012.** Missed incoming увеличивает unread. Дубликаты completion callbacks не создают несколько карточек. **CALL-013.** Call history синхронизируется с собственными устройствами через совместимое расширение и MAM/Carbons, если сервер хранит такие события. Metadata не выдаётся за OMEMO body. ### 19.2. Baseline таймеров | Фаза | Значение | | --- | --- | | Ringing | 60 с | | Negotiation | 90 с | | ICE failed grace | 8 с | | ICE disconnected grace | 12 с | Таймеры должны согласовываться с сервером и remote signaling; они не гарантируют фонового пробуждения. ## 20. Уведомления, push и фоновая работа **PUSH-001.** Поддерживаются локальные уведомления для новых событий активного клиента и отдельная push-интеграция для разрешённых платформенных состояний. **PUSH-002.** Notification создаётся для нового incoming, не для outgoing/повторного MAM. В активно просматриваемом чате дублирующий banner подавляется. **PUSH-003.** Уведомление содержит безопасную идентичность account/conversation/event для перехода в правильный контекст. Повторная доставка push и network event дедуплицируется. **PUSH-004.** Default privacy: push payload не содержит plaintext body, file key или credentials. Preview после получения сообщения определяется пользовательской настройкой; при app lock default preview скрыт. **PUSH-005.** Gateway/registration lifecycle включает register/update token, enable по XMPP, disable/logout/revoke и обработку expired token. Наличие server push feature без registration не отображается как «Push работает». **PUSH-006.** Push — сигнал получения/пробуждения, а не источник истины истории. После него клиент синхронизируется с сервером; утерянный push не должен навсегда потерять сообщение. **PUSH-007.** Базовый single-active-account режим регистрирует доставку для активного аккаунта; переключение согласует старую и новую registration. Уведомления неактивных аккаунтов не обещаются без отдельно реализованного multiaccount background режима. Поздний push сохраняет исходную account identity и не открывает переписку другого аккаунта. ### 20.1. Web **WEB-001.** Для уведомлений вне открытой страницы поставляется поддержанный Web Push/service worker путь и gateway, если браузер/пользователь предоставили возможность. При отсутствии поддержки/permission приложение честно показывает статус «Только при открытом приложении». **WEB-002.** Закрытая страница не считается постоянно подключённой XMPP-сессией. Service worker notification не гарантирует непрерывный microphone, медиазвонок или автоматический показ полноэкранного incoming UI. **WEB-003.** Incoming push может уведомить о вызове и открыть приложение по действию. После открытия проверяются актуальность и состояние вызова; истёкший вызов не «принимается» из старого notification. **WEB-004.** Несколько вкладок координируют активное соединение/device state либо используют изолированные ресурсы с безопасным доступом к crypto state. Не допускаются конкурентные повреждающие записи ratchet sessions. ### 20.2. Desktop **DESK-001.** Пользователь понимает разницу «закрыть окно», «скрыть/свернуть» и «выйти из приложения». Работа в tray/фоновой сессии и автозапуск — явно управляемые настройки. **DESK-002.** При живом фоновом процессе поддерживаются сообщения и incoming calls. Полностью завершённый процесс не получает обещание гарантированного пробуждения без отдельного системного механизма. **DESK-003.** Sleep/wake, network change и logout восстанавливают/отключают соединение и push registration корректно. Tray icon не показывает active call после завершения. ### 20.3. Android **AND-001.** Поставляется интеграция системной доставки уведомлений и входящих вызовов для утверждённого режима устройства. Выбор push provider и доступность устройств без соответствующего сервиса фиксируются в паспорте релиза. **AND-002.** Фоновая работа соответствует lifecycle/permissions ОС. Постоянный socket в обычном скрытом UI не используется как единственная гарантия доставки. **AND-003.** Для ongoing call используется разрешённый системный foreground/call механизм с видимым статусом; control из notification согласован с app state. **AND-004.** Force stop, отозванные разрешения, ограничения батареи и отключённая сеть имеют задокументированную деградацию. Приложение не обещает обход пользовательского запрета ОС. ### 20.4. Приёмка интеграции Push и фоновые звонки нельзя принять только по работающему foreground demo. Для каждой платформы проверяются foreground, скрытый UI, закрытый процесс/вкладка, denied permission, sleep, token rotation, logout и переход по старому notification. Недоступные платформой режимы обозначаются как ограничения, а не как выполненная гарантия доставки. ## 21. Локальная защита и хранение **STORE-001.** История, crypto state и настройки раздельны по accountId; операции сохранения транзакционны либо имеют проверяемый equivalent recovery механизм. **STORE-002.** Native credentials защищены системным secure storage; private key material защищается подходящим устройству key wrapping/access control. Пароль не хранится в открытом preferences файле. **STORE-003.** В Web пароль не хранится plaintext в localStorage, URLs, analytics или общедоступных configuration objects. Persistent login и разблокировка crypto state используют отдельное задокументированное защищённое решение; отсутствие такого решения означает повторный вход, а не незаметное ослабление. **STORE-004.** Web storage может иметь квоты/очистку. Приложение обрабатывает quota/exhaustion и показывает, когда история не сохранена устойчиво. Browser storage не представляется эквивалентом hardware-backed native vault. **STORE-005.** Media cache имеет лимит и eviction. Draft/failed payload не исчезает без объяснения; при невозможном retry действие отключено с причиной. **STORE-006.** Автосохранение не теряет введённый текст при переходах/обычном восстановлении. Draft принадлежит account + conversation; он не переносится в другой чат из-за reuse UI. **STORE-007.** Миграции versioned и тестируются на старых данных/сбоях. Ошибка открытия persistent store показывает ограниченный режим, а не ложное подтверждение durable записи. **LOCK-001.** App lock скрывает всю sensitive UI: чат, media, call details и previews. Поддерживаются пользовательский passcode и доступная системная биометрия. **LOCK-002.** Минимальная длина passcode 4 символа, предусмотрены ограничение частоты ошибочных попыток и корректное хранение verifier/секрета. App lock не объявляется шифрованием базы без отдельного encryption механизма. **LOCK-003.** После запуска и ухода в background lock применяется по настройке. Краткая потеря focus для системного picker/permission dialog не прерывает незавершённую операцию. **LOCK-004.** Web не обещает native biometric unlock на любом браузере; при отсутствии защищённой интеграции остаётся passcode либо повторная авторизация. Lock settings показывают фактическую возможность. ## 22. Приватность и безопасность **SEC-001.** Документируется разделение transport TLS, OMEMO payload, file encryption, DTLS-SRTP media, local protection и UI lock. Термины не подменяют друг друга. **SEC-002.** Server/upload/STUN/TURN/maps/push providers получают только необходимые данные. Перечень providers и metadata доступен пользователю. **SEC-003.** Логи исключают passwords, tokens, private/session keys, decrypted bodies и file key fragments. Сетевые трассы по умолчанию обезличены; диагностический экспорт требует явного действия и предупреждения о metadata. **SEC-004.** Полученные text/filenames/URLs/XML не выполняются как HTML/script/команда. Web rendering безопасен для XSS; переход по внешним URL не передаёт секретный контекст. **SEC-005.** У ограничений входных данных есть предельный размер/depth/count. Недоверенные stanza, challenges, media metadata и custom sync parsers не должны создавать бесконечную работу/выделение памяти. **SEC-006.** Copy/export явно выводит plaintext за границы клиента; локальная очистка не обещает удалить clipboard или пользовательский файл. **SEC-007.** «Удалить у всех» — протокольный отзыв; «Удалить локальные данные» — удаление собственного профиля. В UI нет обещания абсолютного стирания всех копий. **SEC-008.** Для push/служебной own-device синхронизации проверяется источник и принадлежность аккаунту. Remote peer не может подделать собственную call/read history только прислав похожий XML element. ## 23. Совместимость протоколов Обязательный профиль включает: | Область | Протокол/семантика | | --- | --- | | XMPP | Базовые stream/session, roster, presence; XEP-0030 discovery | | Web transport | Защищённый XMPP WebSocket endpoint | | Устойчивость | XEP-0198 Stream Management, XEP-0199 Ping, XEP-0280 Carbons, XEP-0352 CSI | | Группы | XEP-0045 MUC, invitations/room configuration и MUC-MAM | | История | XEP-0313 MAM, XEP-0059 RSM, XEP-0359 origin-id/stanza-id | | Доставка | XEP-0184 Receipts и XEP-0333 Chat Markers | | Действия | XEP-0308 Correction, XEP-0461 Reply, XEP-0428 Fallback, XEP-0444 Reactions, XEP-0424 Retraction | | Медиа | XEP-0363 HTTP Upload, XEP-0066 OOB, XEP-0454 encrypted file URI | | Защита | XEP-0384: совместимые legacy/OMEMO 2; XEP-0420 SCE для нового формата | | Аватары | XEP-0084 User Avatar PEP, optional legacy compatibility | | Звонки | XEP-0166/0167 Jingle/RTP, XEP-0353 JMI, ICE/DTLS-SRTP, XEP-0215 extdisco | | Push | XEP-0357 registration и платформенная доставка | ### 23.1. Own-device расширения Для совместимости устройств Luma используются согласованные пространства имён: - `https://luma.chat/read-marker`: conversation JID, target origin/client id, optional stanza id и timestamp; без видимого body; - `https://luma.chat/call-history`: peer JID, direction, outcome, audio/video, startedAt, duration и устойчивый event id. Wire names read-marker: `conversation`, `id`, optional `stanza`, `when`; call-history: `with`, `direction`, `status`, `video`, `start`, `duration`. Даты сериализуются ISO 8601 с timezone, duration — неотрицательные секунды, ids — непрозрачные строки. Origin-id внешней stanza служит устойчивой идентичностью события. Отсутствие неизвестной новой версии не должно ломать ранее поддержанный payload. Схемы versioned при изменении; неизвестные поля игнорируются безопасно, обязательные валидируются. Повторная доставка идемпотентна. Совместимость с существующими payload не ломается без миграции. Эти расширения не считаются универсально поддерживаемыми всеми XMPP-клиентами. ### 23.2. Матрица испытаний Релиз подтверждает обмен с минимум двумя независимыми XMPP-клиентами из утверждённого набора и минимум одним поддерживаемым сервером. Отдельно проверяются legacy/OMEMO 2, direct/MUC, media и calls. Поддержка XEP по имени без сценариев interoperability недостаточна. ## 24. Нефункциональные требования **NFR-001.** UI Kit является обязательным стандартом visual/interaction behavior. Small/wide layout определяется доступной шириной, не строкой User-Agent. **NFR-002.** Списки/лента виртуализируются или используют equivalent bounded rendering. Большой архив не создаёт все визуальные rows одновременно. **NFR-003.** Проект задаёт измеряемые бюджеты startup, RAM, cache и frame responsiveness для утверждённых reference devices. До утверждения devices не заявляется произвольный универсальный SLA. **NFR-004.** Encryption/decoding/uploads уступают ресурсы active input/capture. Scroll и text entry остаются доступными во время catch-up. **NFR-005.** Системные permissions запрашиваются по контексту. Denied, hardware missing и revoked during session входят в тестовую матрицу. **NFR-006.** Web/Desktop полностью управляются клавиатурой; Android поддерживает системную accessibility. Focus, text scale, reduced motion и contrast проверяются по UI Kit. **NFR-007.** Сборки воспроизводимы: pinned/locked dependencies, migrations, build instructions, CI, license notices и release artifacts. Выбор framework не включён в пользовательские требования. **NFR-008.** CI запускает unit/integration checks и smoke UI tests для поставляемых targets. Подпись/публикация и secrets отделены от публичного репозитория. **NFR-009.** Crash/error reports не включают sensitive content по умолчанию. Телеметрия имеет утверждённую privacy-конфигурацию. ## 25. Критерии приёмки Каждый сценарий выполняется на каждой заявленной платформе либо имеет заранее согласованную аппаратную/системную деградацию. «Не удалось проверить» не равнозначно «Выполнено». | Сценарий | Ожидаемый результат | Требования | | --- | --- | --- | | Первый вход/ошибка/перезапуск | Верный lifecycle, история сохранена, secrets не раскрыты | ACC-001…010 | | Нечисловой port / WSS endpoint | Понятная validation и правильный транспорт | ACC-003…005 | | Переключение во время upload | Нет отправки/записи от другого аккаунта | ACC-008,014 | | Logout с удалением/сохранением | Объяснённая область очистки и push disable | ACC-011…013 | | TLS/auth отказ | Нет ослабления доверия или подделанного PLUS | NET-001…005 | | Смена сети/sleep/reconnect | Один согласованный клиент, без duplicate | NET-006…009 | | Text/receipts/displayed | sending/sent/delivered/read имеют разные основания | MSG-001…005 | | Replay live/Carbons/MAM | Одна запись, правильные ids/delivery | MSG-004,005 | | Draft переход между чатами | Текст остаётся у правильного recipient/account | STORE-006 | | Edit/retract до оригинала | Отложенное корректное применение | EDIT-003, DEL-003 | | Чужая correction/retraction | Не изменяет чужое сообщение | EDIT-002, SEC-008 | | Reply XML/legacy/MUC | Корректный preview/reference без вымышленного id | REPLY-001…005 | | Multi-forward и ошибка | Порядок, новая encryption policy, partial result | FWD-001…004 | | Local delete + MAM | Удалённое не возвращается | DEL-001 | | Реакция/снятие/старое событие | Count/own state и порядок актуальны | REACT-001…005 | | Typing timeout/отключение | Нет вечного composing | TYPE-001…003 | | Group hidden recipients | Fail closed, без plaintext | MUC-003…006 | | OMEMO2/legacy/changed identity | Совместимый формат, правильное доверие | ENC-003…009 | | Upload HTTPS/redirect/error | Secret-safe transport и bounded retry | FILE-004…012 | | 20 media, partial failure, caption | Ошибки не уничтожают успешное/оставшееся | FILE-002,009 | | Viewer/player/export cancel | Correct ratio/control и отсутствие ложного success | MEDIA-001…008 | | Permissions/short record/early release | Нет скрытой capture и autosend partial | REC-001…006 | | Location denied/manual | Точка отправляется без фонового tracking | GEO-001…004 | | Fresh MAM/repeating cursor/disk failure | Checkpoint не теряется/не перескакивает | HIST-002…012 | | JMI/full JID/SDP/late ICE | Correct endpoint и ограниченный recovery | CALL-004…009 | | Call outcome/sync/duplicate callback | Одна правильная history card | CALL-011…013 | | Push и network event вместе | Одна notification/event, правильный чат | PUSH-001…006 | | Web вкладка закрыта / stale call | Notification/open по доступной платформе, без ложного active call | WEB-001…004 | | Desktop скрыт/завершён/sleep | Поведение соответствует заявленному lifecycle | DESK-001…003 | | Android background/permission/force stop | Проверенная доставка и честная деградация | AND-001…004 | | Lock / preview / export | Sensitive UI скрыта, границы защиты объяснены | LOCK-001…004, SEC-006 | | Quota/migration/cache eviction | Нет ложного durable success/повреждения | STORE-001…007 | | Keyboard/text scale/dark/reduced motion | Доступный интерфейс | NFR-006, UI Kit | ## 26. Состав результата разработки 1. Устанавливаемое/развёрнутое приложение каждого согласованного target. 2. Исходники, locked dependencies, reproducible build и CI. 3. Реализованный набор UI components/tokens по `uikit_clear.md`. 4. Версионированные data schemas/migrations и протокольные adapters. 5. Push/gateway/TURN/WSS конфигурация в пределах выбранной инфраструктуры, без secrets в репозитории. 6. Unit, integration, interoperability и UI/accessibility tests с результатами. 7. Пользовательская справка, privacy/security описание и известные ограничения. 8. Паспорт релиза и checklist поддерживаемых систем. ## 27. Паспорт конкретного приложения До начала platform-specific реализации фиксируются решения, не изменяющие пользовательский контракт: | Решение | Что должно быть указано | | --- | --- | | Поддерживаемые targets | Desktop ОС, Android min/target, Web браузеры и режим PWA | | Технологии | UI/runtime, XMPP/crypto/media libraries и стратегия shared code | | Транспорт | Native discovery и Web endpoint configuration | | Backend | WSS, push gateway/provider, TURN, upload и maps | | Secrets | Native vault, Web persistent-login/crypto unlock модель | | Lifecycle | Что означает закрыть окно/вкладку, background и explicit quit | | Лимиты | Размеры файлов/cache, pending limits, performance reference devices | | Совместимость | Серверы/клиенты и редакции encrypted wire formats | | Дистрибуция | Signing, обновления, hosting и каналы поставки | Паспорт заполняется до выпуска. Отсутствующее решение не заменяется непроверенным обещанием общей поддержки всех ОС, браузеров или фоновых состояний.