feat: Phase 6 — Marmot account integration & event routing
Wire Marmot MLS group messaging into the app's Account lifecycle, event processing pipeline, and relay subscription system. New files: - AndroidMlsGroupStateStore: encrypted file-based MLS state storage using KeyStoreEncryption (AES/GCM backed by Android KeyStore) - MarmotManager: central coordinator holding all Marmot components (MlsGroupManager, KeyPackageRotationManager, subscription/inbound/ outbound processors, WelcomeSender) - MarmotGroupEventsEoseManager: relay subscription manager for kind:445 GroupEvent filters via AccountFilterAssembler Account integration: - MarmotManager initialized during Account startup with restoreAll() - Outbound methods: sendMarmotGroupMessage, addMarmotGroupMember, publishMarmotKeyPackage(s), createMarmotGroup - GroupEvent (kind:445) and KeyPackageEvent (kind:30443) added to LocalCache.justConsumeInnerInner() dispatch Event routing: - GroupEventHandler: processes inbound kind:445 events through MarmotInboundProcessor, indexes decrypted inner events in LocalCache - GiftWrapEventHandler extended: detects kind:444 WelcomeEvent after NIP-59 unwrap, routes to MarmotManager.processWelcome(), triggers KeyPackage rotation when needed - WelcomeEvent enhanced with optional "h" tag for group ID routing https://claude.ai/code/session_01W2LHazEt4E3W4hn8f7gWVW
This commit is contained in:
@@ -71,6 +71,7 @@ class MarmotWelcomeSender(
|
||||
recipientPubKey: HexKey,
|
||||
keyPackageEventId: HexKey,
|
||||
relays: List<NormalizedRelayUrl>,
|
||||
nostrGroupId: HexKey? = null,
|
||||
): WelcomeDelivery? {
|
||||
val welcomeBytes = commitResult.welcomeBytes ?: return null
|
||||
|
||||
@@ -83,6 +84,7 @@ class MarmotWelcomeSender(
|
||||
relays = relays,
|
||||
recipientPubKey = recipientPubKey,
|
||||
signer = signer,
|
||||
nostrGroupId = nostrGroupId,
|
||||
)
|
||||
|
||||
return WelcomeDelivery(
|
||||
@@ -109,6 +111,7 @@ class MarmotWelcomeSender(
|
||||
recipientPubKey: HexKey,
|
||||
keyPackageEventId: HexKey,
|
||||
relays: List<NormalizedRelayUrl>,
|
||||
nostrGroupId: HexKey? = null,
|
||||
): WelcomeDelivery {
|
||||
val welcomeBase64 = Base64.encode(welcomeBytes)
|
||||
|
||||
@@ -119,6 +122,7 @@ class MarmotWelcomeSender(
|
||||
relays = relays,
|
||||
recipientPubKey = recipientPubKey,
|
||||
signer = signer,
|
||||
nostrGroupId = nostrGroupId,
|
||||
)
|
||||
|
||||
return WelcomeDelivery(
|
||||
|
||||
+8
@@ -67,6 +67,12 @@ class WelcomeEvent(
|
||||
/** Content encoding (must be "base64") */
|
||||
fun encoding() = tags.welcomeEncoding()
|
||||
|
||||
/** Nostr group ID from the "h" tag (for recipient to identify the group) */
|
||||
fun nostrGroupId(): HexKey? =
|
||||
tags.firstNotNullOfOrNull { tag ->
|
||||
if (tag.size >= 2 && tag[0] == "h") tag[1] else null
|
||||
}
|
||||
|
||||
override fun isContentEncoded() = true
|
||||
|
||||
companion object {
|
||||
@@ -77,12 +83,14 @@ class WelcomeEvent(
|
||||
welcomeBase64: String,
|
||||
keyPackageEventId: HexKey,
|
||||
relays: List<NormalizedRelayUrl>,
|
||||
nostrGroupId: HexKey? = null,
|
||||
createdAt: Long = TimeUtils.now(),
|
||||
initializer: TagArrayBuilder<WelcomeEvent>.() -> Unit = {},
|
||||
) = eventTemplate(KIND, welcomeBase64, createdAt) {
|
||||
keyPackageEventId(keyPackageEventId)
|
||||
welcomeRelays(relays)
|
||||
encoding()
|
||||
nostrGroupId?.let { addUnique(arrayOf("h", it)) }
|
||||
initializer()
|
||||
}
|
||||
}
|
||||
|
||||
+2
@@ -57,6 +57,7 @@ object WelcomeGiftWrap {
|
||||
relays: List<NormalizedRelayUrl>,
|
||||
recipientPubKey: HexKey,
|
||||
signer: NostrSigner,
|
||||
nostrGroupId: HexKey? = null,
|
||||
createdAt: Long = TimeUtils.now(),
|
||||
): GiftWrapEvent {
|
||||
// Step 1: Build the WelcomeEvent template and sign it
|
||||
@@ -65,6 +66,7 @@ object WelcomeGiftWrap {
|
||||
welcomeBase64 = welcomeBase64,
|
||||
keyPackageEventId = keyPackageEventId,
|
||||
relays = relays,
|
||||
nostrGroupId = nostrGroupId,
|
||||
createdAt = createdAt,
|
||||
)
|
||||
val welcomeEvent: WelcomeEvent = signer.sign(welcomeTemplate)
|
||||
|
||||
Reference in New Issue
Block a user