Commit Graph

13620 Commits

Author SHA1 Message Date
Vitor Pamplona 54f667f5bd fix(nests): unstick reconnecting speaker + listener interop on real relays
Two production-path bugs surfaced during a full interop sweep
(`./gradlew :nestsClient:jvmTest -DnestsInterop=true` +
`-DnestsHangInterop=true`) plus the toolchain / harness friction that
was hiding them.

Production fixes:

- ReconnectingNestsSpeaker's hot-swap path opens publishers via
  `openPublisherForHotSwap` instead of `startBroadcasting`, so the
  underlying `MoqLiteNestsSpeaker`'s state machine never made the
  Connected -> Broadcasting transition. The reconnect wrapper mirrors
  that state, so callers waiting on Broadcasting (the VM,
  `connectReconnectingNestsSpeaker` interop tests) hung on Connected
  forever. Adds `HotSwappablePublisherSource.reportBroadcasting(isMuted)`,
  implemented in `MoqLiteNestsSpeaker`, and the hot-swap pump now
  calls it each iteration so a JWT-refreshed session re-enters
  Broadcasting too.

- The stale-group filter on listener reconnect assumes monotonic
  group lineage across publisher session-restarts (true for
  ReconnectingNestsSpeaker, which seeds each new publisher with the
  prior `nextSequence`; false for external publishers like
  kixelated's hang-publish reference, which mints a fresh state on
  every reconnect and restarts at 0). Without compensation, the
  watermark from a session-1 max ~18 dropped the entire post-restart
  stream from session 2. The collect now resets the watermark on the
  first object of a new subscription when its group id arrives well
  below the current watermark — a publisher-restart signal that a
  relay-cache replay (which carries exactly the prior max) wouldn't
  produce.

Test harness fixes — these are what blocked the suites from running
at all on a clean Apple-Silicon box:

- NostrNestsHarness: the cloned `docker-compose-moq.yml` declares no
  `depends_on`, so on a cold `up -d` moq-relay raced moq-auth's Node
  boot, got `Connection refused` on its JWKS GET, exited with no
  retry, and every later QUIC handshake failed with "read loop exited
  (socket closed or peer closed)". Gate on moq-auth's /health first,
  then idempotent `up -d moq-relay` so the relay always boots against
  a live auth sidecar.

- nestsClient/build.gradle.kts: cargo builds inside the hang-interop
  task panic under CMake 4.x because `audiopus_sys` / rustls' aws-lc-sys
  ship a `CMakeLists.txt` that predates CMake 4's minimum-version
  floor. Set `CMAKE_POLICY_VERSION_MINIMUM=3.5` on each cargo Exec.

- JvmOpusEncoder + the hang-interop Test task: opus-java 1.1.1's
  bundled `natives/darwin/libopus.dylib` is x86_64-only, so its
  in-jar loader reports unsupported on Apple Silicon. Probe a small
  set of canonical system libopus locations (`brew install opus` on
  macOS, distro paths on linux) and `System.load` by absolute path so
  the symbols are in the process when JNA's lazy `Opus.INSTANCE`
  init falls back to RTLD_DEFAULT. Gradle also threads
  `/opt/homebrew/opt/opus/lib` onto `jna.library.path` for
  completeness; both paths are no-ops where the in-jar binary
  already loads.

After these:
- :nestsClient:jvmTest -DnestsInterop=true             — 312/312, 0 fail
- :nestsClient:jvmTest -DnestsHangInterop=true         — 312/312, 0 fail
- quic/interop/run-matrix.sh -s aioquic                — 19 passed,
  0 failed, 3 unsupported (E/V2/CM are documented amethyst gaps).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 13:01:55 -04:00
