diff --git a/App/client-ios.xcodeproj/project.pbxproj b/App/client-ios.xcodeproj/project.pbxproj
index b13be6187f4691c5ee9d95e4db8864367a979327..0f9849c8e5b29c44bd270caee0cc7ba2c25f0a8d 100644
--- a/App/client-ios.xcodeproj/project.pbxproj
+++ b/App/client-ios.xcodeproj/project.pbxproj
@@ -448,7 +448,7 @@
 				CODE_SIGN_ENTITLEMENTS = "client-ios/Resources/client-ios.entitlements";
 				CODE_SIGN_IDENTITY = "Apple Development";
 				CODE_SIGN_STYLE = Automatic;
-				CURRENT_PROJECT_VERSION = 292;
+				CURRENT_PROJECT_VERSION = 294;
 				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
 				DEVELOPMENT_TEAM = S6JDM2WW29;
 				ENABLE_BITCODE = NO;
@@ -487,7 +487,7 @@
 				CODE_SIGN_ENTITLEMENTS = "client-ios/Resources/client-ios.entitlements";
 				CODE_SIGN_IDENTITY = "Apple Development";
 				CODE_SIGN_STYLE = Automatic;
-				CURRENT_PROJECT_VERSION = 292;
+				CURRENT_PROJECT_VERSION = 294;
 				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
 				DEVELOPMENT_TEAM = S6JDM2WW29;
 				ENABLE_BITCODE = NO;
@@ -522,7 +522,7 @@
 				CODE_SIGN_ENTITLEMENTS = NotificationExtension/NotificationExtension.entitlements;
 				CODE_SIGN_IDENTITY = "Apple Development";
 				CODE_SIGN_STYLE = Automatic;
