Kgothatso Ngako ed7a866421 test: run a real signing session between two devices, over two databases
a8f6638 changed three things that only exist *between* devices, and every test
it came with checks one device at a time. A room's key state is now signed into
being rather than announced; two devices have to resolve the room's derivation
path independently and land on the same event id; and what they sign as is the
room's own key. None of that is visible from a single database, so none of it
was covered where it could actually break.

This drives the whole protocol for real. Two MantraDatabase instances, a share
each out of Frost.trustedDealerKeygen, and the wire held by hand: broadcast()
queues a MarmotInnerEvent for the outbound pipeline, so ferrying those rows
between databases is the group with MLS taken out -- and MLS has no opinion
about any of the claims here. Nothing is stubbed; the FROST calls, the room's
path resolution, the id rebuild on arrival, the aggregate and its verification
all run.

It lands in jvmTest rather than commonTest because it needs a database, and
MantraDatabaseJvmTest already established that Room's in-memory builder and the
host's bundled SQLite work on this target. jvmTest goes 223 -> 235; commonTest
is unchanged at 217.

The ferry keeps its already-delivered set on the *receiving* device rather than
the sending one. A sender-side set looked equivalent and was not: a message goes
to every other device, so the first delivery hid it from everybody else, and the
three-member test failed because the member who never signed never got the
signature. That is a bug in the fake wire rather than in the group, and it is
the kind a two-device test would never have shown.

## What is pinned

  - propose() writes no state of its own. The creator has decided nothing until
    a quorum signs, which is the whole point of it no longer being an
    announcement.
  - The payload of the room's first message is a 30326 authored by the room's
    id -- not the group's root key, which is what an untweaked cache produces
    and what this used to be.
  - A second device with no path in its room metadata still rebuilds the same
    event id. It resolves the path itself and checks it against the room's id,
    so agreeing on the id is agreeing on every byte signed, the author included.
  - A member who has not approved publishes nothing.
  - Two devices that both applied the signature hold the same state, attributed
    to the room rather than to anybody in particular.
  - The finished signature verifies against the room's id, and passes
    GroupKeyStateEvent.isSignedByGroup, which is the check a receiver runs.
  - The third member of a 2-of-3 picks up the state without ever being asked to
    sign, because completing needs nothing of theirs.
  - A dialect signed in the room is authored by the room, and its stored
    signature verifies against the room's id. That is the half of a8f6638 that
    touches artifacts and dialects, end to end for the first time.
  - A proposer cannot choose the key the group signs as: a proposal carrying a
    true key state re-authored under the root key opens no session at all,
    because the receiver rebuilds the id under its own reading of the room.
  - A true key state nobody signed no longer becomes a row. This is the
    behaviour change worth being able to point at.
  - A room derived at m/9420/0/1 signs at m/9420/0/1, so nothing has quietly
    hardcoded MARMOT_ADMIN_GROUP_PATH.
  - A room not derived from the key at all still signs as the threshold key,
    with a null derivationPath -- the fallback kept for rooms the app no longer
    makes.

## They were checked against mutations, not just run

Tests that pass are not evidence until something makes them fail. Four
deliberate regressions were introduced and reverted:

  - unsignedEventOf deriving at the empty path instead of the room's: 7 failed.
  - signingPath returning its first candidate without checking it derives the
    room: 1 failed, the not-derived room.
  - propose writing the state row locally, the way announce() did: 4 failed.
  - isSignedByGroup accepting an author that is not the room: 7 failed, 3 of
    them in the existing GroupKeyStateTest.

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