diff --git a/native/cpp/CommonCpp/NativeModules/CommCoreModule.cpp b/native/cpp/CommonCpp/NativeModules/CommCoreModule.cpp
index 391f6af03..5145f5b25 100644
--- a/native/cpp/CommonCpp/NativeModules/CommCoreModule.cpp
+++ b/native/cpp/CommonCpp/NativeModules/CommCoreModule.cpp
@@ -1,2166 +1,2183 @@
 #include "CommCoreModule.h"
 #include "../Notifications/BackgroundDataStorage/NotificationsCryptoModule.h"
 #include "BaseDataStore.h"
 #include "CommServicesAuthMetadataEmitter.h"
 #include "DatabaseManager.h"
 #include "InternalModules/GlobalDBSingleton.h"
 #include "InternalModules/RustPromiseManager.h"
 #include "NativeModuleUtils.h"
 #include "TerminateApp.h"
 
 #include <ReactCommon/TurboModuleUtils.h>
 #include <folly/dynamic.h>
 #include <folly/json.h>
 #include <future>
 
 #include "JSIRust.h"
 #include "lib.rs.h"
 #include <algorithm>
 #include <string>
 
 namespace comm {
 
 using namespace facebook::react;
 
 jsi::Value CommCoreModule::getDraft(jsi::Runtime &rt, jsi::String key) {
   std::string keyStr = key.utf8(rt);
   return createPromiseAsJSIValue(
       rt, [=](jsi::Runtime &innerRt, std::shared_ptr<Promise> promise) {
         taskType job = [=, &innerRt]() {
           std::string error;
           std::string draftStr;
           try {
             draftStr = DatabaseManager::getQueryExecutor().getDraft(keyStr);
           } catch (std::system_error &e) {
             error = e.what();
           }
           this->jsInvoker_->invokeAsync([=, &innerRt]() {
             if (error.size()) {
               promise->reject(error);
               return;
             }
             jsi::String draft = jsi::String::createFromUtf8(innerRt, draftStr);
             promise->resolve(std::move(draft));
           });
         };
         GlobalDBSingleton::instance.scheduleOrRunCancellable(
             job, promise, this->jsInvoker_);
       });
 }
 
 jsi::Value CommCoreModule::updateDraft(
     jsi::Runtime &rt,
     jsi::String key,
     jsi::String text) {
   std::string keyStr = key.utf8(rt);
   std::string textStr = text.utf8(rt);
   return createPromiseAsJSIValue(
       rt, [=](jsi::Runtime &innerRt, std::shared_ptr<Promise> promise) {
         taskType job = [=]() {
           std::string error;
           try {
             DatabaseManager::getQueryExecutor().updateDraft(keyStr, textStr);
           } catch (std::system_error &e) {
             error = e.what();
           }
           this->jsInvoker_->invokeAsync([=]() {
             if (error.size()) {
               promise->reject(error);
             } else {
               promise->resolve(true);
             }
           });
         };
         GlobalDBSingleton::instance.scheduleOrRunCancellable(
             job, promise, this->jsInvoker_);
       });
 }
 
 jsi::Value CommCoreModule::moveDraft(
     jsi::Runtime &rt,
     jsi::String oldKey,
     jsi::String newKey) {
   std::string oldKeyStr = oldKey.utf8(rt);
   std::string newKeyStr = newKey.utf8(rt);
 
   return createPromiseAsJSIValue(
       rt, [=](jsi::Runtime &innerRt, std::shared_ptr<Promise> promise) {
         taskType job = [=]() {
           std::string error;
           bool result = false;
           try {
             result = DatabaseManager::getQueryExecutor().moveDraft(
                 oldKeyStr, newKeyStr);
           } catch (std::system_error &e) {
             error = e.what();
           }
           this->jsInvoker_->invokeAsync([=]() {
             if (error.size()) {
               promise->reject(error);
             } else {
               promise->resolve(result);
             }
           });
         };
         GlobalDBSingleton::instance.scheduleOrRunCancellable(
             job, promise, this->jsInvoker_);
       });
 }
 
 jsi::Value CommCoreModule::getClientDBStore(jsi::Runtime &rt) {
   return createPromiseAsJSIValue(
       rt, [=](jsi::Runtime &innerRt, std::shared_ptr<Promise> promise) {
         taskType job = [=, &innerRt]() {
           std::string error;
           std::vector<Draft> draftsVector;
           std::vector<Thread> threadsVector;
           std::vector<std::pair<Message, std::vector<Media>>> messagesVector;
           std::vector<MessageStoreThread> messageStoreThreadsVector;
           std::vector<Report> reportStoreVector;
           std::vector<UserInfo> userStoreVector;
           std::vector<KeyserverInfo> keyserverStoreVector;
           std::vector<CommunityInfo> communityStoreVector;
           std::vector<IntegrityThreadHash> integrityStoreVector;
           std::vector<SyncedMetadataEntry> syncedMetadataStoreVector;
           std::vector<AuxUserInfo> auxUserStoreVector;
           std::vector<ThreadActivityEntry> threadActivityStoreVector;
           try {
             draftsVector = DatabaseManager::getQueryExecutor().getAllDrafts();
             messagesVector =
                 DatabaseManager::getQueryExecutor().getAllMessages();
             threadsVector = DatabaseManager::getQueryExecutor().getAllThreads();
             messageStoreThreadsVector =
                 DatabaseManager::getQueryExecutor().getAllMessageStoreThreads();
             reportStoreVector =
                 DatabaseManager::getQueryExecutor().getAllReports();
             userStoreVector = DatabaseManager::getQueryExecutor().getAllUsers();
             keyserverStoreVector =
                 DatabaseManager::getQueryExecutor().getAllKeyservers();
             communityStoreVector =
                 DatabaseManager::getQueryExecutor().getAllCommunities();
             integrityStoreVector = DatabaseManager::getQueryExecutor()
                                        .getAllIntegrityThreadHashes();
             syncedMetadataStoreVector =
                 DatabaseManager::getQueryExecutor().getAllSyncedMetadata();
             auxUserStoreVector =
                 DatabaseManager::getQueryExecutor().getAllAuxUserInfos();
             threadActivityStoreVector = DatabaseManager::getQueryExecutor()
                                             .getAllThreadActivityEntries();
           } catch (std::system_error &e) {
             error = e.what();
           }
           auto draftsVectorPtr =
               std::make_shared<std::vector<Draft>>(std::move(draftsVector));
           auto messagesVectorPtr = std::make_shared<
               std::vector<std::pair<Message, std::vector<Media>>>>(
               std::move(messagesVector));
           auto threadsVectorPtr =
               std::make_shared<std::vector<Thread>>(std::move(threadsVector));
           auto messageStoreThreadsVectorPtr =
               std::make_shared<std::vector<MessageStoreThread>>(
                   std::move(messageStoreThreadsVector));
           auto reportStoreVectorPtr = std::make_shared<std::vector<Report>>(
               std::move(reportStoreVector));
           auto userStoreVectorPtr = std::make_shared<std::vector<UserInfo>>(
               std::move(userStoreVector));
           auto keyserveStoreVectorPtr =
               std::make_shared<std::vector<KeyserverInfo>>(
                   std::move(keyserverStoreVector));
           auto communityStoreVectorPtr =
               std::make_shared<std::vector<CommunityInfo>>(
                   std::move(communityStoreVector));
           auto integrityStoreVectorPtr =
               std::make_shared<std::vector<IntegrityThreadHash>>(
                   std::move(integrityStoreVector));
           auto syncedMetadataStoreVectorPtr =
               std::make_shared<std::vector<SyncedMetadataEntry>>(
                   std::move(syncedMetadataStoreVector));
           auto auxUserStoreVectorPtr =
               std::make_shared<std::vector<AuxUserInfo>>(
                   std::move(auxUserStoreVector));
           auto threadActivityStoreVectorPtr =
               std::make_shared<std::vector<ThreadActivityEntry>>(
                   std::move(threadActivityStoreVector));
           this->jsInvoker_->invokeAsync([&innerRt,
                                          draftsVectorPtr,
                                          messagesVectorPtr,
                                          threadsVectorPtr,
                                          messageStoreThreadsVectorPtr,
                                          reportStoreVectorPtr,
                                          userStoreVectorPtr,
                                          keyserveStoreVectorPtr,
                                          communityStoreVectorPtr,
                                          integrityStoreVectorPtr,
                                          syncedMetadataStoreVectorPtr,
                                          auxUserStoreVectorPtr,
                                          threadActivityStoreVectorPtr,
                                          error,
                                          promise,
                                          draftStore = this->draftStore,
                                          threadStore = this->threadStore,
                                          messageStore = this->messageStore,
                                          reportStore = this->reportStore,
                                          userStore = this->userStore,
                                          keyserverStore = this->keyserverStore,
                                          communityStore = this->communityStore,
                                          integrityStore = this->integrityStore,
                                          syncedMetadataStore =
                                              this->syncedMetadataStore,
                                          auxUserStore = this->auxUserStore,
                                          threadActivityStore =
                                              this->threadActivityStore]() {
             if (error.size()) {
               promise->reject(error);
               return;
             }
             jsi::Array jsiDrafts =
                 draftStore.parseDBDataStore(innerRt, draftsVectorPtr);
             jsi::Array jsiMessages =
                 messageStore.parseDBDataStore(innerRt, messagesVectorPtr);
             jsi::Array jsiThreads =
                 threadStore.parseDBDataStore(innerRt, threadsVectorPtr);
             jsi::Array jsiMessageStoreThreads =
                 messageStore.parseDBMessageStoreThreads(
                     innerRt, messageStoreThreadsVectorPtr);
             jsi::Array jsiReportStore =
                 reportStore.parseDBDataStore(innerRt, reportStoreVectorPtr);
             jsi::Array jsiUserStore =
                 userStore.parseDBDataStore(innerRt, userStoreVectorPtr);
             jsi::Array jsiKeyserverStore = keyserverStore.parseDBDataStore(
                 innerRt, keyserveStoreVectorPtr);
             jsi::Array jsiCommunityStore = communityStore.parseDBDataStore(
                 innerRt, communityStoreVectorPtr);
             jsi::Array jsiIntegrityStore = integrityStore.parseDBDataStore(
                 innerRt, integrityStoreVectorPtr);
             jsi::Array jsiSyncedMetadataStore =
                 syncedMetadataStore.parseDBDataStore(
                     innerRt, syncedMetadataStoreVectorPtr);
             jsi::Array jsiAuxUserStore =
                 auxUserStore.parseDBDataStore(innerRt, auxUserStoreVectorPtr);
             jsi::Array jsiThreadActivityStore =
                 threadActivityStore.parseDBDataStore(
                     innerRt, threadActivityStoreVectorPtr);
 
             auto jsiClientDBStore = jsi::Object(innerRt);
             jsiClientDBStore.setProperty(innerRt, "messages", jsiMessages);
             jsiClientDBStore.setProperty(innerRt, "threads", jsiThreads);
             jsiClientDBStore.setProperty(innerRt, "drafts", jsiDrafts);
             jsiClientDBStore.setProperty(
                 innerRt, "messageStoreThreads", jsiMessageStoreThreads);
             jsiClientDBStore.setProperty(innerRt, "reports", jsiReportStore);
             jsiClientDBStore.setProperty(innerRt, "users", jsiUserStore);
             jsiClientDBStore.setProperty(
                 innerRt, "keyservers", jsiKeyserverStore);
             jsiClientDBStore.setProperty(
                 innerRt, "communities", jsiCommunityStore);
             jsiClientDBStore.setProperty(
                 innerRt, "integrityThreadHashes", jsiIntegrityStore);
             jsiClientDBStore.setProperty(
                 innerRt, "syncedMetadata", jsiSyncedMetadataStore);
             jsiClientDBStore.setProperty(
                 innerRt, "auxUserInfos", jsiAuxUserStore);
             jsiClientDBStore.setProperty(
                 innerRt, "threadActivityEntries", jsiThreadActivityStore);
 
             promise->resolve(std::move(jsiClientDBStore));
           });
         };
         GlobalDBSingleton::instance.scheduleOrRunCancellable(
             job, promise, this->jsInvoker_);
       });
 }
 
 jsi::Value CommCoreModule::removeAllDrafts(jsi::Runtime &rt) {
   return createPromiseAsJSIValue(
       rt, [=](jsi::Runtime &innerRt, std::shared_ptr<Promise> promise) {
         taskType job = [=]() {
           std::string error;
           try {
             DatabaseManager::getQueryExecutor().removeAllDrafts();
           } catch (std::system_error &e) {
             error = e.what();
           }
           this->jsInvoker_->invokeAsync([=]() {
             if (error.size()) {
               promise->reject(error);
               return;
             }
             promise->resolve(jsi::Value::undefined());
           });
         };
         GlobalDBSingleton::instance.scheduleOrRunCancellable(
             job, promise, this->jsInvoker_);
       });
 }
 
 jsi::Array CommCoreModule::getAllMessagesSync(jsi::Runtime &rt) {
   auto messagesVector = NativeModuleUtils::runSyncOrThrowJSError<
       std::vector<std::pair<Message, std::vector<Media>>>>(rt, []() {
     return DatabaseManager::getQueryExecutor().getAllMessages();
   });
   auto messagesVectorPtr =
       std::make_shared<std::vector<std::pair<Message, std::vector<Media>>>>(
           std::move(messagesVector));
   jsi::Array jsiMessages =
       this->messageStore.parseDBDataStore(rt, messagesVectorPtr);
   return jsiMessages;
 }
 
 void CommCoreModule::processMessageStoreOperationsSync(
     jsi::Runtime &rt,
     jsi::Array operations) {
   return this->messageStore.processStoreOperationsSync(
       rt, std::move(operations));
 }
 
 jsi::Array CommCoreModule::getAllThreadsSync(jsi::Runtime &rt) {
   auto threadsVector =
       NativeModuleUtils::runSyncOrThrowJSError<std::vector<Thread>>(rt, []() {
         return DatabaseManager::getQueryExecutor().getAllThreads();
       });
 
   auto threadsVectorPtr =
       std::make_shared<std::vector<Thread>>(std::move(threadsVector));
   jsi::Array jsiThreads =
       this->threadStore.parseDBDataStore(rt, threadsVectorPtr);
 
   return jsiThreads;
 }
 
 void CommCoreModule::processThreadStoreOperationsSync(
     jsi::Runtime &rt,
     jsi::Array operations) {
   this->threadStore.processStoreOperationsSync(rt, std::move(operations));
 }
 
 void CommCoreModule::processReportStoreOperationsSync(
     jsi::Runtime &rt,
     jsi::Array operations) {
   this->reportStore.processStoreOperationsSync(rt, std::move(operations));
 }
 
 template <typename T>
 void CommCoreModule::appendDBStoreOps(
     jsi::Runtime &rt,
     jsi::Object &operations,
     const char *key,
     T &store,
     std::shared_ptr<std::vector<std::unique_ptr<DBOperationBase>>>
         &destination) {
   auto opsObject = operations.getProperty(rt, key);
   if (opsObject.isObject()) {
     auto ops = store.createOperations(rt, opsObject.asObject(rt).asArray(rt));
     std::move(
         std::make_move_iterator(ops.begin()),
         std::make_move_iterator(ops.end()),
         std::back_inserter(*destination));
   }
 }
 
 jsi::Value CommCoreModule::processDBStoreOperations(
     jsi::Runtime &rt,
     jsi::Object operations) {
   std::string createOperationsError;
 
   auto storeOpsPtr =
       std::make_shared<std::vector<std::unique_ptr<DBOperationBase>>>();
   try {
     this->appendDBStoreOps(
         rt, operations, "draftStoreOperations", this->draftStore, storeOpsPtr);
     this->appendDBStoreOps(
         rt,
         operations,
         "threadStoreOperations",
         this->threadStore,
         storeOpsPtr);
     this->appendDBStoreOps(
         rt,
         operations,
         "messageStoreOperations",
         this->messageStore,
         storeOpsPtr);
     this->appendDBStoreOps(
         rt,
         operations,
         "reportStoreOperations",
         this->reportStore,
         storeOpsPtr);
     this->appendDBStoreOps(
         rt, operations, "userStoreOperations", this->userStore, storeOpsPtr);
     this->appendDBStoreOps(
         rt,
         operations,
         "keyserverStoreOperations",
         this->keyserverStore,
         storeOpsPtr);
     this->appendDBStoreOps(
         rt,
         operations,
         "communityStoreOperations",
         this->communityStore,
         storeOpsPtr);
     this->appendDBStoreOps(
         rt,
         operations,
         "integrityStoreOperations",
         this->integrityStore,
         storeOpsPtr);
     this->appendDBStoreOps(
         rt,
         operations,
         "syncedMetadataStoreOperations",
         this->syncedMetadataStore,
         storeOpsPtr);
     this->appendDBStoreOps(
         rt,
         operations,
         "auxUserStoreOperations",
         this->auxUserStore,
         storeOpsPtr);
     this->appendDBStoreOps(
         rt,
         operations,
         "threadActivityStoreOperations",
         this->threadActivityStore,
         storeOpsPtr);
   } catch (std::runtime_error &e) {
     createOperationsError = e.what();
   }
 
   return facebook::react::createPromiseAsJSIValue(
       rt,
       [=](jsi::Runtime &innerRt,
           std::shared_ptr<facebook::react::Promise> promise) {
         taskType job = [=]() {
           std::string error = createOperationsError;
 
           if (!error.size()) {
             try {
               DatabaseManager::getQueryExecutor().beginTransaction();
               for (const auto &operation : *storeOpsPtr) {
                 operation->execute();
               }
               DatabaseManager::getQueryExecutor().captureBackupLogs();
               DatabaseManager::getQueryExecutor().commitTransaction();
             } catch (std::system_error &e) {
               error = e.what();
               DatabaseManager::getQueryExecutor().rollbackTransaction();
             }
           }
 
           if (!error.size()) {
             ::triggerBackupFileUpload();
           }
 
           this->jsInvoker_->invokeAsync([=]() {
             if (error.size()) {
               promise->reject(error);
             } else {
               promise->resolve(jsi::Value::undefined());
             }
           });
         };
         GlobalDBSingleton::instance.scheduleOrRunCancellable(
             job, promise, this->jsInvoker_);
       });
 }
 
 void CommCoreModule::terminate(jsi::Runtime &rt) {
   TerminateApp::terminate();
 }
 
 const std::string
 getAccountDataKey(const std::string secureStoreAccountDataKey) {
   folly::Optional<std::string> storedSecretKey =
       CommSecureStore::get(secureStoreAccountDataKey);
   if (!storedSecretKey.hasValue()) {
     storedSecretKey = crypto::Tools::generateRandomString(64);
     CommSecureStore::set(secureStoreAccountDataKey, storedSecretKey.value());
   }
   return storedSecretKey.value();
 }
 
 void CommCoreModule::persistCryptoModules(
     bool persistContentModule,
     bool persistNotifsModule) {
   std::string storedSecretKey = getAccountDataKey(secureStoreAccountDataKey);
 
   if (!persistContentModule && !persistNotifsModule) {
     return;
   }
 
   crypto::Persist newContentPersist;
   if (persistContentModule) {
     newContentPersist = this->contentCryptoModule->storeAsB64(storedSecretKey);
   }
 
   crypto::Persist newNotifsPersist;
   if (persistNotifsModule) {
     newNotifsPersist = this->notifsCryptoModule->storeAsB64(storedSecretKey);
   }
 
   std::promise<void> persistencePromise;
   std::future<void> persistenceFuture = persistencePromise.get_future();
   GlobalDBSingleton::instance.scheduleOrRunCancellable(
       [=, &persistencePromise]() {
         try {
           DatabaseManager::getQueryExecutor().beginTransaction();
           if (persistContentModule) {
             DatabaseManager::getQueryExecutor().storeOlmPersistData(
                 DatabaseManager::getQueryExecutor().getContentAccountID(),
                 newContentPersist);
           }
           if (persistNotifsModule) {
             DatabaseManager::getQueryExecutor().storeOlmPersistData(
                 DatabaseManager::getQueryExecutor().getNotifsAccountID(),
                 newNotifsPersist);
           }
           DatabaseManager::getQueryExecutor().commitTransaction();
           persistencePromise.set_value();
         } catch (std::system_error &e) {
           DatabaseManager::getQueryExecutor().rollbackTransaction();
           persistencePromise.set_exception(std::make_exception_ptr(e));
         }
       });
   persistenceFuture.get();
 }
 
 jsi::Value CommCoreModule::initializeCryptoAccount(jsi::Runtime &rt) {
   folly::Optional<std::string> storedSecretKey =
       CommSecureStore::get(this->secureStoreAccountDataKey);
   if (!storedSecretKey.hasValue()) {
     storedSecretKey = crypto::Tools::generateRandomString(64);
     CommSecureStore::set(
         this->secureStoreAccountDataKey, storedSecretKey.value());
   }
 
   return createPromiseAsJSIValue(
       rt, [=](jsi::Runtime &innerRt, std::shared_ptr<Promise> promise) {
         taskType job = [=]() {
           crypto::Persist contentPersist;
           crypto::Persist notifsPersist;
           std::string error;
           try {
             std::optional<std::string> contentAccountData =
                 DatabaseManager::getQueryExecutor().getOlmPersistAccountData(
                     DatabaseManager::getQueryExecutor().getContentAccountID());
             if (contentAccountData.has_value()) {
               contentPersist.account = crypto::OlmBuffer(
                   contentAccountData->begin(), contentAccountData->end());
               // handle sessions data
               std::vector<OlmPersistSession> sessionsData =
                   DatabaseManager::getQueryExecutor()
                       .getOlmPersistSessionsData();
               for (OlmPersistSession &sessionsDataItem : sessionsData) {
                 crypto::OlmBuffer sessionDataBuffer(
                     sessionsDataItem.session_data.begin(),
                     sessionsDataItem.session_data.end());
                 crypto::SessionPersist sessionPersist{
                     sessionDataBuffer, sessionsDataItem.version};
                 contentPersist.sessions.insert(std::make_pair(
                     sessionsDataItem.target_device_id, sessionPersist));
               }
             }
 
             std::optional<std::string> notifsAccountData =
                 DatabaseManager::getQueryExecutor().getOlmPersistAccountData(
                     DatabaseManager::getQueryExecutor().getNotifsAccountID());
             if (notifsAccountData.has_value()) {
               notifsPersist.account = crypto::OlmBuffer(
                   notifsAccountData->begin(), notifsAccountData->end());
             }
 
           } catch (std::system_error &e) {
             error = e.what();
           }
 
           this->cryptoThread->scheduleTask([=]() {
             std::string error;
             this->contentCryptoModule.reset(new crypto::CryptoModule(
                 this->publicCryptoAccountID,
                 storedSecretKey.value(),
                 contentPersist));
 
             this->notifsCryptoModule.reset(new crypto::CryptoModule(
                 this->notifsCryptoAccountID,
                 storedSecretKey.value(),
                 notifsPersist));
 
             try {
               this->persistCryptoModules(
                   contentPersist.isEmpty(), notifsPersist.isEmpty());
             } catch (const std::exception &e) {
               error = e.what();
             }
 
             this->jsInvoker_->invokeAsync([=]() {
               if (error.size()) {
                 promise->reject(error);
                 return;
               }
             });
 
             this->jsInvoker_->invokeAsync(
                 [=]() { promise->resolve(jsi::Value::undefined()); });
           });
         };
         GlobalDBSingleton::instance.scheduleOrRunCancellable(
             job, promise, this->jsInvoker_);
       });
 }
 
 jsi::Value CommCoreModule::getUserPublicKey(jsi::Runtime &rt) {
   return createPromiseAsJSIValue(
       rt, [=](jsi::Runtime &innerRt, std::shared_ptr<Promise> promise) {
         taskType job = [=, &innerRt]() {
           std::string error;
           std::string primaryKeysResult;
           std::string notificationsKeysResult;
           if (this->contentCryptoModule == nullptr ||
               this->notifsCryptoModule == nullptr) {
             error = "user has not been initialized";
           } else {
             primaryKeysResult = this->contentCryptoModule->getIdentityKeys();
             notificationsKeysResult =
                 this->notifsCryptoModule->getIdentityKeys();
           }
 
           std::string notificationsCurve25519Cpp, notificationsEd25519Cpp,
               blobPayloadCpp, signatureCpp, primaryCurve25519Cpp,
               primaryEd25519Cpp;
 
           if (!error.size()) {
             folly::dynamic parsedPrimary;
             try {
               parsedPrimary = folly::parseJson(primaryKeysResult);
             } catch (const folly::json::parse_error &e) {
               error =
                   "parsing identity keys failed with: " + std::string(e.what());
             }
             if (!error.size()) {
               primaryCurve25519Cpp = parsedPrimary["curve25519"].asString();
               primaryEd25519Cpp = parsedPrimary["ed25519"].asString();
 
               folly::dynamic parsedNotifications;
               try {
                 parsedNotifications = folly::parseJson(notificationsKeysResult);
               } catch (const folly::json::parse_error &e) {
                 error = "parsing notifications keys failed with: " +
                     std::string(e.what());
               }
               if (!error.size()) {
                 notificationsCurve25519Cpp =
                     parsedNotifications["curve25519"].asString();
                 notificationsEd25519Cpp =
                     parsedNotifications["ed25519"].asString();
 
                 folly::dynamic blobPayloadJSON = folly::dynamic::object(
                     "primaryIdentityPublicKeys",
                     folly::dynamic::object("ed25519", primaryEd25519Cpp)(
                         "curve25519", primaryCurve25519Cpp))(
                     "notificationIdentityPublicKeys",
                     folly::dynamic::object("ed25519", notificationsEd25519Cpp)(
                         "curve25519", notificationsCurve25519Cpp));
 
                 blobPayloadCpp = folly::toJson(blobPayloadJSON);
                 signatureCpp =
                     this->contentCryptoModule->signMessage(blobPayloadCpp);
               }
             }
           }
 
           this->jsInvoker_->invokeAsync([=, &innerRt]() {
             if (error.size()) {
               promise->reject(error);
               return;
             }
 
             auto primaryCurve25519{
                 jsi::String::createFromUtf8(innerRt, primaryCurve25519Cpp)};
             auto primaryEd25519{
                 jsi::String::createFromUtf8(innerRt, primaryEd25519Cpp)};
             auto jsiPrimaryIdentityPublicKeys = jsi::Object(innerRt);
             jsiPrimaryIdentityPublicKeys.setProperty(
                 innerRt, "ed25519", primaryEd25519);
             jsiPrimaryIdentityPublicKeys.setProperty(
                 innerRt, "curve25519", primaryCurve25519);
 
             auto notificationsCurve25519{jsi::String::createFromUtf8(
                 innerRt, notificationsCurve25519Cpp)};
             auto notificationsEd25519{
                 jsi::String::createFromUtf8(innerRt, notificationsEd25519Cpp)};
             auto jsiNotificationIdentityPublicKeys = jsi::Object(innerRt);
             jsiNotificationIdentityPublicKeys.setProperty(
                 innerRt, "ed25519", notificationsEd25519);
             jsiNotificationIdentityPublicKeys.setProperty(
                 innerRt, "curve25519", notificationsCurve25519);
 
             auto blobPayload{
                 jsi::String::createFromUtf8(innerRt, blobPayloadCpp)};
             auto signature{jsi::String::createFromUtf8(innerRt, signatureCpp)};
 
             auto jsiClientPublicKeys = jsi::Object(innerRt);
             jsiClientPublicKeys.setProperty(
                 innerRt,
                 "primaryIdentityPublicKeys",
                 jsiPrimaryIdentityPublicKeys);
             jsiClientPublicKeys.setProperty(
                 innerRt,
                 "notificationIdentityPublicKeys",
                 jsiNotificationIdentityPublicKeys);
             jsiClientPublicKeys.setProperty(
                 innerRt, "blobPayload", blobPayload);
             jsiClientPublicKeys.setProperty(innerRt, "signature", signature);
             promise->resolve(std::move(jsiClientPublicKeys));
           });
         };
         this->cryptoThread->scheduleTask(job);
       });
 }
 
 jsi::Object parseOLMOneTimeKeys(jsi::Runtime &rt, std::string oneTimeKeysBlob) {
   folly::dynamic parsedOneTimeKeys = folly::parseJson(oneTimeKeysBlob);
 
   auto jsiOneTimeKeysInner = jsi::Object(rt);
 
   for (auto &kvPair : parsedOneTimeKeys["curve25519"].items()) {
     jsiOneTimeKeysInner.setProperty(
         rt,
         kvPair.first.asString().c_str(),
         jsi::String::createFromUtf8(rt, kvPair.second.asString()));
   }
 
   auto jsiOneTimeKeys = jsi::Object(rt);
   jsiOneTimeKeys.setProperty(rt, "curve25519", jsiOneTimeKeysInner);
 
   return jsiOneTimeKeys;
 }
 
 std::string parseOLMPrekey(std::string prekeyBlob) {
   folly::dynamic parsedPrekey;
   try {
     parsedPrekey = folly::parseJson(prekeyBlob);
   } catch (const folly::json::parse_error &e) {
     throw std::runtime_error(
         "parsing prekey failed with: " + std::string(e.what()));
   }
 
   folly::dynamic innerObject = parsedPrekey["curve25519"];
   if (!innerObject.isObject()) {
     throw std::runtime_error("parsing prekey failed: inner object malformed");
   }
 
   if (innerObject.values().begin() == innerObject.values().end()) {
     throw std::runtime_error("parsing prekey failed: prekey missing");
   }
 
   return parsedPrekey["curve25519"].values().begin()->asString();
 }
 
 jsi::Object parseOneTimeKeysResult(
     jsi::Runtime &rt,
     std::string contentOneTimeKeysBlob,
     std::string notifOneTimeKeysBlob) {
   auto contentOneTimeKeys = parseOLMOneTimeKeys(rt, contentOneTimeKeysBlob);
   auto notifOneTimeKeys = parseOLMOneTimeKeys(rt, notifOneTimeKeysBlob);
   auto jsiOneTimeKeysResult = jsi::Object(rt);
   jsiOneTimeKeysResult.setProperty(
       rt, "contentOneTimeKeys", contentOneTimeKeys);
   jsiOneTimeKeysResult.setProperty(
       rt, "notificationsOneTimeKeys", notifOneTimeKeys);
 
   return jsiOneTimeKeysResult;
 }
 
 jsi::Value
 CommCoreModule::getOneTimeKeys(jsi::Runtime &rt, double oneTimeKeysAmount) {
   return createPromiseAsJSIValue(
       rt, [=](jsi::Runtime &innerRt, std::shared_ptr<Promise> promise) {
         taskType job = [=, &innerRt]() {
           std::string error;
           std::string contentResult;
           std::string notifResult;
           if (this->contentCryptoModule == nullptr ||
               this->notifsCryptoModule == nullptr) {
             this->jsInvoker_->invokeAsync([=, &innerRt]() {
               promise->reject("user has not been initialized");
             });
             return;
           }
           try {
             contentResult =
                 this->contentCryptoModule->getOneTimeKeysForPublishing(
                     oneTimeKeysAmount);
             notifResult = this->notifsCryptoModule->getOneTimeKeysForPublishing(
                 oneTimeKeysAmount);
             this->persistCryptoModules(true, true);
           } catch (const std::exception &e) {
             error = e.what();
           }
           this->jsInvoker_->invokeAsync([=, &innerRt]() {
             if (error.size()) {
               promise->reject(error);
               return;
             }
             promise->resolve(
                 parseOneTimeKeysResult(innerRt, contentResult, notifResult));
           });
         };
         this->cryptoThread->scheduleTask(job);
       });
 }
 
 jsi::Value CommCoreModule::validateAndUploadPrekeys(
     jsi::Runtime &rt,
     jsi::String authUserID,
     jsi::String authDeviceID,
     jsi::String authAccessToken) {
   auto authUserIDRust = jsiStringToRustString(authUserID, rt);
   auto authDeviceIDRust = jsiStringToRustString(authDeviceID, rt);
   auto authAccessTokenRust = jsiStringToRustString(authAccessToken, rt);
   return createPromiseAsJSIValue(
       rt, [=](jsi::Runtime &innerRt, std::shared_ptr<Promise> promise) {
         taskType job = [=, &innerRt]() {
           std::string error;
           std::optional<std::string> maybeContentPrekeyToUpload;
           std::optional<std::string> maybeNotifsPrekeyToUpload;
 
           if (this->contentCryptoModule == nullptr ||
               this->notifsCryptoModule == nullptr) {
             this->jsInvoker_->invokeAsync([=, &innerRt]() {
               promise->reject("user has not been initialized");
             });
             return;
           }
 
           try {
             maybeContentPrekeyToUpload =
                 this->contentCryptoModule->validatePrekey();
             maybeNotifsPrekeyToUpload =
                 this->notifsCryptoModule->validatePrekey();
             this->persistCryptoModules(true, true);
 
             if (!maybeContentPrekeyToUpload.has_value()) {
               maybeContentPrekeyToUpload =
                   this->contentCryptoModule->getUnpublishedPrekey();
             }
             if (!maybeNotifsPrekeyToUpload.has_value()) {
               maybeNotifsPrekeyToUpload =
                   this->notifsCryptoModule->getUnpublishedPrekey();
             }
           } catch (const std::exception &e) {
             error = e.what();
           }
 
           if (error.size()) {
             this->jsInvoker_->invokeAsync(
                 [=, &innerRt]() { promise->reject(error); });
             return;
           }
 
           if (!maybeContentPrekeyToUpload.has_value() &&
               !maybeNotifsPrekeyToUpload.has_value()) {
             this->jsInvoker_->invokeAsync(
                 [=]() { promise->resolve(jsi::Value::undefined()); });
             return;
           }
 
           std::string contentPrekeyToUpload;
           if (maybeContentPrekeyToUpload.has_value()) {
             contentPrekeyToUpload = maybeContentPrekeyToUpload.value();
           } else {
             contentPrekeyToUpload = this->contentCryptoModule->getPrekey();
           }
 
           std::string notifsPrekeyToUpload;
           if (maybeNotifsPrekeyToUpload.has_value()) {
             notifsPrekeyToUpload = maybeNotifsPrekeyToUpload.value();
           } else {
             notifsPrekeyToUpload = this->notifsCryptoModule->getPrekey();
           }
 
           std::string prekeyUploadError;
 
           try {
             std::string contentPrekeySignature =
                 this->contentCryptoModule->getPrekeySignature();
             std::string notifsPrekeySignature =
                 this->notifsCryptoModule->getPrekeySignature();
 
             try {
               std::promise<folly::dynamic> prekeyPromise;
               std::future<folly::dynamic> prekeyFuture =
                   prekeyPromise.get_future();
               RustPromiseManager::CPPPromiseInfo promiseInfo = {
                   std::move(prekeyPromise)};
               auto currentID = RustPromiseManager::instance.addPromise(
                   std::move(promiseInfo));
               auto contentPrekeyToUploadRust =
                   rust::String(parseOLMPrekey(contentPrekeyToUpload));
               auto prekeySignatureRust = rust::string(contentPrekeySignature);
               auto notifsPrekeyToUploadRust =
                   rust::String(parseOLMPrekey(notifsPrekeyToUpload));
               auto notificationsPrekeySignatureRust =
                   rust::string(notifsPrekeySignature);
               ::identityRefreshUserPrekeys(
                   authUserIDRust,
                   authDeviceIDRust,
                   authAccessTokenRust,
                   contentPrekeyToUploadRust,
                   prekeySignatureRust,
                   notifsPrekeyToUploadRust,
                   notificationsPrekeySignatureRust,
                   currentID);
               prekeyFuture.get();
             } catch (const std::exception &e) {
               prekeyUploadError = e.what();
             }
 
             if (!prekeyUploadError.size()) {
               this->contentCryptoModule->markPrekeyAsPublished();
               this->notifsCryptoModule->markPrekeyAsPublished();
               this->persistCryptoModules(true, true);
             }
           } catch (std::exception &e) {
             error = e.what();
           }
 
           this->jsInvoker_->invokeAsync([=]() {
             if (error.size()) {
               promise->reject(error);
               return;
             }
             if (prekeyUploadError.size()) {
               promise->reject(prekeyUploadError);
               return;
             }
             promise->resolve(jsi::Value::undefined());
           });
         };
         this->cryptoThread->scheduleTask(job);
       });
 }
 
 jsi::Value CommCoreModule::validateAndGetPrekeys(jsi::Runtime &rt) {
   return createPromiseAsJSIValue(
       rt, [=](jsi::Runtime &innerRt, std::shared_ptr<Promise> promise) {
         taskType job = [=, &innerRt]() {
           std::string error;
           std::string contentPrekey, notifPrekey, contentPrekeySignature,
               notifPrekeySignature;
           std::optional<std::string> contentPrekeyBlob;
           std::optional<std::string> notifPrekeyBlob;
 
           if (this->contentCryptoModule == nullptr ||
               this->notifsCryptoModule == nullptr) {
             this->jsInvoker_->invokeAsync([=, &innerRt]() {
               promise->reject("user has not been initialized");
             });
             return;
           }
           try {
             contentPrekeyBlob = this->contentCryptoModule->validatePrekey();
             if (!contentPrekeyBlob) {
               contentPrekeyBlob =
                   this->contentCryptoModule->getUnpublishedPrekey();
             }
             if (!contentPrekeyBlob) {
               contentPrekeyBlob = this->contentCryptoModule->getPrekey();
             }
 
             notifPrekeyBlob = this->notifsCryptoModule->validatePrekey();
             if (!notifPrekeyBlob) {
               notifPrekeyBlob =
                   this->notifsCryptoModule->getUnpublishedPrekey();
             }
             if (!notifPrekeyBlob) {
               notifPrekeyBlob = this->notifsCryptoModule->getPrekey();
             }
             this->persistCryptoModules(true, true);
 
             contentPrekeySignature =
                 this->contentCryptoModule->getPrekeySignature();
             notifPrekeySignature =
                 this->notifsCryptoModule->getPrekeySignature();
 
             contentPrekey = parseOLMPrekey(contentPrekeyBlob.value());
             notifPrekey = parseOLMPrekey(notifPrekeyBlob.value());
           } catch (const std::exception &e) {
             error = e.what();
           }
 
           this->jsInvoker_->invokeAsync([=, &innerRt]() {
             if (error.size()) {
               promise->reject(error);
               return;
             }
             auto contentPrekeyJSI =
                 jsi::String::createFromUtf8(innerRt, contentPrekey);
             auto contentPrekeySignatureJSI =
                 jsi::String::createFromUtf8(innerRt, contentPrekeySignature);
             auto notifPrekeyJSI =
                 jsi::String::createFromUtf8(innerRt, notifPrekey);
             auto notifPrekeySignatureJSI =
                 jsi::String::createFromUtf8(innerRt, notifPrekeySignature);
 
             auto signedPrekeysJSI = jsi::Object(innerRt);
             signedPrekeysJSI.setProperty(
                 innerRt, "contentPrekey", contentPrekeyJSI);
             signedPrekeysJSI.setProperty(
                 innerRt, "contentPrekeySignature", contentPrekeySignatureJSI);
             signedPrekeysJSI.setProperty(
                 innerRt, "notifPrekey", notifPrekeyJSI);
             signedPrekeysJSI.setProperty(
                 innerRt, "notifPrekeySignature", notifPrekeySignatureJSI);
 
             promise->resolve(std::move(signedPrekeysJSI));
           });
         };
 
         this->cryptoThread->scheduleTask(job);
       });
 }
 
 jsi::Value CommCoreModule::initializeNotificationsSession(
     jsi::Runtime &rt,
     jsi::String identityKeys,
     jsi::String prekey,
     jsi::String prekeySignature,
     jsi::String oneTimeKey,
     jsi::String keyserverID) {
   auto identityKeysCpp{identityKeys.utf8(rt)};
   auto prekeyCpp{prekey.utf8(rt)};
   auto prekeySignatureCpp{prekeySignature.utf8(rt)};
   auto oneTimeKeyCpp{oneTimeKey.utf8(rt)};
   auto keyserverIDCpp{keyserverID.utf8(rt)};
   return createPromiseAsJSIValue(
       rt, [=](jsi::Runtime &innerRt, std::shared_ptr<Promise> promise) {
         taskType job = [=, &innerRt]() {
           std::string error;
           crypto::EncryptedData result;
           try {
             this->notifsCryptoModule->initializeOutboundForSendingSession(
                 keyserverIDCpp,
                 std::vector<uint8_t>(
                     identityKeysCpp.begin(), identityKeysCpp.end()),
                 std::vector<uint8_t>(prekeyCpp.begin(), prekeyCpp.end()),
                 std::vector<uint8_t>(
                     prekeySignatureCpp.begin(), prekeySignatureCpp.end()),
                 std::vector<uint8_t>(
                     oneTimeKeyCpp.begin(), oneTimeKeyCpp.end()));
 
             result = this->notifsCryptoModule->encrypt(
                 keyserverIDCpp,
                 NotificationsCryptoModule::initialEncryptedMessageContent);
 
             std::shared_ptr<crypto::Session> keyserverNotificationsSession =
                 this->notifsCryptoModule->getSessionByDeviceId(keyserverIDCpp);
 
             NotificationsCryptoModule::persistNotificationsSession(
                 keyserverIDCpp, keyserverNotificationsSession);
 
             this->notifsCryptoModule->removeSessionByDeviceId(keyserverIDCpp);
             this->persistCryptoModules(false, true);
           } catch (const std::exception &e) {
             error = e.what();
           }
           this->jsInvoker_->invokeAsync([=, &innerRt]() {
             if (error.size()) {
               promise->reject(error);
               return;
             }
             promise->resolve(jsi::String::createFromUtf8(
                 innerRt,
                 std::string{result.message.begin(), result.message.end()}));
           });
         };
         this->cryptoThread->scheduleTask(job);
       });
 }
 
 jsi::Value CommCoreModule::isNotificationsSessionInitialized(jsi::Runtime &rt) {
   return createPromiseAsJSIValue(
       rt, [=](jsi::Runtime &innerRt, std::shared_ptr<Promise> promise) {
         taskType job = [=, &innerRt]() {
           std::string error;
           bool result;
           try {
             result =
                 NotificationsCryptoModule::isNotificationsSessionInitialized(
                     "Comm");
           } catch (const std::exception &e) {
             error = e.what();
           }
           this->jsInvoker_->invokeAsync([=, &innerRt]() {
             if (error.size()) {
               promise->reject(error);
               return;
             }
             promise->resolve(result);
           });
         };
         this->cryptoThread->scheduleTask(job);
       });
 }
 
 jsi::Value CommCoreModule::updateKeyserverDataInNotifStorage(
     jsi::Runtime &rt,
     jsi::Array keyserversData) {
 
   std::vector<std::pair<std::string, int>> keyserversDataCpp;
   for (auto idx = 0; idx < keyserversData.size(rt); idx++) {
     auto data = keyserversData.getValueAtIndex(rt, idx).asObject(rt);
     std::string keyserverID = data.getProperty(rt, "id").asString(rt).utf8(rt);
     std::string keyserverUnreadCountKey =
         "KEYSERVER." + keyserverID + ".UNREAD_COUNT";
     int unreadCount = data.getProperty(rt, "unreadCount").asNumber();
     keyserversDataCpp.push_back({keyserverUnreadCountKey, unreadCount});
   }
 
   return createPromiseAsJSIValue(
       rt, [=](jsi::Runtime &innerRt, std::shared_ptr<Promise> promise) {
         std::string error;
         try {
           for (const auto &keyserverData : keyserversDataCpp) {
             CommMMKV::setInt(keyserverData.first, keyserverData.second);
           }
         } catch (const std::exception &e) {
           error = e.what();
         }
 
         this->jsInvoker_->invokeAsync([=, &innerRt]() {
           if (error.size()) {
             promise->reject(error);
             return;
           }
           promise->resolve(jsi::Value::undefined());
         });
       });
 }
 
 jsi::Value CommCoreModule::removeKeyserverDataFromNotifStorage(
     jsi::Runtime &rt,
     jsi::Array keyserverIDsToDelete) {
   std::vector<std::string> keyserverIDsToDeleteCpp{};
   for (auto idx = 0; idx < keyserverIDsToDelete.size(rt); idx++) {
     std::string keyserverID =
         keyserverIDsToDelete.getValueAtIndex(rt, idx).asString(rt).utf8(rt);
     std::string keyserverUnreadCountKey =
         "KEYSERVER." + keyserverID + ".UNREAD_COUNT";
     keyserverIDsToDeleteCpp.push_back(keyserverUnreadCountKey);
   }
 
   return createPromiseAsJSIValue(
       rt, [=](jsi::Runtime &innerRt, std::shared_ptr<Promise> promise) {
         std::string error;
         try {
           CommMMKV::removeKeys(keyserverIDsToDeleteCpp);
         } catch (const std::exception &e) {
           error = e.what();
         }
 
         this->jsInvoker_->invokeAsync([=, &innerRt]() {
           if (error.size()) {
             promise->reject(error);
             return;
           }
           promise->resolve(jsi::Value::undefined());
         });
       });
 }
 
 jsi::Value CommCoreModule::getKeyserverDataFromNotifStorage(
     jsi::Runtime &rt,
     jsi::Array keyserverIDs) {
   std::vector<std::string> keyserverIDsCpp{};
   for (auto idx = 0; idx < keyserverIDs.size(rt); idx++) {
     std::string keyserverID =
         keyserverIDs.getValueAtIndex(rt, idx).asString(rt).utf8(rt);
     keyserverIDsCpp.push_back(keyserverID);
   }
 
   return createPromiseAsJSIValue(
       rt, [=](jsi::Runtime &innerRt, std::shared_ptr<Promise> promise) {
         std::string error;
         std::vector<std::pair<std::string, int>> keyserversDataVector{};
 
         try {
           for (const auto &keyserverID : keyserverIDsCpp) {
             std::string keyserverUnreadCountKey =
                 "KEYSERVER." + keyserverID + ".UNREAD_COUNT";
             std::optional<int> unreadCount =
                 CommMMKV::getInt(keyserverUnreadCountKey, -1);
 
             if (!unreadCount.has_value()) {
               continue;
             }
 
             keyserversDataVector.push_back({keyserverID, unreadCount.value()});
           }
         } catch (const std::exception &e) {
           error = e.what();
         }
 
         auto keyserversDataVectorPtr =
             std::make_shared<std::vector<std::pair<std::string, int>>>(
                 std::move(keyserversDataVector));
 
         this->jsInvoker_->invokeAsync(
             [&innerRt, keyserversDataVectorPtr, error, promise]() {
               if (error.size()) {
                 promise->reject(error);
                 return;
               }
 
               size_t numKeyserversData = keyserversDataVectorPtr->size();
               jsi::Array jsiKeyserversData =
                   jsi::Array(innerRt, numKeyserversData);
               size_t writeIdx = 0;
 
               for (const auto &keyserverData : *keyserversDataVectorPtr) {
                 jsi::Object jsiKeyserverData = jsi::Object(innerRt);
                 jsiKeyserverData.setProperty(
                     innerRt, "id", keyserverData.first);
                 jsiKeyserverData.setProperty(
                     innerRt, "unreadCount", keyserverData.second);
                 jsiKeyserversData.setValueAtIndex(
                     innerRt, writeIdx++, jsiKeyserverData);
               }
 
               promise->resolve(std::move(jsiKeyserversData));
             });
       });
 }
 
 jsi::Value CommCoreModule::initializeContentOutboundSession(
     jsi::Runtime &rt,
     jsi::String identityKeys,
     jsi::String prekey,
     jsi::String prekeySignature,
     jsi::String oneTimeKey,
     jsi::String deviceID) {
   auto identityKeysCpp{identityKeys.utf8(rt)};
   auto prekeyCpp{prekey.utf8(rt)};
   auto prekeySignatureCpp{prekeySignature.utf8(rt)};
   auto oneTimeKeyCpp{oneTimeKey.utf8(rt)};
   auto deviceIDCpp{deviceID.utf8(rt)};
   return createPromiseAsJSIValue(
       rt, [=](jsi::Runtime &innerRt, std::shared_ptr<Promise> promise) {
         taskType job = [=, &innerRt]() {
           std::string error;
           crypto::EncryptedData initialEncryptedData;
           int sessionVersion;
           try {
             sessionVersion =
                 this->contentCryptoModule->initializeOutboundForSendingSession(
                     deviceIDCpp,
                     std::vector<uint8_t>(
                         identityKeysCpp.begin(), identityKeysCpp.end()),
                     std::vector<uint8_t>(prekeyCpp.begin(), prekeyCpp.end()),
                     std::vector<uint8_t>(
                         prekeySignatureCpp.begin(), prekeySignatureCpp.end()),
                     std::vector<uint8_t>(
                         oneTimeKeyCpp.begin(), oneTimeKeyCpp.end()));
 
             const std::string initMessage = "{\"type\": \"init\"}";
             initialEncryptedData =
                 contentCryptoModule->encrypt(deviceIDCpp, initMessage);
 
             this->persistCryptoModules(true, false);
           } catch (const std::exception &e) {
             error = e.what();
           }
           this->jsInvoker_->invokeAsync([=, &innerRt]() {
             if (error.size()) {
               promise->reject(error);
               return;
             }
             auto initialEncryptedDataJSI = jsi::Object(innerRt);
             auto message = std::string{
                 initialEncryptedData.message.begin(),
                 initialEncryptedData.message.end()};
             auto messageJSI = jsi::String::createFromUtf8(innerRt, message);
             initialEncryptedDataJSI.setProperty(innerRt, "message", messageJSI);
             initialEncryptedDataJSI.setProperty(
                 innerRt,
                 "messageType",
                 static_cast<int>(initialEncryptedData.messageType));
 
             auto outboundSessionCreationResultJSI = jsi::Object(innerRt);
             outboundSessionCreationResultJSI.setProperty(
                 innerRt, "encryptedData", initialEncryptedDataJSI);
             outboundSessionCreationResultJSI.setProperty(
                 innerRt, "sessionVersion", sessionVersion);
 
             promise->resolve(std::move(outboundSessionCreationResultJSI));
           });
         };
         this->cryptoThread->scheduleTask(job);
       });
 }
 
 jsi::Value CommCoreModule::initializeContentInboundSession(
     jsi::Runtime &rt,
     jsi::String identityKeys,
     jsi::Object encryptedDataJSI,
     jsi::String deviceID,
     double sessionVersion,
     bool overwrite) {
   auto identityKeysCpp{identityKeys.utf8(rt)};
   size_t messageType =
       std::lround(encryptedDataJSI.getProperty(rt, "messageType").asNumber());
   std::string encryptedMessageCpp =
       encryptedDataJSI.getProperty(rt, "message").asString(rt).utf8(rt);
   auto deviceIDCpp{deviceID.utf8(rt)};
   return createPromiseAsJSIValue(
       rt, [=](jsi::Runtime &innerRt, std::shared_ptr<Promise> promise) {
         taskType job = [=, &innerRt]() {
           std::string error;
           std::string decryptedMessage;
           try {
             this->contentCryptoModule->initializeInboundForReceivingSession(
                 deviceIDCpp,
                 std::vector<uint8_t>(
                     encryptedMessageCpp.begin(), encryptedMessageCpp.end()),
                 std::vector<uint8_t>(
                     identityKeysCpp.begin(), identityKeysCpp.end()),
                 static_cast<int>(sessionVersion),
                 overwrite);
             crypto::EncryptedData encryptedData{
                 std::vector<uint8_t>(
                     encryptedMessageCpp.begin(), encryptedMessageCpp.end()),
                 messageType};
             decryptedMessage =
                 this->contentCryptoModule->decrypt(deviceIDCpp, encryptedData);
             this->persistCryptoModules(true, false);
           } catch (const std::exception &e) {
             error = e.what();
           }
           this->jsInvoker_->invokeAsync([=, &innerRt]() {
             if (error.size()) {
               promise->reject(error);
               return;
             }
             promise->resolve(
                 jsi::String::createFromUtf8(innerRt, decryptedMessage));
           });
         };
         this->cryptoThread->scheduleTask(job);
       });
 }
 
 jsi::Value CommCoreModule::encrypt(
     jsi::Runtime &rt,
     jsi::String message,
     jsi::String deviceID) {
   auto messageCpp{message.utf8(rt)};
   auto deviceIDCpp{deviceID.utf8(rt)};
   return createPromiseAsJSIValue(
       rt, [=](jsi::Runtime &innerRt, std::shared_ptr<Promise> promise) {
         taskType job = [=, &innerRt]() {
           std::string error;
           crypto::EncryptedData encryptedMessage;
           try {
             encryptedMessage =
                 contentCryptoModule->encrypt(deviceIDCpp, messageCpp);
             this->persistCryptoModules(true, false);
           } catch (const std::exception &e) {
             error = e.what();
           }
           this->jsInvoker_->invokeAsync([=, &innerRt]() {
             if (error.size()) {
               promise->reject(error);
               return;
             }
             auto encryptedDataJSI = jsi::Object(innerRt);
             auto message = std::string{
                 encryptedMessage.message.begin(),
                 encryptedMessage.message.end()};
             auto messageJSI = jsi::String::createFromUtf8(innerRt, message);
             encryptedDataJSI.setProperty(innerRt, "message", messageJSI);
             encryptedDataJSI.setProperty(
                 innerRt,
                 "messageType",
                 static_cast<int>(encryptedMessage.messageType));
             promise->resolve(std::move(encryptedDataJSI));
           });
         };
         this->cryptoThread->scheduleTask(job);
       });
 }
 
 jsi::Value CommCoreModule::decrypt(
     jsi::Runtime &rt,
     jsi::Object encryptedDataJSI,
     jsi::String deviceID) {
   size_t messageType =
       std::lround(encryptedDataJSI.getProperty(rt, "messageType").asNumber());
   std::string message =
       encryptedDataJSI.getProperty(rt, "message").asString(rt).utf8(rt);
   auto deviceIDCpp{deviceID.utf8(rt)};
   return createPromiseAsJSIValue(
       rt, [=](jsi::Runtime &innerRt, std::shared_ptr<Promise> promise) {
         taskType job = [=, &innerRt]() {
           std::string error;
           std::string decryptedMessage;
           try {
             crypto::EncryptedData encryptedData{
                 std::vector<uint8_t>(message.begin(), message.end()),
                 messageType};
             decryptedMessage =
                 this->contentCryptoModule->decrypt(deviceIDCpp, encryptedData);
             this->persistCryptoModules(true, false);
           } catch (const std::exception &e) {
             error = e.what();
           }
           this->jsInvoker_->invokeAsync([=, &innerRt]() {
             if (error.size()) {
               promise->reject(error);
               return;
             }
             promise->resolve(
                 jsi::String::createFromUtf8(innerRt, decryptedMessage));
           });
         };
         this->cryptoThread->scheduleTask(job);
       });
 }
 
 jsi::Value CommCoreModule::decryptSequentialAndPersist(
     jsi::Runtime &rt,
     jsi::Object encryptedDataJSI,
     jsi::String deviceID,
     jsi::String messageID) {
   size_t messageType =
       std::lround(encryptedDataJSI.getProperty(rt, "messageType").asNumber());
   std::string message =
       encryptedDataJSI.getProperty(rt, "message").asString(rt).utf8(rt);
   auto deviceIDCpp{deviceID.utf8(rt)};
   auto messageIDCpp{messageID.utf8(rt)};
   return createPromiseAsJSIValue(
       rt, [=](jsi::Runtime &innerRt, std::shared_ptr<Promise> promise) {
         taskType job = [=, &innerRt]() {
           std::string error;
           std::string decryptedMessage;
           try {
             crypto::EncryptedData encryptedData{
                 std::vector<uint8_t>(message.begin(), message.end()),
                 messageType};
             decryptedMessage = this->contentCryptoModule->decryptSequential(
                 deviceIDCpp, encryptedData);
 
             std::string storedSecretKey =
                 getAccountDataKey(secureStoreAccountDataKey);
             crypto::Persist newContentPersist =
                 this->contentCryptoModule->storeAsB64(storedSecretKey);
 
             std::promise<void> persistencePromise;
             std::future<void> persistenceFuture =
                 persistencePromise.get_future();
             GlobalDBSingleton::instance.scheduleOrRunCancellable(
                 [=, &persistencePromise]() {
                   try {
                     ReceivedMessageToDevice message{
                         messageIDCpp,
                         deviceIDCpp,
                         decryptedMessage,
                         "decrypted"};
 
                     DatabaseManager::getQueryExecutor().beginTransaction();
                     DatabaseManager::getQueryExecutor()
                         .addReceivedMessageToDevice(message);
                     DatabaseManager::getQueryExecutor().storeOlmPersistData(
                         DatabaseManager::getQueryExecutor()
                             .getContentAccountID(),
                         newContentPersist);
                     DatabaseManager::getQueryExecutor().commitTransaction();
                     persistencePromise.set_value();
                   } catch (std::system_error &e) {
                     DatabaseManager::getQueryExecutor().rollbackTransaction();
                     persistencePromise.set_exception(
                         std::make_exception_ptr(e));
                   }
                 });
             persistenceFuture.get();
           } catch (const std::exception &e) {
             error = e.what();
           }
           this->jsInvoker_->invokeAsync([=, &innerRt]() {
             if (error.size()) {
               promise->reject(error);
               return;
             }
             promise->resolve(
                 jsi::String::createFromUtf8(innerRt, decryptedMessage));
           });
         };
         this->cryptoThread->scheduleTask(job);
       });
 }
 
 jsi::Value CommCoreModule::signMessage(jsi::Runtime &rt, jsi::String message) {
   std::string messageStr = message.utf8(rt);
   return createPromiseAsJSIValue(
       rt, [=](jsi::Runtime &innerRt, std::shared_ptr<Promise> promise) {
         taskType job = [=, &innerRt]() {
           std::string error;
           std::string signature;
           try {
             signature = this->contentCryptoModule->signMessage(messageStr);
           } catch (const std::exception &e) {
             error = "signing message failed with: " + std::string(e.what());
           }
 
           this->jsInvoker_->invokeAsync([=, &innerRt]() {
             if (error.size()) {
               promise->reject(error);
               return;
             }
 
             auto jsiSignature{jsi::String::createFromUtf8(innerRt, signature)};
             promise->resolve(std::move(jsiSignature));
           });
         };
         this->cryptoThread->scheduleTask(job);
       });
 }
 
 CommCoreModule::CommCoreModule(
     std::shared_ptr<facebook::react::CallInvoker> jsInvoker)
     : facebook::react::CommCoreModuleSchemaCxxSpecJSI(jsInvoker),
       cryptoThread(std::make_unique<WorkerThread>("crypto")),
       draftStore(jsInvoker),
       threadStore(jsInvoker),
       messageStore(jsInvoker),
       reportStore(jsInvoker),
       userStore(jsInvoker),
       keyserverStore(jsInvoker),
       communityStore(jsInvoker),
       integrityStore(jsInvoker),
       syncedMetadataStore(jsInvoker),
       auxUserStore(jsInvoker),
       threadActivityStore(jsInvoker) {
   GlobalDBSingleton::instance.enableMultithreading();
 }
 
 double CommCoreModule::getCodeVersion(jsi::Runtime &rt) {
   return this->codeVersion;
 }
 
 jsi::Value CommCoreModule::setNotifyToken(jsi::Runtime &rt, jsi::String token) {
   auto notifyToken{token.utf8(rt)};
   return createPromiseAsJSIValue(
       rt,
       [this,
        notifyToken](jsi::Runtime &innerRt, std::shared_ptr<Promise> promise) {
         taskType job = [this, notifyToken, promise]() {
           std::string error;
           try {
             DatabaseManager::getQueryExecutor().setNotifyToken(notifyToken);
           } catch (std::system_error &e) {
             error = e.what();
           }
 
           this->jsInvoker_->invokeAsync([error, promise]() {
             if (error.size()) {
               promise->reject(error);
             } else {
               promise->resolve(jsi::Value::undefined());
             }
           });
         };
         GlobalDBSingleton::instance.scheduleOrRunCancellable(
             job, promise, this->jsInvoker_);
       });
 }
 
 jsi::Value CommCoreModule::clearNotifyToken(jsi::Runtime &rt) {
   return createPromiseAsJSIValue(
       rt, [this](jsi::Runtime &innerRt, std::shared_ptr<Promise> promise) {
         taskType job = [this, promise]() {
           std::string error;
           try {
             DatabaseManager::getQueryExecutor().clearNotifyToken();
           } catch (std::system_error &e) {
             error = e.what();
           }
           this->jsInvoker_->invokeAsync([error, promise]() {
             if (error.size()) {
               promise->reject(error);
             } else {
               promise->resolve(jsi::Value::undefined());
             }
           });
         };
         GlobalDBSingleton::instance.scheduleOrRunCancellable(
             job, promise, this->jsInvoker_);
       });
 };
 
 jsi::Value
 CommCoreModule::stampSQLiteDBUserID(jsi::Runtime &rt, jsi::String userID) {
   auto currentUserID{userID.utf8(rt)};
   return createPromiseAsJSIValue(
       rt,
       [this,
        currentUserID](jsi::Runtime &innerRt, std::shared_ptr<Promise> promise) {
         taskType job = [this, promise, currentUserID]() {
           std::string error;
           try {
             DatabaseManager::getQueryExecutor().stampSQLiteDBUserID(
                 currentUserID);
           } catch (const std::exception &e) {
             error = e.what();
           }
           this->jsInvoker_->invokeAsync([error, promise]() {
             if (error.size()) {
               promise->reject(error);
             } else {
               promise->resolve(jsi::Value::undefined());
             }
           });
         };
         GlobalDBSingleton::instance.scheduleOrRunCancellable(
             job, promise, this->jsInvoker_);
       });
 }
 
 jsi::Value CommCoreModule::getSQLiteStampedUserID(jsi::Runtime &rt) {
   return createPromiseAsJSIValue(
       rt, [this](jsi::Runtime &innerRt, std::shared_ptr<Promise> promise) {
         taskType job = [this, &innerRt, promise]() {
           std::string error;
           std::string result;
           try {
             result =
                 DatabaseManager::getQueryExecutor().getSQLiteStampedUserID();
           } catch (const std::exception &e) {
             error = e.what();
           }
           this->jsInvoker_->invokeAsync([&innerRt, error, result, promise]() {
             if (error.size()) {
               promise->reject(error);
             } else {
               promise->resolve(jsi::String::createFromUtf8(innerRt, result));
             }
           });
         };
         GlobalDBSingleton::instance.scheduleOrRunCancellable(
             job, promise, this->jsInvoker_);
       });
 }
 
 jsi::Value CommCoreModule::clearSensitiveData(jsi::Runtime &rt) {
   return createPromiseAsJSIValue(
       rt, [this](jsi::Runtime &innerRt, std::shared_ptr<Promise> promise) {
         GlobalDBSingleton::instance.setTasksCancelled(true);
         taskType job = [this, promise]() {
           std::string error;
           try {
             this->innerClearCommServicesAuthMetadata();
             DatabaseManager::clearSensitiveData();
           } catch (const std::exception &e) {
             error = e.what();
           }
           this->jsInvoker_->invokeAsync([error, promise]() {
             if (error.size()) {
               promise->reject(error);
             } else {
               promise->resolve(jsi::Value::undefined());
             }
           });
           GlobalDBSingleton::instance.scheduleOrRun(
               []() { GlobalDBSingleton::instance.setTasksCancelled(false); });
         };
         GlobalDBSingleton::instance.scheduleOrRun(job);
       });
 }
 
 bool CommCoreModule::checkIfDatabaseNeedsDeletion(jsi::Runtime &rt) {
   return DatabaseManager::checkIfDatabaseNeedsDeletion();
 }
 
 void CommCoreModule::reportDBOperationsFailure(jsi::Runtime &rt) {
   DatabaseManager::reportDBOperationsFailure();
 }
 
 jsi::Value CommCoreModule::computeBackupKey(
     jsi::Runtime &rt,
     jsi::String password,
     jsi::String backupID) {
   std::string passwordStr = password.utf8(rt);
   std::string backupIDStr = backupID.utf8(rt);
   return createPromiseAsJSIValue(
       rt, [=](jsi::Runtime &innerRt, std::shared_ptr<Promise> promise) {
         taskType job = [=, &innerRt]() {
           std::string error;
           std::array<::std::uint8_t, 32> backupKey;
           try {
             backupKey = compute_backup_key(passwordStr, backupIDStr);
           } catch (const std::exception &e) {
             error = std::string{"Failed to compute backup key: "} + e.what();
           }
 
           this->jsInvoker_->invokeAsync([=, &innerRt]() {
             if (error.size()) {
               promise->reject(error);
               return;
             }
             auto size = backupKey.size();
             auto arrayBuffer =
                 innerRt.global()
                     .getPropertyAsFunction(innerRt, "ArrayBuffer")
                     .callAsConstructor(innerRt, {static_cast<double>(size)})
                     .asObject(innerRt)
                     .getArrayBuffer(innerRt);
             auto bufferPtr = arrayBuffer.data(innerRt);
             memcpy(bufferPtr, backupKey.data(), size);
             promise->resolve(std::move(arrayBuffer));
           });
         };
         this->cryptoThread->scheduleTask(job);
       });
 }
 
 jsi::Value CommCoreModule::generateRandomString(jsi::Runtime &rt, double size) {
   return createPromiseAsJSIValue(
       rt, [=](jsi::Runtime &innerRt, std::shared_ptr<Promise> promise) {
         taskType job = [=, &innerRt]() {
           std::string error;
           std::string randomString;
           try {
             randomString =
                 crypto::Tools::generateRandomString(static_cast<size_t>(size));
           } catch (const std::exception &e) {
             error = "Failed to generate random string for size " +
                 std::to_string(size) + ": " + e.what();
           }
 
           this->jsInvoker_->invokeAsync(
               [&innerRt, error, randomString, promise]() {
                 if (error.size()) {
                   promise->reject(error);
                 } else {
                   jsi::String jsiRandomString =
                       jsi::String::createFromUtf8(innerRt, randomString);
                   promise->resolve(std::move(jsiRandomString));
                 }
               });
         };
         this->cryptoThread->scheduleTask(job);
       });
 }
 
 jsi::Value CommCoreModule::setCommServicesAuthMetadata(
     jsi::Runtime &rt,
     jsi::String userID,
     jsi::String deviceID,
     jsi::String accessToken) {
   auto userIDStr{userID.utf8(rt)};
   auto deviceIDStr{deviceID.utf8(rt)};
   auto accessTokenStr{accessToken.utf8(rt)};
   return createPromiseAsJSIValue(
       rt,
       [this, userIDStr, deviceIDStr, accessTokenStr](
           jsi::Runtime &innerRt, std::shared_ptr<Promise> promise) {
         std::string error;
         try {
           this->innerSetCommServicesAuthMetadata(
               userIDStr, deviceIDStr, accessTokenStr);
         } catch (const std::exception &e) {
           error = e.what();
         }
         this->jsInvoker_->invokeAsync([error, promise]() {
           if (error.size()) {
             promise->reject(error);
           } else {
             promise->resolve(jsi::Value::undefined());
           }
         });
       });
 }
 
 void CommCoreModule::innerSetCommServicesAuthMetadata(
     std::string userID,
     std::string deviceID,
     std::string accessToken) {
   CommSecureStore::set(CommSecureStore::userID, userID);
   CommSecureStore::set(CommSecureStore::deviceID, deviceID);
   CommSecureStore::set(CommSecureStore::commServicesAccessToken, accessToken);
   CommServicesAuthMetadataEmitter::sendAuthMetadataToJS(accessToken, userID);
 }
 
 jsi::Value CommCoreModule::getCommServicesAuthMetadata(jsi::Runtime &rt) {
   return createPromiseAsJSIValue(
       rt, [this](jsi::Runtime &innerRt, std::shared_ptr<Promise> promise) {
         std::string error;
         std::string userID;
         std::string deviceID;
         std::string accessToken;
         try {
           folly::Optional<std::string> userIDOpt =
               CommSecureStore::get(CommSecureStore::userID);
           if (userIDOpt.hasValue()) {
             userID = userIDOpt.value();
           }
           folly::Optional<std::string> deviceIDOpt =
               CommSecureStore::get(CommSecureStore::deviceID);
           if (deviceIDOpt.hasValue()) {
             deviceID = deviceIDOpt.value();
           }
           folly::Optional<std::string> accessTokenOpt =
               CommSecureStore::get(CommSecureStore::commServicesAccessToken);
           if (accessTokenOpt.hasValue()) {
             accessToken = accessTokenOpt.value();
           }
         } catch (const std::exception &e) {
           error = e.what();
         }
         this->jsInvoker_->invokeAsync(
             [&innerRt, error, userID, deviceID, accessToken, promise]() {
               if (error.size()) {
                 promise->reject(error);
               } else {
                 auto authMetadata = jsi::Object(innerRt);
                 if (!userID.empty()) {
                   authMetadata.setProperty(
                       innerRt,
                       "userID",
                       jsi::String::createFromUtf8(innerRt, userID));
                 }
                 if (!deviceID.empty()) {
                   authMetadata.setProperty(
                       innerRt,
                       "deviceID",
                       jsi::String::createFromUtf8(innerRt, deviceID));
                 }
                 if (!accessToken.empty()) {
                   authMetadata.setProperty(
                       innerRt,
                       "accessToken",
                       jsi::String::createFromUtf8(innerRt, accessToken));
                 }
                 promise->resolve(std::move(authMetadata));
               }
             });
       });
 }
 
 jsi::Value CommCoreModule::clearCommServicesAuthMetadata(jsi::Runtime &rt) {
   return this->setCommServicesAuthMetadata(
       rt,
       jsi::String::createFromUtf8(rt, ""),
       jsi::String::createFromUtf8(rt, ""),
       jsi::String::createFromUtf8(rt, ""));
 }
 
 void CommCoreModule::innerClearCommServicesAuthMetadata() {
   return this->innerSetCommServicesAuthMetadata("", "", "");
 }
 
 jsi::Value CommCoreModule::setCommServicesAccessToken(
     jsi::Runtime &rt,
     jsi::String accessToken) {
   auto accessTokenStr{accessToken.utf8(rt)};
   return createPromiseAsJSIValue(
       rt,
       [this, accessTokenStr](
           jsi::Runtime &innerRt, std::shared_ptr<Promise> promise) {
         std::string error;
         try {
           CommSecureStore::set(
               CommSecureStore::commServicesAccessToken, accessTokenStr);
         } catch (const std::exception &e) {
           error = e.what();
         }
         this->jsInvoker_->invokeAsync([error, promise]() {
           if (error.size()) {
             promise->reject(error);
           } else {
             promise->resolve(jsi::Value::undefined());
           }
         });
       });
 }
 
 jsi::Value CommCoreModule::clearCommServicesAccessToken(jsi::Runtime &rt) {
   return createPromiseAsJSIValue(
       rt, [this](jsi::Runtime &innerRt, std::shared_ptr<Promise> promise) {
         std::string error;
         try {
           CommSecureStore::set(CommSecureStore::commServicesAccessToken, "");
         } catch (const std::exception &e) {
           error = e.what();
         }
         this->jsInvoker_->invokeAsync([error, promise]() {
           if (error.size()) {
             promise->reject(error);
           } else {
             promise->resolve(jsi::Value::undefined());
           }
         });
       });
 }
 
 void CommCoreModule::startBackupHandler(jsi::Runtime &rt) {
   try {
     ::startBackupHandler();
   } catch (const std::exception &e) {
     throw jsi::JSError(rt, e.what());
   }
 }
 
 void CommCoreModule::stopBackupHandler(jsi::Runtime &rt) {
   try {
     ::stopBackupHandler();
   } catch (const std::exception &e) {
     throw jsi::JSError(rt, e.what());
   }
 }
 
-jsi::Value
-CommCoreModule::createNewBackup(jsi::Runtime &rt, jsi::String backupSecret) {
-  std::string backupSecretStr = backupSecret.utf8(rt);
+jsi::Value CommCoreModule::createNewBackupInternal(
+    jsi::Runtime &rt,
+    std::string backupSecret,
+    std::string backupMessage) {
   return createPromiseAsJSIValue(
       rt, [=](jsi::Runtime &innerRt, std::shared_ptr<Promise> promise) {
         this->cryptoThread->scheduleTask([=, &innerRt]() {
           std::string error;
 
           std::string backupID;
           try {
             backupID = crypto::Tools::generateRandomString(32);
           } catch (const std::exception &e) {
             error = "Failed to generate backupID";
           }
 
           std::string pickleKey;
           std::string pickledAccount;
           if (!error.size()) {
             try {
               pickleKey = crypto::Tools::generateRandomString(64);
               crypto::Persist persist =
                   this->contentCryptoModule->storeAsB64(pickleKey);
               pickledAccount =
                   std::string(persist.account.begin(), persist.account.end());
             } catch (const std::exception &e) {
               error = "Failed to pickle crypto account";
             }
           }
 
           if (!error.size()) {
             auto currentID = RustPromiseManager::instance.addPromise(
                 {promise, this->jsInvoker_, innerRt});
             ::createBackup(
                 rust::string(backupID),
-                rust::string(backupSecretStr),
+                rust::string(backupSecret),
                 rust::string(pickleKey),
                 rust::string(pickledAccount),
+                rust::string(backupMessage),
                 currentID);
           } else {
             this->jsInvoker_->invokeAsync(
                 [=, &innerRt]() { promise->reject(error); });
           }
         });
       });
 }
 
+jsi::Value
+CommCoreModule::createNewBackup(jsi::Runtime &rt, jsi::String backupSecret) {
+  std::string backupSecretStr = backupSecret.utf8(rt);
+  return createNewBackupInternal(rt, backupSecretStr, "");
+}
+
+jsi::Value CommCoreModule::createNewSIWEBackup(
+    jsi::Runtime &rt,
+    jsi::String backupSecret,
+    jsi::String siweBackupMsg) {
+  std::string backupSecretStr = backupSecret.utf8(rt);
+  std::string siweBackupMsgStr = siweBackupMsg.utf8(rt);
+  return createNewBackupInternal(rt, backupSecretStr, siweBackupMsgStr);
+}
+
 jsi::Value
 CommCoreModule::restoreBackup(jsi::Runtime &rt, jsi::String backupSecret) {
   std::string backupSecretStr = backupSecret.utf8(rt);
   return createPromiseAsJSIValue(
       rt, [=](jsi::Runtime &innerRt, std::shared_ptr<Promise> promise) {
         auto currentID = RustPromiseManager::instance.addPromise(
             {promise, this->jsInvoker_, innerRt});
         ::restoreBackup(rust::string(backupSecretStr), currentID);
       });
 }
 
 jsi::Value CommCoreModule::restoreBackupData(
     jsi::Runtime &rt,
     jsi::String backupID,
     jsi::String backupDataKey,
     jsi::String backupLogDataKey) {
   std::string backupIDStr = backupID.utf8(rt);
   std::string backupDataKeyStr = backupDataKey.utf8(rt);
   std::string backupLogDataKeyStr = backupLogDataKey.utf8(rt);
   return createPromiseAsJSIValue(
       rt, [=](jsi::Runtime &innerRt, std::shared_ptr<Promise> promise) {
         auto currentID = RustPromiseManager::instance.addPromise(
             {promise, this->jsInvoker_, innerRt});
         ::restoreBackupData(
             rust::string(backupIDStr),
             rust::string(backupDataKeyStr),
             rust::string(backupLogDataKeyStr),
             currentID);
       });
 }
 
 jsi::Value
 CommCoreModule::retrieveBackupKeys(jsi::Runtime &rt, jsi::String backupSecret) {
   std::string backupSecretStr = backupSecret.utf8(rt);
   return createPromiseAsJSIValue(
       rt, [=](jsi::Runtime &innerRt, std::shared_ptr<Promise> promise) {
         auto currentID = RustPromiseManager::instance.addPromise(
             {promise, this->jsInvoker_, innerRt});
         ::retrieveBackupKeys(rust::string(backupSecretStr), currentID);
       });
 }
 
 jsi::Value CommCoreModule::setSIWEBackupSecrets(
     jsi::Runtime &rt,
     jsi::Object siweBackupSecrets) {
   std::string message =
       siweBackupSecrets.getProperty(rt, "message").asString(rt).utf8(rt);
   std::string signature =
       siweBackupSecrets.getProperty(rt, "signature").asString(rt).utf8(rt);
   folly::dynamic backupSecretsJSON =
       folly::dynamic::object("message", message)("signature", signature);
   std::string backupSecrets = folly::toJson(backupSecretsJSON);
 
   return createPromiseAsJSIValue(
       rt, [=](jsi::Runtime &innerRt, std::shared_ptr<Promise> promise) {
         taskType job = [this, promise, backupSecrets]() {
           std::string error;
           try {
             DatabaseManager::getQueryExecutor().setMetadata(
                 "siweBackupSecrets", backupSecrets);
           } catch (const std::exception &e) {
             error = e.what();
           }
           this->jsInvoker_->invokeAsync([error, promise]() {
             if (error.size()) {
               promise->reject(error);
             } else {
               promise->resolve(jsi::Value::undefined());
             }
           });
         };
         GlobalDBSingleton::instance.scheduleOrRunCancellable(
             job, promise, this->jsInvoker_);
       });
 }
 
 jsi::Value CommCoreModule::getSIWEBackupSecrets(jsi::Runtime &rt) {
   return createPromiseAsJSIValue(
       rt, [this](jsi::Runtime &innerRt, std::shared_ptr<Promise> promise) {
         taskType job = [this, &innerRt, promise]() {
           std::string error;
           std::string backupSecrets;
           try {
             backupSecrets = DatabaseManager::getQueryExecutor().getMetadata(
                 "siweBackupSecrets");
           } catch (const std::exception &e) {
             error = e.what();
           }
           this->jsInvoker_->invokeAsync(
               [&innerRt, error, backupSecrets, promise]() {
                 if (error.size()) {
                   promise->reject(error);
                 } else if (!backupSecrets.size()) {
                   promise->resolve(jsi::Value::undefined());
                 } else {
                   folly::dynamic backupSecretsJSON =
                       folly::parseJson(backupSecrets);
                   std::string message = backupSecretsJSON["message"].asString();
                   std::string signature =
                       backupSecretsJSON["signature"].asString();
 
                   auto siweBackupSecrets = jsi::Object(innerRt);
                   siweBackupSecrets.setProperty(
                       innerRt,
                       "message",
                       jsi::String::createFromUtf8(innerRt, message));
                   siweBackupSecrets.setProperty(
                       innerRt,
                       "signature",
                       jsi::String::createFromUtf8(innerRt, signature));
                   promise->resolve(std::move(siweBackupSecrets));
                 }
               });
         };
         GlobalDBSingleton::instance.scheduleOrRunCancellable(
             job, promise, this->jsInvoker_);
       });
 }
 
 jsi::Value CommCoreModule::getAllReceivedMessageToDevice(jsi::Runtime &rt) {
   return createPromiseAsJSIValue(
       rt, [=](jsi::Runtime &innerRt, std::shared_ptr<Promise> promise) {
         taskType job = [=, &innerRt]() {
           std::string error;
           std::vector<ReceivedMessageToDevice> messages;
 
           try {
             messages = DatabaseManager::getQueryExecutor()
                            .getAllReceivedMessageToDevice();
 
           } catch (std::system_error &e) {
             error = e.what();
           }
           auto messagesPtr =
               std::make_shared<std::vector<ReceivedMessageToDevice>>(
                   std::move(messages));
 
           this->jsInvoker_->invokeAsync(
               [&innerRt, messagesPtr, error, promise]() {
                 if (error.size()) {
                   promise->reject(error);
                   return;
                 }
 
                 jsi::Array jsiMessages =
                     jsi::Array(innerRt, messagesPtr->size());
                 size_t writeIdx = 0;
                 for (const ReceivedMessageToDevice &msg : *messagesPtr) {
                   jsi::Object jsiMsg = jsi::Object(innerRt);
                   jsiMsg.setProperty(innerRt, "messageID", msg.message_id);
                   jsiMsg.setProperty(
                       innerRt, "senderDeviceID", msg.sender_device_id);
                   jsiMsg.setProperty(innerRt, "plaintext", msg.plaintext);
                   jsiMsg.setProperty(innerRt, "status", msg.status);
                   jsiMessages.setValueAtIndex(innerRt, writeIdx++, jsiMsg);
                 }
 
                 promise->resolve(std::move(jsiMessages));
               });
         };
         GlobalDBSingleton::instance.scheduleOrRunCancellable(
             job, promise, this->jsInvoker_);
       });
 }
 
 jsi::Value CommCoreModule::removeReceivedMessagesToDevice(
     jsi::Runtime &rt,
     jsi::Array ids) {
   std::vector<std::string> msgIDsCPP{};
   for (auto idx = 0; idx < ids.size(rt); idx++) {
     std::string msgID = ids.getValueAtIndex(rt, idx).asString(rt).utf8(rt);
     msgIDsCPP.push_back(msgID);
   }
 
   return createPromiseAsJSIValue(
       rt,
       [this,
        msgIDsCPP](jsi::Runtime &innerRt, std::shared_ptr<Promise> promise) {
         taskType job = [this, promise, msgIDsCPP]() {
           std::string error;
           try {
             DatabaseManager::getQueryExecutor().removeReceivedMessagesToDevice(
                 msgIDsCPP);
           } catch (std::system_error &e) {
             error = e.what();
           }
           this->jsInvoker_->invokeAsync([error, promise]() {
             if (error.size()) {
               promise->reject(error);
             } else {
               promise->resolve(jsi::Value::undefined());
             }
           });
         };
         GlobalDBSingleton::instance.scheduleOrRunCancellable(
             job, promise, this->jsInvoker_);
       });
 }
 
 } // namespace comm
diff --git a/native/cpp/CommonCpp/NativeModules/CommCoreModule.h b/native/cpp/CommonCpp/NativeModules/CommCoreModule.h
index 3c3734de6..09ef18e7c 100644
--- a/native/cpp/CommonCpp/NativeModules/CommCoreModule.h
+++ b/native/cpp/CommonCpp/NativeModules/CommCoreModule.h
@@ -1,193 +1,201 @@
 #pragma once
 
 #include "../CryptoTools/CryptoModule.h"
 #include "../Tools/CommMMKV.h"
 #include "../Tools/CommSecureStore.h"
 #include "../Tools/WorkerThread.h"
 #include "../_generated/commJSI.h"
 #include "PersistentStorageUtilities/DataStores/AuxUserStore.h"
 #include "PersistentStorageUtilities/DataStores/CommunityStore.h"
 #include "PersistentStorageUtilities/DataStores/DraftStore.h"
 #include "PersistentStorageUtilities/DataStores/IntegrityStore.h"
 #include "PersistentStorageUtilities/DataStores/KeyserverStore.h"
 #include "PersistentStorageUtilities/DataStores/MessageStore.h"
 #include "PersistentStorageUtilities/DataStores/ReportStore.h"
 #include "PersistentStorageUtilities/DataStores/SyncedMetadataStore.h"
 #include "PersistentStorageUtilities/DataStores/ThreadActivityStore.h"
 #include "PersistentStorageUtilities/DataStores/ThreadStore.h"
 #include "PersistentStorageUtilities/DataStores/UserStore.h"
 #include <ReactCommon/TurboModuleUtils.h>
 #include <jsi/jsi.h>
 #include <memory>
 #include <string>
 
 namespace comm {
 
 namespace jsi = facebook::jsi;
 
 class CommCoreModule : public facebook::react::CommCoreModuleSchemaCxxSpecJSI {
   const int codeVersion{342};
   std::unique_ptr<WorkerThread> cryptoThread;
 
   const std::string secureStoreAccountDataKey = "cryptoAccountDataKey";
   const std::string publicCryptoAccountID = "publicCryptoAccountID";
   std::unique_ptr<crypto::CryptoModule> contentCryptoModule;
   const std::string notifsCryptoAccountID = "notifsCryptoAccountID";
   std::unique_ptr<crypto::CryptoModule> notifsCryptoModule;
   DraftStore draftStore;
   ThreadStore threadStore;
   MessageStore messageStore;
   ReportStore reportStore;
   UserStore userStore;
   KeyserverStore keyserverStore;
   CommunityStore communityStore;
   IntegrityStore integrityStore;
   SyncedMetadataStore syncedMetadataStore;
   AuxUserStore auxUserStore;
   ThreadActivityStore threadActivityStore;
 
   void
   persistCryptoModules(bool persistContentModule, bool persistNotifsModule);
+  jsi::Value createNewBackupInternal(
+      jsi::Runtime &rt,
+      std::string backupSecret,
+      std::string backupMessage);
 
   virtual jsi::Value getDraft(jsi::Runtime &rt, jsi::String key) override;
   virtual jsi::Value
   updateDraft(jsi::Runtime &rt, jsi::String key, jsi::String text) override;
   virtual jsi::Value
   moveDraft(jsi::Runtime &rt, jsi::String oldKey, jsi::String newKey) override;
   virtual jsi::Value getClientDBStore(jsi::Runtime &rt) override;
   virtual jsi::Value removeAllDrafts(jsi::Runtime &rt) override;
   virtual jsi::Array getAllMessagesSync(jsi::Runtime &rt) override;
   virtual void processReportStoreOperationsSync(
       jsi::Runtime &rt,
       jsi::Array operations) override;
   virtual void processMessageStoreOperationsSync(
       jsi::Runtime &rt,
       jsi::Array operations) override;
   virtual jsi::Array getAllThreadsSync(jsi::Runtime &rt) override;
   virtual void processThreadStoreOperationsSync(
       jsi::Runtime &rt,
       jsi::Array operations) override;
   virtual jsi::Value
   processDBStoreOperations(jsi::Runtime &rt, jsi::Object operations) override;
   template <typename T>
   void appendDBStoreOps(
       jsi::Runtime &rt,
       jsi::Object &operations,
       const char *key,
       T &store,
       std::shared_ptr<std::vector<std::unique_ptr<DBOperationBase>>>
           &destination);
   virtual jsi::Value initializeCryptoAccount(jsi::Runtime &rt) override;
   virtual jsi::Value getUserPublicKey(jsi::Runtime &rt) override;
   virtual jsi::Value
   getOneTimeKeys(jsi::Runtime &rt, double oneTimeKeysAmount) override;
   virtual jsi::Value validateAndUploadPrekeys(
       jsi::Runtime &rt,
       jsi::String authUserID,
       jsi::String authDeviceID,
       jsi::String authAccessToken) override;
   virtual jsi::Value validateAndGetPrekeys(jsi::Runtime &rt) override;
   virtual jsi::Value initializeNotificationsSession(
       jsi::Runtime &rt,
       jsi::String identityKeys,
       jsi::String prekey,
       jsi::String prekeySignature,
       jsi::String oneTimeKeys,
       jsi::String keyserverID) override;
   virtual jsi::Value
   isNotificationsSessionInitialized(jsi::Runtime &rt) override;
   virtual jsi::Value updateKeyserverDataInNotifStorage(
       jsi::Runtime &rt,
       jsi::Array keyserversData) override;
   virtual jsi::Value removeKeyserverDataFromNotifStorage(
       jsi::Runtime &rt,
       jsi::Array keyserverIDsToDelete) override;
   virtual jsi::Value getKeyserverDataFromNotifStorage(
       jsi::Runtime &rt,
       jsi::Array keyserverIDs) override;
   virtual jsi::Value initializeContentOutboundSession(
       jsi::Runtime &rt,
       jsi::String identityKeys,
       jsi::String prekey,
       jsi::String prekeySignature,
       jsi::String oneTimeKeys,
       jsi::String deviceID) override;
   virtual jsi::Value initializeContentInboundSession(
       jsi::Runtime &rt,
       jsi::String identityKeys,
       jsi::Object encryptedDataJSI,
       jsi::String deviceID,
       double sessionVersion,
       bool overwrite) override;
   virtual jsi::Value
   encrypt(jsi::Runtime &rt, jsi::String message, jsi::String deviceID) override;
   virtual jsi::Value decrypt(
       jsi::Runtime &rt,
       jsi::Object encryptedDataJSI,
       jsi::String deviceID) override;
   virtual jsi::Value decryptSequentialAndPersist(
       jsi::Runtime &rt,
       jsi::Object encryptedDataJSI,
       jsi::String deviceID,
       jsi::String messageID) override;
   virtual jsi::Value
   signMessage(jsi::Runtime &rt, jsi::String message) override;
   virtual void terminate(jsi::Runtime &rt) override;
   virtual double getCodeVersion(jsi::Runtime &rt) override;
   virtual jsi::Value
   setNotifyToken(jsi::Runtime &rt, jsi::String token) override;
   virtual jsi::Value clearNotifyToken(jsi::Runtime &rt) override;
   virtual jsi::Value
   stampSQLiteDBUserID(jsi::Runtime &rt, jsi::String userID) override;
   virtual jsi::Value getSQLiteStampedUserID(jsi::Runtime &rt) override;
   virtual jsi::Value clearSensitiveData(jsi::Runtime &rt) override;
   virtual bool checkIfDatabaseNeedsDeletion(jsi::Runtime &rt) override;
   virtual void reportDBOperationsFailure(jsi::Runtime &rt) override;
   virtual jsi::Value computeBackupKey(
       jsi::Runtime &rt,
       jsi::String password,
       jsi::String backupID) override;
   virtual jsi::Value
   generateRandomString(jsi::Runtime &rt, double size) override;
   virtual jsi::Value setCommServicesAuthMetadata(
       jsi::Runtime &rt,
       jsi::String userID,
       jsi::String deviceID,
       jsi::String accessToken) override;
   virtual void innerSetCommServicesAuthMetadata(
       std::string userID,
       std::string deviceID,
       std::string accessToken);
   virtual jsi::Value getCommServicesAuthMetadata(jsi::Runtime &rt) override;
   virtual jsi::Value clearCommServicesAuthMetadata(jsi::Runtime &rt) override;
   virtual void innerClearCommServicesAuthMetadata();
   virtual jsi::Value setCommServicesAccessToken(
       jsi::Runtime &rt,
       jsi::String accessToken) override;
   virtual jsi::Value clearCommServicesAccessToken(jsi::Runtime &rt) override;
   virtual void startBackupHandler(jsi::Runtime &rt) override;
   virtual void stopBackupHandler(jsi::Runtime &rt) override;
   virtual jsi::Value
   createNewBackup(jsi::Runtime &rt, jsi::String backupSecret) override;
+  virtual jsi::Value createNewSIWEBackup(
+      jsi::Runtime &rt,
+      jsi::String backupSecret,
+      jsi::String siweBackupMsg) override;
   virtual jsi::Value
   restoreBackup(jsi::Runtime &rt, jsi::String backupSecret) override;
   virtual jsi::Value restoreBackupData(
       jsi::Runtime &rt,
       jsi::String backupID,
       jsi::String backupDataKey,
       jsi::String backupLogDataKey) override;
   virtual jsi::Value
   retrieveBackupKeys(jsi::Runtime &rt, jsi::String backupSecret) override;
   virtual jsi::Value setSIWEBackupSecrets(
       jsi::Runtime &rt,
       jsi::Object siweBackupSecrets) override;
   virtual jsi::Value getSIWEBackupSecrets(jsi::Runtime &rt) override;
   virtual jsi::Value getAllReceivedMessageToDevice(jsi::Runtime &rt) override;
   virtual jsi::Value
   removeReceivedMessagesToDevice(jsi::Runtime &rt, jsi::Array ids) override;
 
 public:
   CommCoreModule(std::shared_ptr<facebook::react::CallInvoker> jsInvoker);
 };
 
 } // namespace comm
diff --git a/native/cpp/CommonCpp/_generated/commJSI-generated.cpp b/native/cpp/CommonCpp/_generated/commJSI-generated.cpp
index ed99b8926..81bc21c3e 100644
--- a/native/cpp/CommonCpp/_generated/commJSI-generated.cpp
+++ b/native/cpp/CommonCpp/_generated/commJSI-generated.cpp
@@ -1,241 +1,245 @@
 /**
  * This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen).
  *
  * Do not edit this file as changes may cause incorrect behavior and will be lost
  * once the code is regenerated.
  *
  * @generated by codegen project: GenerateModuleH.js
  */
 
 #include "commJSI.h"
 
 namespace facebook {
 namespace react {
 
 static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_getDraft(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
   return static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->getDraft(rt, args[0].asString(rt));
 }
 static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_updateDraft(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
   return static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->updateDraft(rt, args[0].asString(rt), args[1].asString(rt));
 }
 static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_moveDraft(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
   return static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->moveDraft(rt, args[0].asString(rt), args[1].asString(rt));
 }
 static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_getClientDBStore(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
   return static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->getClientDBStore(rt);
 }
 static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_removeAllDrafts(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
   return static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->removeAllDrafts(rt);
 }
 static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_getAllMessagesSync(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
   return static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->getAllMessagesSync(rt);
 }
 static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_processMessageStoreOperationsSync(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
   static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->processMessageStoreOperationsSync(rt, args[0].asObject(rt).asArray(rt));
   return jsi::Value::undefined();
 }
 static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_getAllThreadsSync(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
   return static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->getAllThreadsSync(rt);
 }
 static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_processReportStoreOperationsSync(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
   static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->processReportStoreOperationsSync(rt, args[0].asObject(rt).asArray(rt));
   return jsi::Value::undefined();
 }
 static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_processThreadStoreOperationsSync(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
   static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->processThreadStoreOperationsSync(rt, args[0].asObject(rt).asArray(rt));
   return jsi::Value::undefined();
 }
 static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_processDBStoreOperations(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
   return static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->processDBStoreOperations(rt, args[0].asObject(rt));
 }
 static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_initializeCryptoAccount(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
   return static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->initializeCryptoAccount(rt);
 }
 static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_getUserPublicKey(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
   return static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->getUserPublicKey(rt);
 }
 static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_getOneTimeKeys(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
   return static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->getOneTimeKeys(rt, args[0].asNumber());
 }
 static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_validateAndGetPrekeys(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
   return static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->validateAndGetPrekeys(rt);
 }
 static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_validateAndUploadPrekeys(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
   return static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->validateAndUploadPrekeys(rt, args[0].asString(rt), args[1].asString(rt), args[2].asString(rt));
 }
 static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_initializeNotificationsSession(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
   return static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->initializeNotificationsSession(rt, args[0].asString(rt), args[1].asString(rt), args[2].asString(rt), args[3].asString(rt), args[4].asString(rt));
 }
 static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_isNotificationsSessionInitialized(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
   return static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->isNotificationsSessionInitialized(rt);
 }
 static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_updateKeyserverDataInNotifStorage(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
   return static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->updateKeyserverDataInNotifStorage(rt, args[0].asObject(rt).asArray(rt));
 }
 static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_removeKeyserverDataFromNotifStorage(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
   return static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->removeKeyserverDataFromNotifStorage(rt, args[0].asObject(rt).asArray(rt));
 }
 static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_getKeyserverDataFromNotifStorage(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
   return static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->getKeyserverDataFromNotifStorage(rt, args[0].asObject(rt).asArray(rt));
 }
 static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_initializeContentOutboundSession(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
   return static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->initializeContentOutboundSession(rt, args[0].asString(rt), args[1].asString(rt), args[2].asString(rt), args[3].asString(rt), args[4].asString(rt));
 }
 static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_initializeContentInboundSession(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
   return static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->initializeContentInboundSession(rt, args[0].asString(rt), args[1].asObject(rt), args[2].asString(rt), args[3].asNumber(), args[4].asBool());
 }
 static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_encrypt(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
   return static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->encrypt(rt, args[0].asString(rt), args[1].asString(rt));
 }
 static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_decrypt(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
   return static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->decrypt(rt, args[0].asObject(rt), args[1].asString(rt));
 }
 static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_decryptSequentialAndPersist(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
   return static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->decryptSequentialAndPersist(rt, args[0].asObject(rt), args[1].asString(rt), args[2].asString(rt));
 }
 static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_signMessage(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
   return static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->signMessage(rt, args[0].asString(rt));
 }
 static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_getCodeVersion(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
   return static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->getCodeVersion(rt);
 }
 static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_terminate(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
   static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->terminate(rt);
   return jsi::Value::undefined();
 }
 static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_setNotifyToken(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
   return static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->setNotifyToken(rt, args[0].asString(rt));
 }
 static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_clearNotifyToken(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
   return static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->clearNotifyToken(rt);
 }
 static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_stampSQLiteDBUserID(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
   return static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->stampSQLiteDBUserID(rt, args[0].asString(rt));
 }
 static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_getSQLiteStampedUserID(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
   return static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->getSQLiteStampedUserID(rt);
 }
 static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_clearSensitiveData(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
   return static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->clearSensitiveData(rt);
 }
 static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_checkIfDatabaseNeedsDeletion(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
   return static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->checkIfDatabaseNeedsDeletion(rt);
 }
 static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_reportDBOperationsFailure(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
   static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->reportDBOperationsFailure(rt);
   return jsi::Value::undefined();
 }
 static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_computeBackupKey(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
   return static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->computeBackupKey(rt, args[0].asString(rt), args[1].asString(rt));
 }
 static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_generateRandomString(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
   return static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->generateRandomString(rt, args[0].asNumber());
 }
 static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_setCommServicesAuthMetadata(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
   return static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->setCommServicesAuthMetadata(rt, args[0].asString(rt), args[1].asString(rt), args[2].asString(rt));
 }
 static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_getCommServicesAuthMetadata(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
   return static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->getCommServicesAuthMetadata(rt);
 }
 static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_clearCommServicesAuthMetadata(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
   return static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->clearCommServicesAuthMetadata(rt);
 }
 static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_setCommServicesAccessToken(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
   return static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->setCommServicesAccessToken(rt, args[0].asString(rt));
 }
 static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_clearCommServicesAccessToken(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
   return static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->clearCommServicesAccessToken(rt);
 }
 static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_startBackupHandler(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
   static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->startBackupHandler(rt);
   return jsi::Value::undefined();
 }
 static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_stopBackupHandler(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
   static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->stopBackupHandler(rt);
   return jsi::Value::undefined();
 }
 static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_createNewBackup(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
   return static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->createNewBackup(rt, args[0].asString(rt));
 }
+static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_createNewSIWEBackup(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
+  return static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->createNewSIWEBackup(rt, args[0].asString(rt), args[1].asString(rt));
+}
 static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_restoreBackup(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
   return static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->restoreBackup(rt, args[0].asString(rt));
 }
 static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_restoreBackupData(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
   return static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->restoreBackupData(rt, args[0].asString(rt), args[1].asString(rt), args[2].asString(rt));
 }
 static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_retrieveBackupKeys(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
   return static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->retrieveBackupKeys(rt, args[0].asString(rt));
 }
 static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_setSIWEBackupSecrets(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
   return static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->setSIWEBackupSecrets(rt, args[0].asObject(rt));
 }
 static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_getSIWEBackupSecrets(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
   return static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->getSIWEBackupSecrets(rt);
 }
 static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_getAllReceivedMessageToDevice(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
   return static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->getAllReceivedMessageToDevice(rt);
 }
 static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_removeReceivedMessagesToDevice(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
   return static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->removeReceivedMessagesToDevice(rt, args[0].asObject(rt).asArray(rt));
 }
 
 CommCoreModuleSchemaCxxSpecJSI::CommCoreModuleSchemaCxxSpecJSI(std::shared_ptr<CallInvoker> jsInvoker)
   : TurboModule("CommTurboModule", jsInvoker) {
   methodMap_["getDraft"] = MethodMetadata {1, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_getDraft};
   methodMap_["updateDraft"] = MethodMetadata {2, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_updateDraft};
   methodMap_["moveDraft"] = MethodMetadata {2, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_moveDraft};
   methodMap_["getClientDBStore"] = MethodMetadata {0, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_getClientDBStore};
   methodMap_["removeAllDrafts"] = MethodMetadata {0, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_removeAllDrafts};
   methodMap_["getAllMessagesSync"] = MethodMetadata {0, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_getAllMessagesSync};
   methodMap_["processMessageStoreOperationsSync"] = MethodMetadata {1, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_processMessageStoreOperationsSync};
   methodMap_["getAllThreadsSync"] = MethodMetadata {0, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_getAllThreadsSync};
   methodMap_["processReportStoreOperationsSync"] = MethodMetadata {1, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_processReportStoreOperationsSync};
   methodMap_["processThreadStoreOperationsSync"] = MethodMetadata {1, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_processThreadStoreOperationsSync};
   methodMap_["processDBStoreOperations"] = MethodMetadata {1, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_processDBStoreOperations};
   methodMap_["initializeCryptoAccount"] = MethodMetadata {0, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_initializeCryptoAccount};
   methodMap_["getUserPublicKey"] = MethodMetadata {0, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_getUserPublicKey};
   methodMap_["getOneTimeKeys"] = MethodMetadata {1, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_getOneTimeKeys};
   methodMap_["validateAndGetPrekeys"] = MethodMetadata {0, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_validateAndGetPrekeys};
   methodMap_["validateAndUploadPrekeys"] = MethodMetadata {3, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_validateAndUploadPrekeys};
   methodMap_["initializeNotificationsSession"] = MethodMetadata {5, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_initializeNotificationsSession};
   methodMap_["isNotificationsSessionInitialized"] = MethodMetadata {0, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_isNotificationsSessionInitialized};
   methodMap_["updateKeyserverDataInNotifStorage"] = MethodMetadata {1, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_updateKeyserverDataInNotifStorage};
   methodMap_["removeKeyserverDataFromNotifStorage"] = MethodMetadata {1, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_removeKeyserverDataFromNotifStorage};
   methodMap_["getKeyserverDataFromNotifStorage"] = MethodMetadata {1, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_getKeyserverDataFromNotifStorage};
   methodMap_["initializeContentOutboundSession"] = MethodMetadata {5, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_initializeContentOutboundSession};
   methodMap_["initializeContentInboundSession"] = MethodMetadata {5, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_initializeContentInboundSession};
   methodMap_["encrypt"] = MethodMetadata {2, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_encrypt};
   methodMap_["decrypt"] = MethodMetadata {2, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_decrypt};
   methodMap_["decryptSequentialAndPersist"] = MethodMetadata {3, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_decryptSequentialAndPersist};
   methodMap_["signMessage"] = MethodMetadata {1, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_signMessage};
   methodMap_["getCodeVersion"] = MethodMetadata {0, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_getCodeVersion};
   methodMap_["terminate"] = MethodMetadata {0, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_terminate};
   methodMap_["setNotifyToken"] = MethodMetadata {1, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_setNotifyToken};
   methodMap_["clearNotifyToken"] = MethodMetadata {0, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_clearNotifyToken};
   methodMap_["stampSQLiteDBUserID"] = MethodMetadata {1, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_stampSQLiteDBUserID};
   methodMap_["getSQLiteStampedUserID"] = MethodMetadata {0, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_getSQLiteStampedUserID};
   methodMap_["clearSensitiveData"] = MethodMetadata {0, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_clearSensitiveData};
   methodMap_["checkIfDatabaseNeedsDeletion"] = MethodMetadata {0, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_checkIfDatabaseNeedsDeletion};
   methodMap_["reportDBOperationsFailure"] = MethodMetadata {0, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_reportDBOperationsFailure};
   methodMap_["computeBackupKey"] = MethodMetadata {2, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_computeBackupKey};
   methodMap_["generateRandomString"] = MethodMetadata {1, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_generateRandomString};
   methodMap_["setCommServicesAuthMetadata"] = MethodMetadata {3, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_setCommServicesAuthMetadata};
   methodMap_["getCommServicesAuthMetadata"] = MethodMetadata {0, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_getCommServicesAuthMetadata};
   methodMap_["clearCommServicesAuthMetadata"] = MethodMetadata {0, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_clearCommServicesAuthMetadata};
   methodMap_["setCommServicesAccessToken"] = MethodMetadata {1, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_setCommServicesAccessToken};
   methodMap_["clearCommServicesAccessToken"] = MethodMetadata {0, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_clearCommServicesAccessToken};
   methodMap_["startBackupHandler"] = MethodMetadata {0, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_startBackupHandler};
   methodMap_["stopBackupHandler"] = MethodMetadata {0, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_stopBackupHandler};
   methodMap_["createNewBackup"] = MethodMetadata {1, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_createNewBackup};
+  methodMap_["createNewSIWEBackup"] = MethodMetadata {2, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_createNewSIWEBackup};
   methodMap_["restoreBackup"] = MethodMetadata {1, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_restoreBackup};
   methodMap_["restoreBackupData"] = MethodMetadata {3, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_restoreBackupData};
   methodMap_["retrieveBackupKeys"] = MethodMetadata {1, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_retrieveBackupKeys};
   methodMap_["setSIWEBackupSecrets"] = MethodMetadata {1, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_setSIWEBackupSecrets};
   methodMap_["getSIWEBackupSecrets"] = MethodMetadata {0, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_getSIWEBackupSecrets};
   methodMap_["getAllReceivedMessageToDevice"] = MethodMetadata {0, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_getAllReceivedMessageToDevice};
   methodMap_["removeReceivedMessagesToDevice"] = MethodMetadata {1, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_removeReceivedMessagesToDevice};
 }
 
 
 } // namespace react
 } // namespace facebook
diff --git a/native/cpp/CommonCpp/_generated/commJSI.h b/native/cpp/CommonCpp/_generated/commJSI.h
index 49197af1b..5eab93579 100644
--- a/native/cpp/CommonCpp/_generated/commJSI.h
+++ b/native/cpp/CommonCpp/_generated/commJSI.h
@@ -1,530 +1,539 @@
 /**
  * This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen).
  *
  * Do not edit this file as changes may cause incorrect behavior and will be lost
  * once the code is regenerated.
  *
  * @generated by codegen project: GenerateModuleH.js
  */
 
 #pragma once
 
 #include <ReactCommon/TurboModule.h>
 #include <react/bridging/Bridging.h>
 
 namespace facebook {
 namespace react {
 
 class JSI_EXPORT CommCoreModuleSchemaCxxSpecJSI : public TurboModule {
 protected:
   CommCoreModuleSchemaCxxSpecJSI(std::shared_ptr<CallInvoker> jsInvoker);
 
 public:
   virtual jsi::Value getDraft(jsi::Runtime &rt, jsi::String key) = 0;
   virtual jsi::Value updateDraft(jsi::Runtime &rt, jsi::String key, jsi::String text) = 0;
   virtual jsi::Value moveDraft(jsi::Runtime &rt, jsi::String oldKey, jsi::String newKey) = 0;
   virtual jsi::Value getClientDBStore(jsi::Runtime &rt) = 0;
   virtual jsi::Value removeAllDrafts(jsi::Runtime &rt) = 0;
   virtual jsi::Array getAllMessagesSync(jsi::Runtime &rt) = 0;
   virtual void processMessageStoreOperationsSync(jsi::Runtime &rt, jsi::Array operations) = 0;
   virtual jsi::Array getAllThreadsSync(jsi::Runtime &rt) = 0;
   virtual void processReportStoreOperationsSync(jsi::Runtime &rt, jsi::Array operations) = 0;
   virtual void processThreadStoreOperationsSync(jsi::Runtime &rt, jsi::Array operations) = 0;
   virtual jsi::Value processDBStoreOperations(jsi::Runtime &rt, jsi::Object operations) = 0;
   virtual jsi::Value initializeCryptoAccount(jsi::Runtime &rt) = 0;
   virtual jsi::Value getUserPublicKey(jsi::Runtime &rt) = 0;
   virtual jsi::Value getOneTimeKeys(jsi::Runtime &rt, double oneTimeKeysAmount) = 0;
   virtual jsi::Value validateAndGetPrekeys(jsi::Runtime &rt) = 0;
   virtual jsi::Value validateAndUploadPrekeys(jsi::Runtime &rt, jsi::String authUserID, jsi::String authDeviceID, jsi::String authAccessToken) = 0;
   virtual jsi::Value initializeNotificationsSession(jsi::Runtime &rt, jsi::String identityKeys, jsi::String prekey, jsi::String prekeySignature, jsi::String oneTimeKey, jsi::String keyserverID) = 0;
   virtual jsi::Value isNotificationsSessionInitialized(jsi::Runtime &rt) = 0;
   virtual jsi::Value updateKeyserverDataInNotifStorage(jsi::Runtime &rt, jsi::Array keyserversData) = 0;
   virtual jsi::Value removeKeyserverDataFromNotifStorage(jsi::Runtime &rt, jsi::Array keyserverIDsToDelete) = 0;
   virtual jsi::Value getKeyserverDataFromNotifStorage(jsi::Runtime &rt, jsi::Array keyserverIDs) = 0;
   virtual jsi::Value initializeContentOutboundSession(jsi::Runtime &rt, jsi::String identityKeys, jsi::String prekey, jsi::String prekeySignature, jsi::String oneTimeKey, jsi::String deviceID) = 0;
   virtual jsi::Value initializeContentInboundSession(jsi::Runtime &rt, jsi::String identityKeys, jsi::Object encryptedContent, jsi::String deviceID, double sessionVersion, bool overwrite) = 0;
   virtual jsi::Value encrypt(jsi::Runtime &rt, jsi::String message, jsi::String deviceID) = 0;
   virtual jsi::Value decrypt(jsi::Runtime &rt, jsi::Object encryptedData, jsi::String deviceID) = 0;
   virtual jsi::Value decryptSequentialAndPersist(jsi::Runtime &rt, jsi::Object encryptedData, jsi::String deviceID, jsi::String messageID) = 0;
   virtual jsi::Value signMessage(jsi::Runtime &rt, jsi::String message) = 0;
   virtual double getCodeVersion(jsi::Runtime &rt) = 0;
   virtual void terminate(jsi::Runtime &rt) = 0;
   virtual jsi::Value setNotifyToken(jsi::Runtime &rt, jsi::String token) = 0;
   virtual jsi::Value clearNotifyToken(jsi::Runtime &rt) = 0;
   virtual jsi::Value stampSQLiteDBUserID(jsi::Runtime &rt, jsi::String userID) = 0;
   virtual jsi::Value getSQLiteStampedUserID(jsi::Runtime &rt) = 0;
   virtual jsi::Value clearSensitiveData(jsi::Runtime &rt) = 0;
   virtual bool checkIfDatabaseNeedsDeletion(jsi::Runtime &rt) = 0;
   virtual void reportDBOperationsFailure(jsi::Runtime &rt) = 0;
   virtual jsi::Value computeBackupKey(jsi::Runtime &rt, jsi::String password, jsi::String backupID) = 0;
   virtual jsi::Value generateRandomString(jsi::Runtime &rt, double size) = 0;
   virtual jsi::Value setCommServicesAuthMetadata(jsi::Runtime &rt, jsi::String userID, jsi::String deviceID, jsi::String accessToken) = 0;
   virtual jsi::Value getCommServicesAuthMetadata(jsi::Runtime &rt) = 0;
   virtual jsi::Value clearCommServicesAuthMetadata(jsi::Runtime &rt) = 0;
   virtual jsi::Value setCommServicesAccessToken(jsi::Runtime &rt, jsi::String accessToken) = 0;
   virtual jsi::Value clearCommServicesAccessToken(jsi::Runtime &rt) = 0;
   virtual void startBackupHandler(jsi::Runtime &rt) = 0;
   virtual void stopBackupHandler(jsi::Runtime &rt) = 0;
   virtual jsi::Value createNewBackup(jsi::Runtime &rt, jsi::String backupSecret) = 0;
+  virtual jsi::Value createNewSIWEBackup(jsi::Runtime &rt, jsi::String backupSecret, jsi::String siweBackupMsg) = 0;
   virtual jsi::Value restoreBackup(jsi::Runtime &rt, jsi::String backupSecret) = 0;
   virtual jsi::Value restoreBackupData(jsi::Runtime &rt, jsi::String backupID, jsi::String backupDataKey, jsi::String backupLogDataKey) = 0;
   virtual jsi::Value retrieveBackupKeys(jsi::Runtime &rt, jsi::String backupSecret) = 0;
   virtual jsi::Value setSIWEBackupSecrets(jsi::Runtime &rt, jsi::Object siweBackupSecrets) = 0;
   virtual jsi::Value getSIWEBackupSecrets(jsi::Runtime &rt) = 0;
   virtual jsi::Value getAllReceivedMessageToDevice(jsi::Runtime &rt) = 0;
   virtual jsi::Value removeReceivedMessagesToDevice(jsi::Runtime &rt, jsi::Array ids) = 0;
 
 };
 
 template <typename T>
 class JSI_EXPORT CommCoreModuleSchemaCxxSpec : public TurboModule {
 public:
   jsi::Value get(jsi::Runtime &rt, const jsi::PropNameID &propName) override {
     return delegate_.get(rt, propName);
   }
 
 protected:
   CommCoreModuleSchemaCxxSpec(std::shared_ptr<CallInvoker> jsInvoker)
     : TurboModule("CommTurboModule", jsInvoker),
       delegate_(static_cast<T*>(this), jsInvoker) {}
 
 private:
   class Delegate : public CommCoreModuleSchemaCxxSpecJSI {
   public:
     Delegate(T *instance, std::shared_ptr<CallInvoker> jsInvoker) :
       CommCoreModuleSchemaCxxSpecJSI(std::move(jsInvoker)), instance_(instance) {}
 
     jsi::Value getDraft(jsi::Runtime &rt, jsi::String key) override {
       static_assert(
           bridging::getParameterCount(&T::getDraft) == 2,
           "Expected getDraft(...) to have 2 parameters");
 
       return bridging::callFromJs<jsi::Value>(
           rt, &T::getDraft, jsInvoker_, instance_, std::move(key));
     }
     jsi::Value updateDraft(jsi::Runtime &rt, jsi::String key, jsi::String text) override {
       static_assert(
           bridging::getParameterCount(&T::updateDraft) == 3,
           "Expected updateDraft(...) to have 3 parameters");
 
       return bridging::callFromJs<jsi::Value>(
           rt, &T::updateDraft, jsInvoker_, instance_, std::move(key), std::move(text));
     }
     jsi::Value moveDraft(jsi::Runtime &rt, jsi::String oldKey, jsi::String newKey) override {
       static_assert(
           bridging::getParameterCount(&T::moveDraft) == 3,
           "Expected moveDraft(...) to have 3 parameters");
 
       return bridging::callFromJs<jsi::Value>(
           rt, &T::moveDraft, jsInvoker_, instance_, std::move(oldKey), std::move(newKey));
     }
     jsi::Value getClientDBStore(jsi::Runtime &rt) override {
       static_assert(
           bridging::getParameterCount(&T::getClientDBStore) == 1,
           "Expected getClientDBStore(...) to have 1 parameters");
 
       return bridging::callFromJs<jsi::Value>(
           rt, &T::getClientDBStore, jsInvoker_, instance_);
     }
     jsi::Value removeAllDrafts(jsi::Runtime &rt) override {
       static_assert(
           bridging::getParameterCount(&T::removeAllDrafts) == 1,
           "Expected removeAllDrafts(...) to have 1 parameters");
 
       return bridging::callFromJs<jsi::Value>(
           rt, &T::removeAllDrafts, jsInvoker_, instance_);
     }
     jsi::Array getAllMessagesSync(jsi::Runtime &rt) override {
       static_assert(
           bridging::getParameterCount(&T::getAllMessagesSync) == 1,
           "Expected getAllMessagesSync(...) to have 1 parameters");
 
       return bridging::callFromJs<jsi::Array>(
           rt, &T::getAllMessagesSync, jsInvoker_, instance_);
     }
     void processMessageStoreOperationsSync(jsi::Runtime &rt, jsi::Array operations) override {
       static_assert(
           bridging::getParameterCount(&T::processMessageStoreOperationsSync) == 2,
           "Expected processMessageStoreOperationsSync(...) to have 2 parameters");
 
       return bridging::callFromJs<void>(
           rt, &T::processMessageStoreOperationsSync, jsInvoker_, instance_, std::move(operations));
     }
     jsi::Array getAllThreadsSync(jsi::Runtime &rt) override {
       static_assert(
           bridging::getParameterCount(&T::getAllThreadsSync) == 1,
           "Expected getAllThreadsSync(...) to have 1 parameters");
 
       return bridging::callFromJs<jsi::Array>(
           rt, &T::getAllThreadsSync, jsInvoker_, instance_);
     }
     void processReportStoreOperationsSync(jsi::Runtime &rt, jsi::Array operations) override {
       static_assert(
           bridging::getParameterCount(&T::processReportStoreOperationsSync) == 2,
           "Expected processReportStoreOperationsSync(...) to have 2 parameters");
 
       return bridging::callFromJs<void>(
           rt, &T::processReportStoreOperationsSync, jsInvoker_, instance_, std::move(operations));
     }
     void processThreadStoreOperationsSync(jsi::Runtime &rt, jsi::Array operations) override {
       static_assert(
           bridging::getParameterCount(&T::processThreadStoreOperationsSync) == 2,
           "Expected processThreadStoreOperationsSync(...) to have 2 parameters");
 
       return bridging::callFromJs<void>(
           rt, &T::processThreadStoreOperationsSync, jsInvoker_, instance_, std::move(operations));
     }
     jsi::Value processDBStoreOperations(jsi::Runtime &rt, jsi::Object operations) override {
       static_assert(
           bridging::getParameterCount(&T::processDBStoreOperations) == 2,
           "Expected processDBStoreOperations(...) to have 2 parameters");
 
       return bridging::callFromJs<jsi::Value>(
           rt, &T::processDBStoreOperations, jsInvoker_, instance_, std::move(operations));
     }
     jsi::Value initializeCryptoAccount(jsi::Runtime &rt) override {
       static_assert(
           bridging::getParameterCount(&T::initializeCryptoAccount) == 1,
           "Expected initializeCryptoAccount(...) to have 1 parameters");
 
       return bridging::callFromJs<jsi::Value>(
           rt, &T::initializeCryptoAccount, jsInvoker_, instance_);
     }
     jsi::Value getUserPublicKey(jsi::Runtime &rt) override {
       static_assert(
           bridging::getParameterCount(&T::getUserPublicKey) == 1,
           "Expected getUserPublicKey(...) to have 1 parameters");
 
       return bridging::callFromJs<jsi::Value>(
           rt, &T::getUserPublicKey, jsInvoker_, instance_);
     }
     jsi::Value getOneTimeKeys(jsi::Runtime &rt, double oneTimeKeysAmount) override {
       static_assert(
           bridging::getParameterCount(&T::getOneTimeKeys) == 2,
           "Expected getOneTimeKeys(...) to have 2 parameters");
 
       return bridging::callFromJs<jsi::Value>(
           rt, &T::getOneTimeKeys, jsInvoker_, instance_, std::move(oneTimeKeysAmount));
     }
     jsi::Value validateAndGetPrekeys(jsi::Runtime &rt) override {
       static_assert(
           bridging::getParameterCount(&T::validateAndGetPrekeys) == 1,
           "Expected validateAndGetPrekeys(...) to have 1 parameters");
 
       return bridging::callFromJs<jsi::Value>(
           rt, &T::validateAndGetPrekeys, jsInvoker_, instance_);
     }
     jsi::Value validateAndUploadPrekeys(jsi::Runtime &rt, jsi::String authUserID, jsi::String authDeviceID, jsi::String authAccessToken) override {
       static_assert(
           bridging::getParameterCount(&T::validateAndUploadPrekeys) == 4,
           "Expected validateAndUploadPrekeys(...) to have 4 parameters");
 
       return bridging::callFromJs<jsi::Value>(
           rt, &T::validateAndUploadPrekeys, jsInvoker_, instance_, std::move(authUserID), std::move(authDeviceID), std::move(authAccessToken));
     }
     jsi::Value initializeNotificationsSession(jsi::Runtime &rt, jsi::String identityKeys, jsi::String prekey, jsi::String prekeySignature, jsi::String oneTimeKey, jsi::String keyserverID) override {
       static_assert(
           bridging::getParameterCount(&T::initializeNotificationsSession) == 6,
           "Expected initializeNotificationsSession(...) to have 6 parameters");
 
       return bridging::callFromJs<jsi::Value>(
           rt, &T::initializeNotificationsSession, jsInvoker_, instance_, std::move(identityKeys), std::move(prekey), std::move(prekeySignature), std::move(oneTimeKey), std::move(keyserverID));
     }
     jsi::Value isNotificationsSessionInitialized(jsi::Runtime &rt) override {
       static_assert(
           bridging::getParameterCount(&T::isNotificationsSessionInitialized) == 1,
           "Expected isNotificationsSessionInitialized(...) to have 1 parameters");
 
       return bridging::callFromJs<jsi::Value>(
           rt, &T::isNotificationsSessionInitialized, jsInvoker_, instance_);
     }
     jsi::Value updateKeyserverDataInNotifStorage(jsi::Runtime &rt, jsi::Array keyserversData) override {
       static_assert(
           bridging::getParameterCount(&T::updateKeyserverDataInNotifStorage) == 2,
           "Expected updateKeyserverDataInNotifStorage(...) to have 2 parameters");
 
       return bridging::callFromJs<jsi::Value>(
           rt, &T::updateKeyserverDataInNotifStorage, jsInvoker_, instance_, std::move(keyserversData));
     }
     jsi::Value removeKeyserverDataFromNotifStorage(jsi::Runtime &rt, jsi::Array keyserverIDsToDelete) override {
       static_assert(
           bridging::getParameterCount(&T::removeKeyserverDataFromNotifStorage) == 2,
           "Expected removeKeyserverDataFromNotifStorage(...) to have 2 parameters");
 
       return bridging::callFromJs<jsi::Value>(
           rt, &T::removeKeyserverDataFromNotifStorage, jsInvoker_, instance_, std::move(keyserverIDsToDelete));
     }
     jsi::Value getKeyserverDataFromNotifStorage(jsi::Runtime &rt, jsi::Array keyserverIDs) override {
       static_assert(
           bridging::getParameterCount(&T::getKeyserverDataFromNotifStorage) == 2,
           "Expected getKeyserverDataFromNotifStorage(...) to have 2 parameters");
 
       return bridging::callFromJs<jsi::Value>(
           rt, &T::getKeyserverDataFromNotifStorage, jsInvoker_, instance_, std::move(keyserverIDs));
     }
     jsi::Value initializeContentOutboundSession(jsi::Runtime &rt, jsi::String identityKeys, jsi::String prekey, jsi::String prekeySignature, jsi::String oneTimeKey, jsi::String deviceID) override {
       static_assert(
           bridging::getParameterCount(&T::initializeContentOutboundSession) == 6,
           "Expected initializeContentOutboundSession(...) to have 6 parameters");
 
       return bridging::callFromJs<jsi::Value>(
           rt, &T::initializeContentOutboundSession, jsInvoker_, instance_, std::move(identityKeys), std::move(prekey), std::move(prekeySignature), std::move(oneTimeKey), std::move(deviceID));
     }
     jsi::Value initializeContentInboundSession(jsi::Runtime &rt, jsi::String identityKeys, jsi::Object encryptedContent, jsi::String deviceID, double sessionVersion, bool overwrite) override {
       static_assert(
           bridging::getParameterCount(&T::initializeContentInboundSession) == 6,
           "Expected initializeContentInboundSession(...) to have 6 parameters");
 
       return bridging::callFromJs<jsi::Value>(
           rt, &T::initializeContentInboundSession, jsInvoker_, instance_, std::move(identityKeys), std::move(encryptedContent), std::move(deviceID), std::move(sessionVersion), std::move(overwrite));
     }
     jsi::Value encrypt(jsi::Runtime &rt, jsi::String message, jsi::String deviceID) override {
       static_assert(
           bridging::getParameterCount(&T::encrypt) == 3,
           "Expected encrypt(...) to have 3 parameters");
 
       return bridging::callFromJs<jsi::Value>(
           rt, &T::encrypt, jsInvoker_, instance_, std::move(message), std::move(deviceID));
     }
     jsi::Value decrypt(jsi::Runtime &rt, jsi::Object encryptedData, jsi::String deviceID) override {
       static_assert(
           bridging::getParameterCount(&T::decrypt) == 3,
           "Expected decrypt(...) to have 3 parameters");
 
       return bridging::callFromJs<jsi::Value>(
           rt, &T::decrypt, jsInvoker_, instance_, std::move(encryptedData), std::move(deviceID));
     }
     jsi::Value decryptSequentialAndPersist(jsi::Runtime &rt, jsi::Object encryptedData, jsi::String deviceID, jsi::String messageID) override {
       static_assert(
           bridging::getParameterCount(&T::decryptSequentialAndPersist) == 4,
           "Expected decryptSequentialAndPersist(...) to have 4 parameters");
 
       return bridging::callFromJs<jsi::Value>(
           rt, &T::decryptSequentialAndPersist, jsInvoker_, instance_, std::move(encryptedData), std::move(deviceID), std::move(messageID));
     }
     jsi::Value signMessage(jsi::Runtime &rt, jsi::String message) override {
       static_assert(
           bridging::getParameterCount(&T::signMessage) == 2,
           "Expected signMessage(...) to have 2 parameters");
 
       return bridging::callFromJs<jsi::Value>(
           rt, &T::signMessage, jsInvoker_, instance_, std::move(message));
     }
     double getCodeVersion(jsi::Runtime &rt) override {
       static_assert(
           bridging::getParameterCount(&T::getCodeVersion) == 1,
           "Expected getCodeVersion(...) to have 1 parameters");
 
       return bridging::callFromJs<double>(
           rt, &T::getCodeVersion, jsInvoker_, instance_);
     }
     void terminate(jsi::Runtime &rt) override {
       static_assert(
           bridging::getParameterCount(&T::terminate) == 1,
           "Expected terminate(...) to have 1 parameters");
 
       return bridging::callFromJs<void>(
           rt, &T::terminate, jsInvoker_, instance_);
     }
     jsi::Value setNotifyToken(jsi::Runtime &rt, jsi::String token) override {
       static_assert(
           bridging::getParameterCount(&T::setNotifyToken) == 2,
           "Expected setNotifyToken(...) to have 2 parameters");
 
       return bridging::callFromJs<jsi::Value>(
           rt, &T::setNotifyToken, jsInvoker_, instance_, std::move(token));
     }
     jsi::Value clearNotifyToken(jsi::Runtime &rt) override {
       static_assert(
           bridging::getParameterCount(&T::clearNotifyToken) == 1,
           "Expected clearNotifyToken(...) to have 1 parameters");
 
       return bridging::callFromJs<jsi::Value>(
           rt, &T::clearNotifyToken, jsInvoker_, instance_);
     }
     jsi::Value stampSQLiteDBUserID(jsi::Runtime &rt, jsi::String userID) override {
       static_assert(
           bridging::getParameterCount(&T::stampSQLiteDBUserID) == 2,
           "Expected stampSQLiteDBUserID(...) to have 2 parameters");
 
       return bridging::callFromJs<jsi::Value>(
           rt, &T::stampSQLiteDBUserID, jsInvoker_, instance_, std::move(userID));
     }
     jsi::Value getSQLiteStampedUserID(jsi::Runtime &rt) override {
       static_assert(
           bridging::getParameterCount(&T::getSQLiteStampedUserID) == 1,
           "Expected getSQLiteStampedUserID(...) to have 1 parameters");
 
       return bridging::callFromJs<jsi::Value>(
           rt, &T::getSQLiteStampedUserID, jsInvoker_, instance_);
     }
     jsi::Value clearSensitiveData(jsi::Runtime &rt) override {
       static_assert(
           bridging::getParameterCount(&T::clearSensitiveData) == 1,
           "Expected clearSensitiveData(...) to have 1 parameters");
 
       return bridging::callFromJs<jsi::Value>(
           rt, &T::clearSensitiveData, jsInvoker_, instance_);
     }
     bool checkIfDatabaseNeedsDeletion(jsi::Runtime &rt) override {
       static_assert(
           bridging::getParameterCount(&T::checkIfDatabaseNeedsDeletion) == 1,
           "Expected checkIfDatabaseNeedsDeletion(...) to have 1 parameters");
 
       return bridging::callFromJs<bool>(
           rt, &T::checkIfDatabaseNeedsDeletion, jsInvoker_, instance_);
     }
     void reportDBOperationsFailure(jsi::Runtime &rt) override {
       static_assert(
           bridging::getParameterCount(&T::reportDBOperationsFailure) == 1,
           "Expected reportDBOperationsFailure(...) to have 1 parameters");
 
       return bridging::callFromJs<void>(
           rt, &T::reportDBOperationsFailure, jsInvoker_, instance_);
     }
     jsi::Value computeBackupKey(jsi::Runtime &rt, jsi::String password, jsi::String backupID) override {
       static_assert(
           bridging::getParameterCount(&T::computeBackupKey) == 3,
           "Expected computeBackupKey(...) to have 3 parameters");
 
       return bridging::callFromJs<jsi::Value>(
           rt, &T::computeBackupKey, jsInvoker_, instance_, std::move(password), std::move(backupID));
     }
     jsi::Value generateRandomString(jsi::Runtime &rt, double size) override {
       static_assert(
           bridging::getParameterCount(&T::generateRandomString) == 2,
           "Expected generateRandomString(...) to have 2 parameters");
 
       return bridging::callFromJs<jsi::Value>(
           rt, &T::generateRandomString, jsInvoker_, instance_, std::move(size));
     }
     jsi::Value setCommServicesAuthMetadata(jsi::Runtime &rt, jsi::String userID, jsi::String deviceID, jsi::String accessToken) override {
       static_assert(
           bridging::getParameterCount(&T::setCommServicesAuthMetadata) == 4,
           "Expected setCommServicesAuthMetadata(...) to have 4 parameters");
 
       return bridging::callFromJs<jsi::Value>(
           rt, &T::setCommServicesAuthMetadata, jsInvoker_, instance_, std::move(userID), std::move(deviceID), std::move(accessToken));
     }
     jsi::Value getCommServicesAuthMetadata(jsi::Runtime &rt) override {
       static_assert(
           bridging::getParameterCount(&T::getCommServicesAuthMetadata) == 1,
           "Expected getCommServicesAuthMetadata(...) to have 1 parameters");
 
       return bridging::callFromJs<jsi::Value>(
           rt, &T::getCommServicesAuthMetadata, jsInvoker_, instance_);
     }
     jsi::Value clearCommServicesAuthMetadata(jsi::Runtime &rt) override {
       static_assert(
           bridging::getParameterCount(&T::clearCommServicesAuthMetadata) == 1,
           "Expected clearCommServicesAuthMetadata(...) to have 1 parameters");
 
       return bridging::callFromJs<jsi::Value>(
           rt, &T::clearCommServicesAuthMetadata, jsInvoker_, instance_);
     }
     jsi::Value setCommServicesAccessToken(jsi::Runtime &rt, jsi::String accessToken) override {
       static_assert(
           bridging::getParameterCount(&T::setCommServicesAccessToken) == 2,
           "Expected setCommServicesAccessToken(...) to have 2 parameters");
 
       return bridging::callFromJs<jsi::Value>(
           rt, &T::setCommServicesAccessToken, jsInvoker_, instance_, std::move(accessToken));
     }
     jsi::Value clearCommServicesAccessToken(jsi::Runtime &rt) override {
       static_assert(
           bridging::getParameterCount(&T::clearCommServicesAccessToken) == 1,
           "Expected clearCommServicesAccessToken(...) to have 1 parameters");
 
       return bridging::callFromJs<jsi::Value>(
           rt, &T::clearCommServicesAccessToken, jsInvoker_, instance_);
     }
     void startBackupHandler(jsi::Runtime &rt) override {
       static_assert(
           bridging::getParameterCount(&T::startBackupHandler) == 1,
           "Expected startBackupHandler(...) to have 1 parameters");
 
       return bridging::callFromJs<void>(
           rt, &T::startBackupHandler, jsInvoker_, instance_);
     }
     void stopBackupHandler(jsi::Runtime &rt) override {
       static_assert(
           bridging::getParameterCount(&T::stopBackupHandler) == 1,
           "Expected stopBackupHandler(...) to have 1 parameters");
 
       return bridging::callFromJs<void>(
           rt, &T::stopBackupHandler, jsInvoker_, instance_);
     }
     jsi::Value createNewBackup(jsi::Runtime &rt, jsi::String backupSecret) override {
       static_assert(
           bridging::getParameterCount(&T::createNewBackup) == 2,
           "Expected createNewBackup(...) to have 2 parameters");
 
       return bridging::callFromJs<jsi::Value>(
           rt, &T::createNewBackup, jsInvoker_, instance_, std::move(backupSecret));
     }
+    jsi::Value createNewSIWEBackup(jsi::Runtime &rt, jsi::String backupSecret, jsi::String siweBackupMsg) override {
+      static_assert(
+          bridging::getParameterCount(&T::createNewSIWEBackup) == 3,
+          "Expected createNewSIWEBackup(...) to have 3 parameters");
+
+      return bridging::callFromJs<jsi::Value>(
+          rt, &T::createNewSIWEBackup, jsInvoker_, instance_, std::move(backupSecret), std::move(siweBackupMsg));
+    }
     jsi::Value restoreBackup(jsi::Runtime &rt, jsi::String backupSecret) override {
       static_assert(
           bridging::getParameterCount(&T::restoreBackup) == 2,
           "Expected restoreBackup(...) to have 2 parameters");
 
       return bridging::callFromJs<jsi::Value>(
           rt, &T::restoreBackup, jsInvoker_, instance_, std::move(backupSecret));
     }
     jsi::Value restoreBackupData(jsi::Runtime &rt, jsi::String backupID, jsi::String backupDataKey, jsi::String backupLogDataKey) override {
       static_assert(
           bridging::getParameterCount(&T::restoreBackupData) == 4,
           "Expected restoreBackupData(...) to have 4 parameters");
 
       return bridging::callFromJs<jsi::Value>(
           rt, &T::restoreBackupData, jsInvoker_, instance_, std::move(backupID), std::move(backupDataKey), std::move(backupLogDataKey));
     }
     jsi::Value retrieveBackupKeys(jsi::Runtime &rt, jsi::String backupSecret) override {
       static_assert(
           bridging::getParameterCount(&T::retrieveBackupKeys) == 2,
           "Expected retrieveBackupKeys(...) to have 2 parameters");
 
       return bridging::callFromJs<jsi::Value>(
           rt, &T::retrieveBackupKeys, jsInvoker_, instance_, std::move(backupSecret));
     }
     jsi::Value setSIWEBackupSecrets(jsi::Runtime &rt, jsi::Object siweBackupSecrets) override {
       static_assert(
           bridging::getParameterCount(&T::setSIWEBackupSecrets) == 2,
           "Expected setSIWEBackupSecrets(...) to have 2 parameters");
 
       return bridging::callFromJs<jsi::Value>(
           rt, &T::setSIWEBackupSecrets, jsInvoker_, instance_, std::move(siweBackupSecrets));
     }
     jsi::Value getSIWEBackupSecrets(jsi::Runtime &rt) override {
       static_assert(
           bridging::getParameterCount(&T::getSIWEBackupSecrets) == 1,
           "Expected getSIWEBackupSecrets(...) to have 1 parameters");
 
       return bridging::callFromJs<jsi::Value>(
           rt, &T::getSIWEBackupSecrets, jsInvoker_, instance_);
     }
     jsi::Value getAllReceivedMessageToDevice(jsi::Runtime &rt) override {
       static_assert(
           bridging::getParameterCount(&T::getAllReceivedMessageToDevice) == 1,
           "Expected getAllReceivedMessageToDevice(...) to have 1 parameters");
 
       return bridging::callFromJs<jsi::Value>(
           rt, &T::getAllReceivedMessageToDevice, jsInvoker_, instance_);
     }
     jsi::Value removeReceivedMessagesToDevice(jsi::Runtime &rt, jsi::Array ids) override {
       static_assert(
           bridging::getParameterCount(&T::removeReceivedMessagesToDevice) == 2,
           "Expected removeReceivedMessagesToDevice(...) to have 2 parameters");
 
       return bridging::callFromJs<jsi::Value>(
           rt, &T::removeReceivedMessagesToDevice, jsInvoker_, instance_, std::move(ids));
     }
 
   private:
     T *instance_;
   };
 
   Delegate delegate_;
 };
 
 } // namespace react
 } // namespace facebook
diff --git a/native/native_rust_library/src/backup.rs b/native/native_rust_library/src/backup.rs
index 9b6df4591..ad2767385 100644
--- a/native/native_rust_library/src/backup.rs
+++ b/native/native_rust_library/src/backup.rs
@@ -1,365 +1,385 @@
 mod compaction_upload_promises;
 mod file_info;
 mod upload_handler;
 
 use crate::argon2_tools::{compute_backup_key, compute_backup_key_str};
 use crate::constants::{aes, secure_store};
 use crate::ffi::{
   create_main_compaction, get_backup_directory_path,
-  get_backup_user_keys_file_path, restore_from_backup_log,
-  restore_from_main_compaction, secure_store_get, string_callback,
-  void_callback,
+  get_backup_user_keys_file_path, get_siwe_backup_message_path,
+  restore_from_backup_log, restore_from_main_compaction, secure_store_get,
+  string_callback, void_callback,
 };
 use crate::utils::future_manager;
 use crate::utils::jsi_callbacks::handle_string_result_as_callback;
 use crate::BACKUP_SOCKET_ADDR;
 use crate::RUNTIME;
 use backup_client::{
   BackupClient, BackupDescriptor, LatestBackupIDResponse, RequestedData,
   TryStreamExt, UserIdentity,
 };
 use serde::{Deserialize, Serialize};
 use std::error::Error;
 use std::path::PathBuf;
 
 pub mod ffi {
   use super::*;
 
   pub use upload_handler::ffi::*;
 
   pub fn create_backup(
     backup_id: String,
     backup_secret: String,
     pickle_key: String,
     pickled_account: String,
+    siwe_backup_msg: String,
     promise_id: u32,
   ) {
     compaction_upload_promises::insert(backup_id.clone(), promise_id);
 
     RUNTIME.spawn(async move {
       let result = create_userkeys_compaction(
         backup_id.clone(),
         backup_secret,
         pickle_key,
         pickled_account,
       )
       .await
       .map_err(|err| err.to_string());
 
       if let Err(err) = result {
         compaction_upload_promises::resolve(&backup_id, Err(err));
         return;
       }
 
+      if !siwe_backup_msg.is_empty() {
+        if let Err(err) =
+          create_siwe_backup_msg_compaction(&backup_id, siwe_backup_msg).await
+        {
+          compaction_upload_promises::resolve(&backup_id, Err(err.to_string()));
+          return;
+        }
+      }
+
       let (future_id, future) = future_manager::new_future::<()>().await;
       create_main_compaction(&backup_id, future_id);
       if let Err(err) = future.await {
         compaction_upload_promises::resolve(&backup_id, Err(err));
         tokio::spawn(upload_handler::compaction::cleanup_files(backup_id));
         return;
       }
 
       trigger_backup_file_upload();
 
       // The promise will be resolved when the backup is uploaded
     });
   }
 
   pub fn restore_backup(backup_secret: String, promise_id: u32) {
     RUNTIME.spawn(async move {
       let result = download_backup(backup_secret)
         .await
         .map_err(|err| err.to_string());
 
       let result = match result {
         Ok(result) => result,
         Err(error) => {
           void_callback(error, promise_id);
           return;
         }
       };
 
       let (future_id, future) = future_manager::new_future::<()>().await;
       restore_from_main_compaction(
         &result.backup_restoration_path.to_string_lossy(),
         &result.backup_data_key,
         future_id,
       );
 
       if let Err(error) = future.await {
         void_callback(error, promise_id);
         return;
       }
 
       if let Err(error) =
         download_and_apply_logs(&result.backup_id, result.backup_log_data_key)
           .await
       {
         void_callback(error.to_string(), promise_id);
         return;
       }
 
       void_callback(String::new(), promise_id);
     });
   }
 
   pub fn retrieve_backup_keys(backup_secret: String, promise_id: u32) {
     RUNTIME.spawn(async move {
       let result = download_backup_keys(backup_secret)
         .await
         .map_err(|err| err.to_string());
 
       let result = match result {
         Ok(result) => result,
         Err(error) => {
           string_callback(error, promise_id, "".to_string());
           return;
         }
       };
 
       let serialize_result = serde_json::to_string(&result);
       handle_string_result_as_callback(serialize_result, promise_id);
     });
   }
 
   pub fn restore_backup_data(
     backup_id: String,
     backup_data_key: String,
     backup_log_data_key: String,
     promise_id: u32,
   ) {
     RUNTIME.spawn(async move {
       let backup_keys = BackupKeysResult {
         backup_id,
         backup_data_key,
         backup_log_data_key,
       };
       let result = download_backup_data(backup_keys)
         .await
         .map_err(|err| err.to_string());
 
       let result = match result {
         Ok(result) => result,
         Err(error) => {
           void_callback(error, promise_id);
           return;
         }
       };
 
       let (future_id, future) = future_manager::new_future::<()>().await;
       restore_from_main_compaction(
         &result.backup_restoration_path.to_string_lossy(),
         &result.backup_data_key,
         future_id,
       );
 
       if let Err(error) = future.await {
         void_callback(error, promise_id);
         return;
       }
 
       if let Err(error) =
         download_and_apply_logs(&result.backup_id, result.backup_log_data_key)
           .await
       {
         void_callback(error.to_string(), promise_id);
         return;
       }
 
       void_callback(String::new(), promise_id);
     });
   }
 }
 
 pub async fn create_userkeys_compaction(
   backup_id: String,
   backup_secret: String,
   pickle_key: String,
   pickled_account: String,
 ) -> Result<(), Box<dyn Error>> {
   let mut backup_key =
     compute_backup_key(backup_secret.as_bytes(), backup_id.as_bytes())?;
 
   let backup_data_key =
     secure_store_get(secure_store::SECURE_STORE_ENCRYPTION_KEY_ID)?;
 
   let backup_log_data_key =
     secure_store_get(secure_store::SECURE_STORE_BACKUP_LOGS_ENCRYPTION_KEY_ID)?;
 
   let user_keys = UserKeys {
     backup_data_key,
     backup_log_data_key,
     pickle_key,
     pickled_account,
   };
   let encrypted_user_keys = user_keys.encrypt(&mut backup_key)?;
 
   let user_keys_file = get_backup_user_keys_file_path(&backup_id)?;
   tokio::fs::write(user_keys_file, encrypted_user_keys).await?;
 
   Ok(())
 }
 
+pub async fn create_siwe_backup_msg_compaction(
+  backup_id: &String,
+  siwe_backup_msg: String,
+) -> Result<(), Box<dyn Error>> {
+  let siwe_backup_msg_file = get_siwe_backup_message_path(&backup_id)?;
+  tokio::fs::write(siwe_backup_msg_file, siwe_backup_msg).await?;
+
+  Ok(())
+}
+
 async fn download_backup(
   backup_secret: String,
 ) -> Result<CompactionDownloadResult, Box<dyn Error>> {
   let backup_keys = download_backup_keys(backup_secret).await?;
   download_backup_data(backup_keys).await
 }
 
 async fn download_backup_keys(
   backup_secret: String,
 ) -> Result<BackupKeysResult, Box<dyn Error>> {
   let backup_client = BackupClient::new(BACKUP_SOCKET_ADDR)?;
   let user_identity = get_user_identity_from_secure_store()?;
 
   let latest_backup_descriptor = BackupDescriptor::Latest {
     username: user_identity.user_id.clone(),
   };
 
   let backup_id_response = backup_client
     .download_backup_data(&latest_backup_descriptor, RequestedData::BackupID)
     .await?;
 
   let LatestBackupIDResponse { backup_id } =
     serde_json::from_slice(&backup_id_response)?;
 
   let mut backup_key = compute_backup_key_str(&backup_secret, &backup_id)?;
 
   let mut encrypted_user_keys = backup_client
     .download_backup_data(&latest_backup_descriptor, RequestedData::UserKeys)
     .await?;
 
   let user_keys =
     UserKeys::from_encrypted(&mut encrypted_user_keys, &mut backup_key)?;
 
   Ok(BackupKeysResult {
     backup_id,
     backup_data_key: user_keys.backup_data_key,
     backup_log_data_key: user_keys.backup_log_data_key,
   })
 }
 
 async fn download_backup_data(
   backup_keys: BackupKeysResult,
 ) -> Result<CompactionDownloadResult, Box<dyn Error>> {
   let backup_client = BackupClient::new(BACKUP_SOCKET_ADDR)?;
   let user_identity = get_user_identity_from_secure_store()?;
 
   let BackupKeysResult {
     backup_id,
     backup_data_key,
     backup_log_data_key,
   } = backup_keys;
 
   let backup_data_descriptor = BackupDescriptor::BackupID {
     backup_id: backup_id.clone(),
     user_identity: user_identity.clone(),
   };
 
   let encrypted_user_data = backup_client
     .download_backup_data(&backup_data_descriptor, RequestedData::UserData)
     .await?;
 
   let backup_restoration_path =
     PathBuf::from(get_backup_directory_path()?).join("restore_compaction");
 
   tokio::fs::write(&backup_restoration_path, encrypted_user_data).await?;
 
   Ok(CompactionDownloadResult {
     backup_restoration_path,
     backup_id,
     backup_data_key,
     backup_log_data_key,
   })
 }
 
 async fn download_and_apply_logs(
   backup_id: &str,
   backup_log_data_key: String,
 ) -> Result<(), Box<dyn Error>> {
   let mut backup_log_data_key = backup_log_data_key.into_bytes();
 
   let backup_client = BackupClient::new(BACKUP_SOCKET_ADDR)?;
   let user_identity = get_user_identity_from_secure_store()?;
 
   let stream = backup_client.download_logs(&user_identity, backup_id).await;
   let mut stream = Box::pin(stream);
 
   while let Some(mut log) = stream.try_next().await? {
     let data = decrypt(
       backup_log_data_key.as_mut_slice(),
       log.content.as_mut_slice(),
     )?;
 
     let (future_id, future) = future_manager::new_future::<()>().await;
     restore_from_backup_log(data, future_id);
     future.await?;
   }
 
   Ok(())
 }
 
 fn get_user_identity_from_secure_store() -> Result<UserIdentity, cxx::Exception>
 {
   Ok(UserIdentity {
     user_id: secure_store_get(secure_store::USER_ID)?,
     access_token: secure_store_get(secure_store::COMM_SERVICES_ACCESS_TOKEN)?,
     device_id: secure_store_get(secure_store::DEVICE_ID)?,
   })
 }
 
 #[derive(Debug, Serialize)]
 struct BackupKeysResult {
   backup_id: String,
   backup_data_key: String,
   backup_log_data_key: String,
 }
 
 struct CompactionDownloadResult {
   backup_id: String,
   backup_restoration_path: PathBuf,
   backup_data_key: String,
   backup_log_data_key: String,
 }
 
 #[derive(Debug, Serialize, Deserialize)]
 struct UserKeys {
   backup_data_key: String,
   backup_log_data_key: String,
   pickle_key: String,
   pickled_account: String,
 }
 
 impl UserKeys {
   fn encrypt(&self, backup_key: &mut [u8]) -> Result<Vec<u8>, Box<dyn Error>> {
     let mut json = serde_json::to_vec(self)?;
     encrypt(backup_key, &mut json)
   }
 
   fn from_encrypted(
     data: &mut [u8],
     backup_key: &mut [u8],
   ) -> Result<Self, Box<dyn Error>> {
     let decrypted = decrypt(backup_key, data)?;
     Ok(serde_json::from_slice(&decrypted)?)
   }
 }
 
 fn encrypt(key: &mut [u8], data: &mut [u8]) -> Result<Vec<u8>, Box<dyn Error>> {
   let encrypted_len = data.len() + aes::IV_LENGTH + aes::TAG_LENGTH;
   let mut encrypted = vec![0; encrypted_len];
 
   crate::ffi::encrypt(key, data, &mut encrypted)?;
 
   Ok(encrypted)
 }
 
 fn decrypt(key: &mut [u8], data: &mut [u8]) -> Result<Vec<u8>, Box<dyn Error>> {
   let decrypted_len = data.len() - aes::IV_LENGTH - aes::TAG_LENGTH;
   let mut decrypted = vec![0; decrypted_len];
 
   crate::ffi::decrypt(key, data, &mut decrypted)?;
 
   Ok(decrypted)
 }
diff --git a/native/native_rust_library/src/backup/upload_handler.rs b/native/native_rust_library/src/backup/upload_handler.rs
index 60bf1e24b..1bfa4ebfd 100644
--- a/native/native_rust_library/src/backup/upload_handler.rs
+++ b/native/native_rust_library/src/backup/upload_handler.rs
@@ -1,318 +1,337 @@
 use super::file_info::BackupFileInfo;
 use super::get_user_identity_from_secure_store;
 use crate::backup::compaction_upload_promises;
 use crate::constants::BACKUP_SERVICE_CONNECTION_RETRY_DELAY;
 use crate::ffi::{
   get_backup_directory_path, get_backup_file_path, get_backup_log_file_path,
-  get_backup_user_keys_file_path,
+  get_backup_user_keys_file_path, get_siwe_backup_message_path,
 };
 use crate::BACKUP_SOCKET_ADDR;
 use crate::RUNTIME;
 use backup_client::UserIdentity;
 use backup_client::{
   BackupClient, Error as BackupError, LogUploadConfirmation, Stream, StreamExt,
 };
 use backup_client::{BackupData, Sink, UploadLogRequest};
 use lazy_static::lazy_static;
 use std::collections::HashSet;
 use std::convert::Infallible;
 use std::error::Error;
 use std::future::Future;
 use std::io::BufRead;
 use std::io::ErrorKind;
 use std::path::PathBuf;
 use std::pin::Pin;
 use std::sync::{Arc, Mutex};
 use tokio::sync::Notify;
 use tokio::task::JoinHandle;
 
 lazy_static! {
   pub static ref UPLOAD_HANDLER: Arc<Mutex<Option<JoinHandle<Infallible>>>> =
     Arc::new(Mutex::new(None));
   static ref TRIGGER_BACKUP_FILE_UPLOAD: Arc<Notify> = Arc::new(Notify::new());
   static ref BACKUP_FOLDER_PATH: PathBuf = PathBuf::from(
     get_backup_directory_path().expect("Getting backup directory path failed")
   );
 }
 
 pub mod ffi {
   use super::*;
 
   pub fn start_backup_handler() -> Result<(), Box<dyn Error>> {
     let mut handle = UPLOAD_HANDLER.lock()?;
     match handle.take() {
       // Don't start backup handler if it's already running
       Some(handle) if !handle.is_finished() => (),
       _ => {
         *handle = Some(RUNTIME.spawn(super::start()?));
       }
     }
 
     Ok(())
   }
 
   pub fn stop_backup_handler() -> Result<(), Box<dyn Error>> {
     let Some(handler) = UPLOAD_HANDLER.lock()?.take() else {
       return Ok(());
     };
     if handler.is_finished() {
       return Ok(());
     }
 
     handler.abort();
     Ok(())
   }
 
   pub fn trigger_backup_file_upload() {
     TRIGGER_BACKUP_FILE_UPLOAD.notify_one();
   }
 }
 
 pub fn start() -> Result<impl Future<Output = Infallible>, Box<dyn Error>> {
   let backup_client = BackupClient::new(BACKUP_SOCKET_ADDR)?;
   let user_identity = get_user_identity_from_secure_store()?;
 
   Ok(async move {
     loop {
       let (tx, rx) = match backup_client.upload_logs(&user_identity).await {
         Ok(ws) => ws,
         Err(err) => {
           println!(
             "Backup handler error when estabilishing connection: '{err:?}'"
           );
           tokio::time::sleep(BACKUP_SERVICE_CONNECTION_RETRY_DELAY).await;
           continue;
         }
       };
 
       let mut tx = Box::pin(tx);
       let mut rx = Box::pin(rx);
 
       let logs_waiting_for_confirmation = Mutex::new(HashSet::<PathBuf>::new());
 
       loop {
         let err = tokio::select! {
           Err(err) = watch_and_upload_files(&backup_client, &user_identity, &mut tx, &logs_waiting_for_confirmation) => err,
           Err(err) = delete_confirmed_logs(&mut rx, &logs_waiting_for_confirmation) => err,
         };
 
         println!("Backup handler error: '{err:?}'");
         match err {
           BackupHandlerError::BackupError(_)
           | BackupHandlerError::WSClosed
           | BackupHandlerError::LockError => break,
           BackupHandlerError::IoError(_)
           | BackupHandlerError::CxxException(_) => continue,
+          BackupHandlerError::FromUtf8Error(_) => break,
         }
       }
 
       tokio::time::sleep(BACKUP_SERVICE_CONNECTION_RETRY_DELAY).await;
       println!("Retrying backup log upload");
     }
   })
 }
 
 async fn watch_and_upload_files(
   backup_client: &BackupClient,
   user_identity: &UserIdentity,
   tx: &mut Pin<Box<impl Sink<UploadLogRequest, Error = BackupError>>>,
   logs_waiting_for_confirmation: &Mutex<HashSet<PathBuf>>,
 ) -> Result<Infallible, BackupHandlerError> {
   loop {
     let mut file_stream = match tokio::fs::read_dir(&*BACKUP_FOLDER_PATH).await
     {
       Ok(file_stream) => file_stream,
       Err(err) if err.kind() == ErrorKind::NotFound => {
         TRIGGER_BACKUP_FILE_UPLOAD.notified().await;
         continue;
       }
       Err(err) => return Err(err.into()),
     };
 
     while let Some(file) = file_stream.next_entry().await? {
       let path = file.path();
 
       if logs_waiting_for_confirmation.lock()?.contains(&path) {
         continue;
       }
 
       let Ok(BackupFileInfo {
         backup_id,
         log_id,
         additional_data,
       }) = path.clone().try_into()
       else {
         continue;
       };
 
       // Skip additional data files (attachments, user keys). They will be
       // handled when we iterate over the corresponding files with the
       // main content
       if additional_data.is_some() {
         continue;
       }
 
       if let Some(log_id) = log_id {
         log::upload_files(tx, backup_id, log_id).await?;
         logs_waiting_for_confirmation.lock()?.insert(path);
       } else {
         compaction::upload_files(backup_client, user_identity, backup_id)
           .await?;
       }
     }
 
     TRIGGER_BACKUP_FILE_UPLOAD.notified().await;
   }
 }
 
 async fn delete_confirmed_logs(
   rx: &mut Pin<
     Box<impl Stream<Item = Result<LogUploadConfirmation, BackupError>>>,
   >,
   logs_waiting_for_confirmation: &Mutex<HashSet<PathBuf>>,
 ) -> Result<Infallible, BackupHandlerError> {
   while let Some(LogUploadConfirmation { backup_id, log_id }) =
     rx.next().await.transpose()?
   {
     let path =
       get_backup_log_file_path(&backup_id, &log_id.to_string(), false)?;
     logs_waiting_for_confirmation
       .lock()?
       .remove(&PathBuf::from(path));
 
     tokio::spawn(log::cleanup_files(backup_id, log_id));
   }
 
   Err(BackupHandlerError::WSClosed)
 }
 
 pub mod compaction {
   use super::*;
 
   pub async fn upload_files(
     backup_client: &BackupClient,
     user_identity: &UserIdentity,
     backup_id: String,
   ) -> Result<(), BackupHandlerError> {
     let user_data_path = get_backup_file_path(&backup_id, false)?;
     let user_data = tokio::fs::read(&user_data_path).await?;
 
     let user_keys_path = get_backup_user_keys_file_path(&backup_id)?;
     let user_keys = tokio::fs::read(&user_keys_path).await?;
 
     let attachments_path = get_backup_file_path(&backup_id, true)?;
     let attachments = match tokio::fs::read(&attachments_path).await {
       Ok(data) => data.lines().collect::<Result<_, _>>()?,
       Err(err) if err.kind() == ErrorKind::NotFound => Vec::new(),
       Err(err) => return Err(err.into()),
     };
 
+    let siwe_backup_msg_path = get_siwe_backup_message_path(&backup_id)?;
+    let siwe_backup_msg = match tokio::fs::read(&siwe_backup_msg_path).await {
+      Ok(data) => match String::from_utf8(data) {
+        Ok(valid_string) => Some(valid_string),
+        Err(err) => return Err(err.into()),
+      },
+      Err(err) if err.kind() == ErrorKind::NotFound => None,
+      Err(err) => return Err(err.into()),
+    };
+
     let backup_data = BackupData {
       backup_id: backup_id.clone(),
       user_data,
       user_keys,
       attachments,
+      siwe_backup_msg,
     };
 
     backup_client
       .upload_backup(user_identity, backup_data)
       .await?;
 
     compaction_upload_promises::resolve(&backup_id, Ok(()));
     tokio::spawn(cleanup_files(backup_id));
 
     Ok(())
   }
 
   pub async fn cleanup_files(backup_id: String) {
     let backup_files_cleanup = async {
       let user_data_path = get_backup_file_path(&backup_id, false)?;
       tokio::fs::remove_file(&user_data_path).await?;
       let user_keys_path = get_backup_user_keys_file_path(&backup_id)?;
       tokio::fs::remove_file(&user_keys_path).await?;
       let attachments_path = get_backup_file_path(&backup_id, true)?;
       match tokio::fs::remove_file(&attachments_path).await {
         Ok(()) => Result::<_, Box<dyn Error>>::Ok(()),
         Err(err) if err.kind() == ErrorKind::NotFound => Ok(()),
         Err(err) => Err(err.into()),
+      }?;
+      let siwe_backup_msg_path = get_siwe_backup_message_path(&backup_id)?;
+      match tokio::fs::remove_file(&siwe_backup_msg_path).await {
+        Ok(()) => Result::<_, Box<dyn Error>>::Ok(()),
+        Err(err) if err.kind() == ErrorKind::NotFound => Ok(()),
+        Err(err) => Err(err.into()),
       }
     };
 
     if let Err(err) = backup_files_cleanup.await {
       println!("Error when cleaning up the backup files: {err:?}");
     }
   }
 }
 
 mod log {
   use backup_client::SinkExt;
 
   use super::*;
 
   pub async fn upload_files(
     tx: &mut Pin<Box<impl Sink<UploadLogRequest, Error = BackupError>>>,
     backup_id: String,
     log_id: usize,
   ) -> Result<(), BackupHandlerError> {
     let log_id_string = log_id.to_string();
     let content_path =
       get_backup_log_file_path(&backup_id, &log_id_string, false)?;
     let content = tokio::fs::read(&content_path).await?;
 
     let attachments_path =
       get_backup_log_file_path(&backup_id, &log_id_string, true)?;
     let attachments = match tokio::fs::read(&attachments_path).await {
       Ok(data) => Some(data.lines().collect::<Result<_, _>>()?),
       Err(err) if err.kind() == ErrorKind::NotFound => None,
       Err(err) => return Err(err.into()),
     };
 
     let log_data = UploadLogRequest {
       backup_id,
       log_id,
       content,
       attachments,
     };
     tx.send(log_data.clone()).await?;
 
     Ok(())
   }
 
   pub async fn cleanup_files(backup_id: String, log_id: usize) {
     let backup_files_cleanup = async {
       let log_id = log_id.to_string();
 
       let path = get_backup_log_file_path(&backup_id, &log_id, false)?;
       tokio::fs::remove_file(&path).await?;
 
       let attachments_path =
         get_backup_log_file_path(&backup_id, &log_id, true)?;
       match tokio::fs::remove_file(&attachments_path).await {
         Ok(()) => Result::<_, Box<dyn Error>>::Ok(()),
         Err(err) if err.kind() == ErrorKind::NotFound => Ok(()),
         Err(err) => Err(err.into()),
       }
     };
 
     if let Err(err) = backup_files_cleanup.await {
       println!("{err:?}");
     }
   }
 }
 
 #[derive(
   Debug, derive_more::Display, derive_more::From, derive_more::Error,
 )]
 pub enum BackupHandlerError {
   BackupError(BackupError),
   WSClosed,
   IoError(std::io::Error),
   CxxException(cxx::Exception),
   LockError,
+  FromUtf8Error(std::string::FromUtf8Error),
 }
 
 impl<T> From<std::sync::PoisonError<T>> for BackupHandlerError {
   fn from(_: std::sync::PoisonError<T>) -> Self {
     Self::LockError
   }
 }
