diff --git a/.claude/skills/quartz-integration/SKILL.md b/.claude/skills/quartz-integration/SKILL.md index 433ee6787f..f060a36c64 100644 --- a/.claude/skills/quartz-integration/SKILL.md +++ b/.claude/skills/quartz-integration/SKILL.md @@ -7,7 +7,7 @@ description: Integration guide for using the Quartz Nostr KMP library in externa Reference for integrating `com.vitorpamplona.quartz:quartz` into external Nostr KMP projects. -**Published artifact**: `com.vitorpamplona.quartz:quartz:1.14.0` (Maven Central) +**Published artifact**: `com.vitorpamplona.quartz:quartz:1.15.0` (Maven Central) **Targets**: JVM 21+, Android (minSdk 21+), iOS (XCFramework `quartz-kmpKit`) **License**: MIT @@ -19,7 +19,7 @@ Reference for integrating `com.vitorpamplona.quartz:quartz` into external Nostr ```toml [versions] -quartz = "1.14.0" +quartz = "1.15.0" [libraries] quartz = { module = "com.vitorpamplona.quartz:quartz", version.ref = "quartz" } @@ -41,7 +41,7 @@ kotlin { ```kotlin dependencies { - implementation("com.vitorpamplona.quartz:quartz:1.14.0") + implementation("com.vitorpamplona.quartz:quartz:1.15.0") } ``` diff --git a/.claude/skills/quartz-integration/references/gradle-setup.md b/.claude/skills/quartz-integration/references/gradle-setup.md index 31fb38d8e6..742822d673 100644 --- a/.claude/skills/quartz-integration/references/gradle-setup.md +++ b/.claude/skills/quartz-integration/references/gradle-setup.md @@ -3,7 +3,7 @@ ## Current version ``` -com.vitorpamplona.quartz:quartz:1.14.0 +com.vitorpamplona.quartz:quartz:1.15.0 ``` Check latest: https://central.sonatype.com/artifact/com.vitorpamplona.quartz/quartz @@ -16,7 +16,7 @@ Check latest: https://central.sonatype.com/artifact/com.vitorpamplona.quartz/qua ```toml [versions] -quartz = "1.14.0" +quartz = "1.15.0" [libraries] quartz = { module = "com.vitorpamplona.quartz:quartz", version.ref = "quartz" } @@ -55,7 +55,7 @@ kotlin { ```kotlin // build.gradle.kts (app module) dependencies { - implementation("com.vitorpamplona.quartz:quartz:1.14.0") + implementation("com.vitorpamplona.quartz:quartz:1.15.0") } ``` @@ -70,7 +70,7 @@ plugins { } dependencies { - implementation("com.vitorpamplona.quartz:quartz:1.14.0") + implementation("com.vitorpamplona.quartz:quartz:1.15.0") // JNA needed for libsodium (NIP-44) on JVM implementation("net.java.dev.jna:jna:5.18.1") } diff --git a/BUILDING.md b/BUILDING.md index d4227b6af8..af87acd699 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -533,14 +533,17 @@ reads an optional per-release changelog from ## Bootstrap runbook (one-time) -> **Status as of v1.14.0:** Winget has been submitted — [microsoft/winget-pkgs#422752](https://github.com/microsoft/winget-pkgs/pull/422752), pending CLA + review. Neither Homebrew package (`amethyst-nostr` cask, `amy` formula) has been submitted yet. -> `https://formulae.brew.sh/api/cask/amethyst-nostr.json` and -> `microsoft/winget-pkgs/manifests/v/VitorPamplona/Amethyst` both 404, so -> **Amethyst does not currently ship through either channel.** The bump -> workflows detect this and skip with a `::warning::` instead of failing, so a -> green release run does *not* mean Homebrew/Winget shipped. The two subsections -> below are the work that activates them; until then treat the desktop app as -> GitHub-Releases-only on macOS and Windows. +> **Status as of v1.15.0:** both Homebrew packages are now live upstream — the +> `amethyst-nostr` cask (`Homebrew/homebrew-cask`, at 1.14.0) and the `amy` +> formula (`Homebrew/homebrew-core`) both answer 200 on `formulae.brew.sh`, so +> `bump-homebrew.yml` finally has something to bump. **Winget is still not +> bootstrapped**: [microsoft/winget-pkgs#422752](https://github.com/microsoft/winget-pkgs/pull/422752) +> is open pending CLA + review, and +> `microsoft/winget-pkgs/manifests/v/VitorPamplona/Amethyst` still 404s. Neither +> is the `geode-relay` formula, which has never been submitted. Those two bump +> workflows detect the absence and skip with a `::warning::` instead of failing, +> so a green release run does *not* mean they shipped; treat the desktop app as +> GitHub-Releases-only on **Windows**. ### Package-manager credentials (and why there are none) @@ -584,7 +587,7 @@ The token then lives only in that maintainer's shell: ```bash export HOMEBREW_GITHUB_API_TOKEN=ghp_... # classic PAT, `repo` scope -scripts/bump-homebrew-cask.sh v1.14.0 +scripts/bump-homebrew-cask.sh v1.15.0 ``` Create one at @@ -600,7 +603,7 @@ Same split, and it needs **no token at all**. `scripts/bump-winget.sh` drives runs fine from macOS or Linux: ```bash -scripts/bump-winget.sh v1.14.0 +scripts/bump-winget.sh v1.15.0 ``` CI (`bump-winget.yml`, `GITHUB_TOKEN` only) does the bookkeeping: downloads the diff --git a/README.md b/README.md index 3b3fed42ee..11c018c51e 100644 --- a/README.md +++ b/README.md @@ -328,16 +328,16 @@ repositories { Add the following line to your `commonMain` dependencies: ```gradle -implementation('com.vitorpamplona.quartz:quartz:1.14.0') +implementation('com.vitorpamplona.quartz:quartz:1.15.0') ``` Variations to each platform are also available: ```gradle -implementation('com.vitorpamplona.quartz:quartz-android:1.14.0') -implementation('com.vitorpamplona.quartz:quartz-jvm:1.14.0') -implementation('com.vitorpamplona.quartz:quartz-iosarm64:1.14.0') -implementation('com.vitorpamplona.quartz:quartz-iossimulatorarm64:1.14.0') +implementation('com.vitorpamplona.quartz:quartz-android:1.15.0') +implementation('com.vitorpamplona.quartz:quartz-jvm:1.15.0') +implementation('com.vitorpamplona.quartz:quartz-iosarm64:1.15.0') +implementation('com.vitorpamplona.quartz:quartz-iossimulatorarm64:1.15.0') ``` Check versions on [MavenCentral](https://central.sonatype.com/search?q=com.vitorpamplona.quartz) diff --git a/RELEASE_OPS.md b/RELEASE_OPS.md index 499ab787ea..7ca75fc9f1 100644 --- a/RELEASE_OPS.md +++ b/RELEASE_OPS.md @@ -190,26 +190,29 @@ RELAY_URLS="wss://relay.zapstore.dev,wss://nos.lol,wss://nostr.mom,wss://vitor.n Keep `wss://relay.zapstore.dev` in the list — that is the relay the Zapstore app itself reads from. -### Homebrew + Winget — ⚠️ not shipping yet +### Homebrew + Winget — ⚠️ Winget not shipping yet `bump-homebrew.yml` and `bump-winget.yml` are wired to open PRs against -`Homebrew/homebrew-cask` (cask `amethyst-nostr`) and `microsoft/winget-pkgs` -(`VitorPamplona.Amethyst`). Both can only *update* a package that already -exists upstream, so until the one-time bootstrap lands they detect the absence -and skip with a `::warning::`. +`Homebrew/homebrew-cask` (cask `amethyst-nostr`), `Homebrew/homebrew-core` +(formula `amy`) and `microsoft/winget-pkgs` (`VitorPamplona.Amethyst`). They can +only *update* a package that already exists upstream, so until the one-time +bootstrap lands they detect the absence and skip with a `::warning::`. Bootstrap status: | Channel | Upstream package | State | |---|---|---| -| **Winget** | `microsoft/winget-pkgs` → `VitorPamplona.Amethyst` | **Submitted at v1.14.0** — [PR #422752](https://github.com/microsoft/winget-pkgs/pull/422752), pending CLA + review | -| **Homebrew cask** | `Homebrew/homebrew-cask` → `amethyst-nostr` | Not submitted | -| **Homebrew formula** | `Homebrew/homebrew-core` → `amy` | Not submitted | +| **Homebrew cask** | `Homebrew/homebrew-cask` → `amethyst-nostr` | **Live** — merged 2026-08-24, upstream at 1.14.0 | +| **Homebrew formula** | `Homebrew/homebrew-core` → `amy` | **Live** | +| **Homebrew formula** | `Homebrew/homebrew-core` → `geode-relay` | Not submitted — renamed from `geode`, which is permanently reserved for Apache Geode | +| **Winget** | `microsoft/winget-pkgs` → `VitorPamplona.Amethyst` | **Submitted at v1.14.0** — [PR #422752](https://github.com/microsoft/winget-pkgs/pull/422752), still open pending CLA + review | -Until each lands, that channel delivers nothing and macOS/Windows users get the -desktop app from GitHub Releases only. Re-check before assuming — the state -above is a snapshot, and `gh api repos/microsoft/winget-pkgs/contents/manifests/v/VitorPamplona` -(404 = still absent) answers it in one call. +Until each lands, that channel delivers nothing and its users get the desktop +app or CLI from GitHub Releases only. Re-check before assuming — the state above +is a snapshot, and two calls answer it: +`gh api repos/microsoft/winget-pkgs/contents/manifests/v/VitorPamplona` and +`curl -s -o /dev/null -w '%{http_code}' https://formulae.brew.sh/api/cask/amethyst-nostr.json` +(404 = still absent). Two separate faults kept this invisible until v1.13.1, both now fixed: @@ -237,9 +240,9 @@ readable by anyone with push access here), so a maintainer runs the last step: ```bash # after merging the sync PRs export HOMEBREW_GITHUB_API_TOKEN=ghp_... # classic PAT, `repo` scope -scripts/bump-homebrew-cask.sh v1.14.0 +scripts/bump-homebrew-cask.sh v1.15.0 -scripts/bump-winget.sh v1.14.0 # no token — uses your `gh` auth +scripts/bump-winget.sh v1.15.0 # no token — uses your `gh` auth ``` Both scripts re-verify the published artifact's sha256 before submitting, and diff --git a/amethyst/build.gradle.kts b/amethyst/build.gradle.kts index 6cf575a8ce..4b9583cc82 100644 --- a/amethyst/build.gradle.kts +++ b/amethyst/build.gradle.kts @@ -85,7 +85,7 @@ android { .get() .toInt() versionName = generateVersionName(libs.versions.app.get(), rootDir) - buildConfigField("String", "RELEASE_NOTES_ID", "\"00d306e01792e48b93638b73b57a7eb8b89622a338b1b4f529622150d46cd710\"") + buildConfigField("String", "RELEASE_NOTES_ID", "\"8fce45589ea44df75e828a04c7d70bb4fabedd6ffc1946a920b2f0c7c990ff9f\"") testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" vectorDrawables { diff --git a/docs/changelog/README.md b/docs/changelog/README.md index 5081422ec5..8396285f58 100644 --- a/docs/changelog/README.md +++ b/docs/changelog/README.md @@ -2,6 +2,7 @@ Release notes for Amethyst, one file per version. Files are named with zero-padded version numbers so they sort correctly in any file browser. Use [`TEMPLATE.md`](TEMPLATE.md) as the starting point for the next release. +- [v1.15.0 — Marmot Updates, a Advanced Search Box, and Books, DVM Updates](v1.15.00.md) - [v1.14.0 — Highlights, Relay Login, and a Much Faster Start](v1.14.00.md) - [v1.13.1 — Follow-up Fixes for Buzz, Concord and the Keyboard](v1.13.01.md) - [v1.13.0 — Web Apps, Communities & Git](v1.13.00.md) diff --git a/docs/changelog/v1.15.00.md b/docs/changelog/v1.15.00.md new file mode 100644 index 0000000000..f1b736392b --- /dev/null +++ b/docs/changelog/v1.15.00.md @@ -0,0 +1,340 @@ +# 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 `` — 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 + +- @npub1gcxzte5zlkncx26j68ez60fzkvtkm9e0vrwdcvsjakxf9mu9qewqlfnj5z +- @npub1e2yuky03caw4ke3zy68lg0fz3r4gkt94hx4fjmlelacyljgyk79svn3eef +- @npub12cfje6nl2nuxplcqfvhg7ljt89fmpj0n0fd24zxsukja5qm9wmtqd7y76c +- @npub1nxa4tywfz9nqp7z9zp7nr7d4nchhclsf58lcqt5y782rmf2hefjquaa6q8 +- @npub1w4uswmv6lu9yel005l3qgheysmr7tk9uvwluddznju3nuxalevvs2d0jr5 +- @npub1y9tpxsfnccnnvg7kanqa8wsyxuu2z72wxr2hd3m29psac3dkucdqmm7p4e +- @npub1gvv9ahktvavf9qjtrgm62le7gplmmchd5usp5wpfhr85hf79kncqj8xchs + +## Translations + +- Dutch by @npub1w4la29u3zv09r6crx5u8yxax0ffxgekzdm2egzjkjckef7xc83fs0ftxcd +- Hindi by @npub1ww6huwu3xye6r05n3qkjeq62wds5pq0jswhl7uc59lchc0n0ns4sdtw5e6 +- Hungarian by @npub1dnvslq0vvrs8d603suykc4harv94yglcxwna9sl2xu8grt2afm3qgfh0tp +- Polish by @npub16gjyljum0ksrrm28zzvejydgxwfm7xse98zwc4hlgq8epxeuggushqwyrm +- Slovenian by @npub1qqqqqqz7nhdqz3uuwmzlflxt46lyu7zkuqhcapddhgz66c4ddynswreecw \ No newline at end of file diff --git a/geode/README.md b/geode/README.md index a700bd3614..cd5031e6ea 100644 --- a/geode/README.md +++ b/geode/README.md @@ -32,7 +32,7 @@ docker run -d --name geode -p 7447:7447 \ ``` with `in_memory = false` and `file = "/var/lib/geode/events.db"` in your -`geode.toml`. Pin a version (`:1.14.0`) instead of `:latest` for reproducible +`geode.toml`. Pin a version (`:1.15.0`) instead of `:latest` for reproducible deploys. To build the image yourself, from the repo root: ```bash @@ -47,7 +47,7 @@ it — a minimal JRE is bundled, so no system Java is required. It installs to `/opt/geode/` with the launcher at `/opt/geode/bin/geode`. ```bash -sudo dpkg -i geode-1.14.0-linux-x64.deb # or: sudo rpm -i geode-1.14.0-linux-x64.rpm +sudo dpkg -i geode-1.15.0-linux-x64.deb # or: sudo rpm -i geode-1.15.0-linux-x64.rpm ``` To run it as a managed service, wire up the shipped systemd unit @@ -69,7 +69,7 @@ formula: [`packaging/homebrew/geode-relay.rb`](packaging/homebrew/geode-relay.rb Download `geode---.tar.gz`, unpack, and run: ```bash -tar xzf geode-1.14.0-linux-x64.tar.gz +tar xzf geode-1.15.0-linux-x64.tar.gz ./geode/bin/geode --config geode/share/geode/config.example.toml ``` diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 6d7757baf2..904ecf238e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,8 +2,8 @@ # Amethyst app version — single source of truth consumed by both Android (amethyst/) # and Desktop (desktopApp/). `appCode` is the Android versionCode: a monotonic # integer that must increment on every release, even when `app` is unchanged. -app = "1.14.0" -appCode = "457" +app = "1.15.0" +appCode = "458" accompanistAdaptive = "0.37.3" # Pinned: 0.3.0 turns CacheMap.entries/keys/values into DeprecationLevel.ERROR (and # throws UnsupportedOperationException at runtime), which breaks quartz's appleMain