_Common capabilities shared by all Ditto Edge SDK platforms. Use the platform filters to show the common and SDK-specific capabilities for a certain SDK.Key takeaway: Ditto SDK 5.2 gives apps faster troubleshooting, more durable wireless network connectivity, improved memory and storage usage, and data encryption for apps built in Swift and Kotlin.The new Because it’s a query, you can trigger it from wherever your app already runs Ditto queries: an admin screen, a remote command from your backend, or a debug socket.Bundles capture more than before. They now include the device’s recent request history (
Ditto SDK 5.2 - Troubleshoot Faster. Operate Better.
Ditto SDK 5.2 includes:- 71 platform-level improvements that benefit every application.
- 100 SDK-specific refinements across our language SDKs.
- Use the SDK Platforms filter and the Table of Contents on the right to learn more.
Release Overview
Most upgrades need no code changes. See Upgrading to 5.2 for the behavior changes to review and the rollback guidance.
Release Summary
1. Troubleshooting: Find and Fix Problems Faster
- Use
EXECUTE FUNCTION system_request_support_bundle()to remotely generate a support bundle. - Support bundles now include request history, index data, and runtime traces.
- The
system:sync_degradationscollection shows which documents aren’t syncing. - A preview DQL function traces how a change moves through the mesh.
- Wi-Fi Aware meshes of three or more Android devices no longer split into isolated pairs.
- Unhealthy Wi-Fi Aware connections refresh instead of restarting the whole stack.
- Bluetooth Low Energy (BLE) on Android no longer crashes when its high-speed data channel can’t open.
- Apple Wireless Direct Link (AWDL) recovers on its own instead of staying down until the app restarts.
- Devices resume sync sooner after a network blip because reconnecting to a recently seen peer skips redundant handshake work.
- Connections to the cloud that have quietly stopped working now close instead of lingering. Native WebSocket connections use TCP keepalive by default.
- Store observers, the live queries behind your UI, use less memory over large result sets and no longer re-run after writes to collections they don’t watch.
- Ditto’s database files no longer grow unchecked on devices that run for months.
- Documents that exceed the hard size limit no longer stall sync for other devices.
- Swift and Kotlin apps can encrypt the local store with their own 32-byte key.
- Every SDK reports typed encryption errors when a store fails to open, and Swift and Kotlin add
close()anddeleteStore()helpers for wiping a device cleanly.
- Every SDK can now set system parameters in
DittoConfig. Ditto applies them before it starts, so startup-only parameters can be set and there’s no window where Ditto runs on the defaults. See Apply System Parameters Before Ditto Starts.
- Most upgrades need no code changes. SDK 5.2 is backward compatible: apps on 5.2 sync with apps on earlier versions.
- Review the behavior changes if you set NGN system parameters, observe virtual collections, or ship Flutter desktop, C# Windows, or Kotlin apps that attach metadata.
- You may see new deprecation warnings after upgrading. See New warnings after upgrading.
- Downgrading to 5.1 needs no special steps. If you need to roll back to 5.0 or v4, see the rollback guidance below first.
1. Troubleshooting: Find and Fix Problems Faster
In edge computing, it’s tough to troubleshoot and fix a device that misbehaves in the field. The device might have spotty connectivity, be far away, or be constantly in use. SDK 5.2 makes it easier to remotely pull and analyze troubleshooting information from a device.Generate and query support bundles with DQL
A support bundle is a package of logs, configuration, and metrics that show how one device is running and using Ditto. It’s the first troubleshooting step for every customer and the Ditto support team when something goes wrong. Previously, you had to pull the bundle from inside your app. In SDK 5.2, you can request one from any Small Peer with a single DQL statement:DQL
system:support_bundles virtual collection lists every bundle on the device, so you can find and manage bundles with a query:DQL
system:request_history entries) and a snapshot of its index metadata (the first 10,000 system:indexes entries). Android developers also get a support bundle screen in DittoAndroidTools to request, view, and export bundles.The system_request_support_bundle() function isn’t available in the JavaScript Web SDK.See which documents aren’t syncing
“Why hasn’t this document synced?” is one of the most common questions our support team hears. The newsystem:sync_degradations virtual collection answers it directly. It lists documents whose sync is degraded, so you can find a stuck document before a user reports it.For deeper diagnosis, the preview system_trace_sync_propagation() DQL function traces how a document change propagates through the mesh. Tracing through a Big Peer isn’t supported yet.More metrics, quieter logs
system:metrics adds query-engine metrics (ditto.query.requests, ditto.query.failed, and ditto.query.execution_time), ten ditto.network.dsoq.* counters for Ditto Sync over QUIC (DSOQ) failures, and transport health values such as peer discovery counts, AWDL status, payload byte totals, and last-activity timestamps. Small Peer info reports disk usage per file in the ditto_store directory.Logs are more useful and less noisy. INFO logs record observer and subscription lifecycle events. Retryable UDP lookup failures and repetitive Streams session messages drop to debug level. On-disk logs no longer lose entries when log files rotate in quick succession.2. Wireless Stability: Bluetooth, Wi-Fi Aware, Apple P2P Wi-Fi
Peer-to-peer radios are what let Ditto apps keep working when the network is gone. Three transports are relevant to most phones and tablets:- Bluetooth Low Energy (BLE) is a short-range, low-power radio in nearly every phone. It’s always available, but it has less bandwidth.
- Wi-Fi Aware lets Android devices connect directly over Wi-Fi without a router or access point in the middle. It has more bandwidth for data transfer.
- Apple Wireless Direct Link (AWDL) is Apple’s version of Wi-Fi Aware. It’s the technology behind AirDrop, and it lets Ditto sync between nearby Apple devices without a router.
Bluetooth LE on Android handles channel failures gracefully
Ditto moves Bluetooth LE sync traffic over a high-speed data channel (an L2CAP channel) when one is available. Android apps no longer crash when that channel can’t be opened or closed, for example when Bluetooth is turned off while peers are connecting. Sync continues over the standard Bluetooth LE path (GATT). Android apps also no longer crash when a transport leaves a Java exception pending on a native thread.Wi-Fi Aware has better mesh formation and stability
Wi-Fi Aware gets the most fixes in this release:- Meshes of three or more devices no longer split into isolated pairs that stop syncing with the rest of the mesh.
- When a connection looks unhealthy, Ditto refreshes its discovery sessions instead of restarting the whole Wi-Fi Aware stack.
- A subsystem restart no longer tears down a connection while a data path is carrying sync traffic, and rapid transport restarts no longer disrupt active connections.
- Two peers that discover each other at the same time no longer create competing connections that fail during startup.
- Connections no longer stall during concurrent peer discovery and connection updates, and Android network resources are released after peers disconnect, which prevents failures during repeated reconnects.
transports_wifi_aware_scan_yield_enabled system parameter lets Wi-Fi Aware briefly release its data paths so a disconnected device or weak link can scan for a stronger access point.Apple peer-to-peer Wi-Fi recovers on its own
AWDL connectivity now recovers automatically after temporary network setup failures instead of staying unavailable until the app restarts. Ditto runs more consistently on devices like an iPad mounted in a kiosk that rarely restarts.These fixes apply to every SDK that runs on the affected platform: Kotlin, Flutter, React Native, and C# on Android, and Swift, Flutter, React Native, C#, Java, Rust, and Go on Apple platforms.3. Connections: Faster Reconnects and Stalled-Connection Detection
A device can only sync while it has a live connection to another peer. Wireless links drop all the time in the real world: a forklift drives behind racking, a phone goes into a pocket, or a router reboots. SDK 5.2 shortens the time it takes to get back to syncing after an interruption, and it makes it harder for a dead connection to linger unnoticed.Faster reconnects to peers you’ve seen before
Before two devices can sync, they go through a handshake. Each device verifies the other’s identity, and the two agree on encryption keys. It’s essential, but it takes time, especially on a slow or lossy link. In 5.2, reconnecting to a recently seen peer over the TCP/TLS LAN transport can use an abbreviated TLS handshake, which skips redundant negotiation. Over the UDP/NGN LAN transport, Ditto resumes the encrypted session instead of renegotiating it. Both cut the time between a network blip and the first synced change.Starting up is smoother too. LAN peer discovery on Linux and Android causes smaller traffic spikes when many Small Peers start at the same time, such as when a store opens for the day or a fleet of vehicles powers on. Default Apple and Windows discovery is unchanged.Stalled connections are detected
Some connections don’t drop cleanly. The operating system thinks they are open, but nothing moves across them. This can happen when the device on the other side vanishes, or a timer on one end stops firing. A stalled connection is worse than a dropped one, because a device keeps waiting on it instead of opening a new, healthy connection.WebSocket is the transport a device uses to reach Ditto’s cloud and other servers. Native WebSocket connections now turn on TCP keepalive by default, on both client and server sockets. Keepalive is a kernel-level check: if a connection sits idle, the operating system sends small probes, and if the probes go unanswered, the device closes the connection so Ditto can reconnect. The defaults are:Ditto’s own protocol-level keepalive is still the first line of defense. TCP keepalive is a backstop for when Ditto’s timers or the runtime itself has stalled. Tune it with the
transports_websocket_so_keepalive_* system parameters. Browser and Wasm clients aren’t affected. On Linux and Android, opt-in *_so_keepalive_user_timeout_enabled parameters also close connections whose sent data goes unacknowledged for too long.Stale state is cleaned up too. The presence graph drops disconnected client connections after a reconnect or permanent failure, repeated mDNS address refreshes no longer leave stale TCP connections behind, and TLS peers with mismatched certificate identities are no longer retried over and over during discovery.Connection limits are enforced correctly
mesh_chooser_max_wlan_connections now counts inbound and outbound LAN connections together, so a peer holds no more connections than you set. Previously, a peer could hold about twice the configured number. When mesh_chooser_at_capacity_action is DropOldest or DropNewest, Ditto now waits until the incoming peer finishes connecting before it disconnects an existing peer, so a peer that never completes its handshake can’t push out a healthy one.4. Memory and Storage Health
Edge devices share a fixed amount of memory and disk among every app, and they often run for months without a restart. Both resources can leak slowly if you’re not careful. SDK 5.2 trims what Ditto holds in memory, skips work that produces no new result, and keeps the on-disk footprint from growing without bound. Existing apps get these gains without code changes.Live queries hold less memory
A store observer is a live query. You register a query once, and Ditto calls you back with fresh results whenever the matching data changes. Most app screens are built on observers, and long-lived observers over large result sets are where memory adds up. In 5.2:- Query results stay compact until you read them.
- Observers no longer keep observed documents fully decoded between updates, and reading a document no longer pins its decoded form in memory.
- Observers whose queries can’t be updated incrementally, such as projections or index-backed lookups, no longer keep a copy of the previous result set.
- Peak memory is lower while an observer builds its callback result.
Less wasted work
Store observers are also more efficient. A store observer runs a query against a collection, and then it updates your app whenever that query’s result changes. Previously, any local write would fire every observer on the device, even if the write touched a collection the observer didn’t watch. A write toorders would make an observer on products check for updates, only to find nothing new.Now, an observer only runs when the collection it watches is written to. Your callbacks fire exactly as before. Devices spend far less time checking collections unnecessarily. Observers on system collections now fire when the data changes instead of polling on a timer, which removes a steady source of background work on idle devices.Bounded storage on long-running devices
Ditto’s local data store is built on SQLite, which keeps a write-ahead log (WAL): a file where new writes land before they’re folded into the main database. On devices that run for weeks or months, that log could grow without bound. Small Peers now keep the WAL in check and reclaim free disk space automatically. If the log keeps growing anyway, Ditto logs a warning that usually points to a long-held read transaction, which gives you a lead to follow.Oversized documents no longer stall sync
Ditto documents have a hard size limit. When a Small Peer receives a replication diff that would push a document over that limit, it now drops that diff. Other documents keep syncing. The oversized document gets no more updates until it’s evicted and synced again. Evicting removes a document from the local store without deleting it anywhere else. Watch for it in your diagnostics if your app stores large documents.5. Encryption at Rest for the SDK Data Store - Swift & Kotlin
Many Ditto apps run on devices that can be lost or stolen: handhelds, tablets, and kiosks. For some customers, that’s a reportable incident. Encryption at rest matters most to customers that handle sensitive data in healthcare, government, or finance, where apps carry personally identifiable information, military operations, or payment details.Encryption at rest means Ditto’s documents on disk are unreadable without a key. Copying the app’s files or pulling the storage from a device doesn’t expose your data. SDK 5.2 adds application-level encryption for the local data store to the Swift and Kotlin SDKs, with a key your app generates and keeps. Support will expand to additional SDKs in the future.Encrypt the local store with your own key
Swift and Kotlin apps can now encrypt the local store at rest with a raw 32-byte key throughDittoConfig.encryptionKey. You generate and manage the key, and Ditto uses it to protect documents on disk. Keep the key in your own secure storage, never next to the store it protects.Typed encryption errors
Every SDK now reports a typed error when a store fails to open for an encryption reason: key required, key unexpected, key invalid, unsupported scheme, or metadata corrupt. Your app can tell a missing key from a corrupted store and respond to each one instead of treating every failure the same way. The older passphrase error cases are deprecated.Wipe a device cleanly
Swift addsDitto.close() and Ditto.deleteStore(databaseID:persistenceDirectory:). Kotlin adds DittoFactory.deleteStore(databaseId, persistenceDirectory). Flutter’s Authenticator.logout accepts a cleanup callback that runs after logout completes. Together with encryption at rest, they make it simpler to remove local data on logout or when a device changes hands.Apply System Parameters Before Ditto Starts
System parameters determine how Ditto behaves on a device. They include settings like connection limits, keepalive timers, and feature switches. Most apps use the default system parameters. Some deployments need to change them, for example a large mesh that needs a higher connection limit.Before 5.2, you used a DQLALTER SYSTEM SET statement to change system parameters after Ditto.open() returned. Some parameters are only read during Ditto.open(), and they could never be changed by a later ALTER SYSTEM call.In 5.2, you can pass system parameters into DittoConfig. Ditto applies them before it starts, and three things become reliable:- Startup-only parameters can be set. Before, they could not be changed from the default.
- There is no window where Ditto unexpectedly runs on the defaults between
open()and yourALTER SYSTEMcall. - Invalid names or values fail at
open()instead of being silently ignored, so a typo shows up on the first run.
ALTER SYSTEM still works the same way at run time. The Rust SDK has had system_parameters since SDK 5.0. SDK 5.2 brings this feature to every other SDK.Upgrading to 5.2
To upgrade, bump your dependency to 5.2.0. Most applications need no code changes. SDK 5.2 is backward compatible: apps on 5.2 sync with apps on earlier versions, so you can roll it out gradually.Behavior changes to plan for
Review these changes if they apply to your app:startSync()fails with a configuration error ifngn_sync_enabledis set withoutngn_data_streams_enabled. Previously the setting was silently ignored.- A Small Peer drops a replication diff that would push a document over the hard size limit. That document gets no more updates until it’s evicted and synced again.
- Most virtual-collection observers fire only when data changes. Code that relied on periodic callbacks should query on its own schedule instead.
- The SQLite write-ahead log size monitor and automatic free-space reclaim are on by default.
- Flutter desktop: the default persistence directory moves to the application support directory. Existing databases in the previous location are still found when
DittoConfig.persistenceDirectoryisnull. - C#: .NET apps that target x86, ARM, or ARM64 Windows fail at compile time. Set
DittoNativeWinSkipArchitectureValidationtotrueto bypass the check. - Kotlin:
DittoStore.newAttachmentoverloads that takeMap<String, Any?>metadata are deprecated and throwIllegalArgumentExceptionwhen a value isn’t aString. Use the newMap<String, String>overloads.
New warnings after upgrading
Depending on how your app uses Ditto, you may see new warnings after upgrading:- Swift and Kotlin: the passphrase encryption error cases are deprecated in favor of the typed encryption errors.
- Kotlin:
DittoStore.newAttachmentoverloads that takeMap<String, Any?>metadata are deprecated. Use theMap<String, String>overloads. - The
network_enable_ngnandreplication_over_ngnsystem parameters are deprecated in favor ofngn_data_streams_enabledandngn_sync_enabled. The old names still work. - Ditto logs a warning when the SQLite write-ahead log keeps growing. It usually points to a long-held read transaction.
Rollback
SDK 5.2 doesn’t change the on-disk store format, so downgrading from 5.2 to 5.1 needs no special steps.The index and document storage format changes that shipped in 5.1 still apply. If you need to downgrade from 5.2 to Ditto 5.0 or v4, migrate through Ditto 5.0.2+ or 4.14.6+. Those versions recognize the 5.1 index format and revert it to the format earlier versions understand. See index migration and downgrade behavior for details.As with any production upgrade, validate the procedure with representative application data before deployment.5.2.0 Common Changelog
Added:- A new
system:sync_degradationsvirtual collection for querying documents whose sync status is degraded (#DS-1314). - A new
system:support_bundlesvirtual collection for querying diagnostic support bundles with DQL (#SDKS-4758). - The
system_request_support_bundle()DQL function, which generates a support bundle on demand. Run it withEXECUTE FUNCTION system_request_support_bundle(). Not available in the JavaScript Web SDK (#SDKS-4758). - The document sync protocol can trace how document changes propagate through the mesh, for diagnostics. Trace propagation through a Big Peer isn’t supported yet (#DS-1345).
- On Small Peers, the preview DQL function
system_trace_sync_propagationrequests propagation traces for later document changes in the same write transaction (#DS-1460). ditto.query.requests,ditto.query.failed, andditto.query.execution_timemetrics insystem:metrics(#QE-1155).- Ten new counters under
ditto.network.dsoq.*insystem:metrics. They expose connection callback failures, malformed messages, reliable and unreliable stream lifecycles, and certificate verification failures (#NETW-1758). - The Small Peer
system:metricscollection now exposes peer discovery counts, AWDL interface status, payload byte totals, and last-activity timestamps (#NETW-1989). - The first 10,000 entries in
system:indexesare now included in the logs bundle to help diagnose problems (#SPO-76). - Small Peer info now includes disk usage entries for specific files in the
ditto_storesubdirectory (#SPO-657). - INFO logs for DQL observer and subscription lifecycle events (#SPO-447).
ngn_data_streams_enabledandngn_sync_enabledsystem parameters for turning on Ditto Data Streams and sync over Data Streams separately (#NETW-2384).- The
forced_login_refresh_jitter_msSmall Peer system parameter, which sets the maximum random delay before refreshing login credentials after a remote peer rejects the current JWT (#NETW-2252). - Opt-in
transports_websocket_so_keepalive_user_timeout_enabledandtransports_tcp_so_keepalive_user_timeout_enabledsystem parameters which close connections when sent data goes unacknowledged for too long. Available on Linux and Android only (#SPO-1197). - The
diffcaster_fixed_mesh_sizesystem parameter for multicast sync on Small Peers, which spreads relay and repair responsibility across a fixed-size mesh. Set the same group size on every peer. The value takes effect at startup (#DS-1493).
- Queries that use a composite index with mixed
ASCandDESCkeys now return results in the correct order (#CCB-85). - Indexed
SELECT *queries now fetch full documents instead of reading only from the index (#CCB-85). - Inserts into indexed collections that were empty before an upgrade now succeed (#CCB-88).
DELETEandEVICTstatements with aUSE IDSclause now delete the specified documents. Previously the workaround was to filter on the_idfield directly, for exampleWHERE _id = 1(#QE-1186).ANY ... SATISFIESconditions that check more than one property, such asANY car IN cars SATISFIES car.color = 'blue' AND car.type = 'SUV' END, are no longer dropped from queries (#QE-1144).- Results are now sorted correctly when
ORDER BYrefers to a projection alias in a plan that sorts before fetching documents (#QE-1166). - The query planner now generates correct index span ranges for expressions of the form
constant operator field, such as1 < a(#QE-1212). SELECTstatements with longANDorORchains in theWHEREclause no longer crash the app (#SDKS-4435).- DQL requests that end abruptly, for example through
StoreObserver::cancel(), now stop cleanly and release their resources, including the transaction (#QE-1176). - Observers no longer stall when many are active at the same time (#SPO-1114).
- On Small Peers, retrying completion of a write transaction that’s already complete no longer cancels another write transaction that’s still in progress (#DS-1409).
- Completing a write transaction while an operation callback is still active now reports that the transaction is in use instead of hanging. You can retry completion after the callback returns (#DS-1410).
- Requesting an attachment that no document references anymore now delivers a terminal
Deletedstatus to the fetch callback instead of retrying forever (#DS-842). - On-disk logs no longer lose entries when log files rotate in quick succession, such as during heavy logging (#DXO-250).
- Peers that authenticate right after a revocation is created are no longer rejected. Revocations apply only to certificates issued before the revocation (#NETW-2280).
- Invalid TLS identity and certificate configuration now returns errors you can act on instead of being silently ignored or panicking (#NETW-2080).
- TLS peers with mismatched certificate identities are no longer retried over and over during peer discovery (#NETW-2378).
- A malformed datagram for a stream that hasn’t opened yet can no longer stall the connection. The datagram is dropped after its first unrecoverable decoding error (#NETW-1758).
- Mesh connections now limit how many incoming messages they buffer, to prevent excessive memory use during peer-to-peer sync (#NETW-2192).
- Peer-to-peer connections now reject malformed handshake metadata without disrupting the connection process (#NETW-2198).
- The presence graph no longer keeps disconnected client connections after a reconnect or a permanent connection failure (#NETW-2200).
- Forwarded IPv6 traffic with extension headers now preserves the correct upper-layer protocol (#NETW-2201).
- Repeated mDNS address refreshes no longer leave stale TCP connections behind, so Ditto stops trying old addresses after a peer’s address changes (#NETW-2212).
- QUIC path RTT and MTU metrics no longer keep stale per-peer values after a connection closes. Their aggregate values now reflect only live connections (#NETW-1766).
- Wi-Fi Aware connections now release Android network resources after peers disconnect, which prevents failures during repeated reconnects (#NETW-2079).
mesh_chooser_max_wlan_connectionsnow limits inbound and outbound LAN connections together. Previously, a peer could hold about twice the configured number (#SPO-1201).mesh_chooser_max_wlan_connectionsis now used even when you set it to its default value. Previously, Ditto used the deprecatedmesh_chooser_max_wlan_clientsinstead (#SPO-1201).- The deprecated
mesh_chooser_max_wlan_clientsis now used as a fallback even when you set it to its default value (#SPO-1201). - When
mesh_chooser_at_capacity_actionisDropOldestorDropNewest, Ditto now waits until the incoming peer has finished connecting before it disconnects an existing peer. Previously a peer that never finished connecting could cause a healthy peer to be dropped (#SPO-1201). - Turning on Ditto Data Streams while keeping sync on legacy channels no longer skips TCP listener auto-start, which could silently break LAN sync (#NETW-2384).
- Multicast sync now pauses sending when its configured network interface disappears and resumes once it’s back. This prevents repeated send errors and excessive log rotation (#NETW-2158).
- DQL query results stay compact until you read them, which reduces memory use for large result sets and store observers (#QE-1093).
- DQL store observers no longer keep observed documents fully decoded between updates, which reduces memory use when observing collections of large documents (#QE-1093).
- DQL store observers whose queries can’t reuse the previous result incrementally, such as projections or index-backed lookups, no longer keep a copy of the previous result set between updates (#QE-1093).
- Reading a document, including the documents a store observer delivers on each update, no longer pins its decoded form in memory. This reduces the memory that long-lived observers keep (#QE-1096).
- Peak memory use is lower while DQL store observers build callback results, especially for large result sets. Observer behavior and query results are unchanged (#SDKS-4510).
- Reconnecting to a recently seen peer over the TCP/TLS LAN transport can now use an abbreviated TLS handshake, which cuts reconnection time after a network interruption (#NETW-1027).
- Reconnecting to a recently seen peer over the UDP/NGN LAN transport is now faster after a network interruption, because the encrypted session is resumed instead of renegotiated (#NETW-1767).
- LAN peer discovery on Linux and Android now causes smaller network traffic spikes when many Small Peers start at the same time. Default Apple and Windows LAN discovery is unchanged (#SPO-1038).
- Most virtual-collection observers now fire only when data changes. Previously they fired periodically whether or not anything changed (#QE-1139).
- Store observers no longer re-run their queries after writes to collections they don’t observe (#QE-1116).
- Native WebSocket connections now turn on TCP keepalive (
SO_KEEPALIVE), on both outbound client sockets and accepted server sockets. It acts as a kernel-level backstop for a connection whose runtime or timers have stalled. It’s on by default with a 30-second idle threshold, a 10-second probe interval, and 3 probes. You can configure it with the newtransports_websocket_so_keepalive_enabled,_idle_secs,_interval_secs, and_retriessystem parameters. Browser and Wasm clients aren’t affected (#SPO-1164). - Setting
ngn_sync_enabledwithoutngn_data_streams_enablednow makesstartSync()fail with a configuration error. Previously the setting was silently ignored (#NETW-2384). - When a Small Peer receives a replication diff that would push a document over the hard size limit, it now drops the diff silently. The document gets no more updates until it’s evicted and synced again (#SPO-1008).
- Multicast peers that need the same attachment no longer each request it, which cuts request traffic by about 75% at the default settings (#DS-1632).
attachments_want_suppression_threshold(default1) andattachments_want_max_consecutive_suppressions(default3) system parameters for tuning multicast attachment requests (#DS-1632).- Pending multicast attachment requests retry as soon as a peer joins the group, which speeds up recovery after a network split (#DS-1632).
- Reliable UDP multicast changes requested on Android or iOS while sync is running now take effect after sync restarts. The active transport stays in place until the new configuration passes validation (#SDKS-4471).
- Small Peers now keep the SQLite write-ahead log from growing without bound on long-running devices, and reclaim free disk space automatically. If the log keeps growing, Ditto logs a warning that usually points to a long-held read transaction (#SPO-671).
- The Small Peer diagnostic support bundle now includes
system:request_historyentries (#SPO-1077). - The UDP transport now logs retryable address and interface lookup failures at debug level instead of error level (#SPO-327).
- Sync on Streams and Attachments on Streams now write fewer repetitive session logs, and expected missing-session disconnects are logged as debug events (#SPO-328).
- The store observer API documentation now explains how to use signal-next to handle backpressure, and how pending updates collapse into at most one callback with the latest result (#SDKS-179).
- The
network_enable_ngnandreplication_over_ngnsystem parameters, in favor ofngn_data_streams_enabledandngn_sync_enabled. The old names still work (#NETW-2384).
5.2.0 C++ Specific Changes
Added:DittoConfig::system_parametersandDittoConfig::set_system_parameters()for applying system parameter overrides when opening aDittoinstance (#SDKS-1637).- Opening a store can now fail with a typed encryption error:
EncryptionKeyRequiredError,EncryptionKeyUnexpectedError,EncryptionKeyInvalidError,EncryptionUnsupportedSchemeError, orEncryptionMetadataCorruptError(#SDKS-4805).
C#-Specific Changes
The C# SDK in 5.2 fixes startup problems on current .NET toolchains:- .NET Android apps that resolve
Xamarin.Kotlin2.x packages, such as .NET 10 MAUI apps, no longer abort at startup with “Built-in class kotlin.Any is not found”. The Wi-Fi Aware and BLE transports start correctly. Ditto.Open()no longer crashes on 32-bit ARM Android devices (armeabi-v7a).- CBOR unsigned integers above
Int64.MaxValuedecode asulonginstead ofnull. - Set system parameters in
DittoConfig.SystemParametersinstead of runningALTER SYSTEMafter open. Ditto applies them before it starts, so startup-only settings now take effect. TypedDittoEncryptionExceptionsubclasses report why a store failed to open. - Android gets the Wi-Fi Aware and Bluetooth LE hardening described in the common changes.
Behavior changes
- .NET apps that target unsupported Windows process architectures (x86, ARM, ARM64) fail with a compile-time error. Set
DittoNativeWinSkipArchitectureValidationtotrueto bypass the check.
5.2.0 C# Specific Changes
Added:DittoConfig.SystemParametersfor applying system parameter overrides when opening aDittoinstance (#SDKS-1639).- Opening a store can now fail with a
DittoEncryptionExceptionsubclass:DittoEncryptionKeyRequiredException,DittoEncryptionKeyUnexpectedException,DittoEncryptionKeyInvalidException,DittoEncryptionUnsupportedSchemeException, orDittoEncryptionMetadataCorruptException(#SDKS-4805). - On Android, the
transports_wifi_aware_scan_yield_enabledsystem parameter (default off) lets Wi-Fi Aware briefly release its data paths so a disconnected device or weak link can scan for a stronger access point (#NETW-2387).
- .NET apps that target unsupported Windows process architectures (x86, ARM, ARM64) now fail with a compile-time error. Set
DittoNativeWinSkipArchitectureValidationtotrueto bypass the check (#SPO-1127).
Ditto.Open()no longer crashes on 32-bit ARM Android devices (armeabi-v7a) (#SDKS-4674).- .NET Android apps that resolve
Xamarin.Kotlin2.x packages, such as .NET 10 MAUI apps, no longer abort at startup with “Built-in class kotlin.Any is not found”, and the Wi-Fi Aware and BLE transports start correctly (#SPO-1204). - On Android,
DittoSyncPermissions.RequestPermissionsAsync()no longer crashes when called from a background thread (#NETW-2077). - AWDL peer-to-peer connectivity on Apple platforms now recovers automatically after temporary network setup failures instead of staying unavailable until the app restarts (#NETW-2081).
- Wi-Fi Aware peers on Android no longer create competing connections when both devices discover each other at the same time, which could cause a connection failure during startup (#NETW-2240).
DittoPresence.ConnectionRequestHandlerfailures now log why the connection request was denied (#SDKS-3486).- CBOR unsigned integers above
Int64.MaxValuein query results and documents now decode asulonginstead ofnull(#SDKS-4508). - Wi-Fi Aware meshes of three or more Android devices no longer split into isolated pairs that stop syncing with the rest of the mesh (#NETW-2324).
- Wi-Fi Aware on Android now refreshes its discovery sessions instead of restarting the whole Wi-Fi Aware stack when a connection looks unhealthy (#NETW-2340).
- Wi-Fi Aware connections on Android are no longer torn down by a subsystem restart while a data path is carrying sync traffic (#NETW-2223).
- Rapid transport restarts on Android no longer disrupt active Wi-Fi Aware connections (#NETW-2241).
- Wi-Fi Aware connections on Android no longer stall during concurrent peer discovery and connection updates (#NETW-2078).
- Wi-Fi Aware on Android no longer reports scanning as online after the transport has stopped (#NETW-2079).
- Android apps no longer crash when a transport leaves a Java exception pending on a native thread (#NETW-2038).
- Android BLE no longer crashes the app when an L2CAP channel can’t be opened or closed, for example when Bluetooth is turned off while peers are connecting. Bluetooth sync continues over GATT (#SPO-1205).
Flutter-Specific Changes
The Flutter SDK in 5.2 fixes a common development crash and adds lifecycle helpers:- Hot restarting an app while a store observer is active during sync no longer crashes.
Authenticator.logoutaccepts an optionalcleanupcallback that runs after logout completes, for example to clear the store.Store.observersreturns the activeStoreObserverinstances, matchingStore.attachmentFetchers.- Set system parameters in
DittoConfig.systemParametersinstead of runningALTER SYSTEMafter open. Ditto applies them before it starts, so startup-only settings now take effect. - Android gets the Wi-Fi Aware and Bluetooth LE hardening described in the common changes, plus
DittoSyncPermissions.requiredMulticastPermissions()for reliable UDP multicast.
Behavior changes
- On desktop platforms,
Ditto.defaultRootDirectorynow resolves to the application support directory instead of the documents directory. WhenDittoConfig.persistenceDirectoryisnull, existing databases in the previous location are still found on upgrade.
5.2.0 Flutter Specific Changes
Added:DittoConfig.systemParametersfor applying system parameter overrides when opening aDittoinstance (#SDKS-1640).Authenticator.logoutaccepts an optionalcleanupcallback (AuthenticationLogoutCleanupHandler) that runs after logout completes, for example to clean up the store (#SDKS-1948).Store.observersgetter that returns the currently activeStoreObserverinstances, matchingStore.attachmentFetchers(#SDKS-3917).DittoSyncPermissions.requiredMulticastPermissions()returns the Android permissions that reliable UDP multicast needs, includingACCESS_LOCAL_NETWORKwhere applicable (#SDKS-4471).- Opening a store can now fail with a
DittoEncryptionExceptionwhose reason iskeyRequired,keyUnexpected,keyInvalid,unsupportedScheme, ormetadataCorrupt(#SDKS-4805). - On Android, the
transports_wifi_aware_scan_yield_enabledsystem parameter (default off) lets Wi-Fi Aware briefly release its data paths so a disconnected device or weak link can scan for a stronger access point (#NETW-2387).
- On desktop platforms,
Ditto.defaultRootDirectorynow resolves to the application support directory (getApplicationSupportDirectory()) instead of the documents directory. WhenDittoConfig.persistenceDirectoryisnull, existing databases in the previous default location are still found on upgrade (#SDKS-3900).
- A crash when a Flutter app was hot restarted while a store observer was active during sync (#SDKS-4719).
Ditto.open()no longer crashes during initialization on 32-bit Android devices that report thearmv8larchitecture (#SDKS-4674).- AWDL peer-to-peer connectivity on Apple platforms now recovers automatically after temporary network setup failures instead of staying unavailable until the app restarts (#NETW-2081).
- Wi-Fi Aware peers on Android no longer create competing connections when both devices discover each other at the same time, which could cause a connection failure during startup (#NETW-2240).
- Wi-Fi Aware meshes of three or more Android devices no longer split into isolated pairs that stop syncing with the rest of the mesh (#NETW-2324).
- Wi-Fi Aware on Android now refreshes its discovery sessions instead of restarting the whole Wi-Fi Aware stack when a connection looks unhealthy (#NETW-2340).
- Wi-Fi Aware connections on Android are no longer torn down by a subsystem restart while a data path is carrying sync traffic (#NETW-2223).
- Rapid transport restarts on Android no longer disrupt active Wi-Fi Aware connections (#NETW-2241).
- Wi-Fi Aware connections on Android no longer stall during concurrent peer discovery and connection updates (#NETW-2078).
- Wi-Fi Aware on Android no longer reports scanning as online after the transport has stopped (#NETW-2079).
- Android apps no longer crash when a transport leaves a Java exception pending on a native thread (#NETW-2038).
- Android BLE no longer crashes the app when an L2CAP channel can’t be opened or closed, for example when Bluetooth is turned off while peers are connecting. Bluetooth sync continues over GATT (#SPO-1205).
5.2.0 Go Specific Changes
Added:DittoConfig.SystemParametersandWithSystemParameters()for applying system parameter overrides when opening aDittoinstance (#SDKS-4500).
- AWDL peer-to-peer connectivity on Apple platforms now recovers automatically after temporary network setup failures instead of staying unavailable until the app restarts (#NETW-2081).
Java-Specific Changes
The Java SDK in 5.2 gains startup and encryption controls:- Negative integers in DQL query arguments are now encoded correctly. Before 5.2, a negative argument was stored as one less than the value passed, so a value passed as
-5was stored as-6. Documents written or matched with negative integers on any 5.x version may be wrong. Read the Fixed section below for more information. - Set system parameters in
DittoConfig.Builder.systemParameters()instead of runningALTER SYSTEMafter open. Ditto applies them before it starts, so startup-only settings now take effect. DittoTransactionInfo.getHintOrNull()returnsnullwhen no transaction hint was provided, so you can tell an unset hint from an empty one.- Typed
DittoException.EncryptionExceptionreasons report why a store failed to open.
5.2.0 Java Specific Changes
Added:DittoConfig.Builder.systemParameters()for applying system parameter overrides when opening aDittoinstance (#SDKS-1635).DittoTransactionInfo.getHintOrNull(), which returnsnullwhen no transaction hint was provided.getHint()still returns an empty string in that case (#SDKS-4318).- Opening a store can now fail with a
DittoException.EncryptionExceptionwhose reason isKeyRequired,KeyUnexpected,KeyInvalid,UnsupportedScheme, orMetadataCorrupt(#SDKS-4805).
- Negative integers in DQL query arguments are now encoded correctly. Before this fix, a negative argument was stored and compared as one less than the value you passed. A value passed as
-5was stored as-6, so documents written or matched with negative integers may be wrong. Values such asInteger.MIN_VALUEalso no longer fail to serialize (#SDKS-4493).
DittoFactory.create()no longer crashes during initialization on 32-bit Android devices that report thearmv8larchitecture (#SDKS-4674).- AWDL peer-to-peer connectivity on Apple platforms now recovers automatically after temporary network setup failures instead of staying unavailable until the app restarts (#NETW-2081).
JavaScript & React Native-Specific Changes
The JavaScript and React Native SDKs in 5.2 unblock current toolchains:- React Native iOS builds succeed with React Native 0.86.
- React Native Android builds honor the Kotlin, SDK, and NDK version overrides in
gradle.properties, which unblocks Expo apps. - React Native connection request handlers can read identity service metadata without a “not implemented” error.
- Set system parameters in
DittoConfig.systemParametersinstead of runningALTER SYSTEMafter open. Ditto applies them before it starts, so startup-only settings now take effect. - Opening a store can fail with a typed
encryption/*error code, such asencryption/key-required. Theencryption/extraneous-passphrase-givencode is deprecated. - React Native on Android gets the Wi-Fi Aware and Bluetooth LE hardening described in the common changes.
5.2.0 JavaScript & React Native Specific Changes
Added:DittoConfig.systemParametersfor applying system parameter overrides when opening aDittoinstance (#SDKS-1636).- Opening a store can now fail with an
encryption/key-required,encryption/key-unexpected,encryption/key-invalid,encryption/unsupported-scheme, orencryption/metadata-corrupterror code (#SDKS-4805). - On Android, the
transports_wifi_aware_scan_yield_enabledsystem parameter (default off) lets Wi-Fi Aware briefly release its data paths so a disconnected device or weak link can scan for a stronger access point (#NETW-2387).
- React Native iOS builds now succeed with React Native 0.86 (#SDKS-4884).
- React Native Android builds now honor the Kotlin, SDK, and NDK version overrides in
gradle.properties, which unblocks Expo apps (#SDKS-4883). - React Native connection request handlers can now read identity service metadata without a “not implemented” error (#SDKS-2575).
- Wi-Fi Aware peers on Android no longer create competing connections when both devices discover each other at the same time, which could cause a connection failure during startup (#NETW-2240).
- Wi-Fi Aware meshes of three or more Android devices no longer split into isolated pairs that stop syncing with the rest of the mesh (#NETW-2324).
- Wi-Fi Aware on Android now refreshes its discovery sessions instead of restarting the whole Wi-Fi Aware stack when a connection looks unhealthy (#NETW-2340).
- Wi-Fi Aware connections on Android are no longer torn down by a subsystem restart while a data path is carrying sync traffic (#NETW-2223).
- Rapid transport restarts on Android no longer disrupt active Wi-Fi Aware connections (#NETW-2241).
- Wi-Fi Aware connections on Android no longer stall during concurrent peer discovery and connection updates (#NETW-2078).
- Wi-Fi Aware on Android no longer reports scanning as online after the transport has stopped (#NETW-2079).
- Android apps no longer crash when a transport leaves a Java exception pending on a native thread (#NETW-2038).
- Android BLE no longer crashes the app when an L2CAP channel can’t be opened or closed, for example when Bluetooth is turned off while peers are connecting. Bluetooth sync continues over GATT (#SPO-1205).
- AWDL peer-to-peer connectivity on Apple platforms now recovers automatically after temporary network setup failures instead of staying unavailable until the app restarts (#NETW-2081).
- The
encryption/extraneous-passphrase-givenerror code (#SDKS-4805).
Kotlin-Specific Changes
The Kotlin SDK gets the largest set of platform changes in 5.2:- Negative integers in DQL query arguments are now encoded correctly. Before 5.2, a negative argument was stored as one less than the value passed, so a value passed as
-5was stored as-6. Documents written or matched with negative integers on any 5.x version may be wrong. Read the Fixed section below for more information. - Encryption at rest with a raw 32-byte key through
DittoConfig.encryptionKey, plusDittoFactory.deleteStore()for wiping a local store and typedDittoException.EncryptionExceptionreasons when a store fails to open. - Set system parameters in
DittoConfig.systemParametersinstead of runningALTER SYSTEMafter open. Ditto applies them before it starts, so startup-only settings now take effect. - Wi-Fi Aware hardening on Android: meshes of three or more devices no longer split into isolated pairs, sessions refresh instead of restarting the stack, and active transfers survive subsystem restarts. The opt-in
transports_wifi_aware_scan_yield_enabledparameter lets a weak link scan for a stronger access point. - Android BLE no longer crashes when an L2CAP channel can’t be opened or closed. Sync continues over GATT.
DittoAuthenticator.login()failures exposeAuthenticationException.failureCode, so your app can tell rejected credentials from retryable failures.DittoSyncPermissions.requiredMulticastPermissions()returns the Android permissions that reliable UDP multicast needs.- DittoAndroidTools adds a support bundle screen for requesting, viewing, and exporting diagnostic bundles.
Behavior changes
DittoStore.newAttachmentoverloads that takeMap<String, Any?>metadata are deprecated and now throwIllegalArgumentExceptionfor non-string values. Use the newMap<String, String>overloads.DittoFactory.create()no longer crashes on 32-bit Android devices that report thearmv8larchitecture.
5.2.0 Kotlin Specific Changes
Added:DittoConfig.encryptionKey, which takes aDittoEncryptionKey.raw(...)value, for encryption at rest with a raw 32-byte key (#SDKS-4776).DittoFactory.deleteStore(databaseId, persistenceDirectory)for deleting a local store (#SDKS-4662).- Opening a store can now fail with a
DittoException.EncryptionExceptionwhose reason isKeyRequired,KeyUnexpected,KeyInvalid,UnsupportedScheme, orMetadataCorrupt(#SDKS-4805). DittoConfig.systemParametersfor applying system parameter overrides when opening aDittoinstance (#SDKS-1635).DittoTransactionInfo.hintOrNull, which isnullwhen no transaction hint was provided.hintstill returns an empty string in that case (#SDKS-4318).DittoStore.newAttachmentoverloads that takeMap<String, String>metadata (#SDKS-4320).DittoSyncPermissions.requiredMulticastPermissions()returns the Android permissions that reliable UDP multicast needs, includingACCESS_LOCAL_NETWORKwhere applicable (#SDKS-4471).- The
transports_wifi_aware_scan_yield_enabledsystem parameter (default off) lets Wi-Fi Aware briefly release its data paths so a disconnected device or weak link can scan for a stronger access point (#NETW-2387). - A support bundle screen in DittoAndroidTools for requesting, viewing, and exporting diagnostic support bundles (#SDKS-4758).
- Negative integers in DQL query arguments are now encoded correctly. Before this fix, a negative argument was stored and compared as one less than the value you passed. A value passed as
-5was stored as-6, so documents written or matched with negative integers may be wrong. Values such asInt.MIN_VALUEalso no longer fail to serialize (#SDKS-4493).
DittoFactory.create()no longer crashes during initialization on 32-bit Android devices that report thearmv8larchitecture (#SDKS-4674).- Android BLE no longer crashes the app when an L2CAP channel can’t be opened or closed, for example when Bluetooth is turned off while peers are connecting. Bluetooth sync continues over GATT (#SPO-1205).
DittoAuthenticator.login()failures now exposeAuthenticationException.failureCode, which distinguishes rejected credentials from retryable or unknown failures (#SPO-1112).DittoStore.fetchAttachment()now resolves exactly once when cancellation races with completion or deletion (#SDKS-4558).- The
DittoStore.fetchAttachment()API documentation now correctly describes the returnedDittoAttachmentFetchResultand how to cancel a fetch (#SDKS-3960). - Wi-Fi Aware meshes of three or more Android devices no longer split into isolated pairs that stop syncing with the rest of the mesh (#NETW-2324).
- Wi-Fi Aware on Android now refreshes its discovery sessions instead of restarting the whole Wi-Fi Aware stack when a connection looks unhealthy (#NETW-2340).
- Wi-Fi Aware connections on Android are no longer torn down by a subsystem restart while a data path is carrying sync traffic (#NETW-2223).
- Rapid transport restarts on Android no longer disrupt active Wi-Fi Aware connections (#NETW-2241).
- Wi-Fi Aware peers no longer create competing connections when both devices discover each other at the same time, which could cause a connection failure during startup (#NETW-2240).
- Wi-Fi Aware connections on Android no longer stall during concurrent peer discovery and connection updates (#NETW-2078).
- Wi-Fi Aware on Android no longer reports scanning as online after the transport has stopped (#NETW-2079).
- Android apps no longer crash when a transport leaves a Java exception pending on a native thread (#NETW-2038).
DittoStore.newAttachmentoverloads that takeMap<String, Any?>metadata. They now throwIllegalArgumentExceptionwhen a value isn’t aString(#SDKS-4320).
5.2.0 Rust Specific Changes
Added:DittoError::is_encryption_errorandDittoError::encryption_error_kindidentify store-open encryption failures: key required, key unexpected, key invalid, unsupported scheme, or metadata corrupt (#SDKS-4805).
- Opening Ditto with invalid system parameters now reports
CoreApiErrorKind::ValidationInvalidDittoConfigfromDittoError::kind()(#SDKS-1638). - AWDL peer-to-peer connectivity on Apple platforms now recovers automatically after temporary network setup failures instead of staying unavailable until the app restarts (#NETW-2081).
DatabaseId::default_auth_url()andDatabaseId::default_sync_url(). Use the URL from the Ditto Portal instead (#SDKS-3024).- The passphrase-era
CoreApiErrorKindandDittoErrorCodevariants (#SDKS-4805).
Swift-Specific Changes
The Swift SDK in 5.2 adds store protection and lifecycle controls:- Encryption at rest with a raw key through
DittoConfig.encryptionKey. Ditto.close()andDitto.deleteStore(databaseID:persistenceDirectory:)for closing and deleting a local store, for example on logout.- Typed encryption errors when a store fails to open:
keyRequired,keyUnexpected,keyInvalid,unsupportedScheme, ormetadataCorrupt. The passphrase error cases are deprecated. - Set system parameters in
DittoConfig.systemParametersinstead of runningALTER SYSTEMafter open. Ditto applies them before it starts, so startup-only settings now take effect. - Boolean arguments created with
JSONSerializationare stored as booleans instead of0or1. - AWDL peer-to-peer connectivity recovers automatically after temporary network setup failures.
5.2.0 Swift Specific Changes
Added:DittoConfig.encryptionKeyfor encryption at rest with a raw key (#SDKS-4775).Ditto.close()andDitto.deleteStore(databaseID:persistenceDirectory:)for closing and deleting a local store (#SDKS-4662).- Opening a store can now fail with a typed encryption error:
keyRequired,keyUnexpected,keyInvalid,unsupportedScheme, ormetadataCorrupt(#SDKS-4805). DittoConfig.systemParametersfor applying system parameter overrides when opening aDittoinstance (#SDKS-1634).
- Boolean arguments to
DittoStore.executecreated withJSONSerializationare now stored as booleans instead of0or1(#SDKS-4104). - AWDL peer-to-peer connectivity on Apple platforms now recovers automatically after temporary network setup failures instead of staying unavailable until the app restarts (#NETW-2081).
- The passphrase encryption error cases (#SDKS-4805).