diff --git a/native/native_rust_library/src/lib.rs b/native/native_rust_library/src/lib.rs
index 45f1e1395..743cebef0 100644
--- a/native/native_rust_library/src/lib.rs
+++ b/native/native_rust_library/src/lib.rs
@@ -1,469 +1,470 @@
 use comm_opaque2::grpc::opaque_error_to_grpc_status as handle_error;
 use grpc_clients::identity::protos::unauth::DeviceType;
 use lazy_static::lazy_static;
 use std::sync::Arc;
 use tokio::runtime::{Builder, Runtime};
 use tonic::Status;
 
 mod argon2_tools;
 mod backup;
 mod constants;
 mod identity;
 mod utils;
 
 use crate::argon2_tools::compute_backup_key_str;
 use crate::utils::jsi_callbacks::{
   handle_string_result_as_callback, handle_void_result_as_callback,
 };
 
 mod generated {
   // We get the CODE_VERSION from this generated file
   include!(concat!(env!("OUT_DIR"), "/version.rs"));
   // We get the IDENTITY_SOCKET_ADDR from this generated file
   include!(concat!(env!("OUT_DIR"), "/socket_config.rs"));
 }
 
 pub use generated::CODE_VERSION;
 pub use generated::{BACKUP_SOCKET_ADDR, IDENTITY_SOCKET_ADDR};
 
 #[cfg(not(target_os = "android"))]
 pub const DEVICE_TYPE: DeviceType = DeviceType::Ios;
 #[cfg(target_os = "android")]
 pub const DEVICE_TYPE: DeviceType = DeviceType::Android;
 
 lazy_static! {
   static ref RUNTIME: Arc<Runtime> =
     Arc::new(Builder::new_multi_thread().enable_all().build().unwrap());
 }
 
 // ffi uses
 use backup::ffi::*;
 use identity::ffi::*;
 use utils::future_manager::ffi::*;
 
 #[cxx::bridge]
 mod ffi {
 
   // Identity Service APIs
   extern "Rust" {
     #[cxx_name = "identityRegisterPasswordUser"]
     fn register_password_user(
       username: String,
       password: String,
       key_payload: String,
       key_payload_signature: String,
       content_prekey: String,
       content_prekey_signature: String,
       notif_prekey: String,
       notif_prekey_signature: String,
       content_one_time_keys: Vec<String>,
       notif_one_time_keys: Vec<String>,
       farcaster_id: String,
       promise_id: u32,
     );
 
     #[cxx_name = "identityLogInPasswordUser"]
     fn log_in_password_user(
       username: String,
       password: String,
       key_payload: String,
       key_payload_signature: String,
       content_prekey: String,
       content_prekey_signature: String,
       notif_prekey: String,
       notif_prekey_signature: String,
       promise_id: u32,
     );
 
     #[cxx_name = "identityRegisterWalletUser"]
     fn register_wallet_user(
       siwe_message: String,
       siwe_signature: String,
       key_payload: String,
       key_payload_signature: String,
       content_prekey: String,
       content_prekey_signature: String,
       notif_prekey: String,
       notif_prekey_signature: String,
       content_one_time_keys: Vec<String>,
       notif_one_time_keys: Vec<String>,
       farcaster_id: String,
       promise_id: u32,
     );
 
     #[cxx_name = "identityLogInWalletUser"]
     fn log_in_wallet_user(
       siwe_message: String,
       siwe_signature: String,
       key_payload: String,
       key_payload_signature: String,
       content_prekey: String,
       content_prekey_signature: String,
       notif_prekey: String,
       notif_prekey_signature: String,
       promise_id: u32,
     );
 
     #[cxx_name = "identityUpdateUserPassword"]
     fn update_user_password(
       user_id: String,
       device_id: String,
       access_token: String,
       password: String,
       promise_id: u32,
     );
 
     #[cxx_name = "identityDeleteWalletUser"]
     fn delete_wallet_user(
       user_id: String,
       device_id: String,
       access_token: String,
       promise_id: u32,
     );
 
     #[cxx_name = "identityDeletePasswordUser"]
     fn delete_password_user(
       user_id: String,
       device_id: String,
       access_token: String,
       password: String,
       promise_id: u32,
     );
 
     #[cxx_name = "identityLogOut"]
     fn log_out(
       user_id: String,
       device_id: String,
       access_token: String,
       promise_id: u32,
     );
 
     #[cxx_name = "identityGetOutboundKeysForUser"]
     fn get_outbound_keys_for_user(
       auth_user_id: String,
       auth_device_id: String,
       auth_access_token: String,
       user_id: String,
       promise_id: u32,
     );
 
     #[cxx_name = "identityGetInboundKeysForUser"]
     fn get_inbound_keys_for_user(
       auth_user_id: String,
       auth_device_id: String,
       auth_access_token: String,
       user_id: String,
       promise_id: u32,
     );
 
     #[cxx_name = "identityRefreshUserPrekeys"]
     fn refresh_user_prekeys(
       auth_user_id: String,
       auth_device_id: String,
       auth_access_token: String,
       content_prekey: String,
       content_prekey_signature: String,
       notif_prekey: String,
       notif_prekey_signature: String,
       promise_id: u32,
     );
 
     #[cxx_name = "identityGenerateNonce"]
     fn generate_nonce(promise_id: u32);
 
     #[cxx_name = "identityVersionSupported"]
     fn version_supported(promise_id: u32);
 
     #[cxx_name = "identityUploadOneTimeKeys"]
     fn upload_one_time_keys(
       auth_user_id: String,
       auth_device_id: String,
       auth_access_token: String,
       content_one_time_keys: Vec<String>,
       notif_one_time_keys: Vec<String>,
       promise_id: u32,
     );
 
     #[cxx_name = "identityGetKeyserverKeys"]
     fn get_keyserver_keys(
       user_id: String,
       device_id: String,
       access_token: String,
       keyserver_id: String,
       promise_id: u32,
     );
 
     #[cxx_name = "identityGetDeviceListForUser"]
     fn get_device_list_for_user(
       auth_user_id: String,
       auth_device_id: String,
       auth_access_token: String,
       user_id: String,
       since_timestamp: i64,
       promise_id: u32,
     );
 
     #[cxx_name = "identityGetDeviceListsForUsers"]
     fn get_device_lists_for_users(
       auth_user_id: String,
       auth_device_id: String,
       auth_access_token: String,
       user_ids: Vec<String>,
       promise_id: u32,
     );
 
     #[cxx_name = "identityUpdateDeviceList"]
     fn update_device_list(
       auth_user_id: String,
       auth_device_id: String,
       auth_access_token: String,
       update_payload: String,
       promise_id: u32,
     );
 
     #[cxx_name = "identityUploadSecondaryDeviceKeysAndLogIn"]
     fn upload_secondary_device_keys_and_log_in(
       user_id: String,
       nonce: String,
       nonce_signature: String,
       key_payload: String,
       key_payload_signature: String,
       content_prekey: String,
       content_prekey_signature: String,
       notif_prekey: String,
       notif_prekey_signature: String,
       content_one_time_keys: Vec<String>,
       notif_one_time_keys: Vec<String>,
       promise_id: u32,
     );
 
     #[cxx_name = "identityLogInExistingDevice"]
     fn log_in_existing_device(
       user_id: String,
       device_id: String,
       nonce: String,
       nonce_signature: String,
       promise_id: u32,
     );
 
     #[cxx_name = "identityFindUserIDForWalletAddress"]
     fn find_user_id_for_wallet_address(wallet_address: String, promise_id: u32);
 
     #[cxx_name = "identityFindUserIDForUsername"]
     fn find_user_id_for_username(username: String, promise_id: u32);
 
     // Farcaster
     #[cxx_name = "identityGetFarcasterUsers"]
     fn get_farcaster_users(farcaster_ids: Vec<String>, promise_id: u32);
 
     #[cxx_name = "identityLinkFarcasterAccount"]
     fn link_farcaster_account(
       user_id: String,
       device_id: String,
       access_token: String,
       farcaster_id: String,
       promise_id: u32,
     );
 
     #[cxx_name = "identityUnlinkFarcasterAccount"]
     fn unlink_farcaster_account(
       user_id: String,
       device_id: String,
       access_token: String,
       promise_id: u32,
     );
 
     // Argon2
     #[cxx_name = "compute_backup_key"]
     fn compute_backup_key_str(
       password: &str,
       backup_id: &str,
     ) -> Result<[u8; 32]>;
   }
 
   unsafe extern "C++" {
     include!("RustCallback.h");
     #[namespace = "comm"]
     #[cxx_name = "stringCallback"]
     fn string_callback(error: String, promise_id: u32, ret: String);
 
     #[namespace = "comm"]
     #[cxx_name = "voidCallback"]
     fn void_callback(error: String, promise_id: u32);
 
     #[namespace = "comm"]
     #[cxx_name = "boolCallback"]
     fn bool_callback(error: String, promise_id: u32, ret: bool);
   }
 
   // AES cryptography
   #[namespace = "comm"]
   unsafe extern "C++" {
     include!("RustAESCrypto.h");
 
     #[allow(unused)]
     #[cxx_name = "aesGenerateKey"]
     fn generate_key(buffer: &mut [u8]) -> Result<()>;
 
     /// The first two argument aren't mutated but creation of Java ByteBuffer
     /// requires the underlying bytes to be mutable.
     #[allow(unused)]
     #[cxx_name = "aesEncrypt"]
     fn encrypt(
       key: &mut [u8],
       plaintext: &mut [u8],
       sealed_data: &mut [u8],
     ) -> Result<()>;
 
     /// The first two argument aren't mutated but creation of Java ByteBuffer
     /// requires the underlying bytes to be mutable.
     #[allow(unused)]
     #[cxx_name = "aesDecrypt"]
     fn decrypt(
       key: &mut [u8],
       sealed_data: &mut [u8],
       plaintext: &mut [u8],
     ) -> Result<()>;
   }
 
   // Comm Services Auth Metadata Emission
   #[namespace = "comm"]
   unsafe extern "C++" {
     include!("RustCSAMetadataEmitter.h");
 
     #[allow(unused)]
     #[cxx_name = "sendAuthMetadataToJS"]
     fn send_auth_metadata_to_js(
       access_token: String,
       user_id: String,
     ) -> Result<()>;
   }
 
   // Backup
   extern "Rust" {
     #[cxx_name = "startBackupHandler"]
     fn start_backup_handler() -> Result<()>;
     #[cxx_name = "stopBackupHandler"]
     fn stop_backup_handler() -> Result<()>;
 
     #[cxx_name = "triggerBackupFileUpload"]
     fn trigger_backup_file_upload();
 
     #[cxx_name = "createBackup"]
     fn create_backup(
       backup_id: String,
       backup_secret: String,
       pickle_key: String,
       pickled_account: String,
+      siwe_backup_msg: String,
       promise_id: u32,
     );
 
     #[cxx_name = "restoreBackup"]
     fn restore_backup(backup_secret: String, promise_id: u32);
 
     #[cxx_name = "restoreBackupData"]
     fn restore_backup_data(
       backup_id: String,
       backup_data_key: String,
       backup_log_data_key: String,
       promise_id: u32,
     );
 
     #[cxx_name = "retrieveBackupKeys"]
     fn retrieve_backup_keys(backup_secret: String, promise_id: u32);
   }
 
   // Secure store
   #[namespace = "comm"]
   unsafe extern "C++" {
     include!("RustSecureStore.h");
 
     #[allow(unused)]
     #[cxx_name = "secureStoreSet"]
     fn secure_store_set(key: &str, value: String) -> Result<()>;
 
     #[cxx_name = "secureStoreGet"]
     fn secure_store_get(key: &str) -> Result<String>;
   }
 
   // C++ Backup creation
   #[namespace = "comm"]
   unsafe extern "C++" {
     include!("RustBackupExecutor.h");
 
     #[cxx_name = "getBackupDirectoryPath"]
     fn get_backup_directory_path() -> Result<String>;
 
     #[cxx_name = "getBackupFilePath"]
     fn get_backup_file_path(
       backup_id: &str,
       is_attachments: bool,
     ) -> Result<String>;
 
     #[cxx_name = "getBackupLogFilePath"]
     fn get_backup_log_file_path(
       backup_id: &str,
       log_id: &str,
       is_attachments: bool,
     ) -> Result<String>;
 
     #[cxx_name = "getBackupUserKeysFilePath"]
     fn get_backup_user_keys_file_path(backup_id: &str) -> Result<String>;
 
     #[cxx_name = "getSIWEBackupMessagePath"]
     fn get_siwe_backup_message_path(backup_id: &str) -> Result<String>;
 
     #[cxx_name = "createMainCompaction"]
     fn create_main_compaction(backup_id: &str, future_id: usize);
 
     #[cxx_name = "restoreFromMainCompaction"]
     fn restore_from_main_compaction(
       main_compaction_path: &str,
       main_compaction_encryption_key: &str,
       future_id: usize,
     );
 
     #[cxx_name = "restoreFromBackupLog"]
     fn restore_from_backup_log(backup_log: Vec<u8>, future_id: usize);
   }
 
   // Future handling from C++
   extern "Rust" {
     #[cxx_name = "resolveUnitFuture"]
     fn resolve_unit_future(future_id: usize);
 
     #[cxx_name = "rejectFuture"]
     fn reject_future(future_id: usize, error: String);
   }
 }
 
 #[derive(
   Debug, derive_more::Display, derive_more::From, derive_more::Error,
 )]
 pub enum Error {
   #[display(fmt = "{}", "_0.message()")]
   TonicGRPC(Status),
   #[display(fmt = "{}", "_0")]
   SerdeJson(serde_json::Error),
   #[display(fmt = "Missing response data")]
   MissingResponseData,
   #[display(fmt = "{}", "_0")]
   GRPClient(grpc_clients::error::Error),
 }
 
 #[cfg(test)]
 #[allow(clippy::assertions_on_constants)]
 mod tests {
   use super::{BACKUP_SOCKET_ADDR, CODE_VERSION, IDENTITY_SOCKET_ADDR};
 
   #[test]
   fn test_code_version_exists() {
     assert!(CODE_VERSION > 0);
   }
 
   #[test]
   fn test_identity_socket_addr_exists() {
     assert!(!IDENTITY_SOCKET_ADDR.is_empty());
     assert!(!BACKUP_SOCKET_ADDR.is_empty());
   }
 }