Vitor Pamplona 3a7e9a90a9 Merge pull request #2908 from nrobi144/fix/account-security-hardening
fix(desktop): Account security hardening — NWC to keychain, single source of truth
2026-05-15 07:08:12 -04:00
nrobi144 325a1f6f6d fix: address code review — NWC key cleanup, CancellationException rethrow
- removeAccountFromStorage now deletes nwc_<npub> keychain entry
  (fixes orphaned wallet key on account removal)
- Rethrow CancellationException in loadSavedAccount and loginWithKey
  catch blocks (fixes broken structured cancellation)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-15 11:33:16 +03:00
nrobi144 46caa4d795 fix(desktop): harden account security — NWC to keychain, single source of truth
CRITICAL: Move NWC wallet secret from plaintext nwc_connection.txt to OS
keychain. The NWC secret is a private key that can authorize Lightning
payments — storing it in plaintext allowed any process to steal funds.

Security fixes:
- NWC secret stored in OS keychain as "nwc_<npub>" (per-account)
- accounts.json.enc is now the sole source of truth for cold boot
- Eliminate bunker_uri.txt, last_account.txt, nwc_connection.txt
- Legacy files deleted on first startup (one-time cleanup)
- logout(deleteKey=true) now removes account from accounts.json.enc
- Corrupted accounts.json.enc backed up as .corrupt.<timestamp>

Cold boot rewrite:
- loadSavedAccount() routes by SignerType from accounts.json.enc
- No longer reads stale bunker_uri.txt (fixes nsec→bunker confusion)
- No longer reads last_account.txt (uses activeNpub from metadata)

Multi-account improvements:
- NWC connections are per-account (switch account = switch wallet)
- Each account type (Internal/Remote/ViewOnly) loads correctly
- saveBunkerAccount() no longer writes to bunker_uri.txt

Updated 8 existing test files to use accountStorage instead of
writing legacy files directly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-15 11:29:42 +03:00
David Kaspar 710482e91d Merge pull request #2909 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2026-05-15 10:01:46 +02:00
Crowdin Bot 3ab0ba2c6a New Crowdin translations by GitHub Action 2026-05-15 08:00:37 +00:00
davotoula 4cdf8c3ec2 i18n: translate favorite-DVMs empty-state and CTA strings
Adds cs-rCZ, pt-rBR, sv-rSE, de-rDE translations for the three new
keys from the favorite-feeds "Add more…" flow:
favorite_dvms_add_more, favorite_dvms_empty_step1,
favorite_dvms_empty_step2.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 09:56:50 +02:00
Vitor Pamplona 49e4447b90 Merge pull request #2907 from vitorpamplona/claude/improve-compose-settings-NKNWr
Refactor ComposeSettingsScreen to use unified tile components
2026-05-14 20:05:38 -04:00
Vitor Pamplona c571a24fe8 Merge pull request #2906 from vitorpamplona/claude/fix-nests-quic-audio-delay-MHzZL
Fix QUIC handshake timeout, IPv4 preference, and SNI handling
2026-05-14 20:05:20 -04:00
Claude f8336b07e7 refactor: modernize Compose Settings screen layout
Switch the Compose Settings screen from the tightly-spaced legacy
SettingsRow list to the card-based SettingsSection / SettingsControlRow
pattern already used by SecurityFiltersScreen. Adds leading icons,
proper breathing room between items, and full-row tap targets for the
switches.
2026-05-14 23:03:00 +00:00
Claude 5d406c5998 fix(nests): address self-audit findings on the interop transport fixes
- QuicWebTransportFactory: re-throw CancellationException from the bounded
  handshake instead of wrapping it in WebTransportException, so a
  parent-scope cancellation no longer breaks structured concurrency.
  Mirrors the existing handling on the post-CONNECT path.
- NostrNestsHarness: correct two misleading comments — the dev endpoint's
  IPv4 guarantee comes from QuicWebTransportFactory's resolve step, not
  from `localhost` itself; and DEFAULT_REVISION is documented as tracking
  `main` (a known drift risk against the pinned moq-relay), not pinned.

