diff --git a/src/app/bannedlistmodel.cpp b/src/app/bannedlistmodel.cpp
index 09be0e6a4e89bc4d77e233e88957a16e9d6ac6c3..e0707cc4d19437fbe1cbc3b6e19de11fad168dd3 100644
--- a/src/app/bannedlistmodel.cpp
+++ b/src/app/bannedlistmodel.cpp
@@ -21,6 +21,8 @@
 
 #include "lrcinstance.h"
 
+#include <api/contact.h>
+
 BannedListModel::BannedListModel(QObject* parent)
     : AbstractListModelBase(parent)
 
diff --git a/src/app/conversationlistmodelbase.cpp b/src/app/conversationlistmodelbase.cpp
index bf5c01e2a6225200abd531bc2870acc8b854d76c..3ebbc0da7e2c985af000cd371a11d0f581be94a4 100644
--- a/src/app/conversationlistmodelbase.cpp
+++ b/src/app/conversationlistmodelbase.cpp
@@ -19,6 +19,8 @@
 
 #include "conversationlistmodelbase.h"
 
+#include <api/contact.h>
+
 ConversationListModelBase::ConversationListModelBase(LRCInstance* instance, QObject* parent)
     : AbstractListModelBase(parent)
 {
diff --git a/src/app/conversationsadapter.cpp b/src/app/conversationsadapter.cpp
index ea7484b09e00568105afe6c7300738f36fe53650..e43d99115101eefa71127cbb4cfcdb14793f1f5e 100644
--- a/src/app/conversationsadapter.cpp
+++ b/src/app/conversationsadapter.cpp
@@ -23,8 +23,9 @@
 #include "systemtray.h"
 
 #ifdef Q_OS_LINUX
-#include "namedirectory.h"
+#include <namedirectory.h>
 #endif
+#include <api/contact.h>
 
 #include <QApplication>
 #include <QJsonObject>
diff --git a/src/app/currentconversation.cpp b/src/app/currentconversation.cpp
index 8fa2b74cecfd3e5aebeaec0483d3ed49ac7d08bc..bc90057e26dece061b9f66d9e679a38c79df4339 100644
--- a/src/app/currentconversation.cpp
+++ b/src/app/currentconversation.cpp
@@ -19,6 +19,7 @@
 #include "currentconversation.h"
 
 #include <api/conversationmodel.h>
+#include <api/contact.h>
 
 CurrentConversation::CurrentConversation(LRCInstance* lrcInstance, QObject* parent)
     : QObject(parent)
diff --git a/src/app/lrcinstance.cpp b/src/app/lrcinstance.cpp
index d6f00540a16d97ab75865226f84df48182e60154..57673739523f38b59e45027ce54107888849fa09 100644
--- a/src/app/lrcinstance.cpp
+++ b/src/app/lrcinstance.cpp
@@ -28,13 +28,11 @@
 #include <QRegularExpression>
 #include <QtConcurrent/QtConcurrent>
 
-LRCInstance::LRCInstance(migrateCallback willMigrateCb,
-                         migrateCallback didMigrateCb,
-                         const QString& updateUrl,
+LRCInstance::LRCInstance(const QString& updateUrl,
                          ConnectivityMonitor* connectivityMonitor,
                          bool debugMode,
                          bool muteDaemon)
-    : lrc_(std::make_unique<Lrc>(willMigrateCb, didMigrateCb, !debugMode || muteDaemon))
+    : lrc_(std::make_unique<Lrc>(!debugMode || muteDaemon))
     , updateManager_(std::make_unique<AppVersionManager>(updateUrl, connectivityMonitor, this))
     , connectivityMonitor_(*connectivityMonitor)
     , threadPool_(new QThreadPool(this))
diff --git a/src/app/lrcinstance.h b/src/app/lrcinstance.h
index 261ea2a20c187a42a0d5948056bc1ba9a1abc936..3a853e1da02eb5d30399a53b10a4d7a2dd707acc 100644
--- a/src/app/lrcinstance.h
+++ b/src/app/lrcinstance.h
@@ -28,16 +28,15 @@
 #include "qtutils.h"
 #include "utils.h"
 
-#include "api/lrc.h"
-#include "api/account.h"
-#include "api/avmodel.h"
-#include "api/behaviorcontroller.h"
-#include "api/contact.h"
-#include "api/contactmodel.h"
-#include "api/conversation.h"
-#include "api/conversationmodel.h"
-#include "api/accountmodel.h"
-#include "api/callmodel.h"
+#include <api/lrc.h>
+#include <api/account.h>
+#include <api/avmodel.h>
+#include <api/behaviorcontroller.h>
+#include <api/contactmodel.h>
+#include <api/conversation.h>
+#include <api/conversationmodel.h>
+#include <api/accountmodel.h>
+#include <api/callmodel.h>
 
 #include <QObject>
 #include <QThreadPool>
@@ -48,7 +47,6 @@ class ConnectivityMonitor;
 
 using namespace lrc::api;
 
-using migrateCallback = std::function<void()>;
 using getConvPredicate = std::function<bool(const conversation::Info& conv)>;
 
 class LRCInstance : public QObject
@@ -61,9 +59,7 @@ class LRCInstance : public QObject
     QML_PROPERTY(bool, currentAccountAvatarSet)
 
 public:
-    explicit LRCInstance(migrateCallback willMigrateCb,
-                         migrateCallback didMigrateCb,
-                         const QString& updateUrl,
+    explicit LRCInstance(const QString& updateUrl,
                          ConnectivityMonitor* connectivityMonitor,
                          bool debugMode,
                          bool muteDaemon);
diff --git a/src/app/mainapplication.cpp b/src/app/mainapplication.cpp
index 8398bbf1b6c2ad56ff129706d315d09195c67b9b..200dee065c15556f19199a1578f8878cce421981 100644
--- a/src/app/mainapplication.cpp
+++ b/src/app/mainapplication.cpp
@@ -298,30 +298,7 @@ MainApplication::initLrc(const QString& downloadUrl,
                          bool debugMode,
                          bool muteDaemon)
 {
-    /*
-     * Init mainwindow and finish splash when mainwindow shows up.
-     */
-    std::atomic_bool isMigrating(false);
-    lrcInstance_.reset(new LRCInstance(
-        [this, &isMigrating] {
-            /*
-             * TODO: splash screen for account migration.
-             */
-            isMigrating = true;
-            while (isMigrating) {
-                this->processEvents();
-            }
-        },
-        [&isMigrating] {
-            while (!isMigrating) {
-                std::this_thread::sleep_for(std::chrono::milliseconds(10));
-            }
-            isMigrating = false;
-        },
-        downloadUrl,
-        cm,
-        debugMode,
-        muteDaemon));
+    lrcInstance_.reset(new LRCInstance(downloadUrl, cm, debugMode, muteDaemon));
     lrcInstance_->subscribeToDebugReceived();
 }
 
diff --git a/src/app/messagesadapter.cpp b/src/app/messagesadapter.cpp
index 9bc034e6ccd77bdaf89789e6ba8e335eaae43408..066e35fa7f6cb33de50a7cc1b7282e2e7fd7047a 100644
--- a/src/app/messagesadapter.cpp
+++ b/src/app/messagesadapter.cpp
@@ -29,6 +29,7 @@
 #include "previewengine.h"
 
 #include <api/datatransfermodel.h>
+#include <api/contact.h>
 
 #include <QApplication>
 #include <QBuffer>
diff --git a/src/app/moderatorlistmodel.cpp b/src/app/moderatorlistmodel.cpp
index 5d78fe524d0cd3aab2744cc98f053725d81bfd75..44e4814af0670980e1c420c09af61ab222bb3bdb 100644
--- a/src/app/moderatorlistmodel.cpp
+++ b/src/app/moderatorlistmodel.cpp
@@ -19,6 +19,8 @@
 
 #include "lrcinstance.h"
 
+#include <api/contact.h>
+
 ModeratorListModel::ModeratorListModel(LRCInstance* instance, QObject* parent)
     : AbstractListModelBase(parent)
 {
@@ -133,4 +135,4 @@ ModeratorListModel::connectAccount()
 {
     if (!lrcInstance_->get_currentAccountId().isEmpty())
         reset();
-}
\ No newline at end of file
+}
diff --git a/src/app/utils.cpp b/src/app/utils.cpp
index 2ebec97d61db395e50089599ea25b8a9fcf7d9fe..da9e73f38defe9dd89e3bca32bdadf8ca4cc6582 100644
--- a/src/app/utils.cpp
+++ b/src/app/utils.cpp
@@ -25,6 +25,8 @@
 #include "jamiavatartheme.h"
 #include "lrcinstance.h"
 
+#include <api/contact.h>
+
 #include <qrencode.h>
 
 #include <QApplication>
diff --git a/src/app/utilsadapter.cpp b/src/app/utilsadapter.cpp
index ec30a5a369ab6e8a4b57101679c37c70571a7b5d..b46add5ce0dae0ba42a577e415bfa79f0ebe0f88 100644
--- a/src/app/utilsadapter.cpp
+++ b/src/app/utilsadapter.cpp
@@ -27,8 +27,8 @@
 #include "utils.h"
 #include "version.h"
 
-#include "api/pluginmodel.h"
-#include "api/datatransfermodel.h"
+#include <api/datatransfermodel.h>
+#include <api/contact.h>
 
 #include <QApplication>
 #include <QBuffer>
diff --git a/src/libclient/accountmodel.cpp b/src/libclient/accountmodel.cpp
index 30dc5850ee432fbec493729287a84eed4f5e5191..e4655a8ecaff715375f9a122ec0bfba0fd433434 100644
--- a/src/libclient/accountmodel.cpp
+++ b/src/libclient/accountmodel.cpp
@@ -33,7 +33,6 @@
 #include "authority/storagehelper.h"
 #include "callbackshandler.h"
 #include "database.h"
-#include "vcard.h"
 
 // old LRC
 #include "api/profile.h"
@@ -64,9 +63,7 @@ public:
     AccountModelPimpl(AccountModel& linked,
                       Lrc& lrc,
                       const CallbacksHandler& callbackHandler,
-                      const BehaviorController& behaviorController,
-                      MigrationCb& willMigrateCb,
-                      MigrationCb& didMigrateCb);
+                      const BehaviorController& behaviorController);
     ~AccountModelPimpl();
 
     using AccountInfoDbMap = std::map<QString, std::pair<account::Info, std::shared_ptr<Database>>>;
@@ -84,10 +81,9 @@ public:
     /**
      * Add the profile information from an account to the db then add it to accounts.
      * @param accountId
-     * @param db an optional migrated database object
      * @note this method get details for an account from the daemon.
      */
-    void addToAccounts(const QString& accountId, std::shared_ptr<Database> db = nullptr);
+    void addToAccounts(const QString& accountId);
 
     /**
      * Remove account from accounts list. Emit accountRemoved.
@@ -196,16 +192,9 @@ public Q_SLOTS:
 
 AccountModel::AccountModel(Lrc& lrc,
                            const CallbacksHandler& callbacksHandler,
-                           const BehaviorController& behaviorController,
-                           MigrationCb& willMigrateCb,
-                           MigrationCb& didMigrateCb)
+                           const BehaviorController& behaviorController)
     : QObject(nullptr)
-    , pimpl_(std::make_unique<AccountModelPimpl>(*this,
-                                                 lrc,
-                                                 callbacksHandler,
-                                                 behaviorController,
-                                                 willMigrateCb,
-                                                 didMigrateCb))
+    , pimpl_(std::make_unique<AccountModelPimpl>(*this, lrc, callbacksHandler, behaviorController))
 {}
 
 AccountModel::~AccountModel() {}
@@ -375,9 +364,7 @@ AccountModel::getAccountInfo(const QString& accountId) const
 AccountModelPimpl::AccountModelPimpl(AccountModel& linked,
                                      Lrc& lrc,
                                      const CallbacksHandler& callbacksHandler,
-                                     const BehaviorController& behaviorController,
-                                     MigrationCb& willMigrateCb,
-                                     MigrationCb& didMigrateCb)
+                                     const BehaviorController& behaviorController)
     : linked(linked)
     , lrc {lrc}
     , behaviorController(behaviorController)
@@ -395,9 +382,8 @@ AccountModelPimpl::AccountModelPimpl(AccountModel& linked,
             return;
         }
 
-    auto accountDbs = authority::storage::migrateIfNeeded(accountIds, willMigrateCb, didMigrateCb);
     for (const auto& id : accountIds) {
-        addToAccounts(id, accountDbs.at(accountIds.indexOf(id)));
+        addToAccounts(id);
     }
 
     connect(&callbacksHandler,
@@ -728,38 +714,39 @@ AccountModelPimpl::slotNewPosition(const QString& accountId,
 }
 
 void
-AccountModelPimpl::addToAccounts(const QString& accountId, std::shared_ptr<Database> db)
+AccountModelPimpl::addToAccounts(const QString& accountId)
 {
-    if (db == nullptr) {
-        try {
-            auto appPath = authority::storage::getPath();
-            auto dbName = accountId + "/history";
-            db = DatabaseFactory::create<Database>(dbName, appPath);
-            // create the profiles path if necessary
-            QDir profilesDir(appPath + accountId + "/profiles");
-            if (!profilesDir.exists()) {
-                profilesDir.mkpath(".");
-            }
-        } catch (const std::runtime_error& e) {
-            qWarning() << e.what();
-            return;
-        }
+    auto appPath = authority::storage::getPath();
+    auto dbName = accountId + "/history";
+
+    // Create and load the database.
+    auto db = std::make_shared<Database>(dbName, appPath);
+    try {
+        db->load();
+    } catch (const std::exception& e) {
+        LC_WARN << e.what();
+        return;
     }
 
-    auto it = accounts.emplace(accountId, std::make_pair(account::Info(), db));
+    // Create the profiles path if necessary.
+    QDir profilesDir(appPath + accountId + "/profiles");
+    if (!profilesDir.exists()) {
+        profilesDir.mkpath(".");
+    }
 
+    auto it = accounts.emplace(accountId, std::make_pair(account::Info(), db));
     if (!it.second) {
-        qWarning("failed to add new account: id already present in map");
+        LC_WARN << "failed to add new account: id already present in map";
         return;
     }
 
-    // Init profile
+    // Initialize the profile.
     account::Info& newAccInfo = (it.first)->second.first;
     newAccInfo.id = accountId;
     newAccInfo.profileInfo.avatar = authority::storage::getAccountAvatar(accountId);
     updateAccountDetails(newAccInfo);
 
-    // Init models for this account
+    // Initialize models for this account.
     newAccInfo.accountModel = &linked;
     newAccInfo.callModel = std::make_unique<CallModel>(newAccInfo,
                                                        lrc,
diff --git a/src/libclient/api/accountmodel.h b/src/libclient/api/accountmodel.h
index f2e1200d497f2de8ba24689a62d8f244cb497a02..753c8f41179b94879c89163111cf94775225d02e 100644
--- a/src/libclient/api/accountmodel.h
+++ b/src/libclient/api/accountmodel.h
@@ -52,9 +52,7 @@ class LIB_EXPORT AccountModel : public QObject
 public:
     AccountModel(Lrc& lrc,
                  const CallbacksHandler& callbackHandler,
-                 const api::BehaviorController& behaviorController,
-                 MigrationCb& willMigrateCb,
-                 MigrationCb& didMigrateCb);
+                 const api::BehaviorController& behaviorController);
 
     ~AccountModel();
     /**
diff --git a/src/libclient/api/lrc.h b/src/libclient/api/lrc.h
index bb083c491f8ee2e7b6c0dbc1c81ae2f9d6a84d13..cf1fada540ebde46e3ddcb1def7b630dbfc0b051 100644
--- a/src/libclient/api/lrc.h
+++ b/src/libclient/api/lrc.h
@@ -41,13 +41,10 @@ class LIB_EXPORT Lrc
 {
 public:
     /**
-     * Construct an Lrc object and optionally invoke callbacks
-     * to control ui informing the user of a possibly lengthy
-     * migration process.
-     * @param willMigrateCb
-     * @param didMigrateCb
+     * Construct an Lrc object.
+     * @param muteDaemon
      */
-    Lrc(MigrationCb willMigrateCb = {}, MigrationCb didMigrateCb = {}, bool muteDaemon = false);
+    Lrc(bool muteDaemon = false);
     ~Lrc();
     /**
      * get a reference on account model.
diff --git a/src/libclient/authority/storagehelper.cpp b/src/libclient/authority/storagehelper.cpp
index 68cfd52c244794a35a168e6ef64b3ca1aa9f1627..05d557974e3bb7b6e2b5e28f3df859c151f59c0d 100644
--- a/src/libclient/authority/storagehelper.cpp
+++ b/src/libclient/authority/storagehelper.cpp
@@ -23,7 +23,6 @@
 #include "api/profile.h"
 #include "api/conversation.h"
 #include "api/datatransfer.h"
-#include "api/lrc.h"
 #include "uri.h"
 #include "vcard.h"
 
@@ -776,578 +775,6 @@ getLastTimestamp(Database& db)
     return result;
 }
 
-//================================================================================
-// This section provides migration helpers from ring.db
-// to per-account databases yielding a file structure like:
-//
-// { local_storage } / jami
-// └──{ account_id }
-// ├── config.yml
-// ├── contacts
-// ├── export.gz
-// ├── incomingTrustRequests
-// ├── knownDevicesNames
-// ├── history.db < --conversations and interactions database
-// ├── profile.vcf < --account vcard
-// ├── profiles < --account contact vcards
-// │   │──{ contact_uri }.vcf
-// │   └── ...
-// ├── ring_device.crt
-// └── ring_device.key
-//================================================================================
-namespace migration {
-
-enum class msgFlag {
-    IS_INCOMING,
-    IS_OUTGOING,
-    IS_CONTACT_ADDED,
-    IS_INVITATION_RECEIVED,
-    IS_INVITATION_ACCEPTED,
-    IS_TEXT
-};
-
-QString profileToVcard(const lrc::api::profile::Info&, const QString&);
-uint64_t getTimeFromTimeStr(const QString&) noexcept;
-std::pair<msgFlag, uint64_t> migrateMessageBody(const QString&, const lrc::api::interaction::Type&);
-VectorString getPeerParticipantsForConversationId(lrc::Database&, const QString&, const QString&);
-void migrateAccountDb(const QString&,
-                      std::shared_ptr<lrc::Database>,
-                      std::shared_ptr<lrc::Database>);
-
-namespace interaction {
-
-static inline api::interaction::Type
-to_type(const QString& type)
-{
-    if (type == "TEXT")
-        return api::interaction::Type::TEXT;
-    else if (type == "CALL")
-        return api::interaction::Type::CALL;
-    else if (type == "CONTACT")
-        return api::interaction::Type::CONTACT;
-    else if (type == "OUTGOING_DATA_TRANSFER")
-        return api::interaction::Type::DATA_TRANSFER;
-    else if (type == "INCOMING_DATA_TRANSFER")
-        return api::interaction::Type::DATA_TRANSFER;
-    else
-        return api::interaction::Type::INVALID;
-}
-
-static inline QString
-to_migrated_status_string(const QString& status)
-{
-    if (status == "FAILED")
-        return "FAILURE";
-    else if (status == "SUCCEED")
-        return "SUCCESS";
-    else if (status == "READ")
-        return "SUCCESS";
-    else if (status == "UNREAD")
-        return "SUCCESS";
-    else
-        return status;
-}
-
-} // namespace interaction
-
-QString
-profileToVcard(const api::profile::Info& profileInfo, const QString& accountId = {})
-{
-    using namespace api;
-    bool compressedImage = std::strncmp(profileInfo.avatar.toStdString().c_str(), "/9g=", 4) == 0;
-    ;
-    QString vCardStr = vCard::Delimiter::BEGIN_TOKEN;
-    vCardStr += vCard::Delimiter::END_LINE_TOKEN;
-    vCardStr += vCard::Property::VERSION;
-    vCardStr += ":2.1";
-    vCardStr += vCard::Delimiter::END_LINE_TOKEN;
-    if (!accountId.isEmpty()) {
-        vCardStr += vCard::Property::UID;
-        vCardStr += ":";
-        vCardStr += accountId;
-        vCardStr += vCard::Delimiter::END_LINE_TOKEN;
-    }
-    vCardStr += vCard::Property::FORMATTED_NAME;
-    vCardStr += ":";
-    vCardStr += profileInfo.alias;
-    vCardStr += vCard::Delimiter::END_LINE_TOKEN;
-    if (profileInfo.type == profile::Type::JAMI) {
-        vCardStr += vCard::Property::TELEPHONE;
-        vCardStr += ":";
-        vCardStr += vCard::Delimiter::SEPARATOR_TOKEN;
-        vCardStr += "other:ring:";
-        vCardStr += profileInfo.uri;
-        vCardStr += vCard::Delimiter::END_LINE_TOKEN;
-    } else {
-        vCardStr += vCard::Property::TELEPHONE;
-        vCardStr += profileInfo.uri;
-        vCardStr += vCard::Delimiter::END_LINE_TOKEN;
-    }
-    vCardStr += vCard::Property::PHOTO;
-    vCardStr += vCard::Delimiter::SEPARATOR_TOKEN;
-    vCardStr += "ENCODING=BASE64";
-    vCardStr += vCard::Delimiter::SEPARATOR_TOKEN;
-    vCardStr += compressedImage ? "TYPE=JPEG:" : "TYPE=PNG:";
-    vCardStr += profileInfo.avatar;
-    vCardStr += vCard::Delimiter::END_LINE_TOKEN;
-    vCardStr += vCard::Delimiter::END_TOKEN;
-    return vCardStr;
-}
-
-uint64_t
-getTimeFromTimeStr(const QString& str) noexcept
-{
-    uint64_t minutes = 0, seconds = 0;
-    std::string timeStr = str.toStdString();
-    std::size_t delimiterPos = timeStr.find(":");
-    if (delimiterPos != std::string::npos) {
-        try {
-            minutes = std::stoull(timeStr.substr(0, delimiterPos));
-            seconds = std::stoull(timeStr.substr(delimiterPos + 1));
-        } catch (const std::exception&) {
-            return 0;
-        }
-    }
-    return minutes * 60 + seconds;
-}
-
-std::pair<msgFlag, uint64_t>
-migrateMessageBody(const QString& body, const api::interaction::Type& type)
-{
-    uint64_t duration {0};
-    // check in english and local to determine the direction of the call
-    static QString emo = "Missed outgoing call";
-    static QString lmo = QObject::tr("Missed outgoing call");
-    static QString eo = "Outgoing call";
-    static QString lo = QObject::tr("Outgoing call");
-    static QString eca = "Contact added";
-    static QString lca = QObject::tr("Contact added");
-    static QString eir = "Invitation received";
-    static QString lir = QObject::tr("Invitation received");
-    static QString eia = "Invitation accepted";
-    static QString lia = QObject::tr("Invitation accepted");
-    auto strBody = body.toStdString();
-    switch (type) {
-    case api::interaction::Type::CALL: {
-        bool en_missedOut = body.contains(emo);
-        bool en_out = body.contains(eo);
-        bool loc_missedOut = body.contains(lmo);
-        bool loc_out = body.contains(lo);
-        bool outgoingCall = en_missedOut || en_out || loc_missedOut || loc_out;
-        std::size_t dashPos = strBody.find("-");
-        if (dashPos != std::string::npos) {
-            duration = getTimeFromTimeStr(toQString(strBody.substr(dashPos + 2)));
-        }
-        return std::make_pair(msgFlag(outgoingCall), duration);
-    } break;
-    case api::interaction::Type::CONTACT:
-        if (body.contains(eca) || body.contains(lca)) {
-            return std::make_pair(msgFlag::IS_CONTACT_ADDED, 0);
-        } else if (body.contains(eir) || body.contains(lir)) {
-            return std::make_pair(msgFlag::IS_INVITATION_RECEIVED, 0);
-        } else if (body.contains(eia) || body.contains(lia)) {
-            return std::make_pair(msgFlag::IS_INVITATION_ACCEPTED, 0);
-        }
-        break;
-    case api::interaction::Type::INVALID:
-    case api::interaction::Type::TEXT:
-    case api::interaction::Type::DATA_TRANSFER:
-    case api::interaction::Type::COUNT__:
-    default:
-        return std::make_pair(msgFlag::IS_TEXT, 0);
-    }
-    return std::make_pair(msgFlag::IS_OUTGOING, 0);
-}
-
-VectorString
-getPeerParticipantsForConversationId(Database& db,
-                                     const QString& profileId,
-                                     const QString& conversationId)
-{
-    return db
-        .select("participant_id",
-                "conversations",
-                "id=:id AND participant_id!=:participant_id",
-                {{":id", conversationId}, {":participant_id", profileId}})
-        .payloads;
-}
-
-void
-migrateAccountDb(const QString& accountId,
-                 std::shared_ptr<Database> db,
-                 std::shared_ptr<Database> legacyDb)
-{
-    using namespace lrc::api;
-    using namespace migration;
-
-    auto accountLocalPath = getPath() + accountId + "/";
-
-    using namespace libjami::Account;
-    MapStringString accountDetails = ConfigurationManager::instance().getAccountDetails(
-        accountId.toStdString().c_str());
-    bool isRingAccount = accountDetails[ConfProperties::TYPE] == "RING";
-    std::map<QString, QString> profileIdUriMap;
-    std::map<QString, QString> convIdPeerUriMap;
-    QString accountProfileId;
-
-    // 1. profiles_accounts
-    // migrate account's avatar/alias from profiles table to {data_dir}/profile.vcf
-    QString accountUri;
-    if (isRingAccount) {
-        accountUri = accountDetails[libjami::Account::ConfProperties::USERNAME].contains("ring:")
-                         ? QString(accountDetails[libjami::Account::ConfProperties::USERNAME])
-                               .remove(QString("ring:"))
-                         : accountDetails[libjami::Account::ConfProperties::USERNAME];
-    } else {
-        accountUri = accountDetails[libjami::Account::ConfProperties::USERNAME];
-    }
-
-    auto accountProfileIds = legacyDb
-                                 ->select("profile_id",
-                                          "profiles_accounts",
-                                          "account_id=:account_id AND is_account=:is_account",
-                                          {{":account_id", accountId}, {":is_account", "true"}})
-                                 .payloads;
-    if (accountProfileIds.size() != 1) {
-        return;
-    }
-    accountProfileId = accountProfileIds[0];
-    auto accountProfile
-        = legacyDb->select("photo, alias", "profiles", "id=:id", {{":id", accountProfileId}})
-              .payloads;
-    profile::Info accountProfileInfo;
-    // if we can not find the uri in the database
-    // (in the case of poorly kept SIP account uris),
-    // than we cannot migrate the conversations and vcard
-    if (!accountProfile.empty()) {
-        accountProfileInfo = {accountUri,
-                              accountProfile[0],
-                              accountProfile[1],
-                              isRingAccount ? profile::Type::JAMI : profile::Type::SIP};
-    }
-    auto accountVcard = profileToVcard(accountProfileInfo, accountId);
-    QDir dir;
-    if (!dir.exists(accountLocalPath)) {
-        dir.mkpath(accountLocalPath);
-    }
-    auto profileFilePath = accountLocalPath + "profile.vcf";
-    QFile file(profileFilePath);
-    if (!file.open(QIODevice::WriteOnly)) {
-        throw std::runtime_error("Can't open file: " + profileFilePath.toStdString());
-    }
-    QTextStream(&file) << accountVcard;
-
-    // 2. profiles
-    // migrate profiles from profiles table to {data_dir}/{uri}.vcf
-    // - for JAMI, the scheme and the hostname is omitted
-    // - for SIP, the uri is must be stripped of prefix and port
-    // e.g. 3d1112ab2bb089370c0744a44bbbb0786418d40b.vcf
-    //      username.vcf or username@hostname.vcf
-
-    // only select non-account profiles
-    auto profileIds = legacyDb
-                          ->select("profile_id",
-                                   "profiles_accounts",
-                                   "account_id=:account_id AND is_account=:is_account",
-                                   {{":account_id", accountId}, {":is_account", "false"}})
-                          .payloads;
-    for (const auto& profileId : profileIds) {
-        auto profile = legacyDb
-                           ->select("uri, alias, photo, type",
-                                    "profiles",
-                                    "id=:id",
-                                    {{":id", profileId}})
-                           .payloads;
-        if (profile.empty()) {
-            continue;
-        }
-        profile::Info profileInfo {profile[0], profile[2], profile[1]};
-        auto uri = URI(profile[0]);
-        auto profileUri = uri.userinfo();
-        if (!isRingAccount && uri.hasHostname()) {
-            profileUri += "@" + uri.hostname();
-        }
-        // insert into map for use during the conversations table migration
-        profileIdUriMap.insert(std::make_pair(profileId, profileUri));
-        auto vcard = profileToVcard(profileInfo);
-        // make sure the directory exists
-        QDir dir(accountLocalPath + "profiles");
-        if (!dir.exists())
-            dir.mkpath(".");
-        profileFilePath = accountLocalPath + "profiles/" + profileUri + ".vcf";
-        QFile file(profileFilePath);
-        // if we catch duplicates here, skip the profile because
-        // the previous db structure does not guarantee unique uris
-        if (file.exists()) {
-            qWarning() << "Profile file already exits: " << profileFilePath;
-            continue;
-        }
-        if (!file.open(QIODevice::WriteOnly)) {
-            qWarning() << "Can't open file: " << profileFilePath;
-            continue;
-        }
-        QTextStream(&file) << vcard;
-    }
-
-    // 3. conversations
-    // migrate old conversations table ==> new conversations table
-    // a) participant_id INTEGER becomes participant TEXT (the uri of the participant)
-    //    use the selected non-account profiles
-    auto conversationIds = legacyDb
-                               ->select("id",
-                                        "conversations",
-                                        "participant_id=:participant_id",
-                                        {{":participant_id", accountProfileId}})
-                               .payloads;
-    if (conversationIds.empty()) {
-        return;
-    }
-    for (auto conversationId : conversationIds) {
-        // only one peer pre-groupchat
-        auto peerProfileId = getPeerParticipantsForConversationId(*legacyDb,
-                                                                  accountProfileId,
-                                                                  conversationId);
-        if (peerProfileId.empty()) {
-            continue;
-        }
-        auto it = profileIdUriMap.find(peerProfileId.at(0));
-        // we cannot insert in the conversations table without a uri
-        if (it == profileIdUriMap.end()) {
-            continue;
-        }
-        convIdPeerUriMap.insert(std::make_pair(conversationId, it->second));
-        try {
-            db->insertInto("conversations",
-                           {{":id", "id"}, {":participant", "participant"}},
-                           {{":id", conversationId}, {":participant", it->second}});
-        } catch (const std::runtime_error& e) {
-            qWarning() << "Couldn't migrate conversation: " << e.what();
-            continue;
-        }
-    }
-
-    // 4. interactions
-    auto allInteractions = legacyDb->select("account_id, author_id, conversation_id, \
-         timestamp, body, type, status, daemon_id",
-                                            "interactions",
-                                            "account_id=:account_id",
-                                            {{":account_id", accountProfileId}});
-    auto interactionIt = allInteractions.payloads.begin();
-    while (interactionIt != allInteractions.payloads.end()) {
-        auto author_id = *(interactionIt + 1);
-        auto convId = *(interactionIt + 2);
-        auto timestamp = *(interactionIt + 3);
-        auto body = *(interactionIt + 4);
-        auto type = interaction::to_type(*(interactionIt + 5));
-        auto statusStr = *(interactionIt + 6);
-        auto daemonId = *(interactionIt + 7);
-
-        auto it = profileIdUriMap.find(author_id);
-        if (it == profileIdUriMap.end() && author_id != accountProfileId) {
-            std::advance(interactionIt, allInteractions.nbrOfCols);
-            continue;
-        }
-        // migrate body+type ==> msgFlag+duration
-        auto migratedMsg = migrateMessageBody(body, type);
-        QString profileUri = it == profileIdUriMap.end() ? "" : it->second;
-        // clear author uri if outgoing
-        switch (migratedMsg.first) {
-        case msgFlag::IS_OUTGOING:
-        case msgFlag::IS_CONTACT_ADDED:
-            profileUri.clear();
-            break;
-        case msgFlag::IS_INCOMING:
-        case msgFlag::IS_INVITATION_RECEIVED:
-        case msgFlag::IS_INVITATION_ACCEPTED: {
-            // try to set profile uri using the conversation id
-            auto it = convIdPeerUriMap.find(convId);
-            if (it == convIdPeerUriMap.end()) {
-                std::advance(interactionIt, allInteractions.nbrOfCols);
-                continue;
-            }
-            profileUri = it->second;
-            break;
-        }
-        case msgFlag::IS_TEXT:
-        default:
-            break;
-        }
-        // Set all read, call and datatransfer, and contact added
-        // interactions to a read state
-        bool is_read = statusStr != "UNREAD" || type == api::interaction::Type::CALL
-                       || type == api::interaction::Type::CONTACT;
-        // migrate status
-        if (migratedMsg.first == msgFlag::IS_INVITATION_RECEIVED) {
-            statusStr = "UNKNOWN";
-        }
-        QString extra_data = migratedMsg.second == 0
-                                 ? ""
-                                 : JSONStringFromInitList(
-                                     {qMakePair(QString("duration"),
-                                                QJsonValue(QString::number(migratedMsg.second)))});
-        if (accountUri == profileUri)
-            profileUri.clear();
-        auto typeStr = api::interaction::to_string(type);
-        try {
-            db->insertInto("interactions",
-                           {{":author", "author"},
-                            {":conversation", "conversation"},
-                            {":timestamp", "timestamp"},
-                            {":body", "body"},
-                            {":type", "type"},
-                            {":status", "status"},
-                            {":is_read", "is_read"},
-                            {":daemon_id", "daemon_id"},
-                            {":extra_data", "extra_data"}},
-                           {{":author", profileUri},
-                            {":conversation", convId},
-                            {":timestamp", timestamp},
-                            {migratedMsg.first != msgFlag::IS_TEXT ? "" : ":body", body},
-                            {":type", api::interaction::to_string(type)},
-                            {":status", interaction::to_migrated_status_string(statusStr)},
-                            {":is_read", is_read ? "1" : "0"},
-                            {daemonId.isEmpty() ? "" : ":daemon_id", daemonId},
-                            {extra_data.isEmpty() ? "" : ":extra_data", extra_data}});
-        } catch (const std::runtime_error& e) {
-            qWarning() << e.what();
-        }
-        std::advance(interactionIt, allInteractions.nbrOfCols);
-    }
-    qDebug() << "Done";
-}
-
-} // namespace migration
-
-std::vector<std::shared_ptr<Database>>
-migrateIfNeeded(const QStringList& accountIds, MigrationCb& willMigrateCb, MigrationCb& didMigrateCb)
-{
-    using namespace lrc::api;
-    using namespace migration;
-
-    std::vector<std::shared_ptr<Database>> dbs(accountIds.size());
-
-    if (!accountIds.size()) {
-        qDebug() << "No accounts to migrate";
-        return dbs;
-    }
-
-    auto appPath = getPath();
-
-    // ring -> jami path migration
-    QDir dataDir(appPath);
-    // create data directory if not created yet
-    dataDir.mkpath(appPath);
-    QDir oldDataDir(appPath);
-    oldDataDir.cdUp();
-    oldDataDir = oldDataDir.absolutePath()
-#if defined(_WIN32)
-                 + "/Savoir-faire Linux/Ring";
-#elif defined(__APPLE__)
-                 + "/ring";
-#else
-                 + "/gnome-ring";
-#endif
-    QStringList filesList = oldDataDir.entryList();
-    QString filename;
-    QDir dir;
-    bool success = true;
-    Q_FOREACH (filename, filesList) {
-        qDebug() << "Migrate " << oldDataDir.absolutePath() << "/" << filename << " to "
-                 << dataDir.absolutePath() + "/" + filename;
-        if (filename != "." && filename != "..") {
-            success &= dir.rename(oldDataDir.absolutePath() + "/" + filename,
-                                  dataDir.absolutePath() + "/" + filename);
-        }
-    }
-    if (success) {
-        // Remove old directory if the migration is successful.
-#if defined(_WIN32)
-        oldDataDir.cdUp();
-#endif
-        oldDataDir.removeRecursively();
-    }
-
-    bool needsMigration = false;
-    std::map<QString, bool> hasMigratedData;
-    for (const auto& accountId : accountIds) {
-        auto hasMigratedDb = QFile(appPath + accountId + "/history.db").exists()
-                             && !QFile(appPath + accountId + "/history.db-journal").exists();
-        hasMigratedData.insert(std::make_pair(accountId, hasMigratedDb));
-        needsMigration |= !hasMigratedDb;
-    }
-    if (!needsMigration) {
-        // if there's any lingering pre-migration data, remove it
-        QFile(dataDir.absoluteFilePath("ring.db")).remove();
-        QDir(dataDir.absoluteFilePath("text/")).removeRecursively();
-        QDir(dataDir.absoluteFilePath("profiles/")).removeRecursively();
-        QDir(dataDir.absoluteFilePath("peer_profiles/")).removeRecursively();
-        qDebug() << "No migration required";
-        return dbs;
-    }
-
-    // A fairly long migration may now occur
-    std::thread migrateThread(
-        [&appPath, &accountIds, &dbs, &didMigrateCb, &dataDir, &hasMigratedData] {
-            // 1. migrate old lrc -> new lrc if needed
-            // 2. migrate new lrc db version 1 -> db version 1.1 if needed
-            // the destructor of LegacyDatabase will remove 'ring.db' and clean out
-            // old lrc files
-            std::shared_ptr<Database> legacyDb;
-            try {
-                legacyDb = lrc::DatabaseFactory::create<LegacyDatabase>(appPath);
-            } catch (const std::runtime_error& e) {
-                qDebug() << "Exception while attempting to load legacy database: " << e.what();
-                if (didMigrateCb)
-                    didMigrateCb();
-                return;
-            }
-
-            // attempt to make a backup of ring.db
-            {
-                QFile dbFile(dataDir.absoluteFilePath("ring.db"));
-                if (dbFile.open(QIODevice::ReadOnly)) {
-                    dbFile.copy(appPath + "ring.db.bak");
-                }
-            }
-
-            // 3. migrate db version 1.1 -> per account dbs version 1
-            int index = 0;
-            for (const auto& accountId : accountIds) {
-                if (hasMigratedData.at(accountId)) {
-                    index++;
-                    continue;
-                }
-                qDebug() << "Migrating account: " << accountId << "...";
-                // try to remove the transaction journal from a failed migration
-                QFile(appPath + accountId + "/history.db-journal").remove();
-                try {
-                    QSqlDatabase::database().transaction();
-                    auto dbName = QString::fromStdString(accountId.toStdString() + "/history");
-                    dbs.at(index) = lrc::DatabaseFactory::create<Database>(dbName, appPath);
-                    auto& db = dbs.at(index++);
-                    migration::migrateAccountDb(accountId, db, legacyDb);
-                    QSqlDatabase::database().commit();
-                } catch (const std::runtime_error& e) {
-                    qWarning().noquote() << "Could not migrate database for account: " << accountId
-                                         << "\n " << e.what();
-                    QSqlDatabase::database().rollback();
-                }
-            }
-
-            // done
-            if (didMigrateCb)
-                didMigrateCb();
-        });
-
-    // if willMigrateCb blocks, it must be unblocked by didMigrateCb
-    if (willMigrateCb)
-        willMigrateCb();
-
-    migrateThread.join();
-
-    return dbs;
-}
-
 } // namespace storage
 
 } // namespace authority
diff --git a/src/libclient/authority/storagehelper.h b/src/libclient/authority/storagehelper.h
index 01486eca56f58896791b87e302869a913425d0c1..b649ae331edb443d663cc5b9f9cf45f50f0137ff 100644
--- a/src/libclient/authority/storagehelper.h
+++ b/src/libclient/authority/storagehelper.h
@@ -346,9 +346,7 @@ uint64_t getLastTimestamp(Database& db);
  * @param willMigrateCb to invoke when migration will occur
  * @param didMigrateCb to invoke when migration has completed
  */
-std::vector<std::shared_ptr<Database>> migrateIfNeeded(const QStringList& accountIds,
-                                                       MigrationCb& willMigrateCb,
-                                                       MigrationCb& didMigrateCb);
+std::vector<std::shared_ptr<Database>> migrateIfNeeded(const QStringList& accountIds);
 
 } // namespace storage
 
diff --git a/src/libclient/database.cpp b/src/libclient/database.cpp
index 1d9f996687185c56dc1fb13590f0f9ae6f08263b..6c3c252b38eaa2074d1a4df1527fe3ed45f511a3 100644
--- a/src/libclient/database.cpp
+++ b/src/libclient/database.cpp
@@ -191,7 +191,9 @@ Database::migrateIfNeeded()
 void
 Database::migrateFromVersion(const QString& currentVersion)
 {
-    (void) currentVersion;
+    // If we ever have a new version, we can migrate the database here.
+    LC_WARN << "Database migration from version " << currentVersion << " to " << version_
+            << " not implemented";
 }
 
 void
@@ -471,547 +473,4 @@ Database::QueryTruncateError::details()
     return qts.readAll();
 }
 
