Remove namecoinResolvedUser StateFlow and the chain-emoji label
from search results. .bit resolution still works through the
existing NIP-05 path — just no special UI decoration.
Address @vitorpamplona's review comments on the last commit:
1. Remove NamecoinResolutionState sealed class and status banners
— the existing NIP-05 resolver already handles .bit, no need for
separate Namecoin-specific UI state (spinner, chain emoji, error)
2. Remove separate toNip05IdOrNull() resolution path
— widen the existing `if (term.contains('@'))` gate in both
SearchBarViewModel and UserSuggestionState to also accept bare
.bit domains (synthesize _@domain.bit → existing NIP-05 flow)
3. Remove d/ and id/ identifier support from search
— per feedback, only NIP-05 style resolution (.bit) in search
4. Remove special Namecoin result rendering in ImportFollowList
— just show UserLine for all results, no separation needed
5. Keep a lightweight 'Namecoin' label on resolved users in search
— namecoinResolvedUser StateFlow tracks the .bit-resolved user,
shown as a small label above the UserCompose row
Net: -405 lines, +50 lines across 6 files.
Widen the existing NIP-05 resolution gate in UserSuggestionState and
SearchBarViewModel to also accept bare .bit domains and d//id/ Namecoin
identifiers. A synthesised Nip05Id is passed to the existing
nip05Client.get() path — which already routes .bit to the
NamecoinNameResolver — so no separate resolution logic is needed.
Add NamecoinResolutionState (Idle/Resolving/Resolved/Error) as a
StateFlow so the UI can show progress. ImportFollowListSelectUserScreen
and SearchScreen display an animated status banner (spinner while
resolving, chain emoji on success, warning on error) and label
Namecoin-resolved profiles in the results list.
The field no longer means 'trust all certificates' — since the Samsung
One UI 7 fix, it means 'use the pinned trust store (hardcoded +
TOFU-pinned certs + system CAs) instead of system-only trust.'
The old name was actively misleading and could cause a future
contributor to interpret the flag literally, potentially reintroducing
the trust-all pattern that Samsung Knox and GrapheneOS reject.
Renamed across all 4 files:
- ElectrumxServer.kt: field definition + updated KDoc
- ElectrumXClient.kt: createSocket() usage + comments
- NamecoinSettings.kt: parseServerString() usage + comments
- NamecoinSettingsTest.kt: test assertions
Address compatibility and security issues identified by reviewing
GrapheneOS source (hardened Conscrypt, strict TLS enforcement):
1. .onion: prefer pinned factory, fall back to trust-all
GrapheneOS patches Conscrypt with stricter TLS enforcement that may
reject no-op X509TrustManagers even for proxied sockets. The .onion
server's cert is already in PINNED_ELECTRUMX_CERTS (same operator as
electrumx.testls.space), so we now use cachedPinnedSslFactory() as
the primary path. onionSslFactory() (trust-all) is kept as a fallback
on SSLHandshakeException only — this handles cert rotation or unknown
.onion servers gracefully.
2. TOFU: require explicit user confirmation before pinning
Previously, Test Connection auto-pinned every cert on success. An
attacker performing MITM during first test would get their cert
permanently trusted. Now each new cert triggers an AlertDialog showing
the full SHA-256 fingerprint. Users must explicitly accept ('Trust')
or reject each cert — aligning with GrapheneOS's philosophy of
explicit trust decisions.
3. Clarify trustAllCerts semantics
The field name is misleading post-refactor: it now means 'use pinned
trust store' not 'trust all certificates'. Added TODO to rename to
usePinnedTrustStore and updated inline comments to prevent future
misinterpretation.
Note: hostname verification on raw SSLSocket is a pre-existing gap
(not introduced by the Samsung fix PR) — SSLSocket.createSocket() uses
the host parameter for SNI only, not hostname verification. A follow-up
should add endpointIdentificationAlgorithm='HTTPS' or fingerprint-based
verification for pinned certs.
Connected to the .onion hidden service via Tor SOCKS proxy and
confirmed it serves the SAME certificate as electrumx.testls.space:
SHA-256: 53:65:D5:BB:26:19:F5:40:1C:D8:8E:FC:AF:FB:A5:B2:...
The .onion cert is already covered by the first pinned cert entry.
Added comments documenting this relationship and instructions for
fetching .onion certs via Tor for future updates.
The .onion still uses onionSslFactory() (trust-all) as the primary
path — this is correct since Tor provides its own authentication.
The pinned cert serves as defense-in-depth.
Tor hidden services are authenticated by their onion address (the
public key hash), making TLS certificate verification redundant.
The .onion server in TOR_ELECTRUMX_SERVERS was going through
pinnedSslFactory() but its cert isn't in the pinned list (and can't
be fetched without Tor). This would cause .onion connections to fail
with SSLHandshakeException when Tor mode is active.
Fix: .onion addresses now use a dedicated onionSslFactory() with
trust-all, which is safe because:
1. Tor provides end-to-end encryption
2. The onion address IS the server identity proof
3. Samsung Knox trust-all rejection doesn't apply to proxied sockets
When a user adds a custom ElectrumX server and runs Test Connection,
the server's TLS certificate is automatically captured and pinned
(Trust On First Use). This allows custom servers with self-signed
certificates to work on Samsung/Xiaomi/OnePlus devices.
Changes:
- ElectrumXClient: addPinnedCert(), setDynamicCerts() for runtime
cert management; testServer() now captures server cert PEM and
SHA-256 fingerprint from SSL session
- NamecoinSharedPreferences: persist pinned certs to DataStore
- AppModules: load pinned certs on startup, sync to ElectrumXClient
- NamecoinSettings: custom servers always set trustAllCerts=true
(ElectrumX servers almost universally use self-signed certs)
- UI: shows cert fingerprint in test results, auto-pins on success
- ServerTestResult: new serverCertPem + certFingerprint fields
Flow: Add server → Test Connection → cert auto-captured and pinned →
future connections trust that cert even on Samsung Knox devices.
Add per-server connection testing with detailed error reporting:
- Test Connection button tests each ElectrumX server individually
- Shows streaming results: ✅ success with response time, ❌ failure
with human-readable error (TLS handshake failed, connection refused,
timeout, DNS failed, invalid response, etc.)
- Diagnostic card shows: last test timestamp + success count, device
info (manufacturer/model/Android/API), TLS version negotiated
- ElectrumXClient.testServer() method for single-server diagnostics
with TLS version capture from SSL session
This gives users (and bug reporters) immediate visibility into why
Namecoin resolution may be failing silently on their device.
Samsung One UI 7 (Android 16) silently rejects TLS connections that use
a no-op X509TrustManager which accepts all certificates. This breaks
Namecoin resolution on all Samsung devices running One UI 7, including
Galaxy A15 (SM-A156E) and Galaxy S24 Ultra (SM-S938B).
Replace trustAllSslFactory() with pinnedSslFactory() that:
- Pins the actual self-signed certificates of known ElectrumX servers
- Also includes system CA certificates for servers with real certs
- Uses a proper TrustManagerFactory chain that Samsung Knox accepts
Additional OEM compatibility hardening:
- Cache the SSLSocketFactory (avoid expensive rebuild per connection)
- Request TLSv1.2 explicitly (Xiaomi MIUI/HyperOS and OnePlus ColorOS
Conscrypt forks may default to TLS 1.0 for raw socket upgrades)
- Enforce TLSv1.2+ enabled protocols on the SSLSocket
- KeyStore fallback to PKCS12 if default type fails (Xiaomi)
- Defensive try/catch on system CA cert re-insertion (some OEMs return
certs that cannot be added to a new KeyStore)
Tested on Android 16 (API 36) emulator — all ElectrumX servers connect
(hostname, IP-address, factory reuse) and .bit resolution works E2E.
Pinned certs:
- electrumx.testls.space:50002 (expires 2027-05-04)
- nmc2.bitcoins.sk:57002 (expires 2030-10-22)
When a user clicks on the reply preview (inner quote) in a chat message,
the chat feed now scrolls to the original replied-to message in the same
screen instead of doing nothing.
https://claude.ai/code/session_01UF4VxoWGvvvwtLYBLBYw9w
* 'main' of https://github.com/vitorpamplona/amethyst:
code review: - Removed redundant equality guards in SideEffect — MutableState already suppresses no-op writes for value types - Changed mutableStateOf({}) to mutableStateOf(null) with explicit nullable type — clearer intent, no accidental no-op invocation - stopRecording() now early-returns with ?: return when not recording, so the Toast only shows for genuinely failed recordings (too short)
entire solid recording indicator bar stops recording when tapped, not just the small stop icon.
New Crowdin translations by GitHub Action
update skill
update translations: CZ, DE, PT, SE
- Removed redundant equality guards in SideEffect — MutableState already suppresses no-op writes for value types
- Changed mutableStateOf({}) to mutableStateOf(null) with explicit nullable type — clearer intent, no accidental no-op invocation
- stopRecording() now early-returns with ?: return when not recording, so the Toast only shows for genuinely failed recordings (too short)