diff --git a/src/app/mainview/components/ChatViewHeader.qml b/src/app/mainview/components/ChatViewHeader.qml
index 1de6afe7c71950e63356ab6b2741dc60949cdf06..a7cadb83aa67aefb1368a12f738e3a9ae644a5bd 100644
--- a/src/app/mainview/components/ChatViewHeader.qml
+++ b/src/app/mainview/components/ChatViewHeader.qml
@@ -246,7 +246,8 @@ Rectangle {
             PushButton {
                 id: detailsButton
 
-                visible: interactionButtonsVisibility && swarmDetailsVisibility
+                visible: interactionButtonsVisibility
+                            && (swarmDetailsVisibility || LRCInstance.currentAccountType === Profile.Type.SIP) // TODO if SIP not a request
 
                 source: JamiResources.swarm_details_panel_svg
                 toolTipText: JamiStrings.details
diff --git a/src/app/mainview/components/ConversationExtrasPanel.qml b/src/app/mainview/components/ConversationExtrasPanel.qml
index f5db3f782d37d228e6f8c9814e8157b8e1421894..f11580246d8c3ba569266999868599e0310fee1d 100644
--- a/src/app/mainview/components/ConversationExtrasPanel.qml
+++ b/src/app/mainview/components/ConversationExtrasPanel.qml
@@ -34,7 +34,7 @@ StackLayout {
 
     function restoreState() {
         // Only applies to Jami accounts, and we musn't be in a call.
-        if (detailsShouldOpen && !inCallView && !CurrentConversation.isSip) {
+        if (detailsShouldOpen && !inCallView) {
             switchToPanel(ChatView.SwarmDetailsPanel, false)
         } else {
             closePanel()
@@ -68,7 +68,7 @@ StackLayout {
     function closePanel() {
         // We need to close the panel, but not save it when appropriate.
         currentIndex = -1
-        if (!inCallView && !CurrentConversation.isSip)
+        if (!inCallView)
             detailsShouldOpen = false
     }
 
diff --git a/src/app/mainview/components/SwarmDetailsPanel.qml b/src/app/mainview/components/SwarmDetailsPanel.qml
index e0ccfd065d46baa58f10a6d926846ed8d3ef560b..15b52f31f08c677b445c10d803d3c6c625e4d5f2 100644
--- a/src/app/mainview/components/SwarmDetailsPanel.qml
+++ b/src/app/mainview/components/SwarmDetailsPanel.qml
@@ -36,10 +36,10 @@ Rectangle {
     property int tabBarItemsLength: tabBar.contentChildren.length
 
     color: CurrentConversation.color
-    property var isAdmin: !CurrentConversation.isCoreDialog &&
-        UtilsAdapter.getParticipantRole(CurrentAccount.id,
+    property var isAdmin: UtilsAdapter.getParticipantRole(CurrentAccount.id,
                                         CurrentConversation.id,
                                         CurrentAccount.uri) === Member.Role.ADMIN
+                          || CurrentConversation.isCoreDialog
 
     ColumnLayout {
         id: swarmProfileDetails
@@ -158,7 +158,7 @@ Rectangle {
                 wrapMode: Text.NoWrap
 
                 text: formattedDescription.elidedText
-                readOnly: !root.isAdmin
+                readOnly: !root.isAdmin || CurrentConversation.isCoreDialog
                 visible: root.isAdmin || text.length > 0
                 placeholderText: JamiStrings.addADescription
                 placeholderTextColor: {
@@ -364,6 +364,7 @@ Rectangle {
                     SwarmDetailsItem {
                         Layout.fillWidth: true
                         Layout.preferredHeight: JamiTheme.settingsFontSize + 2 * JamiTheme.preferredMarginSize + 4
+                        visible: CurrentAccount.type !== Profile.Type.SIP // TODO for SIP save in VCard
 
                         RowLayout {
                             anchors.fill: parent
@@ -410,6 +411,7 @@ Rectangle {
                         id: settingsSwarmItem
                         Layout.fillWidth: true
                         Layout.preferredHeight: JamiTheme.settingsFontSize + 2 * JamiTheme.preferredMarginSize + 4
+                        visible: !CurrentConversation.isCoreDialog
 
                         RowLayout {
                             anchors.fill: parent
@@ -519,6 +521,7 @@ Rectangle {
                     RowLayout {
                         Layout.leftMargin: JamiTheme.preferredMarginSize
                         Layout.preferredHeight: JamiTheme.settingsFontSize + 2 * JamiTheme.preferredMarginSize + 4
+                        visible: CurrentAccount.type !== Profile.Type.SIP
 
                         Text {
                             Layout.fillWidth: true
diff --git a/src/libclient/api/contactmodel.h b/src/libclient/api/contactmodel.h
index 46234849e2f1be4e4138d416be1f272f1735a466..5820d36fb72cb37a7b8d8a3b044f2d256cc1eb3b 100644
--- a/src/libclient/api/contactmodel.h
+++ b/src/libclient/api/contactmodel.h
@@ -87,6 +87,8 @@ public:
     const contact::Info getContact(const QString& contactUri) const;
     ContactInfoMap getSearchResults() const;
 
+    void updateContact(const QString& uri, const MapStringString& infos);
+
     /**
      * Retrieve when a contact is added
      */
diff --git a/src/libclient/authority/storagehelper.cpp b/src/libclient/authority/storagehelper.cpp
index 918b2442564eedd56bccd1f5cf701fba2de7e084..d41baf2f8f3cb79e50241f377df52ddeca1d85e4 100644
--- a/src/libclient/authority/storagehelper.cpp
+++ b/src/libclient/authority/storagehelper.cpp
@@ -65,14 +65,14 @@ getPath()
 }
 
 static QString
-profileVcardPath(const QString& accountId, const QString& uri)
+profileVcardPath(const QString& accountId, const QString& uri, bool ov = false)
 {
     auto accountLocalPath = getPath() + accountId + QDir::separator();
     if (uri.isEmpty())
         return accountLocalPath + "profile.vcf";
 
     auto fileName = QString(uri.toUtf8().toBase64());
-    return accountLocalPath + "profiles" + QDir::separator() + fileName + ".vcf";
+    return accountLocalPath + "profiles" + QDir::separator() + fileName + (ov ? "_o.vcf" : ".vcf");
 }
 
 static QString
@@ -295,10 +295,10 @@ profileToVcard(const api::profile::Info& profileInfo, bool compressImage)
 }
 
 void
-setProfile(const QString& accountId, const api::profile::Info& profileInfo, const bool isPeer)
+setProfile(const QString& accountId, const api::profile::Info& profileInfo, bool isPeer, bool ov)
 {
     auto vcard = vcard::profileToVcard(profileInfo);
-    auto path = profileVcardPath(accountId, isPeer ? profileInfo.uri : "");
+    auto path = profileVcardPath(accountId, isPeer ? profileInfo.uri : "", ov);
     QLockFile lf(path + ".lock");
     QFile file(path);
     QFileInfo fileInfo(path);
@@ -347,7 +347,8 @@ getPeerParticipantsForConversation(Database& db, const QString& conversationId)
 void
 createOrUpdateProfile(const QString& accountId,
                       const api::profile::Info& profileInfo,
-                      const bool isPeer)
+                      bool isPeer,
+                      bool ov)
 {
     if (isPeer) {
         auto contact = storage::buildContactFromProfile(accountId,
@@ -357,19 +358,20 @@ createOrUpdateProfile(const QString& accountId,
             contact.profileInfo.alias = profileInfo.alias;
         if (!profileInfo.avatar.isEmpty())
             contact.profileInfo.avatar = profileInfo.avatar;
-        vcard::setProfile(accountId, contact.profileInfo, isPeer);
+        vcard::setProfile(accountId, contact.profileInfo, isPeer, ov);
         return;
     }
-    vcard::setProfile(accountId, profileInfo, isPeer);
+    vcard::setProfile(accountId, profileInfo, isPeer, ov);
 }
 
 void
 removeProfile(const QString& accountId, const QString& peerUri)
 {
     auto path = profileVcardPath(accountId, peerUri);
-    if (!QFile::remove(path)) {
+    if (!QFile::remove(path))
         qWarning() << "Couldn't remove vcard for" << peerUri << "at" << path;
-    }
+    auto opath = profileVcardPath(accountId, peerUri, true);
+    QFile::remove(opath);
 }
 
 QString
@@ -388,21 +390,38 @@ getAccountAvatar(const QString& accountId)
     return photo;
 }
 
+static QPair<QString, QString>
+getOverridenInfos(const QString& accountId, const QString& peerUri)
+{
+    QString b64filePathOverride = profileVcardPath(accountId, peerUri, true);
+    QFile fileOverride(b64filePathOverride);
+
+    QHash<QByteArray, QByteArray> overridenVCard;
+    QString overridenAlias, overridenAvatar;
+    if (fileOverride.open(QIODevice::ReadOnly)) {
+        overridenVCard = lrc::vCard::utils::toHashMap(fileOverride.readAll());
+        overridenAlias = overridenVCard[vCard::Property::FORMATTED_NAME];
+        for (const auto& key : overridenVCard.keys())
+            if (key.contains("PHOTO"))
+                overridenAvatar = overridenVCard[key];
+    }
+    return {overridenAlias, overridenAvatar};
+}
+
 api::contact::Info
 buildContactFromProfile(const QString& accountId,
-                        const QString& peer_uri,
+                        const QString& peerUri,
                         const api::profile::Type& type)
 {
     lrc::api::profile::Info profileInfo;
-    profileInfo.uri = peer_uri;
+    profileInfo.uri = peerUri;
     profileInfo.type = type;
     auto accountLocalPath = getPath() + accountId + "/";
-    QString b64filePath;
-    b64filePath = profileVcardPath(accountId, peer_uri);
+    QString b64filePath = profileVcardPath(accountId, peerUri);
     QFile file(b64filePath);
     if (!file.open(QIODevice::ReadOnly)) {
         // try non-base64 path
-        QString filePath = accountLocalPath + "profiles/" + peer_uri + ".vcf";
+        QString filePath = accountLocalPath + "profiles/" + peerUri + ".vcf";
         file.setFileName(filePath);
         if (!file.open(QIODevice::ReadOnly)) {
             return {profileInfo, "", true, false};
@@ -416,30 +435,40 @@ buildContactFromProfile(const QString& accountId,
             return {profileInfo, "", true, false};
         }
     }
+
+    auto [overridenAlias, overridenAvatar] = getOverridenInfos(accountId, peerUri);
+
     const auto vCard = lrc::vCard::utils::toHashMap(file.readAll());
     const auto alias = vCard[vCard::Property::FORMATTED_NAME];
     if (lrc::api::Lrc::cacheAvatars.load()) {
-        for (const auto& key : vCard.keys()) {
-            if (key.contains("PHOTO"))
-                profileInfo.avatar = vCard[key];
+        if (overridenAvatar.isEmpty()) {
+            for (const auto& key : vCard.keys()) {
+                if (key.contains("PHOTO"))
+                    profileInfo.avatar = vCard[key];
+            }
+        } else {
+            profileInfo.avatar = overridenAvatar;
         }
     }
-    profileInfo.alias = alias;
+    profileInfo.alias = overridenAlias.isEmpty() ? alias : overridenAlias;
     return {profileInfo, "", type == api::profile::Type::JAMI, false};
 }
 
 QString
-avatar(const QString& accountId, const QString& contactId)
+avatar(const QString& accountId, const QString& peerUri)
 {
-    if (contactId.isEmpty())
+    if (peerUri.isEmpty())
         return getAccountAvatar(accountId);
-    auto accountLocalPath = getPath() + accountId + "/";
+
+    auto [_overridenAlias, overridenAvatar] = getOverridenInfos(accountId, peerUri);
+    if (!overridenAvatar.isEmpty())
+        return overridenAvatar;
+
     QString b64filePath;
-    b64filePath = profileVcardPath(accountId, contactId);
+    b64filePath = profileVcardPath(accountId, peerUri);
     QFile file(b64filePath);
-    if (!file.open(QIODevice::ReadOnly)) {
+    if (!file.open(QIODevice::ReadOnly))
         return {};
-    }
     const auto vCard = lrc::vCard::utils::toHashMap(file.readAll());
     for (const auto& key : vCard.keys()) {
         if (key.contains("PHOTO"))
diff --git a/src/libclient/authority/storagehelper.h b/src/libclient/authority/storagehelper.h
index 1104a8cbb314b9c425d18cd83aecc22f651668f0..eaf66aa061897bc4723398e8c2a7c4965d6acc04 100644
--- a/src/libclient/authority/storagehelper.h
+++ b/src/libclient/authority/storagehelper.h
@@ -89,10 +89,13 @@ QString profileToVcard(const api::profile::Info& profileInfo, bool compressImage
  * @param accountId
  * @param profileInfo
  * @param isPeer
+ * @param ov  If from daemon override must be false, if the client want to override the vcard
+ * should be true
  */
 void setProfile(const QString& accountId,
                 const api::profile::Info& profileInfo,
-                const bool isPeer = false);
+                bool isPeer = false,
+                bool ov = false);
 
 } // namespace vcard
 
@@ -121,10 +124,12 @@ VectorString getPeerParticipantsForConversation(Database& db, const QString& con
  * @param  accountId
  * @param  profileInfo the contact info containing peer profile information
  * @param  isPeer indicates that a the profileInfo is that of a peer
+ * @param  ov if the client is storing a new vcard
  */
 void createOrUpdateProfile(const QString& accountId,
                            const api::profile::Info& profileInfo,
-                           const bool isPeer = false);
+                           bool isPeer = false,
+                           bool ov = false);
 
 /**
  * Remove a profile vCard
diff --git a/src/libclient/contactmodel.cpp b/src/libclient/contactmodel.cpp
index 84d6b2af125c0ff9840cc63753502deba5189f0a..d77270b860fe650f7cdf26dde3214e14c5e2b8f1 100644
--- a/src/libclient/contactmodel.cpp
+++ b/src/libclient/contactmodel.cpp
@@ -376,6 +376,27 @@ ContactModel::getContact(const QString& contactUri) const
     throw std::out_of_range("Contact out of range");
 }
 
+void
+ContactModel::updateContact(const QString& uri, const MapStringString& infos)
+{
+    std::unique_lock<std::mutex> lk(pimpl_->contactsMtx_);
+    auto ci = pimpl_->contacts.find(uri);
+    if (ci != pimpl_->contacts.end()) {
+        if (infos.contains("avatar")) {
+            ci->profileInfo.avatar = storage::vcard::compressedAvatar(infos["avatar"]);
+        } else if (!lrc::api::Lrc::cacheAvatars.load()) {
+            // Else it will be reseted
+            ci->profileInfo.avatar = storage::avatar(owner.id, uri);
+        }
+        if (infos.contains("title"))
+            ci->profileInfo.alias = infos["title"];
+        storage::createOrUpdateProfile(owner.id, ci->profileInfo, true, true);
+        lk.unlock();
+        Q_EMIT profileUpdated(uri);
+        Q_EMIT modelUpdated(uri);
+    }
+}
+
 const QList<QString>&
 ContactModel::getBannedContacts() const
 {
@@ -546,7 +567,7 @@ ContactModel::avatar(const QString& uri) const
             return contact.profileInfo.avatar;
         }
     }
-    // Else search in storage
+    // Else search in storage (because not cached!)
     return storage::avatar(owner.id, uri);
 }
 
diff --git a/src/libclient/conversationmodel.cpp b/src/libclient/conversationmodel.cpp
index 090f77bfda1af2c1666a7093330bcfa5f663ec8b..c008ca5d74d213ef9e300ac0eaea527d1c99d6c8 100644
--- a/src/libclient/conversationmodel.cpp
+++ b/src/libclient/conversationmodel.cpp
@@ -1047,6 +1047,17 @@ void
 ConversationModel::updateConversationInfos(const QString& conversationId,
                                            const MapStringString infos)
 {
+    auto conversationOpt = getConversationForUid(conversationId);
+    if (!conversationOpt.has_value())
+        return;
+    auto& conversation = conversationOpt->get();
+    if (conversation.isCoreDialog()) {
+        // If 1:1, we override a profile (as the peer will send their new profiles)
+        auto peer = pimpl_->peersForConversation(conversation);
+        if (!peer.isEmpty())
+            owner.contactModel->updateContact(peer.at(0), infos);
+        return;
+    }
     MapStringString newInfos = infos;
     // Compress avatar as it will be sent in the conversation's request over the DHT
     if (infos.contains("avatar"))