wt b37c71720b
iOS CI / Build and Test SwiftUI App (push) Canceled after 0s
Build Unsigned iOS and macOS Apps / Build Unsigned iOS IPA (push) Canceled after 0s
Build Unsigned iOS and macOS Apps / Build macOS ZIP (push) Canceled after 0s
added bubble tails
2026-09-14 18:05:13 +07:00
2026-07-30 20:07:51 +07:00
2026-09-02 11:46:11 +07:00
2026-09-02 11:46:11 +07:00
2026-09-07 19:40:47 +07:00
2026-09-07 19:40:47 +07:00
2026-09-14 18:05:13 +07:00
2026-09-07 19:40:47 +07:00
2026-08-30 05:40:44 +07:00
2026-09-02 11:46:11 +07:00
2026-07-30 20:36:49 +07:00
2026-08-30 05:40:44 +07:00
2026-07-30 20:07:51 +07:00
2026-08-30 05:40:44 +07:00
2026-09-02 11:46:11 +07:00
2026-07-30 20:07:51 +07:00
2026-09-02 11:46:11 +07:00

Luma

Luma — рабочий MVP XMPP-клиента для iOS, iPadOS, macOS и Apple Watch с интерфейсом в духе Telegram/iMessage. Клиент не привязан к одному провайдеру: он подключается к обычному XMPP-серверу по JID, включая Prosody.

