From 26beff5878fc9cd0a4bebec6d29d7bbef3b1386c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 28 Jul 2026 15:26:59 +0000 Subject: [PATCH 1/7] chore: create release 1.11.0 (ios 4.11.0, android 4.11.0, node-sdk 6.1.0, browser-sdk 7.1.0, node-bindings 1.11.0, wasm-bindings 1.11.0) --- Cargo.lock | 52 +++++++++++----------- Cargo.toml | 2 +- bindings/node/package.json | 2 +- bindings/wasm/package.json | 2 +- docs/release-notes/android/4.11.0.md | 27 +++++++++++ docs/release-notes/browser-sdk/7.1.0.md | 26 +++++++++++ docs/release-notes/ios/4.11.0.md | 27 +++++++++++ docs/release-notes/node-bindings/1.11.0.md | 26 +++++++++++ docs/release-notes/node-sdk/6.1.0.md | 26 +++++++++++ docs/release-notes/wasm-bindings/1.11.0.md | 26 +++++++++++ sdks/android/gradle.properties | 2 +- sdks/ios/XMTP.podspec | 2 +- sdks/js/browser-sdk/package.json | 2 +- sdks/js/node-sdk/package.json | 2 +- 14 files changed, 191 insertions(+), 33 deletions(-) create mode 100644 docs/release-notes/android/4.11.0.md create mode 100644 docs/release-notes/browser-sdk/7.1.0.md create mode 100644 docs/release-notes/ios/4.11.0.md create mode 100644 docs/release-notes/node-bindings/1.11.0.md create mode 100644 docs/release-notes/node-sdk/6.1.0.md create mode 100644 docs/release-notes/wasm-bindings/1.11.0.md diff --git a/Cargo.lock b/Cargo.lock index 0ec9983984..92e7eb5d50 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1417,7 +1417,7 @@ dependencies = [ [[package]] name = "bindings_node" -version = "1.11.0-dev" +version = "1.11.0" dependencies = [ "async-trait", "bindings_node", @@ -1451,7 +1451,7 @@ dependencies = [ [[package]] name = "bindings_wasm" -version = "1.11.0-dev" +version = "1.11.0" dependencies = [ "alloy", "async-trait", @@ -1496,7 +1496,7 @@ dependencies = [ [[package]] name = "bindings_wasm_macros" -version = "1.11.0-dev" +version = "1.11.0" dependencies = [ "proc-macro2", "quote", @@ -3018,7 +3018,7 @@ dependencies = [ [[package]] name = "error_glossary" -version = "1.11.0-dev" +version = "1.11.0" dependencies = [ "clap", "syn 2.0.119", @@ -5289,7 +5289,7 @@ dependencies = [ [[package]] name = "keepalive-probe" -version = "1.11.0-dev" +version = "1.11.0" dependencies = [ "anyhow", "bytes", @@ -5947,7 +5947,7 @@ dependencies = [ [[package]] name = "mls_validation_service" -version = "1.11.0-dev" +version = "1.11.0" dependencies = [ "alloy", "async-trait", @@ -10453,7 +10453,7 @@ dependencies = [ [[package]] name = "xdbg" -version = "1.11.0-dev" +version = "1.11.0" dependencies = [ "alloy-primitives", "alloy-signer-local", @@ -10518,7 +10518,7 @@ dependencies = [ [[package]] name = "xmtp-db-tools" -version = "1.11.0-dev" +version = "1.11.0" dependencies = [ "anyhow", "clap", @@ -10650,7 +10650,7 @@ dependencies = [ [[package]] name = "xmtp_api" -version = "1.11.0-dev" +version = "1.11.0" dependencies = [ "async-trait", "chrono", @@ -10676,7 +10676,7 @@ dependencies = [ [[package]] name = "xmtp_api_d14n" -version = "1.11.0-dev" +version = "1.11.0" dependencies = [ "arc-swap", "async-trait", @@ -10717,7 +10717,7 @@ dependencies = [ [[package]] name = "xmtp_api_grpc" -version = "1.11.0-dev" +version = "1.11.0" dependencies = [ "async-trait", "futures", @@ -10750,7 +10750,7 @@ dependencies = [ [[package]] name = "xmtp_archive" -version = "1.11.0-dev" +version = "1.11.0" dependencies = [ "aes-gcm", "async-compression", @@ -10779,7 +10779,7 @@ dependencies = [ [[package]] name = "xmtp_common" -version = "1.11.0-dev" +version = "1.11.0" dependencies = [ "async-trait", "const-hex", @@ -10814,7 +10814,7 @@ dependencies = [ [[package]] name = "xmtp_configuration" -version = "1.11.0-dev" +version = "1.11.0" dependencies = [ "openmls", "xmtp-workspace-hack", @@ -10824,7 +10824,7 @@ dependencies = [ [[package]] name = "xmtp_content_types" -version = "1.11.0-dev" +version = "1.11.0" dependencies = [ "aes-gcm", "base64 0.23.0", @@ -10845,7 +10845,7 @@ dependencies = [ [[package]] name = "xmtp_cryptography" -version = "1.11.0-dev" +version = "1.11.0" dependencies = [ "alloy", "bincode", @@ -10874,7 +10874,7 @@ dependencies = [ [[package]] name = "xmtp_db" -version = "1.11.0-dev" +version = "1.11.0" dependencies = [ "arc-swap", "ascii_table", @@ -10923,7 +10923,7 @@ dependencies = [ [[package]] name = "xmtp_id" -version = "1.11.0-dev" +version = "1.11.0" dependencies = [ "alloy", "async-trait", @@ -10959,7 +10959,7 @@ dependencies = [ [[package]] name = "xmtp_logging" -version = "1.11.0-dev" +version = "1.11.0" dependencies = [ "console_error_panic_hook", "opentelemetry", @@ -10981,7 +10981,7 @@ dependencies = [ [[package]] name = "xmtp_macro" -version = "1.11.0-dev" +version = "1.11.0" dependencies = [ "num_cpus", "proc-macro2", @@ -10994,7 +10994,7 @@ dependencies = [ [[package]] name = "xmtp_mls" -version = "1.11.0-dev" +version = "1.11.0" dependencies = [ "aes-gcm", "alloy", @@ -11078,7 +11078,7 @@ dependencies = [ [[package]] name = "xmtp_mls_common" -version = "1.11.0-dev" +version = "1.11.0" dependencies = [ "bon", "const-hex", @@ -11105,7 +11105,7 @@ dependencies = [ [[package]] name = "xmtp_proto" -version = "1.11.0-dev" +version = "1.11.0" dependencies = [ "async-trait", "bincode", @@ -11150,7 +11150,7 @@ dependencies = [ [[package]] name = "xmtpv3" -version = "1.11.0-dev" +version = "1.11.0" dependencies = [ "alloy", "async-trait", @@ -11189,7 +11189,7 @@ dependencies = [ [[package]] name = "xnet-cli" -version = "1.11.0-dev" +version = "1.11.0" dependencies = [ "clap", "color-eyre", @@ -11201,7 +11201,7 @@ dependencies = [ [[package]] name = "xnet-lib" -version = "1.11.0-dev" +version = "1.11.0" dependencies = [ "alloy", "ascii_table", diff --git a/Cargo.toml b/Cargo.toml index 9956acea44..1c551c98ed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,7 +26,7 @@ resolver = "3" [workspace.package] license = "MIT" -version = "1.11.0-dev" +version = "1.11.0" rust-version = "1.94.0" publish = false edition = "2024" diff --git a/bindings/node/package.json b/bindings/node/package.json index 914d0f19f7..eb570580e1 100644 --- a/bindings/node/package.json +++ b/bindings/node/package.json @@ -1,6 +1,6 @@ { "name": "@xmtp/node-bindings", - "version": "1.11.0-dev", + "version": "1.11.0", "repository": { "type": "git", "url": "git+https://github.com/xmtp/libxmtp.git", diff --git a/bindings/wasm/package.json b/bindings/wasm/package.json index dc66b203da..fefb2d3edc 100644 --- a/bindings/wasm/package.json +++ b/bindings/wasm/package.json @@ -1,6 +1,6 @@ { "name": "@xmtp/wasm-bindings", - "version": "1.11.0-dev", + "version": "1.11.0", "description": "WASM bindings for the libXMTP rust library", "keywords": [ "xmtp", diff --git a/docs/release-notes/android/4.11.0.md b/docs/release-notes/android/4.11.0.md new file mode 100644 index 0000000000..14545a130f --- /dev/null +++ b/docs/release-notes/android/4.11.0.md @@ -0,0 +1,27 @@ +--- +sdk = "android" +previous_release_version = "4.10.0" +previous_release_tag = "android-4.10.0" +--- + +# Android SDK 4.11.0 + +## Highlights + + + +## What's Changed + + + +## Breaking Changes + + + +## New Features + + + +## Bug Fixes + + diff --git a/docs/release-notes/browser-sdk/7.1.0.md b/docs/release-notes/browser-sdk/7.1.0.md new file mode 100644 index 0000000000..1dce7ef638 --- /dev/null +++ b/docs/release-notes/browser-sdk/7.1.0.md @@ -0,0 +1,26 @@ +--- +sdk = "browser-sdk" +previous_release_version = "7.0.0" +--- + +# Browser SDK SDK 7.1.0 + +## Highlights + + + +## What's Changed + + + +## Breaking Changes + + + +## New Features + + + +## Bug Fixes + + diff --git a/docs/release-notes/ios/4.11.0.md b/docs/release-notes/ios/4.11.0.md new file mode 100644 index 0000000000..4301874d62 --- /dev/null +++ b/docs/release-notes/ios/4.11.0.md @@ -0,0 +1,27 @@ +--- +sdk = "ios" +previous_release_version = "4.10.0" +previous_release_tag = "ios-4.10.0" +--- + +# iOS SDK 4.11.0 + +## Highlights + + + +## What's Changed + + + +## Breaking Changes + + + +## New Features + + + +## Bug Fixes + + diff --git a/docs/release-notes/node-bindings/1.11.0.md b/docs/release-notes/node-bindings/1.11.0.md new file mode 100644 index 0000000000..b4f6ac5754 --- /dev/null +++ b/docs/release-notes/node-bindings/1.11.0.md @@ -0,0 +1,26 @@ +--- +sdk = "node-bindings" +previous_release_version = "1.11.0-dev" +--- + +# Node SDK 1.11.0 + +## Highlights + + + +## What's Changed + + + +## Breaking Changes + + + +## New Features + + + +## Bug Fixes + + diff --git a/docs/release-notes/node-sdk/6.1.0.md b/docs/release-notes/node-sdk/6.1.0.md new file mode 100644 index 0000000000..d1a0871b43 --- /dev/null +++ b/docs/release-notes/node-sdk/6.1.0.md @@ -0,0 +1,26 @@ +--- +sdk = "node-sdk" +previous_release_version = "6.0.0" +--- + +# Node SDK SDK 6.1.0 + +## Highlights + + + +## What's Changed + + + +## Breaking Changes + + + +## New Features + + + +## Bug Fixes + + diff --git a/docs/release-notes/wasm-bindings/1.11.0.md b/docs/release-notes/wasm-bindings/1.11.0.md new file mode 100644 index 0000000000..b2d0e69925 --- /dev/null +++ b/docs/release-notes/wasm-bindings/1.11.0.md @@ -0,0 +1,26 @@ +--- +sdk = "wasm-bindings" +previous_release_version = "1.11.0-dev" +--- + +# WASM SDK 1.11.0 + +## Highlights + + + +## What's Changed + + + +## Breaking Changes + + + +## New Features + + + +## Bug Fixes + + diff --git a/sdks/android/gradle.properties b/sdks/android/gradle.properties index ff3d0b1ade..7cfa9f7958 100644 --- a/sdks/android/gradle.properties +++ b/sdks/android/gradle.properties @@ -23,4 +23,4 @@ kotlin.code.style=official android.nonTransitiveRClass=true android.nonFinalResIds=false # SDK version - managed by release-tools -version=4.10.0 +version=4.11.0 diff --git a/sdks/ios/XMTP.podspec b/sdks/ios/XMTP.podspec index b49e7ffc02..4f4b8ee88f 100644 --- a/sdks/ios/XMTP.podspec +++ b/sdks/ios/XMTP.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |spec| spec.name = "XMTP" - spec.version = "4.10.0" + spec.version = "4.11.0" spec.summary = "XMTP SDK Cocoapod" diff --git a/sdks/js/browser-sdk/package.json b/sdks/js/browser-sdk/package.json index 874666e34a..cd199d4b43 100644 --- a/sdks/js/browser-sdk/package.json +++ b/sdks/js/browser-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@xmtp/browser-sdk", - "version": "7.0.0", + "version": "7.1.0", "description": "XMTP client SDK for browsers written in TypeScript", "keywords": [ "xmtp", diff --git a/sdks/js/node-sdk/package.json b/sdks/js/node-sdk/package.json index c140b7975e..abe3ad17c1 100644 --- a/sdks/js/node-sdk/package.json +++ b/sdks/js/node-sdk/package.json @@ -1,6 +1,6 @@ { "name": "@xmtp/node-sdk", - "version": "6.0.0", + "version": "6.1.0", "description": "XMTP Node client SDK for interacting with XMTP networks", "keywords": [ "xmtp", From 0e3a42d481ab8b402e88ce424de8eab410e11254 Mon Sep 17 00:00:00 2001 From: "claude[bot]" <41898282+claude[bot]@users.noreply.github.com> Date: Tue, 28 Jul 2026 18:41:05 +0000 Subject: [PATCH 2/7] docs: draft release notes for 1.11.0 [skip ci] --- docs/release-notes/android/4.11.0.md | 117 +++++++++++++++- docs/release-notes/browser-sdk/7.1.0.md | 64 ++++++++- docs/release-notes/ios/4.11.0.md | 140 ++++++++++++++++++- docs/release-notes/node-bindings/1.11.0.md | 151 ++++++++++++++++++++- docs/release-notes/node-sdk/6.1.0.md | 104 +++++++++++++- docs/release-notes/wasm-bindings/1.11.0.md | 108 ++++++++++++++- 6 files changed, 654 insertions(+), 30 deletions(-) diff --git a/docs/release-notes/android/4.11.0.md b/docs/release-notes/android/4.11.0.md index 14545a130f..1ec3cf57c2 100644 --- a/docs/release-notes/android/4.11.0.md +++ b/docs/release-notes/android/4.11.0.md @@ -8,20 +8,127 @@ previous_release_tag = "android-4.10.0" ## Highlights - +- **Streams now follow your app's lifecycle automatically.** Backgrounding your app parks the shared streaming connection; foregrounding revives it. No code changes required. +- **New `catchUpToLive()`** for background jobs and cold start — a bounded, one-shot sync that brings the local store current and stops, instead of holding a live stream you are about to lose. +- **Idempotency keys on sends**, so a retried send produces the same message ID instead of a duplicate. +- **In-memory clients** via `Client.createInMemory(account, options)` for tests and ephemeral flows. +- **Group metadata migration read APIs**: `proposalsEnabled()` and `membershipCapabilities()`. + +This release contains breaking changes. Read the Breaking Changes section before upgrading. ## What's Changed - +This release focuses on connection and lifecycle behavior. The two API-client connections libxmtp used to open per client have been collapsed into one, the `isSecure` option is now unused, and the streaming transport is shared process-wide and kept in step with app foreground/background transitions via `ProcessLifecycleOwner`. + +Alongside that, sends gained caller-settable idempotency keys, `ClientOptions` reorganized its database pool settings into a `DbPoolOptions` object and gained an option to wait for registration to become visible on the network, and groups gained read APIs for the in-progress migration to proposal-based metadata updates. + +Several reliability fixes land here too: a crash on the welcome stream after a group creator left and was re-added, false-positive fork detection after member removal, missed messages in streams following a recoverable error, and replacements for several dead or rate-limited smart-contract-wallet verification RPC endpoints. + +The library now depends on `androidx.lifecycle:lifecycle-process` for stream lifecycle management. ## Breaking Changes - +### `ClientOptions.maxDbPoolSize` and `minDbPoolSize` moved into `dbPoolOptions` + +**This is a breaking change.** + +```kotlin +// Before +ClientOptions(..., maxDbPoolSize = 25u, minDbPoolSize = 5u) + +// After +ClientOptions(..., dbPoolOptions = DbPoolOptions(maxPoolSize = 25u, minPoolSize = 5u)) +``` + +### `Client.connectToSyncApiBackend(api)` has been removed + +**This is a breaking change.** + +libxmtp no longer opens a separate sync connection — one API connection now serves both paths. Use `Client.connectToApiBackend(api)`. + +### `ClientOptions.Api.isSecure` is deprecated and no longer used + +`isSecure` no longer affects the connection and will be removed in a future release. It is also no longer part of the API client cache key. Remove it from your `ClientOptions.Api` constructor calls. + +### `Group.enableProposals` moved behind an unstable namespace + +`enableProposals(force, minVersion)` is now reached through `group.unstable`, which requires an explicit opt-in: + +```kotlin +@OptIn(UnstableApi::class) +suspend fun migrate(group: Group) { + group.unstable.enableProposals() +} +``` + +This migration is one-way and irreversible — a migrated group cannot return to the legacy metadata path. When the API stabilizes it moves onto `Group` directly and the opt-in stops resolving, so you get a compile-time migration signal. ## New Features - +### Automatic stream lifecycle management + +The streaming connection is shared across every `Client` in your process. The SDK now registers a `ProcessLifecycleOwner` observer on the first client you create, parks that connection when the app backgrounds, and revives it on foreground. + +This is on by default. To manage it yourself, set the process-global flag **before creating your first client**: + +```kotlin +Client.manageStreamLifecycle = false +``` + +A process started straight into the background (a service, a broadcast receiver, a WorkManager job) seeds the backgrounded state, so a stream opened there is born parked rather than opening a live connection you cannot use. + +### `catchUpToLive(timeoutMs)` + +Bring the local store current with the network, then stop. Intended for background jobs and cold start, where holding a live stream is wasted work. + +```kotlin +val summary = client.catchUpToLive(timeoutMs = 20_000) +Log.d("XMTP", "${summary.messages} messages, ${summary.conversations} conversations, completed=${summary.completed}") +``` + +Pass `timeoutMs` to bound the run against a background budget (a WorkManager job, an FCM handler); `null` runs to the live edge. Cutting it short is safe — everything processed is persisted and a later call resumes from durable state. Check `summary.completed` before treating the counts as complete: on the deadline path it is `false` and the counts may undercount what was actually stored. + +### Idempotency keys on sends + +`SendOptions` and `MessageVisibilityOptions` accept an `idempotencyKey`. Re-sending identical content with the same key produces the same message ID and is deduplicated, so a retry after a timeout does not create a second message. Defaults to a timestamp when omitted. + +```kotlin +conversation.send( + content = "hello", + options = SendOptions(idempotencyKey = "my-stable-key"), +) +``` + +### In-memory clients + +```kotlin +val client = Client.createInMemory(account = signer, options = options) +``` + +No `.db3` file is created and no directory is touched. `dbPath` is `":memory:"`, `isInMemory` returns `true`, and `deleteLocalDatabase()`, `dropLocalDatabaseConnection()`, and `reconnectLocalDatabase()` are no-ops. `options.dbDirectory` and `options.dbEncryptionKey` are ignored. Intended for tests and ephemeral flows. + +### Wait for registration to become visible on the network + +`ClientOptions.waitForRegistrationVisible` accepts `VisibilityConfirmationOptions(quorumPercentage, quorumAbsolute, timeoutMs)`. When set, client creation waits until a quorum of nodes confirms the new identity and key package are visible before returning, which avoids racing a just-registered installation against propagation. Also available on `ffiRegisterIdentity(signatureRequest, visibilityConfirmationOptions)` if you drive registration yourself. + +### Group metadata migration read APIs + +- `group.proposalsEnabled()` — whether this group has migrated to proposal-based metadata updates. +- `group.membershipCapabilities()` — a snapshot of the group context's extension types plus, per member inbox and installation, the extension types each advertises. Use it to find which member is blocking migration. + +### Native log level control + +`Client.setLibXMTPNativeLogLevel(logLevel)` sets the level for the native logcat layer independently of the persistent file log writer. Use `FfiLogLevel.TRACE` to capture span and activity events. ## Bug Fixes - +- **Crash on the welcome stream** after a group's creator left the group and was later re-added. +- **False-positive fork detection** after a member was removed from a group. +- **Missed messages in streams** after a recoverable error: the stream resumed past envelopes it had not yet delivered. +- **Smart contract wallet verification on Gnosis Chain**, plus Ethereum, Polygon, Arbitrum, Base, and Optimism. Several default RPC endpoints were dead, rate-limited, or rejecting the full-size ERC-6492 verification call; all have been replaced. Gnosis Safe users could previously fail client creation with "Signature validation failed". +- **SCW verifier cache poisoning.** The verifier cache key is now composite, so a result for one chain can no longer be served for another. +- **Archive export dropped migrated groups.** Groups that had migrated to proposal-based metadata were silently omitted from archives instead of exported. +- **Publish rollback.** A failed publish now resets the intent to `ToPublish` instead of leaving it stranded, and swallowed intent-error causes are preserved in `SyncSummary` rather than replaced with a generic error. +- **Membership adds during identity propagation.** `MissingSequenceId` is now retryable, so adding a member whose identity update has not fully propagated succeeds on retry instead of failing. +- **Groups stuck below the protocol version floor** now recover automatically once the client upgrades past the required floor, instead of staying paused. +- **Reduced log noise** across retry, sync, welcome, and worker paths — steady-state and per-item logs moved from info/error to debug/warn, and duplicate database pool-failure spam is deduplicated across workers. Already-processed welcomes log at warn rather than error. diff --git a/docs/release-notes/browser-sdk/7.1.0.md b/docs/release-notes/browser-sdk/7.1.0.md index 1dce7ef638..ac9b082116 100644 --- a/docs/release-notes/browser-sdk/7.1.0.md +++ b/docs/release-notes/browser-sdk/7.1.0.md @@ -7,20 +7,74 @@ previous_release_version = "7.0.0" ## Highlights - +- **`@xmtp/browser-sdk` now ships from the [libxmtp](https://github.com/xmtp/libxmtp) monorepo**, released in lockstep with `@xmtp/wasm-bindings`. The package name and install instructions are unchanged. +- **Idempotency keys on sends**, so a retried send produces the same message ID instead of a duplicate. +- **The `send*` helpers take an options object** instead of a bare `isOptimistic` boolean — the one breaking change in this release. +- **Background worker tuning** via `workerConfig`, and an option to wait for a new installation to become visible on the network before using it. ## What's Changed - +Sends now carry an optional idempotency key, and the per-content-type `send*` helpers take a `SendOpts` object so optimistic delivery and the idempotency key can be set together. The same change flows through the worker bridge, so the `conversation.send*` worker actions carry `opts` instead of `isOptimistic`. + +Underneath, the two API connections each client used to open have been collapsed into one, background workers are configurable via `workerConfig`, and client creation can wait for a quorum of nodes to confirm a new installation is visible. + +This release also picks up the reliability fixes in `@xmtp/wasm-bindings` 1.11.0 — see the [Bug Fixes](#bug-fixes) section. ## Breaking Changes - +### The `send*` helpers take a `SendOpts` object instead of `isOptimistic` + +**This is a breaking change.** + +```ts +// Before +await conversation.sendText("gm", true); + +// After +await conversation.sendText("gm", { optimistic: true }); +``` + +This applies to `sendText`, `sendMarkdown`, `sendReaction`, `sendReadReceipt`, `sendReply`, `sendAttachment`, `sendRemoteAttachment`, `sendMultiRemoteAttachment`, `sendTransactionReference`, `sendWalletSendCalls`, `sendActions`, and `sendIntent`. + +`SendOpts` is exported from the package: + +```ts +import type { SendOpts } from "@xmtp/browser-sdk"; +``` + +If you drive the worker directly, the `conversation.send*` actions now take `opts?: SendOpts` in place of `isOptimistic?: boolean`. ## New Features - +### Idempotency keys on sends + +Both `SendMessageOpts` (on `conversation.send`) and `SendOpts` (on the `send*` helpers) accept `idempotencyKey`. Re-sending identical content with the same key produces the same message ID and is deduplicated, so a retry after a dropped connection does not create a second message. Defaults to a timestamp when omitted. + +```ts +await conversation.sendText("gm", { idempotencyKey: "greeting-2026-07-28" }); +``` + +### Background worker configuration + +`ClientOptions.workerConfig` tunes the background worker scheduler — a global default interval, per-worker interval and jitter overrides, and a list of workers to disable. Worker kinds: `DeviceSync`, `DisappearingMessages`, `KeyPackageCleaner`, `CommitLog`, `TaskRunner`. + +### Wait for registration to become visible on the network + +`ClientOptions.waitForRegistrationVisible` accepts `{ quorumPercentage?, quorumAbsolute?, timeoutMs? }`. When set, client creation waits until a quorum of nodes confirms the new identity and key package are visible before returning, which avoids racing a just-registered installation against propagation. ## Bug Fixes - +- **`streamMessageDeletions` never invoked its close handler.** The stream now signals close when it ends, matching the other streams. +- **Logging initialization no longer fails hard** when a `tracing` subscriber is already installed — it warns and continues. +- **Crash on the welcome stream** after a group's creator left the group and was later re-added. +- **False-positive fork detection** after a member was removed from a group. +- **Missed messages in streams** after a recoverable error: the stream resumed past envelopes it had not yet delivered. +- **Cache read returning wrong rows.** A pairwise filter in the database read cache was expanding into a cross product, so a batch lookup could return unrelated rows. +- **Intentional transaction rollbacks** are no longer reported as telemetry errors, and database disconnect errors now propagate out of workers to the supervisor instead of being swallowed. +- **Smart contract wallet verification on Gnosis Chain**, plus Ethereum, Polygon, Arbitrum, Base, and Optimism. Several default RPC endpoints were dead, rate-limited, or rejecting the full-size ERC-6492 verification call; all have been replaced. +- **SCW verifier cache poisoning.** The verifier cache key is now composite, so a result for one chain can no longer be served for another. +- **Archive export dropped migrated groups.** Groups that had migrated to proposal-based metadata were silently omitted from archives instead of exported. +- **Publish rollback.** A failed publish now resets the intent to `ToPublish` instead of leaving it stranded, and swallowed intent-error causes are preserved in the sync summary rather than replaced with a generic error. +- **Membership adds during identity propagation.** `MissingSequenceId` is now retryable, so adding a member whose identity update has not fully propagated succeeds on retry instead of failing. +- **Groups stuck below the protocol version floor** now recover automatically once the client upgrades past the required floor, instead of staying paused. +- **Reduced log noise** across retry, sync, welcome, and worker paths — steady-state and per-item logs moved from info/error to debug/warn. diff --git a/docs/release-notes/ios/4.11.0.md b/docs/release-notes/ios/4.11.0.md index 4301874d62..79569aede9 100644 --- a/docs/release-notes/ios/4.11.0.md +++ b/docs/release-notes/ios/4.11.0.md @@ -8,20 +8,150 @@ previous_release_tag = "ios-4.10.0" ## Highlights - +- **Streams now follow your app's lifecycle automatically.** Backgrounding your app parks the shared streaming connection; foregrounding revives it. No code changes required. +- **New `catchUpToLive()`** for background fetch and cold start — a bounded, one-shot sync that brings the local store current and stops, instead of holding a live stream you are about to lose. +- **Idempotency keys on sends**, so a retried send produces the same message ID instead of a duplicate. +- **A fix for a client memory leak**, which required a breaking change: `Group`, `Dm`, and `Conversation` now expose `clientInboxId` instead of a `client` back-reference. +- **In-memory clients** via `Client.createInMemory(account:options:)` for tests and ephemeral flows. + +This is a large release with several breaking changes. Read the Breaking Changes section before upgrading. ## What's Changed - +This release focuses on connection and lifecycle behavior. The two API-client connections libxmtp used to open per client have been collapsed into one, the `isSecure` option is now unused, and the streaming transport is shared process-wide and kept in step with app foreground/background transitions. + +Alongside that, sends gained caller-settable idempotency keys, clients gained in-memory and DB-pool configuration, and groups gained read APIs for the in-progress migration to proposal-based metadata updates (`proposalsEnabled()`, `membershipCapabilities()`). + +Several reliability fixes land here too: a client reference cycle that leaked memory, a crash on the welcome stream after a group creator left and was re-added, false-positive fork detection after member removal, missed messages in streams following a recoverable error, remote attachment `contentLength` being written as `-1`, and a device-sync crash on Apple platforms caused by a missing TLS crypto provider. + +To depend on this release via Swift Package Manager: + +```swift +.package( + url: "https://github.com/xmtp/libxmtp.git", + revision: "ios-4.11.0" +), +``` ## Breaking Changes - +### `Group`, `Dm`, and `Conversation` expose `clientInboxId` instead of `client` + +**This is a breaking change.** + +Conversation types no longer hold a reference back to the `Client` that created them. That reference formed a retain cycle with `Client.conversations` and leaked the client (and its database handles) for the lifetime of the process. + +```swift +// Before +let inboxId = group.client.inboxID + +// After +let inboxId = group.clientInboxId +``` + +If you were reaching through `conversation.client` to call client methods, hold your own reference to the `Client` instead. + +### `Client.connectToSyncApiBackend(api:)` has been removed + +**This is a breaking change.** + +libxmtp no longer opens a separate sync connection — one API connection now serves both paths. Use `Client.connectToApiBackend(api:)`. + +### `preferences.syncConsent()` has been removed + +**This is a breaking change.** + +It was deprecated in a prior release. Use `preferences.sync()`. + +### `ClientOptions.Api.isSecure` is deprecated and no longer used + +`isSecure` no longer affects the connection and will be removed in a future release. It is also no longer part of the API client cache key. Remove it from your `ClientOptions.Api` initializers. + +### `Group.enableProposals` moved behind an unstable namespace + +`enableProposals(force:minVersion:)` is now reached through `group.unstable` and gated behind `@_spi(Unstable)`: + +```swift +@_spi(Unstable) import XMTPiOS + +try await group.unstable.enableProposals() +``` + +This migration is one-way and irreversible — a migrated group cannot return to the legacy metadata path. When the API stabilizes it moves onto `Group` directly, and the `.unstable` form breaks at compile time so you get a migration signal. ## New Features - +### Automatic stream lifecycle management + +The streaming connection is shared across every `Client` in your process. XMTPiOS now registers app-lifecycle observers on the first client you create, parks that connection when the app backgrounds, and revives it on foreground. + +This is on by default. To manage it yourself — for example in an app extension — set the process-global flag **before creating your first client**: + +```swift +Client.manageStreamLifecycle = false +``` + +A process launched straight into the background (silent push, `BGTask`) seeds the backgrounded state, so a stream opened there is born parked rather than opening a live connection you cannot use. + +### `catchUpToLive(timeoutMs:)` + +Bring the local store current with the network, then stop. Intended for background fetch and cold start, where holding a live stream is wasted work. + +```swift +let summary = try await client.catchUpToLive(timeoutMs: 20_000) +print(summary.messages, summary.conversations, summary.completed) +``` + +Pass `timeoutMs` to bound the run against an OS background budget; `nil` runs to the live edge. Cutting it short is safe — everything processed is persisted and a later call resumes from durable state. Check `summary.completed` before treating the counts as complete: on the deadline path it is `false` and the counts may undercount what was actually stored. + +### Idempotency keys on sends + +`SendOptions` and `MessageVisibilityOptions` accept an `idempotencyKey`. Re-sending identical content with the same key produces the same message ID and is deduplicated, so a retry after a timeout does not create a second message. Defaults to a timestamp when omitted. + +```swift +try await conversation.send( + content: "hello", + options: SendOptions(idempotencyKey: "my-stable-key") +) +``` + +### In-memory clients + +```swift +let client = try await Client.createInMemory(account: signer, options: options) +``` + +No `.db3` file is created and no directory is touched. `dbPath` is `":memory:"`, `isInMemory` returns `true`, and `deleteLocalDatabase()`, `dropLocalDatabaseConnection()`, and `reconnectLocalDatabase()` are no-ops. `options.dbDirectory` and `options.dbEncryptionKey` are ignored. Intended for tests and ephemeral flows. + +### Database pool configuration + +`ClientOptions.dbPoolOptions` accepts `DbPoolOptions(maxPoolSize:minPoolSize:)` to tune the local SQLite connection pool. + +### Wait for registration to become visible on the network + +`ClientOptions.waitForRegistrationVisible` accepts `VisibilityConfirmationOptions(quorumPercentage:quorumAbsolute:timeoutMs:)`. When set, client creation waits until a quorum of nodes confirms the new identity and key package are visible before returning, which avoids racing a just-registered installation against propagation. Also available on `ffiRegisterIdentity(signatureRequest:visibilityConfirmationOptions:)` if you drive registration yourself. + +### Group metadata migration read APIs + +- `group.proposalsEnabled()` — whether this group has migrated to proposal-based metadata updates. +- `group.membershipCapabilities()` — a snapshot of the group context's extension types plus, per member inbox and installation, the extension types each advertises. Use it to find which member is blocking migration. + +### Trace-level logging and a native log level control + +`Client.LogLevel` gained `.trace`, and `Client.setLibXMTPNativeLogLevel(_:)` sets the level for the native oslog layer independently of the persistent file log writer. At `.trace`, tracing spans surface as `os_signpost` activities visible in Console.app and Instruments. ## Bug Fixes - +- **Client memory leak.** `Client` was retained indefinitely by a reference cycle with its `conversations` property. Conversation types no longer hold a client back-reference (see Breaking Changes). +- **Crash on the welcome stream** after a group's creator left the group and was later re-added. +- **False-positive fork detection** after a member was removed from a group. +- **Missed messages in streams** after a recoverable error: the stream resumed past envelopes it had not yet delivered. +- **Remote attachment `contentLength`.** `RemoteAttachmentCodec` wrote `contentLength: "-1"` when the length was unknown; it now omits the parameter, and both `RemoteAttachment` and `MultiRemoteAttachment` derive the length from the encrypted payload when it is not supplied. +- **Device sync crash on Apple platforms.** The rustls crypto provider is now installed explicitly instead of relying on a static constructor that does not run on Apple targets, which caused a "No provider set" panic in the device-sync worker's HTTP client. +- **Smart contract wallet verification on Gnosis Chain**, plus Ethereum, Polygon, Arbitrum, Base, and Optimism. Several default RPC endpoints were dead, rate-limited, or rejecting the full-size ERC-6492 verification call; all have been replaced. Gnosis Safe users could previously fail `Client.create` with "Signature validation failed". +- **SCW verifier cache poisoning.** The verifier cache key is now composite, so a result for one chain can no longer be served for another. +- **Archive export dropped migrated groups.** Groups that had migrated to proposal-based metadata were silently omitted from archives instead of exported. +- **Publish rollback.** A failed publish now resets the intent to `ToPublish` instead of leaving it stranded, and swallowed intent-error causes are preserved in `SyncSummary` rather than replaced with a generic error. +- **Membership adds during identity propagation.** `MissingSequenceId` is now retryable, so adding a member whose identity update has not fully propagated succeeds on retry instead of failing. +- **Groups stuck below the protocol version floor** now recover automatically once the client upgrades past the required floor, instead of staying paused. +- **Reduced log noise** across retry, sync, welcome, and worker paths — steady-state and per-item logs moved from info/error to debug/warn, and duplicate database pool-failure spam is deduplicated across workers. Already-processed welcomes log at warn rather than error. diff --git a/docs/release-notes/node-bindings/1.11.0.md b/docs/release-notes/node-bindings/1.11.0.md index b4f6ac5754..7b35776cd9 100644 --- a/docs/release-notes/node-bindings/1.11.0.md +++ b/docs/release-notes/node-bindings/1.11.0.md @@ -7,20 +7,161 @@ previous_release_version = "1.11.0-dev" ## Highlights - +- **`client.catchUpToLive()`** — a bounded, one-shot sync for agents that poll for new state instead of holding a live stream. +- **`client.close()`** for clean shutdown, so deleting the database file no longer races detached workers. +- **Idempotency keys on sends**, so a retried send produces the same message ID instead of a duplicate. +- **OpenTelemetry export of both spans and logs**, plus a standalone `initLogging()` you can call before creating a client. +- **Background worker tuning** via `workerConfig` — per-worker intervals, jitter, and enable/disable. +- **Single-connection database mode** for services running many clients in one process. + +This release contains breaking changes. Read the Breaking Changes section before upgrading. ## What's Changed - +The two API connections each client used to open have been collapsed into one, and `isSecure` is gone from the connection path. Logging moved onto a dedicated pipeline that can export spans and logs over OTLP, with a separate stdout level so a log shipper does not duplicate what OTLP already carries. Every exported binding function now emits an error-case span, so failures are attributable per call. + +On the messaging side, sends accept an idempotency key; the top-level `send*` helpers take an options object instead of a bare boolean; and `updateAppData` takes an options object so future fields can be added without another break. Groups gained `proposalsEnabled()` and a `conversation.unstable.enableProposals()` entry point for the in-progress migration to proposal-based metadata updates. + +The binding also switches to mimalloc as its global allocator and installs the rustls crypto provider explicitly, which fixes a device-sync panic on platforms where the previous static constructor did not run. ## Breaking Changes - +### `createClient` and `createClientWithBackend` take a `workerConfig` argument + +**This is a breaking change.** + +A `workerConfig?: WorkerConfigOptions` parameter was inserted between `deviceSyncWorkerMode` and `logOptions`. Positional callers must add it (pass `undefined` for previous behavior). The separate `syncApi` client argument has been removed from the internal wiring — one API client now serves both paths. + +### The top-level `send*` helpers take a `SendOpts` object instead of `isOptimistic` + +**This is a breaking change.** + +```ts +// Before +await conversation.sendText("hello", true); + +// After +await conversation.sendText("hello", { optimistic: true }); +``` + +This applies to `sendText`, `sendMarkdown`, `sendReaction`, `sendReadReceipt`, `sendReply`, `sendAttachment`, `sendRemoteAttachment`, `sendMultiRemoteAttachment`, `sendTransactionReference`, `sendWalletSendCalls`, `sendActions`, and `sendIntent`. + +### `updateAppData` takes an options object + +**This is a breaking change.** + +```ts +// Before +await conversation.updateAppData("payload"); + +// After +await conversation.updateAppData({ value: "payload" }); +``` + +### `registerIdentity` takes an optional second argument + +`registerIdentity(signatureRequest, visibilityConfirmationOptions?)`. Existing single-argument calls continue to work. + +### `prepareMessage` takes an optional third argument + +`prepareMessage(encodedContent, shouldPush, idempotencyKey?)`. Existing two-argument calls continue to work. + +### `getConversationById` validates the group ID + +Group IDs are now fixed-size values. A hex string that does not decode to a valid group ID is rejected with an error rather than producing a conversation handle that fails later. ## New Features - +### `client.catchUpToLive()` + +Bring the local store current with the network, then stop — a bounded, one-shot sync for agents that poll rather than hold a live stream. + +```ts +const summary = await client.catchUpToLive(); +console.log(summary.messages, summary.conversations, summary.completed); +``` + +Counts are plain numbers (not `BigInt`). Idempotent: everything processed is persisted and catch-up does not advance durable message cursors, so a later call resumes from durable state and one with nothing owed reports zero. There is no caller deadline; the run is internally bounded by capped retries with backoff and a wire watchdog. Race it against your own timer if you need a hard wall-clock bound. + +### `client.close()` + +Cancel in-flight workers and streams, then release the database connection. Idempotent. `await` this before deleting the SQLite file or dropping the client reference to avoid late log spew from detached workers firing against a dead database. + +### Idempotency keys on sends + +`SendMessageOpts` and the new `SendOpts` both accept `idempotencyKey`. Re-sending identical content with the same key produces the same message ID and is deduplicated, so a retry after a timeout does not create a second message. Defaults to a timestamp when omitted. + +### OpenTelemetry span and log export + +`LogOptions` gained: + +- `otelEndpoint` — e.g. `"http://collector:4317"`. Spans **and** `tracing` events (as correlated logs) are exported via OTLP. +- `resourceAttributes` — attributes attached to all exported telemetry, e.g. `{ "service.instance.id": "worker-7" }`. +- `stdoutLevel` — level for the stdout console layer only. Defaults to `level`. Set it to `warn` to quiet stdout below the OTLP export level so a log shipper does not duplicate logs already exported. + +Two new process-global functions: + +- `initLogging(options?)` — install the logging pipeline before any client exists. Idempotent; the first call wins, so it is safe to call this early and still pass `logOptions` to `createClient`. +- `flushTelemetry()` — flush buffered spans on graceful shutdown. No-op if telemetry was never enabled. + +### Background worker configuration + +`WorkerConfigOptions` — passed to `createClient` / `createClientWithBackend` — lets you tune the background worker scheduler: + +```ts +const workerConfig = { + defaultIntervalNs: 5_000_000_000n, + workerIntervalsNs: [{ kind: "DeviceSync", intervalNs: 30_000_000_000n }], + workerJittersNs: [{ kind: "CommitLog", jitterNs: 1_000_000_000n }], + disabledWorkers: ["DisappearingMessages"], +}; +``` + +Worker kinds: `DeviceSync`, `DisappearingMessages`, `KeyPackageCleaner`, `CommitLog`, `TaskRunner`. Omitting the whole object preserves default behavior. + +### Single-connection database mode and pool sizing + +`DbOptions` gained `useSingleConnection`. When `true`, the native database uses one connection (one file descriptor) instead of a pool, and `maxDbPoolSize`/`minDbPoolSize` are ignored. Intended for services running many clients in one process. + +### `client.waitForRegistrationVisible(options?)` + +Wait until a quorum of nodes confirms this client's identity update and key package are visible, so you do not race a just-registered installation against propagation. Configurable with `quorumPercentage`, `quorumAbsolute`, and `timeoutMs` (default 30 000 ms). Also available inline as the second argument to `registerIdentity`. + +### Group metadata migration APIs + +- `conversation.proposalsEnabled()` — whether this group has migrated to proposal-based metadata updates. +- `conversation.unstable.enableProposals({ force?, minVersion? })` — stage the migration. One-way and irreversible; hard-fails if any member's latest key package does not advertise the required proposal type. The `.unstable` namespace is the deliberate opt-in and will break at compile time when the API graduates. + +### Error-case tracing on every exported function + +Every `#[napi]` function now records a span on the error path, so a failure is attributable to a specific binding call in your traces. + +### gRPC keepalive is tunable via environment variables + +For deployments on high-latency paths where the default fast keepalive causes spurious `UNAVAILABLE` disconnects. Defaults are unchanged. + +| Variable | Default | +| --- | --- | +| `XMTP_GRPC_KEEPALIVE_INTERVAL_SECS` | 16 | +| `XMTP_GRPC_KEEPALIVE_TIMEOUT_SECS` | 10 | +| `XMTP_GRPC_TCP_KEEPALIVE_SECS` (0 disables) | 16 | +| `XMTP_GRPC_KEEPALIVE_WHILE_IDLE` | true | ## Bug Fixes - +- **Device sync panic on some platforms.** The rustls crypto provider is now installed explicitly instead of relying on a static constructor that does not run everywhere (notably Apple targets), which caused a "No provider set" panic in the device-sync worker's HTTP client. +- **Crash on the welcome stream** after a group's creator left the group and was later re-added. +- **False-positive fork detection** after a member was removed from a group. +- **Missed messages in streams** after a recoverable error: the stream resumed past envelopes it had not yet delivered. +- **Cache read returning wrong rows.** A pairwise filter in the database read cache was expanding into a cross product, so a batch lookup could return unrelated rows. +- **Database lock panics** are now opt-out (still on by default) rather than unconditional. +- **Intentional transaction rollbacks** are no longer reported as telemetry errors. +- **Database disconnect errors** now propagate out of workers to the supervisor instead of being swallowed. +- **Smart contract wallet verification on Gnosis Chain**, plus Ethereum, Polygon, Arbitrum, Base, and Optimism. Several default RPC endpoints were dead, rate-limited, or rejecting the full-size ERC-6492 verification call; all have been replaced. +- **SCW verifier cache poisoning.** The verifier cache key is now composite, so a result for one chain can no longer be served for another. +- **Archive export dropped migrated groups.** Groups that had migrated to proposal-based metadata were silently omitted from archives instead of exported. +- **Publish rollback.** A failed publish now resets the intent to `ToPublish` instead of leaving it stranded, and swallowed intent-error causes are preserved in `SyncSummary` rather than replaced with a generic error. +- **Membership adds during identity propagation.** `MissingSequenceId` is now retryable, so adding a member whose identity update has not fully propagated succeeds on retry instead of failing. +- **Groups stuck below the protocol version floor** now recover automatically once the client upgrades past the required floor, instead of staying paused. +- **`GetNodes` DNS flakes** are now retried instead of failing the call. +- **Reduced log noise** across retry, sync, welcome, and worker paths — steady-state and per-item logs moved from info/error to debug/warn, and duplicate database pool-failure spam is deduplicated across workers. diff --git a/docs/release-notes/node-sdk/6.1.0.md b/docs/release-notes/node-sdk/6.1.0.md index d1a0871b43..8363491a41 100644 --- a/docs/release-notes/node-sdk/6.1.0.md +++ b/docs/release-notes/node-sdk/6.1.0.md @@ -7,20 +7,114 @@ previous_release_version = "6.0.0" ## Highlights - +- **`@xmtp/node-sdk` now ships from the [libxmtp](https://github.com/xmtp/libxmtp) monorepo**, released in lockstep with `@xmtp/node-bindings`. The package name and install instructions are unchanged. +- **Idempotency keys on sends**, so a retried send produces the same message ID instead of a duplicate. +- **The `send*` helpers take an options object** instead of a bare `isOptimistic` boolean — the one breaking change in this release. +- **Database pool and single-connection options**, for services running many clients in one process. +- **OpenTelemetry export of both spans and logs**, plus a standalone `initLogging()` you can call before creating a client. +- **Background worker tuning** via `workerConfig` — per-worker intervals, jitter, and enable/disable. ## What's Changed - +Sends now carry an optional idempotency key, and the per-content-type `send*` helpers take a `SendOpts` object so optimistic delivery and the idempotency key can be set together. `Client.create` and `Client.build` also correctly preserve the `{ backend }` options arm when you pass codecs — previously TypeScript collapsed that union and rejected the option. + +Underneath, the two API connections each client used to open have been collapsed into one, background workers are configurable, logging can export spans and logs over OTLP with a separate stdout level, and the local database can run on a single connection instead of a pool. + +This release also picks up the reliability fixes in `@xmtp/node-bindings` 1.11.0 — see the [Bug Fixes](#bug-fixes) section. ## Breaking Changes - +### The `send*` helpers take a `SendOpts` object instead of `isOptimistic` + +**This is a breaking change.** + +```ts +// Before +await conversation.sendText("gm", true); + +// After +await conversation.sendText("gm", { optimistic: true }); +``` + +This applies to `sendText`, `sendMarkdown`, `sendReaction`, `sendReadReceipt`, `sendReply`, `sendAttachment`, `sendRemoteAttachment`, `sendMultiRemoteAttachment`, `sendTransactionReference`, `sendWalletSendCalls`, `sendActions`, and `sendIntent`. + +`SendOpts` is exported from the package: + +```ts +import type { SendOpts } from "@xmtp/node-sdk"; +``` ## New Features - +### Idempotency keys on sends + +Both `SendMessageOpts` (on `conversation.send`) and `SendOpts` (on the `send*` helpers) accept `idempotencyKey`. Re-sending identical content with the same key produces the same message ID and is deduplicated, so a retry after a timeout does not create a second message. Defaults to a timestamp when omitted. + +```ts +await conversation.sendText("gm", { idempotencyKey: "greeting-2026-07-28" }); +``` + +### Database pool configuration and single-connection mode + +`ClientOptions` gained three storage options: + +- `maxDbPoolSize` — maximum connections in the local database pool (default 25). +- `minDbPoolSize` — connections kept warm (default 5). +- `useSingleConnection` — when `true`, the native database uses one connection (one file descriptor) instead of a pool, and the pool-size options are ignored. Intended for services running many clients in one process. + +### Telemetry and logging + +- `otelEndpoint` — e.g. `"http://collector:4317"`. Spans **and** log events are exported via OTLP, so a downstream OpenTelemetry Collector can derive metrics from the spans and forward the correlated logs. +- `resourceAttributes` — attributes attached to all exported telemetry, e.g. `{ "service.instance.id": "worker-7" }`. +- `stdoutLoggingLevel` — level for the stdout console layer only. Defaults to `loggingLevel`. Set it to `LogLevel.Warn` to quiet stdout below the OTLP export level so a log shipper does not duplicate logs already exported. +- `initLogging(options?)` is now re-exported. Call it before creating any client to install the logging pipeline early; it is idempotent, so passing `logOptions`-style fields to `Client.create` afterwards is still safe. +- Call `flushTelemetry()` on graceful shutdown to flush buffered spans. + +### Background worker configuration + +`ClientOptions.workerConfig` tunes the background worker scheduler — a global default interval, per-worker interval and jitter overrides, and a list of workers to disable. Worker kinds: `DeviceSync`, `DisappearingMessages`, `KeyPackageCleaner`, `CommitLog`, `TaskRunner`. + +```ts +const client = await Client.create(signer, { + env: "production", + workerConfig: { + workerIntervalsNs: [{ kind: "DeviceSync", intervalNs: 30_000_000_000n }], + disabledWorkers: ["DisappearingMessages"], + }, +}); +``` + +`WorkerJitterOverride` is now re-exported alongside `WorkerIntervalOverride` and `WorkerConfigOptions`. + +### Wait for registration to become visible on the network + +`ClientOptions.waitForRegistrationVisible` accepts `{ quorumPercentage?, quorumAbsolute?, timeoutMs? }`. When set, client creation waits until a quorum of nodes confirms the new identity and key package are visible before returning, which avoids racing a just-registered installation against propagation. + +### gRPC keepalive is tunable via environment variables + +For deployments on high-latency paths where the default fast keepalive causes spurious `UNAVAILABLE` disconnects. Defaults are unchanged, and no code change is needed — set the variables in your process environment. + +| Variable | Default | +| --- | --- | +| `XMTP_GRPC_KEEPALIVE_INTERVAL_SECS` | 16 | +| `XMTP_GRPC_KEEPALIVE_TIMEOUT_SECS` | 10 | +| `XMTP_GRPC_TCP_KEEPALIVE_SECS` (0 disables) | 16 | +| `XMTP_GRPC_KEEPALIVE_WHILE_IDLE` | true | ## Bug Fixes - +- **`Client.create` / `Client.build` rejected the `{ backend }` option when codecs were passed.** `Omit` collapsed the options union and dropped the `backend` arm; a distributive omit now preserves it. +- **Device sync panic on some platforms.** The rustls crypto provider is now installed explicitly instead of relying on a static constructor that does not run everywhere, which caused a "No provider set" panic in the device-sync worker's HTTP client. +- **Crash on the welcome stream** after a group's creator left the group and was later re-added. +- **False-positive fork detection** after a member was removed from a group. +- **Missed messages in streams** after a recoverable error: the stream resumed past envelopes it had not yet delivered. +- **Cache read returning wrong rows.** A pairwise filter in the database read cache was expanding into a cross product, so a batch lookup could return unrelated rows. +- **Intentional transaction rollbacks** are no longer reported as telemetry errors, and database disconnect errors now propagate out of workers to the supervisor instead of being swallowed. +- **Smart contract wallet verification on Gnosis Chain**, plus Ethereum, Polygon, Arbitrum, Base, and Optimism. Several default RPC endpoints were dead, rate-limited, or rejecting the full-size ERC-6492 verification call; all have been replaced. +- **SCW verifier cache poisoning.** The verifier cache key is now composite, so a result for one chain can no longer be served for another. +- **Archive export dropped migrated groups.** Groups that had migrated to proposal-based metadata were silently omitted from archives instead of exported. +- **Publish rollback.** A failed publish now resets the intent to `ToPublish` instead of leaving it stranded, and swallowed intent-error causes are preserved in the sync summary rather than replaced with a generic error. +- **Membership adds during identity propagation.** `MissingSequenceId` is now retryable, so adding a member whose identity update has not fully propagated succeeds on retry instead of failing. +- **Groups stuck below the protocol version floor** now recover automatically once the client upgrades past the required floor, instead of staying paused. +- **`GetNodes` DNS flakes** are now retried instead of failing the call. +- **Reduced log noise** across retry, sync, welcome, and worker paths — steady-state and per-item logs moved from info/error to debug/warn, and duplicate database pool-failure spam is deduplicated across workers. diff --git a/docs/release-notes/wasm-bindings/1.11.0.md b/docs/release-notes/wasm-bindings/1.11.0.md index b2d0e69925..c059d14dd1 100644 --- a/docs/release-notes/wasm-bindings/1.11.0.md +++ b/docs/release-notes/wasm-bindings/1.11.0.md @@ -7,20 +7,118 @@ previous_release_version = "1.11.0-dev" ## Highlights - +- **Idempotency keys on sends**, so a retried send produces the same message ID instead of a duplicate. +- **The top-level `send*` helpers now take an options object** instead of a bare `optimistic` boolean. +- **`client.waitForRegistrationVisible()`** — wait until a quorum of nodes confirms a new installation is visible before using it. +- **Background worker tuning** via `workerConfig` — per-worker intervals, jitter, and enable/disable. +- **Group metadata migration APIs**: `proposalsEnabled()` and `conversation.unstable.enableProposals()`. + +This release contains breaking changes. Read the Breaking Changes section before upgrading. ## What's Changed - +The two API connections each client used to open have been collapsed into one, and `isSecure` is gone from the connection path. `createClient` and `createClientWithBackend` gained a `workerConfig` argument for tuning background workers, and `registerIdentity` can now optionally wait for the new installation to become visible across a quorum of nodes. + +On the messaging side, sends accept an idempotency key, the top-level `send*` helpers take an options object, and `updateAppData` takes an options object so future fields can be added without another break. Groups gained `proposalsEnabled()` and a `conversation.unstable.enableProposals()` entry point for the in-progress migration to proposal-based metadata updates. + +Logging initialization is now tolerant of a subscriber already installed by another crate — it warns instead of panicking. ## Breaking Changes - +### `createClient` and `createClientWithBackend` take a `workerConfig` argument + +**This is a breaking change.** + +A `workerConfig?: WorkerConfigOptions` parameter was inserted between `deviceSyncMode` and `logOptions`. Positional callers must add it (pass `undefined` for previous behavior). The separate sync API client argument has been removed from the internal wiring — one API client now serves both paths. + +### The top-level `send*` helpers take a `SendOpts` object instead of `optimistic` + +**This is a breaking change.** + +```ts +// Before +await conversation.sendText("hello", true); + +// After +await conversation.sendText("hello", { optimistic: true }); +``` + +This applies to `sendText`, `sendMarkdown`, `sendReaction`, `sendReadReceipt`, `sendReply`, `sendAttachment`, `sendRemoteAttachment`, `sendMultiRemoteAttachment`, `sendTransactionReference`, `sendWalletSendCalls`, `sendActions`, and `sendIntent`. + +### `updateAppData` takes an options object + +**This is a breaking change.** + +```ts +// Before +await conversation.updateAppData("payload"); + +// After +await conversation.updateAppData({ value: "payload" }); +``` + +### `registerIdentity` takes an optional second argument + +`registerIdentity(signatureRequest, visibilityConfirmationOptions?)`. Existing single-argument calls continue to work. + +### `prepareMessage` takes an optional third argument + +`prepareMessage(encodedContent, shouldPush, idempotencyKey?)`. Existing two-argument calls continue to work. + +### `getConversationById` validates the group ID + +Group IDs are now fixed-size values. A hex string that does not decode to a valid group ID is rejected with an error rather than producing a conversation handle that fails later. ## New Features - +### Idempotency keys on sends + +`SendMessageOpts` and the new `SendOpts` both accept `idempotencyKey`. Re-sending identical content with the same key produces the same message ID and is deduplicated, so a retry after a timeout does not create a second message. Defaults to a timestamp when omitted. + +### `client.waitForRegistrationVisible(options?)` + +Wait until this client's registration is visible on the network. For clients on the current network this falls back to checking `isRegistered`; on the decentralized network it polls nodes directly and returns once a quorum confirms both the identity-update and key-package envelopes are visible. + +Configurable with `quorumPercentage` (default 50%), `quorumAbsolute` (takes precedence when both are set), and `timeoutMs` (default 30 000 ms). Also available inline as the second argument to `registerIdentity`. + +### Background worker configuration + +`WorkerConfigOptions` — passed to `createClient` / `createClientWithBackend` — lets you tune the background worker scheduler: + +```ts +const workerConfig = { + defaultIntervalNs: 5_000_000_000n, + workerIntervalsNs: [{ kind: WorkerKind.DeviceSync, intervalNs: 30_000_000_000n }], + workerJittersNs: [{ kind: WorkerKind.CommitLog, jitterNs: 1_000_000_000n }], + disabledWorkers: [WorkerKind.DisappearingMessages], +}; +``` + +Worker kinds: `DeviceSync`, `DisappearingMessages`, `KeyPackageCleaner`, `CommitLog`, `TaskRunner`. Omitting the whole object preserves default behavior. + +### Group metadata migration APIs + +- `conversation.proposalsEnabled()` — whether this group has migrated to proposal-based metadata updates. +- `conversation.unstable.enableProposals({ force?, minVersion? })` — stage the migration. One-way and irreversible; hard-fails if any member's latest key package does not advertise the required proposal type. The `.unstable` namespace is the deliberate opt-in and will break at compile time when the API graduates. + +### Messages carry their idempotency key + +Stored messages now expose the `idempotencyKey` used when they were sent. ## Bug Fixes - +- **`streamMessageDeletions` never invoked `onClose`.** The stream now calls the close handler when it ends, matching the other streams. +- **Logging initialization no longer fails hard** when another crate has already installed a `tracing` subscriber — it logs a warning and continues instead of panicking. +- **Crash on the welcome stream** after a group's creator left the group and was later re-added. +- **False-positive fork detection** after a member was removed from a group. +- **Missed messages in streams** after a recoverable error: the stream resumed past envelopes it had not yet delivered. +- **Cache read returning wrong rows.** A pairwise filter in the database read cache was expanding into a cross product, so a batch lookup could return unrelated rows. +- **Intentional transaction rollbacks** are no longer reported as telemetry errors. +- **Database disconnect errors** now propagate out of workers to the supervisor instead of being swallowed. +- **Smart contract wallet verification on Gnosis Chain**, plus Ethereum, Polygon, Arbitrum, Base, and Optimism. Several default RPC endpoints were dead, rate-limited, or rejecting the full-size ERC-6492 verification call; all have been replaced. +- **SCW verifier cache poisoning.** The verifier cache key is now composite, so a result for one chain can no longer be served for another. +- **Archive export dropped migrated groups.** Groups that had migrated to proposal-based metadata were silently omitted from archives instead of exported. +- **Publish rollback.** A failed publish now resets the intent to `ToPublish` instead of leaving it stranded, and swallowed intent-error causes are preserved in `SyncSummary` rather than replaced with a generic error. +- **Membership adds during identity propagation.** `MissingSequenceId` is now retryable, so adding a member whose identity update has not fully propagated succeeds on retry instead of failing. +- **Groups stuck below the protocol version floor** now recover automatically once the client upgrades past the required floor, instead of staying paused. +- **Reduced log noise** across retry, sync, welcome, and worker paths — steady-state and per-item logs moved from info/error to debug/warn. From 40be7da30f37aea7f724212e74616005aa8e6adf Mon Sep 17 00:00:00 2001 From: Andrew Plaza Date: Tue, 28 Jul 2026 18:54:03 -0400 Subject: [PATCH 3/7] docs: address release-notes review feedback [skip ci] - Fix duplicated 'SDK' in the node-sdk and browser-sdk headings (scaffold template appends 'SDK' to names that already end in it). - Qualify the Android background-launch guarantee: the lifecycle seed lands a few async hops after registration (StreamLifecycle.kt), so a stream opened in that cold-start window can still open live. iOS seeds synchronously and its note is accurate as written. Co-Authored-By: Claude Fable 5 --- docs/release-notes/android/4.11.0.md | 2 +- docs/release-notes/browser-sdk/7.1.0.md | 2 +- docs/release-notes/node-sdk/6.1.0.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/release-notes/android/4.11.0.md b/docs/release-notes/android/4.11.0.md index 1ec3cf57c2..7b21519c4f 100644 --- a/docs/release-notes/android/4.11.0.md +++ b/docs/release-notes/android/4.11.0.md @@ -75,7 +75,7 @@ This is on by default. To manage it yourself, set the process-global flag **befo Client.manageStreamLifecycle = false ``` -A process started straight into the background (a service, a broadcast receiver, a WorkManager job) seeds the backgrounded state, so a stream opened there is born parked rather than opening a live connection you cannot use. +A process started straight into the background (a service, a broadcast receiver, a WorkManager job) seeds the backgrounded state, so a stream opened there is born parked rather than opening a live connection you cannot use. One caveat: the seed lands a few async hops after the first client is created, so a stream opened immediately in that cold-start window can still briefly open live. Background work that only needs data should prefer `catchUpToLive()`, which uses its own connection. ### `catchUpToLive(timeoutMs)` diff --git a/docs/release-notes/browser-sdk/7.1.0.md b/docs/release-notes/browser-sdk/7.1.0.md index ac9b082116..2263252aa1 100644 --- a/docs/release-notes/browser-sdk/7.1.0.md +++ b/docs/release-notes/browser-sdk/7.1.0.md @@ -3,7 +3,7 @@ sdk = "browser-sdk" previous_release_version = "7.0.0" --- -# Browser SDK SDK 7.1.0 +# Browser SDK 7.1.0 ## Highlights diff --git a/docs/release-notes/node-sdk/6.1.0.md b/docs/release-notes/node-sdk/6.1.0.md index 8363491a41..258f0e36ed 100644 --- a/docs/release-notes/node-sdk/6.1.0.md +++ b/docs/release-notes/node-sdk/6.1.0.md @@ -3,7 +3,7 @@ sdk = "node-sdk" previous_release_version = "6.0.0" --- -# Node SDK SDK 6.1.0 +# Node SDK 6.1.0 ## Highlights From 16edf5c30e155dd997c9142b5369999e5691e5ed Mon Sep 17 00:00:00 2001 From: Andrew Plaza Date: Wed, 29 Jul 2026 10:29:47 -0400 Subject: [PATCH 4/7] fix(nix): re-sign node bindings via signingUtils sign, not bare codesign (#3917) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Problem Since the nixpkgs bump in #3909, the darwin-arm64 node bindings build dies in `postFixup`: ``` libc++abi: terminating due to uncaught exception of type std::runtime_error: Failed to spawn codesign_allocate: No such file or directory Abort trap: 6 codesign --force --sign - "$NODE_LIB" ``` This broke the node leg of the 1.11.0 RC (run 30406484418) and the macOS "Cache all Nix Outputs" job on main (run 30414469253), and will break tonight's nightly. ## Root cause sigtool's `codesign` shells out to `codesign_allocate`, which it finds via `PATH` or the `CODESIGN_ALLOCATE` env var. The new nixpkgs darwin stdenv no longer puts cctools on `PATH`, and our bare `codesign --force --sign -` call sets neither. nixpkgs' own signing machinery (`pkgs/os-specific/darwin/signing-utils/utils.sh`) wires it explicitly: ```sh CODESIGN_ALLOCATE=@codesignAllocate@ @sigtool@/bin/codesign -f -s - ... # codesignAllocate = "${cctools}/bin/${cctools.targetPrefix}codesign_allocate" ``` ## Fix Use that machinery instead of reimplementing it: `darwin.autoSignDarwinBinariesHook` (already in our `nativeBuildInputs`) sources signingUtils into the build shell, providing a `sign` function with `CODESIGN_ALLOCATE` correctly set. Replace the bare `codesign` call with `sign "$NODE_LIB"`, and drop `darwin.sigtool` from `nativeBuildInputs` — its only purpose was providing `codesign` on `PATH` for the call being replaced. `sign` also copies the binary aside before signing and moves it back, which sidesteps the in-place re-sign-after-install_name_tool corruption that signingUtils exists to work around. Verified `nix eval .#packages.aarch64-darwin.node-bindings-darwin-arm64.drvPath` still evaluates. (The actual build is darwin-only; CI's macOS jobs are the real test.) Note: this fixes one of two failure classes in the macOS cache job — the `ring` build-script `-liconv` cross-compile failures are separate and under investigation. 🤖 Generated with [Claude Code](https://claude.com/claude-code) > [!NOTE] > ### Fix macOS re-signing of node bindings to use `signingUtils sign` instead of bare `codesign` > In [node.nix](https://github.com/xmtp/libxmtp/pull/3917/files#diff-fa9b6ba547ea5ae35048580f634e5edb6375afb0138fd6fb6e77fef6ee8c357b), the `postFixup` hook now calls the `sign` function sourced from `autoSignDarwinBinariesHook` instead of invoking `codesign --force --sign -` directly. `darwin.sigtool` is also removed from `nativeBuildInputs` as it is no longer needed. > > > > Macroscope summarized 14b47af. > > --- nix/package/node.nix | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/nix/package/node.nix b/nix/package/node.nix index c325a532b7..03e4e9d22f 100644 --- a/nix/package/node.nix +++ b/nix/package/node.nix @@ -81,11 +81,8 @@ rust.napiBuild ( ''; } // lib.optionalAttrs stdenv.hostPlatform.isDarwin { - # sigtool provides `codesign` on PATH for the postFixup re-sign. - # See https://github.com/xmtp/libxmtp/issues/3513. nativeBuildInputs = commonArgs.nativeBuildInputs ++ [ darwin.autoSignDarwinBinariesHook - darwin.sigtool ]; postFixup = '' NODE_LIB=$(echo $out/dist/bindings_node.*.node) @@ -108,10 +105,11 @@ rust.napiBuild ( install_name_tool -change "$old" "/usr/lib/$(basename "$old")" "$NODE_LIB" done - # install_name_tool invalidates the ad-hoc signature applied by - # darwin.autoSignDarwinBinariesHook; re-sign so the .node loads under - # Gatekeeper on end-user macOS hosts. - codesign --force --sign - "$NODE_LIB" + # install_name_tool invalidates the ad-hoc signature; re-sign with the + # `sign` function from signingUtils (sourced via autoSignDarwinBinariesHook), + # which sets CODESIGN_ALLOCATE — bare sigtool codesign aborts now that + # nixpkgs' darwin stdenv no longer puts cctools on PATH. See #3513. + sign "$NODE_LIB" # Assert no /nix/store references remain — guards against silent # no-ops in the rewrites above and catches the 1.10.0 regression. From aa82fb0fa6b8b3fe9bfe339e10d2373885b78a20 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 29 Jul 2026 10:49:37 -0400 Subject: [PATCH 5/7] docs: AI-suggested release notes for 1.11.0 (#3919) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## AI-Suggested Release Notes Updates Claude reviewed the release notes for **1.11.0** against the actual code changes and suggested improvements. > This PR is automatically updated on each push to `release/1.11.0`. > Review the diff to see what Claude suggests changing. 🤖 Generated with [Claude Code](https://claude.ai/code) > [!NOTE] > ### Add AI-suggested release notes for version 1.11.0 across mobile, browser, and Node SDKs > - Updates [Android 4.11.0](https://github.com/xmtp/libxmtp/pull/3919/files#diff-472922970cc3615e6a445e7252d23171550e9bad9de95b94a15d4605955c0322) and [iOS 4.11.0](https://github.com/xmtp/libxmtp/pull/3919/files#diff-a8f738dae82e2740840eb1273360a5cdf0f6b6ba9e1027d4ef8f8cbfb4da04c9) release notes to document `group.unstable.enableProposals()` under New Features, including `@UnstableApi`/`@_spi(Unstable)` opt-in requirements, one-way irreversibility, hard-fail behavior, and a `force` override. > - Rewrites [node-bindings 1.11.0](https://github.com/xmtp/libxmtp/pull/3919/files#diff-731ac95268688fc797c0fe016ffab8d09812f090d7e666e3527bbf2a66f70db4) breaking changes to clarify `createClient` and `createClientWithBackend` signature changes (dropping `isSecure`, adding `workerConfig`), and adds an opt-in stream-liveness watchdog section with environment variable controls. > - Adds an opt-in stream-liveness watchdog section to [node-sdk 6.1.0](https://github.com/xmtp/libxmtp/pull/3919/files#diff-c643b8d7b52c24277740402d6510e9711141283e6a5ab47016eaffdce8371a1d) describing idle timer and reconnect behavior configured via environment variables, read once per process. > - Updates [browser-sdk 7.1.0](https://github.com/xmtp/libxmtp/pull/3919/files#diff-f5bb8ed9ad46046f92c36b8964709af3b447eeb7ceee24b40c8a15eeabfcbd42) to specify that background worker kinds come from the `WorkerKind` enum rather than string literals. > - Removes the `Messages carry their idempotency key` section from [wasm-bindings 1.11.0](https://github.com/xmtp/libxmtp/pull/3919/files#diff-e3cbfffde03b846dadf3c9829a47e6a920ee49500d235cf8321b8b161b6a57f1). > > > >
> 📊 Macroscope summarized 81183f2. 6 files reviewed, 0 issues evaluated, 0 issues filtered, 0 comments posted > > ### 🗂️ Filtered Issues > No issues evaluated. > > >
> Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com> --- docs/release-notes/android/4.11.0.md | 26 +++++++++---------- docs/release-notes/browser-sdk/7.1.0.md | 2 +- docs/release-notes/ios/4.11.0.md | 24 +++++++++--------- docs/release-notes/node-bindings/1.11.0.md | 29 +++++++++++++++++++--- docs/release-notes/node-sdk/6.1.0.md | 15 +++++++++++ docs/release-notes/wasm-bindings/1.11.0.md | 4 --- 6 files changed, 67 insertions(+), 33 deletions(-) diff --git a/docs/release-notes/android/4.11.0.md b/docs/release-notes/android/4.11.0.md index 7b21519c4f..ddab7bc886 100644 --- a/docs/release-notes/android/4.11.0.md +++ b/docs/release-notes/android/4.11.0.md @@ -50,19 +50,6 @@ libxmtp no longer opens a separate sync connection — one API connection now se `isSecure` no longer affects the connection and will be removed in a future release. It is also no longer part of the API client cache key. Remove it from your `ClientOptions.Api` constructor calls. -### `Group.enableProposals` moved behind an unstable namespace - -`enableProposals(force, minVersion)` is now reached through `group.unstable`, which requires an explicit opt-in: - -```kotlin -@OptIn(UnstableApi::class) -suspend fun migrate(group: Group) { - group.unstable.enableProposals() -} -``` - -This migration is one-way and irreversible — a migrated group cannot return to the legacy metadata path. When the API stabilizes it moves onto `Group` directly and the opt-in stops resolving, so you get a compile-time migration signal. - ## New Features ### Automatic stream lifecycle management @@ -116,6 +103,19 @@ No `.db3` file is created and no directory is touched. `dbPath` is `":memory:"`, - `group.proposalsEnabled()` — whether this group has migrated to proposal-based metadata updates. - `group.membershipCapabilities()` — a snapshot of the group context's extension types plus, per member inbox and installation, the extension types each advertises. Use it to find which member is blocking migration. +### `group.unstable.enableProposals()` (pre-release) + +Staging the migration itself is available, but only through a deliberate opt-in: it lives on `group.unstable` behind `@UnstableApi`. + +```kotlin +@OptIn(UnstableApi::class) +suspend fun migrate(group: Group) { + group.unstable.enableProposals() +} +``` + +The migration is one-way and irreversible — a migrated group cannot return to the legacy metadata path — and it hard-fails if any member's latest key package does not advertise the required proposal type (pass `force = true` to skip that pre-flight scan). When the API stabilizes it moves onto `Group` directly and the opt-in stops resolving, so you get a compile-time migration signal. + ### Native log level control `Client.setLibXMTPNativeLogLevel(logLevel)` sets the level for the native logcat layer independently of the persistent file log writer. Use `FfiLogLevel.TRACE` to capture span and activity events. diff --git a/docs/release-notes/browser-sdk/7.1.0.md b/docs/release-notes/browser-sdk/7.1.0.md index 2263252aa1..c10a24379e 100644 --- a/docs/release-notes/browser-sdk/7.1.0.md +++ b/docs/release-notes/browser-sdk/7.1.0.md @@ -56,7 +56,7 @@ await conversation.sendText("gm", { idempotencyKey: "greeting-2026-07-28" }); ### Background worker configuration -`ClientOptions.workerConfig` tunes the background worker scheduler — a global default interval, per-worker interval and jitter overrides, and a list of workers to disable. Worker kinds: `DeviceSync`, `DisappearingMessages`, `KeyPackageCleaner`, `CommitLog`, `TaskRunner`. +`ClientOptions.workerConfig` tunes the background worker scheduler — a global default interval, per-worker interval and jitter overrides, and a list of workers to disable. Kinds come from the `WorkerKind` enum, exported from the package: `DeviceSync`, `DisappearingMessages`, `KeyPackageCleaner`, `CommitLog`, `TaskRunner`. Use the enum members (`WorkerKind.DeviceSync`), not string literals. ### Wait for registration to become visible on the network diff --git a/docs/release-notes/ios/4.11.0.md b/docs/release-notes/ios/4.11.0.md index 79569aede9..86547ca71e 100644 --- a/docs/release-notes/ios/4.11.0.md +++ b/docs/release-notes/ios/4.11.0.md @@ -67,18 +67,6 @@ It was deprecated in a prior release. Use `preferences.sync()`. `isSecure` no longer affects the connection and will be removed in a future release. It is also no longer part of the API client cache key. Remove it from your `ClientOptions.Api` initializers. -### `Group.enableProposals` moved behind an unstable namespace - -`enableProposals(force:minVersion:)` is now reached through `group.unstable` and gated behind `@_spi(Unstable)`: - -```swift -@_spi(Unstable) import XMTPiOS - -try await group.unstable.enableProposals() -``` - -This migration is one-way and irreversible — a migrated group cannot return to the legacy metadata path. When the API stabilizes it moves onto `Group` directly, and the `.unstable` form breaks at compile time so you get a migration signal. - ## New Features ### Automatic stream lifecycle management @@ -136,6 +124,18 @@ No `.db3` file is created and no directory is touched. `dbPath` is `":memory:"`, - `group.proposalsEnabled()` — whether this group has migrated to proposal-based metadata updates. - `group.membershipCapabilities()` — a snapshot of the group context's extension types plus, per member inbox and installation, the extension types each advertises. Use it to find which member is blocking migration. +### `group.unstable.enableProposals()` (pre-release) + +Staging the migration itself is available, but only through a deliberate opt-in: it lives on `group.unstable` and is gated behind `@_spi(Unstable)`. + +```swift +@_spi(Unstable) import XMTPiOS + +try await group.unstable.enableProposals() +``` + +The migration is one-way and irreversible — a migrated group cannot return to the legacy metadata path — and it hard-fails with `ProposalsNotSupported` if any member's latest key package does not advertise the required proposal type (pass `force: true` to skip that pre-flight scan). When the API stabilizes it moves onto `Group` directly, and the `.unstable` form breaks at compile time so you get a migration signal. + ### Trace-level logging and a native log level control `Client.LogLevel` gained `.trace`, and `Client.setLibXMTPNativeLogLevel(_:)` sets the level for the native oslog layer independently of the persistent file log writer. At `.trace`, tracing spans surface as `os_signpost` activities visible in Console.app and Instruments. diff --git a/docs/release-notes/node-bindings/1.11.0.md b/docs/release-notes/node-bindings/1.11.0.md index 7b35776cd9..57f1aadc32 100644 --- a/docs/release-notes/node-bindings/1.11.0.md +++ b/docs/release-notes/node-bindings/1.11.0.md @@ -26,11 +26,20 @@ The binding also switches to mimalloc as its global allocator and installs the r ## Breaking Changes -### `createClient` and `createClientWithBackend` take a `workerConfig` argument +### `createClient` drops `isSecure` and both create functions gain `workerConfig` **This is a breaking change.** -A `workerConfig?: WorkerConfigOptions` parameter was inserted between `deviceSyncWorkerMode` and `logOptions`. Positional callers must add it (pass `undefined` for previous behavior). The separate `syncApi` client argument has been removed from the internal wiring — one API client now serves both paths. +Two positional changes to `createClient`: + +- The `isSecure: boolean` third argument is **gone**. Every parameter after it shifts left by one. +- A `workerConfig?: WorkerConfigOptions` parameter was inserted between `deviceSyncWorkerMode` and `logOptions`. + +Before: `(v3Host, gatewayHost, isSecure, db, inboxId, accountIdentifier, deviceSyncWorkerMode, logOptions, …)` + +After: `(v3Host, gatewayHost, db, inboxId, accountIdentifier, deviceSyncWorkerMode, workerConfig, logOptions, …)` + +`createClientWithBackend` gains the same `workerConfig` parameter in the same position (pass `undefined` for previous behavior). The separate `syncApi` client argument has been removed from the internal wiring — one API client now serves both paths. ### The top-level `send*` helpers take a `SendOpts` object instead of `isOptimistic` @@ -147,6 +156,21 @@ For deployments on high-latency paths where the default fast keepalive causes sp | `XMTP_GRPC_TCP_KEEPALIVE_SECS` (0 disables) | 16 | | `XMTP_GRPC_KEEPALIVE_WHILE_IDLE` | true | +### Opt-in stream-liveness watchdog + +A subscription whose transport wedges open — an L7 proxy that keeps answering HTTP/2 pings while the backend subscription is gone — used to deliver neither an error nor a close, and the consumer just hung. There is now an idle timer per stream that, when nothing arrives within the timeout, tears the stream down and re-subscribes from the persisted cursor. + +It is **off by default**: with no env vars set, streams behave exactly as before. Long-lived agents can opt in. + +| Variable | Default | +| --- | --- | +| `XMTP_STREAM_WATCHDOG_ENABLED` | false | +| `XMTP_STREAM_WATCHDOG_IDLE_TIMEOUT_SECS` | 300 | +| `XMTP_STREAM_WATCHDOG_RECONNECT_BASE_SECS` | 1 | +| `XMTP_STREAM_WATCHDOG_RECONNECT_JITTER_MS` (0 disables) | 1000 | + +Because there is no server-side keepalive on this path yet, the idle timeout is deliberately long — a healthy but dormant stream will also trip and reconnect roughly once per timeout. Values are read once per process at first use. + ## Bug Fixes - **Device sync panic on some platforms.** The rustls crypto provider is now installed explicitly instead of relying on a static constructor that does not run everywhere (notably Apple targets), which caused a "No provider set" panic in the device-sync worker's HTTP client. @@ -154,7 +178,6 @@ For deployments on high-latency paths where the default fast keepalive causes sp - **False-positive fork detection** after a member was removed from a group. - **Missed messages in streams** after a recoverable error: the stream resumed past envelopes it had not yet delivered. - **Cache read returning wrong rows.** A pairwise filter in the database read cache was expanding into a cross product, so a batch lookup could return unrelated rows. -- **Database lock panics** are now opt-out (still on by default) rather than unconditional. - **Intentional transaction rollbacks** are no longer reported as telemetry errors. - **Database disconnect errors** now propagate out of workers to the supervisor instead of being swallowed. - **Smart contract wallet verification on Gnosis Chain**, plus Ethereum, Polygon, Arbitrum, Base, and Optimism. Several default RPC endpoints were dead, rate-limited, or rejecting the full-size ERC-6492 verification call; all have been replaced. diff --git a/docs/release-notes/node-sdk/6.1.0.md b/docs/release-notes/node-sdk/6.1.0.md index 258f0e36ed..46c4d63bd1 100644 --- a/docs/release-notes/node-sdk/6.1.0.md +++ b/docs/release-notes/node-sdk/6.1.0.md @@ -101,6 +101,21 @@ For deployments on high-latency paths where the default fast keepalive causes sp | `XMTP_GRPC_TCP_KEEPALIVE_SECS` (0 disables) | 16 | | `XMTP_GRPC_KEEPALIVE_WHILE_IDLE` | true | +### Opt-in stream-liveness watchdog + +A subscription whose transport wedges open — an L7 proxy that keeps answering HTTP/2 pings while the backend subscription is gone — used to deliver neither an error nor a close, and the consumer just hung. There is now an idle timer per stream that, when nothing arrives within the timeout, tears the stream down and re-subscribes from the persisted cursor. + +It is **off by default**: with no env vars set, streams behave exactly as before. Long-running agents can opt in. + +| Variable | Default | +| --- | --- | +| `XMTP_STREAM_WATCHDOG_ENABLED` | false | +| `XMTP_STREAM_WATCHDOG_IDLE_TIMEOUT_SECS` | 300 | +| `XMTP_STREAM_WATCHDOG_RECONNECT_BASE_SECS` | 1 | +| `XMTP_STREAM_WATCHDOG_RECONNECT_JITTER_MS` (0 disables) | 1000 | + +Because there is no server-side keepalive on this path yet, the idle timeout is deliberately long — a healthy but dormant stream will also trip and reconnect roughly once per timeout. Values are read once per process at first use. + ## Bug Fixes - **`Client.create` / `Client.build` rejected the `{ backend }` option when codecs were passed.** `Omit` collapsed the options union and dropped the `backend` arm; a distributive omit now preserves it. diff --git a/docs/release-notes/wasm-bindings/1.11.0.md b/docs/release-notes/wasm-bindings/1.11.0.md index c059d14dd1..c073b78511 100644 --- a/docs/release-notes/wasm-bindings/1.11.0.md +++ b/docs/release-notes/wasm-bindings/1.11.0.md @@ -101,10 +101,6 @@ Worker kinds: `DeviceSync`, `DisappearingMessages`, `KeyPackageCleaner`, `Commit - `conversation.proposalsEnabled()` — whether this group has migrated to proposal-based metadata updates. - `conversation.unstable.enableProposals({ force?, minVersion? })` — stage the migration. One-way and irreversible; hard-fails if any member's latest key package does not advertise the required proposal type. The `.unstable` namespace is the deliberate opt-in and will break at compile time when the API graduates. -### Messages carry their idempotency key - -Stored messages now expose the `idempotencyKey` used when they were sent. - ## Bug Fixes - **`streamMessageDeletions` never invoked `onClose`.** The stream now calls the close handler when it ends, matching the other streams. From 420fb2ec4216360a21cb2f7e82ee12e2027118e9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 29 Jul 2026 11:21:39 -0400 Subject: [PATCH 6/7] docs: AI-suggested release notes for 1.11.0 (#3920) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## AI-Suggested Release Notes Updates Claude reviewed the release notes for **1.11.0** against the actual code changes and suggested improvements. > This PR is automatically updated on each push to `release/1.11.0`. > Review the diff to see what Claude suggests changing. 🤖 Generated with [Claude Code](https://claude.ai/code) > [!NOTE] > ### Update 1.11.0 release notes across all SDKs with corrected bug fix descriptions > Replaces inaccurate bug fix bullets about SCW verification RPC endpoints and cache poisoning with accurate descriptions across all 1.11.0/6.1.0/7.1.0 release notes. The corrected bullets cover: dropped group invites for SCW members (retryable verification failures), reliable new-installation reconciliation as a durable task, association-state cache returning wrong rows (pairwise filter expanded into cross product), database disconnect errors propagating from background workers, and downgrade resilience for group intents. Also clarifies that `initLogging(options?)` is async and should be awaited before creating any client in the Node SDK and Node bindings notes. > > > > Macroscope summarized 3d60fc6. > > Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com> --- docs/release-notes/android/4.11.0.md | 9 ++++++--- docs/release-notes/browser-sdk/7.1.0.md | 5 +++-- docs/release-notes/ios/4.11.0.md | 7 +++++-- docs/release-notes/node-bindings/1.11.0.md | 7 ++++--- docs/release-notes/node-sdk/6.1.0.md | 7 ++++--- docs/release-notes/wasm-bindings/1.11.0.md | 5 +++-- 6 files changed, 25 insertions(+), 15 deletions(-) diff --git a/docs/release-notes/android/4.11.0.md b/docs/release-notes/android/4.11.0.md index ddab7bc886..84ef242ea5 100644 --- a/docs/release-notes/android/4.11.0.md +++ b/docs/release-notes/android/4.11.0.md @@ -22,7 +22,7 @@ This release focuses on connection and lifecycle behavior. The two API-client co Alongside that, sends gained caller-settable idempotency keys, `ClientOptions` reorganized its database pool settings into a `DbPoolOptions` object and gained an option to wait for registration to become visible on the network, and groups gained read APIs for the in-progress migration to proposal-based metadata updates. -Several reliability fixes land here too: a crash on the welcome stream after a group creator left and was re-added, false-positive fork detection after member removal, missed messages in streams following a recoverable error, and replacements for several dead or rate-limited smart-contract-wallet verification RPC endpoints. +Several reliability fixes land here too: a crash on the welcome stream after a group creator left and was re-added, false-positive fork detection after member removal, missed messages in streams following a recoverable error, and dropped group invites for members using smart contract wallets. The library now depends on `androidx.lifecycle:lifecycle-process` for stream lifecycle management. @@ -125,8 +125,11 @@ The migration is one-way and irreversible — a migrated group cannot return to - **Crash on the welcome stream** after a group's creator left the group and was later re-added. - **False-positive fork detection** after a member was removed from a group. - **Missed messages in streams** after a recoverable error: the stream resumed past envelopes it had not yet delivered. -- **Smart contract wallet verification on Gnosis Chain**, plus Ethereum, Polygon, Arbitrum, Base, and Optimism. Several default RPC endpoints were dead, rate-limited, or rejecting the full-size ERC-6492 verification call; all have been replaced. Gnosis Safe users could previously fail client creation with "Signature validation failed". -- **SCW verifier cache poisoning.** The verifier cache key is now composite, so a result for one chain can no longer be served for another. +- **Dropped group invites for members using smart contract wallets.** A transient failure while verifying an ERC-1271/6492 signature was classified as permanent, so the welcome cursor advanced and the invite was skipped for good. Those failures are now retryable and the welcome is reprocessed on the next sync. +- **New installations reliably added to your existing groups.** Reconciliation after a welcome now runs as a durable, retried task instead of one inline attempt whose per-group failures were swallowed, so a lost race no longer permanently skips a group. +- **Association-state cache returning wrong rows.** A pairwise filter in the database read cache was expanding into a cross product, so a batch inbox/installation lookup could return unrelated rows. +- **Database disconnect errors** now propagate out of background workers to the supervisor instead of being swallowed. +- **Downgrade resilience for group intents.** Intent rows written by a newer SDK version are now skipped rather than failing every intent query for that group, so downgrading to this version no longer wedges outbound messages in an affected group. - **Archive export dropped migrated groups.** Groups that had migrated to proposal-based metadata were silently omitted from archives instead of exported. - **Publish rollback.** A failed publish now resets the intent to `ToPublish` instead of leaving it stranded, and swallowed intent-error causes are preserved in `SyncSummary` rather than replaced with a generic error. - **Membership adds during identity propagation.** `MissingSequenceId` is now retryable, so adding a member whose identity update has not fully propagated succeeds on retry instead of failing. diff --git a/docs/release-notes/browser-sdk/7.1.0.md b/docs/release-notes/browser-sdk/7.1.0.md index c10a24379e..b5c37e7fa3 100644 --- a/docs/release-notes/browser-sdk/7.1.0.md +++ b/docs/release-notes/browser-sdk/7.1.0.md @@ -71,8 +71,9 @@ await conversation.sendText("gm", { idempotencyKey: "greeting-2026-07-28" }); - **Missed messages in streams** after a recoverable error: the stream resumed past envelopes it had not yet delivered. - **Cache read returning wrong rows.** A pairwise filter in the database read cache was expanding into a cross product, so a batch lookup could return unrelated rows. - **Intentional transaction rollbacks** are no longer reported as telemetry errors, and database disconnect errors now propagate out of workers to the supervisor instead of being swallowed. -- **Smart contract wallet verification on Gnosis Chain**, plus Ethereum, Polygon, Arbitrum, Base, and Optimism. Several default RPC endpoints were dead, rate-limited, or rejecting the full-size ERC-6492 verification call; all have been replaced. -- **SCW verifier cache poisoning.** The verifier cache key is now composite, so a result for one chain can no longer be served for another. +- **Dropped group invites for members using smart contract wallets.** A transient failure while verifying an ERC-1271/6492 signature was classified as permanent, so the welcome cursor advanced and the invite was skipped for good. Those failures are now retryable and the welcome is reprocessed on the next sync. +- **New installations reliably added to existing groups.** Reconciliation after a welcome now runs as a durable, retried task instead of one inline attempt whose per-group failures were swallowed, so a lost race no longer permanently skips a group. +- **Downgrade resilience for group intents.** Intent rows written by a newer version are now skipped rather than failing every intent query for that group, so downgrading to this version no longer wedges outbound messages in an affected group. - **Archive export dropped migrated groups.** Groups that had migrated to proposal-based metadata were silently omitted from archives instead of exported. - **Publish rollback.** A failed publish now resets the intent to `ToPublish` instead of leaving it stranded, and swallowed intent-error causes are preserved in the sync summary rather than replaced with a generic error. - **Membership adds during identity propagation.** `MissingSequenceId` is now retryable, so adding a member whose identity update has not fully propagated succeeds on retry instead of failing. diff --git a/docs/release-notes/ios/4.11.0.md b/docs/release-notes/ios/4.11.0.md index 86547ca71e..35ca2a3da1 100644 --- a/docs/release-notes/ios/4.11.0.md +++ b/docs/release-notes/ios/4.11.0.md @@ -148,8 +148,11 @@ The migration is one-way and irreversible — a migrated group cannot return to - **Missed messages in streams** after a recoverable error: the stream resumed past envelopes it had not yet delivered. - **Remote attachment `contentLength`.** `RemoteAttachmentCodec` wrote `contentLength: "-1"` when the length was unknown; it now omits the parameter, and both `RemoteAttachment` and `MultiRemoteAttachment` derive the length from the encrypted payload when it is not supplied. - **Device sync crash on Apple platforms.** The rustls crypto provider is now installed explicitly instead of relying on a static constructor that does not run on Apple targets, which caused a "No provider set" panic in the device-sync worker's HTTP client. -- **Smart contract wallet verification on Gnosis Chain**, plus Ethereum, Polygon, Arbitrum, Base, and Optimism. Several default RPC endpoints were dead, rate-limited, or rejecting the full-size ERC-6492 verification call; all have been replaced. Gnosis Safe users could previously fail `Client.create` with "Signature validation failed". -- **SCW verifier cache poisoning.** The verifier cache key is now composite, so a result for one chain can no longer be served for another. +- **Dropped group invites for members using smart contract wallets.** A transient failure while verifying an ERC-1271/6492 signature was classified as permanent, so the welcome cursor advanced and the invite was skipped for good. Those failures are now retryable and the welcome is reprocessed on the next sync. +- **New installations reliably added to your existing groups.** Reconciliation after a welcome now runs as a durable, retried task instead of one inline attempt whose per-group failures were swallowed, so a lost race no longer permanently skips a group. +- **Association-state cache returning wrong rows.** A pairwise filter in the database read cache was expanding into a cross product, so a batch inbox/installation lookup could return unrelated rows. +- **Database disconnect errors** now propagate out of background workers to the supervisor instead of being swallowed. +- **Downgrade resilience for group intents.** Intent rows written by a newer SDK version are now skipped rather than failing every intent query for that group, so downgrading to this version no longer wedges outbound messages in an affected group. - **Archive export dropped migrated groups.** Groups that had migrated to proposal-based metadata were silently omitted from archives instead of exported. - **Publish rollback.** A failed publish now resets the intent to `ToPublish` instead of leaving it stranded, and swallowed intent-error causes are preserved in `SyncSummary` rather than replaced with a generic error. - **Membership adds during identity propagation.** `MissingSequenceId` is now retryable, so adding a member whose identity update has not fully propagated succeeds on retry instead of failing. diff --git a/docs/release-notes/node-bindings/1.11.0.md b/docs/release-notes/node-bindings/1.11.0.md index 57f1aadc32..d8c5926647 100644 --- a/docs/release-notes/node-bindings/1.11.0.md +++ b/docs/release-notes/node-bindings/1.11.0.md @@ -110,7 +110,7 @@ Cancel in-flight workers and streams, then release the database connection. Idem Two new process-global functions: -- `initLogging(options?)` — install the logging pipeline before any client exists. Idempotent; the first call wins, so it is safe to call this early and still pass `logOptions` to `createClient`. +- `initLogging(options?)` — install the logging pipeline before any client exists. It is `async`, so `await` it. Idempotent; the first call wins, so it is safe to call this early and still pass `logOptions` to `createClient`. - `flushTelemetry()` — flush buffered spans on graceful shutdown. No-op if telemetry was never enabled. ### Background worker configuration @@ -180,8 +180,9 @@ Because there is no server-side keepalive on this path yet, the idle timeout is - **Cache read returning wrong rows.** A pairwise filter in the database read cache was expanding into a cross product, so a batch lookup could return unrelated rows. - **Intentional transaction rollbacks** are no longer reported as telemetry errors. - **Database disconnect errors** now propagate out of workers to the supervisor instead of being swallowed. -- **Smart contract wallet verification on Gnosis Chain**, plus Ethereum, Polygon, Arbitrum, Base, and Optimism. Several default RPC endpoints were dead, rate-limited, or rejecting the full-size ERC-6492 verification call; all have been replaced. -- **SCW verifier cache poisoning.** The verifier cache key is now composite, so a result for one chain can no longer be served for another. +- **Dropped group invites for members using smart contract wallets.** A transient failure while verifying an ERC-1271/6492 signature was classified as permanent, so the welcome cursor advanced and the invite was skipped for good. Those failures are now retryable and the welcome is reprocessed on the next sync. +- **New installations reliably added to existing groups.** Reconciliation after a welcome now runs as a durable, retried task instead of one inline attempt whose per-group failures were swallowed, so a lost race no longer permanently skips a group. +- **Downgrade resilience for group intents.** Intent rows written by a newer version are now skipped rather than failing every intent query for that group, so downgrading to this version no longer wedges outbound messages in an affected group. - **Archive export dropped migrated groups.** Groups that had migrated to proposal-based metadata were silently omitted from archives instead of exported. - **Publish rollback.** A failed publish now resets the intent to `ToPublish` instead of leaving it stranded, and swallowed intent-error causes are preserved in `SyncSummary` rather than replaced with a generic error. - **Membership adds during identity propagation.** `MissingSequenceId` is now retryable, so adding a member whose identity update has not fully propagated succeeds on retry instead of failing. diff --git a/docs/release-notes/node-sdk/6.1.0.md b/docs/release-notes/node-sdk/6.1.0.md index 46c4d63bd1..c33419e58c 100644 --- a/docs/release-notes/node-sdk/6.1.0.md +++ b/docs/release-notes/node-sdk/6.1.0.md @@ -67,7 +67,7 @@ await conversation.sendText("gm", { idempotencyKey: "greeting-2026-07-28" }); - `otelEndpoint` — e.g. `"http://collector:4317"`. Spans **and** log events are exported via OTLP, so a downstream OpenTelemetry Collector can derive metrics from the spans and forward the correlated logs. - `resourceAttributes` — attributes attached to all exported telemetry, e.g. `{ "service.instance.id": "worker-7" }`. - `stdoutLoggingLevel` — level for the stdout console layer only. Defaults to `loggingLevel`. Set it to `LogLevel.Warn` to quiet stdout below the OTLP export level so a log shipper does not duplicate logs already exported. -- `initLogging(options?)` is now re-exported. Call it before creating any client to install the logging pipeline early; it is idempotent, so passing `logOptions`-style fields to `Client.create` afterwards is still safe. +- `initLogging(options?)` is now re-exported. `await` it before creating any client to install the logging pipeline early; it is idempotent, so passing `logOptions`-style fields to `Client.create` afterwards is still safe. - Call `flushTelemetry()` on graceful shutdown to flush buffered spans. ### Background worker configuration @@ -125,8 +125,9 @@ Because there is no server-side keepalive on this path yet, the idle timeout is - **Missed messages in streams** after a recoverable error: the stream resumed past envelopes it had not yet delivered. - **Cache read returning wrong rows.** A pairwise filter in the database read cache was expanding into a cross product, so a batch lookup could return unrelated rows. - **Intentional transaction rollbacks** are no longer reported as telemetry errors, and database disconnect errors now propagate out of workers to the supervisor instead of being swallowed. -- **Smart contract wallet verification on Gnosis Chain**, plus Ethereum, Polygon, Arbitrum, Base, and Optimism. Several default RPC endpoints were dead, rate-limited, or rejecting the full-size ERC-6492 verification call; all have been replaced. -- **SCW verifier cache poisoning.** The verifier cache key is now composite, so a result for one chain can no longer be served for another. +- **Dropped group invites for members using smart contract wallets.** A transient failure while verifying an ERC-1271/6492 signature was classified as permanent, so the welcome cursor advanced and the invite was skipped for good. Those failures are now retryable and the welcome is reprocessed on the next sync. +- **New installations reliably added to existing groups.** Reconciliation after a welcome now runs as a durable, retried task instead of one inline attempt whose per-group failures were swallowed, so a lost race no longer permanently skips a group. +- **Downgrade resilience for group intents.** Intent rows written by a newer version are now skipped rather than failing every intent query for that group, so downgrading to this version no longer wedges outbound messages in an affected group. - **Archive export dropped migrated groups.** Groups that had migrated to proposal-based metadata were silently omitted from archives instead of exported. - **Publish rollback.** A failed publish now resets the intent to `ToPublish` instead of leaving it stranded, and swallowed intent-error causes are preserved in the sync summary rather than replaced with a generic error. - **Membership adds during identity propagation.** `MissingSequenceId` is now retryable, so adding a member whose identity update has not fully propagated succeeds on retry instead of failing. diff --git a/docs/release-notes/wasm-bindings/1.11.0.md b/docs/release-notes/wasm-bindings/1.11.0.md index c073b78511..72901eb68d 100644 --- a/docs/release-notes/wasm-bindings/1.11.0.md +++ b/docs/release-notes/wasm-bindings/1.11.0.md @@ -111,8 +111,9 @@ Worker kinds: `DeviceSync`, `DisappearingMessages`, `KeyPackageCleaner`, `Commit - **Cache read returning wrong rows.** A pairwise filter in the database read cache was expanding into a cross product, so a batch lookup could return unrelated rows. - **Intentional transaction rollbacks** are no longer reported as telemetry errors. - **Database disconnect errors** now propagate out of workers to the supervisor instead of being swallowed. -- **Smart contract wallet verification on Gnosis Chain**, plus Ethereum, Polygon, Arbitrum, Base, and Optimism. Several default RPC endpoints were dead, rate-limited, or rejecting the full-size ERC-6492 verification call; all have been replaced. -- **SCW verifier cache poisoning.** The verifier cache key is now composite, so a result for one chain can no longer be served for another. +- **Dropped group invites for members using smart contract wallets.** A transient failure while verifying an ERC-1271/6492 signature was classified as permanent, so the welcome cursor advanced and the invite was skipped for good. Those failures are now retryable and the welcome is reprocessed on the next sync. +- **New installations reliably added to existing groups.** Reconciliation after a welcome now runs as a durable, retried task instead of one inline attempt whose per-group failures were swallowed, so a lost race no longer permanently skips a group. +- **Downgrade resilience for group intents.** Intent rows written by a newer version are now skipped rather than failing every intent query for that group, so downgrading to this version no longer wedges outbound messages in an affected group. - **Archive export dropped migrated groups.** Groups that had migrated to proposal-based metadata were silently omitted from archives instead of exported. - **Publish rollback.** A failed publish now resets the intent to `ToPublish` instead of leaving it stranded, and swallowed intent-error causes are preserved in `SyncSummary` rather than replaced with a generic error. - **Membership adds during identity propagation.** `MissingSequenceId` is now retryable, so adding a member whose identity update has not fully propagated succeeds on retry instead of failing. From 6a8e969785c62e0923cf3d8531aff4eaac3c91b7 Mon Sep 17 00:00:00 2001 From: Andrew Plaza Date: Wed, 29 Jul 2026 11:24:51 -0400 Subject: [PATCH 7/7] fix(ci): don't retrigger release-notes drafting on notes-only pushes [skip ci] --- .github/workflows/release-notes.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/release-notes.yml b/.github/workflows/release-notes.yml index ceba8e49c2..633167a1d6 100644 --- a/.github/workflows/release-notes.yml +++ b/.github/workflows/release-notes.yml @@ -3,6 +3,10 @@ name: Release Notes on: push: branches: ["release/**"] + # Notes-only pushes (e.g. merging the drafter's own PR) must not + # retrigger drafting — that redrafts the redraft, forever (#3919/#3920). + paths-ignore: + - "docs/release-notes/**" workflow_dispatch: inputs: release-branch: