Kgothatso Ngako de3b355600 feat: hand back a room's running ceremony rather than opening a second
proposeRitual minted a fresh session on every call. Nothing called it twice
for the same room, so nothing went wrong: the only way in was the shared-key
screen, and canStartRitual() returns false while a session exists that has
not failed.

That is about to stop being true. A robust group opens a ceremony as it is
created, and a NIP-17 room id is derived from its member set -- so making
the same group again returns the same room and asks it again. The guard also
sat in the wrong place regardless: on an observed UI snapshot, a screen away
from the write it was protecting.

## What a second proposal costs

It is not a duplicate row. ChillDKG hashes the participant set and the
threshold into the session identity, so a second ceremony over the same room
is a second `n` and `t` for every member to reconcile, and members join
whichever proposal reaches them first -- relays hand gift wraps back in no
particular order, so which one that is differs per device. The group ends up
split across two ceremonies, neither of which can assemble the participant
count it needs.

Worse if the first one had finished. FrostSigningManager.completedKey falls
back to getLatestSessionForChatRoom when the room has no signed key state
and its id is not derived from the threshold key; a newer, unfinished
session shadows the completed one there, and the group stops being able to
reach the key it actually holds.

## The rule, and where it now lives

The room's live ritual is returned as-is, so a caller gets a session either
way and cannot tell whether it opened one. That is what makes the creation
path safe to re-enter.

The rule itself is unchanged -- it is the one canStartRitual() has always
applied, right down to which stages block. It now also lives next to the
write, where a stale snapshot cannot race it.

FAILED is excluded deliberately: it is the one stage that does not hold the
room's slot. A collapsed ceremony leaves the group with no key and a room
they can still talk in, which is exactly the group that should be able to
try again. Every other stage, COMPLETE included, is a ceremony the room
depends on the outcome of.

Checked before the require()s rather than after. A running ceremony settled
the threshold question when it opened, so validating the argument would be
validating an input with no effect -- and it would turn re-entering with a
different quorum into an exception instead of the ceremony that exists.

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