Documentation Index
Fetch the complete documentation index at: https://docs.ditto.live/llms.txt
Use this file to discover all available pages before exploring further.
Overview
This guide covers the essential changes needed to migrate your Ditto C++ app from v4 to v5. The main architectural shift is removing the deprecated identity-based constructors and requiring the four-phase configuration model (DittoConfig + Ditto::open) that was introduced in late v4. v5 also removes all legacy collection-based APIs in favor of DQL.
If you already use
Ditto::open(DittoConfig) in v4, the initialization code is largely the same β focus on the removed APIs, renames, and DQL migration sections below.AI Agent Prompt
Use this prompt when working with an AI coding assistant to migrate your Ditto C++ app from v4 to v5.Copy AI Migration Prompt (Click to Expand)
Copy AI Migration Prompt (Click to Expand)
Ditto Instance Initialization
v5 removes the deprecatedDitto() constructors and the Identity class hierarchy. Ditto::open(DittoConfig) β which was already available in v4 β is now the only way to create a Ditto instance.
v5 separates initialization into four distinct phases for better clarity and control:
- Clear separation of connection, initialization, and authentication concerns
- Builder pattern for flexible configuration
- No workarounds needed (DQL strict mode, v3 sync disable)
- Type-safe configuration with compile-time validation
Replace Initialization
Replace the deprecated Key changes:
Ditto() constructor with Ditto::open(config).- Use
DittoConfig::default_config()builder pattern instead ofIdentity app_idβdatabase_idIdentity::OnlinePlaygroundβDittoConfig::Connect::server(...)Identity::OfflinePlaygroundβDittoConfig::Connect::small_peers_only()(empty string = no encryption)Identity::SharedKeyβDittoConfig::Connect::small_peers_only("your-shared-key")- Remove
disable_sync_with_v3()calls - Convenience shortcuts available:
set_server_connect(url)andset_small_peers_only_connect(key)can replaceset_connect(DittoConfig::Connect::server(url))
| v4 Identity | v5 Equivalent |
|---|---|
Identity::OnlinePlayground(app_id, token, cloud_sync, url) | DittoConfig::Connect::server(url) + auth.set_expiration_handler(...) + auth.login(token, Authenticator::get_development_provider(), ...) |
Identity::OnlineWithAuthentication(app_id, callback, cloud_sync, url) | DittoConfig::Connect::server(url) + auth.set_expiration_handler(...) + custom auth logic |
Identity::OfflinePlayground(app_id) | DittoConfig::Connect::small_peers_only() + ditto->set_offline_only_license_token(token) |
Identity::SharedKey(app_id, shared_key) | DittoConfig::Connect::small_peers_only(shared_key) |
Identity::Manual(certificate_config) | Removed β no direct equivalent, consult Ditto support |
Update Authentication
Set authentication handler separately after initialization.Key changes:
- Authentication now uses
set_expiration_handlerlambda - Handler signature:
std::function<void(Ditto &ditto, uint32_t sec_remaining)> - First param is
Ditto &reference β access auth viaditto.get_auth()-> - Handler called when auth required (
sec_remaining == 0) or token expiring - Use
Authenticator::get_development_provider()for playground tokens - Custom auth: pass provider name string directly to
login() get_auth()returnsnullptrif not configured for authentication (e.g.,small_peers_onlywithout a server) β always null-check before accessing
Authenticator::get_status()β returns currentAuthenticationStatusAuthenticator::observe_status(callback)β observe status changesAuthenticator::logout(cleanup)β log out and optionally run cleanup
Additional Changes
DQL Strict Mode Behavior Change
Choose the appropriate migration path based on your current v4 configuration:Currently Using DQL with DQL_STRICT_MODE=true (v4 default)
Currently Using DQL with DQL_STRICT_MODE=true (v4 default)
If youβre currently using the v4 default (
DQL_STRICT_MODE=true), you must explicitly set strict mode to true in v5 before starting sync or executing any queries.To migrate to
DQL_STRICT_MODE=false (the new v5 default), contact Ditto Customer Support for guidance.Currently Using DQL with DQL_STRICT_MODE=false
Currently Using DQL with DQL_STRICT_MODE=false
If you explicitly set
DQL_STRICT_MODE=false in v4, no changes are required.v5 uses DQL_STRICT_MODE=false as the default, so your existing DQL queries will behave identically. You can upgrade freely.Optional: You can remove the explicit ALTER SYSTEM SET DQL_STRICT_MODE = false statement in v5 since this is now the default behavior.Currently Using Legacy Query Builder
Currently Using Legacy Query Builder
The Legacy Query Builder has been removed in v5. All queries must be converted to DQL before upgrading.Good news: Legacy Query Builder functionality has 1:1 support with
DQL_STRICT_MODE=false (the v5 default), making migrations straightforward.Migration Steps:-
In v4: Set
DQL_STRICT_MODE=falseafter initialization: - Convert all queries from Legacy Query Builder to DQL See the C++ LegacyβDQL Migration Guide for detailed conversion examples.
-
Upgrade to v5
No DQL configuration changes requiredβv5 defaults to
DQL_STRICT_MODE=false.
Default Persistence Directory
v5 includes the database ID in the default directory name:ditto-{database_id} instead of ditto. This is for only new databases created in v5. v5 does not migrate existing databases to the new structure.
To maintain v4 compatibility:
Observer Changes
v4 and v5 both use callback-based observers. The coreregister_observer API pattern is the same in both versions, returning a StoreObserver that you hold via shared_ptr and call .cancel() on for cleanup.
- v5 adds introspection methods to
StoreObserver:get_query(),get_query_arguments(),get_query_arguments_cbor_data(),get_query_arguments_json_string() - Container type changed:
store.get_observers()returnsstd::unordered_set(v4 returnedstd::set) - The deprecated
store.observerspublic field is removed β usestore.get_observers()
Back Pressure (SignalNext)
The standardregister_observer automatically calls signal_next after your handler returns. Use the StoreObservationHandlerWithSignalNext overload when your handler needs manual control over when the next callback is delivered.
Coalescing behavior: While your handler is processing, Ditto coalesces any intermediate changes. When you call
signal_next(), you receive a single callback with the latest state β not every intermediate change.SignalNext=std::function<void()>(renamed from v4βsNextSignal)StoreObservationHandlerWithSignalNext=std::function<void(QueryResult, SignalNext)>(renamed from v4βsStoreObservationHandlerWithNextSignal)
Presence API Changes
Presence is now accessed throughditto->get_presence() rather than directly on the Ditto object.
| v4.14 | v5.0 |
|---|---|
peer.peer_key_string | peer.peer_key |
peer.is_connected_to_ditto_cloud | peer.is_connected_to_ditto_server |
connection.peer_key_string1 | connection.peer1 |
connection.peer_key_string2 | connection.peer2 |
connection.approximate_distance_in_meters | REMOVED β no replacement |
Presence::graph()β get currentPresenceGraphimmediatelyPresence::peer_metadata()/set_peer_metadata(json)β get/set arbitrary peer metadataPresence::peer_metadata_json_string()/set_peer_metadata_json_string(str)β JSON string variantsPresence::set_connection_request_handler(handler)β control which peers can connect
DiskUsage API Changes
| v4.14 | v5.0 |
|---|---|
DiskUsageChild class | DiskUsageItem class |
DiskObserverContext class | DiskUsageObserver class |
DiskUsage::exec() | DiskUsage::item() |
ditto->get_disk_usage() | ditto->get_disk_usage() (unchanged) |
disk_usage.observe(cb) | disk_usage.observe(cb) (unchanged, callback type same) |
Transport Config Changes
update_transport_config and set_transport_config remain available in v5. The key change is:
| v4.14 | v5.0 |
|---|---|
HttpListenConfig::static_content_path | REMOVED |
| All other TransportConfig fields | Unchanged |
Transaction API Changes
v5 removes the deprecatedWriteTransaction and related classes. Use the DQL-based transaction API instead:
Store::write(fn)withWriteTransactionβ removedWriteTransaction,ScopedWriteTransaction,WriteTransactionResult,UpdateResultβ removed- DQL-based
Store::transaction()andStore::execute_transaction()are the replacement - New:
Store::transaction_returning<T>()β return a value from a transaction TransactionOptionsavailable:set_read_only(bool),set_hint(string)
API Renames
| v4.14 | v5.0 |
|---|---|
ditto->start_sync() | ditto->get_sync().start() |
ditto->stop_sync() | ditto->get_sync().stop() |
ditto->get_is_sync_active() | ditto->get_sync().is_active() |
ditto->observe_peers(cb) | ditto->get_presence().observe(cb) |
Ditto::get_sdk_version() | Ditto::get_version() |
Log class (all static methods) | Logger class |
Log::Callback (raw function pointer) | Logger::Callback (std::function<void(LogLevel, const std::string&)>) |
Log::set_custom_log_cb(callback) | Logger::set_custom_log_cb(callback) |
Log::set_log_file(path) | Removed β continuous file logging has no v5 equivalent. Use Logger::set_custom_log_cb() to implement your own file writer, or call Logger::export_to_file() for one-time log dumps. |
Log::disable_log_file() | Removed with set_log_file |
StoreObservationHandlerWithNextSignal | StoreObservationHandlerWithSignalNext |
NextSignal type alias | SignalNext type alias |
store.observers (deprecated public field) | Removed β use store.get_observers() (returns std::unordered_set) |
Sync::subscriptions (deprecated public field) | Removed β use Sync::get_subscriptions() (returns SyncSubscriptionSet) |
store.get_observers() returns std::set | store.get_observers() returns std::unordered_set |
Sync::get_subscriptions() returns std::set | Sync::get_subscriptions() returns SyncSubscriptionSet (unordered_set) |
Ditto() deprecated constructor | Removed β use Ditto::open(DittoConfig) |
Identity class hierarchy | Removed β use DittoConfig |
DiskUsageChild | DiskUsageItem |
DiskObserverContext | DiskUsageObserver |
DiskUsage::exec() | DiskUsage::item() |
Log β Logger Rename
AllLog:: call sites must be updated to Logger:::
Log::Callback changed from a raw function pointer (void (*)(LogLevel, std::string)) to std::function<void(LogLevel, const std::string&)> β lambdas with captures are now supported.
Logger::export_to_file(path) performs a one-time export of accumulated logs to disk. It returns std::future<uint64_t> (number of bytes written). The output is gzip-compressed JSON lines (.jsonl.gz recommended extension). This is not a replacement for Log::set_log_file() β it existed in v4 as Log::export_to_file() and is only renamed in v5.
Migrating from Log::set_log_file(): Continuous real-time file logging has been removed in v5. If you need ongoing file logging, use Logger::set_custom_log_cb() to register a callback that writes to a file yourself.
Header Count Reduced
v5.0 ships 50 header files (down from 86 in v4.14). 38 headers were removed (legacy collection, document, live query, write transaction, identity, and log APIs). 2 headers were added (Logger.hpp, fnv_1a_hash.hpp). If your build system includes specific headers by path, audit the include list after upgrading.
Ditto Lifetime Management
Ditto::close() is removed. Manage Ditto lifetime via the shared_ptr returned by Ditto::open(). The instance is destroyed when all shared_ptr copies go out of scope.
APIs Removed Without Replacement
| Removed API | Notes |
|---|---|
Ditto::close() | Use shared_ptr lifetime management |
Ditto::disable_sync_with_v3() | v3 protocol support fully dropped |
Ditto() constructors (with Identity) | Use Ditto::open(DittoConfig) |
Identity class hierarchy | Replaced by DittoConfig |
Identity::Manual | No v5 equivalent β consult Ditto support |
Log class | Replaced by Logger class |
Log::get_emoji_log_level_headings_enabled() / set_emoji_log_level_headings_enabled() | Emoji log headings removed |
Log::set_log_file(path) / disable_log_file() | Continuous file logging removed β no direct replacement. Use Logger::set_custom_log_cb() for ongoing file output, or Logger::export_to_file() for one-time dumps. |
HttpListenConfig::static_content_path | Static HTTP content serving removed |
LiveQueryEvent::hash() / hash_mnemonic() | Query hash methods removed |
Connection::approximate_distance_in_meters | Peer distance estimation removed |
store.observers (public field) | Use store.get_observers() method |
Sync::subscriptions (public field) | Use Sync::get_subscriptions() method |
Legacy Collection API β Completely Removed
All collection-based query APIs (38 headers removed) are gone in v5. Use DQL queries viaget_store().execute().
| Removed Header / Class | Replacement |
|---|---|
Collection | DQL queries via store.execute() |
Document / MutableDocument | QueryResult items |
DocumentPath / MutableDocumentPath | Access values via QueryResult |
DocumentIdPath / AbstractDocumentPath | Use _id field in DQL |
PendingCursorOperation / PendingIDSpecificOperation | DQL queries |
PendingCollectionsOperation | DQL: SELECT DISTINCT collection FROM __collections |
LiveQuery / LiveQueryEvent / LiveQueryMove | store.register_observer() |
SingleDocumentLiveQueryEvent | store.register_observer() with WHERE _id = :id |
Subscription (legacy) | sync.register_subscription() |
WriteTransaction / ScopedWriteTransaction | store.transaction() or store.execute_transaction() |
WriteTransactionResult / UpdateResult | QueryResult.mutated_document_ids() |
WriteStrategy | DQL ON ID CONFLICT clauses |
SortDirection | DQL ORDER BY ... ASC/DESC |
Counter / MutableCounter | DQL PN_INCREMENT |
Register / MutableRegister | DQL SET |
CollectionsEvent | DQL queries |
New APIs in v5
Ditto
Ditto::get_version()β replacesget_sdk_version()Ditto::get_presence()β returnsPresence &for presence operations
Logger
Loggerclass β replacesLog; callback type is nowstd::functionLogger::export_to_file(path)β one-time export of accumulated logs; returnsstd::future<uint64_t>(bytes written as gzip-compressed JSON lines). Renamed fromLog::export_to_file()β not a replacement for the removedLog::set_log_file().
Sync
ditto->get_sync().start()/.stop()/.is_active()β sync lifecycle onSyncobjectSyncSubscriptionHash/SyncSubscriptionEqβ hash/eq helpers for subscriptionunordered_setSyncSubscriptionSettype alias βunordered_setofSyncSubscriptionshared_ptrsSyncSubscription::get_query()/get_query_arguments()/get_query_arguments_cbor_data()/get_query_arguments_json_string()β introspect registered subscription
Store & Observers
StoreObservationHandlerWithSignalNext/SignalNextβ renamed fromNextSignalvariantsStoreObserverHash/StoreObserverEqβ hash/eq helpers for observerunordered_setStoreObserver::get_query()/get_query_arguments()/get_query_arguments_cbor_data()/get_query_arguments_json_string()β introspect registered observerStore::execute_transaction()β convenience transaction with auto-commitStore::transaction_returning<T>()β transaction that returns a value
Authentication
Authenticator::get_status()β returns currentAuthenticationStatusAuthenticator::observe_status(callback)β observe authentication status changesAuthenticator::logout(cleanup)β log out with optional cleanup callback
Presence
Presence::observe(cb)β replacesDitto::observe_peers()Presence::graph()β get currentPresenceGraphimmediatelyPresence::peer_metadata()/set_peer_metadata(json)β get/set arbitrary peer metadataPresence::peer_metadata_json_string()/set_peer_metadata_json_string(str)β JSON string variantsPresence::set_connection_request_handler(handler)β control incoming peer connections
DiskUsage
DiskUsageItemβ renamed fromDiskUsageChildDiskUsageObserverβ renamed fromDiskObserverContextDiskUsage::item()β renamed fromDiskUsage::exec()
Configuration
DittoConfig::set_server_connect(url)β convenience shortcut forset_connect(Connect::server(url))DittoConfig::set_small_peers_only_connect(key)β convenience shortcut forset_connect(Connect::small_peers_only(key))
Migration Checklist
Initialization
- Replace deprecated
Ditto()constructor withDitto::open(config) - Create
DittoConfigwithset_database_idandset_connect - Update
Identity::OnlinePlaygroundβDittoConfig::Connect::server(...) - Update
Identity::OfflinePlaygroundβDittoConfig::Connect::small_peers_only() - Update
Identity::SharedKeyβDittoConfig::Connect::small_peers_only("your-shared-key") - Remove
disable_sync_with_v3()calls - Set
DQL_STRICT_MODE=trueBEFORE starting sync if maintaining v4 behavior
Authentication
- Set
set_expiration_handlerlambda after initialization - Handler receives
Ditto &ditto, uint32_t sec_remaining - Access auth inside handler via
ditto.get_auth()->login(...) - Use
Authenticator::get_development_provider()for playground tokens - Remove authentication from identity configuration
- Null-check
get_auth()β returnsnullptrfor non-authenticated configs
Sync
- Update
start_sync()βget_sync().start() - Update
stop_sync()βget_sync().stop() - Update
get_is_sync_active()βget_sync().is_active()
Data Operations
- Replace all collection-based queries with DQL via
get_store().execute() - Use parameterized queries with
:paramNameβ never string interpolation - Replace
.upsert()withINSERT INTO ... ON ID CONFLICT DO UPDATE - Replace
.update {}closures withUPDATE SETDQL - Replace
.remove()withDELETE FROM - Replace
.evict()withEVICT FROM - Replace
.observe_local()withget_store().register_observer() - Replace
.subscribe()withget_sync().register_subscription() - Replace
Store::write(fn)withStore::transaction()orStore::execute_transaction()
Presence
- Update
observe_peers(cb)βget_presence().observe(cb) - Update
peer_key_stringβpeer_key - Update
peer_key_string1/2βpeer1/2 - Update
is_connected_to_ditto_cloudβis_connected_to_ditto_server - Remove
approximate_distance_in_metersusage (no replacement)
Logging
- Replace
Log::withLogger::throughout codebase - Update
Log::Callbackstored variables: raw function pointer βstd::function - Update
Log::set_custom_log_cbβLogger::set_custom_log_cb - Remove
Log::set_log_file(path)calls β continuous file logging removed. UseLogger::set_custom_log_cb()if you need ongoing file output. - Remove
Log::disable_log_file()calls - Remove
Log::set_emoji_log_level_headings_enabled()calls
DiskUsage
- Update
DiskUsageChildβDiskUsageItem - Update
DiskUsage::exec()βDiskUsage::item()
Breaking Changes
- Set
set_persistence_directoryif maintaining v4 directory structure - Replace
StoreObservationHandlerWithNextSignalβStoreObservationHandlerWithSignalNext - Replace
NextSignalβSignalNexttype alias - Replace
store.observersfield access withstore.get_observers()(now returnsunordered_set) - Replace
Sync::subscriptionsfield access withSync::get_subscriptions()(now returnsunordered_set) - Remove
Ditto::close()calls β manage lifetime viashared_ptr - Replace
get_sdk_version()withget_version() - Audit header includes β header count reduced from 86 to 50 (38 removed, 2 added)
- If processing is expensive, use
StoreObservationHandlerWithSignalNextoverload for back-pressure control - Remove
HttpListenConfig::static_content_pathreferences
Verification
- Build compiles with zero errors
- No deprecated API warnings
- Observers update data immediately
- Authentication works before sync starts
- No memory leaks
Common Pitfalls
-
DQL_STRICT_MODE silent change: Not setting
DQL_STRICT_MODE=truewhen your v4 app used the default. Objects that were replaced whole will now merge at field level β causes unexpected data merging. -
Wrong auth accessor: Using
ditto->auth()instead ofditto->get_auth(). Inside the expiration handler, theDitto &reference requiresditto.get_auth()->login(...)(note:.for the reference,->for the shared_ptr). -
Null auth pointer:
get_auth()returnsnullptrforsmall_peers_onlyconfigurations without a server. Always null-check before calling methods on the result. -
String interpolation in queries: Never build queries with string concatenation. Always use parameterized queries:
execute("SELECT * FROM cars WHERE color = :color", {{"color", "blue"}}). -
Counter initialization: Donβt put
0in insert documents for counter fields. Counters are created on firstPN_INCREMENT. Inserting0creates a REGISTER, not a COUNTER. -
Missing
ON ID CONFLICT: INSERT fails if document_idalready exists without a conflict clause. -
Log::Callbacktype change: v4 used a raw function pointer (void(*)(LogLevel, std::string)). v5 usesstd::function<void(LogLevel, const std::string&)>. Code storing callbacks as function pointers must be updated. -
shared_ptrlifetime:Ditto::open()returnsshared_ptr<Ditto>. The instance is destroyed when all copies go out of scope. Do not callclose()β it was removed. -
Observer container type:
store.get_observers()now returnsstd::unordered_setinstead ofstd::set. Code that depends on ordered iteration must be updated.