mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-10-05 19:28:25 +00:00
Merge pull request #4081 from vitorpamplona/chore/release-1.15.0
chore(release): bump to 1.15.0
This commit is contained in:
@@ -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.
|
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`)
|
**Targets**: JVM 21+, Android (minSdk 21+), iOS (XCFramework `quartz-kmpKit`)
|
||||||
**License**: MIT
|
**License**: MIT
|
||||||
|
|
||||||
@@ -19,7 +19,7 @@ Reference for integrating `com.vitorpamplona.quartz:quartz` into external Nostr
|
|||||||
|
|
||||||
```toml
|
```toml
|
||||||
[versions]
|
[versions]
|
||||||
quartz = "1.14.0"
|
quartz = "1.15.0"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
quartz = { module = "com.vitorpamplona.quartz:quartz", version.ref = "quartz" }
|
quartz = { module = "com.vitorpamplona.quartz:quartz", version.ref = "quartz" }
|
||||||
@@ -41,7 +41,7 @@ kotlin {
|
|||||||
|
|
||||||
```kotlin
|
```kotlin
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation("com.vitorpamplona.quartz:quartz:1.14.0")
|
implementation("com.vitorpamplona.quartz:quartz:1.15.0")
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
## Current version
|
## 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
|
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
|
```toml
|
||||||
[versions]
|
[versions]
|
||||||
quartz = "1.14.0"
|
quartz = "1.15.0"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
quartz = { module = "com.vitorpamplona.quartz:quartz", version.ref = "quartz" }
|
quartz = { module = "com.vitorpamplona.quartz:quartz", version.ref = "quartz" }
|
||||||
@@ -55,7 +55,7 @@ kotlin {
|
|||||||
```kotlin
|
```kotlin
|
||||||
// build.gradle.kts (app module)
|
// build.gradle.kts (app module)
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation("com.vitorpamplona.quartz:quartz:1.14.0")
|
implementation("com.vitorpamplona.quartz:quartz:1.15.0")
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -70,7 +70,7 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
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
|
// JNA needed for libsodium (NIP-44) on JVM
|
||||||
implementation("net.java.dev.jna:jna:5.18.1")
|
implementation("net.java.dev.jna:jna:5.18.1")
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-10
@@ -533,14 +533,17 @@ reads an optional per-release changelog from
|
|||||||
|
|
||||||
## Bootstrap runbook (one-time)
|
## 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.
|
> **Status as of v1.15.0:** both Homebrew packages are now live upstream — the
|
||||||
> `https://formulae.brew.sh/api/cask/amethyst-nostr.json` and
|
> `amethyst-nostr` cask (`Homebrew/homebrew-cask`, at 1.14.0) and the `amy`
|
||||||
> `microsoft/winget-pkgs/manifests/v/VitorPamplona/Amethyst` both 404, so
|
> formula (`Homebrew/homebrew-core`) both answer 200 on `formulae.brew.sh`, so
|
||||||
> **Amethyst does not currently ship through either channel.** The bump
|
> `bump-homebrew.yml` finally has something to bump. **Winget is still not
|
||||||
> workflows detect this and skip with a `::warning::` instead of failing, so a
|
> bootstrapped**: [microsoft/winget-pkgs#422752](https://github.com/microsoft/winget-pkgs/pull/422752)
|
||||||
> green release run does *not* mean Homebrew/Winget shipped. The two subsections
|
> is open pending CLA + review, and
|
||||||
> below are the work that activates them; until then treat the desktop app as
|
> `microsoft/winget-pkgs/manifests/v/VitorPamplona/Amethyst` still 404s. Neither
|
||||||
> GitHub-Releases-only on macOS and Windows.
|
> 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)
|
### Package-manager credentials (and why there are none)
|
||||||
|
|
||||||
@@ -584,7 +587,7 @@ The token then lives only in that maintainer's shell:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
export HOMEBREW_GITHUB_API_TOKEN=ghp_... # classic PAT, `repo` scope
|
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
|
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:
|
runs fine from macOS or Linux:
|
||||||
|
|
||||||
```bash
|
```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
|
CI (`bump-winget.yml`, `GITHUB_TOKEN` only) does the bookkeeping: downloads the
|
||||||
|
|||||||
@@ -328,16 +328,16 @@ repositories {
|
|||||||
Add the following line to your `commonMain` dependencies:
|
Add the following line to your `commonMain` dependencies:
|
||||||
|
|
||||||
```gradle
|
```gradle
|
||||||
implementation('com.vitorpamplona.quartz:quartz:1.14.0')
|
implementation('com.vitorpamplona.quartz:quartz:1.15.0')
|
||||||
```
|
```
|
||||||
|
|
||||||
Variations to each platform are also available:
|
Variations to each platform are also available:
|
||||||
|
|
||||||
```gradle
|
```gradle
|
||||||
implementation('com.vitorpamplona.quartz:quartz-android:1.14.0')
|
implementation('com.vitorpamplona.quartz:quartz-android:1.15.0')
|
||||||
implementation('com.vitorpamplona.quartz:quartz-jvm:1.14.0')
|
implementation('com.vitorpamplona.quartz:quartz-jvm:1.15.0')
|
||||||
implementation('com.vitorpamplona.quartz:quartz-iosarm64:1.14.0')
|
implementation('com.vitorpamplona.quartz:quartz-iosarm64:1.15.0')
|
||||||
implementation('com.vitorpamplona.quartz:quartz-iossimulatorarm64:1.14.0')
|
implementation('com.vitorpamplona.quartz:quartz-iossimulatorarm64:1.15.0')
|
||||||
```
|
```
|
||||||
|
|
||||||
Check versions on [MavenCentral](https://central.sonatype.com/search?q=com.vitorpamplona.quartz)
|
Check versions on [MavenCentral](https://central.sonatype.com/search?q=com.vitorpamplona.quartz)
|
||||||
|
|||||||
+17
-14
@@ -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
|
Keep `wss://relay.zapstore.dev` in the list — that is the relay the Zapstore app
|
||||||
itself reads from.
|
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
|
`bump-homebrew.yml` and `bump-winget.yml` are wired to open PRs against
|
||||||
`Homebrew/homebrew-cask` (cask `amethyst-nostr`) and `microsoft/winget-pkgs`
|
`Homebrew/homebrew-cask` (cask `amethyst-nostr`), `Homebrew/homebrew-core`
|
||||||
(`VitorPamplona.Amethyst`). Both can only *update* a package that already
|
(formula `amy`) and `microsoft/winget-pkgs` (`VitorPamplona.Amethyst`). They can
|
||||||
exists upstream, so until the one-time bootstrap lands they detect the absence
|
only *update* a package that already exists upstream, so until the one-time
|
||||||
and skip with a `::warning::`.
|
bootstrap lands they detect the absence and skip with a `::warning::`.
|
||||||
|
|
||||||
Bootstrap status:
|
Bootstrap status:
|
||||||
|
|
||||||
| Channel | Upstream package | State |
|
| 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` | **Live** — merged 2026-08-24, upstream at 1.14.0 |
|
||||||
| **Homebrew cask** | `Homebrew/homebrew-cask` → `amethyst-nostr` | Not submitted |
|
| **Homebrew formula** | `Homebrew/homebrew-core` → `amy` | **Live** |
|
||||||
| **Homebrew formula** | `Homebrew/homebrew-core` → `amy` | Not submitted |
|
| **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
|
Until each lands, that channel delivers nothing and its users get the desktop
|
||||||
desktop app from GitHub Releases only. Re-check before assuming — the state
|
app or CLI from GitHub Releases only. Re-check before assuming — the state above
|
||||||
above is a snapshot, and `gh api repos/microsoft/winget-pkgs/contents/manifests/v/VitorPamplona`
|
is a snapshot, and two calls answer it:
|
||||||
(404 = still absent) answers it in one call.
|
`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:
|
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
|
```bash
|
||||||
# after merging the sync PRs
|
# after merging the sync PRs
|
||||||
export HOMEBREW_GITHUB_API_TOKEN=ghp_... # classic PAT, `repo` scope
|
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
|
Both scripts re-verify the published artifact's sha256 before submitting, and
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ android {
|
|||||||
.get()
|
.get()
|
||||||
.toInt()
|
.toInt()
|
||||||
versionName = generateVersionName(libs.versions.app.get(), rootDir)
|
versionName = generateVersionName(libs.versions.app.get(), rootDir)
|
||||||
buildConfigField("String", "RELEASE_NOTES_ID", "\"00d306e01792e48b93638b73b57a7eb8b89622a338b1b4f529622150d46cd710\"")
|
buildConfigField("String", "RELEASE_NOTES_ID", "\"8fce45589ea44df75e828a04c7d70bb4fabedd6ffc1946a920b2f0c7c990ff9f\"")
|
||||||
|
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
vectorDrawables {
|
vectorDrawables {
|
||||||
|
|||||||
@@ -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.
|
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.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.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)
|
- [v1.13.0 — Web Apps, Communities & Git](v1.13.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 `<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
|
||||||
|
|
||||||
|
- @npub1gcxzte5zlkncx26j68ez60fzkvtkm9e0vrwdcvsjakxf9mu9qewqlfnj5z
|
||||||
|
- @npub1e2yuky03caw4ke3zy68lg0fz3r4gkt94hx4fjmlelacyljgyk79svn3eef
|
||||||
|
- @npub12cfje6nl2nuxplcqfvhg7ljt89fmpj0n0fd24zxsukja5qm9wmtqd7y76c
|
||||||
|
- @npub1nxa4tywfz9nqp7z9zp7nr7d4nchhclsf58lcqt5y782rmf2hefjquaa6q8
|
||||||
|
- @npub1w4uswmv6lu9yel005l3qgheysmr7tk9uvwluddznju3nuxalevvs2d0jr5
|
||||||
|
- @npub1y9tpxsfnccnnvg7kanqa8wsyxuu2z72wxr2hd3m29psac3dkucdqmm7p4e
|
||||||
|
- @npub1gvv9ahktvavf9qjtrgm62le7gplmmchd5usp5wpfhr85hf79kncqj8xchs
|
||||||
|
|
||||||
|
## Translations
|
||||||
|
|
||||||
|
- Dutch by @npub1w4la29u3zv09r6crx5u8yxax0ffxgekzdm2egzjkjckef7xc83fs0ftxcd
|
||||||
|
- Hindi by @npub1ww6huwu3xye6r05n3qkjeq62wds5pq0jswhl7uc59lchc0n0ns4sdtw5e6
|
||||||
|
- Hungarian by @npub1dnvslq0vvrs8d603suykc4harv94yglcxwna9sl2xu8grt2afm3qgfh0tp
|
||||||
|
- Polish by @npub16gjyljum0ksrrm28zzvejydgxwfm7xse98zwc4hlgq8epxeuggushqwyrm
|
||||||
|
- Slovenian by @npub1qqqqqqz7nhdqz3uuwmzlflxt46lyu7zkuqhcapddhgz66c4ddynswreecw
|
||||||
+3
-3
@@ -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
|
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:
|
deploys. To build the image yourself, from the repo root:
|
||||||
|
|
||||||
```bash
|
```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`.
|
`/opt/geode/` with the launcher at `/opt/geode/bin/geode`.
|
||||||
|
|
||||||
```bash
|
```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
|
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-<version>-<os>-<arch>.tar.gz`, unpack, and run:
|
Download `geode-<version>-<os>-<arch>.tar.gz`, unpack, and run:
|
||||||
|
|
||||||
```bash
|
```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
|
./geode/bin/geode --config geode/share/geode/config.example.toml
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
# Amethyst app version — single source of truth consumed by both Android (amethyst/)
|
# Amethyst app version — single source of truth consumed by both Android (amethyst/)
|
||||||
# and Desktop (desktopApp/). `appCode` is the Android versionCode: a monotonic
|
# and Desktop (desktopApp/). `appCode` is the Android versionCode: a monotonic
|
||||||
# integer that must increment on every release, even when `app` is unchanged.
|
# integer that must increment on every release, even when `app` is unchanged.
|
||||||
app = "1.14.0"
|
app = "1.15.0"
|
||||||
appCode = "457"
|
appCode = "458"
|
||||||
accompanistAdaptive = "0.37.3"
|
accompanistAdaptive = "0.37.3"
|
||||||
# Pinned: 0.3.0 turns CacheMap.entries/keys/values into DeprecationLevel.ERROR (and
|
# Pinned: 0.3.0 turns CacheMap.entries/keys/values into DeprecationLevel.ERROR (and
|
||||||
# throws UnsupportedOperationException at runtime), which breaks quartz's appleMain
|
# throws UnsupportedOperationException at runtime), which breaks quartz's appleMain
|
||||||
|
|||||||
Reference in New Issue
Block a user