Kgothatso Ngako 3dea07135c fix: add a group's whole membership in one commit, closing the epoch race
`MarmotOutboundDao.addMembersToChatRoom` stages every member with `proposeAdd` and
issues a single `commit()`. Both callers that know their membership up front now
use it: `SelectChatRoomTypeViewModel.inviteMembers` at room creation, and
`DkgRitualViewModel.inviteAdmins` for the #admins room.

Inviting one at a time created an epoch per member, and each of those commits
raced the previous member's welcome. MarmotInboundManager refuses future-epoch
messages outright, on both wire formats, with no queue and no replay -- so the
member who lost that race was silently stuck an epoch behind while the caller saw
a successful invite. Deriving isOneMemberInitialGroupCreation narrowed that window;
this removes it. No member ever has to process a commit for an epoch they were not
yet in, so there is no longer a race to lose.

One commit yields one welcome: `buildWelcome` emits an EncryptedGroupSecrets per
added member and each joiner finds its own entry by key package reference. The blob
is shared, delivery stays per peer, because each welcome event is tagged with that
peer's key package.

## Why this needed no schema change

Batching at creation time means the single commit happens while the group is still
only its creator, which takes the immediate-welcome branch: nothing is broadcast
and MarmotCommitResult is never written. The bookkeeping that assumes one peer per
commit is simply not on this path.

So the batch is taken only when `members().size == 1`, and anything else falls back
to inviting sequentially -- correct, if not ideal. Batching into an established
group would take the deferred branch, where `peerKeyPackageEventId` is singular and
the ack-triggered delivery in DatabaseNostrRepository expects one welcome; making
that work needs a list there and a fan-out on acknowledgement. Nothing currently
adds several members to an established group, so that is left outstanding and
documented rather than speculatively built.

The group state is persisted after `commit()` and before any welcome goes out, so a
crash between them leaves the group at the epoch the welcomes describe rather than
one behind it.

## Reporting

Members with no published key package still cannot be added -- a Marmot invite
needs one -- and are now returned alongside any that failed to receive their
welcome, rather than the two being conflated. Both still only reach the log; the
coordinator is not yet told.

docs/marmot-membership.md is updated in the same change: batching moves from
outstanding work to described behaviour, with the schema constraint that shapes it
and the remaining fan-out work recorded. The note about sequential invites is
narrowed to where they still happen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 15:02:06 +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%