-				CURRENT_PROJECT_VERSION = 292;
+				CURRENT_PROJECT_VERSION = 294;
 				DEVELOPMENT_TEAM = S6JDM2WW29;
 				ENABLE_BITCODE = NO;
 				FRAMEWORK_SEARCH_PATHS = (
@@ -553,7 +553,7 @@
 				CODE_SIGN_ENTITLEMENTS = NotificationExtension/NotificationExtension.entitlements;
 				CODE_SIGN_IDENTITY = "Apple Development";
 				CODE_SIGN_STYLE = Automatic;
-				CURRENT_PROJECT_VERSION = 292;
+				CURRENT_PROJECT_VERSION = 294;
 				DEVELOPMENT_TEAM = S6JDM2WW29;
 				ENABLE_BITCODE = NO;
 				FRAMEWORK_SEARCH_PATHS = (
diff --git a/Package.swift b/Package.swift
index b1ec908e6564028f197b352659674d5c971a15ad..7bf71c7d49a94848af789fc8e75c5de498248a21 100644
--- a/Package.swift
+++ b/Package.swift
@@ -177,8 +177,21 @@ let package = Package(
         .product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
       ]
     ),
-    .target(name: "CheckVersion"),
-    .target(name: "Voxophone"),
+    .target(
+      name: "CheckVersion",
+      dependencies: [
+        .product(
+          name: "Dependencies",
+          package: "swift-composable-architecture"
+        ),
+      ]
+    ),
+    .target(
+      name: "Voxophone",
+      dependencies: [
+        .target(name: "Shared"),
+      ]
+    ),
     .target(name: "WebsiteFeature"),
     .target(
       name: "CrashReport",
@@ -271,12 +284,24 @@ let package = Package(
     .target(
       name: "Keychain",
       dependencies: [
-        .product(name: "KeychainAccess", package: "KeychainAccess"),
+        .product(
+          name: "KeychainAccess",
+          package: "KeychainAccess"
+        ),
+        .product(
+          name: "Dependencies",
+          package: "swift-composable-architecture"
+        ),
       ]
     ),
     .target(
       name: "Defaults",
-      dependencies: []
+      dependencies: [
+        .product(
+          name: "Dependencies",
+          package: "swift-composable-architecture"
+        ),
+      ]
     ),
     .target(
       name: "CountryListFeature",
diff --git a/Sources/AppCore/AppDependencies.swift b/Sources/AppCore/AppDependencies.swift
index f974596a680bfe8b671315094fc07287bb661787..9c4c392e6b47067a5ca46d226e6ce3ecf623e59e 100644
--- a/Sources/AppCore/AppDependencies.swift
+++ b/Sources/AppCore/AppDependencies.swift
@@ -10,6 +10,8 @@ public struct AppDependencies {
   public var backupHandler: BackupCallbackHandler
   public var hudManager: HUDManager
   public var dbManager: DBManager
+  public var groupRequest: GroupRequestHandler
+  public var groupMessageHandler: GroupMessageHandler
   public var statusBar: StatusBarStylist
   public var messenger: Messenger
   public var authHandler: AuthCallbackHandler
@@ -53,6 +55,14 @@ extension AppDependencies {
       ),
       hudManager: .live(),
       dbManager: dbManager,
+      groupRequest: .live(
+        messenger: messenger,
+        db: dbManager.getDB
+      ),
+      groupMessageHandler: .live(
+        messenger: messenger,
+        db: dbManager.getDB
+      ),
       statusBar: .live(),
       messenger: messenger,
       authHandler: .live(
@@ -67,7 +77,7 @@ extension AppDependencies {
       ),
       backupStorage: .onDisk(),
       mainQueue: DispatchQueue.main.eraseToAnyScheduler(),
-      bgQueue: DispatchQueue.global(qos: .background).eraseToAnyScheduler(),
+      bgQueue: DispatchQueue(label: "xx-messenger", qos: .userInitiated).eraseToAnyScheduler(),
       now: now,
       sendMessage: .live(
         messenger: messenger,
@@ -99,6 +109,8 @@ extension AppDependencies {
     backupHandler: .unimplemented,
     hudManager: .unimplemented,
     dbManager: .unimplemented,
+    groupRequest: .unimplemented,
+    groupMessageHandler: .unimplemented,
     statusBar: .unimplemented,
     messenger: .unimplemented,
     authHandler: .unimplemented,
@@ -171,15 +183,3 @@ extension DependencyValues {
     set { self[StoredDummyTrafficKey.self] = newValue }
   }
 }
-
-private enum StoredNewGroupChatKey: DependencyKey {
-  static var liveValue = Stored<GroupChat?>.inMemory()
-  static var testValue = Stored<GroupChat?>.unimplemented()
-}
-
-extension DependencyValues {
-  public var groupManager: Stored<GroupChat?> {
-    get { self[StoredNewGroupChatKey.self] }
-    set { self[StoredNewGroupChatKey.self] = newValue }
-  }
-}
diff --git a/Sources/AppCore/GroupHandlers/GroupMessageHandler.swift b/Sources/AppCore/GroupHandlers/GroupMessageHandler.swift
new file mode 100644
index 0000000000000000000000000000000000000000..262a53eb914987433dca7cb6a14a3d6f9f1cdb39
--- /dev/null
+++ b/Sources/AppCore/GroupHandlers/GroupMessageHandler.swift
@@ -0,0 +1,55 @@
+import XXModels
+import XXClient
+import Foundation
+import XXMessengerClient
+import XCTestDynamicOverlay
+
+public struct GroupMessageHandler {
+  public typealias OnError = (Error) -> Void
+
+  public var run: (@escaping OnError) -> Cancellable
+
+  public func callAsFunction(onError: @escaping OnError) -> Cancellable {
+    run(onError)
+  }
+}
+
+extension GroupMessageHandler {
+  public static func live(
+    messenger: Messenger,
+    db: DBManagerGetDB
+  ) -> GroupMessageHandler {
+    GroupMessageHandler { onError in
+      messenger.registerGroupChatProcessor(.init { result in
+        switch result {
+        case .success(let callback):
+          do {
+            let payload = try MessagePayload.decode(callback.decryptedMessage.payload)
+            try db().saveMessage(.init(
+              networkId: callback.decryptedMessage.messageId,
+              senderId: callback.decryptedMessage.senderId,
+              recipientId: nil,
+              groupId: callback.decryptedMessage.groupId,
+              date: Date.fromTimestamp(Int(callback.decryptedMessage.timestamp)),
+              status: .received,
+              isUnread: true,
+              text: payload.text,
+              replyMessageId: payload.replyingTo,
+              roundURL: callback.roundUrl
+            ))
+          } catch {
+            onError(error)
+          }
+        case .failure(let error):
+          onError(error)
+        }
+      })
+    }
+  }
+}
+
+extension GroupMessageHandler {
+  public static let unimplemented = GroupMessageHandler(
+    run: XCTUnimplemented("\(Self.self)", placeholder: Cancellable {})
+  )
+}
diff --git a/Sources/AppCore/GroupHandlers/GroupRequestHandler.swift b/Sources/AppCore/GroupHandlers/GroupRequestHandler.swift
new file mode 100644
index 0000000000000000000000000000000000000000..14afb9026aaf52338c7b6af7b7c5f8d5b094a268
--- /dev/null
+++ b/Sources/AppCore/GroupHandlers/GroupRequestHandler.swift
@@ -0,0 +1,99 @@
+import XXModels
+import XXClient
+import Foundation
+import XXMessengerClient
+import XCTestDynamicOverlay
+
+public struct GroupRequestHandler {
+  public typealias OnError = (Error) -> Void
+
+  public var run: (@escaping OnError) -> Cancellable
+
+  public func callAsFunction(onError: @escaping OnError) -> Cancellable {
+    run(onError)
+  }
+}
+
+extension GroupRequestHandler {
+  public static func live(
+    messenger: Messenger,
+    db: DBManagerGetDB
+  ) -> GroupRequestHandler {
+    GroupRequestHandler { onError in
+      messenger.registerGroupRequestHandler(.init { group in
+        do {
+          if let _ = try db().fetchGroups(.init(id: [group.getId()])).first {
+            return
+          }
+          guard let leader = try group.getMembership().first else {
+            return // Failed to get group membership/leader
+          }
+          try db().saveGroup(.init(
+            id: group.getId(),
+            name: String(data: group.getName(), encoding: .utf8)!,
+            leaderId: leader.id,
+            createdAt: Date.fromMSTimestamp(group.getCreatedMS()),
+            authStatus: .pending,
+            serialized: group.serialize()
+          ))
+          if let initialMessageData = group.getInitMessage(),
+             let initialMessage = String(data: initialMessageData, encoding: .utf8) {
+            try db().saveMessage(.init(
+              senderId: leader.id,
+              recipientId: nil,
+              groupId: group.getId(),
+              date: Date.fromMSTimestamp(group.getCreatedMS()),
+              status: .received,
+              isUnread: true,
+              text: initialMessage
+            ))
+          }
+          let members = try group.getMembership()
+          let friends = try db().fetchContacts(.init(id: Set(members.map(\.id)), authStatus: [
+            .friend, .hidden, .confirming,
+            .verified, .requested, .requesting,
+            .verificationInProgress, .requestFailed,
+            .verificationFailed, .confirmationFailed
+          ]))
+          let strangers = Set(members.map(\.id)).subtracting(Set(friends.map(\.id)))
+          try strangers.forEach {
+            if let stranger = try? db().fetchContacts(.init(id: [$0])).first {
+              print(stranger)
+            } else {
+              try db().saveContact(.init(
+                id: $0,
+                username: "Fetching...",
+                authStatus: .stranger,
+                isRecent: false,
+                isBlocked: false,
+                isBanned: false,
+                createdAt: Date.fromMSTimestamp(group.getCreatedMS())
+              ))
+            }
+          }
+          try members.map {
+            XXModels.GroupMember(groupId: group.getId(), contactId: $0.id)
+          }.forEach {
+            try db().saveGroupMember($0)
+          }
+          let multilookup = try messenger.lookupContacts(ids: strangers.map { $0 })
+          for user in multilookup.contacts {
+            if var foo = try? db().fetchContacts(.init(id: [user.getId()])).first,
+               let username = try? user.getFact(.username)?.value {
+              foo.username = username
+              _ = try? db().saveContact(foo)
+            }
+          }
+        } catch {
+          onError(error)
+        }
+      })
+    }
+  }
+}
+
+extension GroupRequestHandler {
+  public static let unimplemented = GroupRequestHandler(
+    run: XCTUnimplemented("\(Self.self)", placeholder: Cancellable {})
+  )
+}
diff --git a/Sources/AppFeature/AppDelegate.swift b/Sources/AppFeature/AppDelegate.swift
index 7cc572aa26a9f469c6787c4927b29da6b821df77..cb3dce57e45259b11df8addbfe7a8f6a04150ec5 100644
--- a/Sources/AppFeature/AppDelegate.swift
+++ b/Sources/AppFeature/AppDelegate.swift
@@ -1,10 +1,14 @@
 import UIKit
 import AppCore
+import Defaults
 import LaunchFeature
 
 public class AppDelegate: UIResponder, UIApplicationDelegate {
+  public var coverView: UIView?
   public var window: UIWindow?
 
+  @KeyObject(.hideAppList, defaultValue: false) var shouldHideAppInAppList
+
   public func application(
     _ application: UIApplication,
     didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
@@ -16,4 +20,18 @@ public class AppDelegate: UIResponder, UIApplicationDelegate {
     window?.makeKeyAndVisible()
     return true
   }
+
+  public func applicationWillResignActive(_ application: UIApplication) {
+    if shouldHideAppInAppList {
+      coverView?.removeFromSuperview()
+      coverView = UIVisualEffectView(effect: UIBlurEffect(style: .regular))
+      coverView?.frame = window?.bounds ?? .zero
+      window?.addSubview(coverView!)
+    }
+  }
+
+  public func applicationDidBecomeActive(_ application: UIApplication) {
+    application.applicationIconBadgeNumber = 0
+    coverView?.removeFromSuperview()
+  }
 }
diff --git a/Sources/AppFeature/DependencyRegistrator.swift b/Sources/AppFeature/DependencyRegistrator.swift
index ed05ba40e4225ee6ce3345aca596276c3fc2915e..3504173f1b48691362bb581d7bd2a40702166318 100644
--- a/Sources/AppFeature/DependencyRegistrator.swift
+++ b/Sources/AppFeature/DependencyRegistrator.swift
@@ -1,32 +1,12 @@
-// MARK: SDK
-
-import UIKit
-import Network
-import QuickLook
-import MobileCoreServices
-
-// MARK: Isolated features
-
-import Bindings
-import Keychain
-import Defaults
-import Voxophone
-import PushFeature
-import CrashReporting
-import VersionChecking
-import ReportingFeature
-import CountryListFeature
-
-// MARK: UI Features
-
 import ScanFeature
 import ChatFeature
 import MenuFeature
 import TermsFeature
+import Dependencies
+import AppNavigation
 import BackupFeature
 import DrawerFeature
 import SearchFeature
-import LaunchFeature
 import RestoreFeature
 import ContactFeature
 import WebsiteFeature
@@ -36,18 +16,11 @@ import SettingsFeature
 import RequestsFeature
 import GroupDraftFeature
 import OnboardingFeature
+import CountryListFeature
 import CreateGroupFeature
 import ContactListFeature
 import RequestPermissionFeature
 
-import Shared
-import XXClient
-import AppNavigation
-import KeychainAccess
-import XXMessengerClient
-
-import ComposableArchitecture
-
 extension NavigatorKey: DependencyKey {
   public static let liveValue: Navigator = CombinedNavigator(
     PresentModalNavigator(),
@@ -114,6 +87,7 @@ extension NavigatorKey: DependencyKey {
       ProfilePhoneController.init
     ),
     PresentSearchNavigator(
+      ChatListController.init,
       SearchContainerController.init(_:)
     ),
     PresentRequestsNavigator(
diff --git a/Sources/AppNavigation/PresentSearch.swift b/Sources/AppNavigation/PresentSearch.swift
index 4dc89878f03d712d3ccf5486a4be4c3dcab82673..4b553b174121c096bd8673ce0636478b181ad459 100644
--- a/Sources/AppNavigation/PresentSearch.swift
+++ b/Sources/AppNavigation/PresentSearch.swift
@@ -4,17 +4,17 @@ import UIKit
 public struct PresentSearch: Action {
   /// - Parameters:
   ///   - searching: Optional string to be searched upon further viewModel intialization
-  ///   - replacing: Flag to differentiate if should be a push or a set stack
+  ///   - fromOnboarding: Flag that differentiates if should be a push or a set stack
   ///   - navigationController: Navigation controller on which will be pushed or stack should be set
   ///   - animated: Animate the transition
   public init(
-    searching: String?,
-    replacing: Bool,
+    searching: String? = nil,
+    fromOnboarding: Bool = false,
     on navigationController: UINavigationController,
     animated: Bool = true
   ) {
     self.searching = searching
-    self.replacing = replacing
+    self.fromOnboarding = fromOnboarding
     self.navigationController = navigationController
     self.animated = animated
   }
@@ -22,8 +22,8 @@ public struct PresentSearch: Action {
   /// Optional string to be searched upon further viewModel intialization
   public var searching: String?
 
-  /// Flag to differentiate if should be a push or a set stack
-  public var replacing: Bool
+  /// Flag that differentiates if should be a push or a set stack
+  public var fromOnboarding: Bool
 
   /// Navigation controller on which stack should be set
   public var navigationController: UINavigationController
@@ -37,15 +37,23 @@ public struct PresentSearchNavigator: TypedNavigator {
   /// View controller which should be pushed or set in navigation stack
   var viewController: (String?) -> UIViewController
 
+  /// View controller which might have to be pushed below in navigation stack
+  var otherViewController: () -> UIViewController
+
   /// - Parameters:
   ///   - viewController: View controller which should be pushed or set in navigation stack
-  public init(_ viewController: @escaping (String?) -> UIViewController) {
+  ///   - otherViewController: View controller which might have to be pushed below in navigation stack
+  public init(
+    _ otherViewController: @escaping () -> UIViewController,
+    _ viewController: @escaping (String?) -> UIViewController
+  ) {
     self.viewController = viewController
+    self.otherViewController = otherViewController
   }
 
   public func perform(_ action: PresentSearch, completion: @escaping () -> Void) {
-    if action.replacing {
-      action.navigationController.setViewControllers([viewController(action.searching)], animated: action.animated)
+    if action.fromOnboarding {
+      action.navigationController.setViewControllers([otherViewController(), viewController(action.searching)], animated: action.animated)
     } else {
       action.navigationController.pushViewController(viewController(action.searching), animated: action.animated)
     }
diff --git a/Sources/ChatFeature/Helpers/CellConfigurator.swift b/Sources/ChatFeature/Helpers/CellConfigurator.swift
index 63b7998cc93808fe34d6d97d32f6c9ddecb903e2..0abf3cca2b166b5fc63da479a23a019dbfb87046 100644
--- a/Sources/ChatFeature/Helpers/CellConfigurator.swift
+++ b/Sources/ChatFeature/Helpers/CellConfigurator.swift
@@ -198,7 +198,7 @@ extension CellFactory {
           return false
         }
         
-        return transfer(item.fileTransferId!).type == "jpeg"
+        return transfer(item.fileTransferId!).type == "image"
         
       }, build: { item, collectionView, indexPath in
         let ft = transfer(item.fileTransferId!)
@@ -228,7 +228,7 @@ extension CellFactory {
           return false
         }
         
-        return transfer(item.fileTransferId!).type == "jpeg"
+        return transfer(item.fileTransferId!).type == "image"
         
       }, build: { item, collectionView, indexPath in
         let ft = transfer(item.fileTransferId!)
diff --git a/Sources/ChatFeature/ViewModels/GroupChatViewModel.swift b/Sources/ChatFeature/ViewModels/GroupChatViewModel.swift
index 2456b811a8af7c96ced78c76b14f1e88e4a606bc..e083c9c4abe8c76603091b542bf5e78e8dc00cb5 100644
--- a/Sources/ChatFeature/ViewModels/GroupChatViewModel.swift
+++ b/Sources/ChatFeature/ViewModels/GroupChatViewModel.swift
@@ -23,7 +23,6 @@ final class GroupChatViewModel {
   @Dependency(\.sendReport) var sendReport
   @Dependency(\.app.dbManager) var dbManager
   @Dependency(\.app.messenger) var messenger
-  @Dependency(\.groupManager) var groupManager
   @Dependency(\.app.hudManager) var hudManager
   @Dependency(\.app.toastManager) var toastManager
   @Dependency(\.reportingStatus) var reportingStatus
@@ -106,18 +105,18 @@ final class GroupChatViewModel {
         replyMessageId: stagedReply?.messageId
       )
       message = try dbManager.getDB().saveMessage(message)
-      let report = try groupManager.get()?.send(
+      let report = try messenger.groupChat()!.send(
         groupId: info.id,
         message: MessagePayload(
           text: text.trimmingCharacters(in: .whitespacesAndNewlines),
           replyingTo: stagedReply?.messageId
         ).encode()
       )
-      message.networkId = report!.messageId
-      message.date = Date.fromTimestamp(Int(report!.timestamp))
+      message.networkId = report.messageId
+      message.date = Date.fromTimestamp(Int(report.timestamp))
       message = try dbManager.getDB().saveMessage(message)
       try messenger.cMix.get()?.waitForRoundResult(
-        roundList: try report!.encode(),
+        roundList: try report.encode(),
         timeoutMS: 15_000,
         callback: .init(handle: { result in
           switch result {
@@ -139,18 +138,18 @@ final class GroupChatViewModel {
       var message = message
       message.status = .sending
       message = try dbManager.getDB().saveMessage(message)
-      let report = try groupManager.get()?.send(
+      let report = try messenger.groupChat()!.send(
         groupId: info.id,
         message: MessagePayload(
           text: message.text.trimmingCharacters(in: .whitespacesAndNewlines),
           replyingTo: stagedReply?.messageId
         ).encode()
       )
-      message.networkId = report!.messageId
-      message.date = Date.fromTimestamp(Int(report!.timestamp))
+      message.networkId = report.messageId
+      message.date = Date.fromTimestamp(Int(report.timestamp))
       message = try dbManager.getDB().saveMessage(message)
       try messenger.cMix.get()?.waitForRoundResult(
-        roundList: try report!.encode(),
+        roundList: try report.encode(),
         timeoutMS: 15_000,
         callback: .init(handle: { result in
           switch result {
diff --git a/Sources/ChatListFeature/Controller/ChatListController.swift b/Sources/ChatListFeature/Controller/ChatListController.swift
index 8ad43f6efa8fea9ea98d3894f6726d9d7b75bbe4..7383b423c17c6acca5df900204af90d7db50c9d9 100644
--- a/Sources/ChatListFeature/Controller/ChatListController.swift
+++ b/Sources/ChatListFeature/Controller/ChatListController.swift
@@ -71,11 +71,7 @@ public final class ChatListController: UIViewController {
       .sink { [unowned self] in
         switch $0 {
         case .didTapSearch:
-          navigator.perform(PresentSearch(
-            searching: nil,
-            replacing: false,
-            on: navigationController!
-          ))
+          navigator.perform(PresentSearch(on: navigationController!))
         case .didTapNewGroup:
           navigator.perform(
             PresentGroupDraft(on: navigationController!)
@@ -218,11 +214,7 @@ public final class ChatListController: UIViewController {
       .searchButton
       .publisher(for: .touchUpInside)
       .sink { [unowned self] in
-        navigator.perform(PresentSearch(
-          searching: nil,
-          replacing: false,
-          on: navigationController!
-        ))
+        navigator.perform(PresentSearch(on: navigationController!))
       }.store(in: &cancellables)
     
     screenView
diff --git a/Sources/ChatListFeature/ViewModel/ChatListViewModel.swift b/Sources/ChatListFeature/ViewModel/ChatListViewModel.swift
index 0e4c28520f58202435ff65d4d2c0a9069fd537fd..8fea8877f547bfdf7d205e95412cf2b783f9762d 100644
--- a/Sources/ChatListFeature/ViewModel/ChatListViewModel.swift
+++ b/Sources/ChatListFeature/ViewModel/ChatListViewModel.swift
@@ -27,7 +27,6 @@ typealias SearchSnapshot = NSDiffableDataSourceSnapshot<SearchSection, SearchIte
 final class ChatListViewModel {
   @Dependency(\.app.dbManager) var dbManager
   @Dependency(\.app.messenger) var messenger
-  @Dependency(\.groupManager) var groupManager
   @Dependency(\.app.hudManager) var hudManager
   @Dependency(\.reportingStatus) var reportingStatus
   
@@ -175,12 +174,9 @@ final class ChatListViewModel {
   }
   
   func leave(_ group: Group) {
-    guard let manager = groupManager.get() else {
-      return
-    }
     hudManager.show()
     do {
-      try manager.leaveGroup(groupId: group.id)
+      try messenger.groupChat()!.leaveGroup(groupId: group.id)
       try dbManager.getDB().deleteMessages(.init(chat: .group(group.id)))
       try dbManager.getDB().deleteGroup(group)
       hudManager.hide()
diff --git a/Sources/ContactListFeature/ContactListController.swift b/Sources/ContactListFeature/ContactListController.swift
index f44b63c0d760b2c0cd55d3a00f972dcb2d1b0e0d..36d8e8b4c65c2801480e0aca20ae60528e706b77 100644
--- a/Sources/ContactListFeature/ContactListController.swift
+++ b/Sources/ContactListFeature/ContactListController.swift
@@ -115,11 +115,7 @@ public final class ContactListController: UIViewController {
       .publisher(for: .touchUpInside)
       .receive(on: DispatchQueue.main)
       .sink { [unowned self] in
-        navigator.perform(PresentSearch(
-          searching: nil,
-          replacing: false,
-          on: navigationController!
-        ))
+        navigator.perform(PresentSearch(on: navigationController!))
       }.store(in: &cancellables)
 
     viewModel
@@ -141,11 +137,7 @@ public final class ContactListController: UIViewController {
   }
 
   @objc private func didTapSearch() {
-    navigator.perform(PresentSearch(
-      searching: nil,
-      replacing: false,
-      on: navigationController!
-    ))
+    navigator.perform(PresentSearch(on: navigationController!))
   }
 
   @objc private func didTapScan() {
diff --git a/Sources/CrashReport/CrashReport.swift b/Sources/CrashReport/CrashReport.swift
index b5eceb577f093b6d61aafe6290cfe943c597ed36..8e6d2b7e4e793c70f16dfed720def386c783f1d0 100644
--- a/Sources/CrashReport/CrashReport.swift
+++ b/Sources/CrashReport/CrashReport.swift
@@ -1,5 +1,4 @@
 import Firebase
-import CrashReporting
 import FirebaseCrashlytics
 import XCTestDynamicOverlay
 
diff --git a/Sources/CreateGroupFeature/CreateGroupViewModel.swift b/Sources/CreateGroupFeature/CreateGroupViewModel.swift
index f63eccede0feef855c3b973d948627160c6d6f89..25ef6090123ff307f2f6555d56bade868c5f5ce8 100644
--- a/Sources/CreateGroupFeature/CreateGroupViewModel.swift
+++ b/Sources/CreateGroupFeature/CreateGroupViewModel.swift
@@ -19,7 +19,6 @@ struct CreateGroupViewModel {
   @Dependency(\.app.bgQueue) var bgQueue
   @Dependency(\.app.dbManager) var dbManager
   @Dependency(\.app.messenger) var messenger
-  @Dependency(\.groupManager) var groupManager
   @Dependency(\.app.hudManager) var hudManager
 
   var statePublisher: AnyPublisher<ViewState, Never> {
@@ -44,10 +43,7 @@ struct CreateGroupViewModel {
 
     bgQueue.schedule {
       do {
-        guard let manager = groupManager.get() else {
-          fatalError("Can't create a group w/out a manager")
-        }
-        let report = try manager.makeGroup(
+        let report = try messenger.groupChat()!.makeGroup(
           membership: members.map(\.id),
           message: welcome?.data(using: .utf8),
           name: name.data(using: .utf8)
diff --git a/Sources/Defaults/KeyObject.swift b/Sources/Defaults/KeyObject.swift
index 290a567e68cb5e72f81ed09f2ada856e1bfe16c3..99635cdab25184351ea1b3421562e185d9628870 100644
--- a/Sources/Defaults/KeyObject.swift
+++ b/Sources/Defaults/KeyObject.swift
@@ -1,5 +1,5 @@
 import Foundation
-import ComposableArchitecture
+import Dependencies
 
 public enum Key: String {
   case email
diff --git a/Sources/LaunchFeature/LaunchController.swift b/Sources/LaunchFeature/LaunchController.swift
index 9371455d9dc07804847e6642d731cccb3d452ec8..32e9b9b6fde7a67a9fafc4ef995bb1ae39d914d2 100644
--- a/Sources/LaunchFeature/LaunchController.swift
+++ b/Sources/LaunchFeature/LaunchController.swift
@@ -57,7 +57,7 @@ public final class LaunchController: UIViewController {
     case .search(username: let username):
       navigator.perform(PresentSearch(
         searching: username,
-        replacing: true,
+        fromOnboarding: true,
         on: navigationController!))
     case .groupChat(id: let groupId):
       if let info = viewModel.getGroupInfoWith(groupId: groupId) {
diff --git a/Sources/LaunchFeature/LaunchViewModel.swift b/Sources/LaunchFeature/LaunchViewModel.swift
index 82798860455c662ca9e773e4dc2ea641852a3a08..68587689fcb00e19365560b9b1752c8f09db69d8 100644
--- a/Sources/LaunchFeature/LaunchViewModel.swift
+++ b/Sources/LaunchFeature/LaunchViewModel.swift
@@ -48,7 +48,6 @@ final class LaunchViewModel {
   @Dependency(\.app.dbManager) var dbManager
   @Dependency(\.keychain) var keychainManager
   @Dependency(\.updateErrors) var updateErrors
-  @Dependency(\.groupManager) var groupManager
   @Dependency(\.app.hudManager) var hudManager
   @Dependency(\.checkVersion) var checkVersion
   @Dependency(\.dummyTraffic) var dummyTraffic
@@ -60,14 +59,18 @@ final class LaunchViewModel {
   @Dependency(\.processBannedList) var processBannedList
 
   @Dependency(\.app.authHandler) var authHandler
+  @Dependency(\.app.groupRequest) var groupRequest
   @Dependency(\.app.backupHandler) var backupHandler
   @Dependency(\.app.messageListener) var messageListener
   @Dependency(\.app.receiveFileHandler) var receiveFileHandler
+  @Dependency(\.app.groupMessageHandler) var groupMessageHandler
 
   var authHandlerCancellable: Cancellable?
+  var groupRequestCancellable: Cancellable?
   var backupHandlerCancellable: Cancellable?
   var networkHandlerCancellable: Cancellable?
   var receiveFileHandlerCancellable: Cancellable?
+  var groupMessageHandlerCancellable: Cancellable?
   var messageListenerHandlerCancellable: Cancellable?
 
   @KeyObject(.username, defaultValue: nil) var username: String?
@@ -164,16 +167,16 @@ final class LaunchViewModel {
 extension LaunchViewModel {
   func setupMessenger() throws {
     authHandlerCancellable = authHandler {
-      print("\($0.localizedDescription)")
+      print($0.localizedDescription)
     }
     backupHandlerCancellable = backupHandler {
-      print("\($0.localizedDescription)")
+      print($0.localizedDescription)
     }
     receiveFileHandlerCancellable = receiveFileHandler {
-      print("\($0.localizedDescription)")
+      print($0.localizedDescription)
     }
     messageListenerHandlerCancellable = messageListener {
-      print("\($0.localizedDescription)")
+      print($0.localizedDescription)
     }
 
     if messenger.isLoaded() == false {
@@ -214,10 +217,18 @@ extension LaunchViewModel {
       try? messenger.resumeBackup()
     }
 
-    try generateGroupManager()
+    groupRequestCancellable = groupRequest {
+      print($0)
+    }
+
+    groupMessageHandlerCancellable = groupMessageHandler {
+      print($0)
+    }
+
+    try messenger.startGroupChat()
 
     try messenger.trackServices {
-      print("\($0.localizedDescription)")
+      print($0.localizedDescription)
     }
 
     try messenger.startFileTransfer()
diff --git a/Sources/LaunchFeature/ViewModel+GroupManager.swift b/Sources/LaunchFeature/ViewModel+GroupManager.swift
deleted file mode 100644
index c0bef6c8933501f14705df1a1022aaed2ca643e8..0000000000000000000000000000000000000000
--- a/Sources/LaunchFeature/ViewModel+GroupManager.swift
+++ /dev/null
@@ -1,192 +0,0 @@
-import Shared
-import AppCore
-import XXModels
-import XXClient
-import Foundation
-import XXMessengerClient
-
-extension LaunchViewModel {
-  func generateGroupManager() throws {
-    groupManager.set(try NewGroupChat.live(
-      e2eId: messenger.e2e()!.getId(),
-      groupRequest: .init(handle: { [weak self] group in
-        guard let self else { return }
-        self.handleGroupRequest(from: group, messenger: self.messenger)
-      }),
-      groupChatProcessor: .init(handle: { [weak self] result in
-        guard let self else { return }
-
-        switch result {
-        case .success(let cb):
-          do {
-            let payload = try MessagePayload.decode(cb.decryptedMessage.payload)
-
-            try self.dbManager.getDB().saveMessage(
-              .init(
-                networkId: cb.decryptedMessage.messageId,
-                senderId: cb.decryptedMessage.senderId,
-                recipientId: nil,
-                groupId: cb.decryptedMessage.groupId,
-                date: Date.fromTimestamp(Int(cb.decryptedMessage.timestamp)),
-                status: .received,
-                isUnread: true,
-                text: payload.text,
-                replyMessageId: payload.replyingTo,
-                roundURL: cb.roundUrl
-              )
-            )
-          } catch {
-            print(error.localizedDescription)
-          }
-        case .failure(let error):
-          print(error.localizedDescription)
-        }
-      })
-    ))
-  }
-
-  func handleGroupRequest(from group: XXClient.Group, messenger: Messenger) {
-    if let _ = try? dbManager.getDB().fetchGroups(.init(id: [group.getId()])).first { return }
-
-    guard var members = try? group.getMembership(), let leader = members.first else {
-      fatalError("Failed to get group membership/leader")
-    }
-
-    try! dbManager.getDB().saveGroup(.init(
-      id: group.getId(),
-      name: String(data: group.getName(), encoding: .utf8)!,
-      leaderId: leader.id,
-      createdAt: Date.fromMSTimestamp(group.getCreatedMS()),
-      authStatus: .pending,
-      serialized: group.serialize()
-    ))
-
-    if let initMessageData = group.getInitMessage(),
-       let initMessage = String(data: initMessageData, encoding: .utf8) {
-      try! dbManager.getDB().saveMessage(.init(
-        senderId: leader.id,
-        recipientId: nil,
-        groupId: group.getId(),
-        date: Date.fromMSTimestamp(group.getCreatedMS()),
-        status: .received,
-        isUnread: true,
-        text: initMessage
-      ))
-    }
-
-    let friends = try! dbManager.getDB().fetchContacts(.init(
-      id: Set(members.map(\.id)),
-      authStatus: [
-        .friend,
-        .hidden,
-        .requesting,
-        .confirming,
-        .verificationInProgress,
-        .verified,
-        .requested,
-        .requestFailed,
-        .verificationFailed,
-        .confirmationFailed
-      ]
-    ))
-
-    let strangers = Set(members.map(\.id)).subtracting(Set(friends.map(\.id)))
-
-    strangers.forEach {
-      if let stranger = try? dbManager.getDB().fetchContacts(.init(id: [$0])).first {
-        print(">>> This is a stranger, but I already knew about his/her existance: \(stranger.id.base64EncodedString().prefix(10))...")
-      } else {
-        try! dbManager.getDB().saveContact(.init(
-          id: $0,
-          marshaled: nil,
-          username: "Fetching...",
-          email: nil,
-          phone: nil,
-          nickname: nil,
-          photo: nil,
-          authStatus: .stranger,
-          isRecent: false,
-          isBlocked: false,
-          isBanned: false,
-          createdAt: Date.fromMSTimestamp(group.getCreatedMS())
-        ))
-      }
-    }
-
-    members.forEach {
-      let model = XXModels.GroupMember(groupId: group.getId(), contactId: $0.id)
-      _ = try? dbManager.getDB().saveGroupMember(model)
-    }
-
-    do {
-      let multiLookup = try messenger.lookupContacts(ids: strangers.map { $0 })
-
-      for user in multiLookup.contacts {
-        if var foo = try? self.dbManager.getDB().fetchContacts(.init(id: [user.getId()])).first,
-           let username = try? user.getFact(.username)?.value {
-          foo.username = username
-          _ = try? self.dbManager.getDB().saveContact(foo)
-        }
-      }
-    } catch {
-      // TODO
-    }
-  }
-}
-
-
-
-//func handleIncomingTransfer(_ receivedFile: ReceivedFile) {
-//  if var model = try? dbManager.getDB().saveFileTransfer(.init(
-//    id: receivedFile.transferId,
-//    contactId: receivedFile.senderId,
-//    name: receivedFile.name,
-//    type: receivedFile.type,
-//    data: nil,
-//    progress: 0.0,
-//    isIncoming: true,
-//    createdAt: Date()
-//  )) {
-//    try! dbManager.getDB().saveMessage(.init(
-//      networkId: nil,
-//      senderId: receivedFile.senderId,
-//      recipientId: messenger.e2e.get()!.getContact().getId(),
-//      groupId: nil,
-//      date: Date(),
-//      status: .receiving,
-//      isUnread: false,
-//      text: "",
-//      replyMessageId: nil,
-//      roundURL: nil,
-//      fileTransferId: model.id
-//    ))
-//
-//    if let manager: XXClient.FileTransfer = try? DI.Container.shared.resolve() {
-//      print(">>> registerReceivedProgressCallback")
-//
-//      try! manager.registerReceivedProgressCallback(
-//        transferId: receivedFile.transferId,
-//        period: 1_000,
-//        callback: .init(handle: { [weak self] in
-//          guard let self else { return }
-//          switch $0 {
-//          case .success(let cb):
-//            if cb.progress.completed {
-//              model.progress = 100
-//              model.data = try! manager.receive(transferId: receivedFile.transferId)
-//            } else {
-//              model.progress = Float(cb.progress.transmitted/cb.progress.total)
-//            }
-//
-//            model = try! self.dbManager.getDB().saveFileTransfer(model)
-//
-//          case .failure(let error):
-//            print(error.localizedDescription)
-//          }
-//        })
-//      )
-//    } else {
-//      //print(DI.Container.shared.dependencies)
-//    }
-//  }
-//}
diff --git a/Sources/MenuFeature/Controllers/MenuController.swift b/Sources/MenuFeature/Controllers/MenuController.swift
index e02c7e62f37001cbb9780905c5fe475070d98f62..17a9a73505fc73230b5fad387c0dd9c247621ea1 100644
--- a/Sources/MenuFeature/Controllers/MenuController.swift
+++ b/Sources/MenuFeature/Controllers/MenuController.swift
@@ -196,7 +196,7 @@ public final class MenuController: UIViewController {
           guard let self, self.currentItem != .share else { return }
           self.navigator.perform(PresentActivitySheet(items: [
             Localized.Menu.shareContent(self.viewModel.referralDeeplink)
-          ], from: self))
+          ], from: self.navController!.topViewController!))
         }
       }.store(in: &cancellables)
 
@@ -247,6 +247,6 @@ public final class MenuController: UIViewController {
         spacingAfter: 39
       ),
       actionButton
-    ], isDismissable: true, from: self))
+    ], isDismissable: true, from: navController!.topViewController!))
   }
 }
diff --git a/Sources/OnboardingFeature/Controllers/OnboardingCodeController.swift b/Sources/OnboardingFeature/Controllers/OnboardingCodeController.swift
index 6fa2993c058cd37554b92efd4dea45209a490cfc..92cdda4e7d229e51328343edce602841a14bb529 100644
--- a/Sources/OnboardingFeature/Controllers/OnboardingCodeController.swift
+++ b/Sources/OnboardingFeature/Controllers/OnboardingCodeController.swift
@@ -107,7 +107,10 @@ public final class OnboardingCodeController: UIViewController {
         if isEmail {
           navigator.perform(PresentOnboardingPhone(on: navigationController!))
         } else {
-          navigator.perform(PresentChatList(on: navigationController!))
+          navigator.perform(PresentSearch(
+            fromOnboarding: true,
+            on: navigationController!
+          ))
         }
       }.store(in: &cancellables)
 
diff --git a/Sources/OnboardingFeature/Controllers/OnboardingPhoneController.swift b/Sources/OnboardingFeature/Controllers/OnboardingPhoneController.swift
index 466a45994a90979bdcbf09e427efad88cef93b24..4eadd53e32ffa9fa633f2fd61824dab9e871c70b 100644
--- a/Sources/OnboardingFeature/Controllers/OnboardingPhoneController.swift
+++ b/Sources/OnboardingFeature/Controllers/OnboardingPhoneController.swift
@@ -86,7 +86,10 @@ public final class OnboardingPhoneController: UIViewController {
       .skipButton
       .publisher(for: .touchUpInside)
       .sink { [unowned self] in
-        navigator.perform(PresentChatList(on: navigationController!))
+        navigator.perform(PresentSearch(
+          fromOnboarding: true,
+          on: navigationController!
+        ))
       }.store(in: &cancellables)
 
     screenView
diff --git a/Sources/OnboardingFeature/Controllers/OnboardingWelcomeController.swift b/Sources/OnboardingFeature/Controllers/OnboardingWelcomeController.swift
index f3bc2af0248b805fae258212d7573d89c9586dc5..701e1359cea59cfd2912d099b7d6fe897aa2de64 100644
--- a/Sources/OnboardingFeature/Controllers/OnboardingWelcomeController.swift
+++ b/Sources/OnboardingFeature/Controllers/OnboardingWelcomeController.swift
@@ -45,7 +45,10 @@ public final class OnboardingWelcomeController: UIViewController {
       .skipButton
       .publisher(for: .touchUpInside)
       .sink { [unowned self] in
-        navigator.perform(PresentChatList(on: navigationController!))
+        navigator.perform(PresentSearch(
+          fromOnboarding: true,
+          on: navigationController!
+        ))
       }.store(in: &cancellables)
 
     screenView.didTapInfo = { [weak self] in
diff --git a/Sources/RequestsFeature/Controllers/RequestsContainerController.swift b/Sources/RequestsFeature/Controllers/RequestsContainerController.swift
index 11a4270c77ca96b452a2bbbcc1fcb2d3f9df560f..49ab70bc1481be2c6797d9af64c2c3162c2026b3 100644
--- a/Sources/RequestsFeature/Controllers/RequestsContainerController.swift
+++ b/Sources/RequestsFeature/Controllers/RequestsContainerController.swift
@@ -79,11 +79,7 @@ public final class RequestsContainerController: UIViewController {
       .connectionsPublisher
       .receive(on: DispatchQueue.main)
       .sink { [unowned self] in
-        navigator.perform(PresentSearch(
-          searching: nil,
-          replacing: false,
-          on: navigationController!
-        ))
+        navigator.perform(PresentSearch(on: navigationController!))
       }.store(in: &cancellables)
 
     screenView
diff --git a/Sources/RequestsFeature/ViewModels/RequestsReceivedViewModel.swift b/Sources/RequestsFeature/ViewModels/RequestsReceivedViewModel.swift
index eb8469c67044a3cb962927a389da2831eb56ee4b..46d4fe29bc99ebb71207264a27663a1f16de7731 100644
--- a/Sources/RequestsFeature/ViewModels/RequestsReceivedViewModel.swift
+++ b/Sources/RequestsFeature/ViewModels/RequestsReceivedViewModel.swift
@@ -20,12 +20,10 @@ struct RequestReceived: Hashable, Equatable {
 }
 
 final class RequestsReceivedViewModel {
-  @Dependency(\.app.dbManager) var dbManager: DBManager
-  @Dependency(\.app.messenger) var messenger: Messenger
-  @Dependency(\.app.hudManager) var hudManager: HUDManager
-  @Dependency(\.reportingStatus) var reportingStatus: ReportingStatus
-
-  //@Dependency var groupManager: GroupChat
+  @Dependency(\.app.dbManager) var dbManager
+  @Dependency(\.app.messenger) var messenger
+  @Dependency(\.app.hudManager) var hudManager
+  @Dependency(\.reportingStatus) var reportingStatus
   
   @KeyObject(.isShowingHiddenRequests, defaultValue: false) var isShowingHiddenRequests: Bool
 
@@ -193,8 +191,8 @@ final class RequestsReceivedViewModel {
       guard let self else { return }
       
       do {
-        //try self.groupManager.joinGroup(serializedGroupData: group.serialized)
-        
+        try self.messenger.groupChat()!.joinGroup(serializedGroupData: group.serialized)
+
         var group = group
         group.authStatus = .participating
         try self.dbManager.getDB().saveGroup(group)
diff --git a/Sources/WebsiteFeature/WebsiteController.swift b/Sources/WebsiteFeature/WebsiteController.swift
index 822ebe38b883047de4c3f02157b883a2c0980bb6..dacb0998608f3f43972f01458dcca6e6357c2c8c 100644
--- a/Sources/WebsiteFeature/WebsiteController.swift
+++ b/Sources/WebsiteFeature/WebsiteController.swift
@@ -11,17 +11,14 @@ public final class WebsiteController: UIViewController {
     super.init(nibName: nil, bundle: nil)
   }
 
+  public override func loadView() {
+    view = webView
+  }
+
   required init?(coder: NSCoder) { nil }
 
   public override func viewDidLoad() {
     super.viewDidLoad()
-    view.backgroundColor = .white
-    view.addSubview(webView)
-
-    webView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
-    webView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
-    webView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
-    webView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
 
     DispatchQueue.main.async { [weak self] in
       guard let self else { return }