- After a call ends, send a plaintext <call-history/> service message
to the user's own bare JID with direction, status, duration, start
time, peer and video flag plus the call id as origin-id.
- Intercept the payload on live, carbon and MAM paths and upsert the
same system call card on every device; deduplication reuses the
origin-id as clientID, and missed incoming calls bump unread.
- Cover payload parsing and round-trip in CallHistorySyncTests, guard
the namespace and interception in verify.sh, and document the flow
in ARCHITECTURE.md.
- Implement SCRAM-SHA-512 (RFC 5802 with SHA-512) as a Luma-side
SaslMechanism and register it ahead of Martin's SHA-256/SHA-1/PLAIN,
so modern servers preferring SHA-512 authenticate with it.
- Verify the math against Python-computed reference vectors (salted
password, client proof, server signature) in SCRAMSHA512Tests and
guard the mechanism registration in Scripts/verify.sh.
- Confirm the TLS stack negotiates TLS 1.3 (negotiatedSSL + ALPN via
SecureTransport, handshake verified against a TLS 1.3-only server)
and document the TLS/SCRAM posture in SECURITY.md.
- Translate Prosody's "doesn't match the one we calculated" SCRAM
failure into an actionable Russian message pointing at the typed
password, since the client math itself is verified against the
RFC 5802 test vector.
- Set explicit username/password text content types on the login
screen, disable smart text features, and add a password visibility
toggle so users can verify what they submit.
- Cover the mapping with a unit test and verify.sh invariants.
- Normalize SCRAM passwords with RFC 4013 SASLprep before the
challenge response, so they match SASLprep-compliant servers
(Martin hashes raw UTF-8) and PLAIN still sends the password
untouched.
- Capture the raw <failure/> condition with LumaSaslFailureModule and
map SASL errors to actionable Russian messages instead of the
cryptic OS-localized "Martin.SaslError, error 5".
- Cover SASLprep and failure message mapping with unit tests and new
verify.sh invariants.
Server information (Monal-style):
- Add ServerInformation model and ServerInfoView reachable from
Settings,
showing server software (XEP-0092), disco#info identities and
features,
conference (MUC) services via disco#items, and STUN/TURN via XEP-0215.
- Add a curated XEP capability list with per-XEP success/error status
derived
from server/account disco features and connection flags.
- Detect PEP (0163) from account disco pubsub#* features, HTTP Upload
(0363)
from the main domain plus its components, and add Roster Versioning
(0237),
Pre-Authenticated Roster Subscription (0379), and SASL SCRAM Downgrade
Protection (0474, neutral because Martin does not expose SSDP).
Connection statistics:
- Add LumaConnectionStatsModule, an XmppStanzaFilter registered before
stream
management, to count sent / acknowledged / received stanzas.
- Track last login and SMACKS session timestamps, and read SASL
mechanisms
and Client State Indication from the raw stream features.
MAM / OMEMO handling:
- Prefer a plaintext <body> fallback when OMEMO decryption fails instead
of
showing "failed to decrypt".
- Keep the optimistic text of our own outgoing message when its echo
cannot
be decrypted back, instead of replacing it with a placeholder.
Emoji picker:
- Add EmojiCatalog and a cross-platform EmojiPickerView.
- Open the picker for reactions (replacing the static quick list) and
from
the composer's smiley button, inserting the chosen emoji into the
draft.
Multi-device:
- Add DeviceResource with a stable per-install UUID-based resource
(Luma-<hex>) persisted in UserDefaults and migrated from legacy
"Luma".
- AccountConfiguration.effectiveResource falls back to
DeviceResource.default
for a blank or legacy resource; LoginView defaults resource to "" with
an
auto placeholder.
- Prevents two devices from sharing the same full JID resource and
kicking
each other off the stream (the "xmpp stream error" conflict).
MAM / history:
- loadOlderHistory now always issues the RSM page request instead of
deferring, so scrolling up reliably loads older messages.
- Track interactive history mutations separately so live messages are
not
dropped while an older-history page is decoding.
- ChatView: replace the unreliable GeometryReader/preference scroll
trigger
with onAppear/onDisappear on a top sentinel; auto-load the first page
when
the conversation is empty.
- AppModel: reset hasMoreOlderHistory for empty conversations and clear
isLoadingOlderHistory on disconnect.
- Decode own/duplicate/notEncrypted messages to
decryption-failed/plaintext
placeholders instead of dropping them, so every MAM stanza is
collected.
OMEMO:
- Remove the encrypt-to-self setup that built a Signal session with the
local
device (cryptographically invalid), which caused an endless "Bad MAC"
loop
and a stream of undecryptable messages in the self-chat.
- Add LumaOMEMOStore.removeSessionWithOwnDevice() and call it on connect
to
purge any self-session persisted by the previous build.
Tests:
- Update AccountConfigurationTests for the unique resource and add
coverage
for legacy-resource migration and explicit-resource preservation.
thread
- Replace O(n^2) origin-id/stanza-id lookups in AppModel with hash
indexes
and stop rebuilding the whole index on every unseen stanza-id, so
archive
application no longer grows quadratically with history size.
- Run OMEMO decryption on a serial background queue instead of the main
actor, and decrypt archived stanzas in small batches with a shorter
yield,
keeping the UI responsive during large archive catch-ups.
- Show the "Синхронизация истории…" banner only for the one-page
bootstrap
window; incremental backlog catch-up continues silently in the
background.
- Fix MUC-MAM: publish decoded group mutations at the end of room
catch-up;
they were previously accumulated and then silently dropped.
- Retry a failed catch-up pass automatically (bounded) and lengthen the
query/apply timeouts so a single slow page no longer leaves history
unloaded until the next app activation.