diff --git a/README.md b/README.md
index c3f572fd1a8e885c9dec994f626fbcca7318c450..dc58f697ff36740f76504073c92ae3d96676b16d 100644
--- a/README.md
+++ b/README.md
@@ -415,4 +415,97 @@ You can verify that all symbols got bound by unzipping `bindings-sources.jar` an
 
 Every time you make a change to the client or bindings, you must rebuild the client bindings into a `.aar` or `iOS.zip`
 to propagate those changes to the app. There's a script that runs gomobile for you in the `bindings-integration`
-repository.
\ No newline at end of file
+repository.
+
+
+## Regenerate Protobuf File
+
+First install the protobuf compiler or update by following the instructions in
+[Installing Protocol Buffer Compiler](#installing-protocol-buffer-compiler)
+below.
+
+Use the following command to compile a protocol buffer.
+
+```shell
+protoc -I. -I../vendor --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative *.proto
+```
+
+* This command must be run from the directory containing the `.proto` file
+  being compiled.
+* The `-I` flag specifies where to find imports used by the `.proto` file and
+  may need to be modified or removed to suit the .proto file being compiled.\
+    * 💡 **Note:** Note: If you are importing a file from the vendor directory,
+      ensure that you have the correct version by running `go mod vendor`.
+* If there is more than one proto file in the directory, replace `*.proto` with
+  the file’s name.
+* If the `.proto` file does not use gRPC, then the `--go-grpc_out` and
+  `--go-grpc_opt` can be excluded.
+
+
+
+## Installing Protocol Buffer Compiler
+
+This guide describes how to install the required dependencies to compile
+`.proto` files to Go.
+
+Before following the instructions below, be sure to remove all old versions of
+`protoc`. If your previous protoc-gen-go file is not installed in your Go bin
+directory, it will also need to be removed.
+
+If you have followed this guide previously when installing `protoc` and need to
+update, you can simply follow the instructions below. No uninstallation or
+removal is necessary.
+
+To compile a protocol buffer, you need the protocol buffer compiler `protoc`
+along with two plugins `protoc-gen-go` and `protoc-gen-go-grpc`. Make sure you
+use the correct versions as listed below.
+
+|                      | Version | Download                                                            | Documentation                                                           |
+|----------------------|--------:|---------------------------------------------------------------------|-------------------------------------------------------------------------|
+| `protoc`             |  3.15.6 | https://github.com/protocolbuffers/protobuf/releases/tag/v3.15.6    | https://developers.google.com/protocol-buffers/docs/gotutorial          |
+| `protoc-gen-go`      |  1.27.1 | https://github.com/protocolbuffers/protobuf-go/releases/tag/v1.27.1 | https://pkg.go.dev/google.golang.org/protobuf@v1.27.1/cmd/protoc-gen-go |
+| `protoc-gen-go-grpc` |   1.2.0 | https://github.com/grpc/grpc-go/releases/tag/v1.2.0                 | https://pkg.go.dev/google.golang.org/grpc/cmd/protoc-gen-go-grpc        |
+
+1. Download the correct release of `protoc` from the
+   [release page](https://github.com/protocolbuffers/protobuf/releases) or use
+   the link from the table above to get the download for your OS.
+
+       wget https://github.com/protocolbuffers/protobuf/releases/download/v3.15.6/protoc-3.15.6-linux-x86_64.zip
+
+2. Extract the files to a folder, such as `$HOME/.local`.
+
+       unzip protoc-3.15.6-linux-x86_64.zip -d $HOME/.local
+
+3. Add the selected directory to your environment’s `PATH` variable, make sure
+   to include it in your `.profile` or `.bashrc` file. Also, include your go bin
+   directory (`$GOPATH/bin` or `$GOBIN`) if it is not already included.
+
+       export PATH="$PATH:$HOME/.local/bin:$GOPATH/bin"
+
+   💡 **Note:** Make sure you update your configuration file once done with
+   source `.profile`.
+
+4. Now check that `protoc` is installed with the correct version by running the
+   following command.
+
+       protoc --version
+
+   Which prints the current version
+
+       libprotoc 3.15.6
+
+5. Next, download `protoc-gen-go` and `protoc-gen-go-grpc` using the version
+   found in the table above.
+
+       go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.27
+       go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2
+
+6. Check that `protoc-gen-go` is installed with the correct version.
+
+       protoc-gen-go --version
+       protoc-gen-go v1.27.1
+
+7. Check that `protoc-gen-go-grpc` is installed with the correct version.
+
+       protoc-gen-go-grpc --version
+       protoc-gen-go-grpc 1.2.0
\ No newline at end of file
diff --git a/backup/backup.go b/backup/backup.go
index d4cc92a408a48cd809b30a93f25bb9ea93b02e59..b7fc57f5e3d46cb4e17277cc2d236b146cffd74f 100644
--- a/backup/backup.go
+++ b/backup/backup.go
@@ -93,6 +93,7 @@ type UpdateBackupFn func(encryptedBackup []byte)
 func InitializeBackup(backupPassphrase string, updateBackupCb UpdateBackupFn,
 	container *xxdk.Container, e2e E2e, session Session, ud UserDiscovery,
 	kv *versioned.KV, rng *fastRNG.StreamGenerator) (*Backup, error) {
+
 	b := &Backup{
 		updateBackupCb: updateBackupCb,
 		container:      container,
diff --git a/backup/backupRestore.go b/backup/backupRestore.go
index 6be4fd04620c011b8d3c318471e4aef04d86b700..2d408c173b49cd1a82d824b480bb3c2c47fcddce 100644
--- a/backup/backupRestore.go
+++ b/backup/backupRestore.go
@@ -92,6 +92,7 @@ func NewCmixFromBackup(ndfJSON, storageDir, backupPassphrase string,
 			phone = f
 		}
 	}
+
 	err = ud.InitStoreFromBackup(storageSess.GetKV(), username, email, phone)
 	return backUp.Contacts.Identities, backUp.JSONParams, err
 }
diff --git a/bindings/fileTransfer.go b/bindings/fileTransfer.go
index 2a25aa4a7829c0fdd576c9c660709261dd23997c..4b8b5adde300191ba85b524fdd1499c3319b1998 100644
--- a/bindings/fileTransfer.go
+++ b/bindings/fileTransfer.go
@@ -9,6 +9,7 @@ package bindings
 
 import (
 	"encoding/json"
+	jww "github.com/spf13/jwalterweatherman"
 	"time"
 
 	"gitlab.com/elixxir/client/fileTransfer"
@@ -127,7 +128,7 @@ type FileTransferReceiveProgressCallback interface {
 //  - paramsJSON - JSON marshalled fileTransfer.Params
 func InitFileTransfer(e2eID int, receiveFileCallback ReceiveFileCallback,
 	e2eFileTransferParamsJson, fileTransferParamsJson []byte) (*FileTransfer, error) {
-
+	jww.INFO.Printf("Calling InitFileTransfer()")
 	// Get user from singleton
 	user, err := e2eTrackerSingleton.get(e2eID)
 	if err != nil {
diff --git a/bindings/group.go b/bindings/group.go
index ddb6f12b985ebfaf7e6d7c1df0c2c3b86cae3c45..b7faa628b8b1439b27b0f27413b7049e0f926a97 100644
--- a/bindings/group.go
+++ b/bindings/group.go
@@ -348,7 +348,7 @@ type GroupRequest interface {
 // The decryptedMessage field will be a JSON marshalled GroupChatMessage.
 type GroupChatProcessor interface {
 	Process(decryptedMessage, msg, receptionId []byte, ephemeralId,
-		roundId int64, err error)
+		roundId int64, roundUrl string, err error)
 	fmt.Stringer
 }
 
@@ -402,13 +402,14 @@ func convertMessageReceive(decryptedMsg gc.MessageReceive) GroupChatMessage {
 // binding-layer primitives equivalents within the GroupChatProcessor.Process.
 func convertGroupChatProcessor(decryptedMsg gc.MessageReceive, msg format.Message,
 	receptionID receptionID.EphemeralIdentity, round rounds.Round) (
-	decryptedMessage, message, receptionId []byte, ephemeralId, roundId int64, err error) {
+	decryptedMessage, message, receptionId []byte, ephemeralId, roundId int64, roundUrl string, err error) {
 
 	decryptedMessage, err = json.Marshal(convertMessageReceive(decryptedMsg))
 	message = msg.Marshal()
 	receptionId = receptionID.Source.Marshal()
 	ephemeralId = receptionID.EphId.Int64()
 	roundId = int64(round.ID)
+	roundUrl = getRoundURL(round.ID)
 	return
 }
 
diff --git a/bindings/ud.go b/bindings/ud.go
index 976321c5b33b5fd964b37dfd4c3aa5e97be66cac..9332ea975b90db019389bfe569a73ccbaa40880d 100644
--- a/bindings/ud.go
+++ b/bindings/ud.go
@@ -203,29 +203,23 @@ func NewOrLoadUd(e2eID int, follower UdNetworkStatus, username string,
 }
 
 // NewUdManagerFromBackup builds a new user discover manager from a backup. It
-// will construct a manager that is already registered and restore already
-// registered facts into store.
+// will construct a manager that is already registered. Confirmed facts have
+// already been restored via the call NewCmixFromBackup.
 //
 // Parameters:
 //  - e2eID - e2e object ID in the tracker
 //  - follower - network follower func wrapped in UdNetworkStatus
-//  - username - The username this user registered with initially. This should
-//               not be nullable, and be JSON marshalled as retrieved from
-//               UserDiscovery.GetFacts().
-//  - emailFactJson - nullable JSON marshalled email [fact.Fact]
-//  - phoneFactJson - nullable JSON marshalled phone [fact.Fact]
 //  - cert - the TLS certificate for the UD server this call will connect with.
 //    You may use the UD server run by the xx network team by using
-//    E2e.GetUdCertFromNdf.
-//  - contactFile - the data within a marshalled contact.Contact. This
+//    [E2e.GetUdCertFromNdf].
+//  - contactFile - the data within a marshalled [contact.Contact]. This
 //    represents the contact file of the server this call will connect with. You
 //    may use the UD server run by the xx network team by using
-//    E2e.GetUdContactFromNdf.
+//    [E2e.GetUdContactFromNdf].
 //  - address - the IP address of the UD server this call will connect with. You
 //    may use the UD server run by the xx network team by using
-//    E2e.GetUdAddressFromNdf.
+//    [E2e.GetUdAddressFromNdf].
 func NewUdManagerFromBackup(e2eID int, follower UdNetworkStatus,
-	usernameJson, emailFactJson, phoneFactJson,
 	cert, contactFile []byte, address string) (*UserDiscovery, error) {
 
 	// Get user from singleton
@@ -234,38 +228,12 @@ func NewUdManagerFromBackup(e2eID int, follower UdNetworkStatus,
 		return nil, err
 	}
 
-	var email, phone, username fact.Fact
-
-	// Parse email if non-nil
-	if emailFactJson != nil {
-		err = json.Unmarshal(emailFactJson, &email)
-		if err != nil {
-			return nil, err
-		}
-	}
-
-	// Parse phone if non-nil
-	if phoneFactJson != nil {
-		err = json.Unmarshal(phoneFactJson, &phone)
-		if err != nil {
-			return nil, err
-		}
-	}
-
-	// Parse username
-	err = json.Unmarshal(usernameJson, &username)
-	if err != nil {
-		return nil, err
-	}
-
 	UdNetworkStatusFn := func() xxdk.Status {
 		return xxdk.Status(follower.UdNetworkStatus())
 	}
 
-	u, err := ud.NewManagerFromBackup(
-		user.api, user.api.GetComms(), UdNetworkStatusFn,
-		username, email, phone,
-		cert, contactFile, address)
+	u, err := ud.NewManagerFromBackup(user.api, user.api.GetComms(),
+		UdNetworkStatusFn, cert, contactFile, address)
 	if err != nil {
 		return nil, err
 	}
@@ -445,9 +413,9 @@ type UdMultiLookupCallback interface {
 }
 
 type lookupResp struct {
-	id      *id.ID
-	contact contact.Contact
-	err     error
+	Id      *id.ID
+	Contact contact.Contact
+	Err     error
 }
 
 // MultiLookupUD returns the public key of all passed in IDs as known by the
@@ -490,26 +458,27 @@ func MultiLookupUD(e2eID int, udContact []byte, cb UdMultiLookupCallback,
 		return err
 	}
 
+	jww.INFO.Printf("ud.MultiLookupUD(%s, %s)", idList, p.Timeout)
+
 	respCh := make(chan lookupResp, len(idList))
 	for _, uid := range idList {
-		localID := uid.DeepCopy()
 		callback := func(c contact.Contact, err error) {
 			respCh <- lookupResp{
-				id:      localID,
-				contact: c,
-				err:     err,
+				Id:      uid,
+				Contact: c,
+				Err:     err,
 			}
 		}
-		go func() {
+		go func(localID *id.ID) {
 			_, _, err := ud.Lookup(user.api, c, callback, localID, p)
 			if err != nil {
 				respCh <- lookupResp{
-					id:      localID,
-					contact: contact.Contact{},
-					err:     err,
+					Id:      localID,
+					Contact: contact.Contact{},
+					Err:     err,
 				}
 			}
-		}()
+		}(uid.DeepCopy())
 
 	}
 
@@ -519,14 +488,14 @@ func MultiLookupUD(e2eID int, udContact []byte, cb UdMultiLookupCallback,
 		var errorString string
 		for numReturned := 0; numReturned < len(idList); numReturned++ {
 			response := <-respCh
-			if response.err != nil {
-				failedIDs = append(failedIDs, response.id)
-				marshaledContactList = append(
-					marshaledContactList, response.contact.Marshal())
-			} else {
+			if response.Err != nil {
+				failedIDs = append(failedIDs, response.Id)
 				errorString = errorString +
 					fmt.Sprintf("Failed to lookup id %s: %+v",
-						response.id, response.err)
+						response.Id, response.Err)
+			} else {
+				marshaledContactList = append(
+					marshaledContactList, response.Contact.Marshal())
 			}
 		}
 
diff --git a/fileTransfer/manager.go b/fileTransfer/manager.go
index 923218fcd2315dae4541de13dc75827f498b98d7..f4ef20744369953459f0f49c1fc24c5b57e18726 100644
--- a/fileTransfer/manager.go
+++ b/fileTransfer/manager.go
@@ -216,21 +216,21 @@ func NewManager(params Params, user FtE2e) (FileTransfer, error) {
 // StartProcesses starts the sending threads. Adheres to the xxdk.Service type.
 func (m *manager) StartProcesses() (stoppable.Stoppable, error) {
 	// Construct stoppables
+	multiStoppable := stoppable.NewMulti(fileTransferStoppable)
 	senderPoolStop := stoppable.NewMulti(workerPoolStoppable)
 	batchBuilderStop := stoppable.NewSingle(batchBuilderThreadStoppable)
 
 	// Start sending threads
+	go m.batchBuilderThread(batchBuilderStop)
+
 	// Note that the startSendingWorkerPool already creates thread for every
 	// worker. As a result, there is no need to run it asynchronously. In fact,
 	// running this asynchronously could result in a race condition where
 	// some worker threads are not added to senderPoolStop before that stoppable
 	// is added to the multiStoppable.
 	m.startSendingWorkerPool(senderPoolStop)
-	go m.batchBuilderThread(batchBuilderStop)
 
 	// Create a multi stoppable
-	multiStoppable := stoppable.NewMulti(fileTransferStoppable)
-	multiStoppable.Add(senderPoolStop)
 	multiStoppable.Add(batchBuilderStop)
 
 	return multiStoppable, nil
diff --git a/fileTransfer/send.go b/fileTransfer/send.go
index ea9bac2027cc65f2db3ede5ee8ef684cb1eb7f05..e4a107f7b3e588318056706dc2b8f6dba9bf3d02 100644
--- a/fileTransfer/send.go
+++ b/fileTransfer/send.go
@@ -74,16 +74,44 @@ func (m *manager) sendingThread(stop *stoppable.Single) {
 	healthChanID := m.cmix.AddHealthCallback(func(b bool) { healthChan <- b })
 	for {
 		select {
+		// A quit signal has been sent by the user. Typically, this is a result
+		// of a user-level shutdown of the client.
 		case <-stop.Quit():
 			jww.DEBUG.Printf("[FT] Stopping file part sending thread (%s): "+
 				"stoppable triggered.", stop.Name())
 			m.cmix.RemoveHealthCallback(healthChanID)
 			stop.ToStopped()
 			return
+
+		// If the network becomes unhealthy, we will cease sending files until
+		// it is resolved.
 		case healthy := <-healthChan:
+			// There exists an edge case where an unhealthy signal is received
+			// due to a user-level shutdown, meaning the health tracker has
+			// ceased operation. If the health tracker is shutdown, a healthy
+			// signal will never be received, and this for loop will run
+			// infinitely. If we are caught in this loop, the stop's Quit()
+			// signal will never be received in case statement above, and this
+			// sender thread will run indefinitely. To avoid lingering threads
+			// in the case of a shutdown, we must actively listen for either the
+			// Quit() signal or a network health update here.
 			for !healthy {
-				healthy = <-healthChan
+				select {
+				case <-stop.Quit():
+					// Listen for a quit signal if the network becomes unhealthy
+					// before a user-level shutdown.
+					jww.DEBUG.Printf("[FT] Stopping file part sending "+
+						"thread (%s): stoppable triggered.", stop.Name())
+					m.cmix.RemoveHealthCallback(healthChanID)
+					stop.ToStopped()
+					return
+
+				// Wait for a healthy signal before continuing to send files.
+				case healthy = <-healthChan:
+				}
 			}
+		// A file part has been sent through the queue and must be sent by
+		// this thread.
 		case packet := <-m.sendQueue:
 			m.sendCmix(packet)
 		}
diff --git a/groupChat/send.go b/groupChat/send.go
index 2f1b17c167b13f5c06dbbc3fd3223dc6c1ef4e0f..5c4d8dee34eca9362cce47115f223725c2ec7bfb 100644
--- a/groupChat/send.go
+++ b/groupChat/send.go
@@ -14,9 +14,7 @@ import (
 	"gitlab.com/elixxir/client/cmix/message"
 	"gitlab.com/elixxir/client/cmix/rounds"
 	gs "gitlab.com/elixxir/client/groupChat/groupStore"
-	"gitlab.com/elixxir/crypto/cyclic"
 	"gitlab.com/elixxir/crypto/group"
-	"gitlab.com/elixxir/primitives/format"
 	"gitlab.com/xx_network/primitives/id"
 	"gitlab.com/xx_network/primitives/netTime"
 	"io"
@@ -64,7 +62,7 @@ func (m *manager) Send(groupID *id.ID, tag string, message []byte) (
 	timeNow := netTime.Now().Round(0)
 
 	// Create a cMix message for each group member
-	groupMessages, err := m.newMessages(g, tag, message, timeNow)
+	groupMessages, msgId, err := m.newMessages(g, tag, message, timeNow)
 	if err != nil {
 		return rounds.Round{}, time.Time{}, group.MessageID{},
 			errors.Errorf(newCmixMsgErr, g.Name, g.ID, err)
@@ -93,14 +91,39 @@ func (m *manager) Send(groupID *id.ID, tag string, message []byte) (
 
 // newMessages builds a list of messages, one for each group chat member.
 func (m *manager) newMessages(g gs.Group, tag string, msg []byte,
-	timestamp time.Time) ([]cmix.TargetedCmixMessage, error) {
+	timestamp time.Time) ([]cmix.TargetedCmixMessage, group.MessageID, error) {
 
 	// Create list of cMix messages
 	messages := make([]cmix.TargetedCmixMessage, 0, len(g.Members))
 	rng := m.getRng().GetStream()
 	defer rng.Close()
 
-	// Create cMix messages in parallel
+	// Generate initial internal message
+	maxCmixMessageLength := m.getCMix().GetMaxMessageLength()
+
+	// Generate public message to determine what length internal message can be
+	pubMsg, err := newPublicMsg(maxCmixMessageLength)
+	if err != nil {
+		return nil, group.MessageID{}, errors.Errorf(newPublicMsgErr, err)
+	}
+
+	// Generate internal message
+	intlMsg, err := newInternalMsg(pubMsg.GetPayloadSize())
+	if err != nil {
+		return nil, group.MessageID{}, errors.Errorf(newInternalMsgErr, err)
+	}
+
+	// Return an error if the message is too large to fit in the payload
+	if intlMsg.GetPayloadMaxSize() < len(msg) {
+		return nil, group.MessageID{}, errors.Errorf(
+			messageLenErr, len(msg), intlMsg.GetPayloadMaxSize())
+	}
+
+	// Generate internal message
+	internalMessagePayload := setInternalPayload(intlMsg, timestamp,
+		m.getReceptionIdentity().ID, msg)
+
+	// Create cMix messages
 	for _, member := range g.Members {
 		// Do not send to the sender
 		if m.getReceptionIdentity().ID.Cmp(member.ID) {
@@ -108,21 +131,22 @@ func (m *manager) newMessages(g gs.Group, tag string, msg []byte,
 		}
 
 		// Add cMix message to list
-		cMixMsg, err := newCmixMsg(g, tag, msg, timestamp, member, rng,
-			m.getReceptionIdentity().ID, m.getCMix().GetMaxMessageLength())
+		cMixMsg, err := newCmixMsg(g, tag, timestamp, member, rng, maxCmixMessageLength,
+			internalMessagePayload)
 		if err != nil {
-			return nil, err
+			return nil, group.MessageID{}, err
 		}
 		messages = append(messages, cMixMsg)
 	}
 
-	return messages, nil
+	return messages, group.NewMessageID(g.ID, internalMessagePayload), nil
 }
 
 // newCmixMsg generates a new cmix.TargetedCmixMessage for the given group
 // member
-func newCmixMsg(g gs.Group, tag string, msg []byte, timestamp time.Time,
-	mem group.Member, rng io.Reader, senderId *id.ID, maxCmixMessageSize int) (
+func newCmixMsg(g gs.Group, tag string, timestamp time.Time,
+	mem group.Member, rng io.Reader, maxCmixMessageSize int,
+	internalMessagePayload []byte) (
 	cmix.TargetedCmixMessage, error) {
 
 	// Initialize targeted message
@@ -135,18 +159,12 @@ func newCmixMsg(g gs.Group, tag string, msg []byte, timestamp time.Time,
 		},
 	}
 
-	// Create three message layers
-	pubMsg, intlMsg, err := newMessageParts(maxCmixMessageSize)
+	// Generate public message
+	pubMsg, err := newPublicMsg(maxCmixMessageSize)
 	if err != nil {
 		return cmixMsg, err
 	}
 
-	// Return an error if the message is too large to fit in the payload
-	if intlMsg.GetPayloadMaxSize() < len(msg) {
-		return cmixMsg, errors.Errorf(
-			messageLenErr, len(msg), intlMsg.GetPayloadMaxSize())
-	}
-
 	// Generate 256-bit salt
 	salt, err := newSalt(rng)
 	if err != nil {
@@ -162,11 +180,9 @@ func newCmixMsg(g gs.Group, tag string, msg []byte, timestamp time.Time,
 		return cmixMsg, errors.WithMessage(err, newKeyErr)
 	}
 
-	// Generate internal message
-	payload := setInternalPayload(intlMsg, timestamp, senderId, msg)
-
 	// Encrypt internal message
-	encryptedPayload := group.Encrypt(key, cmixMsg.Fingerprint, payload)
+	encryptedPayload := group.Encrypt(key, cmixMsg.Fingerprint,
+		internalMessagePayload)
 
 	// Generate public message
 	cmixMsg.Payload = setPublicPayload(pubMsg, salt, encryptedPayload)
@@ -177,35 +193,6 @@ func newCmixMsg(g gs.Group, tag string, msg []byte, timestamp time.Time,
 	return cmixMsg, nil
 }
 
-// getGroupMessageId builds the group message ID.
-func getGroupMessageId(grp *cyclic.Group, groupId, senderId *id.ID,
-	timestamp time.Time, msg []byte) (group.MessageID, error) {
-	cmixMsg := format.NewMessage(grp.GetP().ByteLen())
-	_, intlMsg, err := newMessageParts(cmixMsg.ContentsSize())
-	if err != nil {
-		return group.MessageID{}, errors.WithMessage(err,
-			"Failed to make message parts for message ID")
-	}
-	return group.NewMessageID(groupId,
-		setInternalPayload(intlMsg, timestamp, senderId, msg)), nil
-}
-
-// newMessageParts generates a public payload message and the internal payload
-// message. An error is returned if the messages cannot fit in the payloadSize.
-func newMessageParts(payloadSize int) (publicMsg, internalMsg, error) {
-	pubMsg, err := newPublicMsg(payloadSize)
-	if err != nil {
-		return pubMsg, internalMsg{}, errors.Errorf(newPublicMsgErr, err)
-	}
-
-	intlMsg, err := newInternalMsg(pubMsg.GetPayloadSize())
-	if err != nil {
-		return pubMsg, intlMsg, errors.Errorf(newInternalMsgErr, err)
-	}
-
-	return pubMsg, intlMsg, nil
-}
-
 // newSalt generates a new salt of the specified size.
 func newSalt(rng io.Reader) ([group.SaltLen]byte, error) {
 	var salt [group.SaltLen]byte
diff --git a/groupChat/send_test.go b/groupChat/send_test.go
index 5ecaa63efdca7f1d461fb59c67908988370accc5..c0d4f599f63e7e1ab853af0257c80cf5cb662a6e 100644
--- a/groupChat/send_test.go
+++ b/groupChat/send_test.go
@@ -78,9 +78,9 @@ func TestGroup_newCmixMsg_SaltReaderError(t *testing.T) {
 	m, _ := newTestManager(t)
 
 	_, err := newCmixMsg(
-		gs.Group{ID: id.NewIdFromString("test", id.User, t)}, "",
-		[]byte{}, time.Time{}, group.Member{}, strings.NewReader(""),
-		m.getReceptionIdentity().ID, m.getCMix().GetMaxMessageLength())
+		gs.Group{ID: id.NewIdFromString("test", id.User, t)},
+		"", time.Time{}, group.Member{}, strings.NewReader(""),
+		m.getCMix().GetMaxMessageLength(), []byte("internal Message"))
 	if err == nil || !strings.Contains(err.Error(), expectedErr) {
 		t.Errorf("newCmixMsg failed to return the expected error"+
 			"\nexpected: %s\nreceived: %+v", expectedErr, err)
@@ -97,40 +97,16 @@ func TestGroup_newCmixMsg_InternalMsgSizeError(t *testing.T) {
 
 	// Create test parameters
 	testMsg := make([]byte, 1500)
-	mem := group.Member{ID: id.NewIdFromString("memberID", id.User, t)}
 
 	// Create cMix message
 	prng = rand.New(rand.NewSource(42))
-	_, err := newCmixMsg(g, "", testMsg, netTime.Now(), mem, prng,
-		m.getReceptionIdentity().ID, m.getCMix().GetMaxMessageLength())
+	_, _, err := m.newMessages(g, "", testMsg, netTime.Now())
 	if err == nil || !strings.Contains(err.Error(), expectedErr) {
 		t.Errorf("newCmixMsg failed to return the expected error"+
 			"\nexpected: %s\nreceived: %+v", expectedErr, err)
 	}
 }
 
-// Error path: payload size too small to fit publicMsg.
-func Test_newMessageParts_PublicMsgSizeErr(t *testing.T) {
-	expectedErr := strings.SplitN(newPublicMsgErr, "%", 2)[0]
-
-	_, _, err := newMessageParts(publicMinLen - 1)
-	if err == nil || !strings.Contains(err.Error(), expectedErr) {
-		t.Errorf("newMessageParts did not return the expected error."+
-			"\nexpected: %s\nreceived: %+v", expectedErr, err)
-	}
-}
-
-// Error path: payload size too small to fit internalMsg.
-func Test_newMessageParts_InternalMsgSizeErr(t *testing.T) {
-	expectedErr := strings.SplitN(newInternalMsgErr, "%", 2)[0]
-
-	_, _, err := newMessageParts(publicMinLen)
-	if err == nil || !strings.Contains(err.Error(), expectedErr) {
-		t.Errorf("newMessageParts did not return the expected error."+
-			"\nexpected: %s\nreceived: %+v", expectedErr, err)
-	}
-}
-
 // Tests the consistency of newSalt.
 func Test_newSalt_Consistency(t *testing.T) {
 	prng := rand.New(rand.NewSource(42))
diff --git a/stoppable/multi.go b/stoppable/multi.go
index 3ac77c7507fb058af1deac2b098121b16d3569b0..456e376a7b71851f254f786ae8a13d2e086ee60f 100644
--- a/stoppable/multi.go
+++ b/stoppable/multi.go
@@ -121,15 +121,15 @@ func (m *Multi) Close() error {
 	m.once.Do(func() {
 		var wg sync.WaitGroup
 
-		jww.TRACE.Printf("Sending on quit channel to multi stoppable %q.",
-			m.Name())
+		jww.TRACE.Printf("Sending on quit channel to multi stoppable %q with subprocesseses %v.",
+			m.Name(), m.GetRunningProcesses())
 
 		m.mux.Lock()
 		// Attempt to stop each stoppable in its own goroutine
 		for _, stoppable := range m.stoppables {
 			wg.Add(1)
-			go func(stoppable Stoppable) {
-				if stoppable.Close() != nil {
+			go func(s Stoppable) {
+				if s.Close() != nil {
 					atomic.AddUint32(&numErrors, 1)
 				}
 				wg.Done()
diff --git a/stoppable/single.go b/stoppable/single.go
index ea11ab42b586c3c1543509b5f575cda75f0cfca5..5af656b67f92e8e1e6dfb8404a37b73a698e5d3b 100644
--- a/stoppable/single.go
+++ b/stoppable/single.go
@@ -110,6 +110,10 @@ func (s *Single) Close() error {
 
 		// Send on quit channel
 		s.quit <- struct{}{}
+
+		jww.TRACE.Printf("Sent to quit channel for single stoppable %q.",
+			s.Name())
+
 	})
 
 	if err != nil {
diff --git a/ud/lookup.go b/ud/lookup.go
index 569dc5b99578e9487c350028eb5e4649cbf8efc1..867c2900cbf598be9e041f1de1244a9379e0c919 100644
--- a/ud/lookup.go
+++ b/ud/lookup.go
@@ -44,30 +44,6 @@ func Lookup(user udE2e,
 	return lookup(net, rng, uid, grp, udContact, callback, p)
 }
 
-// BatchLookup performs a Lookup operation on a list of user IDs.
-// The lookup performs a callback on each lookup on the returned contact object
-// constructed from the response.
-func BatchLookup(udContact contact.Contact,
-	net udCmix, callback lookupCallback,
-	rng csprng.Source,
-	uids []*id.ID, grp *cyclic.Group,
-	p single.RequestParams) {
-	jww.INFO.Printf("ud.BatchLookup(%s, %s)", uids, p.Timeout)
-
-	for _, uid := range uids {
-		go func(localUid *id.ID) {
-			_, _, err := lookup(net, rng, localUid, grp,
-				udContact, callback, p)
-			if err != nil {
-				jww.WARN.Printf("Failed batch lookup on user %s: %v",
-					localUid, err)
-			}
-		}(uid)
-	}
-
-	return
-}
-
 // lookup is a helper function which sends a lookup request to the user discovery
 // service. It will construct a contact object off of the returned public key.
 // The callback will be called on that contact object.
diff --git a/ud/manager.go b/ud/manager.go
index fd57a886b7046c09cfb33bf6a58adc0d752730e5..9ea87cf84bef02982f8830ba8afa68b8149938dc 100644
--- a/ud/manager.go
+++ b/ud/manager.go
@@ -129,12 +129,8 @@ func NewOrLoad(user udE2e, comms Comms, follower udNetworkStatus,
 //  - user is an interface that adheres to the xxdk.E2e object.
 //  - comms is an interface that adheres to client.Comms object.
 //  - follower is a method off of xxdk.Cmix which returns the network follower's status.
-//  - username is the name of the user as it is registered with UD. This will be what the end user
-//    provides if through the bindings.
 //  - networkValidationSig is a signature provided by the network (i.e. the client registrar). This may
 //    be nil, however UD may return an error in some cases (e.g. in a production level environment).
-//  - email is a fact.Fact (type Email) which has been registered with UD already.
-//  - phone is a fact.Fact (type Phone) which has been registered with UD already.
 //  - cert is the TLS certificate for the UD server this call will connect with.
 //  - contactFile is the data within a marshalled contact.Contact. This represents the
 //    contact file of the server this call will connect with.
@@ -142,9 +138,9 @@ func NewOrLoad(user udE2e, comms Comms, follower udNetworkStatus,
 //
 // Returns
 //  - A Manager object which is registered to the specified UD service.
-func NewManagerFromBackup(user udE2e, comms Comms, follower udNetworkStatus,
-	username, email, phone fact.Fact,
-	cert, contactFile []byte, address string) (*Manager, error) {
+func NewManagerFromBackup(user udE2e, comms Comms,
+	follower udNetworkStatus, cert, contactFile []byte,
+	address string) (*Manager, error) {
 	jww.INFO.Println("ud.NewManagerFromBackup()")
 	if follower() != xxdk.Running {
 		return nil, errors.New(
@@ -165,13 +161,6 @@ func NewManagerFromBackup(user udE2e, comms Comms, follower udNetworkStatus,
 		return nil, err
 	}
 
-	// Put any passed in missing facts into store
-	err = m.store.BackUpMissingFacts(username, email, phone)
-	if err != nil {
-		return nil, errors.WithMessage(err, "Failed to restore UD store "+
-			"from backup")
-	}
-
 	// Set as registered. Since it's from a backup,
 	// the user is already registered
 	if err = setRegistered(m.getKv()); err != nil {