Skip to main content
SDKSDK-Common
Release Date: Oct 27, 2026
_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.

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.
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.

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_degradations collection shows which documents aren’t syncing.
  • A preview DQL function traces how a change moves through the mesh.
2. Wireless Stability: Bluetooth, Wi-Fi Aware, Apple P2P Wi-Fi
  • 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.
3. Connections: Faster Reconnects and Stalled-Connection Detection
  • 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.
4. Memory and Storage Health
  • 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.
5. Encryption at Rest for the SDK Data Store
  • 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() and deleteStore() helpers for wiping a device cleanly.
Also in this release
  • 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.
Upgrading to 5.2 and rollback
  • 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
The new system:support_bundles virtual collection lists every bundle on the device, so you can find and manage bundles with a query:
DQL
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 (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 new system: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.
SDK 5.2 fixes a set of wireless problems that showed up in production meshes. Most of them previously ended with a user restarting the app.

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.
The new opt-in 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.
Observer behavior and query results are unchanged.

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 to orders 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 through DittoConfig.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 adds Ditto.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 DQL ALTER 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 your ALTER SYSTEM call.
  • 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 if ngn_sync_enabled is set without ngn_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.persistenceDirectory is null.
  • C#: .NET apps that target x86, ARM, or ARM64 Windows fail at compile time. Set DittoNativeWinSkipArchitectureValidation to true to bypass the check.
  • Kotlin: DittoStore.newAttachment overloads that take Map<String, Any?> metadata are deprecated and throw IllegalArgumentException when a value isn’t a String. Use the new Map<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.newAttachment overloads that take Map<String, Any?> metadata are deprecated. Use the Map<String, String> overloads.
  • The network_enable_ngn and replication_over_ngn system parameters are deprecated in favor of ngn_data_streams_enabled and ngn_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_degradations virtual collection for querying documents whose sync status is degraded (#DS-1314).
  • A new system:support_bundles virtual 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 with EXECUTE 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_propagation requests propagation traces for later document changes in the same write transaction (#DS-1460).
  • ditto.query.requests, ditto.query.failed, and ditto.query.execution_time metrics in system:metrics (#QE-1155).
  • Ten new counters under ditto.network.dsoq.* in system:metrics. They expose connection callback failures, malformed messages, reliable and unreliable stream lifecycles, and certificate verification failures (#NETW-1758).
  • The Small Peer system:metrics collection now exposes peer discovery counts, AWDL interface status, payload byte totals, and last-activity timestamps (#NETW-1989).
  • The first 10,000 entries in system:indexes are 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_store subdirectory (#SPO-657).
  • INFO logs for DQL observer and subscription lifecycle events (#SPO-447).
  • ngn_data_streams_enabled and ngn_sync_enabled system parameters for turning on Ditto Data Streams and sync over Data Streams separately (#NETW-2384).
  • The forced_login_refresh_jitter_ms Small 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_enabled and transports_tcp_so_keepalive_user_timeout_enabled system parameters which close connections when sent data goes unacknowledged for too long. Available on Linux and Android only (#SPO-1197).
  • The diffcaster_fixed_mesh_size system 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).
Fixed:
  • Queries that use a composite index with mixed ASC and DESC keys 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).
  • DELETE and EVICT statements with a USE IDS clause now delete the specified documents. Previously the workaround was to filter on the _id field directly, for example WHERE _id = 1 (#QE-1186).
  • ANY ... SATISFIES conditions that check more than one property, such as ANY 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 BY refers 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 as 1 < a (#QE-1212).
  • SELECT statements with long AND or OR chains in the WHERE clause 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 Deleted status 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_connections now limits inbound and outbound LAN connections together. Previously, a peer could hold about twice the configured number (#SPO-1201).
  • mesh_chooser_max_wlan_connections is now used even when you set it to its default value. Previously, Ditto used the deprecated mesh_chooser_max_wlan_clients instead (#SPO-1201).
  • The deprecated mesh_chooser_max_wlan_clients is now used as a fallback even when you set it to its default value (#SPO-1201).
  • When mesh_chooser_at_capacity_action is DropOldest or DropNewest, 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).
Changed:
  • 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 new transports_websocket_so_keepalive_enabled, _idle_secs, _interval_secs, and _retries system parameters. Browser and Wasm clients aren’t affected (#SPO-1164).
  • Setting ngn_sync_enabled without ngn_data_streams_enabled now makes startSync() 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 (default 1) and attachments_want_max_consecutive_suppressions (default 3) 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_history entries (#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).
Deprecated:
  • The network_enable_ngn and replication_over_ngn system parameters, in favor of ngn_data_streams_enabled and ngn_sync_enabled. The old names still work (#NETW-2384).
SDKSwift
Release Date: Oct 27, 2026

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() and Ditto.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, or metadataCorrupt. The passphrase error cases are deprecated.
  • Set system parameters in DittoConfig.systemParameters instead of running ALTER SYSTEM after open. Ditto applies them before it starts, so startup-only settings now take effect.
  • Boolean arguments created with JSONSerialization are stored as booleans instead of 0 or 1.
  • AWDL peer-to-peer connectivity recovers automatically after temporary network setup failures.

5.2.0 Swift Specific Changes

Added:
  • DittoConfig.encryptionKey for encryption at rest with a raw key (#SDKS-4775).
  • Ditto.close() and Ditto.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, or metadataCorrupt (#SDKS-4805).
  • DittoConfig.systemParameters for applying system parameter overrides when opening a Ditto instance (#SDKS-1634).
Fixed:
  • Boolean arguments to DittoStore.execute created with JSONSerialization are now stored as booleans instead of 0 or 1 (#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).
Deprecated:
  • The passphrase encryption error cases (#SDKS-4805).
SDKSDK-Common
Release Date: Aug 18, 2026
_SDK 5.1 common capabilities — shared by all SDK platforms. Shown in the overall SDK view; platform filters show that platform’s own blocks only.

Ditto SDK 5.1 — Faster Local Queries. More Capable Query Engine.

The new Ditto Edge SDK 5.1 delivers major features and optimizations across five areas. The release includes 77 platform and 112 SDK-specific improvements.Key takeaway: Existing applications get better performance and more efficient memory usage on the same hardware simply by upgrading to SDK 5.1.1. Performance: Faster Queries, Lower Memory Use
  • Evictions are 53.6× faster and deletes are 43.0× faster on average. This speeds up routine local data clean-up, like clearing the orders from a point-of-sale terminal.
  • A full-collection COUNT(*) fell from 149.03 ms to 0.89 ms, which is 167× faster on average. This speeds up badge counts and dashboard totals across an app.
2. Query Engine: New Ditto Query Capabilities
  • JOIN combines local collections in a single SELECT, removing the need to run separate queries and merge their results in application code.
  • ADVISE looks at a query and tells you the indexes to create to make it faster, all without running it or reading a document.
3. Security: Expanded Certificate Revocation
  • If a device is lost or stolen, it’s easier to revoke the device’s certificate, disconnect it from the mesh, and prevent the device from sending or receiving data.
  • Revocations travel peer to peer and reach devices even when they can’t see the cloud. Connections from revoked peers are terminated and refused.
4. Troubleshooting: Identify Problems and Recover Automatically
  • Identifying problems is easier with better tools for analyzing deployed devices, starting with support bundles that capture the device’s effective configuration.
  • Corrupted replication metadata is detected and rebuilt automatically, so field issues can be solved without a site visit.
5. Transport at Scale: Multicast Beta
  • Multicast allows Ditto to scale to large networks with many edge devices. Edge devices subscribe to a shared group for data sync, which has lower connection cost at scale.
  • Opt-in beta capability in the Swift, Kotlin/KMP, Flutter, and Rust SDKs. Support will expand to additional SDKs in the future.
Upgrading to 5.1 and rollback
  • Upgrading to Ditto SDK 5.1 changes the on-disk index format.
  • If you need to downgrade from Ditto SDK 5.1 or later, migrate through Ditto 5.0.2+. You can also migrate through Ditto 4.14.6+.

1. Performance: Faster Queries, Lower Memory Use

Edge applications feel database performance differently from cloud applications. A slow local query doesn’t just delay a response from a server. It can block a screen transition, increase battery use, create UI churn, or consume memory on a device with limited resources.SDK 5.1 delivers a major leap in local query performance. Applications can load data faster, complete writes sooner, and react to changes more quickly—even as their local datasets and workloads grow.The improvements span nearly every kind of local data operation: selects, indexed reads, inserts, updates, deletes, evictions, aggregations, and observers. In practice, this means more responsive user experiences, less time waiting for data operations, and greater capacity on the same device hardware.These gains come from improvements throughout the local data path. Ditto performs less decoding and allocation, finds documents more directly, executes mutations more efficiently, avoids unnecessary observer work, and reduces full database scans during subscription changes. An opt-in relaxed durability mode can further reduce disk synchronization for rebuildable sync metadata without changing the durability of application documents.

Android retail benchmark

The gains are broad rather than limited to one optimized query path. In testing on an Orion O6 Android device, SDK 5.1 was faster in 70 of 71 measured scenarios, with one result too close to call. The 72-scenario suite modeled an offline-first retail application with approximately 93,000 documents across seven synced collections.These results compare median runtimes from Ditto 5.0.3 and Ditto 5.1.0. Performance varies by device, data, indexes, and query mix, so test representative workloads on your target hardware.

Dramatically faster document counts

A count is a query that answers one simple question: how many documents match? Apps use counts all the time to show how many orders are open, how many items are in stock, or how many results a search found.One of the largest individual improvements is full-collection document counting. Ditto 5.1.0 adds an optimized path for COUNT(*), reducing the benchmark’s median execution time from 149.03 ms to 0.89 ms—approximately 167× faster. Counts with a filter also improved by approximately 4.4×.These improvements accelerate queries such as SELECT COUNT(*) FROM tasks and SELECT COUNT(*) FROM tasks WHERE status = 'open', making it substantially faster to calculate totals for dashboards, backlog checks, pagination, and application status displays.

Lower memory use

Edge devices like phones, tablets, and point-of-sale terminals have a fixed amount of memory that every app shares. The less memory Ditto uses, the more room your app has for its own work, and the less likely the operating system is to slow it down.In a separate Android workload that grew a collection from 3,000 to 30,000 documents, Ditto 5.1.0 delivered approximately 2.1× as many observer results while using less memory than Ditto 5.0.3.Total PSS estimates the process’s physical RAM footprint, and peak Total PSS is the highest that footprint reached during the test. Native heap covers Ditto’s Rust core—the pool of memory the program sets aside while it runs to hold its working data. Tombstone cleanup, bulk mutations, and retained disconnected sync sessions are also bounded more carefully to reduce peak memory in high-volume deployments.

2. Query Engine: New Ditto Query Capabilities

Performance is only half of the query story in this new release. DQL (Ditto Query Language) gains the two capabilities customers asked for the most: JOIN and composite indexes. Customers can also run ADVISE to identify any indexes that would speed up query performance.

Join collections locally

A join is a query that combines related data from two collections into one result. It matches records that share a value, like a task and the project it belongs to, so your application gets one combined answer instead of two separate lists.SELECT statements on edge devices can now join multiple local collections. This removes the need to coordinate separate queries and merge their results in application code.
DQL
This makes normalized data models practical at the edge. Product catalogs, order histories, task assignments, and multi-tenant views can stay separated into logical collections without forcing every screen to coordinate multiple reads.Joins use data already present in the local store. They do not fetch missing data from peers, and they are not supported in sync-subscription queries. The inner collection normally requires an appropriate index; use ADVISE when you need an index recommendation.

Create composite indexes

An index is a lookup structure the database maintains so it can find matching documents without reading the whole collection, much like the index at the back of a book. A composite index covers two or more fields at once, so a query that filters on one field and sorts by another can be answered in a single lookup.Edge devices now support composite indexes over multiple fields. A single composite index can accelerate queries that repeatedly filter or sort by the same combination of fields—for example, tenant and time, status and assignee, or location and category.The following index is designed for queries that filter tasks by status and sort them by createdAt:
DQL
It can improve queries such as:
DQL
Field order matters. Put fields used in equality filters first, followed by fields used for range filters or sorting. Composite indexes can also include array and object values. If you are unsure which fields to index, run ADVISE against the query to get an index recommendation.

Find the right indexes with ADVISE

ADVISE turns index optimization into a guided workflow. Prefix a query with ADVISE, and Ditto plans how it would execute that query without actually running it or reading any documents. It inspects the query’s filters, sorts, projections, and joins. Then, Ditto spots the places where the engine would have to scan the whole collection. For each one, the response explains why an index would help and provides a ready-to-run CREATE INDEX statement.For example, advise a query that filters tasks by estimated effort:
DQL
Ditto identifies the range predicate and recommends an index on estimateHours:
Run the suggested statement yourself, or use ADVISE AND PROVISION to create the recommended indexes automatically. ADVISE can also recommend composite and covering indexes for more complex filters, sorting, projections, and joins. It is currently available on edge devices.

Return changed documents with RETURNING

RETURNING lets an INSERT, UPDATE, DELETE, EVICT, or TOMBSTONE statement return data from the documents it changed. A data mutation and its confirmation can become one operation. Applications can receive the affected data immediately instead of issuing a second query.For example, update matching documents and return their IDs and new values in one operation:
DQL
RETURNING is especially valuable with deletes and evictions because it can return document contents before they are removed. It also supports projections, expressions, aliases, and aggregates such as RETURNING COUNT(*) AS removed.Ditto 5.1 also adds INSERT ... SELECT for creating documents directly from query results.

Control long-running requests

A single expensive query on an edge device can hold resources that are needed by the rest of an application. Two new system parameters can catch runaway queries before they cause performance issues.Set either parameter to 0 to disable it. Request history can also filter by request type or explicit profiling requests.

3. Security: Expanded Certificate Revocation

Security policies at the edge need to keep working even when devices are not continuously connected to the cloud. Security teams lose sleep over what happens when an edge device is lost or stolen.SDK 5.1 will help security teams sleep better. Certificate revocation information now propagates securely from Ditto Server to edge devices and from peer to peer throughout the mesh. As peers connect, they share the latest revocation information.Revocation enforcement is enabled by default. Peers reject new connections that present a revoked certificate and terminate matching active connections when a revocation arrives. This isolates revoked clients and prevents them from reconnecting through another peer in the mesh.Every hop verifies the revocation’s signature against its trusted certificate authority keys, the source that issues each device’s identity credentials. A compromised peer cannot forge revocations.

4. Troubleshooting: Identify Problems and Recover Automatically

A device misbehaving in the field is a hard problem to tackle in edge computing. It’s expensive and inefficient to fly an engineer out to attach a debugger to a tablet that is 3,000 miles away in the back of a restaurant.SDK 5.1 adds new options to remotely collect edge device data and identify the root cause of an issue. Support bundles now include config_snapshot.json, which records the effective DittoConfig, transport configuration, system parameters, and SDK version at capture time. This provides essential device config information at the start of every investigation or support case.Other new remote diagnostics include:
  • A configurable Unix debug_socket lets you remotely run DQL diagnostics against an edge device.
    • You can inspect a live device directly rather than reproducing the problem in a lab.
  • Nine new network counters under ditto.network.dsoq.* surface Ditto Sync over QUIC (DSOQ) protocol failures in your existing production metrics, without debug logs.
  • SQLite metrics distinguish the application data store from replication metadata databases on supported Unix platforms, showing which one is driving disk activity.
SDK 5.1 also repairs a class of problems on its own. Corrupted per-peer replication metadata is now detected, reset, and rebuilt automatically without affecting application documents. Previously, this class of corruption could require clearing the device’s local store. The result is faster diagnosis, fewer escalations, and fewer cases that end with wiping the app and reinstalling.

5. Transport at Scale: Multicast Beta

Today, Ditto’s default peer-to-peer model establishes a session between every pair of edge devices. The total connection count grows with the square of the mesh size, O(N²). Six devices need 15 connections, and sixty devices need 1,770. Connection maintenance eventually becomes the dominant cost in environments with a large number of edge devices like a mall, a ship, a concert venue, or an aircraft.Multicast is now available in SDK 5.1 as an opt-in beta. Devices join a shared multicast group rather than pairing off, which drops the connection count from O(N²) to O(N). There is one group membership per device.A sender would typically transmit an update once per device in the mesh, which is O(N). Multicast enables the sender to publish a single broadcast to the group, which is approximately O(1). There is one send, no matter how many devices are listening.The transport is built on reliable multicast (NORM, RFC 5740) combined with Ditto’s data reconciliation, so peers recover missed data and catch up after joining or reconnecting.When multicast is configured and available, it becomes the preferred replication path. Traffic is encrypted for the group, and existing peer-to-peer transports remain active as automatic fallback for peers the group cannot reach. Documents and attachments both replicate over multicast, including repair of missing attachment data.
Multicast is a beta capability in SDK 5.1. It ships in the core SDK as an opt-in feature rather than a part of the standard build. It is available in the Swift, Kotlin/KMP, Flutter, and Rust SDKs.Contact Ditto support or your Ditto representative before deploying multicast in production to understand the current beta limitations.

Upgrading to 5.1

The upgrade to Ditto Edge SDK 5.1 is seamless. Simply bump your dependency to 5.1.0 and Ditto handles the rest. An index migration will run automatically the first time your app starts. Data sync is backward-compatible, and 5.1 peers will work with 5.0 and v4 peers while you roll out gradually.

Tested rollback compatibility

Ditto Edge SDK 5.1 has undergone extensive backward-compatibility and rollback testing to ensure production deployments can safely return to supported earlier SDK versions when needed.The 5.1 SDK changes the on-disk index format. If you need to downgrade from Ditto 5.1 or later, migrate through Ditto 5.0.2+. You can also migrate through Ditto 4.14.6+. These versions recognize the updated index format and automatically revert it to the format understood by earlier versions.During the downgrade, composite indexes are replaced with single-field indexes, one for each of the composite index’s keys.See index migration and downgrade behavior for details. As with any production upgrade, validate the procedure with representative application data before deployment.
SDKSwift
Release Date: Aug 18, 2026

Swift-Specific Changes

The Swift SDK in Ditto 5.1 improves concurrency behavior and diagnostics:
  • Store observers now deliver callbacks on the queue specified by the deliverOn parameter.
  • The store observation handler type aliases are now @Sendable.
  • Exceptions thrown by application callbacks preserve their original call stack, so crash reporters attribute them to the throwing code instead of to Ditto.
  • Custom identity providers no longer stall the SDK during the first credential refresh.
DittoSwiftTools adds DiskUsageInspectorView, a SwiftUI diagnostic view for inspecting live disk usage, growth trends, health thresholds, per-collection rankings, and document-size distributions.

Platform changes

  • Intel (x86_64) Mac support and tvOS support are removed.
  • DittoSwiftTools now requires iOS 15+, Mac Catalyst 15+, or macOS 12+.
  • The bundled DittoSwiftPresenceViewer is removed. Use DittoPresenceViewer from DittoSwiftTools instead.
  • The CBOR enum and obsolete CBOR-specific store errors are deprecated.

Swift Specific Changelog

Added:
  • DittoMulticastBetaConfig and DittoPeerToPeer.multicastBeta for configuring the beta reliable UDP multicast transport. The transport is disabled by default and currently supported on iOS and macOS. On iOS, changes requested while sync is active take effect after sync is stopped and successfully started again, when Ditto validates the required platform configuration. (#SDKS-4471)
  • DittoConnectionType.multicast enum option representing beta reliable UDP multicast connections. The transport is currently supported on iOS and macOS. (#SDKS-4471)
  • DiskUsageInspectorView in DittoSwiftTools: a SwiftUI diagnostic view that shows live total disk usage, health status against an adjustable threshold, growth-rate trend, and projected time to threshold. Opt-in scans add per-collection rankings and a per-collection document-size histogram. (#SUPP-284)
Changed:
  • DittoSwiftTools minimum platform requirements are raised to iOS 15+, Mac Catalyst 15+, and macOS 12+ (previously iOS 14+, Mac Catalyst 14+, and macOS 11+). The tvOS 15+ requirement is unchanged. (#SDKS-3141)
  • The DittoStoreObservationHandler, DittoStoreObservationHandlerWithSignalNext, and DittoSignalNext type aliases are now @Sendable. (#SDKS-3856)
Fixed:
  • Uncaught exceptions thrown inside SDK callback closures are now re-raised with the original call stack preserved, so crash reporters attribute the crash to the throwing code instead of reporting a SIGABRT inside Ditto. (#SDKS-3483)
  • Custom identity providers using DITIdentityProvider no longer stall the SDK the first time a credential refresh is requested. The refresh request is released immediately so the peer is not blocked. (#SDKS-3623)
  • DittoStore.registerObserver now dispatches its callback on the queue specified by the deliverOn parameter, resolving a crash when used with Swift concurrency. (#SDKS-3856)
Deprecated:
  • DittoPeerOS.tvOS. The case is retained so peers running older SDK versions on tvOS can still be identified in the presence graph. (#SDKS-3944)
  • The CBOR enum and the DittoError.StoreErrorReason cases invalidDocumentStructure(cbor:) and nonStringKeyInDocument(key:). The SDK no longer produces these; they will be removed in a future version. (#SDKS-4009)
Removed:
  • Support for Intel (x86_64) Macs. (#SDKS-2924)
  • The DittoSwiftPresenceViewer framework target and podspec. Use the DittoPresenceViewer product from DittoSwiftTools via Swift Package Manager instead. (#SDKS-3141)
  • tvOS platform support. (#SDKS-3944)
SDKSDK-Common
Release Date: Jul 22, 2026
Added: a new system parameter transports_websocket_watchdog_interval_secs to adjust WebSocket client watchdog. (#21885) Fixed: Corrected a formalisation bug causing DQL statement filters to never match, that was affecting observers using projections. (#QE-1056) Fixed: Corrected a hang when accessing system:data_sync_info. (#QE-1095)
SDKSDK-Common
Release Date: Jun 23, 2026
Added: a new timeout for WebSocket connect that is adjustable by system parameter transport_websocket_connect_timeout. (#21866) Changed: Mesh chooser now enforces per-transport connection limits instead of a shared WiFi radio budget, preventing one transport (e.g. TCP) from starving others (e.g. AWDL, WiFi Aware). When a transport is at capacity, the behavior for new inbound connections is configurable: reject, drop oldest, drop newest, or accept. Peers at capacity reject inbound connections with a new ConnectError::AtCapacity variant and back off exponentially to avoid retry storms. (#NETW-1586) Added: Automatic downgrading of the SP store from V3 to V2 on start-up. (#QE-595) Added: Automatic downgrading of the SQLite3 schema from V3 to V2 allowing for downgrading from version 5.1. (#QE-595) Added: disable_replication_gc_on_evict system parameter (default false). When set to true, calls to evict() no longer trigger immediate per-peer metadata cleanup; the periodic background replication GC continues to reclaim metadata for disconnected peers once they exceed the TTL (~7 days by default). Intended as an opt-in escape hatch for deployments where eviction-time filesystem work contributes to write-path latency. (#QE-686) Added: Optional background task to reclaim unused space in the Small Peer store and record space usage metrics. (#QE-779) Changed: The way query profile timing and count data is recorded to reduce overheads. (#QE-811) Fixed: A bug in internal subscription bookkeeping caused long-connected Document Sync sessions to become disabled due to spurious capacity errors. (#SPO-1011) Added: The option to set the DITTO_SQLITE3_MAX_CONNECTIONS parameter lower than 32, down until 16 (#SPO-668) Changed: The default value of SQLITE3_MAX_CONNECTIONS parameter to 32, down from 60 (#SPO-668). Changed: The replication_session_request_timeout_secs and blob_session_request_timeout_secs system parameters are no longer functional; the timeouts have been removed. They are accepted for backwards compatibility but have no effect. (#SPO-869)
SDKSDK-Common
Release Date: May 29, 2026
Removed: Spurious dsoq.cbor CBOR warning log during auth client initialization. (#21646) Added: Garbage collection for document sync sessions now imposes limits on the number of disconnected sessions that will be retained, even if the TTL is not exceeded. (#DS-1065) Fixed: the query engines erroneously build index spans not including the higher end for the BETWEEN operator. Fixed to include it. (#QE-896)
SDKSDK-Common
Release Date: May 5, 2026
_SDK 5.0 introduced DQL for all data operations and major core, performance, and upgrade changes. Shared by all SDK platforms; platform filters show that platform’s own blocks.

DQL for All Data Operations

DQL for All Data Operations

Ditto 5.0 completes the transition to DQL (Ditto Query Language) as the single API for all data operations. The legacy query builder has been removed.

What Changed

  • Legacy query builder removed: All store.collection() methods and fluent query APIs are no longer available
  • Full feature parity: Every legacy operation has a DQL equivalent
  • Single query language: DQL handles reads, writes, subscriptions, and observers
  • Works everywhere: Same syntax across mobile, server, and web
  • SQL-familiar: Standard SQL patterns for immediate productivity

Why One Query Language

A unified query language means one implementation to maintain, faster feature delivery, and consistent behavior across all platforms. New capabilities and optimizations benefit every SDK simultaneously.

Core Improvements

Simplified Initialization & Configuration

Ditto 5.0 introduces a completely redesigned initialization flow built around the new DittoConfig pattern. This replaces the previous Identity-based approach with a clearer, more predictable setup that aligns with modern best practices.

What’s New

The new configuration system provides:
  • Unified configuration object: All initialization parameters are set through DittoConfig factory methods
  • Fallible initialization: Explicit error handling during setup catches configuration issues early
  • Asynchronous patterns: Native async/await support where appropriate for each platform
  • Simplified authentication: Clearer authorization client that’s easier to understand and implement

What This Solves

The previous initialization flow required multiple unintuitive settings due to legacy compatibility concerns. For example, developers had to set disableCloudSync = true to connect to a Big Peer, which was confusing and non-obvious.The new pattern consolidates all configuration into a single, coherent flow that makes the relationship between settings explicit and easier to reason about.

Accessing Configuration at Runtime

After initializing Ditto, you can access the configuration object to retrieve settings like the database ID. This replaces methods like getAppID() from v4:
Each SDK provides access to the config object with language-appropriate naming conventions. See the SDK-specific migration guides for exact syntax.

Availability

The DittoConfig pattern was introduced as an option in v4.12 and becomes the only initialization method in v5.0.
Migration guides for each SDK are available in the v5 documentation. If you’re currently on v4.11 or later, the migration path is straightforward.

Schema-Free Data Modeling

Ditto 5.0 transforms the developer experience by making DQL strict mode disabled by default. This eliminates the need to define collection schemas or CRDT types upfront, allowing you to insert nested objects and data structures without pre-defining types.

What’s New

With strict mode disabled by default, Ditto changes how objects are stored and synchronized:Objects default to MAP type instead of REGISTER typeThis fundamental change means:
  • Field-level sync: Ditto syncs individual field changes instead of replacing entire objects
  • Automatic type inference: No need to pre-define collection schemas or CRDT types
  • Nested structures: Insert complex JSON-like documents without type definitions
  • Concurrent updates merge: When peers update different fields simultaneously, both changes are preserved
For example, if two peers update different fields in the same object concurrently, both changes sync successfully rather than one overwriting the other.

What This Solves

This change provides a more simplified data management structure with two key benefits:
  • Add-wins behavior on objects: When peers create or modify objects, additions are preserved rather than overwritten
  • Field-level delta sync on all nodes: Every field in your document syncs independently, reducing bandwidth and enabling fine-grained conflict resolution throughout the entire document structure

New Customers

Schema-free data modeling is enabled by default in Ditto 5.0. No action is needed—simply start building with automatic type inference and field-level sync.

Migrating Customers

If you’re migrating from Ditto 4.X, set DQL_STRICT_MODE = true to ensure your application behavior remains the same:
This maintains the same data modeling semantics you’re currently using. Once your application is stable on v5, follow the strict mode migration guide and work with the Ditto CX team to migrate to schema-free data modeling and take advantage of the improved developer experience.

Migration Considerations

Strict mode is a local configuration setting on each device that controls how DQL interprets and writes data structures.How it works across peers:
  • Data syncs successfully between peers regardless of different strict mode settings
  • Each peer interprets data based on its own strict mode setting when reading/writing
  • With DQL_STRICT_MODE=false: Objects are inferred as MAPs (field-level merging)
  • With DQL_STRICT_MODE=true: Objects without explicit type definitions are treated as REGISTERs (whole object replacement)
Impact on data:
  • Collections/documents created, modified, or read while strict mode is disabled will use inferred types (objects → MAPs by default)
  • Collections/documents created, modified, or read while strict mode is enabled use default REGISTER type and require explicit definitions for other types
Best practices for mixed deployments:
  • If mixing settings, explicitly define MAP types in collection definitions on peers with strict mode enabled
Learn more about strict mode, cross-peer synchronization, and troubleshooting in the DQL Strict Mode documentation.

New DQL Query Features

Ditto 5.0 introduces several new DQL syntax features that expand query capabilities and make complex queries easier to write.

CASE Statements

Add conditional logic to queries with CASE expressions:

BETWEEN Expressions

The BETWEEN operator provides a shorthand for defining an inclusive numeric range for an expression result.Syntax:
Example:
This is equivalent to (a >= 1 AND a <= 10).
Order matters: The order of expressions is important. Reversing the terms implies an inverse range - BETWEEN 1 AND 10 and BETWEEN 10 AND 1 are NOT equivalent.

Array & Object Search Syntax

Test elements within arrays or objects using ANY, EVERY, or ANY AND EVERY operators:Syntax:
  • IN - Searches the array/object directly
  • WITHIN - Searches recursively through nested structures
  • ANY - Returns true if at least one element matches
  • EVERY - Returns true if all elements match (empty arrays/objects pass)
  • ANY AND EVERY - Like EVERY, but empty arrays/objects fail

Array & Object Transformation Syntax

Create new arrays and objects by transforming existing data structures:Array Transformation Syntax:
Object Transformation Syntax:
Key behaviors:
  • If source evaluates to MISSING, the result is MISSING
  • For arrays: if source is not an array/object, the result is NULL
  • For objects: duplicate field names will overwrite previous values
  • Elements/fields where valueExpr evaluates to MISSING are excluded from the result

Extended String Literals

Support for escape sequences in strings:

Hexadecimal Numeric Constants

Additional numeric literal formats:

Performance & Platform

DQL Query Performance Improvements

Your applications will feel noticeably faster and more responsive. Data queries complete faster, delivering a snappier user experience whether users are searching, filtering, or loading content. These performance gains happen automatically—no code changes required.

Query Planner Enhancements

The DQL query planner has been enhanced to recognize more optimization opportunities:
  • Automatic ID scan conversion: Equality filters on _id fields automatically convert to ID scans, bypassing index lookups when exact document IDs are known
  • Deferred document fetching: Query planner can defer fetching full documents until after sorting and applying offset/limit when index access supports it
  • Improved covering index support: More scenarios where the query planner can satisfy queries entirely from index data without retrieving documents
  • Index-only queries: Additional cases where queries can be answered using only index scans

Streaming Query Execution

The query engine now uses streaming interfaces internally:
  • Reduced memory overhead: Results are streamed rather than fully materialized where possible
  • DISTINCT operator streaming: DISTINCT queries now stream results, reducing memory usage for large result sets
  • Improved operator inlining: Query operators can be inlined into producers for better performance

Shared Statement Cache

Ditto 5.0 introduces a shared statement cache that stores and reuses compiled query plans:
  • Plan reuse: Compiled query plans are cached and reused for identical or similar statements, eliminating redundant parsing and planning overhead
  • Automatic validation: Cached plans are automatically verified and invalidated when collection schemas or system directives change
  • Dynamic sizing: The cache automatically resizes based on workload patterns
  • Improved large statement performance: Particularly benefits complex queries and larger statements by avoiding expensive re-compilation
This optimization is especially impactful for applications that execute the same queries repeatedly, such as real-time dashboards or frequently-accessed data views.
These improvements are automatic - no code changes required. Your existing DQL queries will benefit from the enhanced query planner.

Data Sync Performance Improvements

Building on the performance improvements delivered in v4.13 & v4.14, Ditto 5.0 further optimizes data synchronization through tiered blob storage and protocol enhancements, making sync operations faster and more efficient.

What’s New

Version 5.0 introduces:
  • Tiered blob storage: Smaller sync updates avoid unnecessary disk I/O by using optimized storage tiers
  • Improved session handling: Better avoidance of session resets on reconnection when in-flight updates were lost
  • Optimized fsync policy: Document sync avoids forcing files to disk by default, decreasing I/O and improving latency

Impact

Applications upgrading to v5.0 will experience:
  • Faster sync operations: Reduced disk I/O overhead leads to quicker data synchronization
  • Lower latency: Optimized file handling decreases sync latency across the board
  • Better reconnection handling: Fewer redundant updates after temporary disconnections
  • Improved efficiency: Reduced disk operations lower memory and CPU pressure during sync

Local System Observability

Gain unprecedented visibility into how Ditto operates on your devices. Query real-time metrics, inspect runtime configuration, and monitor system health directly using DQL—giving you deeper insights than ever before to debug issues, optimize performance, and understand your application’s behavior.

New Virtual Collections

system:metrics
  • Query performance metrics and diagnostics in real-time
  • Access counters, timers, and other operational metrics via DQL
  • Monitor system health and performance without external tools
system:system_info
  • Query peer_key, database_id, and configuration settings
  • Inspect runtime configuration and system parameters
  • Useful for debugging and operational awareness
system:shared_statements
  • Inspect the query plan cache
  • View cached statements and their execution plans
  • Supports DELETE operations to clear specific cached statements
  • Helps optimize query performance and troubleshoot query planning
Local-Only Collections: System collections are local to each peer and are not replicated across the mesh. To query these collections on remote peers, use Remote Query from the Ditto Portal.

Usage Example

These collections provide unprecedented visibility into Ditto’s internal state, making it easier to monitor, debug, and optimize your applications.

Additional Improvements

Reliability & Error Handling

  • Enhanced diagnostics: Improved logging when peers receive data that cannot be deserialized
  • Recovery mechanisms: Additional recovery paths for document deserialization errors
  • Smart log levels: Connection failures start at warning level, escalate to error only after repeated failures
  • Panic messages: Filtered to remove internal Rust machinery frames for improved readability

Networking Improvements

  • Graceful shutdown: Network connections close cleanly when Ditto is stopped
  • Faster disconnection detection: When a peer crashes, Ditto stops attempting to connect within 15 seconds (previously up to 75 minutes)
  • mDNS improvements: More reliable mDNS discovery, configurable service names, better address filtering
  • BLE improvements: Fixed connection issues on Android 9 and earlier devices
  • WebSocket BYOD support: Bring Your Own Discovery now supports WebSocket connections
  • Connection cleanup: Fixed deadlock where devices could fail to establish new P2P connections until restarted

DQL Engine Improvements

Beyond the query performance improvements detailed above, v5 includes:
  • Better error messages: Improved parser error messages for invalid DQL syntax
  • Transaction safety: Fixed deadlock scenarios in concurrent transactions
  • Index correctness: Fixed issues where index scans could yield incorrect results on document deletion

Logging & Diagnostics

  • Better disk utilization: On-disk logs resume writing to incomplete files, making better use of available space
  • Compressed size limits: Log file limits now apply to compressed size, significantly increasing retention
  • Explicit flushing: Logs explicitly flushed before aborting due to panic
  • Virtual collections: New system:metrics and system:system_info collections for DQL access to metrics and system information

Platform Support

  • Linux aarch64: Kotlin SDK now supports ARM64 Linux (Raspberry Pi, AWS Graviton, etc.)
  • Swift 6: Full Swift 6 support with Sendable conformance
  • 16KB alignment: React Native Android meets Google Play’s November 2025 requirement

Upgrading to v5

Terminology Updates

Ditto 5.0 updates terminology across the platform to align with industry standards and reduce confusion.

Database ID (formerly App ID)

  • appID → databaseID in all configuration methods
  • getAppId() → getConfig().databaseId in SDK APIs
  • Portal and documentation updated to use “Database ID” terminology
Why this matters: The term “App ID” caused confusion, particularly for mobile developers who associate “app” with the mobile application itself rather than the Ditto database instance. “Database ID” more accurately describes what the identifier represents: a unique identifier for your Ditto database that persists across all clients.

Ditto Server (formerly Ditto Cloud)

  • isConnectedToDittoCloud → isConnectedToDittoServer in presence APIs
  • Documentation updated to use “Ditto Server” terminology
Why this matters: This clarifies that the property indicates connection to any Big Peer (Ditto Server), not just those running in Ditto’s cloud service. This is more accurate for deployments using self-hosted Big Peers.

Breaking Changes

Ditto 5.0 is a major version release that removes deprecated APIs and legacy features. For migration guidance, see Migration Guidance.

Removed APIs

Legacy Query Builder (All SDKs)

All legacy query builder APIs have been removed:
  • store.collection() → Use DQL INSERT, UPDATE, EVICT statements
  • collection.find() → Use DQL SELECT queries
  • collection.findById() → Use DQL with _id filter
  • Live queries → Use DQL observers with store.registerObserver()
  • Write transactions → Use store.transaction() with DQL

Legacy Initialization (All SDKs)

  • Identity classes and all subclasses removed
  • Ditto(identity:, persistenceDirectory:) constructors removed
  • Use DittoConfig factory methods and Ditto.open() instead

Sync Methods Moved

  • ditto.startSync() → ditto.sync.start()
  • ditto.stopSync() → ditto.sync.stop()
  • ditto.isSyncActive → ditto.sync.isActive

Other Removals

  • disableSyncWithV3() - no longer needed, v3 sync removed entirely
  • AttachmentToken - use dictionary variant
  • Transport diagnostics APIs - obsolete, removed
  • Various deprecated presence properties (queryOverlapGroup, meshRole, etc.)
  • Emoji log level headings - setting had no effect, removed

Behavioral Changes

Several default behaviors have changed in v5:
  • DQL strict mode: Now defaults to false - no schema definitions required, automatic CRDT type inference
  • String literals in DQL: Double quotes now delimit strings (not identifiers) for JSON compatibility
  • Subscription queries: Reject LIMIT and ORDER BY with <dql> Unsupported feature: Limit or Order by — see Subscriptions
  • Observer ordering: Observers require explicit ORDER BY clause for stable ordering
  • WebSocket sync: Disabled by default in new TransportConfig instances - must explicitly enable
  • Document IDs: null is no longer allowed as a document ID
These behavioral changes may affect existing code. Review your DQL queries and subscription logic when migrating to v5.

SDK Size Reduction

The removal of legacy APIs has reduced SDK footprint by approximately 25%, resulting in:
  • Smaller application binary sizes
  • Reduced memory usage
  • Faster SDK initialization
  • Simpler maintenance and debugging
SDKSwift
Release Date: May 5, 2026

Swift Migration Guide

Upgrading to Ditto 5.0 requires updating your initialization code and migrating from legacy query APIs to DQL. The migration process involves:
  • Updating from DittoIdentity to DittoConfig-based initialization
  • Replacing legacy query builder operations with DQL statements
  • Migrating collection observers to DQL observers
  • Updating authentication patterns
For comprehensive migration instructions, code examples, and best practices, see the Swift v4 to v5 Migration Guide.

Swift-Specific Changes

The Swift SDK has additional platform-specific changes in v5.0 beyond the common breaking changes.

Removed Platform Support

Removed Platform Support:
  • tvOS - No longer supported in v5.0
  • visionOS (beta) - No longer supported in v5.0
If you have concerns about this change, please reach out to Ditto customer support.

Platform Requirements

Minimum Platform Versions:
  • iOS: 15.0 (previously 13.0)
Swift 6 Compatibility:
  • Full support for Swift 6 strict concurrency checking
  • All public classes marked as final
  • Most public types marked as Sendable
  • Introduced dedicated typealiases:
    • DittoTransactionScope for DittoStore.transaction() scope closures
    • DittoAttachmentFetchEventHandler for DittoStore.fetchAttachment() event handlers

Removed

CocoaPods Support:
  • CocoaPods distribution has been completely removed starting with v5.0
  • Use Swift Package Manager (SPM) or XCFrameworks for integration
  • CocoaPods is effectively end-of-life and no longer maintained
ObjC SDK:
  • The deprecated DittoObjC SDK has been removed
  • No longer required as a dependency for DittoSwift
Other Removals:
  • DittoExperimental.jsonByTranscoding(cbor:) method
  • DittoTransportSnapshot - Leftover from previously removed APIs

API Changes

Query Arguments:
  • DittoSyncSubscription.queryArguments no longer guaranteed to be strictly equal to original arguments due to serialization roundtrip
  • DittoStoreObserver.queryArguments no longer guaranteed to be strictly equal to original arguments due to serialization roundtrip
  • Added queryArgumentsCBORData and queryArgumentsJSONData properties for custom decoding
  • Added overloads accepting Codable-conforming arguments for subscriptions and observers
Attachment APIs:
  • Added DittoStore.newAttachment(data:) - Create attachments from in-memory data without file URL
  • Changed DittoAttachment.len type from Int to UInt64
Disk Usage APIs:
  • Renamed DiskUsage to DittoDiskUsage
  • Renamed DiskUsageItem to DittoDiskUsageItem
  • Renamed DiskUsage.DiskUsageObserverHandle to DittoDiskUsageObserver and made it top-level
Peer & Connection Properties:
  • Renamed DittoPeer.peerKeyString to DittoPeer.peerKey
  • Renamed DittoConnectionRequest.peerKeyString to DittoConnectionRequest.peerKey
  • Renamed DittoConnection.peerKeyString1 to DittoConnection.peer1
  • Renamed DittoConnection.peerKeyString2 to DittoConnection.peer2
  • Renamed DittoPeer.osV2 to DittoPeer.os
Authentication APIs:
  • Converted DittoAuthenticationRequest from Objective-C typealias to pure Swift class
  • Converted DittoAuthenticationSuccess from Objective-C typealias to pure Swift class
  • Renamed DittoAuthenticationRequest.appId to DittoAuthenticationRequest.databaseID

Bug Fixes

  • Fixed iOS mDNS occasionally being very slow to operate (#18682)
  • Fixed mDNS failing to advertise/browse with correct info (#NETW-883)

Migration Example

The initialization flow has changed from Identity-based to DittoConfig-based patterns. Here’s how to migrate:

Accessing Configuration at Runtime

After initialization, you can access your Ditto configuration to retrieve settings like the database ID:

Migration Path

All legacy query operations have direct DQL equivalents:Update Operations:
Observers:
Complete migration examples for all legacy query patterns are available in the Legacy to DQL Migration Guide.

How It Works

The key difference is whether you need to explicitly define MAP types for objects:

Migration

Update your code to use the new terminology:
The actual ID values and functionality remain unchanged - only the parameter and property names have been updated.

Swift Specific Changes

Fixed:
  • iOS mDNS occasionally very slow to operate (#18682)
  • mDNS failed to advertise/browse with correct info (#NETW-883)
Changed:
  • The queryArguments property on DittoSyncSubscriptions is no longer guaranteed to be strictly equal to the original arguments passed when registering the sync subscription. This is due to a serialization roundtrip, which may affect equality checks, particularly for non-primitive values. If you want to decode query arguments into a specific type, then use the queryArgumentsCBORData or queryArgumentsJSONData properties now available on DittoSyncSubscription instances and decode things as required (#17003)
  • The queryArguments property on DittoStoreObservers is no longer guaranteed to be strictly equal to the original arguments passed when registering the store observer. This is due to a serialization roundtrip, which may affect equality checks, particularly for non-primitive values. If you want to decode query arguments into a specific type, then use the queryArgumentsCBORData or queryArgumentsJSONData properties now available on DittoStoreObserver instances and decode things as required (#17003)
  • DittoAttachment.len type from Int to UInt64 (#18587)
  • Minimum deployment targets are now macOS 12.0 and iOS 15.0 (#19096)
  • Renamed DiskUsage to DittoDiskUsage to match the rest of the APIs (#19351)
  • Renamed DiskUsageItem to DittoDiskUsageItem to match the rest of the APIs (#19351)
  • Renamed DiskUsage.DiskUsageObserverHandle to DittoDiskUsageObserver and made it top-level to match the rest of the APIs (#19351)
  • DittoAuthenticationRequest and DittoAuthenticationSuccess converted from Objective-C typealiases to pure Swift classes (#19683)
  • DittoAuthenticationRequest.appId property renamed to DittoAuthenticationRequest.databaseID to match the updated terminology in DittoConfig (#19683)
  • DittoPeer.peerKeyString property renamed to DittoPeer.peerKey (#SDKS-1186)
  • DittoConnectionRequest.peerKeyString property renamed to DittoConnectionRequest.peerKey (#SDKS-1186)
  • DittoConnection.peerKeyString1 property renamed to DittoConnection.peer1 (#SDKS-1186)
  • DittoConnection.peerKeyString2 property renamed to DittoConnection.peer2 (#SDKS-1186)
  • The default value of DittoTransportConfig.listen.websocketSync is now false. It must be explicitly set to true to enable websocket sync (#SDKS-1641)
  • Renamed DittoPeer.isConnectedToDittoCloud to DittoPeer.isConnectedToDittoServer to reflect updated terminology (#SDKS-2189)
  • DittoPeer.osV2 property has been renamed to DittoPeer.os. The corresponding DittoPeer initializer parameter has also been updated accordingly
  • Upgraded DittoSwift SDK to support Swift 6 (#17934)
  • Marked all public classes as final (#17934)
  • Marked most public types as Sendable (#17934)
Added:
  • queryArgumentsCBORData and queryArgumentsJSONData properties to DittoSyncSubscription instances. If you want to decode query arguments into a specific type, then use these and decode things as required (#17003)
  • An overload of DittoSync’s registerSubscription method that takes an arguments value that is Codable-conforming (#17003)
  • queryArgumentsCBORData and queryArgumentsJSONData properties to DittoStoreObserver instances. If you want to decode query arguments into a type you can specify then use these and decode things as required (#17026)
  • An overload of DittoStore’s registerObserver method that takes an arguments value that is Codable-conforming (#17026)
  • DittoStore.newAttachment(data:) to create attachments directly from in-memory data, without requiring a file URL (#18587)
  • Introduced dedicated typealias DittoTransactionScope for the scope closure of DittoStore.transaction() (#17934)
  • Introduced dedicated typealias DittoAttachmentFetchEventHandler for the onFetchEvent closure of DittoStore.fetchAttachment() (#17934)
Deprecated:
  • DittoError.TransportErrorReason and DittoError.transportError for future removal (#1969)
Removed:
  • Ditto.disableSyncWithV3() as Ditto v5 no longer supports syncing with v3 (#19340)
  • DittoError.MigrationErrorReason and its only disableSyncWithV3Failed case, since v3 sync is no longer available in Ditto v5 (#19340)
  • The deprecated DittoObjC SDK has been removed and is not required as a dependency for DittoSwift anymore (#17923)
  • CocoaPods support. Starting with version 5.0, DittoSwift is no longer distributed via Cocoapods. Ditto has moved fully to SwiftPM, as Cocoapods is effectively end-of-life and no longer maintained. Please integrate DittoSwift via SwiftPM going forward (#19635)
  • Ditto.transportDiagnostics() and DittoTransportDiagnostics, as these APIs are now obsolete (#SDKS-812)
  • DittoTransportSnapshot, a leftover from previously deprecated and already removed APIs (#SDKS-812)
  • DittoExperimental.jsonByTranscoding(cbor:) method (#SDKS-855)
  • All deprecated APIs
  • DittoAttachmentToken class is obsolete and has been removed. Please use the dictionary variant instead