diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/Integration.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/Integration.xcscheme
new file mode 100644
index 0000000000000000000000000000000000000000..a1d66e16dd7d57f3d7fabe8cddde991cb42eda05
--- /dev/null
+++ b/.swiftpm/xcode/xcshareddata/xcschemes/Integration.xcscheme
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "1340"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "Integration"
+               BuildableName = "Integration"
+               BlueprintName = "Integration"
+               ReferencedContainer = "container:">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+      <Testables>
+      </Testables>
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      allowLocationSimulation = "YES">
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "Integration"
+            BuildableName = "Integration"
+            BlueprintName = "Integration"
+            ReferencedContainer = "container:">
+         </BuildableReference>
+      </MacroExpansion>
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>
diff --git a/App/client-ios/Resources/GoogleService-Info.plist b/App/client-ios/Resources/GoogleService-Info.plist
index 03e09469daae0502a5202f6e63aca9db140d1d77..676030ed57400f2bf5a72dc61d4ba5ed3e5263cb 100644
--- a/App/client-ios/Resources/GoogleService-Info.plist
+++ b/App/client-ios/Resources/GoogleService-Info.plist
@@ -1,36 +1,36 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 <plist version="1.0">
-<dict>
-	<key>CLIENT_ID</key>
-	<string></string>
-	<key>REVERSED_CLIENT_ID</key>
-	<string></string>
-	<key>ANDROID_CLIENT_ID</key>
-	<string></string>
-	<key>API_KEY</key>
-	<string></string>
-	<key>GCM_SENDER_ID</key>
-	<string></string>
-	<key>PLIST_VERSION</key>
-	<string></string>
-	<key>BUNDLE_ID</key>
-	<string></string>
-	<key>PROJECT_ID</key>
-	<string></string>
-	<key>STORAGE_BUCKET</key>
-	<string></string>
-	<key>IS_ADS_ENABLED</key>
-	<false/>
-	<key>IS_ANALYTICS_ENABLED</key>
-	<false/>
-	<key>IS_APPINVITE_ENABLED</key>
-	<false/>
-	<key>IS_GCM_ENABLED</key>
-	<false/>
-	<key>IS_SIGNIN_ENABLED</key>
-	<false/>
-	<key>GOOGLE_APP_ID</key>
-	<string></string>
-</dict>
+    <dict>
+        <key>CLIENT_ID</key>
+        <string>662236151640-herpu89qikpfs9m4kvbi9bs5fpdji5de.apps.googleusercontent.com</string>
+        <key>REVERSED_CLIENT_ID</key>
+        <string>com.googleusercontent.apps.662236151640-herpu89qikpfs9m4kvbi9bs5fpdji5de</string>
+        <key>ANDROID_CLIENT_ID</key>
+        <string>662236151640-2ughgo2dvc59dm4o39b45lbdungp2mct.apps.googleusercontent.com</string>
+        <key>API_KEY</key>
+        <string>AIzaSyCbI2yQ7pbuVSRvraqanjGcS9CDrjD7lNU</string>
+        <key>GCM_SENDER_ID</key>
+        <string>662236151640</string>
+        <key>PLIST_VERSION</key>
+        <string>1</string>
+        <key>BUNDLE_ID</key>
+        <string>io.xxlabs.messenger</string>
+        <key>PROJECT_ID</key>
+        <string>xx-messenger-6e03e</string>
+        <key>STORAGE_BUCKET</key>
+        <string>xx-messenger-6e03e.appspot.com</string>
+        <key>IS_ADS_ENABLED</key>
+        <false></false>
+        <key>IS_ANALYTICS_ENABLED</key>
+        <false></false>
+        <key>IS_APPINVITE_ENABLED</key>
+        <true></true>
+        <key>IS_GCM_ENABLED</key>
+        <true></true>
+        <key>IS_SIGNIN_ENABLED</key>
+        <true></true>
+        <key>GOOGLE_APP_ID</key>
+        <string>1:662236151640:ios:24badb58ab07515d8cef2d</string>
+    </dict>
 </plist>
diff --git a/Sources/App/AppDelegate.swift b/Sources/App/AppDelegate.swift
index ecb65d1f54eb3b05cda31850eb98431c6ec2d31f..11ae32299ca8629cd0e99d3bc518541a596cc26e 100644
--- a/Sources/App/AppDelegate.swift
+++ b/Sources/App/AppDelegate.swift
@@ -1,6 +1,7 @@
 import UIKit
 import BackgroundTasks
 
+import XXModels
 import Theme
 import XXLogger
 import Defaults
