fix(marmot): scope Marmot storage per-account to prevent chat leakage

The three Marmot stores (MLS group state, messages, KeyPackage bundles)
were all initialized with the global app filesDir, so chats and MLS
state from one account were visible to any other logged-in account.

Pass each store a per-account subdirectory (accounts/<pubkey>) derived
from the signer pubkey, so paths become:

  files/accounts/<pubkey>/mls_groups/<groupId>/{state,retained,messages}
  files/accounts/<pubkey>/marmot_keypackages/state

Existing in-memory state was already scoped per-Account; this closes the
last remaining cross-account leak on disk.
This commit is contained in:
Claude
2026-04-22 18:30:24 +00:00
parent e452cf8abd
commit bd2fae9ca5
@@ -100,13 +100,14 @@ class AccountCacheState(
val signerWithClientTag = NostrSignerWithClientTag(signer, CLIENT_TAG_NAME)
val accountDir = File(rootFilesDir(), "accounts/${signer.pubKey}").apply { mkdirs() }
val mlsStore =
try {
val dir = rootFilesDir()
Log.d("AccountCacheState") {
"Initializing AndroidMlsGroupStateStore for ${signer.pubKey.take(8)}… at ${dir.absolutePath}"
"Initializing AndroidMlsGroupStateStore for ${signer.pubKey.take(8)}… at ${accountDir.absolutePath}"
}
AndroidMlsGroupStateStore(dir)
AndroidMlsGroupStateStore(accountDir)
} catch (e: Exception) {
Log.e(
"AccountCacheState",
@@ -121,7 +122,7 @@ class AccountCacheState(
val marmotMessageStore =
try {
AndroidMarmotMessageStore(rootFilesDir())
AndroidMarmotMessageStore(accountDir)
} catch (e: Exception) {
Log.e(
"AccountCacheState",
@@ -133,7 +134,7 @@ class AccountCacheState(
val marmotKeyPackageStore =
try {
AndroidKeyPackageBundleStore(rootFilesDir())
AndroidKeyPackageBundleStore(accountDir)
} catch (e: Exception) {
Log.e(
"AccountCacheState",