Что уже реализовано

  • вход по JID и паролю; пароль хранится в Apple Keychain;
  • DNS SRV, STARTTLS и direct TLS, а также ручной host/port;
  • roster, presence, личные чаты и групповые комнаты XEP-0045 (MUC), включая создание/вход, приглашения, автоподключение и число участников;
  • отдельный раздел «Контакты»: люди синхронизируются с roster Prosody, а рядом показываются сохранённые MUC-комнаты; есть общий поиск, аватары и статусы;
  • OMEMO для исходящих и входящих сообщений в личных и неанонимных MUC-чатах, постоянные ключи, TOFU и ручная проверка отпечатков устройств;
  • глобальный переключатель OMEMO и отдельный режим для каждого чата: наследовать глобальную настройку, всегда шифровать или отправлять без OMEMO;
  • XEP-0198 Stream Management, XEP-0280 Message Carbons и XEP-0313 MAM;
  • delivery receipts и локальная история;
  • редактирование текстовых сообщений через XEP-0308, включая исправления в OMEMO-чатах и синхронизацию через Carbons/MAM;
  • ответы через XEP-0461 и XEP-0428: Luma показывает исходное сообщение уменьшенным полупрозрачным bubble с дугой и открывает по нажатию фокус-режим всей ветки в стиле iMessage; текстовые >-цитаты старых Conversations/Monal также разбираются, а в MUC используется выданный комнатой stanza-id;
  • пересылка текста, геопозиции и медиа в существующий чат или на введённый JID; через пункт «Выбрать» можно отметить несколько сообщений и переслать их в исходном порядке; медиа скачивается/расшифровывается и загружается заново для получателя;
  • «Удалить у всех» через XEP-0424 Message Retraction с tombstone в истории и отдельное постоянное «Удалить у меня», которое не возвращает сообщение после очередной синхронизации MAM; оба действия поддерживают несколько выбранных сообщений;
  • съёмка фото прямо системной камерой iPhone, а также выбор фото и видео из медиатеки; фото, видео, музыка и файлы отправляются через XEP-0363 HTTP File Upload; при включённом OMEMO содержимое шифруется до загрузки, а исходное расширение имени сохраняется для совместимости с Conversations и Monal;
  • множественный выбор до 20 фото/видео или файлов, полноэкранный предпросмотр фото и воспроизведение видео до отправки, удаление отдельных элементов и подпись; после сетевой ошибки неотправленные файлы остаются для повторной попытки, а параллельные загрузки не могут оставить интерфейс заблокированным;
  • фото и видео показывают inline-превью и по нажатию открываются поверх всего окна приложения; полноэкранный просмотр закрывается вертикальным свайпом без отдельного крестика, увеличенное фото продолжает свободно перемещаться внутри, а видео использует системные элементы воспроизведения; музыка получает inline-плеер с названием файла, длительностью и перемоткой;
  • каждое фото и видео помещается в отдельный скруглённый островок с внутренним отступом и aspectFit: миниатюра не обрезается, не выходит за границы и не слипается с соседними вложениями;
  • запись голосовых сообщений и круглых видеосообщений (до 60 секунд) с камеры; голосовые показывают waveform, поддерживают паузу, перемотку и скорости 1×/1.5×/2×, а видеосообщения воспроизводятся прямо в круглом bubble, который слегка увеличивается на время воспроизведения;
  • явно видимый пункт «Отправить геопозицию» в меню +, выбор текущей или произвольной точки на карте и отправка стандартного geo: URI, распознаваемого другими XMPP-клиентами;
  • аватары контактов и обновление собственного аватара через XEP-0084 PEP с совместимым fallback на vCard-temp;
  • личные аудио- и видеозвонки через XMPP Jingle (XEP-0166/XEP-0167), ICE-UDP/DTLS-SRTP и WebRTC; есть входящий экран, mute, динамик, камера и её переключение, а STUN/TURN автоматически берутся через XEP-0215; некорректные ICE-записи от сервера отбрасываются, затем используются публичный STUN и прямые host candidates, поэтому они не блокируют запуск media engine; исходящий session-accept нормализуется по порядку media-линий offer; Jingle Message Initiation сразу адресуется выбранному по presence/caps full JID; trickle ICE ждёт удалённый SDP, но больше не зависает в ожидании ресурса, поэтому transport-info не попадает на другой endpoint и не остаётся в очереди до timeout;
  • завершённые аудио- и видеозвонки показываются в истории чата отдельными Telegram-подобными карточками: входящий/исходящий, длительность, отклонённый, пропущенный, отменённый, без ответа или неудачный; пропущенный входящий помечает чат непрочитанным;
  • у обычных текстовых и файловых сообщений используются чистые скруглённые островки без «хвостиков»;
  • единый заполненный AppAssets.xcassets/AppIcon со всеми 42 размерами для iPhone, iPad, Mac и Apple Watch; один и тот же catalog явно подключён ко всем app-target’ам, а имя закреплено в build settings и Info.plist;
  • адаптивный SwiftUI-интерфейс для iPhone, iPad и Mac;
  • companion-приложение watchOS: последние чаты, диктовка/ответы, а также запись голосового до 60 секунд с отменой и предпрослушиванием; .m4a передаётся на iPhone фоновой файловой очередью WatchConnectivity, после чего iPhone отправляет его через обычный XEP-0363/OMEMO media-пайплайн и возвращает часам итоговый статус; это современный single-target watchOS app, поэтому SwiftUI-код и executable собираются в одном target без устаревшего WatchKit App/Extension контейнера;
  • системные уведомления для новых сообщений, пока приложение запущено в фоне или на экране; в открытом сейчас диалоге дублирующий banner не показывается.

Сборка

Нужны macOS, Xcode 16 или новее и XcodeGen.

brew install xcodegen
cd Luma
make project
open Luma.xcodeproj

После обновления архива обязательно снова выполните make project: новые build settings AppIcon берутся из project.yml. Если устройство или Simulator закэшировали старую пустую иконку, один раз удалите установленную Luma и установите её заново; на macOS после новой сборки перезапустите Dock или сам Mac. Можно вместо команд дважды нажать Regenerate-Luma-Project.command в корне проекта (XcodeGen всё равно должен быть установлен). При переходе с версии 0.8.7 один раз выполните в Xcode Product → Clean Build Folder, чтобы удалить кэш старого application.watchapp2 target.