@@ -91,7 +92,11 @@ public class AppDelegate: UIResponder, UIApplicationDelegate {
                 guard UIApplication.shared.backgroundTimeRemaining > 9 else {
                     if !self.forceFailedPendingMessages {
                         self.forceFailedPendingMessages = true
-                        session.forceFailMessages()
+
+                        // TODO: We need a Message.Assignment for status
+//                        let query = Message.Query(status: [.sending])
+//                        let assignment = Message.Assignments(status: .sendingFailed)
+//                        _ = try? session.dbManager.bulkUpdateMessages(query, assignment)
                     }
 
                     return
diff --git a/Sources/App/DependencyRegistrator.swift b/Sources/App/DependencyRegistrator.swift
index b342330e9012ba1a245d39cbd26f47602c074db4..9102274af7c0c37ce986b8aba6b2607a33cd66ca 100644
--- a/Sources/App/DependencyRegistrator.swift
+++ b/Sources/App/DependencyRegistrator.swift
@@ -259,7 +259,7 @@ extension PushRouter {
                     }
                 case .contactChat(id: let id):
                     if let session = try? DependencyInjection.Container.shared.resolve() as SessionType,
-                       let contact = session.getContactWith(userId: id) {
+                       let contact = try? session.dbManager.fetchContacts(.init(id: [id])).first {
                         navigationController.setViewControllers([
                             ChatListController(),
                             SingleChatController(contact)
@@ -267,7 +267,7 @@ extension PushRouter {
                     }
                 case .groupChat(id: let id):
                     if let session = try? DependencyInjection.Container.shared.resolve() as SessionType,
-                       let info = session.getGroupChatInfoWith(groupId: id) {
+                       let info = try? session.dbManager.fetchGroupInfos(.init(groupId: id)).first {
                         navigationController.setViewControllers([
                             ChatListController(),
                             GroupChatController(info)
diff --git a/Sources/ChatFeature/Controllers/GroupChatController.swift b/Sources/ChatFeature/Controllers/GroupChatController.swift
index 6299ecb676646e6078e4e4a8bf282caa174a0362..f1e5c2fe06d45ccab6ea77b5422a5df83cec9262 100644
--- a/Sources/ChatFeature/Controllers/GroupChatController.swift
+++ b/Sources/ChatFeature/Controllers/GroupChatController.swift
@@ -33,16 +33,16 @@ public final class GroupChatController: UIViewController {
     private let layoutDelegate = LayoutDelegate()
     private var cancellables = Set<AnyCancellable>()
     private var drawerCancellables = Set<AnyCancellable>()
-    private var sections = [ArraySection<ChatSection, ChatItem>]()
+    private var sections = [ArraySection<ChatSection, Message>]()
     private var currentInterfaceActions = SetActor<Set<InterfaceActions>, ReactionTypes>()
 
     public override var canBecomeFirstResponder: Bool { true }
     public override var inputAccessoryView: UIView? { inputComponent }
 
-    public init(_ info: GroupChatInfo) {
+    public init(_ info: GroupInfo) {
         let viewModel = GroupChatViewModel(info)
         self.viewModel = viewModel
-        self.members = .init(with: [])
+        self.members = .init(with: info.members)
 
         self.inputComponent = ChatInputView(store: .init(
             initialState: .init(canAddAttachments: false),
@@ -60,7 +60,14 @@ public final class GroupChatController: UIViewController {
 
         super.init(nibName: nil, bundle: nil)
 
-//        header.setup(title: info.group.name, memberList: info.members.map { ($0.username, $0.photo) })
+        let memberList = info.members.map {
+            Member(
+                title: ($0.nickname ?? $0.username) ?? "Fetching username...",
+                photo: $0.photo
+            )
+        }
+
+        header.setup(title: info.group.name, memberList: memberList)
     }
 
     public required init?(coder: NSCoder) { nil }
@@ -155,7 +162,9 @@ public final class GroupChatController: UIViewController {
 
         viewModel.replyPublisher
             .receive(on: DispatchQueue.main)
-            .sink { [unowned self] in inputComponent.setupReply(message: $0.text, sender: $0.sender) }
+            .sink { [unowned self] senderTitle, messageText in
+                inputComponent.setupReply(message: messageText, sender: senderTitle)
+            }
             .store(in: &cancellables)
     }
 
@@ -328,7 +337,7 @@ extension GroupChatController: UICollectionViewDataSource {
         let showRound: (String?) -> Void = viewModel.showRoundFrom(_:)
 
         if item.status == .received {
-            if item.payload.reply != nil {
+            if item.replyMessageId != nil {
                 let cell: IncomingGroupReplyCell = collectionView.dequeueReusableCell(forIndexPath: indexPath)
 
 //                Bubbler.buildReplyGroup(
@@ -356,7 +365,7 @@ extension GroupChatController: UICollectionViewDataSource {
                 return cell
             }
         } else if item.status == .sendingFailed {
-            if item.payload.reply != nil {
+            if item.replyMessageId != nil {
                 let cell: OutgoingFailedGroupReplyCell = collectionView.dequeueReusableCell(forIndexPath: indexPath)
 
 //                Bubbler.buildReplyGroup(
@@ -383,7 +392,7 @@ extension GroupChatController: UICollectionViewDataSource {
                 return cell
             }
         } else {
-            if item.payload.reply != nil {
+            if item.replyMessageId != nil {
                 let cell: OutgoingGroupReplyCell = collectionView.dequeueReusableCell(forIndexPath: indexPath)
 
 //                Bubbler.buildReplyGroup(
@@ -524,7 +533,7 @@ extension GroupChatController: UICollectionViewDelegate {
             let item = self.sections[indexPath.section].elements[indexPath.item]
 
             let copy = UIAction(title: Localized.Chat.BubbleMenu.copy, state: .off) { _ in
-                UIPasteboard.general.string = item.payload.text
+                UIPasteboard.general.string = item.text
             }
 
             let reply = UIAction(title: Localized.Chat.BubbleMenu.reply, state: .off) { [weak self] _ in
diff --git a/Sources/ChatFeature/Controllers/MembersController.swift b/Sources/ChatFeature/Controllers/MembersController.swift
index c2a7ea2a0bef4319213c6b28641b06ee4e9a4e99..e7bf2c3ece345ace155ed7babbf5c9ec05bf1caf 100644
--- a/Sources/ChatFeature/Controllers/MembersController.swift
+++ b/Sources/ChatFeature/Controllers/MembersController.swift
@@ -6,9 +6,9 @@ import XXModels
 final class MembersController: UIViewController {
     lazy private var stackView = UIStackView()
 
-    private let members: [GroupMember]
+    private let members: [Contact]
 
-    init(with members: [GroupMember]) {
+    init(with members: [Contact]) {
         self.members = members
         super.init(nibName: nil, bundle: nil)
     }
@@ -26,16 +26,17 @@ final class MembersController: UIViewController {
         stackView.distribution = .fillEqually
         view.addSubview(stackView)
 
-        stackView.snp.makeConstraints { make in
-            make.top.equalToSuperview().offset(10)
-            make.left.right.equalToSuperview()
-            make.bottom.equalTo(view.safeAreaLayoutGuide)
+        stackView.snp.makeConstraints {
+            $0.top.equalToSuperview().offset(10)
+            $0.left.right.equalToSuperview()
+            $0.bottom.equalTo(view.safeAreaLayoutGuide)
         }
 
-        for member in members {
+        members.forEach {
             let memberView = MemberView()
-//            memberView.titleLabel.text = member.username
-//            memberView.avatarView.setupProfile(title: member.username, image: member.photo, size: .small)
+            let assignedTitle = ($0.nickname ?? $0.username) ?? "Fetching username..."
+            memberView.titleLabel.text = assignedTitle
+            memberView.avatarView.setupProfile(title: assignedTitle, image: $0.photo, size: .small)
             stackView.addArrangedSubview(memberView)
         }
     }
@@ -57,24 +58,24 @@ private final class MemberView: UIView {
         addSubview(avatarView)
         addSubview(separatorView)
 
-        avatarView.snp.makeConstraints { make in
-            make.top.equalToSuperview().offset(10)
-            make.width.height.equalTo(30)
-            make.left.equalToSuperview().offset(25)
-            make.centerY.equalToSuperview()
+        avatarView.snp.makeConstraints {
+            $0.top.equalToSuperview().offset(10)
+            $0.width.height.equalTo(30)
+            $0.left.equalToSuperview().offset(25)
+            $0.centerY.equalToSuperview()
         }
 
-        titleLabel.snp.makeConstraints { make in
-            make.centerY.equalTo(avatarView)
-            make.left.equalTo(avatarView.snp.right).offset(14)
-            make.right.lessThanOrEqualToSuperview().offset(-10)
+        titleLabel.snp.makeConstraints {
+            $0.centerY.equalTo(avatarView)
+            $0.left.equalTo(avatarView.snp.right).offset(14)
+            $0.right.lessThanOrEqualToSuperview().offset(-10)
         }
 
-        separatorView.snp.makeConstraints { make in
-            make.height.equalTo(1)
-            make.left.equalToSuperview().offset(25)
-            make.right.equalToSuperview()
-            make.bottom.equalToSuperview()
+        separatorView.snp.makeConstraints {
+            $0.height.equalTo(1)
+            $0.left.equalToSuperview().offset(25)
+            $0.right.equalToSuperview()
+            $0.bottom.equalToSuperview()
         }
     }
 
diff --git a/Sources/ChatFeature/Controllers/SingleChatController.swift b/Sources/ChatFeature/Controllers/SingleChatController.swift
index 9e4b4a5a8d858f72c0573881217425eb1e2f9e1b..7f611e182aa3dfdf95a7cc92567e93485afe9398 100644
--- a/Sources/ChatFeature/Controllers/SingleChatController.swift
+++ b/Sources/ChatFeature/Controllers/SingleChatController.swift
@@ -19,6 +19,10 @@ extension FlexibleSpace: CollectionCellContent {
     func prepareForReuse() {}
 }
 
+extension Message: Differentiable {
+    public var differenceIdentifier: Int64 { id! }
+}
+
 public final class SingleChatController: UIViewController {
     @Dependency private var hud: HUDType
     @Dependency private var logger: XXLogger
@@ -44,7 +48,7 @@ public final class SingleChatController: UIViewController {
     private let layoutDelegate = LayoutDelegate()
     private var cancellables = Set<AnyCancellable>()
     private var drawerCancellables = Set<AnyCancellable>()
-    private var sections = [ArraySection<ChatSection, ChatItem>]()
+    private var sections = [ArraySection<ChatSection, Message>]()
     private var currentInterfaceActions: SetActor<Set<InterfaceActions>, ReactionTypes> = SetActor()
 
     var fileURL: URL?
@@ -204,7 +208,9 @@ public final class SingleChatController: UIViewController {
 
         viewModel.replyPublisher
             .receive(on: DispatchQueue.main)
-            .sink { [unowned self] in inputComponent.setupReply(message: $0.text, sender: $0.sender) }
+            .sink { [unowned self] senderTitle, messageText in
+                inputComponent.setupReply(message: messageText, sender: senderTitle)
+            }
             .store(in: &cancellables)
 
         viewModel.navigation
diff --git a/Sources/ChatFeature/Models/ChatItem.swift b/Sources/ChatFeature/Models/ChatItem.swift
deleted file mode 100644
index 8694d9e54ec9727a18a24a2e9185a40ca262b6f1..0000000000000000000000000000000000000000
--- a/Sources/ChatFeature/Models/ChatItem.swift
+++ /dev/null
@@ -1,23 +0,0 @@
-import Models
-import XXModels
-import Foundation
-import DifferenceKit
-
-struct ChatItem: Equatable, Differentiable {
-    let date: Date
-    var uniqueId: Data?
-    let identity: Int64
-    var roundURL: String?
-    let payload: Payload
-    var status: Message.Status
-    var differenceIdentifier: Int64 { identity }
-
-    init(_ message: Message) {
-        self.identity = message.id!
-        self.status = message.status
-        self.payload = Payload(text: message.text, reply: nil)
-        self.roundURL = message.roundURL
-        self.uniqueId = message.networkId
-        self.date = message.date
-    }
-}
diff --git a/Sources/ChatFeature/Models/ChatSection.swift b/Sources/ChatFeature/Models/ChatSection.swift
index cb755bf94795c6a69eb9549b5cacbb023aca1e65..a91c016fd166a1f7b2e41286186dd2cae1f2b340 100644
--- a/Sources/ChatFeature/Models/ChatSection.swift
+++ b/Sources/ChatFeature/Models/ChatSection.swift
@@ -2,11 +2,6 @@ import Foundation
 import DifferenceKit
 
 struct ChatSection: Equatable, Differentiable {
-    // MARK: Properties
-
     var date: Date
-
-    // MARK: DifferenceKit
-
     var differenceIdentifier: Date { date }
 }
diff --git a/Sources/ChatFeature/ViewModels/GroupChatViewModel.swift b/Sources/ChatFeature/ViewModels/GroupChatViewModel.swift
index 663d08f777ba945c7ec563975e6aa832c90008c4..7d611491a6824f1c0301a542a7d9e71e2810b7a8 100644
--- a/Sources/ChatFeature/ViewModels/GroupChatViewModel.swift
+++ b/Sources/ChatFeature/ViewModels/GroupChatViewModel.swift
@@ -15,7 +15,7 @@ enum GroupChatNavigationRoutes: Equatable {
 final class GroupChatViewModel {
     @Dependency private var session: SessionType
 
-    var replyPublisher: AnyPublisher<ReplyModel, Never> {
+    var replyPublisher: AnyPublisher<(String, String), Never> {
         replySubject.eraseToAnyPublisher()
     }
 
@@ -23,18 +23,17 @@ final class GroupChatViewModel {
         routesSubject.eraseToAnyPublisher()
     }
 
-    let info: GroupChatInfo
+    let info: GroupInfo
     private var stagedReply: Reply?
     private var cancellables = Set<AnyCancellable>()
-    private let replySubject = PassthroughSubject<ReplyModel, Never>()
+    private let replySubject = PassthroughSubject<(String, String), Never>()
     private let routesSubject = PassthroughSubject<GroupChatNavigationRoutes, Never>()
 
-    var messages: AnyPublisher<[ArraySection<ChatSection, ChatItem>], Never> {
+    var messages: AnyPublisher<[ArraySection<ChatSection, Message>], Never> {
         session.dbManager.fetchMessagesPublisher(.init(chat: .group(info.group.id)))
             .assertNoFailure()
-            .map { messages -> [ArraySection<ChatSection, ChatItem>] in
-                let domainModels = messages.map { ChatItem($0) }
-                let groupedByDate = Dictionary(grouping: domainModels) { domainModel -> Date in
+            .map { messages -> [ArraySection<ChatSection, Message>] in
+                let groupedByDate = Dictionary(grouping: messages) { domainModel -> Date in
                     let components = Calendar.current.dateComponents([.day, .month, .year], from: domainModel.date)
                     return Calendar.current.date(from: components)!
                 }
@@ -43,14 +42,14 @@ final class GroupChatViewModel {
                     .map { .init(model: ChatSection(date: $0.key), elements: $0.value) }
                     .sorted(by: { $0.model.date < $1.model.date })
             }
-            .map { sections -> [ArraySection<ChatSection, ChatItem>] in
-            var snapshot = [ArraySection<ChatSection, ChatItem>]()
+            .map { sections -> [ArraySection<ChatSection, Message>] in
+            var snapshot = [ArraySection<ChatSection, Message>]()
             sections.forEach { snapshot.append(.init(model: $0.model, elements: $0.elements)) }
             return snapshot
         }.eraseToAnyPublisher()
     }
 
-    init(_ info: GroupChatInfo) {
+    init(_ info: GroupInfo) {
         self.info = info
     }
 
@@ -60,8 +59,8 @@ final class GroupChatViewModel {
         _ = try? session.dbManager.bulkUpdateMessages(query, assignment)
     }
 
-    func didRequestDelete(_ items: [ChatItem]) {
-//        try? session.dbManager.deleteMessages(.init(id: items.map(\.identity)))
+    func didRequestDelete(_ messages: [Message]) {
+        _ = try? session.dbManager.deleteMessages(.init(id: Set(messages.map(\.id))))
     }
 
     func send(_ text: String) {
@@ -72,8 +71,9 @@ final class GroupChatViewModel {
         stagedReply = nil
     }
 
-    func retry(_ model: ChatItem) {
-//        session.retryGroupMessage(model.identity)
+    func retry(_ message: Message) {
+        guard let id = message.id else { return }
+        session.retryMessage(id)
     }
 
     func showRoundFrom(_ roundURL: String?) {
@@ -89,20 +89,24 @@ final class GroupChatViewModel {
     }
 
     func getName(from senderId: Data) -> String {
-        fatalError()
-//        guard let member = info.members.first(where: { $0.userId == senderId }) else { return "You" }
-//        return member.username
+        guard let contact = try? session.dbManager.fetchContacts(.init(id: [senderId])).first else {
+            return "You"
+        }
+
+        return (contact.nickname ?? contact.username) ?? "Fetching username..."
     }
 
     func getText(from messageId: Data) -> String {
-        fatalError()
-//        session.getTextFromGroupMessage(messageId: messageId) ?? "[DELETED]"
+        guard let message = try? session.dbManager.fetchMessages(.init(networkId: messageId)).first else {
+            return "[DELETED]"
+        }
+
+        return message.text
     }
 
-//    func didRequestReply(_ model: GroupChatItem) {
-//        guard let messageId = model.uniqueId else { return }
-//
-//        stagedReply = Reply(messageId: messageId, senderId: model.sender)
-//        replySubject.send(.init(text: model.payload.text, sender: getName(from: model.sender)))
-//    }
+    func didRequestReply(_ message: Message) {
+        guard let networkId = message.networkId else { return }
+        stagedReply = Reply(messageId: networkId, senderId: message.senderId)
+        replySubject.send((getName(from: message.senderId), message.text))
+    }
 }
diff --git a/Sources/ChatFeature/ViewModels/SingleChatViewModel.swift b/Sources/ChatFeature/ViewModels/SingleChatViewModel.swift
index 86e7e3f9ec5083f27fbecd8aa6996da99977c5d1..8690ca1ccc9c850631e6f15fd798a41ab8c54c08 100644
--- a/Sources/ChatFeature/ViewModels/SingleChatViewModel.swift
+++ b/Sources/ChatFeature/ViewModels/SingleChatViewModel.swift
@@ -11,11 +11,6 @@ import Permissions
 import DifferenceKit
 import DependencyInjection
 
-struct ReplyModel {
-    var text: String
-    var sender: String
-}
-
 enum SingleChatNavigationRoutes: Equatable {
     case none
     case camera
@@ -36,22 +31,22 @@ final class SingleChatViewModel {
     private var stagedReply: Reply?
     private var cancellables = Set<AnyCancellable>()
     private let contactSubject: CurrentValueSubject<Contact, Never>
-    private let replySubject = PassthroughSubject<ReplyModel, Never>()
+    private let replySubject = PassthroughSubject<(String, String), Never>()
     private let navigationRoutes = PassthroughSubject<SingleChatNavigationRoutes, Never>()
-    private let sectionsRelay = CurrentValueSubject<[ArraySection<ChatSection, ChatItem>], Never>([])
+    private let sectionsRelay = CurrentValueSubject<[ArraySection<ChatSection, Message>], Never>([])
 
     var hud: AnyPublisher<HUDStatus, Never> { hudRelay.eraseToAnyPublisher() }
     private let hudRelay = CurrentValueSubject<HUDStatus, Never>(.none)
 
     var isOnline: AnyPublisher<Bool, Never> { session.isOnline }
     var contactPublisher: AnyPublisher<Contact, Never> { contactSubject.eraseToAnyPublisher() }
-    var replyPublisher: AnyPublisher<ReplyModel, Never> { replySubject.eraseToAnyPublisher() }
+    var replyPublisher: AnyPublisher<(String, String), Never> { replySubject.eraseToAnyPublisher() }
     var navigation: AnyPublisher<SingleChatNavigationRoutes, Never> { navigationRoutes.eraseToAnyPublisher() }
     var shouldDisplayEmptyView: AnyPublisher<Bool, Never> { sectionsRelay.map { $0.isEmpty }.eraseToAnyPublisher() }
 
-    var messages: AnyPublisher<[ArraySection<ChatSection, ChatItem>], Never> {
-        sectionsRelay.map { sections -> [ArraySection<ChatSection, ChatItem>] in
-            var snapshot = [ArraySection<ChatSection, ChatItem>]()
+    var messages: AnyPublisher<[ArraySection<ChatSection, Message>], Never> {
+        sectionsRelay.map { sections -> [ArraySection<ChatSection, Message>] in
+            var snapshot = [ArraySection<ChatSection, Message>]()
             sections.forEach { snapshot.append(.init(model: $0.model, elements: $0.elements)) }
             return snapshot
         }.eraseToAnyPublisher()
@@ -82,10 +77,8 @@ final class SingleChatViewModel {
 
         session.dbManager.fetchMessagesPublisher(.init(chat: .direct(session.myId, contact.id)))
             .assertNoFailure()
-            .map { messages in
-
-                let domainModels = messages.map { ChatItem($0) }
-                let groupedByDate = Dictionary(grouping: domainModels) { domainModel -> Date in
+            .map {
+                let groupedByDate = Dictionary(grouping: $0) { domainModel -> Date in
                     let components = Calendar.current.dateComponents([.day, .month, .year], from: domainModel.date)
                     return Calendar.current.date(from: components)!
                 }
@@ -133,8 +126,9 @@ final class SingleChatViewModel {
         _ = try? session.dbManager.deleteMessages(.init(chat: .direct(session.myId, contact.id)))
     }
 
-    func didRequestRetry(_ model: ChatItem) {
-        session.retryMessage(model.identity)
+    func didRequestRetry(_ message: Message) {
+        guard let id = message.id else { return }
+        session.retryMessage(id)
     }
    
     func didNavigateSomewhere() {
@@ -167,11 +161,11 @@ final class SingleChatViewModel {
         return false
     }
 
-    func didRequestCopy(_ model: ChatItem) {
-        UIPasteboard.general.string = model.payload.text
+    func didRequestCopy(_ model: Message) {
+        UIPasteboard.general.string = model.text
     }
 
-    func didRequestDeleteSingle(_ model: ChatItem) {
+    func didRequestDeleteSingle(_ model: Message) {
         didRequestDelete([model])
     }
 
@@ -186,17 +180,25 @@ final class SingleChatViewModel {
         stagedReply = nil
     }
 
-    func didRequestReply(_ model: ChatItem) {
-//        guard let messageId = model.uniqueId else { return }
-//
-//        let isIncoming = model.status == .received
-//        stagedReply = Reply(messageId: messageId, senderId: isIncoming ? contact.userId : session.myId)
-//        replySubject.send(.init(text: model.payload.text, sender: isIncoming ? contact.nickname ?? contact.username : "You"))
+    func didRequestReply(_ message: Message) {
+        let senderTitle: String = {
+            if message.senderId == session.myId {
+                return "You"
+            } else {
+                return (contact.nickname ?? contact.username) ?? "Fetching username..."
+            }
+        }()
+
+        replySubject.send((senderTitle, message.text))
+        stagedReply = Reply(messageId: message.networkId!, senderId: message.senderId)
     }
 
     func getText(from messageId: Data) -> String {
-        fatalError()
-//        session.getTextFromMessage(messageId: messageId) ?? "[DELETED]"
+        guard let message = try? session.dbManager.fetchMessages(.init(networkId: messageId)).first else {
+            return "[DELETED]"
+        }
+
+        return message.text
     }
 
     func showRoundFrom(_ roundURL: String?) {
@@ -207,19 +209,19 @@ final class SingleChatViewModel {
         }
     }
 
-    func didRequestDelete(_ items: [ChatItem]) {
-        ///session.delete(messages: items.map { $0.identity })
+    func didRequestDelete(_ items: [Message]) {
+        _ = try? session.dbManager.deleteMessages(.init(id: Set(items.compactMap(\.id))))
     }
 
-    func itemWith(id: Int64) -> ChatItem? {
-        sectionsRelay.value.flatMap(\.elements).first(where: { $0.identity == id })
+    func itemWith(id: Int64) -> Message? {
+        sectionsRelay.value.flatMap(\.elements).first(where: { $0.id == id })
     }
 
     func getName(from senderId: Data) -> String {
         senderId == session.myId ? "You" : contact.nickname ?? contact.username!
     }
 
-    func itemAt(indexPath: IndexPath) -> ChatItem? {
+    func itemAt(indexPath: IndexPath) -> Message? {
         guard sectionsRelay.value.count > indexPath.section else { return nil }
 
         let items = sectionsRelay.value[indexPath.section].elements
diff --git a/Sources/ChatFeature/Views/GroupHeaderView.swift b/Sources/ChatFeature/Views/GroupHeaderView.swift
index d85a49abe97265a8b0ba5bd1dec227b45e97c9ec..b33415707304f070f172764a5ee8a49d89a1a10d 100644
--- a/Sources/ChatFeature/Views/GroupHeaderView.swift
+++ b/Sources/ChatFeature/Views/GroupHeaderView.swift
@@ -1,6 +1,11 @@
 import UIKit
 import Shared
 
+struct Member {
+    let title: String
+    let photo: Data?
+}
+
 final class GroupHeaderView: UIView {
     let titleLabel = UILabel()
     let containerView = UIView()
@@ -39,14 +44,14 @@ final class GroupHeaderView: UIView {
 
     required init?(coder: NSCoder) { nil }
 
-    func setup(title: String, memberList: [(String, Data?)]) {
+    func setup(title: String, memberList: [Member]) {
         titleLabel.text = title
 
-        for member in memberList {
+        memberList.forEach {
             let avatarView = AvatarView()
             avatarView.layer.borderWidth = 3
             avatarView.layer.borderColor = UIColor.white.cgColor
-            avatarView.setupProfile(title: member.0, image: member.1, size: .small)
+            avatarView.setupProfile(title: $0.title, image: $0.photo, size: .small)
             avatarView.snp.makeConstraints { $0.width.height.equalTo(25.0) }
             stackView.addArrangedSubview(avatarView)
         }
diff --git a/Sources/ChatListFeature/Controller/ChatListController.swift b/Sources/ChatListFeature/Controller/ChatListController.swift
index d2d15a400f402ec532ee97a2407ccf88415c1c93..cd12b4ead14a92e07c8eab48337ec6df2845732a 100644
--- a/Sources/ChatListFeature/Controller/ChatListController.swift
+++ b/Sources/ChatListFeature/Controller/ChatListController.swift
@@ -3,9 +3,16 @@ import Theme
 import Models
 import Shared
 import Combine
+import XXModels
 import MenuFeature
 import DependencyInjection
 
+extension Contact: Hashable {
+    public func hash(into hasher: inout Hasher) {
+        hasher.combine(id)
+    }
+}
+
 public final class ChatListController: UIViewController {
     @Dependency private var coordinator: ChatListCoordinating
     @Dependency private var statusBarController: StatusBarStyleControlling
@@ -69,10 +76,10 @@ public final class ChatListController: UIViewController {
                 }
             }.store(in: &cancellables)
 
-        viewModel.badgeCountPublisher
-            .receive(on: DispatchQueue.main)
-            .sink { [unowned self] in topLeftView.updateBadge($0) }
-            .store(in: &cancellables)
+//        viewModel.badgeCountPublisher
+//            .receive(on: DispatchQueue.main)
+//            .sink { [unowned self] in topLeftView.updateBadge($0) }
+//            .store(in: &cancellables)
 
         topLeftView.actionPublisher
             .receive(on: DispatchQueue.main)
@@ -115,19 +122,19 @@ public final class ChatListController: UIViewController {
             collectionView: screenView.listContainerView.collectionView
         ) { collectionView, indexPath, contact in
             let cell: ChatListRecentContactCell = collectionView.dequeueReusableCell(forIndexPath: indexPath)
-            cell.setup(title: contact.nickname ?? contact.username, image: contact.photo)
+            cell.setup(title: contact.nickname ?? contact.username!, image: contact.photo)
             return cell
         }
 
         screenView.listContainerView.collectionView.delegate = self
         screenView.listContainerView.collectionView.dataSource = collectionDataSource
 
-        viewModel.recentsPublisher
-            .receive(on: DispatchQueue.main)
-            .sink { [unowned self] in
-                collectionDataSource.apply($0)
-                shouldBeShowingRecents = $0.numberOfItems > 0
-            }.store(in: &cancellables)
+//        viewModel.recentsPublisher
+//            .receive(on: DispatchQueue.main)
+//            .sink { [unowned self] in
+//                collectionDataSource.apply($0)
+//                shouldBeShowingRecents = $0.numberOfItems > 0
+//            }.store(in: &cancellables)
     }
 
     private func setupBindings() {
@@ -146,33 +153,33 @@ public final class ChatListController: UIViewController {
                 screenView.searchListContainerView.emptyView.updateSearched(content: query)
             }.store(in: &cancellables)
 
-        Publishers.CombineLatest(
-            viewModel.searchPublisher,
-            screenView.searchView.textPublisher.removeDuplicates()
-        )
-        .receive(on: DispatchQueue.main)
-            .sink { [unowned self] items, query in
-                guard query.isEmpty == false else {
-                    screenView.searchListContainerView.isHidden = true
-                    screenView.listContainerView.isHidden = false
-                    screenView.bringSubviewToFront(screenView.listContainerView)
-                    return
-                }
-
-                screenView.listContainerView.isHidden = true
-                screenView.searchListContainerView.isHidden = false
-
-                guard items.numberOfItems > 0 else {
-                    screenView.searchListContainerView.emptyView.isHidden = false
-                    screenView.bringSubviewToFront(screenView.searchListContainerView)
-                    screenView.searchListContainerView.bringSubviewToFront(screenView.searchListContainerView.emptyView)
-                    return
-                }
-
-                screenView.searchListContainerView.bringSubviewToFront(searchTableController.view)
-                screenView.searchListContainerView.emptyView.isHidden = true
-            }
-            .store(in: &cancellables)
+//        Publishers.CombineLatest(
+//            viewModel.searchPublisher,
+//            screenView.searchView.textPublisher.removeDuplicates()
+//        )
+//        .receive(on: DispatchQueue.main)
+//            .sink { [unowned self] items, query in
+//                guard query.isEmpty == false else {
+//                    screenView.searchListContainerView.isHidden = true
+//                    screenView.listContainerView.isHidden = false
+//                    screenView.bringSubviewToFront(screenView.listContainerView)
+//                    return
+//                }
+//
+//                screenView.listContainerView.isHidden = true
+//                screenView.searchListContainerView.isHidden = false
+//
+//                guard items.numberOfItems > 0 else {
+//                    screenView.searchListContainerView.emptyView.isHidden = false
+//                    screenView.bringSubviewToFront(screenView.searchListContainerView)
+//                    screenView.searchListContainerView.bringSubviewToFront(screenView.searchListContainerView.emptyView)
+//                    return
+//                }
+//
+//                screenView.searchListContainerView.bringSubviewToFront(searchTableController.view)
+//                screenView.searchListContainerView.emptyView.isHidden = true
+//            }
+//            .store(in: &cancellables)
 
         screenView.searchView
             .isEditingPublisher
@@ -181,19 +188,19 @@ public final class ChatListController: UIViewController {
             .sink { [unowned self] in isEditingSearch = $0 }
             .store(in: &cancellables)
 
-        viewModel.chatsPublisher
-            .receive(on: DispatchQueue.main)
-            .sink { [unowned self] in
-                guard $0.isEmpty == false else {
-                    screenView.listContainerView.bringSubviewToFront(screenView.listContainerView.emptyView)
-                    screenView.listContainerView.emptyView.isHidden = false
-                    return
-                }
-
-                screenView.listContainerView.bringSubviewToFront(tableController.view)
-                screenView.listContainerView.emptyView.isHidden = true
-            }
-            .store(in: &cancellables)
+//        viewModel.chatsPublisher
+//            .receive(on: DispatchQueue.main)
+//            .sink { [unowned self] in
+//                guard $0.isEmpty == false else {
+//                    screenView.listContainerView.bringSubviewToFront(screenView.listContainerView.emptyView)
+//                    screenView.listContainerView.emptyView.isHidden = false
+//                    return
+//                }
+//
+//                screenView.listContainerView.bringSubviewToFront(tableController.view)
+//                screenView.listContainerView.emptyView.isHidden = true
+//            }
+//            .store(in: &cancellables)
 
         screenView.searchListContainerView
             .emptyView.searchButton
diff --git a/Sources/ChatListFeature/Controller/ChatListSearchTableController.swift b/Sources/ChatListFeature/Controller/ChatListSearchTableController.swift
index c1abc94ac78f3f3e95f1058a2e8ede9021d11616..dbef4946de693b49fe7361f943b291c21a7498d9 100644
--- a/Sources/ChatListFeature/Controller/ChatListSearchTableController.swift
+++ b/Sources/ChatListFeature/Controller/ChatListSearchTableController.swift
@@ -4,16 +4,16 @@ import Models
 import Combine
 import DependencyInjection
 
-class ChatSearchListTableViewDiffableDataSource: UITableViewDiffableDataSource<SearchSection, SearchItem> {
-    override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
-        switch snapshot().sectionIdentifiers[section] {
-        case .chats:
-            return "CHATS"
-        case .connections:
-            return "CONNECTIONS"
-        }
-    }
-}
+//class ChatSearchListTableViewDiffableDataSource: UITableViewDiffableDataSource<SearchSection, SearchItem> {
+//    override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
+//        switch snapshot().sectionIdentifiers[section] {
+//        case .chats:
+//            return "CHATS"
+//        case .connections:
+//            return "CONNECTIONS"
+//        }
+//    }
+//}
 
 final class ChatSearchTableController: UITableViewController {
     @Dependency private var coordinator: ChatListCoordinating
@@ -21,65 +21,65 @@ final class ChatSearchTableController: UITableViewController {
     private let viewModel: ChatListViewModel
     private let cellHeight: CGFloat = 83.0
     private var cancellables = Set<AnyCancellable>()
-    private var tableDataSource: ChatSearchListTableViewDiffableDataSource?
+//    private var tableDataSource: ChatSearchListTableViewDiffableDataSource?
 
     init(_ viewModel: ChatListViewModel) {
         self.viewModel = viewModel
         super.init(style: .grouped)
 
-        tableDataSource = ChatSearchListTableViewDiffableDataSource(
-            tableView: tableView
-        ) { table, indexPath, item in
-            let cell = table.dequeueReusableCell(forIndexPath: indexPath, ofType: ChatListCell.self)
-            switch item {
-            case .chat(let subitem):
-                if case .contact(let info) = subitem {
-                    cell.setupContact(
-                        name: info.contact.nickname ?? info.contact.username,
-                        image: info.contact.photo,
-                        date: Date.fromTimestamp(info.lastMessage!.timestamp),
-                        hasUnread: info.lastMessage!.unread,
-                        preview: info.lastMessage!.payload.text
-                    )
-                }
-
-                if case .group(let info) = subitem {
-                    let date: Date = {
-                        guard let lastMessage = info.lastMessage else {
-                            return info.group.createdAt
-                        }
-
-                        return Date.fromTimestamp(lastMessage.timestamp)
-                    }()
-
-                    let hasUnread: Bool = {
-                        guard let lastMessage = info.lastMessage else {
-                            return false
-                        }
-
-                        return lastMessage.unread
-                    }()
-
-                    cell.setupGroup(
-                        name: info.group.name,
-                        date: date,
-                        preview: info.lastMessage?.payload.text,
-                        hasUnread: hasUnread
-                    )
-                }
-
-            case .connection(let contact):
-                cell.setupContact(
-                    name: contact.nickname ?? contact.username,
-                    image: contact.photo,
-                    date: nil,
-                    hasUnread: false,
-                    preview: contact.username
-                )
-            }
-
-            return cell
-        }
+//        tableDataSource = ChatSearchListTableViewDiffableDataSource(
+//            tableView: tableView
+//        ) { table, indexPath, item in
+//            let cell = table.dequeueReusableCell(forIndexPath: indexPath, ofType: ChatListCell.self)
+//            switch item {
+//            case .chat(let subitem):
+//                if case .contact(let info) = subitem {
+//                    cell.setupContact(
+//                        name: info.contact.nickname ?? info.contact.username,
+//                        image: info.contact.photo,
+//                        date: Date.fromTimestamp(info.lastMessage!.timestamp),
+//                        hasUnread: info.lastMessage!.unread,
+//                        preview: info.lastMessage!.payload.text
+//                    )
+//                }
+//
+//                if case .group(let info) = subitem {
+//                    let date: Date = {
+//                        guard let lastMessage = info.lastMessage else {
+//                            return info.group.createdAt
+//                        }
+//
+//                        return Date.fromTimestamp(lastMessage.timestamp)
+//                    }()
+//
+//                    let hasUnread: Bool = {
+//                        guard let lastMessage = info.lastMessage else {
+//                            return false
+//                        }
+//
+//                        return lastMessage.unread
+//                    }()
+//
+//                    cell.setupGroup(
+//                        name: info.group.name,
+//                        date: date,
+//                        preview: info.lastMessage?.payload.text,
+//                        hasUnread: hasUnread
+//                    )
+//                }
+//
+//            case .connection(let contact):
+//                cell.setupContact(
+//                    name: contact.nickname ?? contact.username!,
+//                    image: contact.photo,
+//                    date: nil,
+//                    hasUnread: false,
+//                    preview: contact.username!
+//                )
+//            }
+//
+//            return cell
+//        }
     }
 
     required init?(coder: NSCoder) { nil }
@@ -91,13 +91,13 @@ final class ChatSearchTableController: UITableViewController {
         tableView.tableFooterView = UIView()
         tableView.sectionIndexColor = .blue
         tableView.register(ChatListCell.self)
-        tableView.dataSource = tableDataSource
+//        tableView.dataSource = tableDataSource
         view.backgroundColor = Asset.neutralWhite.color
 
-        viewModel.searchPublisher
-            .receive(on: DispatchQueue.main)
-            .sink { [unowned self] in tableDataSource?.apply($0, animatingDifferences: false) }
-            .store(in: &cancellables)
+//        viewModel.searchPublisher
+//            .receive(on: DispatchQueue.main)
+//            .sink { [unowned self] in tableDataSource?.apply($0, animatingDifferences: false) }
+//            .store(in: &cancellables)
     }
 }
 
@@ -110,19 +110,19 @@ extension ChatSearchTableController {
     }
 
     override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
-        if let item = tableDataSource?.itemIdentifier(for: indexPath) {
-            switch item {
-            case .chat(let chat):
-                switch chat {
-                case .contact(let info):
-                    guard info.contact.status == .friend else { return }
-                    coordinator.toSingleChat(with: info.contact, from: self)
-                case .group(let info):
-                    coordinator.toGroupChat(with: info, from: self)
-                }
-            case .connection(let contact):
-                coordinator.toContact(contact, from: self)
-            }
-        }
+//        if let item = tableDataSource?.itemIdentifier(for: indexPath) {
+//            switch item {
+//            case .chat(let chat):
+//                switch chat {
+//                case .contact(let info):
+//                    guard info.contact.status == .friend else { return }
+//                    coordinator.toSingleChat(with: info.contact, from: self)
+//                case .group(let info):
+//                    coordinator.toGroupChat(with: info, from: self)
+//                }
+//            case .connection(let contact):
+//                coordinator.toContact(contact, from: self)
+//            }
+//        }
     }
 }
diff --git a/Sources/ChatListFeature/Controller/ChatListTableController.swift b/Sources/ChatListFeature/Controller/ChatListTableController.swift
index 17b744113f0ecc43eb229f62cd5f4ddd5d8da385..ab8f5ec4e205f1161b73a5b678648d75feeee3b0 100644
--- a/Sources/ChatListFeature/Controller/ChatListTableController.swift
+++ b/Sources/ChatListFeature/Controller/ChatListTableController.swift
@@ -9,7 +9,7 @@ import DependencyInjection
 final class ChatListTableController: UITableViewController {
     @Dependency private var coordinator: ChatListCoordinating
 
-    private var rows = [Chat]()
+    private var rows = [Any]()
     private let viewModel: ChatListViewModel
     private let cellHeight: CGFloat = 83.0
     private var cancellables = Set<AnyCancellable>()
@@ -31,28 +31,28 @@ final class ChatListTableController: UITableViewController {
         tableView.register(ChatListCell.self)
         tableView.tableFooterView = UIView()
 
-        viewModel
-            .chatsPublisher
-            .receive(on: DispatchQueue.main)
-            .sink { [unowned self] in
-                guard !self.rows.isEmpty else {
-                    self.rows = $0
-                    tableView.reloadData()
-                    return
-                }
-
-                self.tableView.reload(
-                    using: StagedChangeset(source: self.rows, target: $0),
-                    deleteSectionsAnimation: .automatic,
-                    insertSectionsAnimation: .automatic,
-                    reloadSectionsAnimation: .none,
-                    deleteRowsAnimation: .automatic,
-                    insertRowsAnimation: .automatic,
-                    reloadRowsAnimation: .none
-                ) { [unowned self] in
-                    self.rows = $0
-                }
-            }.store(in: &cancellables)
+//        viewModel
+//            .chatsPublisher
+//            .receive(on: DispatchQueue.main)
+//            .sink { [unowned self] in
+//                guard !self.rows.isEmpty else {
+//                    self.rows = $0
+//                    tableView.reloadData()
+//                    return
+//                }
+//
+//                self.tableView.reload(
+//                    using: StagedChangeset(source: self.rows, target: $0),
+//                    deleteSectionsAnimation: .automatic,
+//                    insertSectionsAnimation: .automatic,
+//                    reloadSectionsAnimation: .none,
+//                    deleteRowsAnimation: .automatic,
+//                    insertRowsAnimation: .automatic,
+//                    reloadRowsAnimation: .none
+//                ) { [unowned self] in
+//                    self.rows = $0
+//                }
+//            }.store(in: &cancellables)
     }
 }
 
@@ -78,7 +78,7 @@ extension ChatListTableController {
 
         let delete = UIContextualAction(style: .normal, title: nil) { [weak self] _, _, complete in
             guard let self = self else { return }
-            self.didRequestDeletionOf(self.rows[indexPath.row])
+//            self.didRequestDeletionOf(self.rows[indexPath.row])
             complete(true)
         }
 
@@ -88,13 +88,13 @@ extension ChatListTableController {
     }
 
     override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
-        switch rows[indexPath.row] {
-        case .contact(let info):
-            guard info.contact.status == .friend else { return }
-            coordinator.toSingleChat(with: info.contact, from: self)
-        case .group(let info):
-            coordinator.toGroupChat(with: info, from: self)
-        }
+//        switch rows[indexPath.row] {
+//        case .contact(let info):
+//            guard info.contact.status == .friend else { return }
+//            coordinator.toSingleChat(with: info.contact, from: self)
+//        case .group(let info):
+//            coordinator.toGroupChat(with: info, from: self)
+//        }
     }
 
     override func tableView(
@@ -102,96 +102,97 @@ extension ChatListTableController {
         cellForRowAt indexPath: IndexPath
     ) -> UITableViewCell {
 
-        let cell = tableView.dequeueReusableCell(forIndexPath: indexPath, ofType: ChatListCell.self)
-
-        if case .contact(let info) = rows[indexPath.row] {
-            cell.setupContact(
-                name: info.contact.nickname ?? info.contact.username,
-                image: info.contact.photo,
-                date: Date.fromTimestamp(info.lastMessage!.timestamp),
-                hasUnread: info.lastMessage!.unread,
-                preview: info.lastMessage!.payload.text
-            )
-        }
-
-        if case .group(let info) = rows[indexPath.row] {
-            let date: Date = {
-                guard let lastMessage = info.lastMessage else {
-                    return info.group.createdAt
-                }
-
-                return Date.fromTimestamp(lastMessage.timestamp)
-            }()
-
-            let hasUnread: Bool = {
-                guard let lastMessage = info.lastMessage else {
-                    return false
-                }
-
-                return lastMessage.unread
-            }()
-
-            cell.setupGroup(
-                name: info.group.name,
-                date: date,
-                preview: info.lastMessage?.payload.text,
-                hasUnread: hasUnread
-            )
-        }
-
-        return cell
+//        let cell = tableView.dequeueReusableCell(forIndexPath: indexPath, ofType: ChatListCell.self)
+//
+//        if case .contact(let info) = rows[indexPath.row] {
+//            cell.setupContact(
+//                name: info.contact.nickname ?? info.contact.username,
+//                image: info.contact.photo,
+//                date: Date.fromTimestamp(info.lastMessage!.timestamp),
+//                hasUnread: info.lastMessage!.unread,
+//                preview: info.lastMessage!.payload.text
+//            )
+//        }
+//
+//        if case .group(let info) = rows[indexPath.row] {
+//            let date: Date = {
+//                guard let lastMessage = info.lastMessage else {
+//                    return info.group.createdAt
+//                }
+//
+//                return Date.fromTimestamp(lastMessage.timestamp)
+//            }()
+//
+//            let hasUnread: Bool = {
+//                guard let lastMessage = info.lastMessage else {
+//                    return false
+//                }
+//
+//                return lastMessage.unread
+//            }()
+//
+//            cell.setupGroup(
+//                name: info.group.name,
+//                date: date,
+//                preview: info.lastMessage?.payload.text,
+//                hasUnread: hasUnread
+//            )
+//        }
+//
+//        return cell
+        fatalError()
     }
 
-    private func didRequestDeletionOf(_ item: Chat) {
-        let title: String
-        let subtitle: String
-        let actionTitle: String
-        let actionClosure: () -> Void
-
-        switch item {
-        case .group(let info):
-            title = Localized.ChatList.DeleteGroup.title
-            subtitle = Localized.ChatList.DeleteGroup.subtitle
-            actionTitle = Localized.ChatList.DeleteGroup.action
-            actionClosure = { [weak viewModel] in viewModel?.leave(info.group) }
-
-        case .contact(let info):
-            title = Localized.ChatList.Delete.title
-            subtitle = Localized.ChatList.Delete.subtitle
-            actionTitle = Localized.ChatList.Delete.delete
-            actionClosure = { [weak viewModel] in viewModel?.clear(info.contact) }
-        }
-
-        let actionButton = DrawerCapsuleButton(model: .init(title: actionTitle, style: .red))
-
-        let drawer = DrawerController(with: [
-            DrawerText(
-                font: Fonts.Mulish.bold.font(size: 26.0),
-                text: title,
-                color: Asset.neutralActive.color,
-                alignment: .left,
-                spacingAfter: 19
-            ),
-            DrawerText(
-                font: Fonts.Mulish.regular.font(size: 16.0),
-                text: subtitle,
-                color: Asset.neutralBody.color,
-                alignment: .left,
-                lineHeightMultiple: 1.1,
-                spacingAfter: 39
-            ),
-            actionButton
-        ])
-
-        actionButton.action.receive(on: DispatchQueue.main)
-            .sink {
-                drawer.dismiss(animated: true) { [weak self] in
-                    guard let self = self else { return }
-                    self.drawerCancellables.removeAll()
-                    actionClosure()
-                }
-            }.store(in: &drawerCancellables)
-
-        coordinator.toDrawer(drawer, from: self)
-    }
+//    private func didRequestDeletionOf(_ item: Chat) {
+//        let title: String
+//        let subtitle: String
+//        let actionTitle: String
+//        let actionClosure: () -> Void
+//
+//        switch item {
+//        case .group(let info):
+//            title = Localized.ChatList.DeleteGroup.title
+//            subtitle = Localized.ChatList.DeleteGroup.subtitle
+//            actionTitle = Localized.ChatList.DeleteGroup.action
+//            actionClosure = { [weak viewModel] in viewModel?.leave(info.group) }
+//
+//        case .contact(let info):
+//            title = Localized.ChatList.Delete.title
+//            subtitle = Localized.ChatList.Delete.subtitle
+//            actionTitle = Localized.ChatList.Delete.delete
+//            actionClosure = { [weak viewModel] in viewModel?.clear(info.contact) }
+//        }
+//
+//        let actionButton = DrawerCapsuleButton(model: .init(title: actionTitle, style: .red))
+//
+//        let drawer = DrawerController(with: [
+//            DrawerText(
+//                font: Fonts.Mulish.bold.font(size: 26.0),
+//                text: title,
+//                color: Asset.neutralActive.color,
+//                alignment: .left,
+//                spacingAfter: 19
+//            ),
+//            DrawerText(
+//                font: Fonts.Mulish.regular.font(size: 16.0),
+//                text: subtitle,
+//                color: Asset.neutralBody.color,
+//                alignment: .left,
+//                lineHeightMultiple: 1.1,
+//                spacingAfter: 39
+//            ),
+//            actionButton
+//        ])
+//
+//        actionButton.action.receive(on: DispatchQueue.main)
+//            .sink {
+//                drawer.dismiss(animated: true) { [weak self] in
+//                    guard let self = self else { return }
+//                    self.drawerCancellables.removeAll()
+//                    actionClosure()
+//                }
+//            }.store(in: &drawerCancellables)
+//
+//        coordinator.toDrawer(drawer, from: self)
+//    }
 }
diff --git a/Sources/ChatListFeature/Coordinator/ChatListCoordinator.swift b/Sources/ChatListFeature/Coordinator/ChatListCoordinator.swift
index 42b7dda644aa04aa7f928ad8006d59715d442b4b..bb412859b16fe99591c8b658fc4b8798fe27fb00 100644
--- a/Sources/ChatListFeature/Coordinator/ChatListCoordinator.swift
+++ b/Sources/ChatListFeature/Coordinator/ChatListCoordinator.swift
@@ -1,6 +1,7 @@
 import UIKit
 import Shared
 import Models
+import XXModels
 import MenuFeature
 import ChatFeature
 import Presentation
@@ -16,7 +17,7 @@ public protocol ChatListCoordinating {
     func toContact(_: Contact, from: UIViewController)
     func toSingleChat(with: Contact, from: UIViewController)
     func toDrawer(_: UIViewController, from: UIViewController)
-    func toGroupChat(with: GroupChatInfo, from: UIViewController)
+    func toGroupChat(with: GroupInfo, from: UIViewController)
 }
 
 public struct ChatListCoordinator: ChatListCoordinating {
@@ -31,7 +32,7 @@ public struct ChatListCoordinator: ChatListCoordinating {
     var contactsFactory: () -> UIViewController
     var contactFactory: (Contact) -> UIViewController
     var singleChatFactory: (Contact) -> UIViewController
-    var groupChatFactory: (GroupChatInfo) -> UIViewController
+    var groupChatFactory: (GroupInfo) -> UIViewController
     var sideMenuFactory: (MenuItem, UIViewController) -> UIViewController
 
     public init(
@@ -41,7 +42,7 @@ public struct ChatListCoordinator: ChatListCoordinating {
         contactsFactory: @escaping () -> UIViewController,
         contactFactory: @escaping (Contact) -> UIViewController,
         singleChatFactory: @escaping (Contact) -> UIViewController,
-        groupChatFactory: @escaping (GroupChatInfo) -> UIViewController,
+        groupChatFactory: @escaping (GroupInfo) -> UIViewController,
         sideMenuFactory: @escaping (MenuItem, UIViewController) -> UIViewController
     ) {
         self.scanFactory = scanFactory
@@ -81,7 +82,7 @@ public extension ChatListCoordinator {
         pushPresenter.present(screen, from: parent)
     }
 
-    func toGroupChat(with group: GroupChatInfo, from parent: UIViewController) {
+    func toGroupChat(with group: GroupInfo, from parent: UIViewController) {
         let screen = groupChatFactory(group)
         pushPresenter.present(screen, from: parent)
     }
diff --git a/Sources/ChatListFeature/Models/Chat.swift b/Sources/ChatListFeature/Models/Chat.swift
index 3e159479aeaade0a5b1355d19eccf627f5d5f3b7..80106ee6bb532c08d444d00de9540afe95380ca6 100644
--- a/Sources/ChatListFeature/Models/Chat.swift
+++ b/Sources/ChatListFeature/Models/Chat.swift
@@ -1,34 +1,35 @@
-import Models
-import Foundation
-import DifferenceKit
-
-enum Chat: Equatable, Differentiable, Hashable {
-    case group(GroupChatInfo)
-    case contact(SingleChatInfo)
-
-    var differenceIdentifier: Data {
-        switch self {
-        case .contact(let info):
-            return info.contact.userId
-        case .group(let info):
-            return info.group.groupId
-        }
-    }
-
-    var orderingDate: Date {
-        switch self {
-        case .group(let info):
-            if let lastMessage = info.lastMessage {
-                return Date.fromTimestamp(lastMessage.timestamp)
-            } else {
-                return info.group.createdAt
-            }
-        case .contact(let info):
-            guard let lastMessage = info.lastMessage else {
-                fatalError("Should have an E2E chat without a last message")
-            }
-
-            return Date.fromTimestamp(lastMessage.timestamp)
-        }
-    }
-}
+//import Models
+//import XXModels
+//import Foundation
+//import DifferenceKit
+//
+//enum Chat: Equatable, Differentiable, Hashable {
+//    case group(GroupChatInfo)
+//    case contact(SingleChatInfo)
+//
+//    var differenceIdentifier: Data {
+//        switch self {
+//        case .contact(let info):
+//            return info.contact.userId
+//        case .group(let info):
+//            return info.group.groupId
+//        }
+//    }
+//
+//    var orderingDate: Date {
+//        switch self {
+//        case .group(let info):
+//            if let lastMessage = info.lastMessage {
+//                return Date.fromTimestamp(lastMessage.timestamp)
+//            } else {
+//                return info.group.createdAt
+//            }
+//        case .contact(let info):
+//            guard let lastMessage = info.lastMessage else {
+//                fatalError("Should have an E2E chat without a last message")
+//            }
+//
+//            return Date.fromTimestamp(lastMessage.timestamp)
+//        }
+//    }
+//}
diff --git a/Sources/ChatListFeature/ViewModel/ChatListViewModel.swift b/Sources/ChatListFeature/ViewModel/ChatListViewModel.swift
index ceb096f383a164fd6ead74e9819089b4e45a5807..a7cffa120aa560aa5827f6e964048427705faadd 100644
--- a/Sources/ChatListFeature/ViewModel/ChatListViewModel.swift
+++ b/Sources/ChatListFeature/ViewModel/ChatListViewModel.swift
@@ -3,6 +3,7 @@ import UIKit
 import Shared
 import Models
 import Combine
+import XXModels
 import Defaults
 import Integration
 import DependencyInjection
@@ -12,13 +13,13 @@ enum SearchSection {
     case connections
 }
 
-enum SearchItem: Equatable, Hashable {
-    case chat(Chat)
-    case connection(Contact)
-}
+//enum SearchItem: Equatable, Hashable {
+//    case chat(Chat)
+//    case connection(Contact)
+//}
 
 typealias RecentsSnapshot = NSDiffableDataSourceSnapshot<SectionId, Contact>
-typealias SearchSnapshot = NSDiffableDataSourceSnapshot<SearchSection, SearchItem>
+//typealias SearchSnapshot = NSDiffableDataSourceSnapshot<SearchSection, SearchItem>
 
 final class ChatListViewModel {
     @Dependency private var session: SessionType
@@ -27,96 +28,96 @@ final class ChatListViewModel {
         session.isOnline
     }
 
-    var chatsPublisher: AnyPublisher<[Chat], Never> {
-        chatsSubject.eraseToAnyPublisher()
-    }
+//    var chatsPublisher: AnyPublisher<[Chat], Never> {
+//        chatsSubject.eraseToAnyPublisher()
+//    }
 
     var hudPublisher: AnyPublisher<HUDStatus, Never> {
         hudSubject.eraseToAnyPublisher()
     }
 
-    var recentsPublisher: AnyPublisher<RecentsSnapshot, Never> {
-        session.contacts(.isRecent).map {
-            let section = SectionId()
-            var snapshot = RecentsSnapshot()
-            snapshot.appendSections([section])
-            snapshot.appendItems($0, toSection: section)
-            return snapshot
-        }.eraseToAnyPublisher()
-    }
-
-    var searchPublisher: AnyPublisher<SearchSnapshot, Never> {
-        Publishers.CombineLatest3(
-            session.contacts(.all),
-            chatsPublisher,
-            searchSubject
-                .removeDuplicates()
-                .debounce(for: .milliseconds(100), scheduler: DispatchQueue.main)
-                .eraseToAnyPublisher()
-        )
-            .map { (contacts, chats, query) in
-                let connectionItems = contacts.filter {
-                    let username = $0.username.lowercased().contains(query.lowercased())
-                    let nickname = $0.nickname?.lowercased().contains(query.lowercased()) ?? false
-                    return username || nickname
-                }.map(SearchItem.connection)
-
-                let chatItems = chats.filter {
-                    switch $0 {
-                    case .contact(let info):
-                        let username = info.contact.username.lowercased().contains(query.lowercased())
-                        let nickname = info.contact.nickname?.lowercased().contains(query.lowercased()) ?? false
-                        let lastMessage = info.lastMessage?.payload.text.lowercased().contains(query.lowercased()) ?? false
-                        return username || nickname || lastMessage
-
-                    case .group(let info):
-                        let name = info.group.name.lowercased().contains(query.lowercased())
-                        let last = info.lastMessage?.payload.text.lowercased().contains(query.lowercased()) ?? false
-                        return name || last
-                    }
-                }.map(SearchItem.chat)
-
-                var snapshot = SearchSnapshot()
-
-                if connectionItems.count > 0 {
-                    snapshot.appendSections([.connections])
-                    snapshot.appendItems(connectionItems, toSection: .connections)
-                }
-
-                if chatItems.count > 0 {
-                    snapshot.appendSections([.chats])
-                    snapshot.appendItems(chatItems, toSection: .chats)
-                }
-
-                return snapshot
-            }.eraseToAnyPublisher()
-    }
-
-    var badgeCountPublisher: AnyPublisher<Int, Never> {
-        Publishers.CombineLatest(
-            session.contacts(.received),
-            session.groups(.pending)
-        )
-        .map { $0.0.count + $0.1.count }
-        .eraseToAnyPublisher()
-    }
+//    var recentsPublisher: AnyPublisher<RecentsSnapshot, Never> {
+//        session.contacts(.isRecent).map {
+//            let section = SectionId()
+//            var snapshot = RecentsSnapshot()
+//            snapshot.appendSections([section])
+//            snapshot.appendItems($0, toSection: section)
+//            return snapshot
+//        }.eraseToAnyPublisher()
+//    }
+
+//    var searchPublisher: AnyPublisher<SearchSnapshot, Never> {
+//        Publishers.CombineLatest3(
+//            session.contacts(.all),
+//            chatsPublisher,
+//            searchSubject
+//                .removeDuplicates()
+//                .debounce(for: .milliseconds(100), scheduler: DispatchQueue.main)
+//                .eraseToAnyPublisher()
+//        )
+//            .map { (contacts, chats, query) in
+//                let connectionItems = contacts.filter {
+//                    let username = $0.username.lowercased().contains(query.lowercased())
+//                    let nickname = $0.nickname?.lowercased().contains(query.lowercased()) ?? false
+//                    return username || nickname
+//                }.map(SearchItem.connection)
+//
+//                let chatItems = chats.filter {
+//                    switch $0 {
+//                    case .contact(let info):
+//                        let username = info.contact.username.lowercased().contains(query.lowercased())
+//                        let nickname = info.contact.nickname?.lowercased().contains(query.lowercased()) ?? false
+//                        let lastMessage = info.lastMessage?.payload.text.lowercased().contains(query.lowercased()) ?? false
+//                        return username || nickname || lastMessage
+//
+//                    case .group(let info):
+//                        let name = info.group.name.lowercased().contains(query.lowercased())
+//                        let last = info.lastMessage?.payload.text.lowercased().contains(query.lowercased()) ?? false
+//                        return name || last
+//                    }
+//                }.map(SearchItem.chat)
+//
+//                var snapshot = SearchSnapshot()
+//
+//                if connectionItems.count > 0 {
+//                    snapshot.appendSections([.connections])
+//                    snapshot.appendItems(connectionItems, toSection: .connections)
+//                }
+//
+//                if chatItems.count > 0 {
+//                    snapshot.appendSections([.chats])
+//                    snapshot.appendItems(chatItems, toSection: .chats)
+//                }
+//
+//                return snapshot
+//            }.eraseToAnyPublisher()
+//    }
+//
+//    var badgeCountPublisher: AnyPublisher<Int, Never> {
+//        Publishers.CombineLatest(
+//            session.contacts(.received),
+//            session.groups(.pending)
+//        )
+//        .map { $0.0.count + $0.1.count }
+//        .eraseToAnyPublisher()
+//    }
 
     private var cancellables = Set<AnyCancellable>()
     private let searchSubject = CurrentValueSubject<String, Never>("")
-    private let chatsSubject = CurrentValueSubject<[Chat], Never>([])
+//    private let chatsSubject = CurrentValueSubject<[Chat], Never>([])
     private let hudSubject = CurrentValueSubject<HUDStatus, Never>(.none)
 
     init() {
-        Publishers.CombineLatest(
-            session.singleChats(.all),
-            session.groupChats(.accepted)
-        ).map {
-            let groups = $0.1.map(Chat.group)
-            let chats = $0.0.map(Chat.contact)
-            return (chats + groups).sorted { $0.orderingDate > $1.orderingDate }
-        }
-        .sink { [unowned self] in chatsSubject.send($0) }
-        .store(in: &cancellables)
+//        Publishers.CombineLatest(
+//            session.singleChats(.all),
+//            session.groupChats(.accepted)
+//        ).map {
+//            let groups = $0.1.map(Chat.group)
+//            let chats = $0.0.map(Chat.contact)
+//            return (chats + groups).sorted { $0.orderingDate > $1.orderingDate }
+//        }
+//        .sink { [unowned self] in chatsSubject.send($0) }
+//        .store(in: &cancellables)
     }
 
     func updateSearch(query: String) {
@@ -128,7 +129,7 @@ final class ChatListViewModel {
 
         do {
             try session.leave(group: group)
-            session.deleteAll(from: group)
+            try session.dbManager.deleteMessages(.init(chat: .group(group.id)))
             hudSubject.send(.none)
         } catch {
             hudSubject.send(.error(.init(with: error)))
@@ -136,6 +137,6 @@ final class ChatListViewModel {
     }
 
     func clear(_ contact: Contact) {
-        session.deleteAll(from: contact)
+        _ = try? session.dbManager.deleteMessages(.init(chat: .direct(session.myId, contact.id)))
     }
 }
diff --git a/Sources/ContactListFeature/Controllers/ContactListTableController.swift b/Sources/ContactListFeature/Controllers/ContactListTableController.swift
index 8acfc91190671f875f840b82073935505c0f1d8b..f940366ae08cfe77f9cd54e5d1008a55b7149bc6 100644
--- a/Sources/ContactListFeature/Controllers/ContactListTableController.swift
+++ b/Sources/ContactListFeature/Controllers/ContactListTableController.swift
@@ -47,8 +47,9 @@ final class ContactListTableController: UITableViewController {
     override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
         let cell: SmallAvatarAndTitleCell = tableView.dequeueReusableCell(forIndexPath: indexPath)
         let contact = sections[indexPath.section][indexPath.row]
-        cell.titleLabel.text = contact.nickname ?? contact.username
-        cell.avatarView.setupProfile(title: contact.nickname ?? contact.username, image: contact.photo, size: .medium)
+        let name = (contact.nickname ?? contact.username) ?? "Fetching username..."
+        cell.titleLabel.text = name
+        cell.avatarView.setupProfile(title: name, image: contact.photo, size: .medium)
         return cell
     }
 
diff --git a/Sources/ContactListFeature/Controllers/CreateGroupController.swift b/Sources/ContactListFeature/Controllers/CreateGroupController.swift
index f5a0ebd656b8bd41e216342ccceddd98f3338c00..5d0690ffa0869de661b537adbd86f27df93527e9 100644
--- a/Sources/ContactListFeature/Controllers/CreateGroupController.swift
+++ b/Sources/ContactListFeature/Controllers/CreateGroupController.swift
@@ -74,7 +74,7 @@ public final class CreateGroupController: UIViewController {
         ) { [weak viewModel] collectionView, indexPath, contact in
             let cell: CreateGroupCollectionCell = collectionView.dequeueReusableCell(forIndexPath: indexPath)
 
-            cell.setup(title: contact.nickname ?? contact.username, image: contact.photo)
+            cell.setup(title: contact.nickname ?? contact.username!, image: contact.photo)
             cell.didTapRemove = { viewModel?.didSelect(contact: contact) }
 
             return cell
@@ -85,7 +85,7 @@ public final class CreateGroupController: UIViewController {
         ) { [weak self] tableView, indexPath, contact in
             let cell = tableView.dequeueReusableCell(forIndexPath: indexPath, ofType: SmallAvatarAndTitleCell.self)
             cell.titleLabel.text = contact.nickname ?? contact.username
-            cell.avatarView.setupProfile(title: contact.nickname ?? contact.username, image: contact.photo, size: .medium)
+            cell.avatarView.setupProfile(title: contact.nickname ?? contact.username!, image: contact.photo, size: .medium)
 
             if let selectedElements = self?.selectedElements, selectedElements.contains(contact) {
                 tableView.selectRow(at: indexPath, animated: true, scrollPosition: .none)
@@ -183,3 +183,9 @@ extension CreateGroupController: UITableViewDelegate {
         }
     }
 }
+
+extension Contact: Hashable {
+    public func hash(into hasher: inout Hasher) {
+        hasher.combine(id)
+    }
+}
diff --git a/Sources/ContactListFeature/Coordinator/ContactListCoordinator.swift b/Sources/ContactListFeature/Coordinator/ContactListCoordinator.swift
index 8cf906fa621df4ae1dca668b40df77cd2ae14149..baf38264a85467038888c2b37e618598a7190acd 100644
--- a/Sources/ContactListFeature/Coordinator/ContactListCoordinator.swift
+++ b/Sources/ContactListFeature/Coordinator/ContactListCoordinator.swift
@@ -16,7 +16,7 @@ public protocol ContactListCoordinating {
     func toSideMenu(from: UIViewController)
     func toContact(_: Contact, from: UIViewController)
     func toSingleChat(with: Contact, from: UIViewController)
-    func toGroupChat(with: GroupChatInfo, from: UIViewController)
+    func toGroupChat(with: GroupInfo, from: UIViewController)
     func toGroupDrawer(with: Int, from: UIViewController, _: @escaping (String, String?) -> Void)
 }
 
@@ -32,7 +32,7 @@ public struct ContactListCoordinator: ContactListCoordinating {
     var requestsFactory: () -> UIViewController
     var contactFactory: (Contact) -> UIViewController
     var singleChatFactory: (Contact) -> UIViewController
-    var groupChatFactory: (GroupChatInfo) -> UIViewController
+    var groupChatFactory: (GroupInfo) -> UIViewController
     var sideMenuFactory: (MenuItem, UIViewController) -> UIViewController
     var groupDrawerFactory: (Int, @escaping (String, String?) -> Void) -> UIViewController
 
@@ -43,7 +43,7 @@ public struct ContactListCoordinator: ContactListCoordinating {
         requestsFactory: @escaping () -> UIViewController,
         contactFactory: @escaping (Contact) -> UIViewController,
         singleChatFactory: @escaping (Contact) -> UIViewController,
-        groupChatFactory: @escaping (GroupChatInfo) -> UIViewController,
+        groupChatFactory: @escaping (GroupInfo) -> UIViewController,
         sideMenuFactory: @escaping (MenuItem, UIViewController) -> UIViewController,
         groupDrawerFactory: @escaping (Int, @escaping (String, String?) -> Void) -> UIViewController
     ) {
@@ -102,7 +102,7 @@ public extension ContactListCoordinator {
         pushPresenter.present(screen, from: parent)
     }
 
-    func toGroupChat(with info: GroupChatInfo, from parent: UIViewController) {
+    func toGroupChat(with info: GroupInfo, from parent: UIViewController) {
         let screen = groupChatFactory(info)
         pushPresenter.present(screen, from: parent)
     }
diff --git a/Sources/ContactListFeature/Helpers/IndexedListCollator.swift b/Sources/ContactListFeature/Helpers/IndexedListCollator.swift
new file mode 100644
index 0000000000000000000000000000000000000000..249af6fad35653a205a8d485dbab088cc8ad9578
--- /dev/null
+++ b/Sources/ContactListFeature/Helpers/IndexedListCollator.swift
@@ -0,0 +1,53 @@
+import UIKit
+import XXModels
+
+public protocol IndexableItem {
+    var indexedOn: NSString { get }
+}
+
+class IndexedListCollator<Item: IndexableItem> {
+    private final class CollationWrapper: NSObject {
+        let value: Any
+        @objc let indexedOn: NSString
+
+        init(value: Any, indexedOn: NSString) {
+            self.value = value
+            self.indexedOn = indexedOn
+        }
+
+        func unwrappedValue<UnwrappedType>() -> UnwrappedType {
+            return value as! UnwrappedType
+        }
+    }
+
+    public init() {}
+
+    public func sectioned(items: [Item]) -> (sections: [[Item]], collation: UILocalizedIndexedCollation) {
+        let collation = UILocalizedIndexedCollation.current()
+        let selector = #selector(getter: CollationWrapper.indexedOn)
+
+        let wrappedItems = items.map { item in
+            CollationWrapper(value: item, indexedOn: item.indexedOn)
+        }
+
+        let sortedObjects = collation.sortedArray(from: wrappedItems, collationStringSelector: selector) as! [CollationWrapper]
+
+        var sections = collation.sectionIndexTitles.map { _ in [Item]() }
+        sortedObjects.forEach { item in
+            let sectionNumber = collation.section(for: item, collationStringSelector: selector)
+            sections[sectionNumber].append(item.unwrappedValue())
+        }
+
+        return (sections: sections.filter { !$0.isEmpty }, collation: collation)
+    }
+}
+
+extension Contact: IndexableItem {
+    public var indexedOn: NSString {
+        guard let nickname = nickname else {
+            return "\(username!.first!)" as NSString
+        }
+
+        return "\(nickname.first!)" as NSString
+    }
+}
diff --git a/Sources/ContactListFeature/ViewModels/ContactListViewModel.swift b/Sources/ContactListFeature/ViewModels/ContactListViewModel.swift
index 72e4582e977fae4f0377923baf099fb754cbcc79..a21ec093a9c251354bd208804076212166a749d4 100644
--- a/Sources/ContactListFeature/ViewModels/ContactListViewModel.swift
+++ b/Sources/ContactListFeature/ViewModels/ContactListViewModel.swift
@@ -8,27 +8,26 @@ final class ContactListViewModel {
     @Dependency private var session: SessionType
 
     var contacts: AnyPublisher<[Contact], Never> {
-        session.contacts(.friends).eraseToAnyPublisher()
+        session.dbManager.fetchContactsPublisher(.init(authStatus: [.friend]))
+            .assertNoFailure()
+            .eraseToAnyPublisher()
     }
 
     var requestCount: AnyPublisher<Int, Never> {
-        Publishers.CombineLatest(
-            session.contacts(.received),
-            session.groups(.pending)
-        ).map { (contacts, groups) in
-            let contactRequests = contacts.filter {
-                $0.status == .verified ||
-                $0.status == .confirming ||
-                $0.status == .confirmationFailed ||
-                $0.status == .verificationFailed ||
-                $0.status == .verificationInProgress
-            }
+        let groupQuery = Group.Query(authStatus: [.pending])
+        let contactsQuery = Contact.Query(authStatus: [
+            .verified,
+            .confirming,
+            .confirmationFailed,
+            .verificationFailed,
+            .verificationInProgress
+        ])
 
-            let groupRequests = groups.filter {
-                $0.status == .pending
-            }
-
-            return contactRequests.count + groupRequests.count
-        }.eraseToAnyPublisher()
+        return Publishers.CombineLatest(
+            session.dbManager.fetchContactsPublisher(contactsQuery).catch { _ in Just([]) },
+            session.dbManager.fetchGroupsPublisher(groupQuery).catch { _ in Just([]) }
+        )
+        .map { $0.0.count + $0.1.count }
+        .eraseToAnyPublisher()
     }
 }
diff --git a/Sources/ContactListFeature/ViewModels/CreateGroupViewModel.swift b/Sources/ContactListFeature/ViewModels/CreateGroupViewModel.swift
index 3c421bfe59b5640d00f6f371a3b7a87dd4ba71e1..38717ac21153c846c1165fa023369fc7b15305f1 100644
--- a/Sources/ContactListFeature/ViewModels/CreateGroupViewModel.swift
+++ b/Sources/ContactListFeature/ViewModels/CreateGroupViewModel.swift
@@ -28,13 +28,13 @@ final class CreateGroupViewModel {
         hudRelay.eraseToAnyPublisher()
     }
 
-    var info: AnyPublisher<GroupChatInfo, Never> {
+    var info: AnyPublisher<GroupInfo, Never> {
         infoRelay.eraseToAnyPublisher()
     }
 
     private var allContacts = [Contact]()
     private var cancellables = Set<AnyCancellable>()
-    private let infoRelay = PassthroughSubject<GroupChatInfo, Never>()
+    private let infoRelay = PassthroughSubject<GroupInfo, Never>()
     private let hudRelay = CurrentValueSubject<HUDStatus, Never>(.none)
     private let contactsRelay = CurrentValueSubject<[Contact], Never>([])
     private let selectedContactsRelay = CurrentValueSubject<[Contact], Never>([])
@@ -42,8 +42,9 @@ final class CreateGroupViewModel {
     // MARK: Lifecycle
 
     init() {
-        session.contacts(.friends)
-            .map { $0.sorted(by: { $0.username < $1.username })}
+        session.dbManager.fetchContactsPublisher(.init(authStatus: [.friend]))
+            .assertNoFailure()
+            .map { $0.sorted(by: { $0.username! < $1.username! })}
             .sink { [unowned self] in
                 allContacts = $0
                 contactsRelay.send($0)
@@ -66,7 +67,7 @@ final class CreateGroupViewModel {
             return
         }
 
-        contactsRelay.send(allContacts.filter { $0.username.contains(text.lowercased()) })
+        contactsRelay.send(allContacts.filter { $0.username!.contains(text.lowercased()) })
     }
 
     func create(name: String, welcome: String?, members: [Contact]) {
@@ -78,8 +79,8 @@ final class CreateGroupViewModel {
             self.hudRelay.send(.none)
 
             switch $0 {
-            case .success((let group, let members)):
-                self.infoRelay.send(.init(group: group, members: members))
+            case .success(let info):
+                self.infoRelay.send(info)
             case .failure(let error):
                 self.hudRelay.send(.error(.init(with: error)))
             }
diff --git a/Sources/Integration/Mocks/BindingsMock.swift b/Sources/Integration/Mocks/BindingsMock.swift
index 61ccceb42f58ccae7adc40a76c4e13d230f53a47..8161105f3a53739334440786612165dd342b3b90 100644
--- a/Sources/Integration/Mocks/BindingsMock.swift
+++ b/Sources/Integration/Mocks/BindingsMock.swift
@@ -120,7 +120,7 @@ public final class BindingsMock: BindingsInterface {
             self?.requestsSubject.send(.carlRequested)
             self?.requestsSubject.send(.angelinaRequested)
             self?.requestsSubject.send(.elonRequested)
-            self?.groupRequestsSubject.send(.mockGroup)
+            //self?.groupRequestsSubject.send(.mockGroup)
 
             DispatchQueue.global().asyncAfter(deadline: .now() + 1) { [weak self] in
                 self?.confirmationsSubject.send(.georgeDiscovered)
diff --git a/Sources/Integration/Session/Session+Group.swift b/Sources/Integration/Session/Session+Group.swift
index ced2ada351cc2d0bb1ae5bb120a257be306565e7..b0bd6efd513b2118468f459e0041db9eac7d55ab 100644
--- a/Sources/Integration/Session/Session+Group.swift
+++ b/Sources/Integration/Session/Session+Group.swift
@@ -2,8 +2,6 @@ import Models
 import XXModels
 import Foundation
 
-public typealias GroupCompletion = (Result<(Group, [GroupMember]), Error>) -> Void
-
 extension Session {
     public func join(group: Group) throws {
         guard let manager = client.groupManager else { fatalError("A group manager was not created") }
@@ -21,7 +19,12 @@ extension Session {
         try dbManager.deleteGroup(group)
     }
 
-    public func createGroup(name: String, welcome: String?, members: [Contact], _ completion: @escaping GroupCompletion) {
+    public func createGroup(
+        name: String,
+        welcome: String?,
+        members: [Contact],
+        _ completion: @escaping (Result<GroupInfo, Error>) -> Void
+    ) {
         guard let manager = client.groupManager else { fatalError("A group manager was not created") }
 
         let me = client.bindings.meMarshalled
@@ -32,8 +35,7 @@ extension Session {
 
             switch $0 {
             case .success(let group):
-                completion(.success((group, self.processGroupCreation(group, memberIds: memberIds, welcome: welcome))))
-                break
+                completion(.success(self.processGroupCreation(group, memberIds: memberIds, welcome: welcome)))
             case .failure(let error):
                 completion(.failure(error))
             }
@@ -41,60 +43,47 @@ extension Session {
     }
 
     @discardableResult
-    func processGroupCreation(_ group: Group, memberIds: [Data], welcome: String?) -> [GroupMember] {
-        // TODO: Implement this checking on which members of the group are my members etc.
+    func processGroupCreation(_ group: Group, memberIds: [Data], welcome: String?) -> GroupInfo {
+        /// Save the group
+        ///
+        _ = try? dbManager.saveGroup(group)
+
+        /// Save the members
+        ///
+        memberIds.forEach { _ = try? dbManager.saveGroupMember(.init(groupId: group.id, contactId: $0)) }
+
+        /// Save the welcome message (if any)
+        ///
+        if let welcome = welcome {
+            _ = try? dbManager.saveMessage(.init(
+                networkId: nil,
+                senderId: group.leaderId,
+                recipientId: client.bindings.meMarshalled,
+                groupId: group.id,
+                date: Date(),
+                status: .received,
+                isUnread: true,
+                text: welcome,
+                replyMessageId: nil,
+                roundURL: nil,
+                fileTransferId: nil
+            ))
+        }
 
-//        try! dbManager.saveGroup(group)
-//
-//        var members: [GroupMember] = []
-//
-//        if let contactsOnGroup: [Contact] = try? dbManager.fetchContacts(.init(id: Set(memberIds)) {
-//            //contactsOnGroup.forEach { members.append(GroupMember(contact: $0, group: group)) }
-//        }
-//
-//        let strangersOnGroup = memberIds
-//            .filter { !members.map { $0.contactId }.contains($0) }
-//            .filter { $0 != client.bindings.myId }
-//
-//        if !strangersOnGroup.isEmpty {
-//            for stranger in strangersOnGroup.enumerated() {
-//                members.append(GroupMember(
-//                    userId: stranger.element,
-//                    groupId: group.groupId,
-//                    status: .pendingUsername,
-//                    username: "Fetching username...",
-//                    photo: nil
-//                ))
-//            }
-//        }
-//
-//        members.forEach { try! dbManager.saveGroupMember($0) }
-//
-//        if group.leaderId != client.bindings.meMarshalled, inappnotifications {
-//            DeviceFeedback.sound(.contactAdded)
-//            DeviceFeedback.shake(.notification)
-//        }
-//
-//        scanStrangers {}
-//
-//        if let welcome = welcome {
-//            _ = try? dbManager.saveMessage(.init(
-//                networkId: nil,
-//                senderId: group.leaderId,
-//                recipientId: client.bindings.meMarshalled,
-//                groupId: group.id,
-//                date: Date(),
-//                status: .received,
-//                isUnread: true,
-//                text: welcome,
-//                replyMessageId: nil,
-//                roundURL: nil,
-//                fileTransferId: nil
-//            ))
-//        }
-//
-//        return members
-        fatalError()
+        /// Buzz if the group was not created by me
+        ///
+        if group.leaderId != client.bindings.meMarshalled, inappnotifications {
+            DeviceFeedback.sound(.contactAdded)
+            DeviceFeedback.shake(.notification)
+        }
+
+        scanStrangers {}
+
+        guard let info = try? dbManager.fetchGroupInfos(.init(groupId: group.id)).first else {
+            fatalError()
+        }
+
+        return info
     }
 }
 
@@ -162,7 +151,7 @@ extension Session {
     }
 
     public func scanStrangers(_ completion: @escaping () -> Void) {
-        // TODO: How this will work?
+        // TODO: Needs a request for Contacts without username set
 
 //        DispatchQueue.global().async { [weak self] in
 //            guard let self = self, let ud = self.client.userDiscovery else { return }
diff --git a/Sources/Integration/Session/SessionType.swift b/Sources/Integration/Session/SessionType.swift
index 19b020dbcca50e9e9bb43a64d46faf02cc569064..fbb531ed07795dd6d22fea7fc1409ddfb85da11a 100644
--- a/Sources/Integration/Session/SessionType.swift
+++ b/Sources/Integration/Session/SessionType.swift
@@ -63,6 +63,6 @@ public protocol SessionType {
         name: String,
         welcome: String?,
         members: [Contact],
-        _ completion: @escaping (Result<(Group, [GroupMember]), Error>) -> Void
+        _ completion: @escaping (Result<GroupInfo, Error>) -> Void
     )
 }
diff --git a/Sources/LaunchFeature/LaunchCoordinator.swift b/Sources/LaunchFeature/LaunchCoordinator.swift
index 59f6c5f2f5ed421f2109c6226d154d8e85c1a190..4f5a56291b19634df4c46edc2634acb55ba5812e 100644
--- a/Sources/LaunchFeature/LaunchCoordinator.swift
+++ b/Sources/LaunchFeature/LaunchCoordinator.swift
@@ -8,7 +8,7 @@ public protocol LaunchCoordinating {
     func toRequests(from: UIViewController)
     func toOnboarding(with: String, from: UIViewController)
     func toSingleChat(with: Contact, from: UIViewController)
-    func toGroupChat(with: GroupChatInfo, from: UIViewController)
+    func toGroupChat(with: GroupInfo, from: UIViewController)
 }
 
 public struct LaunchCoordinator: LaunchCoordinating {
@@ -18,14 +18,14 @@ public struct LaunchCoordinator: LaunchCoordinating {
     var chatListFactory: () -> UIViewController
     var onboardingFactory: (String) -> UIViewController
     var singleChatFactory: (Contact) -> UIViewController
-    var groupChatFactory: (GroupChatInfo) -> UIViewController
+    var groupChatFactory: (GroupInfo) -> UIViewController
 
     public init(
         requestsFactory: @escaping () -> UIViewController,
         chatListFactory: @escaping () -> UIViewController,
         onboardingFactory: @escaping (String) -> UIViewController,
         singleChatFactory: @escaping (Contact) -> UIViewController,
-        groupChatFactory: @escaping (GroupChatInfo) -> UIViewController
+        groupChatFactory: @escaping (GroupInfo) -> UIViewController
     ) {
         self.requestsFactory = requestsFactory
         self.chatListFactory = chatListFactory
@@ -57,7 +57,7 @@ public extension LaunchCoordinator {
         replacePresenter.present(chatListScreen, singleChatScreen, from: parent)
     }
 
-    func toGroupChat(with group: GroupChatInfo, from parent: UIViewController) {
+    func toGroupChat(with group: GroupInfo, from parent: UIViewController) {
         let chatListScreen = chatListFactory()
         let groupChatScreen = groupChatFactory(group)
         replacePresenter.present(chatListScreen, groupChatScreen, from: parent)
diff --git a/Sources/LaunchFeature/LaunchViewModel.swift b/Sources/LaunchFeature/LaunchViewModel.swift
index 0eb26949bdbcb0d3b9382dc1f53b90726faf4ae9..054432b4403e7177bf129c21680a58ac4abb6b12 100644
--- a/Sources/LaunchFeature/LaunchViewModel.swift
+++ b/Sources/LaunchFeature/LaunchViewModel.swift
@@ -120,23 +120,21 @@ final class LaunchViewModel {
     }
 
     func getContactWith(userId: Data) -> Contact? {
-        fatalError()
-//        guard let session = try? DependencyInjection.Container.shared.resolve() as SessionType,
-//              let contact = session.getContactWith(userId: userId) else {
-//            return nil
-//        }
-//
-//        return contact
+        guard let session = try? DependencyInjection.Container.shared.resolve() as SessionType,
+              let contact = try? session.dbManager.fetchContacts(.init(id: [userId])).first else {
+            return nil
+        }
+
+        return contact
     }
 
-    func getGroupInfoWith(groupId: Data) -> GroupChatInfo? {
-        fatalError()
-//        guard let session: SessionType = try? DependencyInjection.Container.shared.resolve(),
-//              let info = session.getGroupChatInfoWith(groupId: groupId) else {
-//            return nil
-//        }
-//
-//        return info
+    func getGroupInfoWith(groupId: Data) -> GroupInfo? {
+        guard let session: SessionType = try? DependencyInjection.Container.shared.resolve(),
+              let info = try? session.dbManager.fetchGroupInfos(.init(groupId: groupId)).first else {
+            return nil
+        }
+
+        return info
     }
 
     private func versionFailed(error: Error) {
diff --git a/Sources/RequestsFeature/Controllers/RequestsReceivedController.swift b/Sources/RequestsFeature/Controllers/RequestsReceivedController.swift
index c92d5cb26e82317750b6b039bad65c563c1f7eea..e9de6a320d86525050224105aa8e6fa1d374b733 100644
--- a/Sources/RequestsFeature/Controllers/RequestsReceivedController.swift
+++ b/Sources/RequestsFeature/Controllers/RequestsReceivedController.swift
@@ -3,6 +3,7 @@ import UIKit
 import Models
 import Shared
 import Combine
+import XXModels
 import Countries
 import ToastFeature
 import DrawerFeature
@@ -110,10 +111,10 @@ extension RequestsReceivedController: UICollectionViewDelegate {
 
         switch request {
         case .group(let group):
-            guard group.status == .pending || group.status == .hidden else { return }
+            guard group.authStatus == .pending || group.authStatus == .hidden else { return }
             presentGroupRequestDrawer(forGroup: group)
         case .contact(let contact):
-            guard contact.status == .verified || contact.status == .hidden else { return }
+            guard contact.authStatus == .verified || contact.authStatus == .hidden else { return }
             presentSingleRequestDrawer(forContact: contact)
         }
     }
@@ -211,7 +212,7 @@ extension RequestsReceivedController {
 
         let drawerNickname = DrawerText(
             font: Fonts.Mulish.extraBold.font(size: 26.0),
-            text: contact.nickname ?? contact.username,
+            text: contact.nickname ?? contact.username!,
             color: Asset.neutralDark.color,
             spacingAfter: 20
         )
@@ -392,7 +393,7 @@ extension RequestsReceivedController {
 
         let drawerUsername = DrawerText(
             font: Fonts.Mulish.extraBold.font(size: 26.0),
-            text: contact.username,
+            text: contact.username!,
             color: Asset.neutralDark.color,
             spacingAfter: 25
         )
@@ -452,7 +453,7 @@ extension RequestsReceivedController {
         items.append(drawerNicknameTitle)
 
         let drawerNicknameInput = DrawerInput(
-            placeholder: contact.username,
+            placeholder: contact.username!,
             validator: .init(
                 wrongIcon: .image(Asset.sharedError.image),
                 correctIcon: .image(Asset.sharedSuccess.image),
diff --git a/Sources/RequestsFeature/Coordinator/RequestsCoordinator.swift b/Sources/RequestsFeature/Coordinator/RequestsCoordinator.swift
index 9a570bc2172552dbed46a50181c0fac862e940cd..3c798cfc0243bffbbf54939c37d7ea7eb90c1294 100644
--- a/Sources/RequestsFeature/Coordinator/RequestsCoordinator.swift
+++ b/Sources/RequestsFeature/Coordinator/RequestsCoordinator.swift
@@ -12,8 +12,8 @@ public protocol RequestsCoordinating {
     func toSideMenu(from: UIViewController)
     func toContact(_: Contact, from: UIViewController)
     func toSingleChat(with: Contact, from: UIViewController)
+    func toGroupChat(with: GroupInfo, from: UIViewController)
     func toDrawer(_:  UIViewController, from: UIViewController)
-    func toGroupChat(with: GroupChatInfo, from: UIViewController)
     func toDrawerBottom(_:  UIViewController, from: UIViewController)
     func toNickname(from: UIViewController, prefilled: String, _: @escaping StringClosure)
 }
@@ -27,7 +27,7 @@ public struct RequestsCoordinator: RequestsCoordinating {
     var searchFactory: () -> UIViewController
     var contactFactory: (Contact) -> UIViewController
     var singleChatFactory: (Contact) -> UIViewController
-    var groupChatFactory: (GroupChatInfo) -> UIViewController
+    var groupChatFactory: (GroupInfo) -> UIViewController
     var sideMenuFactory: (MenuItem, UIViewController) -> UIViewController
     var nicknameFactory: (String, @escaping StringClosure) -> UIViewController
 
@@ -35,7 +35,7 @@ public struct RequestsCoordinator: RequestsCoordinating {
         searchFactory: @escaping () -> UIViewController,
         contactFactory: @escaping (Contact) -> UIViewController,
         singleChatFactory: @escaping (Contact) -> UIViewController,
-        groupChatFactory: @escaping (GroupChatInfo) -> UIViewController,
+        groupChatFactory: @escaping (GroupInfo) -> UIViewController,
         sideMenuFactory: @escaping (MenuItem, UIViewController) -> UIViewController,
         nicknameFactory: @escaping (String, @escaping StringClosure) -> UIViewController
     ) {
@@ -58,7 +58,7 @@ public extension RequestsCoordinator {
     }
 
     func toGroupChat(
-        with info: GroupChatInfo,
+        with info: GroupInfo,
         from parent: UIViewController
     ) {
         let screen = groupChatFactory(info)
diff --git a/Sources/RequestsFeature/Models/Request.swift b/Sources/RequestsFeature/Models/Request.swift
index 097b55b85aa4e2efe4a106c44e2efda30a5dc9d6..2048d39962a7ccb968a6aa38759892b48c47ba63 100644
--- a/Sources/RequestsFeature/Models/Request.swift
+++ b/Sources/RequestsFeature/Models/Request.swift
@@ -16,7 +16,7 @@ enum Request: Hashable, Equatable {
         case .group:
             return .verified
         case .contact(let contact):
-            return contact.status.toRequestStatus()
+            return contact.authStatus.toRequestStatus()
         }
     }
 
@@ -41,7 +41,7 @@ enum RequestStatus {
     case failedToRequest
 }
 
-extension Contact.Status {
+extension Contact.AuthStatus {
     func toRequestStatus() -> RequestStatus {
         switch self {
         case .friend, .stranger:
@@ -67,3 +67,15 @@ extension Contact.Status {
         }
     }
 }
+
+extension Contact: Hashable {
+    public func hash(into hasher: inout Hasher) {
+        hasher.combine(id)
+    }
+}
+
+extension Group: Hashable {
+    public func hash(into hasher: inout Hasher) {
+        hasher.combine(id)
+    }
+}
diff --git a/Sources/RequestsFeature/ViewModels/RequestsFailedViewModel.swift b/Sources/RequestsFeature/ViewModels/RequestsFailedViewModel.swift
index f67882bb3fca1237ac3f138b549d262755e7fafe..9214c9ef3f19d337ae66f0f9a6c8368741ff1096 100644
--- a/Sources/RequestsFeature/ViewModels/RequestsFailedViewModel.swift
+++ b/Sources/RequestsFeature/ViewModels/RequestsFailedViewModel.swift
@@ -24,7 +24,8 @@ final class RequestsFailedViewModel {
     var backgroundScheduler: AnySchedulerOf<DispatchQueue> = DispatchQueue.global().eraseToAnyScheduler()
 
     init() {
-        session.contacts(.failed)
+        session.dbManager.fetchContactsPublisher(.init(authStatus: [.requestFailed]))
+            .assertNoFailure()
             .map { data -> NSDiffableDataSourceSnapshot<Section, Request> in
                 var snapshot = NSDiffableDataSourceSnapshot<Section, Request>()
                 snapshot.appendSections([.appearing])
diff --git a/Sources/RequestsFeature/ViewModels/RequestsReceivedViewModel.swift b/Sources/RequestsFeature/ViewModels/RequestsReceivedViewModel.swift
index e231498dbb03f04bb7ae1267fb9884d556f1f8a4..52a4fe660deefc6b49cca421b44f98e41759a193 100644
--- a/Sources/RequestsFeature/ViewModels/RequestsReceivedViewModel.swift
+++ b/Sources/RequestsFeature/ViewModels/RequestsReceivedViewModel.swift
@@ -48,51 +48,60 @@ final class RequestsReceivedViewModel {
     private let groupConfirmationSubject = PassthroughSubject<Group, Never>()
     private let contactConfirmationSubject = PassthroughSubject<Contact, Never>()
     private let itemsSubject = CurrentValueSubject<NSDiffableDataSourceSnapshot<Section, RequestReceived>, Never>(.init())
-    private var groupChats = [GroupChatInfo]()
 
     var backgroundScheduler: AnySchedulerOf<DispatchQueue> = DispatchQueue.global().eraseToAnyScheduler()
 
     init() {
-        Publishers.CombineLatest4(
-            session.groups(.pending),
-            session.contacts(.all),
-            session.groupMembers(.all),
+        let groupsQuery = Group.Query(
+            authStatus: [
+                .hidden,
+                .pending
+            ])
+
+        let contactsQuery = Contact.Query(
+            authStatus: [
+                .hidden,
+                .verified,
+                .verificationFailed,
+                .verificationInProgress
+            ])
+
+        let groupStream = session.dbManager.fetchGroupsPublisher(groupsQuery).assertNoFailure()
+        let contactsStream = session.dbManager.fetchContactsPublisher(contactsQuery).assertNoFailure()
+
+        Publishers.CombineLatest3(
+            groupStream,
+            contactsStream,
             updateSubject.eraseToAnyPublisher()
         )
         .subscribe(on: DispatchQueue.main)
         .receive(on: DispatchQueue.global())
         .map { [unowned self] data -> NSDiffableDataSourceSnapshot<Section, RequestReceived> in
-            let contactRequests = data.1.filter {
-                $0.status == .hidden ||
-                $0.status == .verified ||
-                $0.status == .verificationFailed ||
-                $0.status == .verificationInProgress
-            }
-
             var snapshot = NSDiffableDataSourceSnapshot<Section, RequestReceived>()
             snapshot.appendSections([.appearing, .hidden])
 
-            let requests = data.0.map(Request.group) + contactRequests.map(Request.contact)
+            let requests = data.0.map(Request.group) + data.1.map(Request.contact)
             let receivedRequests = requests.map { request -> RequestReceived in
                 switch request {
                 case let .group(group):
-                    var leaderTitle = ""
 
-                    if let leader = data.1.first(where: { $0.userId == group.leader }) {
-                        leaderTitle = leader.nickname ?? leader.username
-                    } else if let leader = data.2.first(where: { $0.userId == group.leader }) {
-                        leaderTitle = leader.username
+                    func leaderName() -> String {
+                        if let leader = data.1.first(where: { $0.id == group.leaderId }) {
+                            return (leader.nickname ?? leader.username) ?? "Leader is not a friend"
+                        } else {
+                            return "[Error retrieving leader]"
+                        }
                     }
 
                     return RequestReceived(
                         request: request,
-                        isHidden: group.status == .hidden,
-                        leader: leaderTitle
+                        isHidden: group.authStatus == .hidden,
+                        leader: leaderName()
                     )
                 case let .contact(contact):
                     return RequestReceived(
                         request: request,
-                        isHidden: contact.status == .hidden,
+                        isHidden: contact.authStatus == .hidden,
                         leader: nil
                     )
                 }
@@ -113,10 +122,6 @@ final class RequestsReceivedViewModel {
             receiveCompletion: { _ in },
             receiveValue: { [unowned self] in itemsSubject.send($0) }
         ).store(in: &cancellables)
-
-        session.groupChats(.accepted)
-            .sink { [unowned self] in groupChats = $0 }
-            .store(in: &cancellables)
     }
 
     func didToggleHiddenRequestsSwitcher() {
@@ -137,7 +142,10 @@ final class RequestsReceivedViewModel {
     }
 
     func didRequestHide(group: Group) {
-        session.hideRequestOf(group: group)
+        if var group = try? session.dbManager.fetchGroups(.init(id: [group.id])).first {
+            group.authStatus = .hidden
+            _ = try? session.dbManager.saveGroup(group)
+        }
     }
 
     func didRequestAccept(group: Group) {
@@ -158,52 +166,55 @@ final class RequestsReceivedViewModel {
         _ group: Group,
         _ completion: @escaping (Result<[DrawerTableCellModel], Error>) -> Void
     ) {
-        session.scanStrangers { [weak self] in
-            guard let self = self else { return }
-
-            Publishers.CombineLatest(
-                self.session.contacts(.all),
-                self.session.groupMembers(.fromGroup(group.groupId))
-            )
-            .sink { (allContacts, groupMembers) in
-
-                guard !groupMembers.map(\.status).contains(.pendingUsername) else {
-                    completion(.failure(NSError.create(""))) // Some members are still pending username lookup...
-                    return
-                }
-
-                // Now that all members are set with their usernames lets find our friends:
-                //
-                let contactsAlsoMembers = allContacts.filter { groupMembers.map(\.userId).contains($0.userId) }
-                let membersNonContacts = groupMembers.filter { !contactsAlsoMembers.map(\.userId).contains($0.userId) }
-
-                var models = [DrawerTableCellModel]()
-
-                contactsAlsoMembers.forEach {
-                    models.append(.init(
-                        title: $0.nickname ?? $0.username,
-                        image: $0.photo,
-                        isCreator: $0.userId == group.leader,
-                        isConnection: true
-                    ))
-                }
-
-                membersNonContacts.forEach {
-                    models.append(.init(
-                        title: $0.username,
-                        image: nil,
-                        isCreator: $0.userId == group.leader,
-                        isConnection: false
-                    ))
-                }
-
-                completion(.success(models))
-            }.store(in: &self.cancellables)
-        }
+//        session.scanStrangers { [weak self] in
+//            guard let self = self else { return }
+//
+//            Publishers.CombineLatest(
+//                self.session.dbManager.fetchContactsPublisher(.init()).assertNoFailure(),
+//                self.session.dbManager.fetchGroupInfosPublisher(.init(groupId: group.id)).assertNoFailure()
+//            )
+//            .sink { (allContacts, groupMembers) in
+//
+//                guard !groupMembers.map(\.authStatus).contains(.pendingUsername) else {
+//                    completion(.failure(NSError.create(""))) // Some members are still pending username lookup...
+//                    return
+//                }
+//
+//                // Now that all members are set with their usernames lets find our friends:
+//                //
+//                let contactsAlsoMembers = allContacts.filter { groupMembers.map(\.userId).contains($0.userId) }
+//                let membersNonContacts = groupMembers.filter { !contactsAlsoMembers.map(\.userId).contains($0.userId) }
+//
+//                var models = [DrawerTableCellModel]()
+//
+//                contactsAlsoMembers.forEach {
+//                    models.append(.init(
+//                        title: $0.nickname ?? $0.username,
+//                        image: $0.photo,
+//                        isCreator: $0.userId == group.leaderId,
+//                        isConnection: true
+//                    ))
+//                }
+//
+//                membersNonContacts.forEach {
+//                    models.append(.init(
+//                        title: $0.username,
+//                        image: nil,
+//                        isCreator: $0.userId == group.leaderId,
+//                        isConnection: false
+//                    ))
+//                }
+//
+//                completion(.success(models))
+//            }.store(in: &self.cancellables)
+//        }
     }
 
     func didRequestHide(contact: Contact) {
-        session.hideRequestOf(contact: contact)
+        if var contact = try? session.dbManager.fetchContacts(.init(id: [contact.id])).first {
+            contact.authStatus = .hidden
+            _ = try? session.dbManager.saveContact(contact)
+        }
     }
 
     func didRequestAccept(contact: Contact, nickname: String? = nil) {
@@ -223,8 +234,11 @@ final class RequestsReceivedViewModel {
         }
     }
 
-    func groupChatWith(group: Group) -> GroupChatInfo {
-        guard let info = groupChats.first(where: { $0.group.groupId == group.groupId }) else { fatalError() }
+    func groupChatWith(group: Group) -> GroupInfo {
+        guard let info = try? session.dbManager.fetchGroupInfos(.init(groupId: group.id)).first else {
+            fatalError()
+        }
+
         return info
     }
 }
diff --git a/Sources/RequestsFeature/ViewModels/RequestsSentViewModel.swift b/Sources/RequestsFeature/ViewModels/RequestsSentViewModel.swift
index ebead6be165ce126a66b729321eac1fe8135fbbb..3789428ffa0a9539e35b3344fdd820ee896d03a2 100644
--- a/Sources/RequestsFeature/ViewModels/RequestsSentViewModel.swift
+++ b/Sources/RequestsFeature/ViewModels/RequestsSentViewModel.swift
@@ -32,7 +32,8 @@ final class RequestsSentViewModel {
     var backgroundScheduler: AnySchedulerOf<DispatchQueue> = DispatchQueue.global().eraseToAnyScheduler()
 
     init() {
-        session.contacts(.requested)
+        session.dbManager.fetchContactsPublisher(.init(authStatus: [.requested]))
+            .assertNoFailure()
             .removeDuplicates()
             .map { data -> NSDiffableDataSourceSnapshot<Section, RequestSent> in
                 var snapshot = NSDiffableDataSourceSnapshot<Section, RequestSent>()
diff --git a/Sources/RequestsFeature/Views/RequestCell.swift b/Sources/RequestsFeature/Views/RequestCell.swift
index 5a6bd6db7f9845d54745521bc9f49fd2c8dc753e..d82093630e165f487d023eca2b099f5447492a37 100644
--- a/Sources/RequestsFeature/Views/RequestCell.swift
+++ b/Sources/RequestsFeature/Views/RequestCell.swift
@@ -89,7 +89,7 @@ final class RequestCell: UICollectionViewCell {
         }
 
         setupContact(
-            title: contact.nickname ?? contact.username,
+            title: contact.nickname ?? contact.username!,
             photo: contact.photo,
             phone: phone,
             email: contact.email,
@@ -128,7 +128,7 @@ final class RequestCell: UICollectionViewCell {
         }
 
         setupContact(
-            title: contact.nickname ?? contact.username,
+            title: contact.nickname ?? contact.username!,
             photo: contact.photo,
             phone: phone,
             email: contact.email,
@@ -165,7 +165,7 @@ final class RequestCell: UICollectionViewCell {
             }
 
             setupContact(
-                title: contact.nickname ?? contact.username,
+                title: contact.nickname ?? contact.username!,
                 photo: contact.photo,
                 phone: phone,
                 email: contact.email,
diff --git a/Sources/ScanFeature/ViewModels/ScanViewModel.swift b/Sources/ScanFeature/ViewModels/ScanViewModel.swift
index e91c94dad4b19f4cdfd3f408712f41df02b6705d..b940226d75390dc33a79118d44ca74ca4cbe3240 100644
--- a/Sources/ScanFeature/ViewModels/ScanViewModel.swift
+++ b/Sources/ScanFeature/ViewModels/ScanViewModel.swift
@@ -54,10 +54,12 @@ final class ScanViewModel {
                     return
                 }
 
-                if let previouslyAdded = self.session.find(by: usernameAndId.0) {
+
+
+                if let previouslyAdded = try? self.session.dbManager.fetchContacts(.init(id: [usernameAndId.1])).first {
                     var error = ScanError.unknown(Localized.Scan.Error.general)
 
-                    switch previouslyAdded.status {
+                    switch previouslyAdded.authStatus {
                     case .friend:
                         error = .alreadyFriends(usernameAndId.0)
                     case .requested, .verified:
@@ -71,16 +73,16 @@ final class ScanViewModel {
                 }
 
                 let contact = Contact(
-                    photo: nil,
-                    userId: usernameAndId.1,
-                    email: try? self.session.extract(fact: .email, from: data),
-                    phone: try? self.session.extract(fact: .phone, from: data),
-                    status: .stranger,
+                    id: usernameAndId.1,
                     marshaled: data,
                     username: usernameAndId.0,
+                    email: try? self.session.extract(fact: .email, from: data),
+                    phone: try? self.session.extract(fact: .phone, from: data),
                     nickname: nil,
-                    createdAt: Date(),
-                    isRecent: false
+                    photo: nil,
+                    authStatus: .stranger,
+                    isRecent: false,
+                    createdAt: Date()
                 )
 
                 self.succeed(with: contact)
diff --git a/Sources/SearchFeature/Controllers/SearchController.swift b/Sources/SearchFeature/Controllers/SearchController.swift
index 76d823020b0f4b7e5a595a3529cc6bcd3898745b..7f4250766c733d97139cd3c54d43c556adf88c7a 100644
--- a/Sources/SearchFeature/Controllers/SearchController.swift
+++ b/Sources/SearchFeature/Controllers/SearchController.swift
@@ -234,7 +234,7 @@ public final class SearchController: UIViewController {
     public func tableView(_: UITableView, didSelectRowAt indexPath: IndexPath) {
         let contact = viewModel.itemsRelay.value[indexPath.row]
 
-        guard contact.status == .stranger else {
+        guard contact.authStatus == .stranger else {
             coordinator.toContact(contact, from: self)
             return
         }
@@ -441,7 +441,7 @@ extension SearchController {
         ])
 
         let drawerNicknameInput = DrawerInput(
-            placeholder: contact.username,
+            placeholder: contact.username!,
             validator: .init(
                 wrongIcon: .image(Asset.sharedError.image),
                 correctIcon: .image(Asset.sharedSuccess.image),
@@ -488,7 +488,7 @@ extension SearchController {
                 guard allowsSave else { return }
 
                 drawer.dismiss(animated: true) {
-                    self.viewModel.didSet(nickname: nickname ?? contact.username, for: contact)
+                    self.viewModel.didSet(nickname: nickname ?? contact.username!, for: contact)
                 }
             }
             .store(in: &drawerCancellables)
diff --git a/Sources/SearchFeature/Controllers/SearchTableController.swift b/Sources/SearchFeature/Controllers/SearchTableController.swift
index 625ab603defabb7d9c86b8bead13119198091e2c..a18b5856fe98c454c1a54b3ee4ca3c70d25d49dd 100644
--- a/Sources/SearchFeature/Controllers/SearchTableController.swift
+++ b/Sources/SearchFeature/Controllers/SearchTableController.swift
@@ -1,6 +1,7 @@
 import UIKit
-import Combine
 import Models
+import Combine
+import XXModels
 
 final class SearchTableController: UITableViewController {
     // MARK: Properties
@@ -49,7 +50,7 @@ final class SearchTableController: UITableViewController {
         let cell = tableView.dequeueReusableCell(forIndexPath: indexPath, ofType: SearchCell.self)
         cell.title.text = dataSource[indexPath.row].username
         cell.subtitle.text = dataSource[indexPath.row].username
-        cell.avatar.setupProfile(title: dataSource[indexPath.row].username, image: nil, size: .large)
+        cell.avatar.setupProfile(title: dataSource[indexPath.row].username!, image: nil, size: .large)
         return cell
     }
 
diff --git a/Sources/SearchFeature/ViewModels/SearchViewModel.swift b/Sources/SearchFeature/ViewModels/SearchViewModel.swift
index 2ff6fccc9c6a30f3ce88935c18cc364433a4a902..7b52fb9762f47cfab9d17f2fd02383560be228bd 100644
--- a/Sources/SearchFeature/ViewModels/SearchViewModel.swift
+++ b/Sources/SearchFeature/ViewModels/SearchViewModel.swift
@@ -168,7 +168,7 @@ final class SearchViewModel {
 
         backgroundScheduler.schedule { [weak self] in
             guard let self = self else { return }
-            self.session.update(contact)
+            _ = try? self.session.dbManager.saveContact(contact)
         }
     }