diff --git a/native/schema/CommCoreModuleSchema.js b/native/schema/CommCoreModuleSchema.js
index 6b6103c2e..fa2ca6a9a 100644
--- a/native/schema/CommCoreModuleSchema.js
+++ b/native/schema/CommCoreModuleSchema.js
@@ -1,166 +1,170 @@
 // @flow
 
 'use strict';
 
 import { TurboModuleRegistry } from 'react-native';
 import type { TurboModule } from 'react-native/Libraries/TurboModule/RCTExport.js';
 
 import type { ClientDBMessageStoreOperation } from 'lib/ops/message-store-ops.js';
 import type { ClientDBReportStoreOperation } from 'lib/ops/report-store-ops.js';
 import type { ClientDBThreadStoreOperation } from 'lib/ops/thread-store-ops.js';
 import type {
   OneTimeKeysResult,
   SignedPrekeys,
   ClientPublicKeys,
   EncryptedData,
   OutboundSessionCreationResult,
 } from 'lib/types/crypto-types.js';
 import type { ClientDBMessageInfo } from 'lib/types/message-types.js';
 import type { SIWEBackupSecrets } from 'lib/types/siwe-types.js';
 import type { ReceivedMessageToDevice } from 'lib/types/sqlite-types.js';
 import type {
   ClientDBStore,
   ClientDBStoreOperations,
 } from 'lib/types/store-ops-types';
 import type { ClientDBThreadInfo } from 'lib/types/thread-types.js';
 
 type CommServicesAuthMetadata = {
   +userID?: ?string,
   +deviceID?: ?string,
   +accessToken?: ?string,
 };
 
 interface Spec extends TurboModule {
   +getDraft: (key: string) => Promise<string>;
   +updateDraft: (key: string, text: string) => Promise<boolean>;
   +moveDraft: (oldKey: string, newKey: string) => Promise<boolean>;
   +getClientDBStore: () => Promise<ClientDBStore>;
   +removeAllDrafts: () => Promise<void>;
   +getAllMessagesSync: () => $ReadOnlyArray<ClientDBMessageInfo>;
   +processMessageStoreOperationsSync: (
     operations: $ReadOnlyArray<ClientDBMessageStoreOperation>,
   ) => void;
   +getAllThreadsSync: () => $ReadOnlyArray<ClientDBThreadInfo>;
   +processReportStoreOperationsSync: (
     operations: $ReadOnlyArray<ClientDBReportStoreOperation>,
   ) => void;
   +processThreadStoreOperationsSync: (
     operations: $ReadOnlyArray<ClientDBThreadStoreOperation>,
   ) => void;
   +processDBStoreOperations: (operations: Object) => Promise<void>;
   +initializeCryptoAccount: () => Promise<string>;
   +getUserPublicKey: () => Promise<ClientPublicKeys>;
   +getOneTimeKeys: (oneTimeKeysAmount: number) => Promise<OneTimeKeysResult>;
   +validateAndGetPrekeys: () => Promise<SignedPrekeys>;
   +validateAndUploadPrekeys: (
     authUserID: string,
     authDeviceID: string,
     authAccessToken: string,
   ) => Promise<void>;
   +initializeNotificationsSession: (
     identityKeys: string,
     prekey: string,
     prekeySignature: string,
     oneTimeKey: string,
     keyserverID: string,
   ) => Promise<string>;
   +isNotificationsSessionInitialized: () => Promise<boolean>;
   +updateKeyserverDataInNotifStorage: (
     keyserversData: $ReadOnlyArray<{ +id: string, +unreadCount: number }>,
   ) => Promise<void>;
   +removeKeyserverDataFromNotifStorage: (
     keyserverIDsToDelete: $ReadOnlyArray<string>,
   ) => Promise<void>;
   +getKeyserverDataFromNotifStorage: (
     keyserverIDs: $ReadOnlyArray<string>,
   ) => Promise<$ReadOnlyArray<{ +id: string, +unreadCount: number }>>;
   +initializeContentOutboundSession: (
     identityKeys: string,
     prekey: string,
     prekeySignature: string,
     oneTimeKey: string,
     deviceID: string,
   ) => Promise<OutboundSessionCreationResult>;
   +initializeContentInboundSession: (
     identityKeys: string,
     encryptedContent: Object,
     deviceID: string,
     sessionVersion: number,
     overwrite: boolean,
   ) => Promise<string>;
   +encrypt: (message: string, deviceID: string) => Promise<EncryptedData>;
   +decrypt: (encryptedData: Object, deviceID: string) => Promise<string>;
   +decryptSequentialAndPersist: (
     encryptedData: Object,
     deviceID: string,
     messageID: string,
   ) => Promise<string>;
   +signMessage: (message: string) => Promise<string>;
   +getCodeVersion: () => number;
   +terminate: () => void;
   +setNotifyToken: (token: string) => Promise<void>;
   +clearNotifyToken: () => Promise<void>;
   +stampSQLiteDBUserID: (userID: string) => Promise<void>;
   +getSQLiteStampedUserID: () => Promise<string>;
   +clearSensitiveData: () => Promise<void>;
   +checkIfDatabaseNeedsDeletion: () => boolean;
   +reportDBOperationsFailure: () => void;
   +computeBackupKey: (password: string, backupID: string) => Promise<Object>;
   +generateRandomString: (size: number) => Promise<string>;
   +setCommServicesAuthMetadata: (
     userID: string,
     deviceID: string,
     accessToken: string,
   ) => Promise<void>;
   +getCommServicesAuthMetadata: () => Promise<CommServicesAuthMetadata>;
   +clearCommServicesAuthMetadata: () => Promise<void>;
   +setCommServicesAccessToken: (accessToken: string) => Promise<void>;
   +clearCommServicesAccessToken: () => Promise<void>;
   +startBackupHandler: () => void;
   +stopBackupHandler: () => void;
   +createNewBackup: (backupSecret: string) => Promise<void>;
+  +createNewSIWEBackup: (
+    backupSecret: string,
+    siweBackupMsg: string,
+  ) => Promise<void>;
   +restoreBackup: (backupSecret: string) => Promise<string>;
   +restoreBackupData: (
     backupID: string,
     backupDataKey: string,
     backupLogDataKey: string,
   ) => Promise<void>;
   +retrieveBackupKeys: (backupSecret: string) => Promise<string>;
   +setSIWEBackupSecrets: (siweBackupSecrets: Object) => Promise<void>;
   +getSIWEBackupSecrets: () => Promise<?Object>;
   +getAllReceivedMessageToDevice: () => Promise<ReceivedMessageToDevice[]>;
   +removeReceivedMessagesToDevice: (
     ids: $ReadOnlyArray<string>,
   ) => Promise<void>;
 }
 
 export interface CoreModuleSpec extends Spec {
   +computeBackupKey: (
     password: string,
     backupID: string,
   ) => Promise<ArrayBuffer>;
   +decrypt: (encryptedData: EncryptedData, deviceID: string) => Promise<string>;
   +decryptSequentialAndPersist: (
     encryptedData: EncryptedData,
     deviceID: string,
     messageID: string,
   ) => Promise<string>;
   +initializeContentInboundSession: (
     identityKeys: string,
     encryptedContent: EncryptedData,
     deviceID: string,
     sessionVersion: number,
     overwrite: boolean,
   ) => Promise<string>;
   +setSIWEBackupSecrets: (
     siweBackupSecrets: SIWEBackupSecrets,
   ) => Promise<void>;
   +getSIWEBackupSecrets: () => Promise<?SIWEBackupSecrets>;
   +processDBStoreOperations: (
     operations: ClientDBStoreOperations,
   ) => Promise<void>;
 }
 
 export default (TurboModuleRegistry.getEnforcing<Spec>(
   'CommTurboModule',
 ): Spec);