После открытия проекта:

  1. Выберите свою Apple Development Team для Luma, LumaMac и LumaWatch.
  2. При необходимости замените bundle identifiers app.luma.chat* в project.yml на уникальные.
  3. Дождитесь загрузки Swift packages.
  4. Запустите scheme Luma на iPhone/iPad или LumaMac на Mac.

Для проверки из терминала на Mac:

make verify

Версии Martin, MartinOMEMO и WebRTC закреплены в project.yml. Зависимости Martin имеют copyleft-лицензии, поэтому Luma распространяется под AGPL-3.0-or-later.

Prosody

Минимально нужны pep, smacks, carbons, mam и MUC component. pep используется и для OMEMO, и для XEP-0084 аватаров. Для медиа и вложений добавьте компонент http_file_share; для надёжных звонков за NAT — coturn и turn_external. Готовый пример и команды проверки находятся в Docs/PROSODY.md.

Важные границы MVP

  • OMEMO-модуль использует широко развёрнутый профиль с namespace eu.siacs.conversations.axolotl, совместимый со многими версиями Monal и Conversations. Поддержку нового namespace urn:xmpp:omemo:2 следует добавить отдельным криптографическим адаптером до объявления полной совместимости с актуальным XEP-0384.
  • Надёжный push при выгруженном iOS-приложении не может работать только через XMPP-сокет. Для production нужны Apple Developer credentials, APNs provider, XEP-0357 push-компонент и регистрация приложения в этом компоненте.
  • При первом запуске Luma запрашивает ограниченную последнюю страницу серверного MAM-архива, чтобы огромная история не блокировала интерфейс, и показывает индикатор «Синхронизация истории…» только на этом первичном этапе. После успешного прохода сохраняются серверный UID последней записи и время; следующие подключения продолжают RSM строго после UID. Временное перекрытие используется только для старых snapshots без UID или один раз, если сервер удалил сохранённый UID. Сырые stanza собираются вне main queue, а расшифровка OMEMO выполняется на отдельной фоновой очереди — главный поток освобождается на время Signal-криптографии, поэтому большая история не лагает интерфейс. Инкрементальная догрузка backlog продолжается в фоне без вечного спиннера, а сообщения применяются атомарными пачками с дедупликацией по origin-id/stanza id через хеш-индексы. После сбоя отдельной страницы синхронизация автоматически повторяется ограниченное число раз, не дожидаясь повторного открытия приложения.
  • Клиент не вводит собственного ограничения размера вложения: фактический предел сообщает XEP-0363 upload-компонент сервера. Видеосообщение ограничено 60 секундами.
  • XEP-0424 retract является запросом на удаление: федеративный XMPP не может гарантировать стирание уже полученной или скопированной версии в чужом клиенте. «Удалить у меня» влияет только на это устройство.
  • Групповое OMEMO требует неанонимную MUC-комнату и OMEMO-устройство у каждого получателя. Luma создаёт новые комнаты members-only/persistent с muc#roomconfig_whois=anyone; для несовместимой чужой комнаты безопасная отправка блокируется без автоматического перехода на plaintext. Сервер также должен разрешать участникам читать списки member/admin/owner.
  • Звонки в MVP работают один-на-один, пока приложение запущено и XMPP-соединение активно. WebRTC защищает медиапоток DTLS-SRTP, но это отдельный механизм от OMEMO. Для надёжного входящего звонка после выгрузки iOS-приложения нужны PushKit/CallKit, APNs provider и серверный push-компонент. Карточка звонка хранится в локальной истории участвовавшего устройства Luma; она не отправляется собеседнику как фальшивое текстовое сообщение и поэтому не синхронизируется через MAM.
  • Реакции и групповые звонки относятся к следующему этапу.

Архитектура описана в Docs/ARCHITECTURE.md, модель угроз и ограничения хранения — в Docs/SECURITY.md.

S
Description
modern XMPP-client application
Readme AGPL-3.0
5.5 MiB
0.10.6
Latest
2026-07-30 20:10:01 +07:00
Languages
Swift 96.8%
Shell 3.2%