#Amethyst v1.16.0: My Fitness, BOLT12 Payments, and a Leaner Core Highlights: - Adds **My Fitness**, a dashboard over everything you have trained. - Moves **BOLT12 offers** into the profile payment rail, and zaps over BOLT11 when a wallet refuses one. - Opens the **zapped note** from wallet history. - Fixes **Blossom uploads**, broken on most servers since 1.15.0. - Takes **~40 MB off every `amy` distribution**. - Stops counting a relay that hung up as connected. - Updates **Arti to 2.6.0** for two medium-severity fixes. ## New Features ### My Fitness - Adds a training dashboard, reached from the drawer or a pinned bottom-bar slot. - Builds it from both sources: Health Connect, and the kind 1301 workouts you published. Neither is required. - Summarises four weeks: this week against last, weekly averages, a breakdown by activity, best efforts, active days and a day streak. - Shows distance, calories, steps, climb and heart rate only when a workout carries them. - Offers to share a workout only when it is not already published. - Prefers the Health Connect copy when the same effort appears in both sources — it carries the heart rate, steps and climb the published event drops. - Filters the Workouts feed by **Mine** from the top nav. - Reads workouts in the POWR dialect, which carry `start`/`end` and no `duration`. ### Payments - Renders each BOLT12 offer as a chip in the profile payment rail. Offers were already payable, but only through a small button in the profile header. - Removes both header wallet buttons. The rail lists everything they reached. - Shows the zap bolt to recipients who publish an offer and no lightning address. The send path already paid them; the picker just never offered it. - Falls back to a BOLT11 zap when a wallet refuses an offer and the recipient also publishes a lightning address. - Retries only refusals that mean nothing was attempted. A `PAYMENT_FAILED` can still settle later, so retrying it could pay twice. - Reports a timeout when a wallet never answers, instead of hanging the zap. - Opens the zapped note when you tap a zap in wallet history. ## Performance - Stops emptying the media connection pool on every client rebuild. Both HTTP clients share one pool, so the proxy check misread every rebuild as a route change. - Drops pooled connections once per real Tor route change instead. - Stops `debugState()` walking the whole cache on every backgrounding. Its nine scans were built eagerly and then discarded unread. - Signs one Blossom read-auth token per server, not one per racing caller. ## Improvements and Bug fixes - Sends the Blossom authorization token as standard padded Base64 again. Deployed servers reject 1.15.0's unpadded base64url, so roughly two uploads in three failed. - Points the Sovbit default Blossom server at `files.sovbit.host`. - Answers a relay's WebSocket CLOSE frame, in both the shared and the Android socket. Without it a closed socket looked connected for up to four minutes, silently discarding everything sent. - Counts connected relays from the pool's connected flow, which the pool now clears itself whenever it drops a relay. - Renders a GIF with no `imeta` as a loading placeholder. It used to collapse the whole note to a gap in the feed until the fetch finished. - Drains the event-sync outbox before closing the client. Events from the final page were reported Done and never delivered. - Fixes 34 bugs found auditing `commons` and `commonsUI`. Among them: AES-GCM initialised with a spec every JDK rejects; an `Onion-Location` header from any host re-pointing Tor traffic for 24 hours; a bundled insert wedged after one exception; a URL validator backtracking exponentially per keystroke. - Uses `https` in preview data and image-URL placeholders. ## Desktop - Stops a silent account wipe on macOS. One Deny on the Keychain prompt rotated the encryption key and orphaned every saved account. - Consolidates the keychain into a single `vault-v1` item, so a cold boot prompts once. - Allows first-launch key bootstrap on Linux and Windows. ## Cli - Ships ~40 MB lighter, now that `amy` no longer drags Compose UI and Skiko behind `commons`. - Fails fast when a buzz-agent wrapper cannot be made executable. ## Quartz - Answers a duplicate `EVENT` with `OK true`, per NIP-01, and a superseded replaceable the same way. Clients used to get raw SQLite text they could not classify, and retried forever. - Gives each socket adapter its own session, removing the pool's locking and identity checks. ## Build & Documentation - Updates Arti 2.3.0 → 2.6.0, the JNI crate to 0.22, the NDK to r30 and Rust to 1.98.1. Picks up TROVE-2026-24 and TROVE-2026-27, both reachable in normal use. - Splits Compose UI out of `commons` into a new `commonsUI` module. 236 files moved and no consumer import changed. - Bumps LightCompressor-enhanced to 2.2.3. - Drops the unused Guardian Project Maven repository. - Runs relay-backed tests against geode instead of external relays. - Clears the compiler, Android Lint and Gradle 10 deprecation warnings. - Bumps `appCode` to 461. ## Contributors - @Vitor Pamplona - @⚡ Dee Kay ⚡ - mstrofnone ## Translations - Polish by @Polak - Hindi by @राज - Hungarian by @summoner

Replies (7)

@Amethyst that matches it from the query side. a relay that drops the connection answers in under a second with zero events and no error, which reads the same as a healthy empty result, and counting how many relays closed the subscription is what caught it for me.
Roger's avatar
Roger 1 week ago
The BOLT12 fallback is the sharp edge in that list. An offer is not a payment promise, so the only safe signal for retrying over BOLT11 is that the invoice request itself came back with an error - once an invoice was issued the payload is on the wire and a wallet may settle it minutes later. Testing that path against an offer whose invoice request fails on an unsupported feature bit is cheaper than discovering it with a double payment.
↑