#Amethyst v1.21.0: NApplets in Notes, drss.io Listening, WebXDC, and Pico 8
Highlights:
- **Runs napplets inside their notes**, on Android 11+ and on desktop, and
plays **webxdc apps** attached to posts in a sandbox with no network.
- **Hosts napplets and nSites on desktop** in a bundled Chromium, at parity
with Android.
- **Plays Pico-8** posted as `.p8.png` images, full screen, with
touch, keyboard and gamepad controls.
- **Listens with drss.io**: episodes resume where you stopped, a listen
queue, history, chapters, clips and speed, and drss lists that stay in sync.
- Adds a **Post Layout** setting: Indented, Full Width or Borderless notes.
- **Downloads every Home content type from your follows**, and adds forum
threads, calendar events, code snippets and git to Home.
- Offers **web of trust from a card on Home**, with the provider's relay
adopted in one step.
- Shows **HDR video in HDR** in the fullscreen viewer, and keeps HDR when
compressing with H.265.
- **Plays chess over Nostr** in every build: a Chess feed with your games
and the lobbies of your lists, draw offers, abandonment claims and end
screens, interoperable with Jester and nostr.blue.
- **Redesigns the poll composer** in New Post, and puts the drawing button in
every composer.
- Hardens napplets, nSites and webxdc after a security review: decrypting
needs its own grant, cookies and WebRTC stay out of every frame, and site
cards prefetch within a budget.
- Fixes memory leaks across view models, navigation and app-wide caches, and
uploads that failed with "Stream Closed".
## New Features
### Napplets, nSites and webxdc
- Runs a napplet in place inside its note: Run here, a bar with Open and
Stop, and the last frame left behind when it stops. The feed never prompts:
anything that would ask you is refused and offered as Open instead.
- Runs webxdc apps (`.xdc` attachments and kind-1063 files) from an app card
with Start, in their own storage per account and app, with no network, no
WebRTC and no access to your keys. Shows a progress bar while the archive
downloads, and opens a newer archive of the same app instead of the old one.
- Plays webxdc apps inside their notes, like napplets.
- Adds NAP-OUTBOX: napplets read and publish through people's own relays,
with bounded routing and consent per read or signed kind.
- Follows the current NIP-5D manifests and NAP contracts: single-artifact
manifests, verified icons and kind-aware storage. Napplets published with
the old path-aggregate manifest must be republished.
- Takes WebRTC away from napplets, and from Tor sites on Android.
- Closes the router's local-network and DNS leaks, keeps each site's cached
files to itself per account, and keeps a napplet's game saves when the app
is killed.
- Tells you when Android can't open an nSite.
- Adds NAP-FS: a napplet opens and saves files you pick in the system file
dialog, on Android and desktop, so apps like PhotoHop open. An app inside
a note never opens a picker, and repeated dismissals back off.
- Adds NAP-INC, so napplets like Relay Browser, Blobbi and Emoji Pack
Creator launch and talk to each other, scoped to the account they run as.
- Adds NAP-UPLOAD, which uploads through your Blossom servers, and
NAP-COMMON: profiles, follows, NIP-19 codes, and follow, react and report
under the same prompts as publishing.
- Adds NAP-INTENT: a napplet hands a task to another, and your favorites,
in their order, choose which app handles it.
- Names the capabilities Amethyst doesn't support on a napplet's card
instead of hiding Open.
- Delivers any file type a napplet loads (3D models, archives) except SVG,
HTML and XML, which would run with the napplet's grants.
- Keeps desktop favorites across restarts, and loads their manifests at
start on desktop as on Android.
- Hardens the hosts after a security review:
- Relay reads decrypt DMs, gift wraps and private lists only with a
decrypt grant, and never against a peer the app chose; only events with a
valid signature reach an app.
- `signEvent` needs the signer capability, which only websites hold.
- A declined prompt backs off before it can ask again, Block denies every
capability, and each app has one storage quota across its instances.
- Inline launches run the newest version of an app seen on the device, so
an old signed version can't run with today's grants.
- Cookies and WebRTC are taken from every frame an app makes, the in-app
browser follows the page's Tor route for WebRTC, and desktop turns off
Chromium's network prediction.
- `window.nostr` is offered only on https, .onion and the device itself, and
the file chooser hands pages only other apps' files and fresh captures.
- Site cards prefetch within one shared budget, only from public https
servers, and not when the media setting wouldn't load images.
- Deletes the unpacked webxdc apps opened longest ago once they pass 1 GB,
and caps a Concord channel's webxdc signals at 4 MB.
### Game Cartridges
- Adds a Play button to images that are fantasy-console carts (a 160x205
`.p8.png`), opening a full-screen player on Android and desktop.
- Reads only the first 24 bytes of a candidate image to detect a cart, through
the Tor-aware image client, and only when images may load.
- Plays with a sliding direction pad and O/X buttons that a thumb rolls
between, a haptic tick per press, keyboard and gamepad keys, and sound.
- Opens the game's pause menu on Back instead of closing the player, keeps
the screen on and hides the system bars in landscape.
- Keeps game saves when the app is killed, each cart's saves its own (by the
hash of the cart), and only the two latest games in memory.
- Stops re-encoding a PNG cart on upload, which destroyed the game in its
pixels.
### Chess
- Moves Chess into the navigation catalog as a feed, in release builds too:
listed under Feeds in the drawer and menus, and pinnable to the bottom bar.
- Shows one feed driven by the top-bar list: "Mine" is the games you started,
were challenged to or moved in; any other list is a lobby of its people's
games, and Global every open challenge.
- Plays like a chess site: tap or drag a piece, last-move and check
highlights, and the board always from your own side.
- Adds draw offers that ride on your next move, and a Claim win when the
opponent has been silent for three days.
- Replays only the validated main line, so a player can't rewrite earlier
moves, and refuses illegal moves.
- Shows end screens with the result and the score by player, move notes with
their board, and the games a user ended on their profile's Notes tab.
- Redesigns the new-game dialog and the NIP-64 game viewer.
### drss.io
- Draws drss.io records as cards: shows, episodes, folders and feed lists.
- Resumes a long-form episode from where drss.io says you stopped, and
marks episodes you have heard. Amethyst reads drss's listening progress
(kind 30078) but never writes it.
- Adds a Listen screen with the episodes you are part way through, then the
unfinished ones from your river, in one player that moves on by itself.
- Adds a listening History by day with a Feeds heard chart.
- Adds -15s/+30s, chapters from the show notes, per-feed speed, and clips
saved as NIP-84 highlights of the episode.
- Reads a drss folder or feed list as a feed, from the Home picker or "Read
as feed" on its card.
- Adds a drss lists organizer that sets the read and write lists, and renames,
moves, merges and deletes lists and folders, with list pictures.
- Lets a show's owner edit its settings and on-air status.
- Keeps drss's reader records, like playback progress, out of feeds.
### Post Layout
- Adds a Post Layout setting with three modes: Indented (the author picture
in a column beside the post, as before), Full Width (the author on top, the
post across the width) and Borderless (Full Width with images, videos,
Markdown media and link cards running out to the screen edges).
- Heads a full-width repost with both authors' rows beside their pictures.
### Home
- Requests every content type Home can show from your follows, so music,
podcasts, fundraisers and attestations no longer appear only after another
screen loaded them.
- Adds forum threads, calendar events, zap goals, code snippets, stream clips,
git repositories, issues and pull requests, publications and torrent
comments to Home, with new toggles in Settings › Home.
- Loads bursty catalog kinds (ebooks, magazines, music, podcasts, git repos)
on their own subscription so one album upload can't crowd notes out.
- Keeps git repository announcements off Home.
### Web of Trust
- Shows a circle card on Home to accounts that follow 10 or more people and
have no trust provider, and turns web of trust on from there.
- Sends your profile, follows, mutes and relay list to the provider before
sign-up, so the first scores start from your current graph.
- Adopts the provider's relay into your trusted, search, indexer and favorite
relays, and moves feeds set to Global onto it.
- Redesigns the card with animated rings, a typical circle size and an
Activate button; the provider stays a visible choice, and after Activate the
card explains that the first scores take about 10 minutes, then closes.
### Polls
- Redesigns the New Post poll composer on the group poll's design: options
drawn as the rows a voter taps, an empty row that takes the next option,
Enter and Tab to move between rows, and duplicates flagged.
- Takes @ mentions, :emoji: and image links in poll options.
- Gathers the settings in one card: Vote with zaps, Multiple answers, the
zap range and Close after.
### Publications and Payments
- Renders PressStr ebooks (kind 33953), magazines (34609) and magazine
issues (39731) as cards, opening PDFs in the in-app reader.
- Translates and crops kind-30041 publication sections like notes, with
Copy Translated.
- Shows a one-line "Lightning Invoice · 50 sats" or "LNURL" label where
previews are off, instead of a wall of bech32.
- Says when an invoice expires.
### Media
- Shows HLG and PQ video in HDR in the fullscreen viewer.
- Compresses video with LightCompressor-enhanced 2.3.0: HDR video stays
10-bit HDR with H.265 where the encoder can keep it, and is tone mapped to
proper SDR everywhere else. iPhone Dolby Vision recordings compress instead
of failing.
- Tells you under the H.265 switch whether a picked HDR video stays HDR or is
converted to SDR, and why.
- Names the picked video's own resolution on the Android quality slider.
- Stops the video encode when you cancel a post.
- Adds the drawing button to every composer: comments, public messages,
articles, listings, group DMs, edits and git issues.
## Improvements and Bug fixes
- Stops uploading to NIP-95, which is deprecated. NIP-95 files still display.
- Fixes uploads that failed with "Stream Closed" when OkHttp retried a
Blossom or NIP-96 upload over a dropped connection.
- Encrypts each file of a multi-file NIP-17 DM under its own key and nonce,
instead of crashing on the second file.
- Retries only the files that failed, reports errors uploaders return
silently, and leaves files removed mid-upload out of the result.
- Stops downloading translation models for blocked languages (Hebrew,
Indonesian and Norwegian slipped through), deletes the models of blocked
languages, and translates Filipino notes.
- Adds a Translation Models setting: Always, Wi-Fi only or Never.
- Fixes memory leaks: view models scoped to their item, eager collectors
added per recomposition, app-wide watchers pinning notes, chess polling
after leaving chess, and caches that only grew.
- Bounds each Concord channel's message buffer and prunes Concord channels
under memory pressure.
- Keeps other people's local relays (localhost, LAN, tailnet) out of the
dialer while keeping your own, and exempts only your own from Tor.
- Stops signer rejections from crashing the app, and handles a refusal in
each flow instead of dropping it.
- Doesn't crash when a gift wrap or seal opens to something that isn't an
event.
- Serializes a NIP-46 signer's reloads of the paired apps' relays.
- Dedupes Blossom servers so the media servers list can't crash.
- Shows zaps to the host of service-signed live streams in the chat.
- Closes the media viewer into the thumbnail of the media on screen.
- Keeps the emoji panel open while searching.
- Seeds the profile-zap notification card from the decrypt cache.
- Centers the hidden-note warning inside reposts.
- Deletes an addressable's current version even after a cleanup.
- Keeps a saved account saved across a transient login of it.
- Clears a screen's view models when navigation hands it new arguments.
- Switches tabs from the drawer's Navigate rows when the drawer stands in for
the bottom bar.
- Paints the theme background under navigation, so going back on a light
window no longer flashes dark.
- Makes Namecoin resolution errors selectable, and replaces the dead ElectrumX
default server.
- Offers member removal in Marmot groups to admins only.
- Fixes a crash when replacing one mention with another while editing a
post on Samsung Keyboard, and stops composers restarting the keyboard
session on every recomposition.
- Renders the descriptions of Shorts, Longs and Stories as rich text, with
mentions, clickable links and translation, and drops a description that
only repeats the title.
- Keeps a cashu token, invoice or NIP-19 link whole when Show More shortens
a long note, so its card renders before the note is expanded.
## Desktop
- Opens napplets and nSites in Chromium through JCEF, downloaded on first
use, each in its own window with the main window's macOS title bar.
- Puts a napplet window's toolbar in the macOS title bar row beside the
traffic lights, drops its Close button for the window's own, and opens
the shield's access sheet as a popover without a title bar.
- Fixes crashes on macOS when closing a napplet window and when clicking
while Chromium starts for the first time.
- Stops a focus loop with Chromium that overflowed the stack each time a
napplet inside a note was clicked.
- Runs napplets inside notes with off-screen Chromium.
- Plays videos the native engine can't decode, like WebM on macOS, in
Chromium, plus WebM and Ogg audio, through the same Tor-aware media relay.
- Plays GIFs of any length by decoding frames as they show.
- Filters OS notifications by the Notifications top-nav filter.
- Keeps each card's video its own when posts share one file.
- Opens links on Windows through the shell's own process.
- Reads relay sockets on virtual threads, so mass dials no longer queue
behind thread start.
- Starts logged in or with a new account through `AMETHYST_LOGIN` and
`AMETHYST_SIGNUP`, and adds local relays through `AMETHYST_LOCAL_RELAYS`,
for test runs.
- Loads feed videos far faster, most of all on slow hosts: a scroll no longer
starts every video it passes, the video you stop on gets the bandwidth while
it starts, and videos scrolled past stop downloading at once.
- Plays hour-long episodes whose index is several MB by fetching the index to
disk before the player opens the file, and tries a video natively once more
before giving it to Chromium.
- Plays AV1 videos in Chromium on Macs without AV1 hardware (M1 and M2),
which played only their sound under a blurred picture.
- Fixes a crash on macOS while playing videos: the app ships its own build of
the video library's macOS player, without the audio tap that crashed.
- Falls back through your other Blossom servers when one refuses an upload,
as Android does, and counts an upload as done when the server already
holds the exact file.
## Cli
- Backdates the seal and the wrap in `amy gift wrap`; `--no-backdate` opts
out.
- Sends Content-Digest when mirroring a release APK to Blossom.
- Adds `amy cordn migrate resume`, and `migrate export` stands the old home
down.
- Reads relay sockets on virtual threads.
- Adds `amy chess challenge|show|move|resign|play`, with draw offers and
claims, and a minimax bot to play the app end to end over Nostr.
## Quartz
- Moves to Quartz 2.13.0, which drops Jackson for Kite everywhere, adds
the drss.io (with kind-30078 listening progress), PressStr and NIP-5D
classes, and validates a chess game's main line.
- Moves cordn's protocol code to cordn-kmp 1.2.1, and Marmot's leave,
permissions, timeline, edit, poll and emoji rules to marmot-kmp 2.2.1.
- Plays carts with kiko-kmp 0.2.3, a new library for the fantasy-console
runtime, through its kiko-player and kiko-compose modules.
- Builds on crypto-kmp 2.1.0, mls-kmp 1.3.0 and quic-kmp 1.2.0.
## Build & Documentation
- Bumps `LightCompressor-enhanced` to 2.3.0.
- Bumps the Android Gradle Plugin to 9.5.0, Ktor to 3.6.1 and Lucene to
10.6.0.
- Builds every module and target with no compiler or Gradle warnings.
- Adds a desktop video timeline log (`-Damethyst.videoTimeline=true`) that
shows where a video's start time goes.
- Runs Android lint in the pre-push hook.
- Moves hardcoded strings from the shared UI, commons and desktop into
Compose resources, with plurals for counted strings.
- Makes a thrashing Kotlin daemon exit instead of grinding.
- Runs the pre-push build without parallel tasks, which ran the Kotlin
daemon out of memory.
- Disables the automatic Crowdin sync while the project is suspended.
## Contributors
- @Vitor Pamplona
- @⚡ Dee Kay ⚡
- @mstrofnone ⚡🥜
- @sandwich
- Kaio Rabelo
## Translations
- Czech, German, Swedish, and Portuguese by @⚡ Dee Kay ⚡
- Portuguese by Kaio Rabelo
- Dutch by @Bartus
- Hindi by @राज
- Hungarian by @summoner
- Polish by @Polak
- Slovenian by @StellarStoic
- Uzbek by fiddleway
Amethyst
amethyst@vitorpamplona.com
npub142gy...xrj0
Nostr Client for Android
#Amethyst v1.20.0: Desktop Self-Update, an Emoji Picker, Hand-Drawn Posts, and Developer Tools
Highlights:
- **Updates the desktop app by itself** from releases signed with
Amethyst's key over Nostr (NIP-82), and tells Homebrew, Flatpak and
read-only installs how to update instead.
- Adds an **emoji picker** to every composer: Unicode emoji with search and
skin tones, your favorites, and a section per custom emoji pack. It docks
where the keyboard would be.
- Lets you **draw a post by hand** and post it as a GIF that replays the
drawing.
- Gives **~250 new event kinds their own cards** instead of raw JSON or
ciphertext, and a readable fallback card for kinds no one knows yet.
- Adds a small **Developer Tools**: a NIP-19 converter, an event inspector, a kind
browser that samples real events, and a raw relay console.
- Opens a **profile card on mouse-over** of faces and names, with follow,
message and block.
- **Spell-checks** the desktop composers and prose fields.
- Creates **git repositories** from the app, and attaches pictures to git
issues.
- Signs in via NIP-46 with a **NIP-05 address** through the signer its domain
advertises, and shows NIP-46 `auth_url` pages instead of waiting silently.
- Brings **Marmot groups up to MDK v0.14.0** (White Noise): encrypted
polls, custom emoji, admin deletion and in-group reports.
- Parses relay messages with **Kite**, Quartz's own JSON engine, in about
half the time of the old path.
## New Features
### Emoji Picker and Reactions
- Adds an emoji picker to the note, comment, DM, public chat, Concord and
Nest composers: Unicode categories, name search, skin tones, your
reaction choices as Favorites, and one section per selected NIP-30 pack.
- Docks the picker as a panel at the bottom of the view that owns the
composer, keeps it open between picks, and closes it with Back, Esc, its
close button or when the keyboard comes up.
- Draws emoji in the system's color emoji font on desktop, which had no
way to type an emoji.
- Rebuilds the reaction choices screen around the same picker: chosen
emoji as removable chips on top, one search over Unicode names and pack
shortcodes, and a second tap to remove a choice.
- Saves reaction choices on every tap, signing one event for a burst of
taps, and drops the Save button.
- Keeps keycaps and flags whole, and finds a choice saved in another skin
tone, so a tap no longer adds a near-duplicate.
- Imports images and `.zip` archives into an emoji pack in one go, each
image named after its file, with a live grid of the uploads and a summary
of what was skipped and why.
- Explains an empty emoji pack and offers the import from there.
### Hand-Drawn Posts
- Adds a Draw button to the short-note composer: brush (with stylus
pressure), pencil, spray, eraser and mirror, eight inks and a pen size.
- Draws on plain paper, sticky notes, a notebook page, grid, dots, a
chalkboard, or your own picture, kept per account to draw on again.
- Previews the replay before posting, then posts a looping GIF of the
drawing being made, plus a PNG of the finished drawing.
- Suggests alt text with the on-device describer on the Play build.
### Event Cards
- Adds feed and thread cards for about 250 kinds that printed raw JSON,
ciphertext or nothing: DVM jobs, Buzz records, agents and workflows,
moderation, git CI, P2P orders, chess, drafts and many more.
- Shows encrypted and machine-only records as one quiet "Private" line with
their alt text.
- Draws kinds Quartz doesn't know as a card of their title, summary,
picture, people, links and tags, and keeps them reachable by link.
- Sends the author prose in those cards through the translatable rich text,
with links, mentions and custom emoji.
- Adds cards for Open Markets product collections, shipping options and
product reviews, and status badges and shipping costs on listings.
- Renders NIP-51 release artifact sets with their notes.
- Draws decentralized list items in three tiers, from raw tags to custom
views for GitHub accounts, books and food places, with maps for places.
- Shows a rated list item's title and cover in entity ratings.
- Turns 3D Simple Nostr Objects in place in the feed, and accepts objects
over 512 vertices.
### Developer Tools
- Adds a Developer Tools hub under Settings › App settings.
- Converts any NIP-19 code or hex to every other encoding and opens it.
- Inspects a pasted event, relay frame or nembed: recomputes the id,
verifies the signature and flags odd fields. "Inspect event" opens it
from a note's menu.
- Samples a kind's recent events from your relays and groups them by
client or shape, each with a real example's JSON.
- Opens a raw WebSocket console to one relay, with REQ, COUNT and EVENT
builders and AUTH behind a confirmation.
### Profiles and Hover Cards
- Opens a profile card after resting the mouse 250 ms on a face or name,
in feeds, threads, chats and notification galleries, with message,
follow, lists and the block/report menu.
- Shows the NIP-85 follower count on the card when a provider has one.
- Adds a Repos tab to profiles with git repositories.
- Redesigns the edit-nickname dialog around the person's avatar and name.
- Plays GIF banners as muted images on the profile and hover card.
### Git
- Creates NIP-34 repositories from the Git Repositories screen, with a live
preview, GRASP servers and an identifier check, as `ngit init` writes them.
- Shows the owner of an empty repository how to push the first commit.
- Attaches pictures and videos to a new git issue.
- Searches git repositories by author with `from:npub…`, `from:name.bit`
or a NIP-05, rewriting the name in place once it resolves.
- Drops the Git Repositories inline filter, which sat hidden under the top
bar, for the top bar's search.
### Remote Signers (NIP-46)
- Signs in with `name@domain` through the remote signer the domain's
`nostr.json` advertises, instead of a read-only account.
- Shows a signer's `auth_url` page with an Open button, and takes it down
when the request ends.
- Moves to the signer's relays when it asks (`switch_relays`), and tells
the signer on logout.
- As a signer, moves nostrconnect apps to your inbox relays, and answers a
pairing only once it listens on the app's relays.
- Reaches signer relays directly, like your own relays, instead of waiting
for Tor.
### Marmot Groups
- Adds encrypted polls to Marmot groups: a new poll sheet with @ mentions
and `:emoji:`, votes cast inside the group with Change vote, and tallies
counted the way MDK counts them.
- Sends and shows custom emoji in group messages, edits, polls and
reactions as encrypted attachments, never fetching an emoji's public URL,
and suggests them as you type `:` in the group composer.
- Lets group admins delete a message for everyone (kind 4891), and reports
a group message inside the group, where an admin can dismiss the reports.
- Opens each attachment with the media key of the epoch that carried it,
and sends an upload in the epoch that encrypted it, so a commit during an
upload no longer leaves members unable to open it.
- Uses the current group-avatar scheme, publishes new members' commits to
the group's relays, and keeps KeyPackages confirmed and fresh.
- Keeps deleted group messages gone after a restart and out of
notifications.
- Stops showing votes, reports and labels as chat bubbles, and shows
`<t:…>` timestamps in your locale.
### Accent Colors and Synced Settings
- Lets each account pick its own accent color under Account Settings, and
keeps the application color for the login screen and the rest.
- Themes account-bound screens (signer consent, calls, nests) with their
account's color.
- Syncs more account settings across devices through NIP-78: home tabs,
feed filters, custom feeds, chat view modes, relay AUTH policy, upload
defaults and calls.
### Notes and Feeds
- Adds Show Less to expanded notes, pinned in the author column on notes
taller than the screen.
- Adds a back-to-top button to every feed screen and the profile.
- Makes Share a submenu of the 3-dot menu, and the reaction row's Share
button a dropdown.
- Shares a note as an image with its pictures, link previews and a still of
its videos, and waits for them to load.
- Previews three relays of a relay-list card and opens long lists, up to
thousands of relays, in their own screen.
- Shows each listed relay's icon, name and badges, and adds it to one of
your relay lists in one tap.
- Folds a Concord community's actions into one menu and its description
into the top bar's panel.
- Opens cordn.net share links and QR codes, and folds Cordn's header
actions into one menu.
- Clears cookies and site data for all sites from the browser menu.
## Performance
- Parses relay messages with Kite, Quartz's hand-written JSON engine: about
half the time of the Jackson path with ~40% less allocation, and 30%
faster again in Quartz 2.2.0.
- Interns an event's strings when the cache keeps it, not while parsing:
~60% of the parse, paid even by duplicates.
- Parses a zap receipt's embedded request only when it is read.
- Shares one object per relay URL: re-reading an 8,464-relay list went from
33-150 ms to 6-8 ms.
- Stops `fetchAsFlow` and `subscribeAsFlow` copying the whole list per
event, ~12.5M copies for a 5,000-event answer.
- Prunes muted authors in one pass per store, instead of one scan per mute.
- Stops verifying new kind 0 and kind 5 events twice, and rebuilding the
notification cards twice per deletion.
- Lets galleries wait for their first image's shape instead of polling it
five times a second.
- Sizes Android's warm video players by RAM and prefetches the first
512 KiB of the videos below the screen, releasing them under memory
pressure.
- Starts the watched video first on desktop media feeds: a cold Shorts open
went from 8-13 s to 4-5 s on a 4 MB/s link.
- Stops reading a link preview's page at the end of its head: a GitHub repo
page reads ~34 KB instead of ~495 KB.
## Improvements and Bug fixes
- Loads a single relay picked in a feed's top bar, which sent no request in
25 feeds (Pictures, Shorts, Videos, Music, Polls and more).
- Trusts the relays you favorited as feeds, which stayed unconnected.
- Fixes a second profile, community, repository or follow pack on the same
relay getting only the first one's recent history.
- Fixes an older version of an article, wiki or listing replacing a newer
one, and an old deletion removing the current version.
- Keeps a deletion that lands during a feed update from coming back.
- Watches for newer versions of on-screen articles and lists.
- Checks an NWC response's signature before it uses up the pending payment.
- Validates nSite path hashes, rumor ids, NIP-28 channel metadata, private
zap targets and nutzap amounts from other people.
- Asks before acting on `nostrconnect://` and relay-group invite links.
- Stops a malformed lightning address crashing the app on OkHttp's thread.
- Bounds nembed inflation to 256 KiB and parses `lnurlp` URLs without a
backtracking regex.
- Keeps pinned DM rooms on top when their newest entry is a draft.
- Keeps bare URLs whole in markdown notes, so `_SL1499_.jpg` images load.
- Shows anamorphic videos at their display shape, and uploads them that way.
- Keeps glyph icons at their weight under Android's Bold text.
- Stops the notification service crashing on start with
`ForegroundServiceDidNotStartInTimeException`.
- Fixes the scheduled-posts list crashing, and drains scheduled posts
through the shared publisher with its signature checks.
- Says when relays are waiting for a Tor that is not coming up: the
drawer's Tor icon turns red after a minute, and relay lists mark onion
badges "Waiting for Tor".
- Lets DM and untrusted relays connect after "Continue without Tor" on
Android, which still waited for Tor.
- Sends reactions, deletions, poll votes and reports on a Marmot or Concord
message through its room. Voting on a group poll from its notification
used to sign a public event.
- Shows edited text in Concord previews, and asks before "Delete for
everyone".
- Gives freshly logged-in accounts their Cashu counter store, which failed
until a restart.
- Fixes a nostrconnect login hang on Tor, a cancelled nutzap shown as a mint
failure, and a cancelled Marmot reset left half done.
- Fixes a crash while typing a coordinator pubkey in Cordn and a crash from
duplicate Concord list entries.
- Keeps a restarted audio-room publisher's audio after its broadcast ends.
- Fixes follow toggles answering for the previous hashtag, geohash or relay.
- Finds UI Preferences when searching for its options.
- Reads "What should we call you?" on sign-up.
## Desktop
- Updates itself from GitHub Release installs (DMG, MSI, zip, AppImage,
tarball, deb/rpm), checking each file's SHA-256 against the signed
release, and shows the release notes. Settings › App › Updates controls it.
- Draws OpenStreetMap maps for places, locations, geocaches and the
geocaches Map tab.
- Opens `nostr:` and `amethyst:` links, routing them to a running copy.
- Mirrors the drawer in the macOS menu bar, with a submenu per settings
category and a Window menu.
- Draws at the desktop's fractional scale on X11 (125%, 150%).
- Downscales images with mipmaps, so avatars are no longer blocky, and
decodes large JPEGs at a fraction of their size.
- Plays audio-only media (music, podcasts, voice notes) on macOS and
Windows.
- Gives each on-screen video card its own player, keeps videos in a 2 GB
disk cache, shapes Shorts before they play, and replays ended videos.
- Pauses a feed video on leaving its screen unless you chose to watch it.
- Folds the notifications panel into a strip with a missed-count badge.
- Shares notes as images to the clipboard.
- Offers a QR code and Copy for a payment no installed wallet can open.
- Scans QR codes from a file or the clipboard, posts calendar reminders and
refreshes the Web of Trust.
- Runs a second copy beside the first with its own Tor and caches.
- Mines proof of work with all cores but two, and shows the live hash rate.
- Stops GStreamer's segfault trap from closing the app on Linux.
- Fixes the reactions grid crashing on scroll, and dark-mode white columns
in the deck and chat panes.
## Cli
- Adds `amy releases publish`, which signs the desktop, Zapstore (APK) and
Orchard (Flatpak) NIP-82 events in one run.
- Adds `amy emoji` for NIP-30 packs and bulk imports from a folder, `.zip`
or list.
- Adds `amy publish --file` and `amy fetch --out` to move a corpus between
relays.
- Resumes `amy fetch --paginate` after a dropped connection.
- **Breaking:** `amy fetch --paginate` exits 1 with `incomplete` on a
partial walk, and reports silent relays as `timeout`.
- Logs in with `amy login name@domain` through the domain's signer, and
sends `switch_relays` and `logout` from bunker accounts.
- Saves a bunker account under the key its signer signs for.
- Speaks MDK v0.14.0 in `amy marmot`: `message delete` removes another
member's message as an admin, `group download-image` is new, `group
set-image` accepts `--mime`, and media sends stay in their epoch.
- Writes `--json` output and `~/.amy` files with kotlinx, byte for byte as
before.
## Quartz
- Moves to its own repository, vitorpamplona/quartz, published to Maven
Central. Crypto now comes from crypto-kmp, QUIC from quic-kmp, MLS from
mls-kmp, and the Marmot and cordn group managers from marmot-kmp and
cordn-kmp.
- Adds Kite (`JsonEngine.KITE`), a hand-written reader and writer for every
Nostr message, byte-identical to Jackson's output.
- Adds `publishStream` for windowed bulk publishing, `keepConnected`, and
`fetchAllPagesResuming`.
- Replays a fresh connection's subscriptions before anything else goes out,
keeps the messages a relay sent before closing, and redials when the
backoff ends.
- Fixes lost listener registrations under concurrency, the pool dropping a
relay under an in-flight EVENT, and `fetchAsFlow` never ending on a
refusal.
- Adds Open Markets collections, shipping options and reviews, and
OpenAgents capability and package events.
- Picks full or midstate SHA-256 per event for NIP-13 mining.
- Rejects malformed ids, keys and kinds in `naddr`, `nevent` and NIP-10
tags, and keeps NIP-11 documents with kind ranges or large fees.
## Geode
- Answers an oversized EVENT with `OK false` instead of only a NOTICE.
- Stops a negative filter limit from reading the whole store.
- Fixes a blank search narrowing a filter, and a `tagsAll` `#d` deleting
every row.
## Build & Documentation
- Removes Jackson from the app modules, which moved to kotlinx.
- Runs the CI lint job's KMP metadata compiles before pushing to main.
- Runs each JVM suite once in CI and starts every job at once.
- Fits a pre-push build in 16 GB of RAM.
- Adds a Flathub manifest that builds Amethyst from source, and a Linux
screenshot in the metainfo.
- Moves the release macOS legs to macos-15 and pins the JetBrains Runtime.
- Generates the changelog's contributors with `scripts/contributors.sh`.
- Moves to marmot-kmp 2.0.0 and mls-kmp 1.2.0.
- Bumps Gradle, Kotlin, Compose Multiplatform and dependencies.
- Bumps `appCode` to 465.
## Contributors
- @Vitor Pamplona
- @⚡ Dee Kay ⚡
- @mstrofnone ⚡🥜
## Translations
- Czech, German, Swedish, and Brazilian Portuguese by @⚡ Dee Kay ⚡
- Hindi by @राज
- Hungarian by @summoner
- Polish by @Polak
- Slovenian by @StellarStoic
- Uzbek by fiddleway
#Amethyst v1.19.0: A New Login, Remote Signers on Android, Wayland, and Marketplace Cards
Highlights:
- Redesigns the **login and sign-up screens** around signers: your signer
app or a remote signer first, then the key field, on every platform.
- Adds **NIP-46 remote signer login on Android**, and makes it work with
Opal and other signers that percent-encode their bunker addresses.
- Opens **gift wraps in parallel** for remote-signer accounts: a cold start
that waited ~85s using a remote signer for DMs now takes ~20s.
- Gives the **desktop first-class key storage**: a status card in Key
Backup, a password file when there is no system keyring, and an opt-out.
- Draws **natively on Wayland** in every Linux release, with the
compositor's own title bar where it has one.
- **Compresses videos on desktop** through the system's ffmpeg, with the
same quality table as Android.
- Honors **relay rate limits**: a relay's `rate-limited:` back-off now holds
and re-sends the refused commands instead of losing them.
- Adds cards for **NIP-15 marketplaces**, NIP-85 trust assertions, Trusted
Lists, decentralized lists and NIP-29 group metadata.
## New Features
### Login and Sign-up
- Redesigns the login, sign-up and remote-signer screens on one shared frame:
the logo over a soft glow, rounded fields and buttons, Tor settings as a
chip, and a brand-and-card split on windows 840 dp and wider.
- Shows the same order everywhere: the signer app where one is installed,
the remote signer wherever it works, then "or", then the key field.
- Names and draws the installed signer app ("Login with Amber" and its
icon), or says "a signer app" when there are several.
- Lights up Login once a key is typed, and Create Account once there is a
name, and puts the terms checkbox at the top, gating every way in.
- Puts each error right under what it is about: the key field, the password
field, the signer app's button or the remote signer's button.
- Fits the logged-off screens and the remote-signer dialog on small phones,
and keeps the Add New Account bar above the form, not over it.
- Uses white and frosted cards instead of grey for the login, remote-signer,
key-storage and Key Backup surfaces.
### Remote Signers (NIP-46)
- Logs in with a NIP-46 remote signer on Android too: paste a `bunker://`
or show the `nostrconnect://` code.
- Hands the code to a signer app with Open signer, from an in-app menu when
several take it, never to Amethyst itself.
- Shows the code in a large frame ringed by the logo's colors while it
waits, with Copy, Open signer and the relays as pills.
- Lets you pick the relay your signer uses, or one of your inbox relays,
when it can't reach the defaults.
- Uses relay.nip46.com, nrs.primal.net and bucket.coracle.social, the relays
Amber and Opal default to, instead of relay.nsec.app, which stopped
answering.
- Reads percent-encoded bunker addresses, as Opal writes them, and fails
with an error instead of spinning forever.
- Accepts only an answer that carries the code's secret. Anyone who saw the
code could reply first and pose as the signer.
- Resends the same request on a retry, so a signer that queues requests for
approval (Amber) no longer asks twice.
- Waits five minutes for the signer instead of two, and introduces each
platform by its own name and icon.
- Opens gift wraps, seals and drafts up to 8 at a time ahead of the indexer.
With Amber over NIP-46, a desktop cold start opened its 118 wraps in
~20 s instead of ~85 s.
- Tries remote-signer accounts last when decrypting push notifications, so
an offline bunker no longer holds every other account's notifications.
### Desktop Key Storage
- Shows where your keys live in a status card at the top of Key Backup,
with Try again when the keychain refuses and Unlock when the key file is
locked.
- Keeps keys in a password-protected NIP-49 file (`keys.ncryptsec`) when the
computer has no system keyring, asking for the password once per launch.
- Adds "Remember my key on this device": turn it off and the key is asked
for at each launch.
- Adds Save keys to take back this run's "Don't save".
- Stops rewriting the private key to the macOS Keychain on every settings
save, which brought the Keychain password prompt back over and over.
- Fixes view-only, bunker and signer-app logins failing to load after a
restart on Windows and Linux.
- Fixes the Linux OS keyring in release builds (deb, rpm, AppImage and
Flatpak), which always fell back to the password file.
- Treats KWallet's missing item as absent, so a fresh KWallet no longer
shows "Keychain error" on every launch.
- Upgrades a read-only session when you Unlock, Try again or log in again
with the nsec.
- Reports a key the vault could not keep once per run, instead of on every
settings save.
- Keeps the `nostrconnect://` secret out of the logs.
### Desktop Video Compression
- Compresses videos through the system's ffmpeg at the quality slider's
level, with Android's resolution and bitrate table, HEVC when asked, and
H.264 otherwise.
- Strips video metadata by remuxing the original when it is the one going
up, and asks before uploading if the strip fails.
- Names the picked video and its target on the slider, such as
"High · 4K video → 2160p, 8 Mbps".
- Hides the H.265 and GIF-to-MP4 switches without ffmpeg, showing an
install hint instead.
- Tells you when an encode was discarded for not being smaller.
- Finds Homebrew and other package-manager installs of ffmpeg when the app
is launched from Finder.
### Marketplaces (NIP-15)
- Adds cards for stalls, products, marketplaces, auctions, bids and bid
confirmations, which showed as raw JSON or not at all.
- Shows a product's gallery, price, stock, stall, specs and a Message
seller button that drafts a DM with the listing.
- Shows an auction's starting bid, its start and end with the merchant's
extensions, an Upcoming/Live/Ended badge, and its highest bid and bid
count.
- Reads marketplace events leniently (prices as strings, missing ids, tag
form auctions from hash21 and mkauction), and keeps other apps' records
on the same kinds out.
### Trust and List Cards
- Adds one card for NIP-85 trusted assertions (30382-30385): what it scores,
the rank, hops, every other metric as a tile, timestamps as dates, and the
provider.
- Shows a kind 10040's designated providers, with the services each serves.
- Adds cards for Trusted Lists (30392-30395) with members best first and
their scores.
- Adds cards for decentralized list headers and items (9998/9999,
39998/39999), loading a header's items while it is on screen, with `url`,
`tel` and `email` fields as links.
- Adds a card for NIP-29 group metadata (39000) that opens the group.
- Renders hashtags, links and people in bookmark lists and sets, and quotes
pinned notes instead of printing raw `nevent` strings.
- Pages long rosters (3, then 50 per Show more) and caps chip rows with a
"+N more" chip, since event authors decide how many tags a card gets.
### Feeds and Cards
- Adds an info button to each feed filter that explains what it shows, and
on the Notifications screen, what it does to notifications.
- Loads the relay lists of people-list members you don't follow, so list
feeds, follow packs and All Follows find their notes.
- Draws live streams with their summary, start time, viewer count, and a
Watch recording button over the cover once ended.
- Shows a classified's location and a Sold badge, a zap goal's end
countdown, a calendar event's own time zone, and a long-form article's
reading time and topics.
- Renders every kind as the thread's main note the way the feed does:
RSVPs, calendars, lists, chess, live activities and more drew nothing or
plain text there.
- Resolves NIP-30 custom emoji on reaction notification cards, and draws an
untagged `:shortcode:` reaction as text instead of an empty image.
- Plays Blossom's `.mpga` audio files and Podcasting 2.0 trailers.
- Draws a kind 31337 as a music track only when it has a file to play, and
leaves the rest (ad-skip data, game state) out of the feeds.
## Performance
- Makes the outbox's delivery state mutable under its lock: a publish cost
O(relays²) in copies, ~3,400 ns per relay at 100 relays, now ~150 ns.
- Makes a screen's subscription invalidation lock-free: 1.1 ns instead of
up to 8,300 ns while an update is running.
- Returns at once when pruning frees a note that is not a zap.
## Improvements and Bug fixes
- Shows a relay that refused a post with a back-off as retrying while the
client waits it out.
- Stops one relay's hang-up during a publish retry from reconnecting the
whole relay pool.
- Fixes acks from different relays being lost, which kept an event in the
outbox and re-sent it on every reconnect.
- Fixes a forged copy of a note getting the real one dropped as its
duplicate, and deleting an addressable's old version removing the
current one.
- Downloads the Web of Trust network in ~10 s and 39 MB instead of minutes
and 380 MB: it no longer fetches every deletion the provider signed, and
learns of retracted cards over NIP-77.
- Routes the Web of Trust provider's relays like your own: clearnet by
default instead of through Tor.
- Stops a Web of Trust download on mobile data when it is not allowed,
keeps it through list updates, and checkpoints it so an interruption
loses less.
- Fixes the zoom animation for images scrolled partly out of the feed or
under the bars, and makes GIFs shrink back into their thumbnail.
- Fixes tapping a quoted reply in a chat scrolling to the wrong message.
- Stops the like icon crossfading from an empty heart on every scroll.
- Stops demoting the threads of audio rooms and video playback, which made
audio stutter under load.
- Stops an audio room's service restarting after the app was killed and
holding audio focus and a wake lock for a room that was gone.
- Stops any app from controlling picture-in-picture video playback.
- Reads timestamps written in milliseconds as seconds, which made goals
and live streams end in "54,000 years".
- Keeps the Git Repositories search's `kind:git` chip out of the Namecoin
and `.bit` lookups.
- Gives a room's name width before its label in Messages, so a Cordn group
no longer shows only its coordinator.
- Follows the testls.space ElectrumX port swap for Namecoin.
## Desktop
- Draws natively on Wayland in every Linux package, with the JetBrains
Runtime. Set `AMETHYST_X11=1` to stay on XWayland.
- Uses the compositor's title bar on KDE, sway, Hyprland, COSMIC and niri;
GNOME keeps the GTK one.
- Fixes video playback on Windows, which said "Video playback unavailable"
in 1.18.0, and on macOS release builds.
- Keeps the chart library intact in release builds, which ProGuard broke.
- Lets the `.deb` install on Ubuntu 22.04, Debian 12 and their derivatives.
- Fixes `.deb` and `.rpm` installs and removals failing on systems without
`/usr/share/desktop-directories`, and upgrades that removed the menu
entry.
- Requires the system libraries and a font in the `.rpm`, so minimal Fedora
and openSUSE installs launch.
- Renames the Flatpak app ID to `com.vitorpamplona.amethyst.Amethyst`, so it
doesn't share the Android package's listing in Nostr app stores. See
`BUILDING.md` to move your v1.18.0 data.
- Fixes the Web of Trust download, which never started on desktop, and
keeps Cordn's group state on disk.
- Drops the image viewer's URL tooltip and click-to-copy; Copy URL stays in
its menu.
- Removes the Intel macOS options from the README: only Apple Silicon is
built.
## Cli
- Fixes `amy` 1.18.0's self-contained bundles crashing on every command with
`NoClassDefFoundError: java/lang/management/ManagementFactory`.
- Lists relays `amy fetch --paginate` could not page under `unpaged_relays`,
and warns that the result may be partial.
- Adds `retry_after_ms` to `rejected_by` entries.
- Fetches all of a search such as `include:spam` on search.brainstorm.world:
`amy fetch` returned 5,000 of 35,578 events, and `amy sync` none.
## Quartz
- Implements NIP-01 rate-limit back-offs (nostr-protocol/nips#2498):
`OkMessage` and `ClosedMessage` carry `params` and `retryAfterMs`.
- Holds a relay's refused commands on its connection (`RateLimitGate`) and
re-sends them when the back-off passes, across reconnects and pool
changes, with the subscription's current filters.
- Adds `RateLimiter` and `RateLimitPolicy` for relays, with
`PolicyResult.Rejected.rateLimited`.
- Pages a NIP-50 search with no words to rank, keeps `observer:` searches on
one page, and carries the search on `fetchByIds`.
- Re-asks a page cut short inside its boundary second instead of reporting
DRAINED.
- Fixes a decoder hang on short OK, CLOSED and NEG-ERR frames, a truncated
JSON object looping, and a field like `jE` replacing an event's `id`.
- Fixes `subscribeAsFlow` and `fetchAsFlow` dropping the newest list on a
burst.
- Fixes `QuicReader` overflowing on a peer-chosen length, and a bad datagram
escaping to the crash handler.
- Reads NIP-15 marketplace events leniently, with `isWellFormed()`,
`AuctionData.endsAt()` and `AuctionBidSummary`.
- Adds `AssertionMetric` readers, Trusted List accessors and D-list
`fieldDeclarations()`, `displayHints()` and `listImage()`, with `tel` and
`email` field types.
- Adds `TimeUtils.toUnixSeconds` for millisecond timestamps.
- `EmojiUrlTag.decode` returns null unless both shortcode and url are present.
## Geode
- Adds a `[rate_limits]` section for events and REQs per client per minute,
answering with the NIP-01 back-off and 429 with `Retry-After` over NIP-FE.
Off by default.
- Counts an IPv6 client by its /64.
- Caps WebSocket frames at 1 MiB by default; one header could cost 2 GB.
## Build & Documentation
- Fails the desktop release if ProGuard strips a JNI native method, a
D-Bus transport or leaves a stale jar signature.
- Fails the `amy` build when its runtime lacks a JDK module its jars use.
- Self-tests the OS keyring on every packaged desktop build in CI.
- Speeds up `.deb` post-processing from 156 s to 51 s.
- Splits the iOS CI jobs and caches the Kotlin/Native toolchain, so they
finish under their timeout.
- Adds `tools/desktop-run` to run and screenshot the shared UI headless, and
asks agents to look at a UI change before calling it done.
- Clears the build and lint warnings across modules, and removes 125 unused
Android strings and 10 unused drawables.
- Publishes the Flatpak to Orchard.
- States that issue bounties are not a promise of payment.
## Contributors
- @Vitor Pamplona
- @⚡ Dee Kay ⚡
- @mstrofnone ⚡🥜
## Translations
- Czech, German, Swedish, and Brazilian Portuguese by @⚡ Dee Kay ⚡
- Hindi by @राज
- Hungarian by @summoner
- Polish by @Polak
#Amethyst v1.18.0: Amethyst Desktop, Web of Trust, Privacy Lock, and a Deck
Highlights:
- Ships **Amethyst Desktop**: the desktop app now runs the same screens and navigation as Android, and the legacy desktop app is gone.
- Adds a **Web of Trust** network from your NIP-85 provider. Strangers' DMs go
to New Requests, and Curated notifications and replies leave them out.
- Adds a **Privacy lock** for the whole app, your messages or your wallet,
with biometrics on Android and a password on desktop.
- Adds **Custom feeds** built from people, hashtags, kinds and relays, and an
opt-in **Deck** of columns for wide windows.
- Rewrites **web-app signing prompts** to say what each request does, and
stops one stuck prompt from blocking every later signature.
- Lets you **drag the panes** in wide windows, and opens every chat type
beside its list.
- Redesigns **My Fitness** around weekly goals, with streaks, history and
charts.
- Makes the **Messages toggles** stop loading NIP-04 or NIP-17, instead of
only hiding them.
- Renders **NIP-82 software releases** with their app, release notes, and
downloads for your device first.
## New Features
### Privacy Lock
- Adds a Privacy lock settings screen with three switches, all off by default:
lock the entire app, lock messages, lock the wallet. Plus an idle timeout.
- Unlocks with biometrics or the device credential on Android, and with an
app password on desktop.
- Locks you out for 30 s after five wrong passwords, doubling up to
5 minutes, and keeps the lockout across restarts.
- Asks for the credential before a lock is turned off or its timeout made
longer.
- Hides message text in notifications while the messages or app lock is on,
and guards the secret-key backup behind any lock.
- Locks the Marmot and Cordn group screens, drafts, and the payment and mint
top-up screens too.
### Custom Feeds and Deck
- Adds custom feeds: pick people, hashtags, kinds and relays, minus hidden
people and keywords. They show in the Home feed picker under "Your feeds",
with a manage screen and an editor.
- Adds an opt-in Deck (Settings › App › Deck) for windows 1,200 dp and wider,
in place of the notification panel.
- Makes every Deck column any screen, with its own back stack. Columns can be
added, moved, resized, reset and closed, across up to nine workspaces.
- Adds Deck shortcuts on desktop: Ctrl/Cmd+T, +W, +Shift+PageUp/PageDown,
+Alt+1–9 and +Shift+S, plus a Deck menu on macOS.
- Imports the legacy desktop app's feeds, workspaces and lock settings.
### Web of Trust
- Downloads your NIP-85 rank provider's trust cards into a compact index on
the device, and keeps it current with small, cheap updates.
- Treats people below a minimum trust score as strangers. Their DMs go to New
Requests, Curated notifications drop them (in-app and push, zaps exempt),
and their thread replies start collapsed. Follows always pass.
- Adds a Web of Trust settings screen: the network's size over a histogram of
ranks, a minimum-score slider with a live count, and sync status.
- Sets up Brainstorm's GrapeRank with a guided sign-up, or any NIP-85 provider
by its key and relay.
- Reads rank and follower badges from the index, without a subscription per
profile.
- Shows on a profile whether the person is in your network, with their score
or "You follow them". Tapping it opens the Web of Trust settings.
- Collapses runs of public-chat messages from people outside your network
into one "3 messages from outside your network · Show" row, in NIP-28,
live-stream, ephemeral and relay-group chats.
- Orders people search, mentions and people lists by follows, then trust
score, then everyone else.
- Resumes a cut-off trust network download instead of starting over.
- Lets you copy another person's trust view, or enter a provider key with its
tags as chips, under "Set up by hand".
- Opens the provider's website in Amethyst's browser, signed in with this
account.
- Adds the Brainstorm relay to the default search and indexer relays.
### Signing
- Times out a web-app signing prompt after 120 s. A prompt left behind Home
used to block every later request from every web app.
- Brings a pending prompt back when Amethyst returns to the foreground.
- Shows what each request does: the private message inside a seal, the relay
of a relay login, the method and URL of a NIP-98 request, and the verb, file
and server of a Blossom upload or delete.
- Previews a profile edit as the new profile, with "Changes your bio ·
Removes your lightning address" lines.
- Summarizes follow, mute, deletion and report changes in one line, and shows
reports as report cards.
- Asks before signing a report under the Reasonable trust level, and asks
once, not twice, in Paranoid mode.
- Warns when a prompt has been open a minute or more that the app may have
stopped waiting.
- Fixes Connected Apps for web apps: Forget now forgets an app's decisions, a
new trust level takes effect, and an app no longer shows once per account.
A forgotten app could keep signing logins without a prompt.
- Only the buttons and Back decide a prompt now. A tap beside it no longer
denies silently.
- Removes the 40-requests-per-10-seconds limit on Amethyst's own NIP-46
signer for paired apps, and answers their requests in parallel, so busy
clients are no longer refused. Unpaired keys get 10 requests a minute each,
100 in total, and the rest are dropped unanswered.
### Messages
- Turns NIP-04 or NIP-17 loading, paging and subscriptions off when you turn
its toggle off. With NIP-17 off, its rooms, composer and calls are gone, and
the gift-wrap subscription asks only for what Marmot or Concord still need.
- Brings cached messages back when a protocol is turned on again.
- Pages NIP-17 history one round at a time. A quiet chat offers Keep looking
and Stop instead of walking every gift wrap you ever received, and names the
relays that didn't answer.
- Pages NIP-17 history from your DM relay list only, 500 gift wraps at a time.
- Pages history (messages, notifications, Cashu wallet) only for the account
on screen. With several accounts logged in, a NIP-17 group showed only its
last message.
- Keeps pinned conversations in the list when none of their messages is
loaded yet.
- Opens every chat type beside its list on wide screens: Buzz, Marmot, Cordn,
Concord, relay groups and location channels, like DMs.
- Softens the bubble colors: other people's bubbles are lighter in the light
theme, and your own keep the accent's hue without the neon glare.
- Checks the stock Concord relays for invites every 15 minutes instead of
keeping them open.
### Wide Windows
- Lets you drag the splitters between the drawer and the content, the content
and the notification panel, and a chat list and its chat. Widths are saved
per device.
- Opens a note's menu as a dropdown by its ⋮ button, with keyboard navigation
and submenus, instead of a dialog in the middle of the screen.
- Wraps the composer's tools under the text instead of scrolling them
sideways, with hover tooltips.
- Opens the account switcher as a menu from the docked drawer.
- Scrolls the feed with the mouse wheel over the empty gutters beside it.
- Adds a refresh button to the docked notification panel, and shows today's
counts and the 7-day chart in it.
- Opens the drawer from a tab pager only after a 120 dp swipe or a fast
flick, instead of on the first pixel, and keeps wheel and trackpad scrolls
with the pager.
- Puts Navigate before You in the drawer when there is no bottom bar.
### My Fitness
- Redesigns My Fitness around weekly goals: rings for active minutes, workouts
and active days, plus an optional weekly distance, each "on track" or
"behind pace".
- Adds a goal editor. The default is 150 active minutes a week, the WHO
guideline.
- Adds 12 weeks of weekly active time, a 18-week consistency heatmap with
streaks, your busiest days and times, and the activity mix against the
previous four weeks.
- Fetches your own published workouts, instead of only those another screen
had loaded.
- Stops counting fasting, diet and meditation as active time.
### Composer and Uploads
- Adds a custom-emoji picker to the note, comment, DM and public-chat
composers. It inserts your NIP-30 emoji as `:shortcode:`.
- Makes the image quality slider also set a maximum size: 640, 1280 or
1920 px, then uncompressed. Posts start at 1280 px on phones, now at JPEG
quality 85 instead of 75.
- Resizes photos on Android to exactly that size. A 12 MP photo used to go up
at about 2016 px whatever the setting.
- Previews the first image compressed in the upload dialog, with its size and
dimensions next to the original's. Tap it to compare both full screen at
their real resolution, with zoom; tap again to flip between them.
- Deletes the temporary copies a compression leaves behind when the quality
slider moves on before it finishes.
- Tags music uploads with their `format` (m4a, mp3, …), so other clients
know the `.mp4` Blossom returns is audio.
- Adds imeta (hash, size, type, dimensions, blurhash) to images pasted as
links in the note, comment, public message, classified and new group DM
composers, so readers can lay them out before they load. Links without an
image extension count when their preview is an image.
- Waits up to 3 s for a link just pasted before posting, so its imeta goes
out with the post.
- Fetches a pasted link once for its preview card and its imeta.
### Feeds and Cards
- Renders NIP-82 software releases with the app's icon and name, markdown
release notes and OS chips.
- Groups release downloads by OS, with the best match for this device on top,
and splits pre-releases from the latest release.
- Trusts a release only from the app's publisher or its listed maintainers.
- Opens Zapstore apps instead of an empty group chat. Only group content
follows its `h` tag to a NIP-29 group.
- Loads an app's releases on its own page, even when it is opened from a
link, profile or mention.
- Fills bare app cards with their OSes, license and links, renders app
descriptions like a note body with translation, and groups platform chips
by OS.
- Adds cards for Kanban boards and cards, WalletScrutiny verdicts and Mostro
instance terms.
- Adds a CI status badge and runs sheet, and cover notes, to NIP-34 git PRs,
patches and issues.
- Finds Marmot key packages of White Noise users who publish only the legacy
kind 443, so they can be invited.
- Shows the caption above the reactions on video and file cards.
- Plays an audio-only `.mp4` as audio, with its cover, by reading the file's
track list. Blossom serves every M4A upload as `.mp4` / `video/mp4`, so
these showed a black video box. A music track or podcast whose file is a
real music video plays as video instead of inside the waveform strip.
- Shows the cover, title and player of audio tracks, and the player of audio
headers that carry their file in a `url` tag.
## Performance
- Moves blocking and lock-touching work off `Dispatchers.Default`: external
signer calls, proof-of-work mining, relay health saves, cache scans and
cyberspace bag sweeps. One slow job no longer stalls feeds and relay
verification behind it.
## Improvements and Bug fixes
- Shows each relay's median post, query and first-result times, and a
"Slow N×" chip on a relay that lags the others.
- Hides the broadcast banner once your outbox relays accept a post, instead of
waiting for every relay. It can always be dismissed, and an outbox failure
stays with a Retry.
- Stops a dead outbox relay or a dropped connection from showing a post as
failed.
- Counts every connected relay under a job in the always-on notification, and
says "Connected to N relays" instead of "inbox relays".
- Asks before opening a scanned 64-character hex key as a profile, since it
might be a private key.
- Completes multi-part QR codes from the camera and from images, waits for a
tap when several codes are in view, and keeps zoom and torch when the camera
reopens.
- Fixes the wallet invoice QR being clipped in wide windows.
- Converts GIFs to MP4 in comment, public message, classified and edit
composers, which ignored the switch.
- Fixes uploads of keyboard GIFs and stickers failing with a permission error
after the upload dialog had been open a while.
- Makes text fields in embedded web apps behave as in the browser: Enter
sends, cleared fields stay cleared, chips, paste, maxlength, and Next on
forms.
- Fixes the image zoom animation popping to full size instead of growing from
the thumbnail with its rounded corners.
- Fixes a pasted `nevent` to a music track, or any addressable event, showing
twice in search.
- Shows a reaction notification for a reaction to your reply. It was checked
against the thread root's author.
- Shows nested NIP-22 replies in community feeds.
- Fixes zap splits without a relay being saved so that Amethyst and other
clients read them as weight 0. Old splits still load.
- Counts on-chain and BOLT12 zaps toward your zap income only when you are the
payee, and attaches a BOLT12 zap only to the live activity it names.
- Fixes hashtag feeds asking only the first relay.
- Shows "Messages" in the Messages top bar instead of the Amethyst logo.
- Fixes weekday labels on the notification chart after a daylight-saving
change.
- Opens scanned links written in uppercase, and keeps a split nsec in an
image from being listed piece by piece.
- Reports an LNURL server's error as "service unavailable" with its message.
- Stops a bare word in a relay hint turning into a fake relay.
- Fixes a StackOverflowError on reply cycles in threads, and a link detector
that took 27 s on colon-heavy text.
- Fixes a second reaction with the same emoji missing from the reaction
gallery.
- Keeps a feed's subscription when the app comes back right at the end of its
background grace period.
- Opens a chat when you tap its name next to a community label.
- Fixes a crash on videos with side-loaded captions. They now show as
subtitles, and formats no parser reads are left out.
- Fixes padding under the disappearing bars on My Fitness.
- Fixes the call timer showing blank in Polish, and the web-app profile-edit
prompt not naming the changed fields in Polish.
- Shows errors with a severity icon, a selectable message and the stack trace
behind Details. Messages queue instead of replacing each other.
- Moves the navigation to Navigation 3. Tabs keep their scroll position, and
pinned web apps no longer share one saved screen.
## Desktop
- Runs the shared app: the same screens, navigation shell, login, account
switcher and settings as Android. The legacy desktop app is deleted, and its
accounts, feeds, deck and lock settings are imported.
- Gains what the legacy app lacked: zaps and the reactions row, chess, Git
repositories, calendar RSVPs, post editing, markdown with inline media,
Cordn groups, the audio-room list, and crash reports offered on the next
start.
- Drops the hashtag-spam filter, the WoT badge, the live-now bar, the relay
dashboard, the advanced search and the GIF search.
- Adds a 2560 px step to the image quality slider, and starts posts at
1920 px.
- Keeps phone photos upright: compression applies the EXIF orientation, and
the metadata strip keeps that one tag. Photos stored sideways were uploaded
on their side.
- Strips metadata from photos with two EXIF blocks, as some editors save
them. These were uploaded unstripped, GPS location included.
- Keeps the space for images and GIFs without a declared size while they
load, and caches decoded GIF frames so a GIF shown again isn't downloaded or
decoded again.
- Fixes tall photos spilling over the text and reactions around them.
- Draws QR codes with the shared renderer. Long payloads fall back to a lower
error-correction level instead of crashing.
- Plays video like Android: autoplay muted, pause out of view, resume where it
stopped, Android-style controls and a now-playing bar. Encrypted media plays,
and Tor-routed videos stream through Tor.
- Renders music tracks, podcast episodes, audio tracks, audio headers, voice
messages and audio in posts, which drew nothing before, with a waveform
player that seeks on click. Podcast soundbites and chapters jump there.
- Plays audio linked in posts, including audio-only `.mp4`s, in the audio
player instead of the video engine ("Can't play this video").
- Fits portrait videos, preloads the next videos in a feed, and no longer
blinks to black when one starts.
- Zooms photos and videos from their card to full screen and back.
- Redesigns the now-playing bar with thin sliders and hours in long times, and
keeps its stop button in narrow windows.
- Renders PDFs in notes, with a full-window reader that turns pages with the
arrow keys.
- Shows OS notifications with the same title and text as Android, when
previews are on, and skips muted authors.
- Replaces the bare system error box with a crash window that can send the
report to the developers.
- Uses desktop text sizes, a lighter hover, and a menu bar only on macOS. The
title bar blends into the app on macOS.
- Sizes the drawer's text and rows for the desktop, and keeps the photo
viewer's close button clear of the macOS window buttons.
- Opens a notice instead of crashing on screens the desktop doesn't have yet,
and hides their entry points, such as DM calls and the geocache Map tab.
- Drops the media setting's Wi-Fi choice, where it behaved like Always.
- Keeps the top bar on Scheduled posts when there is no post store, instead
of a blank screen.
- Shows a text reply button on voice notes where recording isn't available.
- Brings a background window forward on the first click without pressing what
is under the pointer, and stops hover while scrolling.
- Fixes the pull-to-refresh indicator staying half-pulled after a mouse-wheel
scroll.
- Opens at a larger size and reopens where you left it, and never saves a
window collapsed to a sliver.
- Names the app "Amethyst" in Linux docks and app switchers.
- Only hands http, https and lightning links to the OS.
- Keeps accounts, drafts and settings in the Flatpak across restarts. Every
Flatpak shipped so far lost them.
- Renames the Flatpak app ID to `com.vitorpamplona.amethyst`, on runtime
26.08. Remove the old `com.vitorpamplona.amethyst.Desktop` bundle and log
in again (see `BUILDING.md`).
- Declares GStreamer as a dependency of the Linux `.deb` and `.rpm`.
## Cli
- Adds `amy notes show`, `thread`, `reply`, `quote`, `react` and `repost`,
plus `delete`, `notifications` and `notes feed --hashtag`, on the app's own
routing, tagging and thread code.
- Adds `amy trust setup`, `status`, `sync`, `check` and `copy` for the Web of
Trust network.
- Reports `relay_errors` from `amy fetch`, and fails with `no_relay_served`
when no relay served the request. It used to exit 0 with `count: 0`.
- Streams `amy fetch --paginate --limit 0` with flat memory, and logs every
REQ with `--verbose`.
- Runs every `amy sync` direction through negentropy.
- Answers AUTH in `amy fetch`.
- Refuses reposts and quotes of NIP-29 group content, and sends group content
only to its host relay.
- Shows a zap's sender only when the zap request proves it, so a spoofed
zapper no longer shows.
- Fails interaction verbs with `read_only` on npub-only accounts, filters
`amy notifications --type` at the relay, and applies deletions by address.
- Trims `notes post` text and rewrites bare npub/note words like the composer.
- Keeps amy's data files owner-only on Windows (NTFS ACLs), without eight
warnings per file.
## Quartz
- Makes `fetchAllPages` hold up on production relays: it learns each relay's
limit, drops repeated events, paces throttled relays, and walks each filter
on its own cursor. Multi-filter walks lost most of their events before.
- Fixes `negentropySync` finishing "clean" on relays that truncate their set,
and makes `fetchByIds` public.
- Returns null from `count` at once on relays without NIP-45.
- Fixes a WebSocket dial race that opened two connections per relay, and
REQs sent while a WebSocket was still opening being dropped silently.
- Breaking: `fetchAllPages` drops `DEFAULT_PAGE_SIZE`; `pageSize` is now an
optional cap. Adds `streamAllPagesFromPoolWithHooks` for walks too large to
hold in memory.
- Keeps a relay's CLOSED or connection message in `PagedFetchResult.message`,
and adds an `onRelayResult` callback to `fetchAllPagesFromPool`.
- Adds NIP-85 trust network sync and a compact index.
- Types the event kinds found by a census of 743 relays: Marmot 443, NIP-34
cover notes and NIP-C1 CI, Mostro, RoboSats, Ditto, Zapstore identity
proofs, Kanban, WalletScrutiny, NIP-4E key transfer, private note storage
and more.
- Completes hint providers on 184 kinds and NIP-50 indexing on 72. Existing
stores need `reindexFullTextSearch()`.
- Adds `RelayUrlNormalizer.normalizeHintOrNull`, a strict parser for relay
hints from other clients.
- Names every tag relationship with an accessor, such as `mentions()`,
`quotedEvents()` and `recipient()`.
- Writes the `a` and `f` tags NIP-82 releases require, and adds NIP-82 version
ordering.
- Verifies Zapstore identity proofs (kind 30509) against an APK signing
certificate (RSA, EC, Ed25519) on JVM and Android.
- Reads NIP-4E draft 10044 keys, which were always empty before, and adds
`EncryptionKeyStore`. Parses NIP-69 bonds as decimals.
- Registers `FeedDefinitionEvent` (31890) and `EncryptionKeyListEvent` (10044)
in `EventFactory`, and indexes an emoji pack's public shortcodes instead of
its encrypted content.
- Breaking: adds abstract `BaseRepostEvent.originalAuthorKeys()` and
`ZapReceiptEventInterface.zappedAddresses()`, and `DecoupledCipher` takes an
`EncryptionKeyStore`.
- Shares secp256k1 across every target and one native crypto set across
Apple and Linux, and implements `LargeSoftCache` on iOS.
- Adds a "One-shot Reads" section to `CLIENT.md` (`fetchAll`, `fetchFirst`,
`fetchAllPages`, `count`), switches it to the Maven artifact and the bundled
OkHttp transport, and ships an installable agent skill for projects that
use Quartz (`quartz/agent-skill/`).
- Adds `AudioTrackEvent.title()`, `url()`, `image()` (with `mediaUrl()`,
`coverUrl()`, `displayTitle()`), `AudioHeaderEvent.url()` / `mediaUrl()`,
and a `format` parameter to `MusicTrackEvent.edit`.
## Build & Documentation
- Continues One UI: Navigation 3 with the navigation shell in `commonsUI`,
more screens and ViewModels moved to `commons`/`commonsUI`, a headless
`BaseAccountViewModel` with typed user notices, and feature-based ViewModel
packages.
- Adds plans for iOS and web apps on the shared UI.
- Adds Navigation 3 1.1.2 and Apache PDFBox 3.0.8 (desktop only), both
Apache-2.0, and removes `navigation-compose`.
- Turns on incremental compilation for the KMP modules: a one-line change in
`commons` rebuilds in 9.5 s instead of 38.6 s.
- Builds zxing-cpp reproducibly from any NDK path, from a pinned commit.
- Requires a human to review the diff and test on a device before an AI
contribution becomes a PR.
- Gives Claude Code and Codex the same rules, skills and pre-push checks:
`AGENTS.md` holds the rules, `.agents/skills` links to `.claude/skills`, and
the checks live in `tools/agent-hooks/`. A new check, run before every push
and in CI, keeps them in sync.
- Cuts the agent skills from 33 to 14, each rewritten against the current
code, so the list fits Codex's skill budget.
- Fixes the Compose string-escaping pre-push check, which never blocked a
push.
- Documents Homebrew as autobumped, and verifies the cask instead of
submitting it.
- Bumps `appCode` to 463.
## Contributors
- @Vitor Pamplona
- @⚡ Dee Kay ⚡
## Translations
- Czech, German, Swedish, and Brazilian Portuguese by @⚡ Dee Kay ⚡
- Hindi by @राज
- Hungarian by @summoner
- Polish by @Polak
#Amethyst v1.17.0: Cordn, Geocaching, a New QR Scanner, and One UI
Highlights:
- Adds **Cordn** group chats: MLS groups over an MCP coordinator, with the
full chat experience, device migration and encrypted backups.
- Adds **Geocaching** (NIP-CC): a hub, a map, hunts, and QR-verified finds.
- Rebuilds the **QR scanner** on CameraX + zxing-cpp. It reads off-axis, blurry
and low-contrast codes the old one could not.
- Brings **Concord** up to spec: pins, Direct Invites, private channels with key
rotation, disappearing messages, kick and dissolve.
- Brings the **in-app browser** close to an installed Chrome PWA, with a new
omnibox and a search-engine setting.
- Shows **Ultra HDR** photos, plays **audio posts** as cards with
picture-in-picture, and shares what you listen to as **Now Playing**.
- Asks before another app's version of your **backed-up lists** drops items.
- Moves ~200 screens into shared UI on the way to **one UI** for Android and
Desktop.
## New Features
### Cordn
- Adds Cordn group chats to the Messages inbox, beside DMs, Marmot and Concord.
Messages never enter the event cache, so they are never searchable.
- Builds the chat on the shared components: bubbles, gestures, reactions,
quoted replies, day and unread dividers, and the rich text renderer.
- Adds replies, reactions, edits, deletions and pins, interoperable with the
reference client. Pins sit in a one-line carousel above the chat.
- Uses the shared composer: @-mentions, emoji shortcodes, GIF keyboard and paste.
- Adds encrypted attachments over Blossom and encrypted voice notes, with the
shared upload dialog and media viewer.
- Keeps drafts, unread counts and messages on disk, so rooms no longer open
empty after a relaunch.
- Redesigns New group as four steps: name, people, coordinator, and who can
add and remove. Shows how many of your people each coordinator can reach.
- Invites the whole roster in one commit, so one failed invite cannot fork the
group. People without a key package get a share link instead.
- Finds coordinators from their ContextVM announcements and names them by
profile, instead of asking for a 64-character key.
- Adds member management: add by name, npub or NIP-05, remove, rename, and admin
mode. Enforces admin rights like the reference client.
- Adds a metadata-exposure card explaining what each coordinator can learn.
- Shares groups as `cordn1…` links and QR codes, and adds an invitations screen
with Join and Decline.
- Adds join requests, coordinator health and management, and a key-package
screen that keeps your pool topped up.
- Adds device migration: the old phone shows a handoff code, the new phone scans
it and restores every group with its history. Keys are never in the code.
- Adds encrypted backup and restore of groups and their messages.
- Groups all Cordn settings under one entry, and adds Cordn Groups to the drawer
and the bottom-bar catalog.
### Geocaching (NIP-CC)
- Renders geocache listings and found logs as cards, with a photo hero, map
inset, rating, distance and badges.
- Adds a Geocaches hub (Nearby, Map, Hunts, Finds, Mine), cache details with
their log thread, and composers for caches, hunts and logs.
- Verifies a find by scanning the cache's QR code. The secret signs once and is
never stored.
- Hides hints behind a tap, and detects whether each was written in plaintext
or ROT13.
- Sorts Nearby by distance only when you ask.
- Opens caches and hunts from feeds, notifications, `naddr` links, QR scans and
search.
### QR Codes
- Rebuilds the scanner on CameraX + zxing-cpp, with autofocus, tap-to-focus,
pinch zoom and a torch. The old decoder read 31 of 48 test codes.
- Lets you pick when several codes are in frame or in a picture, and reassembles
multi-part codes.
- Reads codes from the gallery, the clipboard and the share sheet, and adds a
"Scan QR code" launcher shortcut.
- Explains a code it cannot open instead of closing, and keeps private keys off
the result screen and out of the logs.
- Keeps the screen bright while showing your own QR code.
### Concord
- Adds pinned messages, with a pin badge in the channel header and
jump-to-message.
- Adds Direct Invites: invite by npub, and accept or decline from the hub,
Notifications or Messages.
- Adds Public and Private community modes. A ban in a Private community rotates
the keys.
- Adds private channels, with Make private, Make public and Rotate key.
- Adds disappearing messages, with a timer from 1 day to 1 year.
- Adds cooperative Kick and community dissolution.
- Moves the Community List to the fragmented kind 33302, migrating the old list.
- Lets you edit your own thread replies and retract a reaction by tapping it
again.
- Shows the community description at the top of the channel list.
### Marmot
- Adds editing your own messages, compatible with White Noise edits.
- Detects a group this device has fallen out of sync with, and offers a Reset
that keeps history.
- Names unnamed groups after their members, and hides names on bubbles in
two-person groups, like DMs.
- Commits a member's leave automatically when you are an admin.
- Applies White Noise admin deletions.
- Adds Marmot Groups to the drawer, and makes the list read like the DM list.
- Warns when another device of yours is receiving your invites, and offers to
move them here.
### Buzz
- Brings Buzz to parity with upstream: artifacts, huddle liveness, team catalog,
projects and thread windows.
- Writes channel messages as kind 9, as Buzz's own clients do, and lets you edit
them.
- Labels agents as "Agent · managed by Alice", and tells apart members with the
same name.
### Browser
- Brings the in-app browser close to an installed Chrome PWA: new windows,
Tor-aware downloads, fullscreen, find in page, text size, desktop site and
crash recovery.
- Redraws every browser surface with one shared pill.
- Reworks the address field into an omnibox with paste-and-go and completions.
- Adds a search-engine setting: DuckDuckGo, Brave, Startpage, Mojeek, Qwant,
Ecosia, Kagi, Google or Bing.
- Adds per-site camera, microphone and location permissions, with "Only this
time".
- Adds "Add to Home screen". Shortcuts open in Amethyst and wait for Tor.
- Shows page alerts and prompts, which used to be silently cancelled.
- Replaces the WebView error page with a readable one, with Try again and Try
without Tor.
- Asks before every download, with the real name, size and source, and blocks
files pretending to be another type.
### Media
- Shows Ultra HDR photos in HDR, capped at 2x in the feed.
- Plays audio files in posts as a waveform or cover card, with
picture-in-picture.
- Shares what you are listening to as a NIP-38 music status, from Amethyst,
other apps or desktop players. Off by default.
- Adds captions to NIP-71 videos, with a CC toggle in the player.
- Shows video credits, and checks collaborators who accepted.
- Plays divine.video's adaptive HLS streams.
- Adds Cast on Android 17, and names the format a receiver refuses.
### Backups
- Asks before another app's version of your follows, mutes, relays, profile,
wallets or settings drops items you backed up.
- Shows a card on Home for each conflict, with one-tap Accept.
- Adds a review screen per conflict that lists every removed, added and changed
entry.
- Keeps open conflicts across restarts, and closes them when the other app puts
the items back.
### Key Backup
- Shows the nsec as a numbered, grouped grid you can copy onto paper. It hides
again when the app goes to the background.
- Adds a confirm-then-encrypt flow for the ncryptsec backup.
- Requires passwords of at least 12 characters for new ncryptsecs. Old ones
still open.
- Accepts hand-typed keys at login, with uppercase letters, spaces or dashes.
### Profiles
- Reads NIP-92 `imeta` for profile pictures and banners, trying the declared
fallback URLs when the main one fails.
- Shows the banner's blurhash while it loads.
- Publishes an `imeta` for freshly uploaded pictures and banners, on Android and
Desktop.
### Signing
- Shows a relay login (kind 22242) as a login card in signing prompts, naming
each relay exactly as signed. Odd-looking addresses get a "check before
signing" warning.
- Makes the event preview in signing prompts read-only, so you can no longer
react, zap, vote or RSVP on an event that does not exist yet.
### Notifications
- Shows DMs, Marmot groups and Buzz rooms as Android conversations, with
per-chat priority and launcher shortcuts.
- Shows inline-reply progress, with a Retry that keeps your text.
### Calendar
- Shows follows' "Going" RSVPs on Home, with a toggle in Settings.
- Adds a "Friends going" lens of upcoming events your list attends.
### Feeds and Lists
- Renders 3D Simple Nostr Objects, shards and cyberspace avatars, with a
full-screen viewer.
- Opens cyberspace region bags, showing how long the search will take.
- Renders every NIP-51 list kind as a card, instead of encrypted text.
- Adds favorite follow sets: star any follow set to use it as a feed.
- Shows prediction markets and ballots as their own cards, and hides kind-38000
spam.
- Shows Zapstore app curation sets, NIP-34 git status events and video curation
lists as cards.
- Shows a compact link preview for pages without an OpenGraph image.
- Shows follow-list petnames as a display-name fallback.
- Adds Cash App, Nano, Revolut, silent payments and BIP-353 payment targets.
### Messages
- Redesigns the Messages list: unread drives emphasis, and previews show @Name or
"Photo" instead of raw links.
- Redesigns the chat screen: softer bubbles, clock times and the avatar outside
the bubble.
- Shows sent messages immediately with a sending state, and retries a failed
send on tap.
- Plays voice messages and encrypted audio in the waveform player.
- Shows a DM rename as "Alice renamed the conversation".
### Uploads
- Falls back through your Blossom servers when an upload fails. The signer is
asked once.
- Shows why an upload failed, and lets you remove a failed attachment.
### Relays
- Adds an event allow list, roles and claims to relay management (NIP-86).
- Detects when a NIP-29 group has moved to another relay, and supports group
banners and `a` pins.
### Android
- Enables ARM Memory Tagging on supporting devices to catch native memory bugs.
## Performance
- Loads My Fitness progressively instead of behind a 2–3s spinner.
- Cuts the DEX from 31.1 MB to 29.9 MB by dropping kotlin-reflect.
- Confirms a Marmot group change at the first relay that accepts it, so creating
a group no longer waits for the slowest relay.
- Stops the Marmot key-package lookup shortly after the first package arrives, so
adding a member no longer takes most of a minute.
- Stops rewriting the whole Marmot message log and group state on every send.
- Removes a duplicate encryption on every Marmot send.
- Loads cached Blossom images straight from disk, also offline.
- Remembers missing Blossom files for 30s, so videos no longer stall 10s per
retry.
- Makes the audio visualizer smooth: 46 frames a second instead of 5.
- Speeds up the 3D renderer 4x and cuts its GC from 51% of a core to 7.7%.
- Speeds up the event cache's list observers 1.3–4x.
- Stops calendar screens rescanning the cache on every event.
- Stops the fullscreen image dialog recomposing on every frame of a swipe.
- Stops rebuilding Notifications and Messages on every Concord update.
## Improvements and Bug fixes
- Shows Tor status in the drawer. The icon hides when Tor is off and opens Tor
settings on tap.
- Fixes devices stuck on "Loading account" forever because they reject the
AES/GCM/PKCS7Padding cipher.
- Fixes encrypted stores that could write but not read back.
- Moves settings and account data from SharedPreferences to DataStore, and keys
off the deprecated androidx.security. Old files stay for downgrades.
- Fixes Tor never working on 32-bit ARM and x86 devices, which shipped without
the Tor library.
- Fixes notifications opening on a black screen after a cold start.
- Opens DM and private reply notifications in their conversation, under the
right account.
- Fixes kind-5 deletions matching any author: one user could hide another's
note.
- Decrypts private zaps for the zapped user, showing the real zapper.
- Fixes zap goals reading only 0% or 100%.
- Saves Cashu counters before using them, so a crash cannot lock funds at the
mint.
- Keeps proof-of-work posts mining in the background, and saves the draft first.
- Fetches your own profile and lists from inbox relays when you have no write
relays.
- Fits subscriptions under a relay's filter cap instead of losing the whole REQ.
- Keeps a subscription alive when a relay refuses one kind by name.
- Splits large profile requests, so relays that cap results do not leave authors
unnamed.
- Fixes quoted notes stuck on "Loading quoted note...".
- Sends calendar RSVPs to the host's and every invitee's inbox relays.
- Keeps the calendar's view and scroll when you come back to it, and stops
appointments flickering.
- Fixes Marmot groups forking on relay echoes, simultaneous commits, and
commits that leave you alone.
- Sends Marmot reactions and deletions inside the group, so White Noise sees
them.
- Fixes Concord messages being invisible in Accordion and other applesauce
clients.
- Fixes Concord joins: channels show at once, and failures are reported.
- Keeps a private channel's old keys, so history survives a rotation.
- Lets a banned Concord member return when re-invited after an unban.
- Fixes Direct Invites that never arrived.
- Fixes Buzz Desktop interop: uploads, mentions, profile, and relay refusal
reasons.
- Shows Buzz Join only to non-members and Leave only to members.
- Keeps browser pages on Tor whenever any surface uses Tor, and fails closed
when Tor cannot be applied.
- Keeps NIP-07 replies with the page that asked, and holds signing and payments
from tabs you are not looking at.
- Brings back black browser tabs and restores the page after Android reclaims
memory.
- Stops deleting a private DM from publishing a public NIP-09.
- Fixes NIP-49 rejecting some valid keys and crashing on very high scrypt costs.
- Plays videos from a local Blossom cache over Tor, and falls back to the server
when the cache is closed.
- Restores YouTube link previews.
- Stops the keyboard staying up after leaving a text field.
- Keeps the bottom bar on every pinnable tab.
- Returns a tablet rail tap to that tab's root.
- Makes all floating action buttons circular.
- Fixes amounts rounding wrongly, such as 12,500 showing as "12k".
- Fixes "100%%" in relay uptime labels.
- Fixes NWC notifications for wallets on the new NIP-47 extensions spec.
- Shows the bunker's error when a NIP-46 signer refuses.
- Encrypts every private relay entry, including ones other clients left in
plain text.
- Uses plurals for counts, so Czech and Polish read correctly.
- Fixes two release-only crashes on launch in the minified build.
## Desktop
- Stops offline users being stuck on "Connecting to Tor...": after 10s it offers
a regular connection.
- Reads now-playing from Linux MPRIS, macOS Spotify and Music, and Windows media
controls.
- Shows names and avatars for thread replies, and stops holding every note in
memory.
- Shows the nsec in the hand-copyable grid, and requires 12-character passwords.
- Uses the shared report dialog, which now also mutes the author.
- Renames the AppImage to `amethyst-desktop-<ver>-<arch>.AppImage`.
## Cli
- Adds `amy cordn` for refs, exposure, coordinators, key packages, groups,
invites, sending, `watch` and `migrate`.
- Adds `amy sno` to validate 3D objects and avatar proof of work.
- Adds `amy cyberspace`, which matches the reference cyberspace-cli.
- Adds `amy concord` pins, invites, private channels, kick, dissolve and timer.
- Adds `amy admin` commands for NIP-86 allow lists, roles and claims.
- Adds `amy relaygroup pin/unpin` and joins from invite links.
- Catches up correctly on Marmot groups after being offline.
- Accepts hand-copied keys in `amy login` and `amy key decrypt`.
## Quartz
- Adds ContextVM (MCP over Nostr) with all 12 CEPs, and the Cordn protocol.
- Moves the MLS engine to `quartz/mls/`, shared by Marmot and Cordn, with
several RFC 9420 interop fixes.
- Adds NIP-CC geocaching, Simple Nostr Objects, cyberspace region bags and
Tapestry Decentralized Lists.
- Adds NIP-FE relay commands over HTTP, with a client and a server.
- Adds NIP-67 EOSE completeness hints and a NIP-43 membership engine.
- Follows the latest NIP-29, NIP-47, NIP-51, NIP-86 and NIP-A3.
- Fixes NIP-44 padding above 16 MB and rejects non-canonical lengths.
- Fixes NIP-49 to check the AEAD tag and zero derived keys.
- Fixes NIP-88 rejecting every spec-conformant `polltype` tag.
- Fixes NIP-62 vanish requests for relays without a trailing slash.
- Stops page limits truncating NIP-77 negentropy reconciles.
- Answers a superseded replaceable with `OK false replaced:`.
- Indexes every natural-language tag on Decentralized Lists, so authors and
subjects become searchable, while skipping URLs and ids. Indexed text drops
from 105 MB to 75 MB on 1.5M events. Existing databases need
`reindexFullTextSearch()`.
- Makes local search match a searchable kind only by what it indexes, so local
and store search agree.
- Adds typed diffs between versions of replaceable events.
- Fixes many tag readers and bounds checks on hostile tags.
- Renames event classes to their NIP names, keeping deprecated aliases.
- Breaking: removes `ForkTag`. Use `experimental.forks.parseForkedAddress`.
- Fixes iOS ConcurrentModificationException crashes in LargeCache.
## Geode
- Serves NIP-FE relay commands over HTTP, with limits, CORS and NIP-98 sign-in.
- Adds NIP-43 membership: members-only writes, invites and roles.
## Build & Documentation
- Enables R8 obfuscation, and publishes mapping files with each release.
- Adds `scripts/retrace.sh` to deobfuscate a crash report in one command.
- Checks at release time that every reflected class survived R8.
- Builds Arti and zxing-cpp reproducibly for all four ABIs.
- Moves 1,906 strings to shared Compose resources.
- Moves the event cache, Account, AccountViewModel and most screens into
`commons`/`commonsUI`. 204 of 252 screens are now shared.
- Makes `./gradlew test` run every KMP module's JVM tests: 11,690 instead of
3,567.
- Updates AGP to 9.4.1, Gradle to 9.8.0, Compose Multiplatform to 1.12.1, Ktor
to 3.6.0, Coil to 3.6.3 and zoomable to 2.16.0.
- Runs Android Lint in its own CI job.
- Bumps `appCode` to 462.
## Contributors
- @Vitor Pamplona
- @⚡ Dee Kay ⚡
- @cloud fodder
- @Jameson Lopp
- atesfurkan
## Translations
- Czech, German, Swedish, and Brazilian Portuguese by @⚡ Dee Kay ⚡
- Polish by @Polak
- Hungarian by @summoner
- Hindi by @राज
- Russian by Anton Zhao
- Slovenian by @StellarStoic
#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
#Amethst v1.15.0: Marmot Updates, a Advanced Search Box, and Books, DVM Updates
Highlights:
- Rebuilds **Marmot** onto the adopted spec — Amethyst groups now interoperate
with White Noise, in both directions.
- Makes the **search box's tokens real filters**, locally and on relays, and puts
Android and Desktop on one pipeline.
- Reads **publications, wikis, citations, library records and ratings**.
- Adds **NIP-A3 payment targets** to the zap picker, and stops NWC failing in
silence.
- Shows which **DVMs are alive** before you pick one.
- Brings **NIP-53 live streaming** to Desktop — discover, watch and chat.
- Stops fresh installs **stranding on a Tor bootstrap**.
## New Features
### Marmot Groups Catch Up With the Spec
Marmot deprecated the MIP documents in July and MDK followed, which left our
implementation — shipped in v1.09.0 — no longer a valid Marmot client under
either profile the current spec defines. This release moves it onto the adopted
"current profile" and proves it against the reference.
- Implements the MLS extensions draft's `app_data_dictionary`, the account
identity proof v2 that makes a group classifiable at all, and the six group
components that replace the single packed `marmot_group_data` blob.
- Replaces the old same-epoch tie-break — lowest `created_at`, then event id,
both of which a sender picks — with the spec's convergence: candidate branches
derived by replaying MLS bytes, a bounded pass, and a six-step comparison over
authenticated values only.
- Enforces publish-before-apply. A local commit is prepared on a clone and
becomes canonical only once a relay in the group's own scope acknowledges it,
so a failed publish can no longer fork this client away from the group.
- Fixes the defects that made us unaddressable: no account could mint a
KeyPackage at all; KeyPackages were published as bare bytes instead of a framed
MLSMessage; a Commit rebuilt our own leaf from defaults, silently demoting the
group creator and making every invite we sent invisible; and a last-resort
KeyPackage was discarded after one Welcome, so only the first invite anyone
ever sent us worked.
- Fixes two MLS defects that meant nobody but a group's creator could invite into
a group of three or more: a joiner was never given its Welcome path secret, and
an UpdatePath was decrypted at our own leaf rather than at the node we hold a
key for.
- Makes a departure actually take effect. A leaver's `SelfRemove` was committed
and then dropped, so the group believed they were gone while they stayed in the
tree holding its keys.
- Adds group disbanding, with the whole Commit shape the spec fixes, and an
admin-only action behind its own confirmation.
- Sends attachments in the adopted encrypted-media v2 shape. Every image Amethyst
sent into a group was invisible to every other implementation; an admin can now
also switch a group over to encrypted attachments.
- Honours disappearing messages, pinned to the epoch that delivered each message
rather than to the current setting, with a retention picker at group creation.
- Renders message edits and group system rows, and draws a system row only when
this client derived it — a peer's assertion of "X removed Y" is no longer drawn
as history.
- Adds several members in one commit, the way MDK does, instead of one commit per
invitee.
- Adds a group avatar behind a plain https link, with the canonical URL rules the
component requires and an SSRF guard at the renderer.
- Adds push token gossip in the shape the spec adopted, with the kind-451 owner
proof, so relaying someone's record cannot repoint, re-sign or restamp it.
- Adds agent text stream previews over the repo's own QUIC stack, in both
directions, with certificate pinning instead of blind trust.
- Runs the reference implementation's own fixtures, its portable scenario
vectors, and a headless interop harness against MDK 0.9.21 — 29 of 29, both
directions, including media in both.
### Search
- Ports the vespa-relay search field's token language: `from:`/`to:`,
`since:`/`until:`, `#tag`, `label:`, `group:`, `kind:`, `lang:`, `domain:`,
NIP-73 scopes, `-exclusions` and quoted phrases, drawn as chips over ordinary
editable text.
- Runs them. Android handed the raw box — chips and all — to both the relay's
NIP-50 `search` and the local scan, so `from:npub1… bitcoin` asked relays for
the literal text of its own tokens and a `kind:` chip narrowed nothing.
- Puts both front ends on one `SearchPipeline`, so a caller cannot skip
post-filters or ranking. Android had never applied `-term` exclusions or the
reply/media pseudo-kinds, and Relevance sorted by date.
- Opens a picker under a half-written `kind:`, and gives the people picker the
composer's own — NIP-05 resolution, relay lookups and follow ranking included.
- Names what a chip stands for: a group's name instead of its opaque id, a city
instead of a geohash.
- Seeds the box with the filter of the screen it was opened from, across 17 more
feeds, and remembers what you searched for on Android.
- Says what it is waiting on: the spinner now turns while a relay still owes an
EOSE, and tapping it lists the relays outstanding.
- Answers with one kind window derived from Quartz instead of three hand-kept
lists — the audit found fifteen searchable, renderable kinds the search had
never asked for, pictures and every video kind among them.
- Hides deletion (kind 5) and vanish (kind 62) events from results.
### Reading: Publications, Library, Wiki and Ratings
- Gives a kind-30040 publication a table of contents you can read from, listing
sections by `a` **and** `e` tag and naming each row from the index alone.
- Renders kind-30041 sections: AsciiDoc converted to the same CommonMark renderer
long-form uses, with wikilinks resolved to nevents and naddrs.
- Gives a chapter a crumb back to its book and a pager through the book.
- Parses and renders the library kinds — 31/32/33 citations, 30045 directories,
30142 learning resources and 32176 Blossom piece indexes — and shows a learning
resource's attached PDF inline instead of as a blue link.
- Parses and renders the NIP-54 wiki kinds (818 merge requests, 819 acceptances,
30819 redirects) and kind-17 external reactions.
- Parses kind-34259 entity ratings and kind-31987 relay reviews, and rebuilds the
rating card around cover art and stars that actually fill.
- Renders kind 0 as a profile card, in the feed and in thread view, instead of
raw JSON.
- Links each Birdex species to its Wikidata entry.
### Payments and Zaps
- Offers a NIP-A3 pay-to hand-off in the zap picker, gated on an installed app
actually resolving the URI and wearing that app's own icon.
- Hides on-chain zap amounts the wallet cannot fund, checked against the exact
coin selection the send path uses.
- Names the payee on outgoing NWC transactions (NWC-06), and only claims a
binding for a zap request the provider accepted.
- Never lets a NIP-47 refusal or timeout reach you as silence — a wallet
answering `QUOTA_EXCEEDED` or `RESTRICTED` showed nothing at all.
- Stops sending optional NIP-47 params as `null`, which some wallets refuse, and
stops downgrading to NIP-04 encryption on a cold info cache.
- Adds a Block Relay button to the relay NOTIFY payment prompt, so a paid relay
asking on every reconnect can be answered once.
### DVM Liveness
- Adds DVM heartbeats (kind 11998), hides DVMs whose last beat is older than 15
minutes from Discover, and marks the rest with an offline dot or banner.
- Fetches beats from each DVM's own outbox relays, not only the discovery
selection, so a live DVM your relays do not carry stops looking dead.
### Relay Login
- Makes the prompt's "remember" switch mean the same thing for both answers. It
was read only by the Log in button, so refusing with it on wrote nothing and
the same dialog came back on the next reconnect.
- Relabels the buttons to the answer they give, and adds "Never, all relays"
opposite "Always, all relays", both behind one confirmation that names the
consequence.
- Stops an account-wide answer being swallowed when the confirmation outlives the
prompt's 60-second window, and answers the prompts queued behind it.
### Everything Else
- Shares your screen in a call, on Android.
- Opens a real file picker for `<input type="file">` — it was a silent no-op in
the in-app browser, in nSites and in napplets — and offers the camera where a
mobile browser would.
- Exposes NIP-44 encrypt and decrypt on the injected `window.nostr`, so a web app
logging in with Amethyst can seal a NIP-59 gift wrap.
- Emits the uppercase `D` day-index tags on kind 31923, so calendar events are
discoverable by date.
- Notifies on the rest of NIP-34: PR replies, merges, closes, reopens and drafts.
A merged PR used to arrive on the device and go nowhere.
- Remembers which side-menu sections you collapsed (#4010).
- Leaves the always-on notification card at the bare relay count, with the
per-relay breakdown behind a "Show details" action.
- Lifts the 3-relay cap on Concord invite links, which blew up the invite button
for any community with more.
- Saves videos to `Movies/` instead of `Pictures/` (#4009).
## Performance
- Draws Amethyst's own icons from a generated font rather than rasterising an
ImageVector per card: frame duration P90 -10.7%, overrun P90 -17.4%.
- Defers feed animation transitions until there is something to animate: overrun
P90 -19.4%.
- Stops copying whole GIFs into RAM before decoding them. One 69.8 MB GIF cost
66 MB of heap plus 66 MB of native memory and ~700 ms of pure copying, on every
scroll back into view.
- Restores the `ImageDecoder` path for still images, which the same identity
check had cost every network image in the app.
- Makes the concurrent-fetch de-dupe actually work — it was rebuilt per request,
so it never saw a second caller — rate-limits the cache reconciler to once a
day, and reclaims cache files orphaned by a process death.
- Stops blocking an OkHttp thread to sign Blossom read-auth, which could occupy
every per-host slot on a gated host, and collapses a cold burst onto one
signature.
- Asks for many addresses in one relay filter instead of one filter each: opening
a 240-section publication went from a black screen for ~2 minutes at 120% CPU
to first paint at ~22s.
- Rewrites Curve25519 field arithmetic on 10 limbs of radix 2^25.5 and runs the
ladder without allocating: x25519 541us → 121us, and against MDK
`create_group/1` goes from 4.7x slower to 1.5x while `send_app_message` is
6.5x faster.
- Stops `FilterMatcher` allocating once per event — three separate ways, on a
path that runs tens of thousands of times per keystroke during a search.
- Adds an allocation-free read path for NIP-50 indexable text to all 133
searchable kinds, pinned byte-for-byte against the write path.
- Shares one IME-inset holder per window instead of one per call site.
- Prices the NIP-19 bracket peel and makes it free.
- Defers log message construction to the lambda overload, including the loops the
first sweep's line-anchored greps missed.
- Bounds the route-text scan by the budget rather than the input.
- Bounds in-flight on-device AI work and stops blocking IO threads on it.
- Single-flights the LNURL endpoint resolver: a zap-receipt burst was making ~20
requests to one provider per user action.
## Improvements and Bug fixes
- Stops a fresh install stranding on a Tor bootstrap. The watchdog fired once per
`Connecting` span and a timeout produces no status change, so after exactly two
attempts it went silent — permanently. Arti now bootstraps on demand instead of
blocking the whole directory download behind its lifecycle lock.
- Classifies the app's stand-in relays like the user's own until their lists
arrive, so a new install stops routing everything over Tor by construction:
first relay socket moved from login+5.9s to login+1.2s, and events ingested by
the 20s census from 0–2,590 to 3,719–6,051.
- Only substitutes default relays when there is no event, not when the list is
empty — publishing an empty search or inbox list is a choice, and the app was
overriding it.
- Stops relay list updates wiping the public entries another client wrote
(#4075).
- Recovers IME padding when Compose's insets listener freezes. A back gesture
could leave a keyboard-sized gap app-wide, permanently, until the activity was
killed; the correction is now 120ms rather than 400ms.
- Keeps `TextFieldState` writes on the UI thread, fixing a crash inside Compose's
undo bookkeeping on the keyboard's own callback.
- Follows the phone's light/dark setting on the launch splash, and carries a
pinned LIGHT/DARK choice into it through the per-application night mode.
- Uses the HLS master playlist and its full quality ladder, sizes the ladder to
the player rather than to a fixed policy, caps it on metered connections, and
fixes a picture-in-picture race.
- Caps MediaSession artwork so the platform never re-scales it — a crash on ROMs
that recycle the source bitmap.
- Stops cancelling ML Kit GenAI inference futures, which killed the process from
a thread the app does not own. The composer cancelled one on every keystroke.
- Makes the full-screen viewer follow the system bars instead of reserving the
strip they would occupy, and brings the PDF viewer's chrome to parity.
- Caps navigation route text arguments. Sending a crash report to the developer
crashed the app: ICU's regex stack overflows at ~100,000 characters and reports
"no match" rather than an error.
- Narrows the `FileProvider` external root to the app-specific directory.
- Stops deleted lists showing their UUID in the feed filter picker (#3949).
- Restores GIF insertion on the comment reply screens, where a keyboard GIF did
nothing and a shared one threw away the reply in progress, and wires it into
four more composers.
- Keeps calls alive when the system destroys `MainActivity`, releases the
screen-capture Surface instead of leaking one per session, and stops reopening
the camera during teardown.
- Stops reporting coroutine cancellation as a signer failure, which surfaced as a
"signer not found" toast on essentially every fast settings toggle.
- Stops a picker's scrolling from dragging the top and bottom bars with it.
- Tints the un-boosted repost icon like the rest of the action row, and fixes the
liked and reposted colours.
- Stops labelling the mixed media feed "Shorts" — two different destinations in
the bottom-bar picker carried the same label.
- Converts Android string escaping to Compose escaping inside the Crowdin sync,
and gates it in CI, after three syncs in a row reintroduced ~2,900 escaped
apostrophes that render with a visible backslash.
## Desktop
- Adds NIP-53 live streaming: a "Live now" section in Discover with ranking and
search, a per-column live bar on the Following and Global feeds, and a watch
overlay with player, live chat and live-mode controls, openable from anywhere.
- Routes kinds 30311 and 1311 into the desktop cache, which had no channel store
at all, with a 500-message chat cap.
- Shares the unified search pipeline, and gives the spotlight the same people
picker — typing `from:` there opened nothing.
- Surfaces macOS notification-permission errors and times the request out, rather
than parking forever on a banner the user missed.
## Cli
- Redesigns `amy status` around who is signed in and what they saved, adding
relay config, follows, Marmot messages, Concord communities and the operator
key — and fixing a store report that read 0 events on the default backend.
- Drives the Marmot work end to end: current-profile group creation, retention,
media policy and transfer, disband, and `amy marmot stream`.
## Quartz
- Adds the adopted Marmot profile: the app-data dictionary carrier, account
identity proof v2, the six group components, the lifecycle state machine,
branch selection, the bounded convergence pass, the candidate-graph builder,
encrypted media v2, the kind-451 push owner proof and the agent-text-stream
codecs.
- Replaces `org.json` with `kotlinx.serialization` across the remaining sources.
- Backs linuxX64's `LargeCache` with a striped-lock hash table instead of
copy-on-write, which was O(n) per put and not thread-safe. Filling 100k entries
goes from 70ms to 15ms with 1 GC instead of 24 — and the whole linuxX64 suite
now passes (3,495 tests, from 78 failures), so the CI leg runs all of it.
- Matches `java.net.URLEncoder` on native and drops the `urlencoder` dependency.
The two implemented different specifications, so Android and iOS emitted
different bytes for the same magnet link or NWC deep link.
- Routes the roles the NIP-50 search extractor was stranding in the body tier: 19
kinds gain a branch, including the marketplace and Podcasting 2.0 families
whose titles could never reach a title band.
- Gives the Trusted List member score a 0–100 scale, reads the generic Trusted
List entry in a NIP-85 Treasure Map, carries Map entries in both halves of the
10040, and indexes Trusted List titles for NIP-50 search.
- Holds a transport failure as provisional for one retry when a relay drops the
socket between our EVENT and its OK, and restores pool membership first so the
retry dials something.
- Waits out the second instead of stamping `created_at` in the future when
superseding a replaceable event.
- Adds `DvmHeartbeatEvent` (kind 11998) and the publication, library, wiki and
rating parsers.
## Build & Documentation
- Bumps Kotlin to 2.4.20, Compose Multiplatform to 1.12.0 and AGP to 9.4.0, plus
the rest of the version catalog.
- Executes the commons migration sweep: 38 relayClient files, the OkHttp client
stack, 37 model and service singles, the top-nav feed datasource layer and the
search history move to `commons`; the 12 typealias shims are deleted and their
1,165 imports repointed; 25 files are promoted from `jvmAndroid` to
`commonMain` after a per-file concurrency review.
- Migrates 2,565 string keys — about 105,800 locale entries — to the shared
Compose resource catalog, and gates the orphaned-translation and escaping traps
pre-push and in CI.
- Publishes desktop test reports when the desktop job fails, which was the only
test-running job with none.
- Renames the `geode` Homebrew formula to `geode-relay`, since `geode` is
permanently reserved for Apache Geode, and fixes a style violation in both
formulae.
- Adds `marmotBench`, a head-to-head against MDK's own benchmark, reporting bytes
allocated per operation alongside latency.
## Contributors
- @Vitor Pamplona
- @⚡ Dee Kay ⚡
- @Minced Beef Energy
- @Don't Believe The Vibe 🌱🍋🍊
- @greenart7c3
- @narmic
- @mstrofnone ⚡🥜
## Translations
- Dutch by @Bartus
- Hindi by @राज
- Hungarian by @summoner
- Polish by @Polak
- Slovenian by @StellarStoic