* 'main' of https://github.com/vitorpamplona/amethyst:
New Crowdin translations by GitHub Action
update translations: CZ, DE, PT, SE
fix: restore local WatchAndDisplayNip05Row in ShowUserSuggestionList
refactor: remove Namecoin label from search results
feat: show Namecoin label on .bit search results without ViewModel state
refactor: remove Namecoin label from search results per review
refactor: simplify .bit search resolution per review feedback
feat: resolve bare .bit domains and show Namecoin resolution status in UI
New Crowdin translations by GitHub Action
New Crowdin translations by GitHub Action
refactor: rename trustAllCerts → usePinnedTrustStore
fix: harden TLS for GrapheneOS and security-conscious Android ROMs
doc: verify .onion ElectrumX cert via Tor, document pinning
fix: bypass cert pinning for .onion ElectrumX servers
feat: TOFU cert pinning for custom ElectrumX servers
feat: add Test Connection diagnostics to Namecoin settings
fix: pin ElectrumX server certs instead of trust-all TrustManager
# Conflicts:
# amethyst/src/main/java/com/vitorpamplona/amethyst/AppModules.kt
# amethyst/src/main/java/com/vitorpamplona/amethyst/ui/navigation/AppNavigation.kt
The local private WatchAndDisplayNip05Row uses NonClickableObserveAndDisplayNIP05
(plain text domain), while the shared one in ShowQRScreen uses the clickable
ObserveAndDisplayNIP05. They are not the same function — restore the local copy
to preserve the correct non-clickable behavior in user suggestions.
- Add localCache param to NotificationsScreen
- Seed reactions and zaps from cache on initial compose
- Notifications now persist across tab navigation
- All screens now have cache seeding (Feed, Thread, Profile,
Bookmarks, Reads, Notifications)
- Move BookmarkListState from amethyst/ to commons/commonMain/
- Change cache param from LocalCache to ICacheProvider
- Android file becomes typealias to commons version
- No settings or decryptionCache dependencies (simplest State class)
- Pins bookmarkList AddressableNote via strong ref for GC retention
- Add cleanMemory() to DesktopLocalCache (sweeps stale WeakRef entries)
- Add startCleanupLoop() to Coordinator with heap monitoring
(MemoryMXBean, checks every 30s, cleans at >75% heap or every 5min)
- cleanObservers() clears unused NoteFlowSets on notes + addressables
- Try-catch per operation so one failure doesn't skip the rest
- 2-minute startup grace period before first cleanup
- Cleanup job cancelled on clear() (account switch / logout)
- DesktopLocalCache now uses LargeSoftCache (WeakReference-based, GC-driven)
instead of BoundedLargeCache (strong refs, 50k cap, arbitrary eviction)
- Delete BoundedLargeCache.kt — no longer needed
- Rewrite findUsersStartingWith to use forEach instead of values()
- Remove BoundedLargeCache eviction tests (no longer applicable)
- Notes now only disappear when nothing references them, not arbitrarily
Per Vitor's feedback: "this maximum size approach might not work well,
as things will just disappear"
- Move LargeSoftCache from amethyst/model to commons/jvmAndroid/model/cache
so both Android and Desktop share the same WeakReference cache implementation
- Change ICacheProvider return types from Any? to proper types (User?, Note?)
since these model classes already live in commons
- Remove unnecessary casts in ThreadAssembler, ChatNewMessageState, SearchBarState
- Improve LargeSoftCache.cleanUp() to use single-pass iterator (zero allocation)
- Update Android imports in LocalCache, LargeSoftCacheAddressExt, and test
Per Vitor's feedback on PR #1905: BoundedLargeCache evicts arbitrarily.
LargeSoftCache uses WeakReferences so GC respects the reference graph.
Both screens now show cached data instantly on navigation instead of
showing loading states while re-fetching from relays.
- BookmarksScreen: reads cached BookmarkListEvent from addressableNotes,
seeds public bookmark events from notes cache
- ReadsScreen: seeds long-form notes (kind 30023) from notes cache
Relay subscriptions still run to fetch fresh data in parallel.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Cached value now stays visible until relay data exceeds it, preventing
the count jumping from cached→0→ticking back up on each navigation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Profile screen now reads initial values from cache so navigating back
shows data instantly instead of re-fetching everything from scratch.
- Seed displayName/about/picture from User.metadataOrNull() on compose
- Add followerCounts/followingCounts maps to DesktopLocalCache
- Write counts on each update, read on profile open
- Follower count still ticks up live (good UX) but starts from cached value
- 4 new tests for profile count caching + metadata cache reads
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Root cause of "0 relays, 0 notes, 0 follows": all screens used
connectedRelays as the relay set for subscriptions, but NostrClient
uses relay-on-demand — relays only connect when openReqSubscription
is called with their URL. This created a deadlock: screens waited for
connected relays, but relays only connect when screens subscribe.
Fix: use relayStatuses.keys (registered/available relays) which are
populated by addDefaultRelays() at startup. openReqSubscription
triggers connection on-demand.
Affected screens: FeedScreen, UserProfileScreen, ThreadScreen,
NotificationsScreen, BookmarksScreen, ReadsScreen, NewDmDialog.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add RelayHealthIndicator component — shows elapsed time since last
relay event. Hidden when <30s (healthy), shows "45s ago" / "3m ago"
when stale. Placed above BunkerHeartbeatIndicator in NavigationRail.
Reads from Coordinator's subscriptionHealth StateFlow.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Rewrite FeedScreen from inline EventCollectionState + 8 rememberSubscription()
calls to cache-centric DesktopFeedViewModel pattern:
- ViewModel keyed on feedMode with DisposableEffect cleanup (destroy())
- FeedState pattern matching (Loading/Loaded/Empty/Error)
- FeedNoteCard takes Note instead of Event, reads counts from Note.flowSet
- DisposableEffect for note.clearFlow() cleanup in LazyColumn
- DisposableEffect for Coordinator interaction subscription lifecycle
- LaunchedEffect for rate-limited metadata loading via Coordinator
- Extract FeedHeader into separate composable
- Add destroy() to DesktopFeedViewModel (ViewModel.clear() is internal in KMP)
- Update UserProfileScreen to look up Note from cache for FeedNoteCard
Removes ~350 lines of inline state management (EventCollectionState,
zapsByEvent, reactionIdsByEvent, replyIdsByEvent, repostIdsByEvent,
followedUsers, bookmarkList, 8 rememberSubscription blocks).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add try-catch in consumeEvent() so one bad event doesn't kill the
pipeline. Add requestInteractions()/releaseInteractions() with Job
tracking via ConcurrentHashMap for screen-scoped subscription lifecycle.
Add SubscriptionHealth tracking (lastEventReceivedAt, eoseReceived)
exposed as StateFlow for UI consumption.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
toNoteDisplayData() now reads User.toBestDisplayName() from cache
instead of always showing npub. Falls back to npub if no metadata.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Resolve LruCache vs LargeCache: use LargeCache (lock-free ConcurrentSkipListMap)
with BoundedLargeCache wrapper for size enforcement on put()
- Confirm LargeCache available on desktop via quartz jvmAndroid source set
- Detail FeedNoteCard rewrite: Note model field mapping, 5 subscription removals
- Fix filter examples to use filterIntoSet (LargeCache API)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add quartz protocol package for NIP-86 JSON-RPC relay management with
NIP-98 HTTP authorization, and a management UI accessible from the
relay information screen when the relay advertises NIP-86 support.
Quartz (nip86RelayManagement/):
- Nip86Method: all 21 method constants from the spec
- Nip86Request: serializable JSON-RPC request with factory methods
- Nip86Response: response model with typed result parsing helpers
- Nip86Client: builds NIP-98 auth headers, serializes requests,
parses responses for each result type (pubkeys, events, kinds, IPs)
Amethyst:
- Nip86Retriever: OkHttp-based HTTP executor for NIP-86 calls
- RelayManagementViewModel: state management for all NIP-86 operations
- RelayManagementScreen: tabbed UI (Pubkeys, Events, Kinds, IPs,
Settings) with add/remove dialogs, moderation queue, and relay
settings fields
- Route.RelayManagement added to navigation
- "Manage Relay" button shown in RelayInformationScreen top bar when
relay's supported_nips includes "86"
https://claude.ai/code/session_01QckCAm1T8pJqqmURBiNtaQ
Add support for NIP-77 (Negentropy Syncing) protocol messages using
the negentropy-kmp library for efficient set reconciliation between
client and relay.
New files:
- NegOpenCmd, NegMsgCmd, NegCloseCmd: Client-to-relay commands
- NegMsgMessage, NegErrMessage: Relay-to-client messages
- NegentropySession: Client-side reconciliation orchestrator
- NegentropyServerSession: Server-side reconciliation handler
- NegentropyManager: High-level sync manager with listener callbacks
Updated serializers:
- Jackson MessageSerializer/Deserializer for NEG-MSG, NEG-ERR
- Jackson CommandSerializer/Deserializer for NEG-OPEN, NEG-MSG, NEG-CLOSE
- Kotlin MessageKSerializer/CommandKSerializer for all NIP-77 types
Compatible with strfry relay's negentropy implementation via the
negentropy-kmp library which implements the same Protocol V1 spec.
https://claude.ai/code/session_01Dc6W1G1jURAAR9kzyVvk6g
Add a new screen allowing users to send NIP-62 relay deletion requests.
Users can select a specific relay or ALL RELAYS, pick a date (data
created before that date will be requested for deletion), and provide
an optional reason. Includes confirmation dialog with clear warnings
about the irreversible nature of vanish requests.
https://claude.ai/code/session_019Xrprdfq6pVN8beYrYUSr4
Derive the label from the existing searchTerm — if it ends with .bit,
show a chain-emoji label above each user result. No new StateFlow or
ViewModel state management needed.