# Доставка личных сообщений ## 1. Инвариант У каждого пользователя действует ровно один сервер доступа: первый элемент access_servers[0] в его Solana PDA. Остальные элементы старой PDA, если они существуют, не участвуют в маршрутизации и не являются fallback. UI позволяет только заменить текущий сервер другим. Межсерверной репликации личной переписки между серверами одного пользователя нет. При смене сервера история автоматически не переносится. ## 2. Подписанные копии Формат SHiNE_DM не меняется. Для сообщения клиент создаёт: - входящую копию для получателя; - исходящую копию для отправителя. Сервер отправителя принимает пару через SendMessagePair, атомарно сохраняет её и создаёт изменяемое состояние доставки. ## 3. Основной маршрут 1. UI отправителя вызывает SendMessagePair на своём access-сервере. 2. Сервер сохраняет исходящую копию отправителя и входящую копию, необходимую для доставки и повторных попыток. 3. Сервер читает единственный маршрут получателя из user_access_servers_current. 4. Если это другой физический сервер, он вызывает внутреннюю операцию ReceiveIncomingMessage и передаёт только входящую копию. 5. Сервер получателя проверяет пользовательскую подпись, идемпотентно сохраняет входящую копию и уведомляет активные сессии. 6. Успешное сохранение означает delivered. ReceiveIncomingMessage не является синхронизацией двух серверов одного пользователя. Это основная доставка между серверами разных пользователей. ## 4. Повторные попытки Попытки привязаны ко времени первоначального принятия: - сразу; - через 30 секунд; - через 5 минут; - через 25 минут; - через 1 час. Перед каждой попыткой заново читается первый актуальный access-сервер получателя. После финальной неудачи состояние становится failed. Второй сервер отправителя не опрашивается, полная пара ему не передаётся. ## 5. Состояния - accepted — пара сохранена сервером отправителя; - delivered — входящая копия сохранена единственным сервером получателя; - failed — финальная попытка завершилась неудачей; - read-receipt — отдельное подписанное DM-событие. Подписанные DM-контейнеры остаются криптографическими фактами. Сетевое deliveryState остаётся изменяемой серверной метаданной. ## 6. Удаления DeleteMessage и DeleteConversation доставляют подписанный tombstone только на единственные access-серверы обеих сторон. Повторное применение идемпотентно. ## 7. Удалённая репликация После перехода на один сервер доступа удалены: - ReceiveOutcomingMessage; - DmSyncBatch; - GetDmDeliveryStatus; - dm_sync_outbox; - dm_sync_peer_state; - ACK-флаги synced; - периодический DM-backfill между access-серверами. Блокчейн-синхронизация через server-PDA sync_servers остаётся отдельным механизмом и продолжает работать.