diff --git a/app/build.gradle b/app/build.gradle
index 6f314732fd..45d5a18a68 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -157,7 +157,7 @@ dependencies {
     testImplementation 'org.robolectric:shadows-multidex:4.2'
 }
 
-def canonicalVersionCode = 145
+def canonicalVersionCode = 147
 def canonicalVersionName = "1.9.0"
 
 def postFixSize = 10
@@ -235,7 +235,7 @@ android {
 
     buildTypes {
         release {
-            minifyEnabled true
+            minifyEnabled false
 
             proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'),
                     'proguard/proguard-dagger.pro',
diff --git a/app/proguard/proguard.pro b/app/proguard/proguard.pro
index 2634d86b94..863701970c 100644
--- a/app/proguard/proguard.pro
+++ b/app/proguard/proguard.pro
@@ -2,6 +2,7 @@
 -keepattributes SourceFile,LineNumberTable
 -keep class org.whispersystems.** { *; }
 -keep class org.thoughtcrime.securesms.** { *; }
+-keep class org.session.** { *; }
 -keepclassmembers class ** {
     public void onEvent*(**);
 }
diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java
index e86543ae8f..88d8c679f3 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java
@@ -255,8 +255,7 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
                 "SmsSentJob",
                 "SmsReceiveJob",
                 "PushGroupUpdateJob",
-                "ResetThreadSessionJob",
-                "SendDeliveryReceiptJob");
+                "ResetThreadSessionJob");
       }
 
       if (oldVersion < lokiV22) {
@@ -269,7 +268,8 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
                 "TypingSendJob",
                 "AttachmentUploadJob",
                 "RequestGroupInfoJob",
-                "ClosedGroupUpdateMessageSendJobV2");
+                "ClosedGroupUpdateMessageSendJobV2",
+                "SendDeliveryReceiptJob");
       }
 
       db.setTransactionSuccessful();
diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/MultiDeviceProtocol.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/MultiDeviceProtocol.kt
index 81e137171a..69dfc70126 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/MultiDeviceProtocol.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/MultiDeviceProtocol.kt
@@ -79,8 +79,8 @@ object MultiDeviceProtocol {
                 closedGroupUpdate.publicKey = ByteString.copyFrom(Hex.fromStringCondensed(closedGroup.publicKey))
                 closedGroupUpdate.name = closedGroup.name
                 val encryptionKeyPair = SignalServiceProtos.KeyPair.newBuilder()
-                encryptionKeyPair.publicKey = ByteString.copyFrom(closedGroup.encryptionKeyPair.publicKey.serialize().removing05PrefixIfNeeded())
-                encryptionKeyPair.privateKey = ByteString.copyFrom(closedGroup.encryptionKeyPair.privateKey.serialize())
+                encryptionKeyPair.publicKey = ByteString.copyFrom(closedGroup.encryptionKeyPair!!.publicKey.serialize().removing05PrefixIfNeeded())
+                encryptionKeyPair.privateKey = ByteString.copyFrom(closedGroup.encryptionKeyPair!!.privateKey.serialize())
                 closedGroupUpdate.encryptionKeyPair =  encryptionKeyPair.build()
                 closedGroupUpdate.addAllMembers(closedGroup.members.map { ByteString.copyFrom(Hex.fromStringCondensed(it)) })
                 closedGroupUpdate.addAllAdmins(closedGroup.admins.map { ByteString.copyFrom(Hex.fromStringCondensed(it)) })
diff --git a/libsession/src/main/java/org/session/libsession/messaging/jobs/MessageSendJob.kt b/libsession/src/main/java/org/session/libsession/messaging/jobs/MessageSendJob.kt
index d23aed8846..1a630b55ae 100644
--- a/libsession/src/main/java/org/session/libsession/messaging/jobs/MessageSendJob.kt
+++ b/libsession/src/main/java/org/session/libsession/messaging/jobs/MessageSendJob.kt
@@ -83,14 +83,14 @@ class MessageSendJob(val message: Message, val destination: Destination) : Job {
         //serialize Message and Destination properties
         val kryo = Kryo()
         kryo.isRegistrationRequired = false
-        val serializedMessage = ByteArray(4096)
-        val serializedDestination = ByteArray(4096)
-        var output = Output(serializedMessage)
+        val output = Output(ByteArray(4096), -1) // maxBufferSize '-1' will dynamically grow internally if we run out of room serializing the message
         kryo.writeClassAndObject(output, message)
         output.close()
-        output = Output(serializedDestination)
+        val serializedMessage = output.toBytes()
+        output.clear()
         kryo.writeClassAndObject(output, destination)
         output.close()
+        val serializedDestination = output.toBytes()
         return Data.Builder().putByteArray(KEY_MESSAGE, serializedMessage)
                 .putByteArray(KEY_DESTINATION, serializedDestination)
                 .build();
diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/Destination.kt b/libsession/src/main/java/org/session/libsession/messaging/messages/Destination.kt
index 47e48de38d..ef389b08b0 100644
--- a/libsession/src/main/java/org/session/libsession/messaging/messages/Destination.kt
+++ b/libsession/src/main/java/org/session/libsession/messaging/messages/Destination.kt
@@ -7,25 +7,14 @@ import org.session.libsignal.service.loki.utilities.toHexString
 
 sealed class Destination {
 
-    class Contact() : Destination() {
-        var publicKey: String = ""
-        internal constructor(publicKey: String): this() {
-            this.publicKey = publicKey
-        }
+    class Contact(var publicKey: String) : Destination() {
+        internal constructor(): this("")
     }
-    class ClosedGroup() : Destination() {
-        var groupPublicKey: String = ""
-        internal constructor(groupPublicKey: String): this() {
-            this.groupPublicKey = groupPublicKey
-        }
+    class ClosedGroup(var groupPublicKey: String) : Destination() {
+        internal constructor(): this("")
     }
-    class OpenGroup() : Destination() {
-        var channel: Long = 0
-        var server: String = ""
-        internal constructor(channel: Long, server: String): this() {
-            this.channel = channel
-            this.server = server
-        }
+    class OpenGroup(var channel: Long, var server: String) : Destination() {
+        internal constructor(): this(0, "")
     }
 
     companion object {
diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/control/ClosedGroupControlMessage.kt b/libsession/src/main/java/org/session/libsession/messaging/messages/control/ClosedGroupControlMessage.kt
index bad3ad566f..8179865208 100644
--- a/libsession/src/main/java/org/session/libsession/messaging/messages/control/ClosedGroupControlMessage.kt
+++ b/libsession/src/main/java/org/session/libsession/messaging/messages/control/ClosedGroupControlMessage.kt
@@ -30,18 +30,30 @@ class ClosedGroupControlMessage() : ControlMessage() {
 
     // Kind enum
     sealed class Kind {
-        class New(val publicKey: ByteString, val name: String, val encryptionKeyPair: ECKeyPair, val members: List<ByteString>, val admins: List<ByteString>) : Kind()
+        class New(var publicKey: ByteString, var name: String, var encryptionKeyPair: ECKeyPair?, var members: List<ByteString>, var admins: List<ByteString>) : Kind() {
+            internal constructor(): this(ByteString.EMPTY, "", null, listOf(), listOf())
+        }
         /// - Note: Deprecated in favor of more explicit group updates.
-        class Update(val name: String, val members: List<ByteString>) : Kind()
+        class Update(var name: String, var members: List<ByteString>) : Kind() {
+            internal constructor(): this("", listOf())
+        }
         /// An encryption key pair encrypted for each member individually.
         ///
         /// - Note: `publicKey` is only set when an encryption key pair is sent in a one-to-one context (i.e. not in a group).
-        class EncryptionKeyPair(val publicKey: ByteString?, val wrappers: Collection<KeyPairWrapper>) : Kind()
-        class NameChange(val name: String) : Kind()
-        class MembersAdded(val members: List<ByteString>) : Kind()
-        class MembersRemoved( val members: List<ByteString>) : Kind()
-        class MemberLeft : Kind()
-        class EncryptionKeyPairRequest: Kind()
+        class EncryptionKeyPair(var publicKey: ByteString?, var wrappers: Collection<KeyPairWrapper>) : Kind() {
+            internal constructor(): this(null, listOf())
+        }
+        class NameChange(var name: String) : Kind() {
+            internal constructor(): this("")
+        }
+        class MembersAdded(var members: List<ByteString>) : Kind() {
+            internal constructor(): this(listOf())
+        }
+        class MembersRemoved(var members: List<ByteString>) : Kind() {
+            internal constructor(): this(listOf())
+        }
+        class MemberLeft() : Kind()
+        class EncryptionKeyPairRequest(): Kind()
 
         val description: String = run {
             when(this) {
@@ -118,8 +130,8 @@ class ClosedGroupControlMessage() : ControlMessage() {
         val kind = kind ?: return false
         return when(kind) {
             is Kind.New -> {
-                !kind.publicKey.isEmpty && kind.name.isNotEmpty() && kind.encryptionKeyPair.publicKey != null
-                        && kind.encryptionKeyPair.privateKey != null && kind.members.isNotEmpty() && kind.admins.isNotEmpty()
+                !kind.publicKey.isEmpty && kind.name.isNotEmpty() && kind.encryptionKeyPair!!.publicKey != null
+                        && kind.encryptionKeyPair!!.privateKey != null && kind.members.isNotEmpty() && kind.admins.isNotEmpty()
             }
             is Kind.Update -> kind.name.isNotEmpty()
             is Kind.EncryptionKeyPair -> true
@@ -145,8 +157,8 @@ class ClosedGroupControlMessage() : ControlMessage() {
                     closedGroupControlMessage.publicKey = kind.publicKey
                     closedGroupControlMessage.name = kind.name
                     val encryptionKeyPair = SignalServiceProtos.KeyPair.newBuilder()
-                    encryptionKeyPair.publicKey = ByteString.copyFrom(kind.encryptionKeyPair.publicKey.serialize().removing05PrefixIfNeeded())
-                    encryptionKeyPair.privateKey = ByteString.copyFrom(kind.encryptionKeyPair.privateKey.serialize())
+                    encryptionKeyPair.publicKey = ByteString.copyFrom(kind.encryptionKeyPair!!.publicKey.serialize().removing05PrefixIfNeeded())
+                    encryptionKeyPair.privateKey = ByteString.copyFrom(kind.encryptionKeyPair!!.privateKey.serialize())
                     closedGroupControlMessage.encryptionKeyPair =  encryptionKeyPair.build()
                     closedGroupControlMessage.addAllMembers(kind.members)
                     closedGroupControlMessage.addAllAdmins(kind.admins)
diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/control/ConfigurationMessage.kt b/libsession/src/main/java/org/session/libsession/messaging/messages/control/ConfigurationMessage.kt
index ecb56edf02..fe06cc6c01 100644
--- a/libsession/src/main/java/org/session/libsession/messaging/messages/control/ConfigurationMessage.kt
+++ b/libsession/src/main/java/org/session/libsession/messaging/messages/control/ConfigurationMessage.kt
@@ -14,11 +14,13 @@ import org.session.libsignal.service.loki.utilities.removing05PrefixIfNeeded
 import org.session.libsignal.service.loki.utilities.toHexString
 import org.session.libsignal.utilities.Hex
 
-class ConfigurationMessage(val closedGroups: List<ClosedGroup>, val openGroups: List<String>, val contacts: List<Contact>, val displayName: String, val profilePicture: String?, val profileKey: ByteArray): ControlMessage() {
+class ConfigurationMessage(var closedGroups: List<ClosedGroup>, var openGroups: List<String>, var contacts: List<Contact>, var displayName: String, var profilePicture: String?, var profileKey: ByteArray): ControlMessage() {
 
-    class ClosedGroup(val publicKey: String, val name: String, val encryptionKeyPair: ECKeyPair, val members: List<String>, val admins: List<String>) {
+    class ClosedGroup(var publicKey: String, var name: String, var encryptionKeyPair: ECKeyPair?, var members: List<String>, var admins: List<String>) {
         val isValid: Boolean get() = members.isNotEmpty() && admins.isNotEmpty()
 
+        internal constructor(): this("", "", null, listOf(), listOf())
+
         override fun toString(): String {
             return name
         }
@@ -30,7 +32,7 @@ class ConfigurationMessage(val closedGroups: List<ClosedGroup>, val openGroups:
                 val name = proto.name
                 val encryptionKeyPairAsProto = proto.encryptionKeyPair
                 val encryptionKeyPair = ECKeyPair(DjbECPublicKey(encryptionKeyPairAsProto.publicKey.toByteArray().removing05PrefixIfNeeded()),
-                                                  DjbECPrivateKey(encryptionKeyPairAsProto.privateKey.toByteArray()))
+                        DjbECPrivateKey(encryptionKeyPairAsProto.privateKey.toByteArray()))
                 val members = proto.membersList.map { it.toByteArray().toHexString() }
                 val admins = proto.adminsList.map { it.toByteArray().toHexString() }
                 return ClosedGroup(publicKey, name, encryptionKeyPair, members, admins)
@@ -42,8 +44,8 @@ class ConfigurationMessage(val closedGroups: List<ClosedGroup>, val openGroups:
             result.publicKey = ByteString.copyFrom(Hex.fromStringCondensed(publicKey))
             result.name = name
             val encryptionKeyPairAsProto = SignalServiceProtos.KeyPair.newBuilder()
-            encryptionKeyPairAsProto.publicKey = ByteString.copyFrom(encryptionKeyPair.publicKey.serialize().removing05PrefixIfNeeded())
-            encryptionKeyPairAsProto.privateKey = ByteString.copyFrom(encryptionKeyPair.privateKey.serialize())
+            encryptionKeyPairAsProto.publicKey = ByteString.copyFrom(encryptionKeyPair!!.publicKey.serialize().removing05PrefixIfNeeded())
+            encryptionKeyPairAsProto.privateKey = ByteString.copyFrom(encryptionKeyPair!!.privateKey.serialize())
             result.encryptionKeyPair = encryptionKeyPairAsProto.build()
             result.addAllMembers(members.map { ByteString.copyFrom(Hex.fromStringCondensed(it)) })
             result.addAllAdmins(admins.map { ByteString.copyFrom(Hex.fromStringCondensed(it)) })
@@ -51,7 +53,10 @@ class ConfigurationMessage(val closedGroups: List<ClosedGroup>, val openGroups:
         }
     }
 
-    class Contact(val publicKey: String, val name: String, val profilePicture: String?, val profileKey: ByteArray?) {
+    class Contact(var publicKey: String, var name: String, var profilePicture: String?, var profileKey: ByteArray?) {
+
+        internal constructor(): this("", "", null, null)
+
         companion object {
             fun fromProto(proto: SignalServiceProtos.ConfigurationMessage.Contact): Contact? {
                 if (!proto.hasName() || !proto.hasProfileKey()) return null
@@ -128,6 +133,8 @@ class ConfigurationMessage(val closedGroups: List<ClosedGroup>, val openGroups:
         }
     }
 
+    internal constructor(): this(listOf(), listOf(), listOf(), "", null, byteArrayOf())
+
     override fun toProto(): SignalServiceProtos.Content? {
         val configurationProto = SignalServiceProtos.ConfigurationMessage.newBuilder()
         configurationProto.addAllClosedGroups(closedGroups.mapNotNull { it.toProto() })
diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageReceiverHandler.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageReceiverHandler.kt
index 64a4e39c62..bde902a31e 100644
--- a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageReceiverHandler.kt
+++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageReceiverHandler.kt
@@ -127,7 +127,7 @@ private fun MessageReceiver.handleConfigurationMessage(message: ConfigurationMes
     val allClosedGroupPublicKeys = storage.getAllClosedGroupPublicKeys()
     for (closeGroup in message.closedGroups) {
         if (allClosedGroupPublicKeys.contains(closeGroup.publicKey)) continue
-        handleNewClosedGroup(message.sender!!, message.sentTimestamp!!, closeGroup.publicKey, closeGroup.name, closeGroup.encryptionKeyPair, closeGroup.members, closeGroup.admins, message.sentTimestamp!!)
+        handleNewClosedGroup(message.sender!!, message.sentTimestamp!!, closeGroup.publicKey, closeGroup.name, closeGroup.encryptionKeyPair!!, closeGroup.members, closeGroup.admins, message.sentTimestamp!!)
     }
     val allOpenGroups = storage.getAllOpenGroups().map { it.value.server }
     for (openGroup in message.openGroups) {
@@ -239,7 +239,7 @@ private fun MessageReceiver.handleNewClosedGroup(message: ClosedGroupControlMess
     val groupPublicKey = kind.publicKey.toByteArray().toHexString()
     val members = kind.members.map { it.toByteArray().toHexString() }
     val admins = kind.admins.map { it.toByteArray().toHexString() }
-    handleNewClosedGroup(message.sender!!, message.sentTimestamp!!, groupPublicKey, kind.name, kind.encryptionKeyPair, members, admins, message.sentTimestamp!!)
+    handleNewClosedGroup(message.sender!!, message.sentTimestamp!!, groupPublicKey, kind.name, kind.encryptionKeyPair!!, members, admins, message.sentTimestamp!!)
 }
 
 // Parameter @sender:String is just for inserting incoming info message
diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSender.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSender.kt
index d54fdc6f15..4609a2b925 100644
--- a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSender.kt
+++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSender.kt
@@ -12,9 +12,6 @@ import org.session.libsession.messaging.messages.control.ClosedGroupControlMessa
 import org.session.libsession.messaging.messages.control.ConfigurationMessage
 import org.session.libsession.messaging.messages.control.ExpirationTimerUpdate
 import org.session.libsession.messaging.messages.visible.*
-import org.session.libsession.messaging.sending_receiving.attachments.Attachment as SignalAttachment
-import org.session.libsession.messaging.sending_receiving.linkpreview.LinkPreview as SignalLinkPreview
-import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel as SignalQuote
 import org.session.libsession.messaging.opengroups.OpenGroupAPI
 import org.session.libsession.messaging.opengroups.OpenGroupMessage
 import org.session.libsession.messaging.threads.Address
@@ -24,11 +21,15 @@ import org.session.libsession.snode.SnodeAPI
 import org.session.libsession.snode.SnodeConfiguration
 import org.session.libsession.snode.SnodeMessage
 import org.session.libsession.utilities.SSKEnvironment
+import org.session.libsignal.service.internal.push.PushTransportDetails
 import org.session.libsignal.service.internal.push.SignalServiceProtos
 import org.session.libsignal.service.loki.api.crypto.ProofOfWork
 import org.session.libsignal.service.loki.utilities.hexEncodedPublicKey
 import org.session.libsignal.utilities.Base64
 import org.session.libsignal.utilities.logging.Log
+import org.session.libsession.messaging.sending_receiving.attachments.Attachment as SignalAttachment
+import org.session.libsession.messaging.sending_receiving.linkpreview.LinkPreview as SignalLinkPreview
+import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel as SignalQuote
 
 
 object MessageSender {
@@ -76,21 +77,21 @@ object MessageSender {
         // Set the timestamp, sender and recipient
         message.sentTimestamp ?: run { message.sentTimestamp = System.currentTimeMillis() } /* Visible messages will already have their sent timestamp set */
         message.sender = userPublicKey
+        val isSelfSend = (message.recipient == userPublicKey)
+        // Set the failure handler (need it here already for precondition failure handling)
+        fun handleFailure(error: Exception) {
+            handleFailedMessageSend(message, error)
+            if (destination is Destination.Contact && message is VisibleMessage && !isSelfSend) {
+                SnodeConfiguration.shared.broadcaster.broadcast("messageFailed", message.sentTimestamp!!)
+            }
+            deferred.reject(error)
+        }
         try {
             when (destination) {
                 is Destination.Contact -> message.recipient = destination.publicKey
                 is Destination.ClosedGroup -> message.recipient = destination.groupPublicKey
                 is Destination.OpenGroup -> throw preconditionFailure
             }
-            val isSelfSend = (message.recipient == userPublicKey)
-            // Set the failure handler (need it here already for precondition failure handling)
-            fun handleFailure(error: Exception) {
-                handleFailedMessageSend(message, error)
-                if (destination is Destination.Contact && message is VisibleMessage && !isSelfSend) {
-                    SnodeConfiguration.shared.broadcaster.broadcast("messageFailed", message.sentTimestamp!!)
-                }
-                deferred.reject(error)
-            }
             // Validate the message
             if (!message.isValid()) { throw Error.InvalidMessage }
             // Stop here if this is a self-send, unless it's:
@@ -118,7 +119,7 @@ object MessageSender {
             // Convert it to protobuf
             val proto = message.toProto() ?: throw Error.ProtoConversionFailed
             // Serialize the protobuf
-            val plaintext = proto.toByteArray()
+            val plaintext = PushTransportDetails.getPaddedMessageBody(proto.toByteArray())
             // Encrypt the serialized protobuf
             val ciphertext: ByteArray
             when (destination) {
@@ -183,15 +184,14 @@ object MessageSender {
                         errorCount += 1
                         if (errorCount != promiseCount) { return@fail } // Only error out if all promises failed
                         handleFailure(it)
-                        deferred.reject(it)
                     }
                 }
             }.fail {
                 Log.d("Loki", "Couldn't send message due to error: $it.")
-                deferred.reject(it)
+                handleFailure(it)
             }
         } catch (exception: Exception) {
-            deferred.reject(exception)
+            handleFailure(exception)
         }
         return promise
     }