https://claude.ai/code/session_01PoQupfdoKU3ryQwLwTeXeM
2026-05-14 23:02:52 +00:00
Claude ec92a82e4e fix(nests): pin the interop harness's moq-relay to the version moq-auth expects
The local Docker interop stack builds moq-relay from a `./moq` checkout
that NostrNestsHarness cloned at unpinned `main` (~0.10.25+), while
nostrnests's moq-auth sidecar mints tokens for moq-relay 0.10.10 — the
version nostrnests pins in their own nests-relay/Dockerfile.relay. The
JWKS/JWT-signature handling drifted across those releases, so the relay
rejected every connection with `failed to decode the token` even though
the QUIC/TLS/WebTransport handshake completed cleanly.

Pin DEFAULT_MOQ_REVISION to the moq-relay-v0.10.10 release tag so the
harness builds the relay moq-auth was written against, and fetch tags so
the tag is checkout-able on a pre-existing clone.

https://claude.ai/code/session_01PoQupfdoKU3ryQwLwTeXeM
2026-05-14 22:51:37 +00:00
Claude 692a385f5b diag(nests): include the resolved socket target in handshake failures
A stalled/failed QUIC handshake gave no hint whether the socket connected
over IPv4 or fell into the ::1 blackhole. The HandshakeFailed messages
now carry `socket=<addr>:<port> sni=<host>` so the failure itself shows
which address resolvePreferIpv4 picked.

https://claude.ai/code/session_01PoQupfdoKU3ryQwLwTeXeM
2026-05-14 22:02:27 +00:00
Claude 6a60918cc6 fix(nests): connect over a resolved IPv4 address while keeping hostname SNI
After switching the dev harness to `localhost`, the QUIC handshake still
stalled and the relay logged nothing at all — packets weren't reaching
it. `localhost` resolves to ::1 first on most systems, and IPv6 loopback
isn't reliably routable (Docker's IPv6 UDP port-forwarding silently
blackholes), so the UDP socket was connecting into a hole.

QuicWebTransportFactory.connect now resolves the authority host to a
concrete address preferring IPv4 for the UDP socket target, while still
passing the original hostname as the TLS SNI server name — so SNI keeps
matching the server certificate and stays a legal RFC 6066 host_name.