diff --git a/services/commtest/tests/backup_integration_test.rs b/services/commtest/tests/backup_integration_test.rs
index 9c3475fd7..d57f4add9 100644
--- a/services/commtest/tests/backup_integration_test.rs
+++ b/services/commtest/tests/backup_integration_test.rs
@@ -1,212 +1,214 @@
 use backup_client::{
   BackupClient, BackupData, BackupDescriptor, DownloadedLog,
   Error as BackupClientError, LogUploadConfirmation, RequestedData, SinkExt,
   StreamExt, TryStreamExt,
 };
 use bytesize::ByteSize;
 use comm_lib::{
   auth::UserIdentity,
   backup::{LatestBackupIDResponse, UploadLogRequest},
 };
 use commtest::{
   service_addr,
   tools::{generate_stable_nbytes, Error},
 };
 use reqwest::StatusCode;
 use std::collections::HashSet;
 use uuid::Uuid;
 
 #[tokio::test]
 async fn backup_integration_test() -> Result<(), Error> {
   let backup_client = BackupClient::new(service_addr::BACKUP_SERVICE_HTTP)?;
 
   let user_identity = UserIdentity {
     user_id: "1".to_string(),
     access_token: "dummy access token".to_string(),
     device_id: "dummy device_id".to_string(),
   };
 
   let backup_datas = generate_backup_data();
 
   // Upload backups
   for (backup_data, log_datas) in &backup_datas {
     backup_client
       .upload_backup(&user_identity, backup_data.clone())
       .await?;
 
     let (tx, rx) = backup_client.upload_logs(&user_identity).await?;
 
     tokio::pin!(tx);
     tokio::pin!(rx);
 
     for log_data in log_datas {
       tx.send(log_data.clone()).await?;
     }
 
     let result: HashSet<LogUploadConfirmation> =
       rx.take(log_datas.len()).try_collect().await?;
     let expected = log_datas
       .iter()
       .map(|data| LogUploadConfirmation {
         backup_id: data.backup_id.clone(),
         log_id: data.log_id,
       })
       .collect();
 
     assert_eq!(result, expected);
   }
 
   // Test direct lookup
   let (backup_data, log_datas) = &backup_datas[1];
 
   let second_backup_descriptor = BackupDescriptor::BackupID {
     backup_id: backup_data.backup_id.clone(),
     user_identity: user_identity.clone(),
   };
 
   let user_keys = backup_client
     .download_backup_data(&second_backup_descriptor, RequestedData::UserKeys)
     .await?;
   assert_eq!(user_keys, backup_data.user_keys);
 
   let user_data = backup_client
     .download_backup_data(&second_backup_descriptor, RequestedData::UserData)
     .await?;
   assert_eq!(user_data, backup_data.user_data);
 
   // Test latest backup lookup
   let latest_backup_descriptor = BackupDescriptor::Latest {
     // Initial version of the backup service uses `user_id` in place of a username
     username: "1".to_string(),
   };
 
   let backup_id_response = backup_client
     .download_backup_data(&latest_backup_descriptor, RequestedData::BackupID)
     .await?;
   let response: LatestBackupIDResponse =
     serde_json::from_slice(&backup_id_response)?;
   assert_eq!(response.backup_id, backup_data.backup_id);
 
   let user_keys = backup_client
     .download_backup_data(&latest_backup_descriptor, RequestedData::UserKeys)
     .await?;
   assert_eq!(user_keys, backup_data.user_keys);
 
   // Test log download
   let log_stream = backup_client
     .download_logs(&user_identity, &backup_data.backup_id)
     .await;
 
   let downloaded_logs: Vec<DownloadedLog> = log_stream.try_collect().await?;
 
   let expected_logs: Vec<DownloadedLog> = log_datas
     .iter()
     .map(|data| DownloadedLog {
       content: data.content.clone(),
       attachments: data.attachments.clone(),
     })
     .collect();
   assert_eq!(downloaded_logs, expected_logs);
 
   // Test backup cleanup
   let (removed_backup, _) = &backup_datas[0];
   let removed_backup_descriptor = BackupDescriptor::BackupID {
     backup_id: removed_backup.backup_id.clone(),
     user_identity: user_identity.clone(),
   };
 
   let response = backup_client
     .download_backup_data(&removed_backup_descriptor, RequestedData::UserKeys)
     .await;
 
   let Err(BackupClientError::ReqwestError(error)) = response else {
     panic!("First backup should have been removed, instead got response: {response:?}");
   };
 
   assert_eq!(
     error.status(),
     Some(StatusCode::NOT_FOUND),
     "Expected status 'not found'"
   );
 
   // Test log cleanup
   let log_stream = backup_client
     .download_logs(&user_identity, &removed_backup.backup_id)
     .await;
 
   let downloaded_logs: Vec<DownloadedLog> = log_stream.try_collect().await?;
 
   if !downloaded_logs.is_empty() {
     panic!(
       "Logs for first backup should have been removed, \
       instead got: {downloaded_logs:?}"
     )
   }
 
   Ok(())
 }
 
 fn generate_backup_data() -> [(BackupData, Vec<UploadLogRequest>); 2] {
   [
     (
       BackupData {
         backup_id: "b1".to_string(),
         user_keys: generate_stable_nbytes(
           ByteSize::kib(4).as_u64() as usize,
           Some(b'a'),
         ),
         user_data: generate_stable_nbytes(
           ByteSize::mib(4).as_u64() as usize,
           Some(b'A'),
         ),
         attachments: vec![],
+        siwe_backup_msg: None,
       },
       generate_log_data("b1", b'a'),
     ),
     (
       BackupData {
         backup_id: "b2".to_string(),
         user_keys: generate_stable_nbytes(
           ByteSize::kib(4).as_u64() as usize,
           Some(b'b'),
         ),
         user_data: generate_stable_nbytes(
           ByteSize::mib(4).as_u64() as usize,
           Some(b'B'),
         ),
         attachments: vec![],
+        siwe_backup_msg: None,
       },
       generate_log_data("b2", b'b'),
     ),
   ]
 }
 
 fn generate_log_data(backup_id: &str, value: u8) -> Vec<UploadLogRequest> {
   const IN_DB_SIZE: usize = ByteSize::kib(4).as_u64() as usize;
   const IN_BLOB_SIZE: usize = ByteSize::kib(400).as_u64() as usize;
 
   (1..30)
     .map(|log_id| {
       let size = if log_id % 2 == 0 {
         IN_DB_SIZE
       } else {
         IN_BLOB_SIZE
       };
       let attachments = if log_id % 10 == 0 {
         Some(vec![Uuid::new_v4().to_string()])
       } else {
         None
       };
       let mut content = generate_stable_nbytes(size, Some(value));
       let unique_suffix = log_id.to_string();
       content.extend(unique_suffix.as_bytes());
 
       UploadLogRequest {
         backup_id: backup_id.to_string(),
         log_id,
         content,
         attachments,
       }
     })
     .collect()
 }
