#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
- VitorPamplona (nprofile…vhl6)
- ⚡ Dee Kay ⚡ (nprofile…m3sx)
- mstrofnone
## Translations
- Polish by Polak (nprofile…8c53)
- Hindi by राज (nprofile…z0lu)
- Hungarian by summoner (nprofile…ns2l)