-/*****************************************************************************
- *                                                                           *
- *                               LegacyDatabase                              *
- *                                                                           *
- ****************************************************************************/
-LegacyDatabase::LegacyDatabase(const QString& basePath)
-    : Database("ring", basePath)
-{
-    version_ = LEGACY_DB_VERSION;
-}
-
-LegacyDatabase::~LegacyDatabase()
-{
-    remove();
-    // remove old LRC files
-    QDir(basePath_ + "text/").removeRecursively();
-    QDir(basePath_ + "profiles/").removeRecursively();
-    QDir(basePath_ + "peer_profiles/").removeRecursively();
-}
-
-void
-LegacyDatabase::load()
-{
-    // open the database.
-    if (not db_.open()) {
-        std::stringstream ss;
-        ss << "cannot open database: " << connectionName_.toStdString();
-        throw std::runtime_error(ss.str());
-    }
-
-    // if db is empty we create them.
-    if (db_.tables().empty()) {
-        try {
-            QSqlDatabase::database(connectionName_).transaction();
-            createTables();
-            QSqlDatabase::database(connectionName_).commit();
-        } catch (QueryError& e) {
-            QSqlDatabase::database(connectionName_).rollback();
-            throw std::runtime_error("Could not correctly create the database");
-        }
-        migrateOldFiles();
-    } else {
-        migrateIfNeeded();
-    }
-}
-
-void
-LegacyDatabase::createTables()
-{
-    QSqlQuery query(db_);
-
-    auto tableProfiles = "CREATE TABLE profiles (id INTEGER PRIMARY KEY,  \
-                                                 uri TEXT NOT NULL,       \
-                                                 alias TEXT,              \
-                                                 photo TEXT,              \
-                                                 type TEXT,               \
-                                                 status TEXT)";
-
-    auto tableConversations = "CREATE TABLE conversations (id INTEGER,\
-                                                           participant_id INTEGER, \
-                                                           FOREIGN KEY(participant_id) REFERENCES profiles(id))";
-
-    auto tableInteractions = "CREATE TABLE interactions (id INTEGER PRIMARY KEY,\
-                                                         account_id INTEGER, \
-                                                         author_id INTEGER, \
-                                                         conversation_id INTEGER, \
-                                                         timestamp INTEGER, \
-                                                         body TEXT,     \
-                                                         type TEXT,  \
-                                                         status TEXT, \
-                                                         daemon_id TEXT, \
-                                                         FOREIGN KEY(account_id) REFERENCES profiles(id), \
-                                                         FOREIGN KEY(author_id) REFERENCES profiles(id), \
-                                                         FOREIGN KEY(conversation_id) REFERENCES conversations(id))";
-
-    auto tableProfileAccounts
-        = "CREATE TABLE profiles_accounts (profile_id INTEGER NOT NULL,                    \
-                                                                 account_id TEXT NOT NULL,                        \
-                                                                 is_account TEXT,                                 \
-                                                                 FOREIGN KEY(profile_id) REFERENCES profiles(id))";
-    // add profiles table
-    if (not db_.tables().contains("profiles", Qt::CaseInsensitive)
-        and not query.exec(tableProfiles)) {
-        throw QueryError(std::move(query));
-    }
-
-    // add conversations table
-    if (not db_.tables().contains("conversations", Qt::CaseInsensitive)
-        and not query.exec(tableConversations)) {
-        throw QueryError(std::move(query));
-    }
-
-    // add interactions table
-    if (not db_.tables().contains("interactions", Qt::CaseInsensitive)
-        and not query.exec(tableInteractions)) {
-        throw QueryError(std::move(query));
-    }
-
-    // add profiles accounts table
-    if (not db_.tables().contains("profiles_accounts", Qt::CaseInsensitive)
-        and not query.exec(tableProfileAccounts)) {
-        throw QueryError(std::move(query));
-    }
-
-    storeVersion(version_);
-}
-
-void
-LegacyDatabase::migrateOldFiles()
-{
-    migrateLocalProfiles();
-    migratePeerProfiles();
-    migrateTextHistory();
-    linkRingProfilesWithAccounts(true);
-}
-
-void
-LegacyDatabase::migrateLocalProfiles()
-{
-    const QDir profilesDir = basePath_ + "profiles/";
-    const QStringList entries = profilesDir.entryList({QStringLiteral("*.vcf")}, QDir::Files);
-    Q_FOREACH (const QString& item, entries) {
-        auto filePath = profilesDir.path() + '/' + item;
-        QString content;
-        QFile file(filePath);
-        if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
-            content = QString::fromUtf8(file.readAll());
-        } else {
-            qWarning() << "Could not open .vcf file";
-            continue;
-        }
-
-        const auto vCard = lrc::vCard::utils::toHashMap(content.toUtf8());
-        const auto alias = vCard[lrc::vCard::Property::FORMATTED_NAME];
-        const auto avatar = vCard["PHOTO;ENCODING=BASE64;TYPE=PNG"];
-
-        const QStringList accountIds = ConfigurationManager::instance().getAccountList();
-        for (auto accountId : accountIds) {
-            // NOTE: If the daemon is down, but dbus answered, id can contains
-            // "Remote peer disconnected", "The name is not activable", etc.
-            // So avoid to migrate useless directories.
-            for (auto& id : accountIds)
-                if (id.indexOf(" ") != -1) {
-                    qWarning() << "Invalid dbus answer. Daemon not running";
-                    return;
-                }
-            MapStringString account = ConfigurationManager::instance().getAccountDetails(
-                accountId.toStdString().c_str());
-            auto accountURI
-                = account[libjami::Account::ConfProperties::USERNAME].contains("ring:")
-                      ? account[libjami::Account::ConfProperties::USERNAME].toStdString().substr(
-                          std::string("ring:").size())
-                      : account[libjami::Account::ConfProperties::USERNAME].toStdString();
-
-            for (const auto& accountId : accountIds) {
-                MapStringString account = ConfigurationManager::instance().getAccountDetails(
-                    accountId.toStdString().c_str());
-                auto type = account[libjami::Account::ConfProperties::TYPE] == "SIP" ? "SIP"
-                                                                                     : "RING";
-
-                auto uri = account[libjami::Account::ConfProperties::USERNAME].contains("ring:")
-                               ? QString(account[libjami::Account::ConfProperties::USERNAME])
-                                     .remove(0, QString("ring:").size())
-                               : account[libjami::Account::ConfProperties::USERNAME];
-                if (select("id", "profiles", "uri=:uri", {{":uri", uri}}).payloads.empty()) {
-                    insertInto("profiles",
-                               {{":uri", "uri"},
-                                {":alias", "alias"},
-                                {":photo", "photo"},
-                                {":type", "type"},
-                                {":status", "status"}},
-                               {{":uri", uri},
-                                {":alias", alias},
-                                {":photo", avatar},
-                                {":type", type},
-                                {":status", "TRUSTED"}});
-                    auto profileIds = select("id", "profiles", "uri=:uri", {{":uri", uri}}).payloads;
-                    if (!profileIds.empty()
-                        && select("profile_id",
-                                  "profiles_accounts",
-                                  "account_id=:account_id AND is_account=:is_account",
-                                  {{":account_id", accountId}, {":is_account", "true"}})
-                               .payloads.empty()) {
-                        insertInto("profiles_accounts",
-                                   {{":profile_id", "profile_id"},
-                                    {":account_id", "account_id"},
-                                    {":is_account", "is_account"}},
-                                   {{":profile_id", profileIds[0]},
-                                    {":account_id", accountId},
-                                    {":is_account", "true"}});
-                    }
-                }
-            }
-        }
-    }
-}
-
-void
-LegacyDatabase::migratePeerProfiles()
-{
-    const QDir profilesDir = basePath_ + "peer_profiles/";
-
-    const QStringList entries = profilesDir.entryList({QStringLiteral("*.vcf")}, QDir::Files);
-
-    Q_FOREACH (const QString& item, entries) {
-        auto filePath = profilesDir.path() + '/' + item;
-        QString content;
-        QFile file(filePath);
-        if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
-            content = QString::fromUtf8(file.readAll());
-        } else {
-            qWarning() << "Could not open vcf file";
-            continue;
-        }
-
-        const auto vCard = lrc::vCard::utils::toHashMap(content.toUtf8());
-        auto uri = vCard["TEL;other"];
-        const auto alias = vCard["FN"];
-        const auto avatar = vCard["PHOTO;ENCODING=BASE64;TYPE=PNG"];
-        const QString type = uri.startsWith("ring:") ? "RING" : "SIP";
-        if (uri.startsWith("ring:")) {
-            uri = uri.mid(QString("ring:").size());
-        }
-
-        if (select("id", "profiles", "uri=:uri", {{":uri", uri}}).payloads.empty()) {
-            insertInto("profiles",
-                       {{":uri", "uri"},
-                        {":alias", "alias"},
-                        {":photo", "photo"},
-                        {":type", "type"},
-                        {":status", "status"}},
-                       {{":uri", uri},
-                        {":alias", alias},
-                        {":photo", avatar},
-                        {":type", type},
-                        {":status", "TRUSTED"}});
-        }
-    }
-}
-
-void
-LegacyDatabase::migrateTextHistory()
-{
-    // load all text recordings so we can recover CMs that are not in the call history
-    QDir dir(basePath_ + "text/");
-    if (dir.exists()) {
-        // get .json files, sorted by time, latest first
-        QStringList filters;
-        filters << "*.json";
-        auto list = dir.entryInfoList(filters,
-                                      QDir::Files | QDir::NoSymLinks | QDir::Readable,
-                                      QDir::Time);
-
-        for (int i = 0; i < list.size(); ++i) {
-            QFileInfo fileInfo = list.at(i);
-
-            QString content;
-            QFile file(fileInfo.absoluteFilePath());
-            if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
-                content = QString::fromUtf8(file.readAll());
-            } else {
-                qWarning() << "Could not open text recording json file";
-                continue;
-            }
-
-            if (!content.isEmpty()) {
-                QJsonParseError err;
-                auto loadDoc = QJsonDocument::fromJson(content.toUtf8(), &err).object();
-
-                if (loadDoc.find("peers") == loadDoc.end())
-                    continue;
-                if (loadDoc.find("groups") == loadDoc.end())
-                    continue;
-                // Load account
-                auto peersObject = loadDoc["peers"].toArray()[0].toObject();
-
-                MapStringString details = ConfigurationManager::instance().getAccountDetails(
-                    peersObject["accountId"].toString());
-                if (!details.contains(libjami::Account::ConfProperties::USERNAME))
-                    continue;
-
-                auto accountUri = details[libjami::Account::ConfProperties::USERNAME];
-                auto isARingContact = accountUri.startsWith("ring:");
-                if (isARingContact) {
-                    accountUri = accountUri.mid(QString("ring:").length());
-                }
-                auto accountIds = select("id", "profiles", "uri=:uri", {{":uri", accountUri}})
-                                      .payloads;
-                auto contactIds = select("id",
-                                         "profiles",
-                                         "uri=:uri",
-                                         {{":uri", peersObject["uri"].toString()}})
-                                      .payloads;
-                if (contactIds.empty()) {
-                    insertInto("profiles",
-                               {{":uri", "uri"},
-                                {":alias", "alias"},
-                                {":photo", "photo"},
-                                {":type", "type"},
-                                {":status", "status"}},
-                               {{":uri", peersObject["uri"].toString()},
-                                {":alias", ""},
-                                {":photo", ""},
-                                {":type", "RING"},
-                                {":status", "TRUSTED"}});
-                    // NOTE: this profile is in a case where it's not a contact for the daemon but a
-                    // conversation with an account. So we choose to add the profile to daemon's contacts
-                    if (isARingContact) {
-                        ConfigurationManager::instance()
-                            .addContact(peersObject["accountId"].toString(),
-                                        peersObject["uri"].toString());
-                    }
-                    contactIds = select("id",
-                                        "profiles",
-                                        "uri=:uri",
-                                        {{":uri", peersObject["uri"].toString()}})
-                                     .payloads;
-                }
-                if (accountIds.empty()) {
-                    qDebug() << "Can't find profile for URI: "
-                             << peersObject["accountId"].toString() << ". Ignore this file.";
-                } else if (contactIds.empty()) {
-                    qDebug() << "Can't find profile for URI: " << peersObject["uri"].toString()
-                             << ". Ignore this file.";
-                } else {
-                    auto contactId = contactIds[0];
-                    // link profile id to account id
-                    auto profiles = select("profile_id",
-                                           "profiles_accounts",
-                                           "profile_id=:profile_id AND \
-                                            account_id=:account_id AND  \
-                                            is_account=:is_account",
-                                           {{":profile_id", contactId},
-                                            {":account_id", peersObject["accountId"].toString()},
-                                            {":is_account", "false"}})
-                                        .payloads;
-
-                    if (profiles.empty()) {
-                        insertInto("profiles_accounts",
-                                   {{":profile_id", "profile_id"},
-                                    {":account_id", "account_id"},
-                                    {":is_account", "is_account"}},
-                                   {{":profile_id", contactId},
-                                    {":account_id", peersObject["accountId"].toString()},
-                                    {":is_account", "false"}});
-                    }
-                    auto accountId = accountIds[0];
-                    auto newConversationsId
-                        = select("IFNULL(MAX(id), 0) + 1", "conversations", "1=1", {}).payloads[0];
-                    try {
-                        QSqlDatabase::database().transaction();
-                        insertInto("conversations",
-                                   {{":id", "id"}, {":participant_id", "participant_id"}},
-                                   {{":id", newConversationsId}, {":participant_id", accountId}});
-                        insertInto("conversations",
-                                   {{":id", "id"}, {":participant_id", "participant_id"}},
-                                   {{":id", newConversationsId}, {":participant_id", contactId}});
-                        QSqlDatabase::database().commit();
-                    } catch (QueryInsertError& e) {
-                        qDebug() << e.details();
-                        QSqlDatabase::database().rollback();
-                    }
-
-                    // Load interactions
-                    auto groupsArray = loadDoc["groups"].toArray();
-                    for (const auto& groupObject : groupsArray) {
-                        auto messagesArray = groupObject.toObject()["messages"].toArray();
-                        for (const auto& messageRef : messagesArray) {
-                            auto messageObject = messageRef.toObject();
-                            auto direction = messageObject["direction"].toInt();
-                            auto body = messageObject["payloads"]
-                                            .toArray()[0]
-                                            .toObject()["payload"]
-                                            .toString();
-                            insertInto("interactions",
-                                       {{":account_id", "account_id"},
-                                        {":author_id", "author_id"},
-                                        {":conversation_id", "conversation_id"},
-                                        {":timestamp", "timestamp"},
-                                        {":body", "body"},
-                                        {":type", "type"},
-                                        {":status", "status"}},
-                                       {{":account_id", accountId},
-                                        {":author_id", direction ? accountId : contactId},
-                                        {":conversation_id", newConversationsId},
-                                        {":timestamp", messageObject["timestamp"].toString()},
-                                        {":body", body},
-                                        {":type", "TEXT"},
-                                        {":status", direction ? "SUCCEED" : "READ"}});
-                        }
-                    }
-                }
-            } else {
-                qWarning() << "Text recording file is empty";
-            }
-        }
-    }
-}
-
-void
-LegacyDatabase::migrateFromVersion(const QString& currentVersion)
-{
-    if (currentVersion == "1") {
-        migrateSchemaFromVersion1();
-    }
-}
-
-void
-LegacyDatabase::migrateSchemaFromVersion1()
-{
-    QSqlQuery query(db_);
-    auto tableProfileAccounts
-        = "CREATE TABLE profiles_accounts (profile_id INTEGER NOT NULL,                     \
-                                                                 account_id TEXT NOT NULL,                        \
-                                                                 is_account TEXT,                                 \
-                                                                 FOREIGN KEY(profile_id) REFERENCES profiles(id))";
-    // add profiles accounts table
-    if (not db_.tables().contains("profiles_accounts", Qt::CaseInsensitive)
-        and not query.exec(tableProfileAccounts)) {
-        throw QueryError(std::move(query));
-    }
-    linkRingProfilesWithAccounts(false);
-}
-
-void
-LegacyDatabase::linkRingProfilesWithAccounts(bool contactsOnly)
-{
-    const QStringList accountIds = ConfigurationManager::instance().getAccountList();
-    for (auto accountId : accountIds) {
-        // NOTE: If the daemon is down, but dbus answered, id can contains
-        // "Remote peer disconnected", "The name is not activable", etc.
-        // So avoid to migrate useless directories.
-        for (auto& id : accountIds)
-            if (id.indexOf(" ") != -1) {
-                qWarning() << "Invalid dbus answer. Daemon not running";
-                return;
-            }
-        MapStringString account = ConfigurationManager::instance().getAccountDetails(
-            accountId.toStdString().c_str());
-        auto accountURI = account[libjami::Account::ConfProperties::USERNAME].contains("ring:")
-                              ? QString(account[libjami::Account::ConfProperties::USERNAME])
-                                    .remove(0, QString("ring:").size())
-                              : account[libjami::Account::ConfProperties::USERNAME];
-        auto profileIds = select("id", "profiles", "uri=:uri", {{":uri", accountURI}}).payloads;
-        if (profileIds.empty()) {
-            continue;
-        }
-        if (!contactsOnly) {
-            // if is_account is true we should have only one profile id for account id
-            if (select("profile_id",
-                       "profiles_accounts",
-                       "account_id=:account_id AND is_account=:is_account",
-                       {{":account_id", accountId}, {":is_account", "true"}})
-                    .payloads.empty()) {
-                insertInto("profiles_accounts",
-                           {{":profile_id", "profile_id"},
-                            {":account_id", "account_id"},
-                            {":is_account", "is_account"}},
-                           {{":profile_id", profileIds[0]},
-                            {":account_id", accountId},
-                            {":is_account", "true"}});
-            }
-        }
-
-        if (account[libjami::Account::ConfProperties::TYPE]
-            == libjami::Account::ProtocolNames::RING) {
-            // update RING contacts
-            const VectorMapStringString& contacts_vector
-                = ConfigurationManager::instance().getContacts(accountId.toStdString().c_str());
-            // update contacts profiles
-            for (auto contact_info : contacts_vector) {
-                auto contactURI = contact_info["id"];
-                updateProfileAccountForContact(contactURI, accountId);
-            }
-            // update pending contacts profiles
-            const VectorMapStringString& pending_tr
-                = ConfigurationManager::instance().getTrustRequests(accountId.toStdString().c_str());
-            for (auto tr_info : pending_tr) {
-                auto contactURI = tr_info[libjami::Account::TrustRequest::FROM];
-                updateProfileAccountForContact(contactURI, accountId);
-            }
-        } else if (account[libjami::Account::ConfProperties::TYPE]
-                   == libjami::Account::ProtocolNames::SIP) {
-            // update SIP contacts
-            auto conversations = select("id",
-                                        "conversations",
-                                        "participant_id=:participant_id",
-                                        {{":participant_id", profileIds[0]}})
-                                     .payloads;
-            for (const auto& c : conversations) {
-                auto otherParticipants = select("participant_id",
-                                                "conversations",
-                                                "id=:id AND participant_id!=:participant_id",
-                                                {{":id", c}, {":participant_id", profileIds[0]}})
-                                             .payloads;
-                for (const auto& participant : otherParticipants) {
-                    auto rows = select("profile_id",
-                                       "profiles_accounts",
-                                       "profile_id=:profile_id AND \
-                                        account_id=:account_id AND  \
-                                        is_account=:is_account",
-                                       {{":profile_id", participant},
-                                        {":account_id", accountId},
-                                        {":is_account", "false"}})
-                                    .payloads;
-                    if (rows.empty()) {
-                        insertInto("profiles_accounts",
-                                   {{":profile_id", "profile_id"},
-                                    {":account_id", "account_id"},
-                                    {":is_account", "is_account"}},
-                                   {{":profile_id", participant},
-                                    {":account_id", accountId},
-                                    {":is_account", "false"}});
-                    }
-                }
-            }
-        }
-    }
-}
-
-void
-LegacyDatabase::updateProfileAccountForContact(const QString& contactURI, const QString& accountId)
-{
-    auto profileIds = select("id", "profiles", "uri=:uri", {{":uri", contactURI}}).payloads;
-    if (profileIds.empty()) {
-        return;
-    }
-    auto rows = select("profile_id",
-                       "profiles_accounts",
-                       "account_id=:account_id AND is_account=:is_account",
-                       {{":account_id", accountId}, {":is_account", "false"}})
-                    .payloads;
-    if (std::find(rows.begin(), rows.end(), profileIds[0]) == rows.end()) {
-        insertInto("profiles_accounts",
-                   {{":profile_id", "profile_id"},
-                    {":account_id", "account_id"},
-                    {":is_account", "is_account"}},
-                   {{":profile_id", profileIds[0]},
-                    {":account_id", accountId},
-                    {":is_account", "false"}});
-    }
-}
-
 } // namespace lrc