diff --git a/services/commtest/tests/backup_performance_test.rs b/services/commtest/tests/backup_performance_test.rs
index f60a226ee..6168bc4f5 100644
--- a/services/commtest/tests/backup_performance_test.rs
+++ b/services/commtest/tests/backup_performance_test.rs
@@ -1,171 +1,172 @@
 use backup_client::{
   BackupClient, BackupData, BackupDescriptor, RequestedData,
 };
 use bytesize::ByteSize;
 use comm_lib::{auth::UserIdentity, backup::LatestBackupIDResponse};
 use commtest::{
   service_addr,
   tools::{generate_stable_nbytes, obtain_number_of_threads, Error},
 };
 use tokio::{runtime::Runtime, task::JoinSet};
 
 #[tokio::test]
 async fn backup_performance_test() -> Result<(), Error> {
   let backup_client = BackupClient::new(service_addr::BACKUP_SERVICE_HTTP)?;
 
   let number_of_threads = obtain_number_of_threads();
 
   let rt = Runtime::new().unwrap();
 
   println!(
     "Running performance tests for backup, number of threads: {}",
     number_of_threads
   );
 
   let mut backup_data = vec![];
   for i in 0..number_of_threads {
     backup_data.push(BackupData {
       backup_id: format!("b{i}"),
       user_keys: generate_stable_nbytes(
         ByteSize::kib(4).as_u64() as usize,
         Some(i as u8),
       ),
       user_data: generate_stable_nbytes(
         ByteSize::mib(4).as_u64() as usize,
         Some(i as u8),
       ),
       attachments: vec![],
+      siwe_backup_msg: None,
     });
   }
 
   let user_identities = [
     UserIdentity {
       user_id: "1".to_string(),
       access_token: "dummy access token".to_string(),
       device_id: "dummy device_id".to_string(),
     },
     UserIdentity {
       user_id: "2".to_string(),
       access_token: "dummy access token".to_string(),
       device_id: "dummy device_id".to_string(),
     },
   ];
 
   tokio::task::spawn_blocking(move || {
     println!("Creating new backups");
     rt.block_on(async {
       let mut set = JoinSet::new();
       for (i, item) in backup_data.iter().cloned().enumerate() {
         let backup_client = backup_client.clone();
         let user = user_identities[i % user_identities.len()].clone();
 
         set.spawn(async move {
           backup_client.upload_backup(&user, item).await.unwrap();
         });
       }
 
       while let Some(result) = set.join_next().await {
         result.unwrap();
       }
     });
 
     let mut latest_ids_for_user = vec![];
     println!("Reading latest ids");
     rt.block_on(async {
       let mut handlers = vec![];
       for user in &user_identities {
         let backup_client = backup_client.clone();
         let descriptor = BackupDescriptor::Latest {
           username: user.user_id.clone(),
         };
         handlers.push(tokio::spawn(async move {
           let response = backup_client
             .download_backup_data(&descriptor, RequestedData::BackupID)
             .await
             .unwrap();
 
           serde_json::from_slice::<LatestBackupIDResponse>(&response).unwrap()
         }));
       }
 
       for handler in handlers {
         latest_ids_for_user.push(handler.await.unwrap().backup_id);
       }
     });
 
     assert_eq!(latest_ids_for_user.len(), user_identities.len());
 
     let mut latest_user_keys_for_user = vec![];
     println!("Reading latest user keys");
     rt.block_on(async {
       let mut handlers = vec![];
       for user in &user_identities {
         let backup_client = backup_client.clone();
         let descriptor = BackupDescriptor::Latest {
           username: user.user_id.clone(),
         };
         handlers.push(tokio::spawn(async move {
           backup_client
             .download_backup_data(&descriptor, RequestedData::UserKeys)
             .await
             .unwrap()
         }));
       }
 
       for handler in handlers {
         latest_user_keys_for_user.push(handler.await.unwrap());
       }
     });
 
     assert_eq!(latest_user_keys_for_user.len(), user_identities.len());
     for (backup_id, user_keys) in
       latest_ids_for_user.iter().zip(latest_user_keys_for_user)
     {
       let backup = backup_data
         .iter()
         .find(|data| data.backup_id == *backup_id)
         .expect("Request should return existing backup data");
 
       assert_eq!(backup.user_keys, user_keys);
     }
 
     let mut latest_user_data_for_user = vec![];
     println!("Reading latest user data");
     rt.block_on(async {
       let mut handlers = vec![];
       for (i, backup_id) in latest_ids_for_user.iter().enumerate() {
         let backup_client = backup_client.clone();
         let descriptor = BackupDescriptor::BackupID {
           backup_id: backup_id.clone(),
           user_identity: user_identities[i % user_identities.len()].clone(),
         };
         handlers.push(tokio::spawn(async move {
           backup_client
             .download_backup_data(&descriptor, RequestedData::UserData)
             .await
             .unwrap()
         }));
       }
 
       for handler in handlers {
         latest_user_data_for_user.push(handler.await.unwrap());
       }
     });
 
     assert_eq!(latest_user_data_for_user.len(), user_identities.len());
     for (backup_id, user_data) in
       latest_ids_for_user.iter().zip(latest_user_data_for_user)
     {
       let backup = backup_data
         .iter()
         .find(|data| data.backup_id == *backup_id)
         .expect("Request should return existing backup data");
 
       assert_eq!(backup.user_data, user_data);
     }
   })
   .await
   .expect("Task panicked");
 
   Ok(())
 }
