|
|
|
|
@ -14,6 +14,7 @@
|
|
|
|
|
- Подготовка, шифрование, проверка и durable публикация медиа в рабочих потоках. |
|
|
|
|
- Прямая передача файлов либо custody на отдельном M, доставка после restart и подтверждённое удаление. |
|
|
|
|
- TTL и квота M; вложения и статусы доставки в Qt и Android. |
|
|
|
|
- Голосовые Opus и видео MP4 с зашифрованными метаданными; общий ввод и отображение с каналами. |
|
|
|
|
|
|
|
|
|
## Идентичность и шифрование |
|
|
|
|
|
|
|
|
|
@ -37,9 +38,20 @@ AES-256-CCM: nonce 13 байт, tag 16 байт. Максимальная пор
|
|
|
|
|
|
|
|
|
|
Автор подписывает все поля до sig. conv связывает автора с получателем. |
|
|
|
|
Для `ct=file` перед подписью добавляется descriptor `[UUID:16][plaintext_size:8][SHA256(ciphertext):32]`. |
|
|
|
|
В зашифрованном теле находится имя файла; пути хранения строятся только из UUID. |
|
|
|
|
В зашифрованном теле находится `attachment_info`; пути хранения строятся только из UUID. |
|
|
|
|
Размер plaintext сообщения ограничен 1024 байтами. seq — положительное int64. |
|
|
|
|
|
|
|
|
|
```text |
|
|
|
|
[kind:1][name_len:2][duration_ms:4][width:2][height:2][UTF8_name:name_len][waveform:100, только voice] |
|
|
|
|
``` |
|
|
|
|
|
|
|
|
|
Поля метаданных многобайтной длины имеют big-endian формат. kind: 1=file, |
|
|
|
|
2=voice, 3=video. Имя — basename до 255 байт, без разделителей, управляющих символов |
|
|
|
|
и некорректного UTF-8. FILE имеет нулевые duration/dimensions; VOICE — положительную |
|
|
|
|
длительность и 100 символов уровней waveform; VIDEO — длительность и размеры. |
|
|
|
|
Длительность ограничена 24 часами, размеры — 16384. Метаданные проверяются до |
|
|
|
|
локального commit и до подтверждения получателем; S/M видят только ciphertext. |
|
|
|
|
|
|
|
|
|
Медиа ciphertext — последовательность порций с отдельным tag каждой порции. |
|
|
|
|
Последняя порция короче; пустой файл содержит один tag. Размер plaintext и SHA256 |
|
|
|
|
ciphertext должны входить в подписанный manifest. Потоковая расшифровка проверяет все |
|
|
|
|
@ -54,6 +66,9 @@ tags, точный размер и хеш; владелец публикует
|
|
|
|
|
- `dm_media`: файлы endpoint, загрузки и отдельная custody M. |
|
|
|
|
- `dm_mailbox_media`: durable задания S, выбор M и повтор удаления до DELETE_ACK. |
|
|
|
|
- `file_transfer`: подписанная передача байтов, backpressure и отдельный CM handle каждой операции. |
|
|
|
|
- `attachment`: формат, валидация и тип метаданных, без сети и БД. |
|
|
|
|
- `voice_file`: общий Opus-кодек отправки; `attachment_send` готовит файл в worker |
|
|
|
|
и передаёт результат отдельно в `chat_core` либо `dm_media`. |
|
|
|
|
|
|
|
|
|
```text |
|
|
|
|
dm_conversations(conv_id, peer_node_id, peer_x25519, peer_ed25519, peer_name, |
|
|
|
|
@ -146,8 +161,30 @@ Qt и Android получают историю через события ядра
|
|
|
|
|
45 — JSON бесед. Снимки ограничены 200 записями. Отправка вложения вызывает |
|
|
|
|
`dm_send_file` в uasync; длительная обработка выполняется в `media_async`. |
|
|
|
|
Android копирует выбранный URI и сохраняет полученный файл через `Dispatchers.IO`; |
|
|
|
|
Qt сохраняет файл из контекстного меню в отдельном потоке. Фото и видео пока |
|
|
|
|
отправляются как оригинальные файловые вложения, без канального транскодирования. |
|
|
|
|
Qt сохраняет файл из контекстного меню в отдельном потоке. Выбор «как файл» сохраняет |
|
|
|
|
оригинал; «как видео» в Qt использует общий FFmpeg worker и метаданные конечного MP4. |
|
|
|
|
Android записывает MP4 через CameraX; импорт MP4 проходит через платформенный |
|
|
|
|
MediaMetadataRetriever в IO. Другие контейнеры на Android отправляются как файлы. |
|
|
|
|
|
|
|
|
|
Голосовая запись отдаёт worker собственный PCM либо компрессор без копирования |
|
|
|
|
в GUI/uasync; worker выполняет flush, Opus-кодирование и вычисление waveform. |
|
|
|
|
Кодирование Qt и Android использует один `voice_file`. Адресат фиксируется до |
|
|
|
|
записи, выбора URI и подготовки; завершение не читает текущую беседу GUI. |
|
|
|
|
Подготовленный PM-файл и Android cache удаляются после копирования, ошибки или отмены; |
|
|
|
|
пользовательский исходник Qt остаётся во владении пользователя. |
|
|
|
|
|
|
|
|
|
Событие 46 `[target_len:1][target][job_UUID:16][state:1]` сообщает начало (0), |
|
|
|
|
готовность (1) и ошибку (2). Target канала — числовой ID, PM — `dm:<conv_id>`. |
|
|
|
|
Подготовка, доставка и воспроизведение имеют независимые состояния. UI отображает |
|
|
|
|
только задания текущей беседы. Состояния файла и delivery receipt приходят в JSON |
|
|
|
|
41; `content_type`, duration, dimensions и waveform преобразуются в общие роли |
|
|
|
|
сообщения. Канальные скачивания вызываются контроллером только для каналов; |
|
|
|
|
PM-файлы доставляются автоматически через `dm_media`. |
|
|
|
|
|
|
|
|
|
Android использует общие `ConversationComposer`/`ConversationMessages` для обеих |
|
|
|
|
бесед; Qt — `InputBar` и `MessageDelegate`. Превью Qt создаются в общем пуле |
|
|
|
|
с двумя workers; декодирование длинного Opus-файла выполняется вне GUI. Оба плеера |
|
|
|
|
проверяют заголовок, пакеты и end marker; Qt ограничивает PCM буфер 256 МиБ. |
|
|
|
|
|
|
|
|
|
## Проверки |
|
|
|
|
|
|
|
|
|
@ -165,11 +202,21 @@ ACK чужого тела, отказ mailbox без роли, точное уд
|
|
|
|
|
прямая передача, custody, restart S/M при выключенном A, точный файл на B, |
|
|
|
|
удаление копии и тела S, поздние STORE/STORED, TTL, отказ по квоте, неверная квитанция, |
|
|
|
|
SQL rollback регистрации, состояния JSON и отмена операции с CM handle. |
|
|
|
|
Голосовое передаётся напрямую; метаданные видео сохраняются после offline custody |
|
|
|
|
и restart S/M при выключенном источнике. |
|
|
|
|
|
|
|
|
|
`test_dm_worker`: повторное шифрование 8 МиБ не менее 0,5 с и проверка расшифровки; |
|
|
|
|
work выполняется вне uasync, done в его потоке, heartbeat продолжает работать. |
|
|
|
|
Destroy отменяет уже posted completion, callback вызывается ровно один раз. |
|
|
|
|
|
|
|
|
|
`test_attachment`: строгий формат, повреждения и граничные длины, реальное |
|
|
|
|
кодирование/декодирование 50 Opus-кадров, длительность и waveform. |
|
|
|
|
`test_attachment_send`: выбранная беседа после смены текущего target, освобождение |
|
|
|
|
PCM в worker, остановка до completion без регистрации и удаление отклонённого cache. |
|
|
|
|
Qt `test_voice_file`: реальные PCM/Opus, повреждённые файлы и отмена async decoder. |
|
|
|
|
|
|
|
|
|
Проверка 01.10.2026: чистые сборки Qt и Android APK; полный `check.sh` — |
|
|
|
|
115 PASS, 0 FAIL, 1 SKIP (auto_socket_dynamic требует root), proxy/burst/load PASS. |
|
|
|
|
117 PASS, 0 FAIL, 1 SKIP (auto_socket_dynamic требует root), proxy/burst/load PASS. |
|
|
|
|
Qt CTest: 7 PASS, 0 FAIL; Android APK собран после `clean`. |
|
|
|
|
В тесте worker максимальный интервал heartbeat составил 10,3 мс. |
|
|
|
|
Интерактивная проверка GUI на телефоне и десктопе не выполнялась. |
|
|
|
|
|