diff --git a/src/libclient/database.h b/src/libclient/database.h
index 6289ce1f73a97295d13a4fa24a31e88c7692d4f6..9f43bfb8938d7bc49ed0fbfcad444cdc488b9dd7 100644
--- a/src/libclient/database.h
+++ b/src/libclient/database.h
@@ -277,61 +277,4 @@ protected:
     QSqlDatabase db_;
 };
 
-/**
- *  @brief A legacy database to help migrate from the single db epoch.
- *  @note not thread safe.
- */
-class LegacyDatabase final : public Database
-{
-    Q_OBJECT
-
-public:
-    /**
-     * Create a migratory legacy database.
-     * @exception QueryError database query error.
-     */
-    LegacyDatabase(const QString& basePath);
-    ~LegacyDatabase();
-
-    void load() override;
-
-protected:
-    void createTables() override;
-
-private:
-    /**
-     * Migration helpers from old LRC. Parse JSON for history and VCards and add it into the database.
-     */
-    void migrateOldFiles();
-    void migrateLocalProfiles();
-    void migratePeerProfiles();
-    void migrateTextHistory();
-
-    void migrateFromVersion(const QString& version) override;
-
-    /**
-     * Migration helpers from version 1
-     */
-    void migrateSchemaFromVersion1();
-    void linkRingProfilesWithAccounts(bool contactsOnly);
-    void updateProfileAccountForContact(const QString& contactURI, const QString& accountID);
-};
-
-namespace DatabaseFactory {
-template<typename T, class... Args>
-std::enable_if_t<std::is_constructible<T, Args...>::value, std::shared_ptr<Database>>
-create(Args&&... args)
-{
-    auto pdb = std::static_pointer_cast<Database>(std::make_shared<T>(std::forward<Args>(args)...));
-    // To allow override of the db load method we don't
-    // call it from the constructor.
-    try {
-        pdb->load();
-    } catch (const std::runtime_error& e) {
-        throw std::runtime_error(e);
-    }
-    return pdb;
-}
-} // namespace DatabaseFactory
-
 } // namespace lrc
