finish SwiftData migration with @Query views
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

- Drive chat list, timeline and forward picker from SwiftData @Query
  instead of AppModel's in-memory arrays; inject the per-account
  ModelContext from RootView with an in-memory fallback.
- Physically delete locally deleted messages (context.delete) and
  purge rows marked deleted by older builds on store open, so @Query
  views never resurrect them.
- Delete the legacy JSON snapshot only after a successful import save
  and restore the completeUntilFirstUserAuthentication data protection
  attribute on the store file.
- Mirror the old ChatArchive tolerant decoding in LegacyArchiveImporter
  so snapshots from older schema versions (missing reactions,
  isGroupMessage, roster fields) still import.
- Cover the new behaviour in ArchiveStoreTests and guard it with new
  verify.sh invariants; update AGENTS.md, ARCHITECTURE.md and
  SECURITY.md.
This commit is contained in:
wt committed 2026-08-29 00:57:08 +07:00
1 parent 277d8654c3
commit 2884810ec4
16 files changed
+653 -65

No files matched your search

+9 -5
View File
@@ -9,7 +9,9 @@
3. `LumaOMEMOStore` адаптирует постоянное состояние к Signal storage callbacks;
AES-GCM выполняется CryptoKit.
4. `AppModel` сводит сетевые события в UI-состояние, дедуплицирует MAM/live
сообщения и сохраняет snapshot локально.
сообщения и сохраняет их в per-account SwiftData-хранилище (`ArchiveStore`,
отдельный `ModelContainer` на аккаунт); список чатов, лента и список
получателей при пересылке читают данные напрямую через `@Query`.
5. SwiftUI views используют один и тот же слой моделей на iOS/iPadOS/macOS.
6. watchOS не держит отдельный XMPP-сокет: iPhone передаёт компактный snapshot,
текстовые ответы возвращаются немедленно или через гарантированную очередь,
@@ -60,7 +62,7 @@ Signal-криптография не блокирует главный пото
автоматически повторяется ограниченное число раз. При уходе приложения с экрана
и на время записи/отправки видеосообщения активный MAM-запрос закрывается, чтобы
дешифрование истории не конкурировало с камерой и upload. Только после commit
`AppModel` обновляет SwiftUI, локальный snapshot и Apple Watch; незавершённый
`AppModel` обновляет SwiftUI, SwiftData-хранилище и Apple Watch; незавершённый
проход при timeout/disconnect отбрасывается целиком.
Редактирование реализовано стандартным XEP-0308: исправление получает новый id и
@@ -100,8 +102,9 @@ XEP-0424 retraction проходит через тот же plaintext/OMEMO-пу
после чего заменяет payload tombstone, очищает медиакэш и блокирует дальнейшее
редактирование/пересылку. Retract, пришедший раньше исходной stanza при MAM,
временно удерживается в ограниченной очереди. Локальное удаление физически
убирает запись из snapshot и сохраняет отдельный набор подавленных id, чтобы
последующая MAM-синхронизация не добавила её обратно.
удаляет строку из SwiftData-хранилища (при открытии хранилище также вычищает
строки, помеченные удалёнными старыми сборками) и сохраняет отдельный набор
подавленных id, чтобы последующая MAM-синхронизация не добавила её обратно.
Пересылка текста создаёт новое сообщение с явной подписью источника. Медиа не
переиспользует старый `aesgcm://` URL: клиент получает расшифрованные байты и
@@ -205,6 +208,7 @@ PushKit/CallKit и серверной APNs-инфраструктуры.
- APNs provider + XEP-0357 registration;
- современный `urn:xmpp:omemo:2` adapter и миграция устройств;
- MIX и реакции;
- SQLite/SQLCipher вместо JSON snapshot;
- шифрование базы SQLCipher поверх SwiftData SQLite (история уже хранится
в SwiftData, а не в JSON snapshot);
- PushKit/CallKit для фоновых входящих звонков и групповые звонки;
- share extension и notification service extension.
+13 -11
View File
@@ -1,7 +1,7 @@
# Безопасность и модель угроз MVP
- Пароль XMPP сохраняется в Keychain и не попадает в `UserDefaults` или
snapshot истории.
локальное хранилище истории.
- Проверка сертификата не отключается. Ручной host меняет маршрут подключения,
но XMPP domain/JID остаётся исходной идентичностью.
- TLS trust оценивается системным Apple Security framework как сертификат
@@ -49,22 +49,24 @@
STUN/TURN-сервис видит сетевые адреса и объём трафика. TURN ретранслирует уже
зашифрованный медиапоток. Если Prosody не публикует собственный сервис через
XEP-0215, резервный публичный STUN получает запрос на определение адреса.
- На iOS файлы OMEMO-состояния и локальной истории получают data-protection
`completeUntilFirstUserAuthentication`.
- На iOS файл OMEMO-состояния и база локальной истории (SwiftData
`.store`) получают data-protection `completeUntilFirstUserAuthentication`.
## Известные ограничения
- На macOS история и OMEMO state лежат в sandbox Application Support без
дополнительного SQLCipher-слоя; защиту обеспечивает учётная запись и FileVault
пользователя. Для повышенной модели угроз нужна зашифрованная база.
- На macOS история (SwiftData SQLite в Application Support) и OMEMO state
лежат в sandbox без дополнительного SQLCipher-слоя; защиту обеспечивает
учётная запись и FileVault пользователя. Для повышенной модели угроз нужна
зашифрованная база.
- Локальная история содержит уже расшифрованные тексты, как и у большинства
клиентов. Полное удаление требует выхода с опцией удаления истории.
- «Удалить у меня» удаляет запись только из локального snapshot этого устройства
и запоминает id против повторной MAM-загрузки; оно не удаляет серверный архив,
другие собственные устройства или копию собеседника.
- «Удалить у меня» удаляет запись только из локального SwiftData-хранилища
этого устройства и запоминает id против повторной MAM-загрузки; оно не
удаляет серверный архив, другие собственные устройства или копию собеседника.
- Глобальная политика шифрования хранится в `UserDefaults` отдельно для каждого
JID, а переопределение чата — в локальном snapshot. Эти значения не являются
секретами, но прямо влияют на конфиденциальность следующей отправки.
JID, а переопределение чата — в локальном SwiftData-хранилище. Эти значения
не являются секретами, но прямо влияют на конфиденциальность следующей
отправки.
- Аватары XEP-0084/vCard не шифруются OMEMO. Их видимость определяется
настройками PEP и roster на сервере; не используйте аватар как секретное фото.
- Используемая реализация MartinOMEMO поддерживает распространённый legacy