Files
amethyst/amethyst
Claude f5fc432e95 refactor(commons): move the marmot encrypted stores and the NIP-11 / online caches
Two clusters from the survey of what was left in amethyst/, both cases of code
that was shared-layer already and just happened to live in the app.

The four Marmot stores go to commons/jvmAndroid/marmot, next to the
EncryptedAppendLog they were already built on. Nothing in them was Android
despite the prefix: no android.* import, no Context, just
(rootDir: File, encryption: SecretEncryption) and the quartz interfaces. One
call site, AccountCacheState. The KDoc lines claiming "Android implementation"
and "AES/GCM via Android KeyStore" are reworded: SecretEncryption is
expect/actual, AndroidKeyStore on Android and a key file on desktop, so the old
wording is now only half true. Commons already referred to two of them by name
in its own KDoc; those references are updated.

They are Encrypted* rather than File*, and that is not cosmetic. cli/stores
already declares FileMlsGroupStateStore, FileMarmotMessageStore,
FileKeyPackageBundleStore and FilePublishObligationStore — deliberately
UNENCRYPTED test-harness stores, in a module that depends on commons. Two
same-named classes with opposite encryption semantics, one import away from each
other, is how MLS state ends up written in plaintext. Encrypted* is also what
these actually are, and reads correctly next to EncryptedAppendLog.

Nip11CachedRetriever + Nip11Retriever + RetrieveResult go to
commons/relays/nip11, and OnlineChecker to commons/service. RetrieveResult had
to travel: the cache is typed on it and commons cannot import from amethyst.
Nip11RetrieverTest travels too — it sat in the same package and used the class
with no import line. LoadRelayInfo and RelaySupportsNip stay behind; both reach
Amethyst.instance.

android.util.LruCache -> androidx.collection.LruCache is not the pure import
swap it looks like, and the compiler said so: androidx bounds V to Any, while
android.util.LruCache is a Java platform type that accepted
LruCache<NormalizedRelayUrl, RetrieveResult?>. Checked all four put() sites
first — every one stores a concrete RetrieveResult, so the nullable argument
never meant anything and get() still returns null on a miss, which is what the
readers already branch on. Dropping it is behaviour-preserving.

NotifyCoordinator does NOT move despite being grouped with the other two:
android.util.LruCache really is its only platform import, but it takes
accountForPubkey: (HexKey) -> Account?, and Account lives in amethyst/model. It
needs that abstraction, not an import swap.

17 new tests. OnlineChecker's predicates decide whether the UI shows a player or
an offline placeholder, so the five-minute TTL is pinned in both directions — a
stale online entry must stop reading online, and a stale offline one must stop
suppressing retries — along with resetIfOfflineToRetry dropping only failures,
since evicting good entries would refetch every URL that already worked. Its
suspend probe is left alone: it needs a real OkHttp round trip and commons has
no MockWebServer, so there is no honest way to drive it. Same reason
Nip11CachedRetriever gets nothing new here; its fetch and error-caching paths
are all network.

The Marmot tests cover what restart depends on: group state, sender ratchet and
message log surviving a new store over the same directory, deletes removing both
the state and the listing, and two account directories not seeing each other.
Writing them found validation I had not noticed reading the code — group ids
must be hex, which is a path-traversal guard — after a first pass using readable
labels failed every test on it. That guard is pinned now too: "../escape" and
friends are rejected rather than resolved to a path.

Verified: :commons:jvmTest (2549, 0 failures), :amethyst:testPlayDebugUnitTest,
:cli:compileKotlin, :commons:verifyKmpPurity,
:commons:compileCommonMainKotlinMetadata, :amethyst:compilePlayDebugKotlin.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AXvKXakvup4inNFfAhhr4L
2026-09-25 21:14:33 +00:00
..
2024-06-24 14:13:55 -04:00