diff --git a/src/libclient/lrc.cpp b/src/libclient/lrc.cpp
index 4338c4772b57cc7dde83cd3826cf5f8210993dc7..e486e5cbf57f61979ede857bcd7ec5777749d098 100644
--- a/src/libclient/lrc.cpp
+++ b/src/libclient/lrc.cpp
@@ -31,14 +31,12 @@
 #include "api/avmodel.h"
 #include "api/pluginmodel.h"
 #include "api/behaviorcontroller.h"
-#include "api/datatransfermodel.h"
 #include "api/accountmodel.h"
 #include "callbackshandler.h"
 #include "dbus/callmanager.h"
 #include "dbus/configurationmanager.h"
 #include "dbus/instancemanager.h"
 #include "dbus/configurationmanager.h"
-#include "authority/storagehelper.h"
 
 Q_LOGGING_CATEGORY(libclientLog, "libclient")
 
@@ -54,7 +52,7 @@ bool isFinished(const QString& callState);
 class LrcPimpl
 {
 public:
-    LrcPimpl(Lrc& linked, MigrationCb& willMigrateCb, MigrationCb& didMigrateCb);
+    LrcPimpl(Lrc& linked);
 
     const Lrc& linked;
     std::unique_ptr<BehaviorController> behaviorController;
@@ -64,7 +62,7 @@ public:
     std::unique_ptr<PluginModel> PluginModel_;
 };
 