diff --git a/shared/backup_client/src/lib.rs b/shared/backup_client/src/lib.rs
index 3f3ab2206..f36df93f8 100644
--- a/shared/backup_client/src/lib.rs
+++ b/shared/backup_client/src/lib.rs
@@ -1,370 +1,376 @@
 #[cfg(target_arch = "wasm32")]
 mod web;
 
 use async_stream::{stream, try_stream};
 pub use comm_lib::auth::UserIdentity;
 pub use comm_lib::backup::{
   DownloadLogsRequest, LatestBackupIDResponse, LogWSRequest, LogWSResponse,
   UploadLogRequest,
 };
 pub use futures_util::{Sink, SinkExt, Stream, StreamExt, TryStreamExt};
 use hex::ToHex;
 use reqwest::{
   header::InvalidHeaderValue,
   multipart::{Form, Part},
   Body,
 };
 use serde::{Deserialize, Serialize};
 use sha2::{Digest, Sha256};
 use std::time::Duration;
 use tokio_tungstenite_wasm::{
   connect, Error as TungsteniteError, Message::Binary,
 };
 
 const LOG_DOWNLOAD_RETRY_DELAY: Duration = Duration::from_secs(5);
 const LOG_DOWNLOAD_MAX_RETRY: usize = 3;
 
 #[cfg(target_arch = "wasm32")]
 use wasm_bindgen::prelude::wasm_bindgen;
 
 #[cfg_attr(target_arch = "wasm32", wasm_bindgen)]
 #[derive(Debug, Clone)]
 pub struct BackupClient {
   url: reqwest::Url,
 }
 
 impl BackupClient {
   pub fn new<T: TryInto<reqwest::Url>>(url: T) -> Result<Self, T::Error> {
     Ok(BackupClient {
       url: url.try_into()?,
     })
   }
 }
 
 /// Backup functions
 impl BackupClient {
   pub async fn upload_backup(
     &self,
     user_identity: &UserIdentity,
     backup_data: BackupData,
   ) -> Result<(), Error> {
     let BackupData {
       backup_id,
       user_keys,
       user_data,
       attachments,
+      siwe_backup_msg,
     } = backup_data;
 
     let client = reqwest::Client::new();
-    let form = Form::new()
+    let mut form = Form::new()
       .text("backup_id", backup_id)
       .text(
         "user_keys_hash",
         Sha256::digest(&user_keys).encode_hex::<String>(),
       )
       .part("user_keys", Part::stream(Body::from(user_keys)))
       .text(
         "user_data_hash",
         Sha256::digest(&user_data).encode_hex::<String>(),
       )
       .part("user_data", Part::stream(Body::from(user_data)))
       .text("attachments", attachments.join("\n"));
 
+    if let Some(siwe_backup_msg_value) = siwe_backup_msg {
+      form = form.text("siwe_backup_msg", siwe_backup_msg_value);
+    }
+
     let response = client
       .post(self.url.join("backups")?)
       .bearer_auth(user_identity.as_authorization_token()?)
       .multipart(form)
       .send()
       .await?;
 
     response.error_for_status()?;
 
     Ok(())
   }
 
   pub async fn download_backup_data(
     &self,
     backup_descriptor: &BackupDescriptor,
     requested_data: RequestedData,
   ) -> Result<Vec<u8>, Error> {
     let client = reqwest::Client::new();
     let url = self.url.join("backups/")?;
     let url = match backup_descriptor {
       BackupDescriptor::BackupID { backup_id, .. } => {
         url.join(&format!("{backup_id}/"))?
       }
       BackupDescriptor::Latest { username } => {
         url.join(&format!("latest/{username}/"))?
       }
     };
     let url = match &requested_data {
       RequestedData::BackupID => url.join("backup_id")?,
       RequestedData::UserKeys => url.join("user_keys")?,
       RequestedData::UserData => url.join("user_data")?,
     };
 
     let mut request = client.get(url);
 
     if let BackupDescriptor::BackupID { user_identity, .. } = backup_descriptor
     {
       request = request.bearer_auth(user_identity.as_authorization_token()?)
     }
 
     let response = request.send().await?;
 
     let result = response.error_for_status()?.bytes().await?.to_vec();
 
     Ok(result)
   }
 }
 
 /// Log functions
 impl BackupClient {
   pub async fn upload_logs(
     &self,
     user_identity: &UserIdentity,
   ) -> Result<
     (
       impl Sink<UploadLogRequest, Error = Error>,
       impl Stream<Item = Result<LogUploadConfirmation, Error>>,
     ),
     Error,
   > {
     let (tx, rx) = self.create_log_ws_connection(user_identity).await?;
 
     let rx = rx.map(|response| match response? {
       LogWSResponse::LogUploaded { backup_id, log_id } => {
         Ok(LogUploadConfirmation { backup_id, log_id })
       }
       LogWSResponse::ServerError => Err(Error::ServerError),
       msg => Err(Error::InvalidBackupMessage(msg)),
     });
 
     Ok((tx, rx))
   }
 
   /// Handles complete log download.
   /// It will try and retry download a few times, but if the issues persist
   /// the next item returned will be the last received error and the stream
   /// will be closed.
   pub async fn download_logs<'this>(
     &'this self,
     user_identity: &'this UserIdentity,
     backup_id: &'this str,
   ) -> impl Stream<Item = Result<DownloadedLog, Error>> + 'this {
     stream! {
       let mut last_downloaded_log = None;
       let mut fail_count = 0;
 
       'retry: loop {
         let stream = self.log_download_stream(user_identity, backup_id, &mut last_downloaded_log).await;
         let mut stream = Box::pin(stream);
 
         while let Some(item) = stream.next().await {
           match item {
             Ok(log) => yield Ok(log),
             Err(err) => {
               println!("Error when downloading logs: {err:?}");
 
               fail_count += 1;
               if fail_count >= LOG_DOWNLOAD_MAX_RETRY {
                 yield Err(err);
                 break 'retry;
               }
 
               #[cfg(target_arch = "wasm32")]
               let _ = web::sleep(LOG_DOWNLOAD_RETRY_DELAY).await;
               #[cfg(not(target_arch = "wasm32"))]
               tokio::time::sleep(LOG_DOWNLOAD_RETRY_DELAY).await;
               continue 'retry;
             }
           }
         }
 
         // Everything downloaded
         return;
       }
 
       println!("Log download failed!");
     }
   }
 
   /// Handles singular connection websocket connection. Returns error in case
   /// anything goes wrong e.g. missing log or connection error.
   async fn log_download_stream<'stream>(
     &'stream self,
     user_identity: &'stream UserIdentity,
     backup_id: &'stream str,
     last_downloaded_log: &'stream mut Option<usize>,
   ) -> impl Stream<Item = Result<DownloadedLog, Error>> + 'stream {
     try_stream! {
       let (tx, rx) = self.create_log_ws_connection(user_identity).await?;
 
       let mut tx = Box::pin(tx);
       let mut rx = Box::pin(rx);
 
       tx.send(DownloadLogsRequest {
         backup_id: backup_id.to_string(),
         from_id: *last_downloaded_log,
       })
       .await?;
 
       while let Some(response) = rx.try_next().await? {
         let expected_log_id = last_downloaded_log.unwrap_or(0);
         match response {
           LogWSResponse::LogDownload {
             content,
             attachments,
             log_id,
           } if log_id == expected_log_id + 1 => {
             *last_downloaded_log = Some(log_id);
             yield DownloadedLog {
               content,
               attachments,
             };
           }
           LogWSResponse::LogDownload { .. } => {
             Err(Error::LogMissing)?;
           }
           LogWSResponse::LogDownloadFinished {
             last_log_id: Some(log_id),
           } if log_id == expected_log_id => {
             tx.send(DownloadLogsRequest {
               backup_id: backup_id.to_string(),
               from_id: *last_downloaded_log,
             })
             .await?
           }
           LogWSResponse::LogDownloadFinished { last_log_id: None } => return,
           LogWSResponse::LogDownloadFinished { .. } => {
             Err(Error::LogMissing)?;
           }
           msg => Err(Error::InvalidBackupMessage(msg))?,
         }
       }
 
       Err(Error::WSClosed)?;
     }
   }
 
   async fn create_log_ws_connection<Request: Into<LogWSRequest>>(
     &self,
     user_identity: &UserIdentity,
   ) -> Result<
     (
       impl Sink<Request, Error = Error>,
       impl Stream<Item = Result<LogWSResponse, Error>>,
     ),
     Error,
   > {
     let url = self.create_ws_url()?;
     let stream = connect(url).await?;
 
     let (mut tx, rx) = stream.split();
 
     tx.send(Binary(bincode::serialize(&LogWSRequest::Authenticate(
       user_identity.clone(),
     ))?))
     .await?;
 
     let tx = tx.with(|request: Request| async {
       let request: LogWSRequest = request.into();
       let request = bincode::serialize(&request)?;
       Ok(Binary(request))
     });
 
     let rx = rx.filter_map(|msg| async {
       let bytes = match msg {
         Ok(Binary(bytes)) => bytes,
         Ok(_) => return Some(Err(Error::InvalidWSMessage)),
         Err(err) => return Some(Err(err.into())),
       };
 
       match bincode::deserialize(&bytes) {
         Ok(response) => Some(Ok(response)),
         Err(err) => Some(Err(err.into())),
       }
     });
 
     Ok((tx, rx))
   }
 
   fn create_ws_url(&self) -> Result<reqwest::Url, Error> {
     let mut url = self.url.clone();
 
     match url.scheme() {
       "http" => url.set_scheme("ws").map_err(|_| Error::UrlSchemaError)?,
       "https" => url.set_scheme("wss").map_err(|_| Error::UrlSchemaError)?,
       _ => (),
     };
     let url = url.join("logs")?;
 
     Ok(url)
   }
 }
 
 #[derive(Debug, Clone)]
 pub struct BackupData {
   pub backup_id: String,
   pub user_keys: Vec<u8>,
   pub user_data: Vec<u8>,
   pub attachments: Vec<String>,
+  pub siwe_backup_msg: Option<String>,
 }
 
 #[derive(Debug, Clone, Serialize, Deserialize)]
 #[serde(tag = "type")]
 pub enum BackupDescriptor {
   BackupID {
     #[serde(rename = "backupID")]
     backup_id: String,
     #[serde(rename = "userIdentity")]
     user_identity: UserIdentity,
   },
   Latest {
     username: String,
   },
 }
 
 #[cfg_attr(target_arch = "wasm32", wasm_bindgen)]
 #[derive(Debug, Clone)]
 pub enum RequestedData {
   BackupID,
   UserKeys,
   UserData,
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct LogUploadConfirmation {
   pub backup_id: String,
   pub log_id: usize,
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct DownloadedLog {
   pub content: Vec<u8>,
   pub attachments: Option<Vec<String>>,
 }
 
 #[derive(Debug, derive_more::Display, derive_more::From)]
 pub enum Error {
   InvalidAuthorizationHeader,
   UrlSchemaError,
   UrlError(url::ParseError),
   ReqwestError(reqwest::Error),
   TungsteniteError(TungsteniteError),
   JsonError(serde_json::Error),
   BincodeError(bincode::Error),
   InvalidWSMessage,
   #[display(fmt = "Error::InvalidBackupMessage({:?})", _0)]
   InvalidBackupMessage(LogWSResponse),
   ServerError,
   LogMissing,
   WSClosed,
 }
 impl std::error::Error for Error {}
 
 impl From<InvalidHeaderValue> for Error {
   fn from(_: InvalidHeaderValue) -> Self {
     Self::InvalidAuthorizationHeader
   }
 }