From 81d71690c2aecd8fb70558cbd9c8f4abb7fa95f0 Mon Sep 17 00:00:00 2001 From: evgeny Date: Sun, 4 Oct 2026 15:55:42 +0200 Subject: [PATCH] Document PM invitation flow, lifetime and verification --- doc/pm_invites.md | 57 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 doc/pm_invites.md diff --git a/doc/pm_invites.md b/doc/pm_invites.md new file mode 100644 index 00000000..bc2d17af --- /dev/null +++ b/doc/pm_invites.md @@ -0,0 +1,57 @@ +# Приглашение в группу через PM + +В desktop chatgui и Android кнопка «+» доступна в строке другого участника +и в шапке личной беседы. Она открывает один диалог с фиксированным получателем +и выбором группы, в которой отправитель состоит. Группа не выбирается автоматически. + +После нажатия «Пригласить» клиент создаёт свежую ссылку через `invite_build`, +ожидает подтверждения регистрации ключа и отправляет ссылку обычным текстовым PM. +Успех означает сохранение сообщения и исходящей задачи в одной транзакции. +Доставку, повтор и шифрование обеспечивает существующий механизм DM. +После успешной отправки открывается личная беседа с получателем. + +Срок действия ключа — **10 минут с момента создания**, а не получения PM. +Он указан в диалогах отправки и подключения. Поздняя offline-доставка +может принести уже просроченную ссылку; в этом случае нужно новое приглашение. +Ссылка не содержит время истечения, поэтому клиент показывает длительность, +без обратного отсчёта и вычисления оставшегося времени. + +Нажатие на `utun://` в сообщении запускает существующий JOIN и показывает его результат. +Если клиент уже состоит в группе, открывается эта группа. Перенос длинной ссылки +не меняет её содержимое; на desktop перетаскивание выделяет текст без запуска JOIN. + +## Контракт операции + +`dm_invite_trampoline` выполняется в uasync-потоке. Запрос содержит `request_id`, +ID получателя, выбранную группу и исходную общую группу для создания PM. +Из шапки существующей PM исходная группа может быть пустой. + +На instance допускается одно ожидание регистрации приглашения в PM. +Повтор во время ожидания возвращает BUSY. Закрытие диалога отменяет только +соответствующий `request_id`; уже отправленное сообщение не отзывается. +Отмена и остановка DM завершают принятый запрос ровно один раз. + +`CHAT_EVT_DM_INVITE_RESULT` содержит 28 байт: +`[request_id:u64][result:i32][peer:u64][conv_id:u64]` в порядке байтов ядра. +Desktop и Android сопоставляют результат с запросом и получателем. +Ошибки регистрации или сохранения сообщения оставляют диалог открытым для повторной попытки. + +JOIN во вторую группу может использовать уже открытый транспорт общей группы. +Поэтому обработчики JOIN_REQUEST и JOIN_READY явно планируют member_sync, +а JOIN_READY обновляет кеш каналов до запуска синхронизации. + +## Проверка и диагностика + +- `tests/test_dm_invite`: настоящая UDP-доставка ссылки в другую группу, JOIN, + репликация участника и отправка группового сообщения после вступления; + отмена, BUSY, неверная группа, rollback при ошибке commit и остановка DM. +- `tests/test_chat_join`, `tests/test_chat_join_e2e`, `tests/test_dm_e2e`: + существующие сценарии регистрации ключей, JOIN и PM-доставки. +- Qt `test_invite_links`: переносы двух ссылок, их полное содержимое при нажатии, + выделение текста и выбор получателя кнопкой в строке участника. +- Qt `test_emoji_playback`: перетаскивание ссылки не активирует подключение. + +Для сетевого теста подробности включаются через `UTUN_TEST_DEBUG=1`. +В приложении полезны категории `dm=debug`, `chat_sync=debug`, `member_sync=debug`. +Операция пишет request, peer, group, результат и TTL; сообщения диагностики +новой операции не выводят саму invite-ссылку.