-Lrc::Lrc(MigrationCb willDoMigrationCb, MigrationCb didDoMigrationCb, bool muteDaemon)
+Lrc::Lrc(bool muteDaemon)
 {
     lrc::api::Lrc::holdConferences.store(true);
 #ifndef ENABLE_LIBWRAP
@@ -79,7 +77,7 @@ Lrc::Lrc(MigrationCb willDoMigrationCb, MigrationCb didDoMigrationCb, bool muteD
     // Ensure Daemon is running/loaded (especially on non-DBus platforms)
     // before instantiating LRC and its members
     InstanceManager::instance(muteDaemon);
-    lrcPimpl_ = std::make_unique<LrcPimpl>(*this, willDoMigrationCb, didDoMigrationCb);
+    lrcPimpl_ = std::make_unique<LrcPimpl>(*this);
 }
 
 Lrc::~Lrc()
@@ -245,15 +243,11 @@ Lrc::monitor(bool continuous)
     ConfigurationManager::instance().monitor(continuous);
 }
 
-LrcPimpl::LrcPimpl(Lrc& linked, MigrationCb& willMigrateCb, MigrationCb& didMigrateCb)
+LrcPimpl::LrcPimpl(Lrc& linked)
     : linked(linked)
     , behaviorController(std::make_unique<BehaviorController>())
     , callbackHandler(std::make_unique<CallbacksHandler>(linked))
