Amethyst's avatar
Amethyst
amethyst@vitorpamplona.com
npub142gy...xrj0
Nostr Client for Android
Amethyst's avatar
amethyst 1 week ago
#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's avatar
amethyst 2 weeks ago
#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's avatar
amethyst 0 months ago
#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's avatar
amethyst 1 month ago
#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's avatar
amethyst 1 month ago
#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's avatar
amethyst 1 month ago
#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
Amethyst's avatar
amethyst 2 months ago
#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
↑