Kgothatso Ngako 3576c00ce2 feat: put every ritual message in the group's chat, naming who sent it
61480dd gave the group three lines: a ceremony started, it finished, somebody
abandoned it. Between the first and the second the ritual was a black box. A
ceremony that fixes the group's signing quorum for good ran with nothing to watch,
and a stalled one -- the common case, since it finishes only once every member's
device has taken part -- gave no way to see which member it was waiting on.

Every protocol message now gets a line naming the member whose device sent it:

  30311 host key     ...joined the shared key ceremony, publishing the key their
                        device is identified by for the rest of it.
  30312 round 1      ...sent their contribution to the key. Every member's is
                        mixed in, so no single device ever holds the whole thing.
  30313 coord r1     ...combined everyone's contributions and sent the result back
                        to be checked.
  30314 round 2      ...checked the combined result and signed to confirm it
                        matches what they sent.
  30315 certificate  ...gathered everyone's confirmations into a certificate. A
                        device that has it can finish and keep its share of the key.

The wording says what the step accomplishes rather than what it is called. "sent
pmsg1" is not a useful thing to read in a group chat, and the protocol names are
already on the shared-key screen this line taps through to.

30310 and 30316 keep the announcers they have: both say more than the message
carries -- the quorum in one case, who walked away in the other -- so folding them
in here would have lost that. `complete` stays the one unauthored line, because a
finished ceremony has no actor: the group ends up with a key, nobody hands it to
them.

This changes nothing about the protocol and adds no traffic. It extends the
mechanism 61480dd established rather than adding one: the rows are written locally
from ritual messages this device already has, with no giftWrapPayloadId and no
event behind them, so they cannot disagree with the ritual they describe and no
new kind goes on the wire. Adding the five types to DKG_TYPES and
DKG_AUTHORED_TYPES was enough to get RitualNotice's system-line treatment and its
name prefix, which resolves from the joined profile and so follows a rename.

The one genuinely new problem is idempotency, and it is the reason for six hooks
rather than one. ChatMessage has no key to make a second insert a no-op -- its id
is autogenerated -- while ritual messages arrive repeatedly: relays redeliver, and
replayStoredMessages feeds the whole backlog through record() again on every
resume. DkgParticipantMessage absorbs both, being keyed on
(sessionId, participantPublicKey, kind); a chat row cannot. So each announce is
guarded by reading the row it is about to write over, the same read-before-write
fail() already documents:

  - record(), for the three per-member kinds, checks getMessage before the upsert
  - record(), for the two aggregates, reads the column before update()
  - publishOwn(), for this device's own messages, checks ownMessage
  - the two coordinator broadcast sites, already inside `== null` guards

publishOwn's check is deliberately redundant with its callers', which all check
before publishing. The chat row is the thing with no fallback, so the check that
matters sits next to the write. An echo of this device's own broadcast finds the
row already stored and stays silent.

Worth knowing before this meets a real group: it is 3n + 4 lines per ritual -- 19
for five members, 34 for ten -- and a ritual is a burst, not a trickle. They are
compact single lines, but they will dominate a transcript while a ceremony runs.
If that reads as noise, the cheap fix is collapsing consecutive ritual lines into
one expandable line in ChatMessageListViewModel; the types are distinct enough to
group on, so no data change would be needed. That judgement wants a real ritual
first, which is also the only thing that will exercise these paths -- they are the
same paths the protocol messages take, and none of it has run on a device.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 10:43:01 +02:00
2026-07-05 23:21:02 +02:00
2026-07-28 09:10:20 +02:00
2026-03-24 04:47:19 +02:00
2026-03-23 01:41:39 +02:00
2026-03-23 01:41:39 +02:00
2026-03-23 01:41:39 +02:00

This is a Kotlin Multiplatform project targeting Android, iOS, Desktop (JVM).

  • /composeApp is for code that will be shared across your Compose Multiplatform applications. It contains several subfolders:

    • commonMain is for code thats common for all targets.
    • Other folders are for Kotlin code that will be compiled for only the platform indicated in the folder name. For example, if you want to use Apples CoreCrypto for the iOS part of your Kotlin app, the iosMain folder would be the right place for such calls. Similarly, if you want to edit the Desktop (JVM) specific part, the jvmMain folder is the appropriate location.
  • /iosApp contains iOS applications. Even if youre sharing your UI with Compose Multiplatform, you need this entry point for your iOS app. This is also where you should add SwiftUI code for your project.

Build and Run Android Application

To build and run the development version of the Android app, use the run configuration from the run widget in your IDEs toolbar or build it directly from the terminal:

  • on macOS/Linux
    ./gradlew :composeApp:assembleDebug
    
  • on Windows
    .\gradlew.bat :composeApp:assembleDebug
    

Build and Run Desktop (JVM) Application

To build and run the development version of the desktop app, use the run configuration from the run widget in your IDEs toolbar or run it directly from the terminal:

  • on macOS/Linux
    ./gradlew :composeApp:run
    
  • on Windows
    .\gradlew.bat :composeApp:run
    

Build and Run iOS Application

To build and run the development version of the iOS app, use the run configuration from the run widget in your IDEs toolbar or open the /iosApp directory in Xcode and run it from there.


Learn more about Kotlin Multiplatform

Description
Mantra as a kotlin multiplatform project
https://mantra.press
Readme 8.2 MiB
Languages
Kotlin 100%