-    , accountModel(std::make_unique<AccountModel>(linked,
-                                                  *callbackHandler,
-                                                  *behaviorController,
-                                                  willMigrateCb,
-                                                  didMigrateCb))
+    , accountModel(std::make_unique<AccountModel>(linked, *callbackHandler, *behaviorController))
     , AVModel_ {std::make_unique<AVModel>(*callbackHandler)}
     , PluginModel_ {std::make_unique<PluginModel>()}
 {}
diff --git a/src/libclient/typedefs.h b/src/libclient/typedefs.h
index 0e3afa36494f3be191e69f683b5b4d329d0b3686..a3e9ff88da1b4767ae2ffa6a47f7e378082da02b 100644
--- a/src/libclient/typedefs.h
+++ b/src/libclient/typedefs.h
@@ -267,6 +267,3 @@ private:
         return p | second; \
     } \
     DO_PRAGMA(GCC diagnostic pop)
-
-#include <functional>
-typedef std::function<void()> MigrationCb;
diff --git a/tests/qml/main.cpp b/tests/qml/main.cpp
index c3b3072e69459584e40534213bab80ffcf335b9d..2fbecebe47a562a758c7ce3457c2760516374a64 100644
--- a/tests/qml/main.cpp
+++ b/tests/qml/main.cpp
@@ -22,12 +22,11 @@
 #include "qmlregister.h"
 #include "systemtray.h"
 