https://claude.ai/code/session_01PoQupfdoKU3ryQwLwTeXeM
2026-05-14 21:51:35 +00:00
Vitor Pamplona 18c669e7bb Merge pull request #2905 from vitorpamplona/claude/update-changelog-efQ9o
Release v1.09.0: We are going crazy
2026-05-14 17:42:11 -04:00
Claude ed1f10c071 fix(quic): omit IP-literal SNI, bound the handshake, fix dev harness host
Three findings from the local nests interop hang (relay logged "Illegal
SNI extension: ignoring IP address presented as hostname"):

- TlsClientHello sent the connect host verbatim as TLS SNI, including
  IP literals. RFC 6066 §3 forbids IP literals in server_name; a strict
  relay (rustls) discards the extension, ends up with no SNI to select
  its certificate on, and the handshake stalls. Both ClientHello
  builders now omit server_name when the host is an IP literal
  (new isIpLiteralHostname predicate).

- QuicWebTransportFactory.connect never bounded awaitHandshake() — only
  readConnectResponse had a timeout — so a stalled handshake hung
  connect() indefinitely. It now fails fast after connectTimeoutMillis
  with a message naming the likely cause.

- NostrNestsHarness pointed moqEndpoint at 127.0.0.1, which forced the
  IP-literal SNI path against the dev relay. Switched to localhost: a
  valid hostname that matches the relay's generated dev cert and still
  resolves to loopback.

https://claude.ai/code/session_01PoQupfdoKU3ryQwLwTeXeM
2026-05-14 21:41:07 +00:00
Claude 84c5013d10 docs: de-jargon v1.09.0 changelog for outside readers
Replaces internal class names and implementation jargon with
reader-facing descriptions, and drops a few pure-internal lines
(AccountManager migration, SQLite parity matrix, remember-wrapper
removal, ProGuard/R8 keep rules).

https://claude.ai/code/session_01PyGyMpa9XvTHrB4iqZX5oZ
2026-05-14 21:39:43 +00:00
Claude 806d357569 diag(nests): explain why a WebTransport CONNECT failed instead of bare :status=0
QuicWebTransportFactory threw `:status=0` for every distinct CONNECT
failure mode — relay never answered, relay FIN'd the request bidi with
no H3 response, relay sent a HEADERS frame lacking :status, or the whole
connection was torn down by a CONNECTION_CLOSE. The four cases need
different fixes but produced identical, undiagnosable errors.

readConnectResponse now records bytes/H3-frames seen on the request
stream and reports which of those cases occurred; the thrown exception
also carries a connection-state snapshot (conn.status, closeReason,
closeErrorCode, requestStreamClosed) captured before driver.close(), so
a relay CONNECTION_CLOSE (e.g. H3_SETTINGS_ERROR from a rejected SETTINGS
frame) is distinguishable from a request-stream-only close.

https://claude.ai/code/session_01PoQupfdoKU3ryQwLwTeXeM
2026-05-14 21:23:24 +00:00
Claude d32b0fdb4c docs: scope-accuracy audit pass on v1.09.0 changelog
Walks the full v1.08.0..HEAD non-merge history and corrects items that
were absent or under-represented relative to what shipped:

- Fixes inaccuracy: DLNA casting was removed before release; casting is
  Chromecast-only on the Play build.
- Promotes Interest Sets (NIP-51) from a fix-line to a feature.
- Adds configurable bottom nav, drawer reorganization, autoplay-videos
  setting, lifecycle-aware subscriptions, adaptive video cache, account
  settings republish-to-new-relays, Tor clearnet fallback, and more.
- Expands NIP-34 (PRs, Status, Repo State), the custom secp256k1 entry
  (Kotlin core + C/JNI accelerated path), Namecoin, and Marmot scope.

https://claude.ai/code/session_01PyGyMpa9XvTHrB4iqZX5oZ
2026-05-14 21:23:00 +00:00
Vitor Pamplona 014763cf46 Merge pull request #2904 from vitorpamplona/claude/investigate-coil-image-loading-XIIOT
Replace ConcurrentRequestStrategy.UNCOORDINATED with DeDupeConcurrentRequestStrategy
2026-05-14 17:15:04 -04:00
Claude 457f140dd4 docs: add ThumbHash support as its own v1.09.0 feature
It was only noted under HLS video uploads, but ThumbHash was added
app-wide alongside BlurHash across events, uploads and the UI.

https://claude.ai/code/session_01PyGyMpa9XvTHrB4iqZX5oZ
2026-05-14 21:13:54 +00:00
Claude e3647a46c4 docs: audit pass — add missing v1.09.0 features
Adds mid-April features the original draft skipped: custom emoji packs
(NIP-30), standalone drawer screens (Articles/Products/Public Chats/
Communities/Live Streams/Follow Packs), richer live stream chat,
media content warnings, the video quality picker, desktop App Drawer
and workspace management, plus PDF reader zoom details.

https://claude.ai/code/session_01PyGyMpa9XvTHrB4iqZX5oZ
2026-05-14 21:11:11 +00:00
Claude d370c7e40c perf(images): coalesce duplicate in-flight Coil network requests
Switch the three image NetworkFetcher sites from
ConcurrentRequestStrategy.UNCOORDINATED to DeDupeConcurrentRequestStrategy,
Coil's coordinated default. UNCOORDINATED let every request hit the
network independently, so the same avatar across many visible feed
notes became N parallel downloads competing for the per-host
dispatcher slots and queueing the genuinely distinct images behind
them. The de-duping strategy lets concurrent requests for a URL share
the first download and then read from disk.

https://claude.ai/code/session_01BeWZGTz48kzURQLTYyN5x9
2026-05-14 21:10:20 +00:00
Vitor Pamplona c6ce45aad3 Merge pull request #2902 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2026-05-14 17:09:27 -04:00
Claude b53c0ff958 docs: name amy CLI in v1.09.0 What's New list
https://claude.ai/code/session_01PyGyMpa9XvTHrB4iqZX5oZ
2026-05-14 21:06:21 +00:00
Claude cbd4980f3e docs: add ObservableEventStore + Pull Notification to v1.09.0 changelog
Gives the reactive ObservableEventStore layer its own Quartz bullet
(it is store-agnostic, not SQLite-specific) and lists Pull Notification
(internal Pokey) under What's New.

https://claude.ai/code/session_01PyGyMpa9XvTHrB4iqZX5oZ
2026-05-14 21:00:45 +00:00
Crowdin Bot 4b927215b7 New Crowdin translations by GitHub Action 2026-05-14 20:57:40 +00:00
Vitor Pamplona 422b67faa5 Merge pull request #2903 from davotoula/feat/favorite-feeds-add-more
Favourite feeds: clearer empty state + pinned "Add more…" CTA
2026-05-14 16:55:33 -04:00
Claude e18df212c9 docs: drop bold emphasis from v1.09.0 changelog entry
https://claude.ai/code/session_01PyGyMpa9XvTHrB4iqZX5oZ
2026-05-14 20:52:53 +00:00
Claude f12bc25b98 docs: break long v1.09.0 changelog bullets into sub-points
Splits dense comma-separated changelog lines into nested sub-bullets
and short sentences for easier scanning.

https://claude.ai/code/session_01PyGyMpa9XvTHrB4iqZX5oZ
2026-05-14 20:47:51 +00:00
davotoula d0ee9f6079 i18n: translate add_client_tag_title/explainer (cs, pt-BR, sv, de)
Add translations for the new NIP-89 client tag settings strings
across cs-rCZ, pt-rBR, sv-rSE, de-rDE locales.
2026-05-14 22:47:26 +02:00
Claude 6b8467bde3 test(nests): measure end-to-end send→arrival audio latency in interop sweeps
SendTraceScenario already swept framesPerGroup and recorded per-frame
send durations + arrival wall-times, but never correlated them — so it
could verify frame delivery but not the send-side latency a listener
perceives. Record each frame's send wall-clock timestamp and derive the
true send→arrival latency (time-to-first-frame + p50/p99/min/max) per
subscriber. With framesPerGroup > 1 this surfaces the batching window
(~framesPerGroup * cadence) as the latency floor.

Add framesPerGroup=10 and =50 (the production default) sweep entries to
both the prod and local-harness suites so the batching-vs-latency
tradeoff curve is visible in one run.

https://claude.ai/code/session_01PoQupfdoKU3ryQwLwTeXeM
2026-05-14 20:46:27 +00:00
davotoula 7d4eaa4faa Code review:
- hoist star inline id, fix TalkBack alt text, add translator note
- split empty state, dedupe CTA, remember inline content
2026-05-14 22:42:08 +02:00
davotoula e73715f64a Layout changes:
pin "Add more…" button to bottom of populated favourites screen
group empty-state icon/headline/steps and add top breathing room
spread empty-state evenly and add numbered step prefixes
replace empty-state subtitle with 2-step instruction including inline star icon
hide explainer when favorite feeds exist and add "Add more…" button
2026-05-14 22:42:07 +02:00
Claude 42113544c8 docs: add v1.09.0 changelog entry
Adds the v1.09.0 "We are going crazy" release notes, including the
last 2-3 weeks of work: Desktop multi-account, scheduled posts, LAN
video casting, thread muting, configurable home tabs, NIP-9A community
rules, the geode relay module, expanded QUIC RFC coverage and
quic-interop-runner results, moq-lite nestsClient, and the May fixes.

https://claude.ai/code/session_01PyGyMpa9XvTHrB4iqZX5oZ
2026-05-14 20:27:49 +00:00
Vitor Pamplona 5f89648c54 Merge branch 'main' of https://github.com/vitorpamplona/amethyst 2026-05-14 15:42:22 -04:00
Vitor Pamplona bb934cfc07 Fixing nests scrolling bar 2026-05-14 15:40:47 -04:00
Vitor Pamplona f2068d6ac3 Merge pull request #2901 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2026-05-14 15:24:24 -04:00
Crowdin Bot a42e759d01 New Crowdin translations by GitHub Action 2026-05-14 19:23:21 +00:00
Vitor Pamplona e609fcdd38 Merge branch 'main' of https://github.com/vitorpamplona/amethyst 2026-05-14 15:20:26 -04:00
Vitor Pamplona 0fcbfef72c Merge pull request #2898 from vitorpamplona/claude/fix-audio-replay-bug-ztEPz
Drop stale groups on reconnect to prevent audio replay
2026-05-14 15:14:26 -04:00
Vitor Pamplona 30df95d4d6 Merge pull request #2900 from davotoula/fix/relay-broadcast-cycle
bugfix: break a-tag cycle in computeRelayListToBroadcast
2026-05-14 15:14:08 -04:00
Vitor Pamplona a474282b8e Fixes bottom bar look to match previous colors 2026-05-14 15:12:55 -04:00
David Kaspar 7ed37e818f Merge pull request #2899 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2026-05-14 21:04:39 +02:00
Crowdin Bot 961cce7f18 New Crowdin translations by GitHub Action 2026-05-14 18:54:14 +00:00
davotoula a1aa9ebb02 fix(account): break a-tag cycle in computeRelayListToBroadcast
An addressable event whose `a` tags reach itself — directly (self-mention)
or via any cycle of other in-cache events — would recurse unbounded
through `AddressHintProvider` / `EventHintProvider` branches and crash
with StackOverflowError on publish (`signAndComputeBroadcast` →
`computeRelayListToBroadcast`). The crash was reproducible on the device
when publishing a kind-1 note that referenced an article whose own event
carries a self `a` tag (e.g. YakiHonne-authored long-form posts include
their own naddr in their `a` tags).
2026-05-14 20:53:28 +02:00
davotoula 7dbb78644a i18n: translate compose/profile-UI/security/nest strings into cs/de/pt-BR/sv
Adds Czech, German, Brazilian Portuguese, and Swedish translations
for 19 new keys covering auto-create-drafts compose settings,
profile-UI section toggles, security-screen empty states and
section headers, external-resource comment scopes, and nest
host-action failure toasts.
2026-05-14 20:50:48 +02:00
Vitor Pamplona 08adffb862 Merge pull request #2897 from vitorpamplona/claude/drag-drop-corner-icon-EoFbd
Move drag gesture detection to icon Box in VideoPlayerButtonItemCard
2026-05-14 14:46:43 -04:00
Vitor Pamplona 35be1aafd4 Merge pull request #2896 from vitorpamplona/claude/enable-client-tag-security-filters-i0rRK
Invert client tag setting: add instead of disable
2026-05-14 14:45:43 -04:00
Claude 39f2b939c3 fix: drop stale relay-replayed groups on Nests listener reconnect
On every re-subscribe (listener reconnect or publisher-cycle), the
moq-lite relay re-serves its cached latest group from the first frame.
The wrapper forwarded it straight into the decoder, so in a fully-muted
room the same old clip looped once per reconnect. Track the highest
group sequence delivered by prior subscriptions and drop any group not
strictly newer, mirroring kixelated/hang's Container.Consumer.#run.

https://claude.ai/code/session_01G9h2dzkEj6Y2F1Yr2kCojp
2026-05-14 18:43:53 +00:00