-#include "api/profile.h"
-#include "api/account.h"
-#include "api/conversationmodel.h"
-#include "api/contactmodel.h"
-
-#include <atomic>
+#include <api/profile.h>
+#include <api/account.h>
+#include <api/conversationmodel.h>
+#include <api/contactmodel.h>
+#include <api/contact.h>
 
 #include <QFontDatabase>
 #include <QQmlContext>
@@ -45,7 +44,9 @@
 #include <windows.h>
 #endif
 
+#include <atomic>
 #include <thread>
+
 using namespace std::literals::chrono_literals;
 
 class Setup : public QObject
@@ -86,8 +87,7 @@ public Q_SLOTS:
 
         QFontDatabase::addApplicationFont(":/images/FontAwesome.otf");
 
-        lrcInstance_.reset(
-            new LRCInstance(nullptr, nullptr, "", connectivityMonitor_.get(), true, muteDaemon_));
+        lrcInstance_.reset(new LRCInstance("", connectivityMonitor_.get(), true, muteDaemon_));
         lrcInstance_->subscribeToDebugReceived();
 
         auto downloadPath = settingsManager_->getValue(Settings::Key::DownloadPath);
diff --git a/tests/unittests/globaltestenvironment.h b/tests/unittests/globaltestenvironment.h
index f432ef5c237092044788b81ea3db911b91ac1e90..96d0cf978dffd2eb1cf2250adb4af9535510e53f 100644
--- a/tests/unittests/globaltestenvironment.h
+++ b/tests/unittests/globaltestenvironment.h
@@ -16,8 +16,7 @@
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 
-#include "mainapplication.h"
-#include "qmlregister.h"
+#include "lrcinstance.h"
 #include "appsettingsmanager.h"
 #include "connectivitymonitor.h"
 #include "systemtray.h"
@@ -47,8 +46,7 @@ public:
         systemTray.reset(new SystemTray(settingsManager.get(), nullptr));
 
         std::atomic_bool isMigrating(false);
-        lrcInstance.reset(
-            new LRCInstance(nullptr, nullptr, "", connectivityMonitor.get(), debugMode, muteDaemon));
+        lrcInstance.reset(new LRCInstance("", connectivityMonitor.get(), debugMode, muteDaemon));
         lrcInstance->subscribeToDebugReceived();
 
         // setup the adapters (their lifetimes are that of MainApplication)