diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 196fafd674d0b6763552739bfd87de96b23ccf3e..0e232306e01c4b38cea29bb638bcdd9f96243e7e 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -76,12 +76,15 @@ tag:
 
 bindings-ios:
   stage: build
+  dependencies: []
   except:
     - tags
   tags:
     - ios
   script:
-    - go get -u golang.org/x/mobile/cmd/gomobile
+    - export PATH=$PATH:$HOME/go/bin
+    - go install golang.org/x/mobile/cmd/gomobile@latest
+    - go get golang.org/x/mobile/cmd/gobind
     - gomobile init
     - gomobile bind -target ios gitlab.com/elixxir/client/bindings
     - ls
@@ -92,14 +95,18 @@ bindings-ios:
 
 bindings-android:
   stage: build
+  dependencies: []
   except:
     - tags
   tags:
     - android
   script:
+    - export PATH=$PATH:/usr/local/go/bin:$HOME/go/bin:/android-sdk/platform-tools/
     - export ANDROID_HOME=/android-sdk
-    - export PATH=$PATH:/android-sdk/platform-tools/:/usr/local/go/bin
-    - go get -u golang.org/x/mobile/cmd/gomobile
+
+    # Build the bindings
+    - go install golang.org/x/mobile/cmd/gomobile@latest
+    - go get golang.org/x/mobile/cmd/gobind
     - gomobile init
     - gomobile bind -target android -androidapi 21 gitlab.com/elixxir/client/bindings
   artifacts:
diff --git a/README.md b/README.md
index e494e57d83614256744b08ea2adf0af371103f4d..0d0938ebbe404dc1cefd87833031017d3d9c6564 100644
--- a/README.md
+++ b/README.md
@@ -27,7 +27,9 @@ The client is open source software released under the simplified BSD License.
 The command line tool is intended for testing xx network functionality and not
 for regular user use. 
 
-Compilation (assuming golang 1.13 or newer):
+These instructions assume that you have [Go 1.17.X installed](https://go.dev/doc/install), and GCC installed for Cgo (such as `build-essential` on Debian or Ubuntu).
+
+Compilation steps:
 
 ```
 git clone https://gitlab.com/elixxir/client.git client
@@ -160,7 +162,10 @@ Available Commands:
 Flags:
       --accept-channel            Accept the channel request for the corresponding recipient ID
       --auth-timeout uint         The number of seconds to wait for an authentication channelto confirm (default 120)
+      --delete-all-requests       Delete the all contact requests, both sent and received.
       --delete-channel            Delete the channel information for the corresponding recipient ID
+      --delete-receive-requests   Delete the all received contact requests.
+      --delete-sent-requests      Delete the all sent contact requests.
       --destfile string           Read this contact file for the destination id
   -d, --destid string             ID to send message to (if below 40, will be precanned. Use '0x' or 'b64:' for hex and base64 representations) (default "0")
       --e2eMaxKeys uint           Max keys used before blocking until a rekey completes (default 800)
diff --git a/api/authenticatedChannel.go b/api/authenticatedChannel.go
index 84fd751178f710482c49c24df38449ef731f0779..93446db2b9bee15a5755f49d62ce42bcedf15794 100644
--- a/api/authenticatedChannel.go
+++ b/api/authenticatedChannel.go
@@ -158,6 +158,14 @@ func (c *Client) MakePrecannedAuthenticatedChannel(precannedID uint) (contact.Co
 		Source: precan.ID[:],
 	}, me)
 
+	// group request
+	c.storage.GetEdge().Add(edge.Preimage{
+		Data:   sessionPartner.GetGroupRequestPreimage(),
+		Type:   preimage.GroupRq,
+		Source: precan.ID[:],
+	}, me)
+
+
 	return precan, err
 }
 
diff --git a/api/client.go b/api/client.go
index c705187068226c528ac1ac24f1dfd28c49f1eb8f..a289fea1e030118346df4011ef5601e49438fe6d 100644
--- a/api/client.go
+++ b/api/client.go
@@ -304,7 +304,7 @@ func Login(storageDir string, password []byte, parameters params.Network) (*Clie
 	}
 
 	// initialize the auth tracker
-	c.auth = auth.NewManager(c.switchboard, c.storage, c.network)
+	c.auth = auth.NewManager(c.switchboard, c.storage, c.network, parameters.ReplayRequests)
 
 	// Add all processes to the followerServices
 	err = c.registerFollower()
@@ -363,7 +363,7 @@ func LoginWithNewBaseNDF_UNSAFE(storageDir string, password []byte,
 	}
 
 	// initialize the auth tracker
-	c.auth = auth.NewManager(c.switchboard, c.storage, c.network)
+	c.auth = auth.NewManager(c.switchboard, c.storage, c.network, parameters.ReplayRequests)
 
 	err = c.registerFollower()
 	if err != nil {
@@ -420,7 +420,7 @@ func LoginWithProtoClient(storageDir string, password []byte, protoClientJSON []
 	}
 
 	// initialize the auth tracker
-	c.auth = auth.NewManager(c.switchboard, c.storage, c.network)
+	c.auth = auth.NewManager(c.switchboard, c.storage, c.network, parameters.ReplayRequests)
 
 	err = c.registerFollower()
 	if err != nil {
@@ -684,6 +684,24 @@ func (c *Client) GetNodeRegistrationStatus() (int, int, error) {
 	return numRegistered, len(nodes) - numStale, nil
 }
 
+// DeleteAllRequests clears all requests from client's auth storage.
+func (c *Client) DeleteAllRequests() error {
+	jww.DEBUG.Printf("Deleting all requests")
+	return c.GetStorage().Auth().DeleteAllRequests()
+}
+
+// DeleteSentRequests clears sent requests from client's auth storage.
+func (c *Client) DeleteSentRequests() error {
+	jww.DEBUG.Printf("Deleting all sent requests")
+	return c.GetStorage().Auth().DeleteSentRequests()
+}
+
+// DeleteReceiveRequests clears receive requests from client's auth storage.
+func (c *Client) DeleteReceiveRequests() error {
+	jww.DEBUG.Printf("Deleting all received requests")
+	return c.GetStorage().Auth().DeleteReceiveRequests()
+}
+
 // DeleteContact is a function which removes a partner from Client's storage
 func (c *Client) DeleteContact(partnerId *id.ID) error {
 	jww.DEBUG.Printf("Deleting contact with ID %s", partnerId)
@@ -696,6 +714,7 @@ func (c *Client) DeleteContact(partnerId *id.ID) error {
 	e2ePreimage := partner.GetE2EPreimage()
 	rekeyPreimage := partner.GetSilentPreimage()
 	fileTransferPreimage := partner.GetFileTransferPreimage()
+	groupRequestPreimage := partner.GetGroupRequestPreimage()
 
 	//delete the partner
 	if err = c.storage.E2e().DeletePartner(partnerId); err != nil {
@@ -729,13 +748,23 @@ func (c *Client) DeleteContact(partnerId *id.ID) error {
 			"from %s on contact deletion: %+v", partnerId, err)
 	}
 
-	if err = c.storage.Auth().Delete(partnerId); err != nil {
-		return err
+	if err = c.storage.GetEdge().Remove(edge.Preimage{
+		Data:   groupRequestPreimage,
+		Type:   preimage.GroupRq,
+		Source: partnerId[:],
+	}, c.storage.GetUser().ReceptionID); err != nil {
+		jww.WARN.Printf("Failed delete the preimage for group request "+
+			"from %s on contact deletion: %+v", partnerId, err)
 	}
 
 	//delete conversations
 	c.storage.Conversations().Delete(partnerId)
 
+	// call delete requests to make sure nothing is lingering.
+	// this is for saftey to ensure the contact can be readded
+	// in the future
+	_ = c.storage.Auth().Delete(partnerId)
+
 	return nil
 }
 
diff --git a/api/event.go b/api/event.go
index 86e75b14ef212033ca906858e265c713022ee709..c5fcf03a90842095990568893051afea5a7e6cee 100644
--- a/api/event.go
+++ b/api/event.go
@@ -94,7 +94,7 @@ func (e *eventManager) reportEventsHandler(stop *stoppable.Single) {
 			stop.ToStopped()
 			return
 		case evt := <-e.eventCh:
-			jww.DEBUG.Printf("Received event: %s", evt)
+			jww.TRACE.Printf("Received event: %s", evt)
 			// NOTE: We could call each in a routine but decided
 			// against it. It's the users responsibility not to let
 			// the event queue explode. The API will report errors
diff --git a/api/results.go b/api/results.go
index 4a31b66b525e60037479ac4042017b21cfece313..04e91e15e7c6e3407ecfab759dd9eb1f598d9885 100644
--- a/api/results.go
+++ b/api/results.go
@@ -89,17 +89,11 @@ func (c *Client) getRoundResults(roundList []id.Round, timeout time.Duration,
 	roundEvents := c.GetRoundEvents()
 	roundsResults := make(map[id.Round]RoundResult)
 	allRoundsSucceeded := true
+	anyRoundTimedOut := false
 	numResults := 0
 
 	oldestRound := networkInstance.GetOldestRoundID()
 
-	// Set a lower timeout so there is room for retries,
-	// while ensuring it does not go too low and cause too many timeouts
-	roundEventTimeout := 5 * time.Second
-	if timeout < roundEventTimeout {
-		roundEventTimeout = timeout
-	}
-
 	// Parse and adjudicate every round
 	for _, rnd := range roundList {
 		// Every round is timed out by default, until proven to have finished
@@ -116,7 +110,7 @@ func (c *Client) getRoundResults(roundList []id.Round, timeout time.Duration,
 			} else {
 				// If in progress, add a channel monitoring its state
 				roundEvents.AddRoundEventChan(rnd, sendResults,
-					roundEventTimeout, states.COMPLETED, states.FAILED)
+					timeout-time.Millisecond, states.COMPLETED, states.FAILED)
 				numResults++
 			}
 		} else {
@@ -129,7 +123,7 @@ func (c *Client) getRoundResults(roundList []id.Round, timeout time.Duration,
 			} else {
 				// Otherwise, monitor its progress
 				roundEvents.AddRoundEventChan(rnd, sendResults,
-					roundEventTimeout, states.COMPLETED, states.FAILED)
+					timeout-time.Millisecond, states.COMPLETED, states.FAILED)
 				numResults++
 			}
 		}
@@ -148,7 +142,7 @@ func (c *Client) getRoundResults(roundList []id.Round, timeout time.Duration,
 
 			// If we know about all rounds, return
 			if numResults == 0 {
-				roundCallback(allRoundsSucceeded, false, roundsResults)
+				roundCallback(allRoundsSucceeded, anyRoundTimedOut, roundsResults)
 				return
 			}
 
@@ -158,29 +152,34 @@ func (c *Client) getRoundResults(roundList []id.Round, timeout time.Duration,
 				roundCallback(false, true, roundsResults)
 				return
 			case roundReport := <-sendResults:
+				numResults--
+
 				// Skip if the round is nil (unknown from historical rounds)
 				// they default to timed out, so correct behavior is preserved
-				if roundReport.RoundInfo == nil {
-					allRoundsSucceeded = false
-					numResults--
-				} else if roundReport.TimedOut {
-					// Generate a message to track the timed out round
-					timeoutRequest := &pb.HistoricalRounds{
-						Rounds: []uint64{roundReport.RoundInfo.ID},
+				roundId := roundReport.RoundInfo.GetRoundId()
+				if roundReport.TimedOut {
+					roundInfo, err := networkInstance.GetRound(roundId)
+					// If we have the round in the buffer
+					if err == nil {
+						// Check if the round is done (completed or failed) or in progress
+						if states.Round(roundInfo.State) == states.COMPLETED {
+							roundsResults[roundId] = Succeeded
+						} else if states.Round(roundInfo.State) == states.FAILED {
+							roundsResults[roundId] = Failed
+							allRoundsSucceeded = false
+						}
+						return
 					}
-					// Request that round's information, feeding back into sendResults
-					jww.DEBUG.Printf("Sending HistoricalRounds retry for Round %d", roundReport.RoundInfo.ID)
-					go c.getHistoricalRounds(timeoutRequest, sendResults, commsInterface)
+					allRoundsSucceeded = false
+					anyRoundTimedOut = true
 				} else {
 					// If available, denote the result
-					roundId := id.Round(roundReport.RoundInfo.ID)
 					if states.Round(roundReport.RoundInfo.State) == states.COMPLETED {
 						roundsResults[roundId] = Succeeded
 					} else {
 						roundsResults[roundId] = Failed
 						allRoundsSucceeded = false
 					}
-					numResults--
 				}
 			}
 		}
@@ -219,9 +218,17 @@ func (c *Client) getHistoricalRounds(msg *pb.HistoricalRounds,
 	}
 
 	// Service historical rounds, sending back to the caller thread
-	for _, ri := range resp.Rounds {
-		sendResults <- ds.EventReturn{
-			RoundInfo: ri,
+	for i, ri := range resp.Rounds {
+		if ri == nil {
+			// Handle unknown by historical rounds
+			sendResults <- ds.EventReturn{
+				RoundInfo: &pb.RoundInfo{ID: msg.Rounds[i]},
+				TimedOut:  true,
+			}
+		} else {
+			sendResults <- ds.EventReturn{
+				RoundInfo: ri,
+			}
 		}
 	}
 }
diff --git a/api/results_test.go b/api/results_test.go
index 68e21e9aca1a317bada3ea1497c90166b1b4289c..c5245dd9a193d2f6c8bdb84e3f3862f1f884b1c2 100644
--- a/api/results_test.go
+++ b/api/results_test.go
@@ -40,7 +40,7 @@ func TestClient_GetRoundResults(t *testing.T) {
 	// Create a new copy of the test client for this test
 	client, err := newTestingClient(t)
 	if err != nil {
-		t.Fatalf("Failed in setup: %v", err)
+		t.Fatalf("Failed in setup: %+v", err)
 	}
 
 	// Construct the round call back function signature
@@ -171,7 +171,7 @@ func TestClient_GetRoundResults_HistoricalRounds(t *testing.T) {
 			t.Errorf("Failed to sign round in set up: %v", err)
 		}
 
-		err = client.network.GetInstance().RoundUpdate(ri)
+		_, err = client.network.GetInstance().RoundUpdate(ri)
 		if err != nil {
 			t.Errorf("Failed to upsert round in set up: %v", err)
 		}
diff --git a/api/utils.go b/api/utils.go
index 96eb44f8834cb5b07cf020556aeb637caf2dd00a..2486d4d66143bb7fe6b660a9d23da26e70828652 100644
--- a/api/utils.go
+++ b/api/utils.go
@@ -20,7 +20,9 @@ const (
 	// Maximum input image size (in bytes)
 	maxSize int64 = 12000000
 	// Desired number of pixels in output image
-	desiredSize = 307200
+	desiredSize = 640*480
+	// Desired number of pixels in output image for preview
+	desiredPreviewSize = 32*24
 )
 
 // CompressJpeg takes a JPEG image in byte format
@@ -73,3 +75,55 @@ func CompressJpeg(imgBytes []byte) ([]byte, error) {
 	// Return the compressed image in byte form
 	return newImgBuf.Bytes(), nil
 }
+
+
+// CompressJpeg takes a JPEG image in byte format
+// and compresses it based on desired output size
+func CompressJpegForPreview(imgBytes []byte) ([]byte, error) {
+	// Convert bytes to a reader
+	imgBuf := bytes.NewReader(imgBytes)
+
+	// Ensure the size of the image is under the limit
+	if imgSize := imgBuf.Size(); imgSize > maxSize {
+		return nil, errors.Errorf("Image is too large: %d/%d", imgSize, maxSize)
+	}
+
+	// Decode the image information
+	imgInfo, err := jpeg.DecodeConfig(imgBuf)
+	if err != nil {
+		return nil, errors.Errorf("Unable to decode image config: %+v", err)
+	}
+
+	// If the dimensions of the image are below desiredSize, no compression is required
+	if imgInfo.Width*imgInfo.Height < desiredSize {
+		return imgBytes, nil
+	}
+
+	// Reset the buffer to the beginning to begin decoding the image
+	_, err = imgBuf.Seek(0, 0)
+	if err != nil {
+		return nil, errors.Errorf("Unable to reset image buffer: %+v", err)
+	}
+
+	// Decode image into image.Image object
+	img, err := jpeg.Decode(imgBuf)
+	if err != nil {
+		return nil, errors.Errorf("Unable to decode image: %+v", err)
+	}
+
+	// Determine the new width of the image based on desiredSize
+	newWidth := uint(math.Sqrt(float64(desiredSize) * (float64(imgInfo.Width) / float64(imgInfo.Height))))
+
+	// Resize the image based on newWidth while preserving aspect ratio
+	newImg := resize.Resize(newWidth, 0, img, resize.Bicubic)
+
+	// Encode the new image to a buffer
+	newImgBuf := new(bytes.Buffer)
+	err = jpeg.Encode(newImgBuf, newImg, nil)
+	if err != nil {
+		return nil, errors.Errorf("Unable to encode image: %+v", err)
+	}
+
+	// Return the compressed image in byte form
+	return newImgBuf.Bytes(), nil
+}
diff --git a/api/utils_test.go b/api/utils_test.go
index 973f15747b12741f0f83724a196a30b89800be1d..f1fea1358c4bd46a33c3a8f66d7cf56568485d67 100644
--- a/api/utils_test.go
+++ b/api/utils_test.go
@@ -68,7 +68,10 @@ func newTestingClient(face interface{}) (*Client, error) {
 
 	p := gateway.DefaultPoolParams()
 	p.MaxPoolSize = 1
-	sender, _ := gateway.NewSender(p, c.rng, def, commsManager, c.storage, nil)
+	sender, err := gateway.NewSender(p, c.rng, def, commsManager, c.storage, nil)
+	if err != nil {
+		return nil, err
+	}
 	c.network = &testNetworkManagerGeneric{instance: thisInstance, sender: sender}
 
 	return c, nil
@@ -85,6 +88,8 @@ func getNDF(face interface{}) *ndf.NetworkDefinition {
 
 	cert, _ := utils.ReadFile(testkeys.GetNodeCertPath())
 	nodeID := id.NewIdFromBytes([]byte("gateway"), face)
+	gwId := nodeID.DeepCopy()
+	gwId.SetType(id.Gateway)
 	return &ndf.NetworkDefinition{
 		Registration: ndf.Registration{
 			TlsCertificate: string(cert),
@@ -100,7 +105,7 @@ func getNDF(face interface{}) *ndf.NetworkDefinition {
 		},
 		Gateways: []ndf.Gateway{
 			{
-				ID:             nodeID.Bytes(),
+				ID:             gwId.Bytes(),
 				Address:        "",
 				TlsCertificate: string(cert),
 			},
diff --git a/api/version_vars.go b/api/version_vars.go
index 8a44f54952836daac733cc3e151032f5e6b042cb..9ef72f196cc77927fd45ecad2a70d4a28d5ce7ae 100644
--- a/api/version_vars.go
+++ b/api/version_vars.go
@@ -1,43 +1,62 @@
 // Code generated by go generate; DO NOT EDIT.
 // This file was generated by robots at
-// 2022-01-04 12:45:01.875155 -0600 CST m=+0.041061278
+// 2022-02-15 12:19:19.667352 -0600 CST m=+0.033427370
 package api
 
-const GITVERSION = `1144194c Merge branch 'dev' into 'release'`
+const GITVERSION = `d8832766 made splitSends default to false`
 const SEMVER = "4.0.0"
 const DEPENDENCIES = `module gitlab.com/elixxir/client
 
-go 1.13
+go 1.17
 
 require (
 	github.com/cloudflare/circl v1.0.1-0.20211008185751-59b49bc148ce
 	github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3
 	github.com/golang/protobuf v1.5.2
-	github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00 // indirect
-	github.com/magiconair/properties v1.8.4 // indirect
-	github.com/mitchellh/mapstructure v1.4.0 // indirect
 	github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
-	github.com/pelletier/go-toml v1.8.1 // indirect
 	github.com/pkg/errors v0.9.1
-	github.com/smartystreets/assertions v1.0.1 // indirect
-	github.com/spf13/afero v1.5.1 // indirect
-	github.com/spf13/cast v1.3.1 // indirect
 	github.com/spf13/cobra v1.1.1
 	github.com/spf13/jwalterweatherman v1.1.0
 	github.com/spf13/viper v1.7.1
 	gitlab.com/elixxir/bloomfilter v0.0.0-20200930191214-10e9ac31b228
-	gitlab.com/elixxir/comms v0.0.4-0.20220104174855-044783c5c1e6
-	gitlab.com/elixxir/crypto v0.0.7-0.20220104174238-dbd761b30553
-	gitlab.com/elixxir/ekv v0.1.5
-	gitlab.com/elixxir/primitives v0.0.3-0.20220104173924-275cb9d7834f
-	gitlab.com/xx_network/comms v0.0.4-0.20211227194445-c099754b3cda
-	gitlab.com/xx_network/crypto v0.0.5-0.20211227194420-f311e8920467
-	gitlab.com/xx_network/primitives v0.0.4-0.20211222205802-03e9d7d835b0
-	golang.org/x/crypto v0.0.0-20210921155107-089bfa567519
-	golang.org/x/net v0.0.0-20210525063256-abc453219eb5
-	google.golang.org/genproto v0.0.0-20210105202744-fe13368bc0e1 // indirect
+	gitlab.com/elixxir/comms v0.0.4-0.20220214214811-4a1bd320aa45
+	gitlab.com/elixxir/crypto v0.0.7-0.20220211185439-4a6d9f41f8ab
+	gitlab.com/elixxir/ekv v0.1.6
+	gitlab.com/elixxir/primitives v0.0.3-0.20220211185255-f9bc3df21e1d
+	gitlab.com/xx_network/comms v0.0.4-0.20220211184526-00dc9cfe8e2e
+	gitlab.com/xx_network/crypto v0.0.5-0.20220211184244-5803ecaafd59
+	gitlab.com/xx_network/primitives v0.0.4-0.20220211183913-d6f5fd114a2a
+	golang.org/x/crypto v0.0.0-20220128200615-198e4374d7ed
+	golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2
 	google.golang.org/grpc v1.42.0
 	google.golang.org/protobuf v1.27.1
+)
+
+require (
+	github.com/badoux/checkmail v1.2.1 // indirect
+	github.com/elliotchance/orderedmap v1.4.0 // indirect
+	github.com/fsnotify/fsnotify v1.4.9 // indirect
+	github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00 // indirect
+	github.com/hashicorp/hcl v1.0.0 // indirect
+	github.com/inconshreveable/mousetrap v1.0.0 // indirect
+	github.com/magiconair/properties v1.8.4 // indirect
+	github.com/mitchellh/go-homedir v1.1.0 // indirect
+	github.com/mitchellh/mapstructure v1.4.0 // indirect
+	github.com/pelletier/go-toml v1.8.1 // indirect
+	github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect
+	github.com/smartystreets/assertions v1.0.1 // indirect
+	github.com/spf13/afero v1.5.1 // indirect
+	github.com/spf13/cast v1.3.1 // indirect
+	github.com/spf13/pflag v1.0.5 // indirect
+	github.com/subosito/gotenv v1.2.0 // indirect
+	github.com/ttacon/builder v0.0.0-20170518171403-c099f663e1c2 // indirect
+	github.com/ttacon/libphonenumber v1.2.1 // indirect
+	github.com/tyler-smith/go-bip39 v1.1.0 // indirect
+	github.com/zeebo/blake3 v0.1.1 // indirect
+	gitlab.com/xx_network/ring v0.0.3-0.20210527191221-ce3f170aabd5 // indirect
+	golang.org/x/sys v0.0.0-20210902050250-f475640dd07b // indirect
+	golang.org/x/text v0.3.6 // indirect
+	google.golang.org/genproto v0.0.0-20210105202744-fe13368bc0e1 // indirect
 	gopkg.in/ini.v1 v1.62.0 // indirect
 	gopkg.in/yaml.v2 v2.4.0 // indirect
 )
diff --git a/auth/callback.go b/auth/callback.go
index 3151fdb43de00b988d3356ea1833c9979c83cfba..7ae57d144bfb2a3c6e0b7b8cd02cb610d4694553 100644
--- a/auth/callback.go
+++ b/auth/callback.go
@@ -9,6 +9,8 @@ package auth
 
 import (
 	"fmt"
+	"strings"
+
 	"github.com/cloudflare/circl/dh/sidh"
 	"github.com/pkg/errors"
 	jww "github.com/spf13/jwalterweatherman"
@@ -26,7 +28,6 @@ import (
 	"gitlab.com/elixxir/primitives/fact"
 	"gitlab.com/elixxir/primitives/format"
 	"gitlab.com/xx_network/crypto/csprng"
-	"strings"
 )
 
 func (m *Manager) StartProcesses() (stoppable.Stoppable, error) {
@@ -165,7 +166,7 @@ func (m *Manager) handleRequest(cmixMsg format.Message,
 		return
 	} else {
 		//check if the relationship already exists,
-		rType, sr2, _, err := m.storage.Auth().GetRequest(partnerID)
+		rType, _, c, err := m.storage.Auth().GetRequest(partnerID)
 		if err != nil && !strings.Contains(err.Error(), auth.NoRequest) {
 			// if another error is received, print it and exit
 			em := fmt.Sprintf("Received new Auth request for %s, "+
@@ -183,6 +184,15 @@ func (m *Manager) handleRequest(cmixMsg format.Message,
 					"is a duplicate", partnerID)
 				jww.WARN.Print(em)
 				events.Report(5, "Auth", "DuplicateRequest", em)
+				// if the caller of the API wants requests replayed,
+				// replay the duplicate request
+				if m.replayRequests {
+					cbList := m.requestCallbacks.Get(c.ID)
+					for _, cb := range cbList {
+						rcb := cb.(interfaces.RequestCallback)
+						go rcb(c)
+					}
+				}
 				return
 			// if we sent a request, then automatically confirm
 			// then exit, nothing else needed
@@ -201,13 +211,9 @@ func (m *Manager) handleRequest(cmixMsg format.Message,
 				}
 
 				// Check if I need to resend by comparing the
-				// SIDH Keys
-				mySIDH := sr2.GetMySIDHPubKey()
-				theirSIDH := partnerSIDHPubKey
-				myBytes := make([]byte, mySIDH.Size())
-				theirBytes := make([]byte, theirSIDH.Size())
-				mySIDH.Export(myBytes)
-				theirSIDH.Export(theirBytes)
+				// IDs
+				myBytes := m.storage.GetUser().ReceptionID.Bytes()
+				theirBytes := partnerID.Bytes()
 				for i := 0; i < len(myBytes); i++ {
 					if myBytes[i] > theirBytes[i] {
 						// OK, this side is dropping
@@ -224,9 +230,29 @@ func (m *Manager) handleRequest(cmixMsg format.Message,
 				}
 
 				// If I do, delete my request on disk
-				_, _, partnerContact, _ := m.storage.Auth().GetRequest(partnerID)
 				m.storage.Auth().Delete(partnerID)
 
+				//process the inner payload
+				facts, _, err := fact.UnstringifyFactList(
+					string(requestFmt.msgPayload))
+				if err != nil {
+					em := fmt.Sprintf("failed to parse facts and message "+
+						"from Auth Request: %s", err)
+					jww.WARN.Print(em)
+					events.Report(10, "Auth", "RequestError", em)
+					return
+				}
+
+				// create the contact, note that we use the data
+				// sent in the request and not any data we had
+				// already
+				partnerContact := contact.Contact{
+					ID:             partnerID,
+					DhPubKey:       partnerPubKey,
+					OwnershipProof: copySlice(ownership),
+					Facts:          facts,
+				}
+
 				// add a confirmation to disk
 				if err = m.storage.Auth().AddReceived(partnerContact,
 					partnerSIDHPubKey); err != nil {
@@ -250,6 +276,12 @@ func (m *Manager) handleRequest(cmixMsg format.Message,
 
 				jww.INFO.Printf("ConfirmRequestAuth to %s on round %d",
 					partnerID, rndNum)
+				c := partnerContact
+				cbList := m.confirmCallbacks.Get(c.ID)
+				for _, cb := range cbList {
+					ccb := cb.(interfaces.ConfirmCallback)
+					go ccb(c)
+				}
 				return
 			}
 		}
@@ -290,7 +322,7 @@ func (m *Manager) handleRequest(cmixMsg format.Message,
 	cbList := m.requestCallbacks.Get(c.ID)
 	for _, cb := range cbList {
 		rcb := cb.(interfaces.RequestCallback)
-		go rcb(c, "")
+		go rcb(c)
 	}
 	return
 }
@@ -434,6 +466,13 @@ func (m *Manager) doConfirm(sr *auth.SentRequest, grp *cyclic.Group,
 		Source: sr.GetPartner()[:],
 	}, me)
 
+	//group Request
+	m.storage.GetEdge().Add(edge.Preimage{
+		Data:   sessionPartner.GetGroupRequestPreimage(),
+		Type:   preimage.GroupRq,
+		Source: sr.GetPartner()[:],
+	}, me)
+
 	// delete the in progress negotiation
 	// this undoes the request lock
 	if err := m.storage.Auth().Delete(sr.GetPartner()); err != nil {
diff --git a/auth/confirm.go b/auth/confirm.go
index 2a8169d21eb5be5f80ccbe5519aeb6a5a1e0d783..f0f7ba84975ae9f8e261c68014fabe8f347bc2d7 100644
--- a/auth/confirm.go
+++ b/auth/confirm.go
@@ -147,6 +147,13 @@ func ConfirmRequestAuth(partner contact.Contact, rng io.Reader,
 		Source: partner.ID[:],
 	}, me)
 
+	//group Request
+	storage.GetEdge().Add(edge.Preimage{
+		Data:   sessionPartner.GetGroupRequestPreimage(),
+		Type:   preimage.GroupRq,
+		Source: partner.ID[:],
+	}, me)
+
 	// delete the in progress negotiation
 	// this unlocks the request lock
 	//fixme - do these deletes at a later date
@@ -161,6 +168,7 @@ func ConfirmRequestAuth(partner contact.Contact, rng io.Reader,
 
 	param := params.GetDefaultCMIX()
 	param.IdentityPreimage = preimg
+	param.DebugTag = "auth.Confirm"
 	/*send message*/
 	round, _, err := net.SendCMIX(cmixMsg, partner.ID, param)
 	if err != nil {
diff --git a/auth/manager.go b/auth/manager.go
index b5803d69feb7ae67073e351b1c7fd24c3ac5b597..20b341c3891212edf308ec9c7afcf43a5f1588f5 100644
--- a/auth/manager.go
+++ b/auth/manager.go
@@ -23,16 +23,19 @@ type Manager struct {
 
 	storage *storage.Session
 	net     interfaces.NetworkManager
+
+	replayRequests bool
 }
 
 func NewManager(sw interfaces.Switchboard, storage *storage.Session,
-	net interfaces.NetworkManager) *Manager {
+	net interfaces.NetworkManager, replayRequests bool) *Manager {
 	m := &Manager{
 		requestCallbacks: newCallbackMap(),
 		confirmCallbacks: newCallbackMap(),
 		rawMessages:      make(chan message.Receive, 1000),
 		storage:          storage,
 		net:              net,
+		replayRequests:   replayRequests,
 	}
 
 	sw.RegisterChannel("Auth", switchboard.AnyUser(), message.Raw, m.rawMessages)
@@ -89,3 +92,17 @@ func (m *Manager) AddSpecificConfirmCallback(id *id.ID, cb interfaces.ConfirmCal
 func (m *Manager) RemoveSpecificConfirmCallback(id *id.ID) {
 	m.confirmCallbacks.RemoveSpecific(id)
 }
+
+// ReplayRequests will iterate through all pending contact requests and resend them
+// to the desired contact.
+func (m *Manager) ReplayRequests() {
+	cList := m.storage.Auth().GetAllReceived()
+	for i := range cList {
+		c := cList[i]
+		cbList := m.requestCallbacks.Get(c.ID)
+		for _, cb := range cbList {
+			rcb := cb.(interfaces.RequestCallback)
+			go rcb(c)
+		}
+	}
+}
diff --git a/auth/manager_test.go b/auth/manager_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..7332a2db6d45ba7e2c0d9ddbfb18bc02d06ab286
--- /dev/null
+++ b/auth/manager_test.go
@@ -0,0 +1,105 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright © 2020 xx network SEZC                                          //
+//                                                                           //
+// Use of this source code is governed by a license that can be found in the //
+// LICENSE file                                                              //
+///////////////////////////////////////////////////////////////////////////////
+
+package auth
+
+import (
+	"github.com/cloudflare/circl/dh/sidh"
+	"gitlab.com/elixxir/client/interfaces"
+	"gitlab.com/elixxir/client/storage"
+	"gitlab.com/elixxir/client/storage/auth"
+	util "gitlab.com/elixxir/client/storage/utility"
+	"gitlab.com/elixxir/client/storage/versioned"
+	"gitlab.com/elixxir/crypto/contact"
+	"gitlab.com/elixxir/crypto/cyclic"
+	"gitlab.com/elixxir/ekv"
+	"gitlab.com/xx_network/crypto/csprng"
+	"gitlab.com/xx_network/crypto/large"
+	"gitlab.com/xx_network/primitives/id"
+	"io"
+	"math/rand"
+	"testing"
+	"time"
+)
+
+func TestManager_ReplayRequests(t *testing.T) {
+	s := storage.InitTestingSession(t)
+	numReceived := 10
+
+	// Construct barebones manager
+	m := Manager{
+		requestCallbacks: newCallbackMap(),
+		storage:          s,
+		replayRequests:   true,
+	}
+
+	ch := make(chan struct{}, numReceived)
+
+	// Add multiple received contact requests
+	for i := 0; i < numReceived; i++ {
+		c := contact.Contact{ID: id.NewIdFromUInt(rand.Uint64(), id.User, t)}
+		rng := csprng.NewSystemRNG()
+		_, sidhPubKey := genSidhAKeys(rng)
+
+		if err := m.storage.Auth().AddReceived(c, sidhPubKey); err != nil {
+			t.Fatalf("AddReceived() returned an error: %+v", err)
+		}
+
+		m.requestCallbacks.AddSpecific(c.ID, interfaces.RequestCallback(func(c contact.Contact) {
+			ch <- struct{}{}
+		}))
+
+	}
+
+	m.ReplayRequests()
+
+	timeout := time.NewTimer(1 * time.Second)
+	numChannelReceived := 0
+loop:
+	for {
+		select {
+		case <-ch:
+			numChannelReceived++
+		case <-timeout.C:
+			break loop
+		}
+	}
+
+	if numReceived != numChannelReceived {
+		t.Errorf("Unexpected number of callbacks called"+
+			"\nExpected: %d"+
+			"\nReceived: %d", numChannelReceived, numReceived)
+	}
+}
+
+func makeTestStore(t *testing.T) (*auth.Store, *versioned.KV, []*cyclic.Int) {
+	kv := versioned.NewKV(make(ekv.Memstore))
+	grp := cyclic.NewGroup(large.NewInt(173), large.NewInt(0))
+	privKeys := make([]*cyclic.Int, 10)
+	for i := range privKeys {
+		privKeys[i] = grp.NewInt(rand.Int63n(170) + 1)
+	}
+
+	store, err := auth.NewStore(kv, grp, privKeys)
+	if err != nil {
+		t.Fatalf("Failed to create new Store: %+v", err)
+	}
+
+	return store, kv, privKeys
+}
+
+func genSidhAKeys(rng io.Reader) (*sidh.PrivateKey, *sidh.PublicKey) {
+	sidHPrivKeyA := util.NewSIDHPrivateKey(sidh.KeyVariantSidhA)
+	sidHPubKeyA := util.NewSIDHPublicKey(sidh.KeyVariantSidhA)
+
+	if err := sidHPrivKeyA.Generate(rng); err != nil {
+		panic("failure to generate SidH A private key")
+	}
+	sidHPrivKeyA.GeneratePublicKey(sidHPubKeyA)
+
+	return sidHPrivKeyA, sidHPubKeyA
+}
diff --git a/auth/request.go b/auth/request.go
index 1f63d7e157caa9cb360d66f0fd3ec82c51ab7d69..7148cf5a4b0dc1d171da6eb62178cd08c60620b7 100644
--- a/auth/request.go
+++ b/auth/request.go
@@ -173,6 +173,7 @@ func RequestAuth(partner, me contact.Contact, rng io.Reader,
 	/*send message*/
 	p := params.GetDefaultCMIX()
 	p.IdentityPreimage = preimage.GenerateRequest(partner.ID)
+	p.DebugTag = "auth.Request"
 	round, _, err := net.SendCMIX(cmixMsg, partner.ID, p)
 	if err != nil {
 		// if the send fails just set it to failed, it will
diff --git a/bindings/authenticatedChannels.go b/bindings/authenticatedChannels.go
index 67d2a885098b34671d75014189b682ed75a6a32c..36888a604177ae36b37d80f97ad61b01700cb51a 100644
--- a/bindings/authenticatedChannels.go
+++ b/bindings/authenticatedChannels.go
@@ -63,7 +63,7 @@ func (c *Client) RequestAuthenticatedChannel(recipientMarshaled,
 func (c *Client) RegisterAuthCallbacks(request AuthRequestCallback,
 	confirm AuthConfirmCallback) {
 
-	requestFunc := func(requestor contact.Contact, message string) {
+	requestFunc := func(requestor contact.Contact) {
 		requestorBind := &Contact{c: &requestor}
 		request.Callback(requestorBind)
 	}
@@ -136,3 +136,8 @@ func (c *Client) GetRelationshipFingerprint(partnerID []byte) (string, error) {
 
 	return c.api.GetRelationshipFingerprint(partner)
 }
+
+// ReplayRequests Resends all pending requests over the normal callbacks
+func (c *Client) ReplayRequests() () {
+	c.api.GetAuthRegistrar().ReplayRequests()
+}
\ No newline at end of file
diff --git a/bindings/client.go b/bindings/client.go
index 47e4b34a34943c09a740eaec61ef9c6f26965538..1996507cbc0a005ae20006763f6596fe1ca25834 100644
--- a/bindings/client.go
+++ b/bindings/client.go
@@ -265,6 +265,7 @@ func (c *Client) WaitForNetwork(timeoutMS int) bool {
 func (c *Client) NetworkFollowerStatus() int {
 	return int(c.api.NetworkFollowerStatus())
 }
+
 // HasRunningProcessies checks if any background threads are running.
 // returns true if none are running. This is meant to be
 // used when NetworkFollowerStatus() returns Stopping.
@@ -449,6 +450,21 @@ func (c *Client) GetNodeRegistrationStatus() (*NodeRegistrationsStatus, error) {
 	return &NodeRegistrationsStatus{registered, total}, err
 }
 
+// DeleteAllRequests clears all requests from Client's auth storage.
+func (c *Client) DeleteAllRequests() error {
+	return c.api.DeleteAllRequests()
+}
+
+// DeleteSentRequests clears sent requests from Client's auth storage.
+func (c *Client) DeleteSentRequests() error {
+	return c.api.DeleteSentRequests()
+}
+
+// DeleteReceiveRequests clears receive requests from Client's auth storage.
+func (c *Client) DeleteReceiveRequests() error {
+	return c.api.DeleteReceiveRequests()
+}
+
 // DeleteContact is a function which removes a contact from Client's storage
 func (c *Client) DeleteContact(b []byte) error {
 	contactObj, err := UnmarshalContact(b)
diff --git a/bindings/group.go b/bindings/group.go
index a1f32b34e9c613bbb4357f6c2705ee930a7d3c01..aee0b0f7cc400bf55ed41673fc7c30773ea791fd 100644
--- a/bindings/group.go
+++ b/bindings/group.go
@@ -124,8 +124,8 @@ func (g *GroupChat) Send(groupIdBytes, message []byte) (*GroupSendReport, error)
 		return nil, errors.Errorf("Failed to unmarshal group ID: %+v", err)
 	}
 
-	round, timestamp, err := g.m.Send(groupID, message)
-	return &GroupSendReport{round, timestamp}, err
+	round, timestamp, msgID, err := g.m.Send(groupID, message)
+	return &GroupSendReport{round, timestamp, msgID}, err
 }
 
 // GetGroups returns an IdList containing a list of group IDs that the user is a
@@ -239,6 +239,7 @@ func (ngr *NewGroupReport) Unmarshal(b []byte) error {
 type GroupSendReport struct {
 	roundID   id.Round
 	timestamp time.Time
+	messageID group.MessageID
 }
 
 // GetRoundID returns the ID of the round that the send occurred on.
@@ -257,6 +258,16 @@ func (gsr *GroupSendReport) GetTimestampMS() int64 {
 	return int64(ts)
 }
 
+// GetMessageID returns the ID of the round that the send occurred on.
+func (gsr *GroupSendReport) GetMessageID() []byte {
+	return gsr.messageID[:]
+}
+
+// GetRoundURL returns the URL of the round that the send occurred on.
+func (gsr *GroupSendReport) GetRoundURL() string {
+	return getRoundURL(gsr.roundID)
+}
+
 ////
 // Group Structure
 ////
@@ -407,6 +418,11 @@ func (gmr *GroupMessageReceive) GetRoundID() int64 {
 	return int64(gmr.RoundID)
 }
 
+// GetRoundURL returns the ID of the round the message was sent on.
+func (gmr *GroupMessageReceive) GetRoundURL() string {
+	return getRoundURL(gmr.RoundID)
+}
+
 // GetRoundTimestampNano returns the timestamp, in nanoseconds, of the round the
 // message was sent on.
 func (gmr *GroupMessageReceive) GetRoundTimestampNano() int64 {
diff --git a/bindings/message.go b/bindings/message.go
index 680d9caf778d8d8ceb700b0a3d3ad2b8b6b20d4b..3eee3b343627338fb907ba81b93fe2e73f8f4731 100644
--- a/bindings/message.go
+++ b/bindings/message.go
@@ -65,3 +65,8 @@ func (m *Message) GetRoundTimestampNano() int64 {
 func (m *Message) GetRoundId() int64 {
 	return int64(m.r.RoundId)
 }
+
+// GetRoundURL returns the message's round URL
+func (m *Message) GetRoundURL() string {
+	return getRoundURL(m.r.RoundId)
+}
diff --git a/bindings/notifications.go b/bindings/notifications.go
index 71db73728f59e1fcd4d382fe79de310eddf860d4..7f393cf7271c253cf5e07771f9f570d89b69a3de 100644
--- a/bindings/notifications.go
+++ b/bindings/notifications.go
@@ -59,6 +59,7 @@ func (mnfmr *ManyNotificationForMeReport) Len() int {
 //	"e2e"		sender user ID		reception of an E2E message
 //	"group"		group ID			reception of a group chat message
 //  "endFT"     sender user ID		Last message sent confirming end of file transfer
+//  "groupRQ"   sender user ID		Request from sender to join a group chat
 func NotificationsForMe(notifCSV, preimages string) (*ManyNotificationForMeReport, error) {
 	//handle deserialization of preimages
 	var preimageList []edge.Preimage
diff --git a/bindings/send.go b/bindings/send.go
index be9366974b339fdd06539394cda02e34d94b785b..3b85a34b0f05fe6f119a7d45ffe4f195604fb8e3 100644
--- a/bindings/send.go
+++ b/bindings/send.go
@@ -199,6 +199,13 @@ func (sr *SendReport) GetMessageID() []byte {
 	return sr.mid[:]
 }
 
+func (sr *SendReport) GetRoundURL() string {
+	if sr.rl != nil && sr.rl.Len() > 0 {
+		return getRoundURL(sr.rl.list[0])
+	}
+	return dashboardBaseURL
+}
+
 // GetTimestampMS returns the message's timestamp in milliseconds
 func (sr *SendReport) GetTimestampMS() int64 {
 	ts := sr.ts.UnixNano()
diff --git a/bindings/ud.go b/bindings/ud.go
index 727f4118333c2813c8acd227000deffab78ad6e3..f9a12eb39972f94743a833e9ec4e081139b5ac40 100644
--- a/bindings/ud.go
+++ b/bindings/ud.go
@@ -17,14 +17,13 @@ import (
 	"time"
 )
 
-//This package wraps the user discovery system
+// This package wraps the user discovery system
 
-// User Discovery object
 type UserDiscovery struct {
 	ud *ud.Manager
 }
 
-// Returns a new user discovery object. Only call this once. It must be called
+// NewUserDiscovery returns a new user discovery object. Only call this once. It must be called
 // after StartNetworkFollower is called and will fail if the network has never
 // been contacted.
 // This function technically has a memory leak because it causes both sides of
@@ -55,7 +54,7 @@ func (ud *UserDiscovery) Register(username string) error {
 	return ud.ud.Register(username)
 }
 
-// Adds a fact for the user to user discovery. Will only succeed if the
+// AddFact adds a fact for the user to user discovery. Will only succeed if the
 // user is already registered and the system does not have the fact currently
 // registered for any user.
 // Will fail if the fact string is not well formed.
@@ -73,14 +72,14 @@ func (ud *UserDiscovery) AddFact(fStr string) (string, error) {
 	return ud.ud.SendRegisterFact(f)
 }
 
-// Confirms a fact first registered via AddFact. The confirmation ID comes from
+// ConfirmFact confirms a fact first registered via AddFact. The confirmation ID comes from
 // AddFact while the code will come over the associated communications system
 func (ud *UserDiscovery) ConfirmFact(confirmationID, code string) error {
 	return ud.ud.SendConfirmFact(confirmationID, code)
 }
 
-// Removes a previously confirmed fact.  Will fail if the passed fact string is
-// not well formed or if the fact is not associated with this client.
+// RemoveFact removes a previously confirmed fact.  Will fail if the passed fact string is
+// not well-formed or if the fact is not associated with this client.
 // Users cannot remove username facts and must instead remove the user.
 func (ud *UserDiscovery) RemoveFact(fStr string) error {
 	f, err := fact.UnstringifyFact(fStr)
@@ -108,7 +107,7 @@ type SearchCallback interface {
 	Callback(contacts *ContactList, error string)
 }
 
-// Searches for the passed Facts.  The factList is the stringification of a
+// Search for the passed Facts.  The factList is the stringification of a
 // fact list object, look at /bindings/list.go for more on that object.
 // This will reject if that object is malformed. The SearchCallback will return
 // a list of contacts, each having the facts it hit against.
@@ -141,7 +140,7 @@ type SingleSearchCallback interface {
 	Callback(contact *Contact, error string)
 }
 
-// Searches for the passed Facts.  The fact is the stringification of a
+// SearchSingle searches for the passed Facts.  The fact is the stringification of a
 // fact object, look at /bindings/contact.go for more on that object.
 // This will reject if that object is malformed. The SearchCallback will return
 // a list of contacts, each having the facts it hit against.
@@ -173,7 +172,7 @@ type LookupCallback interface {
 	Callback(contact *Contact, error string)
 }
 
-// Looks for the contact object associated with the given userID.  The
+// Lookup the contact object associated with the given userID.  The
 // id is the byte representation of an id.
 // This will reject if that id is malformed. The LookupCallback will return
 // the associated contact if it exists.
@@ -202,7 +201,7 @@ func (ud UserDiscovery) Lookup(idBytes []byte, callback LookupCallback,
 
 }
 
-// MultiLookupCallback returns the result of many paralel lookups
+// MultiLookupCallback returns the result of many parallel lookups
 type MultiLookupCallback interface {
 	Callback(Succeeded *ContactList, failed *IdList, errors string)
 }
@@ -293,3 +292,18 @@ func (ud UserDiscovery) MultiLookup(ids *IdList, callback MultiLookupCallback,
 
 	return nil
 }
+
+// SetAlternativeUserDiscovery sets the alternativeUd object within manager.
+// Once set, any user discovery operation will go through the alternative
+// user discovery service.
+// To undo this operation, use UnsetAlternativeUserDiscovery.
+// The contact file is the already read in bytes, not the file path for the contact file.
+func (ud *UserDiscovery) SetAlternativeUserDiscovery(address, cert, contactFile []byte) error {
+	return ud.ud.SetAlternativeUserDiscovery(cert, address, contactFile)
+}
+
+// UnsetAlternativeUserDiscovery clears out the information from
+// the Manager object.
+func (ud *UserDiscovery) UnsetAlternativeUserDiscovery() error {
+	return ud.ud.UnsetAlternativeUserDiscovery()
+}
diff --git a/bindings/url.go b/bindings/url.go
new file mode 100644
index 0000000000000000000000000000000000000000..0322b6c3dd05318bbce6a1d9dee8ed9caeebf7da
--- /dev/null
+++ b/bindings/url.go
@@ -0,0 +1,20 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright © 2020 xx network SEZC                                          //
+//                                                                           //
+// Use of this source code is governed by a license that can be found in the //
+// LICENSE file                                                              //
+///////////////////////////////////////////////////////////////////////////////
+
+package bindings
+
+
+import (
+	"gitlab.com/xx_network/primitives/id"
+	"fmt"
+)
+
+const dashboardBaseURL = "https://dashboard.xx.network"
+
+func getRoundURL(round id.Round) string {
+	return fmt.Sprintf("%s/rounds/%d?xxmessenger=true", dashboardBaseURL, round)
+}
diff --git a/bindings/utils.go b/bindings/utils.go
index b7249f6b910005962f27cbeb34679698e8c69c63..93afd4aab7bbf66970f9041551c3c32f6bdf19a7 100644
--- a/bindings/utils.go
+++ b/bindings/utils.go
@@ -15,3 +15,9 @@ import "gitlab.com/elixxir/client/api"
 func CompressJpeg(imgBytes []byte) ([]byte, error) {
 	return api.CompressJpeg(imgBytes)
 }
+
+// CompressJpegForPreview takes a JPEG image in byte format
+// and compresses it based on desired output size
+func CompressJpegForPreview(imgBytes []byte) ([]byte, error) {
+	return api.CompressJpegForPreview(imgBytes)
+}
diff --git a/cmd/group.go b/cmd/group.go
index b973fb5c7f2b7addcb12a26c2a59b7b4c1524c22..1c7365921187bc6fda2754b2950498709c5f46f9 100644
--- a/cmd/group.go
+++ b/cmd/group.go
@@ -218,7 +218,7 @@ func sendGroup(groupIdString string, msg []byte, gm *groupChat.Manager) {
 
 	jww.INFO.Printf("Sending to group %s message %q", groupID, msg)
 
-	rid, timestamp, err := gm.Send(groupID, msg)
+	rid, timestamp, _, err := gm.Send(groupID, msg)
 	if err != nil {
 		jww.FATAL.Panicf("Sending message to group %s: %+v", groupID, err)
 	}
diff --git a/cmd/root.go b/cmd/root.go
index 28dd11f80212f4c8e1e7749020c54df2d042707b..c9f094a1883304107ea6cbdf5cd7d0c51fc6a7a4 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -304,10 +304,22 @@ var rootCmd = &cobra.Command{
 			deleteChannel(client, recipientID)
 		}
 
+		if viper.GetBool("delete-receive-requests") {
+			client.DeleteReceiveRequests()
+		}
+
+		if viper.GetBool("delete-sent-requests") {
+			client.DeleteSentRequests()
+		}
+
+		if viper.GetBool("delete-all-requests") {
+			client.DeleteAllRequests()
+		}
+
 		msg := message.Send{
 			Recipient:   recipientID,
 			Payload:     []byte(msgBody),
-			MessageType: message.Text,
+			MessageType: message.XxMessage,
 		}
 		paramsE2E := params.GetDefaultE2E()
 		paramsUnsafe := params.GetDefaultUnsafe()
@@ -328,10 +340,12 @@ var rootCmd = &cobra.Command{
 						var roundIDs []id.Round
 						var roundTimeout time.Duration
 						if unsafe {
+							paramsE2E.DebugTag = "cmd.Unsafe"
 							roundIDs, err = client.SendUnsafe(msg,
 								paramsUnsafe)
 							roundTimeout = paramsUnsafe.Timeout
 						} else {
+							paramsE2E.DebugTag = "cmd.E2E"
 							roundIDs, _, _, err = client.SendE2E(msg,
 								paramsE2E)
 							roundTimeout = paramsE2E.Timeout
@@ -453,7 +467,7 @@ func initClientCallbacks(client *api.Client) (chan *id.ID,
 	swboard := client.GetSwitchboard()
 	recvCh := make(chan message.Receive, 10000)
 	listenerID := swboard.RegisterChannel("DefaultCLIReceiver",
-		switchboard.AnyUser(), message.Text, recvCh)
+		switchboard.AnyUser(), message.XxMessage, recvCh)
 	jww.INFO.Printf("Message ListenerID: %v", listenerID)
 
 	// Set up auth request handler, which simply prints the
@@ -471,7 +485,7 @@ func initClientCallbacks(client *api.Client) (chan *id.ID,
 	})
 	if viper.GetBool("unsafe-channel-creation") {
 		authMgr.AddGeneralRequestCallback(func(
-			requestor contact.Contact, message string) {
+			requestor contact.Contact) {
 			jww.INFO.Printf("Channel Request: %s",
 				requestor.ID)
 			_, err := client.ConfirmAuthenticatedChannel(
@@ -599,7 +613,7 @@ func initClient() *api.Client {
 		viper.GetUint("e2eNumReKeys"))
 	netParams.E2EParams.RekeyThreshold = viper.GetFloat64("e2eRekeyThreshold")
 	netParams.ForceHistoricalRounds = viper.GetBool("forceHistoricalRounds")
-	netParams.FastPolling = viper.GetBool(" slowPolling")
+	netParams.FastPolling = !viper.GetBool("slowPolling")
 	netParams.ForceMessagePickupRetry = viper.GetBool("forceMessagePickupRetry")
 	if netParams.ForceMessagePickupRetry {
 		period := 3 * time.Second
@@ -679,13 +693,11 @@ func deleteChannel(client *api.Client, partnerId *id.ID) {
 	}
 }
 
-func printChanRequest(requestor contact.Contact, message string) {
+func printChanRequest(requestor contact.Contact) {
 	msg := fmt.Sprintf("Authentication channel request from: %s\n",
 		requestor.ID)
 	jww.INFO.Printf(msg)
 	fmt.Printf(msg)
-	msg = fmt.Sprintf("Authentication channel request message: %s\n", message)
-	jww.INFO.Printf(msg)
 	// fmt.Printf(msg)
 }
 
@@ -995,7 +1007,7 @@ func init() {
 		"", 500, "The delay between sending the messages in ms")
 	viper.BindPFlag("sendDelay", rootCmd.Flags().Lookup("sendDelay"))
 	rootCmd.Flags().BoolP("splitSends",
-		"", true, "Force sends to go over multiple rounds if possible")
+		"", false, "Force sends to go over multiple rounds if possible")
 	viper.BindPFlag("splitSends", rootCmd.Flags().Lookup("splitSends"))
 
 	rootCmd.Flags().BoolP("verify-sends", "", false,
@@ -1030,6 +1042,21 @@ func init() {
 	viper.BindPFlag("delete-channel",
 		rootCmd.PersistentFlags().Lookup("delete-channel"))
 
+	rootCmd.PersistentFlags().Bool("delete-receive-requests", false,
+		"Delete the all received contact requests.")
+	viper.BindPFlag("delete-receive-requests",
+		rootCmd.PersistentFlags().Lookup("delete-receive-requests"))
+
+	rootCmd.PersistentFlags().Bool("delete-sent-requests", false,
+		"Delete the all sent contact requests.")
+	viper.BindPFlag("delete-sent-requests",
+		rootCmd.PersistentFlags().Lookup("delete-sent-requests"))
+
+	rootCmd.PersistentFlags().Bool("delete-all-requests", false,
+		"Delete the all contact requests, both sent and received.")
+	viper.BindPFlag("delete-all-requests",
+		rootCmd.PersistentFlags().Lookup("delete-all-requests"))
+
 	rootCmd.Flags().BoolP("send-auth-request", "", false,
 		"Send an auth request to the specified destination and wait"+
 			"for confirmation")
diff --git a/cmd/single.go b/cmd/single.go
index 27300ef1e6adb9965cc79682890df1ad6f0f7914..cd6cc0a8815caa9969f02123274a0023512988e9 100644
--- a/cmd/single.go
+++ b/cmd/single.go
@@ -42,7 +42,7 @@ var singleCmd = &cobra.Command{
 		swBoard := client.GetSwitchboard()
 		recvCh := make(chan message.Receive, 10000)
 		listenerID := swBoard.RegisterChannel("DefaultCLIReceiver",
-			switchboard.AnyUser(), message.Text, recvCh)
+			switchboard.AnyUser(), message.XxMessage, recvCh)
 		jww.INFO.Printf("Message ListenerID: %v", listenerID)
 
 		// Set up auth request handler, which simply prints the user ID of the
@@ -53,7 +53,7 @@ var singleCmd = &cobra.Command{
 		// If unsafe channels, then add auto-acceptor
 		if viper.GetBool("unsafe-channel-creation") {
 			authMgr.AddGeneralRequestCallback(func(
-				requester contact.Contact, message string) {
+				requester contact.Contact) {
 				jww.INFO.Printf("Got request: %s", requester.ID)
 				_, err := client.ConfirmAuthenticatedChannel(requester)
 				if err != nil {
diff --git a/cmd/ud.go b/cmd/ud.go
index 4dd6463cebea7a0f880c94977cda44ad058498e3..cebbe7371079cbc9619aa7c74c4fc68ab419b0c7 100644
--- a/cmd/ud.go
+++ b/cmd/ud.go
@@ -42,7 +42,7 @@ var udCmd = &cobra.Command{
 		swBoard := client.GetSwitchboard()
 		recvCh := make(chan message.Receive, 10000)
 		listenerID := swBoard.RegisterChannel("DefaultCLIReceiver",
-			switchboard.AnyUser(), message.Text, recvCh)
+			switchboard.AnyUser(), message.XxMessage, recvCh)
 		jww.INFO.Printf("Message ListenerID: %v", listenerID)
 
 		// Set up auth request handler, which simply prints the user ID of the
@@ -53,7 +53,7 @@ var udCmd = &cobra.Command{
 		// If unsafe channels, add auto-acceptor
 		if viper.GetBool("unsafe-channel-creation") {
 			authMgr.AddGeneralRequestCallback(func(
-				requester contact.Contact, message string) {
+				requester contact.Contact) {
 				jww.INFO.Printf("Got Request: %s", requester.ID)
 				_, err := client.ConfirmAuthenticatedChannel(requester)
 				if err != nil {
@@ -127,7 +127,6 @@ var udCmd = &cobra.Command{
 
 		confirmID := viper.GetString("confirm")
 		if confirmID != "" {
-			// TODO: Lookup code
 			err = userDiscoveryMgr.SendConfirmFact(confirmID, confirmID)
 			if err != nil {
 				fmt.Printf("Couldn't confirm fact: %s\n",
@@ -136,25 +135,27 @@ var udCmd = &cobra.Command{
 			}
 		}
 
+		// Handle lookup (verification) process
+		// Note: Cryptographic verification occurs above the bindings layer
 		lookupIDStr := viper.GetString("lookup")
 		if lookupIDStr != "" {
-			lookupID, ok := parseRecipient(lookupIDStr)
-			if !ok {
-				jww.FATAL.Panicf("Could not parse recipient: %s", lookupIDStr)
-			}
+			lookupID, _ := parseRecipient(lookupIDStr)
+			//if !ok {
+			//	jww.FATAL.Panicf("Could not parse recipient: %s", lookupIDStr)
+			//}
 			err = userDiscoveryMgr.Lookup(lookupID,
 				func(newContact contact.Contact, err error) {
 					if err != nil {
-						jww.FATAL.Panicf("%+v", err)
+						jww.FATAL.Panicf("UserDiscovery Lookup error: %+v", err)
 					}
 					printContact(newContact)
-				}, 90*time.Second)
+				}, 30*time.Second)
 
 			if err != nil {
 				jww.WARN.Printf("Failed UD lookup: %+v", err)
 			}
 
-			time.Sleep(91 * time.Second)
+			time.Sleep(31 * time.Second)
 		}
 
 		usernameSearchStr := viper.GetString("searchusername")
diff --git a/dummy/send.go b/dummy/send.go
index 2ae9d92888b5960bee25687f1b9a4f4078a2cd2b..a01f6ba69cc1940fda4735a9278cf460fbd3b7a9 100644
--- a/dummy/send.go
+++ b/dummy/send.go
@@ -102,7 +102,7 @@ func (m *Manager) sendMessages(msgs map[id.ID]format.Message) error {
 					"preimage in e2e send: %+v", err)
 			}
 			rng.Close()
-
+			p.DebugTag = "dummy"
 			_, _, err := m.net.SendCMIX(msg, &recipient, p)
 			if err != nil {
 				jww.WARN.Printf("Failed to send dummy message %d/%d via "+
diff --git a/fileTransfer/fileMessage.go b/fileTransfer/fileMessage.go
deleted file mode 100644
index b73b948c90cc4d99bad2d459be6a8f98df61f2a5..0000000000000000000000000000000000000000
--- a/fileTransfer/fileMessage.go
+++ /dev/null
@@ -1,130 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////
-// Copyright © 2020 xx network SEZC                                          //
-//                                                                           //
-// Use of this source code is governed by a license that can be found in the //
-// LICENSE file                                                              //
-///////////////////////////////////////////////////////////////////////////////
-
-package fileTransfer
-
-import (
-	"encoding/binary"
-	"github.com/pkg/errors"
-	ftCrypto "gitlab.com/elixxir/crypto/fileTransfer"
-)
-
-// Size constants.
-const (
-	paddingLen = ftCrypto.NonceSize      // The length of the padding in bytes
-	partNumLen = 2                       // The length of the part number in bytes
-	fmMinSize  = partNumLen + paddingLen // Minimum size for the partMessage
-)
-
-// Error messages.
-const (
-	newFmSizeErr       = "size of external payload (%d) must be greater than %d"
-	unmarshalFmSizeErr = "size of passed in bytes (%d) must be greater than %d"
-	setFileFmErr       = "length of part bytes (%d) must be smaller than maximum payload size %d"
-)
-
-/*
-+-----------------------------------------+
-|          CMIX Message Contents          |
-+---------+-------------+-----------------+
-| Padding | Part Number |    File Data    |
-| 8 bytes |   2 bytes   | remaining space |
-+---------+-------------+-----------------+
-*/
-
-// partMessage contains part of the data being transferred and 256-bit padding
-// that is used as a nonce.
-type partMessage struct {
-	data    []byte // Serial of all contents
-	padding []byte // Random padding bytes
-	partNum []byte // The part number of the file
-	part    []byte // File part data
-}
-
-// newPartMessage generates a new part message that fits into the specified
-// external payload size. An error is returned if the external payload size is
-// too small to fit the part message.
-func newPartMessage(externalPayloadSize int) (partMessage, error) {
-	if externalPayloadSize < fmMinSize {
-		return partMessage{},
-			errors.Errorf(newFmSizeErr, externalPayloadSize, fmMinSize)
-	}
-
-	return mapPartMessage(make([]byte, externalPayloadSize)), nil
-}
-
-// mapPartMessage maps the data to the components of a partMessage. It is mapped
-// by reference; a copy is not made.
-func mapPartMessage(data []byte) partMessage {
-	return partMessage{
-		data:    data,
-		padding: data[:paddingLen],
-		partNum: data[paddingLen : paddingLen+partNumLen],
-		part:    data[paddingLen+partNumLen:],
-	}
-}
-
-// unmarshalPartMessage converts the bytes into a partMessage. An error is
-// returned if the size of the data is too small for a partMessage.
-func unmarshalPartMessage(b []byte) (partMessage, error) {
-	if len(b) < fmMinSize {
-		return partMessage{},
-			errors.Errorf(unmarshalFmSizeErr, len(b), fmMinSize)
-	}
-
-	return mapPartMessage(b), nil
-}
-
-// marshal returns the byte representation of the partMessage.
-func (m partMessage) marshal() []byte {
-	return m.data
-}
-
-// getPadding returns the padding in the message.
-func (m partMessage) getPadding() []byte {
-	return m.padding
-}
-
-// setPadding sets the partMessage padding to the given bytes. Note that this
-// padding should be random bytes generated via the appropriate crypto function.
-func (m partMessage) setPadding(b []byte) {
-	copy(m.padding, b)
-}
-
-// getPartNum returns the file part number.
-func (m partMessage) getPartNum() uint16 {
-	return binary.LittleEndian.Uint16(m.partNum)
-}
-
-// setPartNum sets the file part number.
-func (m partMessage) setPartNum(num uint16) {
-	b := make([]byte, partNumLen)
-	binary.LittleEndian.PutUint16(b, num)
-	copy(m.partNum, b)
-}
-
-// getPart returns the file part data from the message.
-func (m partMessage) getPart() []byte {
-	return m.part
-}
-
-// setPart sets the partMessage part to the given bytes. An error is returned if
-// the size of the provided part data is too large to store.
-func (m partMessage) setPart(b []byte) error {
-	if len(b) > len(m.part) {
-		return errors.Errorf(setFileFmErr, len(b), len(m.part))
-	}
-
-	copy(m.part, b)
-
-	return nil
-}
-
-// getPartSize returns the number of bytes available to store part data.
-func (m partMessage) getPartSize() int {
-	return len(m.part)
-}
diff --git a/fileTransfer/fileMessage_test.go b/fileTransfer/fileMessage_test.go
deleted file mode 100644
index 77b72eac9fa78a04d6c4c36aceda9a0effbd8a3e..0000000000000000000000000000000000000000
--- a/fileTransfer/fileMessage_test.go
+++ /dev/null
@@ -1,283 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////
-// Copyright © 2020 xx network SEZC                                          //
-//                                                                           //
-// Use of this source code is governed by a license that can be found in the //
-// LICENSE file                                                              //
-///////////////////////////////////////////////////////////////////////////////
-
-package fileTransfer
-
-import (
-	"bytes"
-	"encoding/binary"
-	"fmt"
-	"math/rand"
-	"testing"
-)
-
-// Tests that newPartMessage returns a partMessage of the expected size.
-func Test_newPartMessage(t *testing.T) {
-	externalPayloadSize := 256
-
-	fm, err := newPartMessage(externalPayloadSize)
-	if err != nil {
-		t.Errorf("newPartMessage returned an error: %+v", err)
-	}
-
-	if len(fm.data) != externalPayloadSize {
-		t.Errorf("Size of partMessage data does not match payload size."+
-			"\nexpected: %d\nreceived: %d", externalPayloadSize, len(fm.data))
-	}
-}
-
-// Error path: tests that newPartMessage returns the expected error when the
-// external payload size is too small.
-func Test_newPartMessage_SmallPayloadSizeError(t *testing.T) {
-	externalPayloadSize := fmMinSize - 1
-	expectedErr := fmt.Sprintf(newFmSizeErr, externalPayloadSize, fmMinSize)
-
-	_, err := newPartMessage(externalPayloadSize)
-	if err == nil || err.Error() != expectedErr {
-		t.Errorf("newPartMessage did not return the expected error when the "+
-			"given external payload size is too small."+
-			"\nexpected: %s\nreceived: %+v", expectedErr, err)
-	}
-}
-
-// Tests that mapPartMessage maps the data to the correct parts of the
-// partMessage.
-func Test_mapPartMessage(t *testing.T) {
-	// Generate expected values
-	_, expectedData, expectedPadding, expectedPartNum, expectedFile :=
-		newRandomFileMessage()
-
-	fm := mapPartMessage(expectedData)
-
-	if !bytes.Equal(expectedData, fm.data) {
-		t.Errorf("Incorrect data.\nexpected: %q\nreceived: %q",
-			expectedData, fm.data)
-	}
-
-	if !bytes.Equal(expectedPadding, fm.padding) {
-		t.Errorf("Incorrect padding data.\nexpected: %q\nreceived: %q",
-			expectedPadding, fm.padding)
-	}
-
-	if !bytes.Equal(expectedPartNum, fm.partNum) {
-		t.Errorf("Incorrect part number.\nexpected: %q\nreceived: %q",
-			expectedPartNum, fm.partNum)
-	}
-
-	if !bytes.Equal(expectedFile, fm.part) {
-		t.Errorf("Incorrect part data.\nexpected: %q\nreceived: %q",
-			expectedFile, fm.part)
-	}
-
-}
-
-// Tests that unmarshalPartMessage returns a partMessage with the expected
-// values.
-func Test_unmarshalPartMessage(t *testing.T) {
-	// Generate expected values
-	_, expectedData, expectedPadding, expectedPartNumb, expectedFile :=
-		newRandomFileMessage()
-
-	fm, err := unmarshalPartMessage(expectedData)
-	if err != nil {
-		t.Errorf("unmarshalPartMessage return an error: %+v", err)
-	}
-
-	if !bytes.Equal(expectedData, fm.data) {
-		t.Errorf("Incorrect data.\nexpected: %q\nreceived: %q",
-			expectedData, fm.data)
-	}
-
-	if !bytes.Equal(expectedPadding, fm.padding) {
-		t.Errorf("Incorrect padding data.\nexpected: %q\nreceived: %q",
-			expectedPadding, fm.padding)
-	}
-
-	if !bytes.Equal(expectedPartNumb, fm.partNum) {
-		t.Errorf("Incorrect part number.\nexpected: %q\nreceived: %q",
-			expectedPartNumb, fm.partNum)
-	}
-
-	if !bytes.Equal(expectedFile, fm.part) {
-		t.Errorf("Incorrect part data.\nexpected: %q\nreceived: %q",
-			expectedFile, fm.part)
-	}
-}
-
-// Error path: tests that unmarshalPartMessage returns the expected error when
-// the provided data is too small to be unmarshalled into a partMessage.
-func Test_unmarshalPartMessage_SizeError(t *testing.T) {
-	data := make([]byte, fmMinSize-1)
-	expectedErr := fmt.Sprintf(unmarshalFmSizeErr, len(data), fmMinSize)
-
-	_, err := unmarshalPartMessage(data)
-	if err == nil || err.Error() != expectedErr {
-		t.Errorf("unmarshalPartMessage did not return the expected error when "+
-			"the given bytes are too small to be a partMessage."+
-			"\nexpected: %s\nreceived: %+v", expectedErr, err)
-	}
-}
-
-// Tests that partMessage.marshal returns the correct data.
-func Test_fileMessage_marshal(t *testing.T) {
-	fm, expectedData, _, _, _ := newRandomFileMessage()
-
-	data := fm.marshal()
-
-	if !bytes.Equal(expectedData, data) {
-		t.Errorf("Marshalled data does not match expected."+
-			"\nexpected: %q\nreceived: %q", expectedData, data)
-	}
-}
-
-// Tests that partMessage.getPadding returns the correct padding data.
-func Test_fileMessage_getPadding(t *testing.T) {
-	fm, _, expectedPadding, _, _ := newRandomFileMessage()
-
-	padding := fm.getPadding()
-
-	if !bytes.Equal(expectedPadding, padding) {
-		t.Errorf("Padding data does not match expected."+
-			"\nexpected: %q\nreceived: %q", expectedPadding, padding)
-	}
-}
-
-// Tests that partMessage.setPadding sets the correct data.
-func Test_fileMessage_setPadding(t *testing.T) {
-	fm, err := newPartMessage(256)
-	if err != nil {
-		t.Errorf("Failed to create new partMessage: %+v", err)
-	}
-
-	expectedPadding := make([]byte, paddingLen)
-	rand.New(rand.NewSource(42)).Read(expectedPadding)
-
-	fm.setPadding(expectedPadding)
-
-	if !bytes.Equal(expectedPadding, fm.getPadding()) {
-		t.Errorf("Failed to set correct padding.\nexpected: %q\nreceived: %q",
-			expectedPadding, fm.getPadding())
-	}
-}
-
-// Tests that partMessage.getPartNum returns the correct part number.
-func Test_fileMessage_getPartNum(t *testing.T) {
-	fm, _, _, expectedPartNum, _ := newRandomFileMessage()
-
-	partNum := fm.getPartNum()
-	expected := binary.LittleEndian.Uint16(expectedPartNum)
-
-	if expected != partNum {
-		t.Errorf("Part number does not match expected."+
-			"\nexpected: %d\nreceived: %d", expected, partNum)
-	}
-}
-
-// Tests that partMessage.setPartNum sets the correct part number.
-func Test_fileMessage_setPartNum(t *testing.T) {
-	fm, err := newPartMessage(256)
-	if err != nil {
-		t.Errorf("Failed to create new partMessage: %+v", err)
-	}
-
-	expectedPartNum := make([]byte, partNumLen)
-	rand.New(rand.NewSource(42)).Read(expectedPartNum)
-	expected := binary.LittleEndian.Uint16(expectedPartNum)
-
-	fm.setPartNum(expected)
-
-	if expected != fm.getPartNum() {
-		t.Errorf("Failed to set correct part number.\nexpected: %d\nreceived: %d",
-			expected, fm.getPartNum())
-	}
-}
-
-// Tests that partMessage.getPart returns the correct part data.
-func Test_fileMessage_getFile(t *testing.T) {
-	fm, _, _, _, expectedFile := newRandomFileMessage()
-
-	file := fm.getPart()
-
-	if !bytes.Equal(expectedFile, file) {
-		t.Errorf("File data does not match expected."+
-			"\nexpected: %q\nreceived: %q", expectedFile, file)
-	}
-}
-
-// Tests that partMessage.setPart sets the correct part data.
-func Test_fileMessage_setFile(t *testing.T) {
-	fm, err := newPartMessage(256)
-	if err != nil {
-		t.Errorf("Failed to create new partMessage: %+v", err)
-	}
-
-	fileData := make([]byte, 64)
-	rand.New(rand.NewSource(42)).Read(fileData)
-	expectedFile := make([]byte, fm.getPartSize())
-	copy(expectedFile, fileData)
-
-	err = fm.setPart(expectedFile)
-	if err != nil {
-		t.Errorf("setPart returned an error: %+v", err)
-	}
-
-	if !bytes.Equal(expectedFile, fm.getPart()) {
-		t.Errorf("Failed to set correct part data.\nexpected: %q\nreceived: %q",
-			expectedFile, fm.getPart())
-	}
-}
-
-// Error path: tests that partMessage.setPart returns the expected error when
-// the provided part data is too large for the message.
-func Test_fileMessage_setFile_FileTooLargeError(t *testing.T) {
-	fm, err := newPartMessage(fmMinSize + 1)
-	if err != nil {
-		t.Errorf("Failed to create new partMessage: %+v", err)
-	}
-
-	expectedErr := fmt.Sprintf(setFileFmErr, fm.getPartSize()+1, fm.getPartSize())
-
-	err = fm.setPart(make([]byte, fm.getPartSize()+1))
-	if err == nil || err.Error() != expectedErr {
-		t.Errorf("setPart did not return the expected error when the given "+
-			"part data is too large to fit in the partMessage."+
-			"\nexpected: %s\nreceived: %+v", expectedErr, err)
-	}
-}
-
-// Tests that partMessage.getPartSize returns the expected available space for
-// the part data.
-func Test_fileMessage_getFileSize(t *testing.T) {
-	expectedSize := 256
-
-	fm, err := newPartMessage(fmMinSize + expectedSize)
-	if err != nil {
-		t.Errorf("Failed to create new partMessage: %+v", err)
-	}
-
-	if expectedSize != fm.getPartSize() {
-		t.Errorf("File size incorrect.\nexpected: %d\nreceived: %d",
-			expectedSize, fm.getPartSize())
-	}
-}
-
-// newRandomFileMessage generates a new partMessage filled with random data and
-// return the partMessage and its individual parts.
-func newRandomFileMessage() (partMessage, []byte, []byte, []byte, []byte) {
-	prng := rand.New(rand.NewSource(42))
-	padding := make([]byte, paddingLen)
-	prng.Read(padding)
-	partNum := make([]byte, partNumLen)
-	prng.Read(partNum)
-	part := make([]byte, 64)
-	prng.Read(part)
-	data := append(append(padding, partNum...), part...)
-
-	fm := mapPartMessage(data)
-
-	return fm, data, padding, partNum, part
-}
diff --git a/fileTransfer/manager.go b/fileTransfer/manager.go
index e91303742ebc27ccb95810e004cd69b2d8449d62..b188bcc203fe5e286507a5f7e7de4cb34485a836 100644
--- a/fileTransfer/manager.go
+++ b/fileTransfer/manager.go
@@ -101,8 +101,8 @@ type Manager struct {
 	sendQueue chan queuedPart
 
 	// Indicates if old transfers saved to storage have been recovered after
-	// file transfer is closed and reopened
-	oldTransfersRecovered bool
+	// file transfer is closed and reopened; this is an atomic
+	oldTransfersRecovered *uint32
 
 	// File transfer parameters
 	p Params
@@ -159,14 +159,16 @@ func newManager(client *api.Client, store *storage.Session,
 	}
 
 	jww.DEBUG.Printf(""+
-		"[FT] Created mew file transfer manager with params: %+v", p)
+		"[FT] Created new file transfer manager with params: %+v", p)
+
+	oldTransfersRecovered := uint32(0)
 
 	return &Manager{
 		receiveCB:             receiveCB,
 		sent:                  sent,
 		received:              received,
 		sendQueue:             make(chan queuedPart, sendQueueBuffLen),
-		oldTransfersRecovered: false,
+		oldTransfersRecovered: &oldTransfersRecovered,
 		p:                     p,
 		client:                client,
 		store:                 store,
diff --git a/fileTransfer/manager_test.go b/fileTransfer/manager_test.go
index c1133aa32a8f0f0e2fa09139678cdfd224e44fa2..63952f154487962fc6e504c56d58f6d0d00cda02 100644
--- a/fileTransfer/manager_test.go
+++ b/fileTransfer/manager_test.go
@@ -388,14 +388,13 @@ func TestManager_Resend_NoFingerprints(t *testing.T) {
 func TestManager_CloseSend_NoFingerprints(t *testing.T) {
 	m, sti, _ := newTestManagerWithTransfers(
 		[]uint16{16}, false, false, nil, nil, nil, t)
-	prng := NewPrng(42)
 	partSize, _ := m.getPartSize()
 
 	// Use up all the fingerprints in the transfer
 	transfer, _ := m.sent.GetTransfer(sti[0].tid)
 	for fpNum := uint16(0); fpNum < sti[0].numFps; fpNum++ {
 		partNum := fpNum % sti[0].numParts
-		_, _, _, _, err := transfer.GetEncryptedPart(partNum, partSize, prng)
+		_, _, _, err := transfer.GetEncryptedPart(partNum, partSize+2)
 		if err != nil {
 			t.Errorf("Failed to encrypt part %d (%d): %+v", partNum, fpNum, err)
 		}
diff --git a/fileTransfer/oldTransferRecovery.go b/fileTransfer/oldTransferRecovery.go
index 2957e1b156835be260c470910b8767d0653a8b4b..7fc4d41c61915d070717a3001c4154237cf0f81c 100644
--- a/fileTransfer/oldTransferRecovery.go
+++ b/fileTransfer/oldTransferRecovery.go
@@ -12,6 +12,7 @@ import (
 	jww "github.com/spf13/jwalterweatherman"
 	ftCrypto "gitlab.com/elixxir/crypto/fileTransfer"
 	"gitlab.com/xx_network/primitives/id"
+	"sync/atomic"
 )
 
 // Error messages.
@@ -29,7 +30,9 @@ const roundResultsMaxAttempts = 5
 func (m Manager) oldTransferRecovery(healthyChan chan bool, chanID uint64) {
 
 	// Exit if old transfers have already been recovered
-	if m.oldTransfersRecovered {
+	// TODO: move GetUnsentPartsAndSentRounds to manager creation and remove the
+	//  atomic
+	if !atomic.CompareAndSwapUint32(m.oldTransfersRecovered, 0, 1) {
 		jww.DEBUG.Printf("[FT] Old file transfer recovery thread not " +
 			"starting: none to recover (app was not closed)")
 		return
@@ -38,6 +41,9 @@ func (m Manager) oldTransferRecovery(healthyChan chan bool, chanID uint64) {
 	// Get list of unsent parts and rounds that parts were sent on
 	unsentParts, sentRounds, err := m.sent.GetUnsentPartsAndSentRounds()
 
+	jww.DEBUG.Printf("Adding unsent parts from %d recovered transfers: %v",
+		len(unsentParts), unsentParts)
+
 	// Add all unsent parts to the queue
 	for tid, partNums := range unsentParts {
 		m.queueParts(tid, partNums)
@@ -106,7 +112,9 @@ func (m Manager) updateSentRounds(healthyChan chan bool,
 					roundList, err)
 			} else {
 				jww.INFO.Printf(
-					"[FT] Successfully recovered old file transfers.")
+					"[FT] Successfully recovered old file transfers: %v",
+					sentRounds)
+
 				return nil
 			}
 			getRoundResultsAttempts++
diff --git a/fileTransfer/receive.go b/fileTransfer/receive.go
index 9a70f5853212bae431653c096d9168d567008b2a..3552fdf5635a819f66cbecfb9945ca01ab529846 100644
--- a/fileTransfer/receive.go
+++ b/fileTransfer/receive.go
@@ -8,7 +8,6 @@
 package fileTransfer
 
 import (
-	"github.com/pkg/errors"
 	jww "github.com/spf13/jwalterweatherman"
 	"gitlab.com/elixxir/client/interfaces/message"
 	"gitlab.com/elixxir/client/stoppable"
@@ -64,16 +63,8 @@ func (m *Manager) readMessage(msg message.Receive) (format.Message, error) {
 		return cMixMsg, err
 	}
 
-	// Unmarshal cMix message contents into a file part message
-	partMsg, err := unmarshalPartMessage(cMixMsg.GetContents())
-	if err != nil {
-		return cMixMsg, errors.Errorf(unmarshalPartMessageErr, err)
-	}
-
 	// Add part to received transfer
-	rt, tid, completed, err := m.received.AddPart(partMsg.getPart(),
-		partMsg.getPadding(), cMixMsg.GetMac(), partMsg.getPartNum(),
-		cMixMsg.GetKeyFP())
+	rt, tid, completed, err := m.received.AddPart(cMixMsg)
 	if err != nil {
 		return cMixMsg, err
 	}
diff --git a/fileTransfer/receiveNew_test.go b/fileTransfer/receiveNew_test.go
index 872012beee4ed019a035212c85f45a16ea7a1926..4aea311faa6e0fedadf6f627e8f4b43eb544753e 100644
--- a/fileTransfer/receiveNew_test.go
+++ b/fileTransfer/receiveNew_test.go
@@ -51,7 +51,7 @@ func TestManager_receiveNewFileTransfer(t *testing.T) {
 	}
 	marshalledMsg, err := proto.Marshal(protoMsg)
 	if err != nil {
-		t.Errorf("Failed to marshal proto message: %+v", err)
+		t.Errorf("Failed to Marshal proto message: %+v", err)
 	}
 	receiveMsg := message.Receive{
 		Payload:     marshalledMsg,
@@ -120,7 +120,7 @@ func TestManager_receiveNewFileTransfer_Stop(t *testing.T) {
 	}
 	marshalledMsg, err := proto.Marshal(protoMsg)
 	if err != nil {
-		t.Errorf("Failed to marshal proto message: %+v", err)
+		t.Errorf("Failed to Marshal proto message: %+v", err)
 	}
 	receiveMsg := message.Receive{
 		Payload:     marshalledMsg,
diff --git a/fileTransfer/send.go b/fileTransfer/send.go
index dbab0994774046b8ce5fa62ab8030fbde4335fd8..0ae1bdfae8e58456395d4cc5cbc300fd13f7040a 100644
--- a/fileTransfer/send.go
+++ b/fileTransfer/send.go
@@ -33,7 +33,7 @@ import (
 const (
 	// Manager.sendParts
 	sendManyCmixWarn   = "[FT] Failed to send %d file parts %v via SendManyCMIX: %+v"
-	setInProgressErr   = "[FT] Failed to set parts %v to in-progress for transfer %s"
+	setInProgressErr   = "[FT] Failed to set parts %v to in-progress for transfer %s: %+v"
 	getRoundResultsErr = "[FT] Failed to get round results for round %d for file transfers %v: %+v"
 
 	// Manager.buildMessages
@@ -47,7 +47,7 @@ const (
 	finishedEndE2eMsfErr    = "[FT] Failed to send E2E message to %s on completion of file transfer %s: %+v"
 	roundFailureWarn        = "[FT] Failed to send file parts for file transfers %v on round %d: round %s"
 	finishFailNoTransferErr = "[FT] Failed to requeue in-progress parts on failure of round %d for transfer %s: %+v"
-	unsetInProgressErr      = "[FT] Failed to remove parts from in-progress list for transfer %s: round %s"
+	unsetInProgressErr      = "[FT] Failed to remove parts from in-progress list for transfer %s: round %s: %+v"
 
 	// Manager.sendEndE2eMessage
 	endE2eGetPartnerErr = "failed to get file transfer partner %s: %+v"
@@ -211,7 +211,7 @@ func (m *Manager) handleSend(partList *[]queuedPart, lastSend *time.Time,
 	// Send all the messages
 	err := m.sendParts(*partList, sentRounds)
 	if err != nil {
-		jww.FATAL.Panic(err)
+		jww.ERROR.Print(err)
 	}
 
 	// Update the timestamp of the send
@@ -247,6 +247,7 @@ func (m *Manager) sendParts(partList []queuedPart,
 	p := params.GetDefaultCMIX()
 	p.SendTimeout = m.p.SendTimeout
 	p.ExcludedRounds = sentRounds
+	p.DebugTag = "ft.Part"
 
 	// Send parts
 	rid, _, err := m.net.SendManyCMIX(messages, p)
@@ -270,7 +271,7 @@ func (m *Manager) sendParts(partList []queuedPart,
 	for tid, transfer := range transfers {
 		exists, err := transfer.SetInProgress(rid, groupedParts[tid]...)
 		if err != nil {
-			return errors.Errorf(setInProgressErr, groupedParts[tid], tid)
+			return errors.Errorf(setInProgressErr, groupedParts[tid], tid, err)
 		}
 
 		transfer.CallProgressCB(nil)
@@ -364,30 +365,14 @@ func (m *Manager) newCmixMessage(transfer *ftStorage.SentTransfer,
 	// Create new empty cMix message
 	cmixMsg := format.NewMessage(m.store.Cmix().GetGroup().GetP().ByteLen())
 
-	// Create new empty file part message of size equal to the available payload
-	// size in the cMix message
-	partMsg, err := newPartMessage(cmixMsg.ContentsSize())
-	if err != nil {
-		return cmixMsg, err
-	}
-
-	// Get encrypted file part, file part MAC, padding (nonce), and fingerprint
-	encPart, mac, padding, fp, err := transfer.GetEncryptedPart(
-		partNum, partMsg.getPartSize(), rng)
-	if err != nil {
-		return cmixMsg, err
-	}
-
-	// Construct file part message from padding (
-	partMsg.setPadding(padding)
-	partMsg.setPartNum(partNum)
-	err = partMsg.setPart(encPart)
+	// Get encrypted file part, file part MAC, nonce (nonce), and fingerprint
+	encPart, mac, fp, err := transfer.GetEncryptedPart(partNum, cmixMsg.ContentsSize())
 	if err != nil {
-		return cmixMsg, err
+		return format.Message{}, err
 	}
 
 	// Construct cMix message
-	cmixMsg.SetContents(partMsg.marshal())
+	cmixMsg.SetContents(encPart)
 	cmixMsg.SetKeyFP(fp)
 	cmixMsg.SetMac(mac)
 
@@ -460,7 +445,8 @@ func (m *Manager) makeRoundEventCallback(
 					// Remove parts from in-progress list
 					partsToResend, err := st.UnsetInProgress(rid)
 					if err != nil {
-						jww.ERROR.Printf(unsetInProgressErr, tid, roundResult)
+						jww.ERROR.Printf(
+							unsetInProgressErr, tid, roundResult, err)
 					}
 
 					// Call progress callback after change in progress
@@ -492,6 +478,7 @@ func (m *Manager) sendEndE2eMessage(recipient *id.ID) error {
 	// Send the message under file transfer preimage
 	e2eParams := params.GetDefaultE2E()
 	e2eParams.IdentityPreimage = partner.GetFileTransferPreimage()
+	e2eParams.DebugTag = "ft.End"
 
 	// Store the message in the critical messages buffer first to ensure it is
 	// present if the send fails
@@ -529,7 +516,7 @@ func (m *Manager) sendEndE2eMessage(recipient *id.ID) error {
 	// the session and the log
 	m.store.GetCriticalMessages().Succeeded(sendMsg, e2eParams)
 	jww.INFO.Printf("[FT] Sending of message %s informing %s that a transfer "+
-		"ended successful.", e2eMsgID, recipient)
+		"completed successfully.", e2eMsgID, recipient)
 
 	return nil
 }
@@ -565,12 +552,12 @@ func (m *Manager) getPartSize() (int, error) {
 
 	// Create new empty file part message of size equal to the available payload
 	// size in the cMix message
-	partMsg, err := newPartMessage(cmixMsg.ContentsSize())
+	partMsg, err := ftStorage.NewPartMessage(cmixMsg.ContentsSize())
 	if err != nil {
 		return 0, err
 	}
 
-	return partMsg.getPartSize(), nil
+	return partMsg.GetPartSize(), nil
 }
 
 // partitionFile splits the file into parts of the specified part size.
diff --git a/fileTransfer/sendNew.go b/fileTransfer/sendNew.go
index 99a935b737a6d0cfff495cc4e458ab1314fd3414..b28fe263958c3ff5b95455faee9f3623f44267b1 100644
--- a/fileTransfer/sendNew.go
+++ b/fileTransfer/sendNew.go
@@ -43,6 +43,7 @@ func (m *Manager) sendNewFileTransfer(recipient *id.ID, fileName,
 	// Sends as a silent message to avoid a notification
 	p := params.GetDefaultE2E()
 	p.CMIX.IdentityPreimage = relationship.GetSilentPreimage()
+	p.DebugTag = "ft.New"
 
 	// Send E2E message
 	rounds, _, _, err := m.net.SendE2E(sendMsg, p, nil)
diff --git a/fileTransfer/send_test.go b/fileTransfer/send_test.go
index ee10658b1fe6c190cbaa9a04879c72829a726787..2e3d3d16d8325be32ba975decb14b3af41f57636 100644
--- a/fileTransfer/send_test.go
+++ b/fileTransfer/send_test.go
@@ -662,20 +662,20 @@ func TestManager_newCmixMessage(t *testing.T) {
 			"\nexpected: %s\nrecieved: %s", fp, cmixMsg.GetKeyFP())
 	}
 
-	partMsg, err := unmarshalPartMessage(cmixMsg.GetContents())
+	decrPart, err := ftCrypto.DecryptPart(key, cmixMsg.GetContents(),
+		 cmixMsg.GetMac(), 0,cmixMsg.GetKeyFP())
 	if err != nil {
-		t.Errorf("Failed to unmarshal part message: %+v", err)
+		t.Errorf("Failed to decrypt file part: %+v", err)
 	}
 
-	decrPart, err := ftCrypto.DecryptPart(key, partMsg.getPart(),
-		partMsg.getPadding(), cmixMsg.GetMac(), partMsg.getPartNum())
+	partMsg, err := ftStorage.UnmarshalPartMessage(decrPart)
 	if err != nil {
-		t.Errorf("Failed to decrypt file part: %+v", err)
+		t.Errorf("Failed to unmarshal part message: %+v", err)
 	}
 
-	if !bytes.Equal(decrPart, parts[0]) {
+	if !bytes.Equal(partMsg.GetPart(), parts[0]) {
 		t.Errorf("Decrypted part does not match expected."+
-			"\nexpected: %q\nreceived: %q", parts[0], decrPart)
+			"\nexpected: %q\nreceived: %q", parts[0], partMsg.GetPart())
 	}
 }
 
@@ -998,7 +998,7 @@ func Test_makeListOfPartNums(t *testing.T) {
 	}
 }
 
-// Tests that the part size returned by Manager.getPartSize matches the manually
+// Tests that the part size returned by Manager.GetPartSize matches the manually
 // calculated part size.
 func TestManager_getPartSize(t *testing.T) {
 	m := newTestManager(false, nil, nil, nil, nil, t)
@@ -1006,13 +1006,13 @@ func TestManager_getPartSize(t *testing.T) {
 	// Calculate the expected part size
 	primeByteLen := m.store.Cmix().GetGroup().GetP().ByteLen()
 	cmixMsgUsedLen := format.AssociatedDataSize
-	filePartMsgUsedLen := fmMinSize
+	filePartMsgUsedLen := ftStorage.FmMinSize
 	expected := 2*primeByteLen - cmixMsgUsedLen - filePartMsgUsedLen-1
 
 	// Get the part size
 	partSize, err := m.getPartSize()
 	if err != nil {
-		t.Errorf("getPartSize returned an error: %+v", err)
+		t.Errorf("GetPartSize returned an error: %+v", err)
 	}
 
 	if expected != partSize {
diff --git a/fileTransfer/utils_test.go b/fileTransfer/utils_test.go
index 7738b4529052317bf77a952da2d0e5dea311249d..a982d3ba226565db9e4886132f164998560826a6 100644
--- a/fileTransfer/utils_test.go
+++ b/fileTransfer/utils_test.go
@@ -191,17 +191,20 @@ func newTestManager(sendErr bool, sendChan, sendE2eChan chan message.Receive,
 	avgSendSize := avgNumMessages * (8192 / 8)
 	p.MaxThroughput = int(time.Second) * avgSendSize
 
+	oldTransfersRecovered := uint32(0)
+
 	m := &Manager{
-		receiveCB:       receiveCB,
-		sent:            sent,
-		received:        received,
-		sendQueue:       make(chan queuedPart, sendQueueBuffLen),
-		p:               p,
-		store:           storage.InitTestingSession(t),
-		swb:             switchboard.New(),
-		net:             net,
-		rng:             fastRNG.NewStreamGenerator(1000, 10, csprng.NewSystemRNG),
-		getRoundResults: rr,
+		receiveCB:             receiveCB,
+		sent:                  sent,
+		received:              received,
+		sendQueue:             make(chan queuedPart, sendQueueBuffLen),
+		oldTransfersRecovered: &oldTransfersRecovered,
+		p:                     p,
+		store:                 storage.InitTestingSession(t),
+		swb:                   switchboard.New(),
+		net:                   net,
+		rng:                   fastRNG.NewStreamGenerator(1000, 10, csprng.NewSystemRNG),
+		getRoundResults:       rr,
 	}
 
 	return m
@@ -495,6 +498,7 @@ func (tnm *testNetworkManager) SendManyCMIX(messages []message.TargetedCmixMessa
 		for _, msg := range messages {
 			tnm.sendChan <- message.Receive{
 				Payload: msg.Message.Marshal(),
+				Sender:  &id.ID{0},
 				RoundId: tnm.rid,
 			}
 		}
diff --git a/go.mod b/go.mod
index e4762cd35b89dc5ca2a563a8bcaac06f38f08d55..4a0f6367b73509140ecc2885b5e5b7bd87396bd4 100644
--- a/go.mod
+++ b/go.mod
@@ -1,36 +1,39 @@
 module gitlab.com/elixxir/client
 
-go 1.13
+go 1.17
 
 require (
 	github.com/cloudflare/circl v1.0.1-0.20211008185751-59b49bc148ce
 	github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3
 	github.com/golang/protobuf v1.5.2
-	github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00 // indirect
-	github.com/magiconair/properties v1.8.4 // indirect
-	github.com/mitchellh/mapstructure v1.4.0 // indirect
 	github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
-	github.com/pelletier/go-toml v1.8.1 // indirect
 	github.com/pkg/errors v0.9.1
-	github.com/smartystreets/assertions v1.0.1 // indirect
-	github.com/spf13/afero v1.5.1 // indirect
-	github.com/spf13/cast v1.3.1 // indirect
 	github.com/spf13/cobra v1.1.1
 	github.com/spf13/jwalterweatherman v1.1.0
 	github.com/spf13/viper v1.7.1
 	gitlab.com/elixxir/bloomfilter v0.0.0-20200930191214-10e9ac31b228
-	gitlab.com/elixxir/comms v0.0.4-0.20220104174855-044783c5c1e6
-	gitlab.com/elixxir/crypto v0.0.7-0.20220104174238-dbd761b30553
-	gitlab.com/elixxir/ekv v0.1.5
-	gitlab.com/elixxir/primitives v0.0.3-0.20220104173924-275cb9d7834f
-	gitlab.com/xx_network/comms v0.0.4-0.20211227194445-c099754b3cda
-	gitlab.com/xx_network/crypto v0.0.5-0.20211227194420-f311e8920467
-	gitlab.com/xx_network/primitives v0.0.4-0.20211222205802-03e9d7d835b0
-	golang.org/x/crypto v0.0.0-20210921155107-089bfa567519
-	golang.org/x/net v0.0.0-20210525063256-abc453219eb5
-	google.golang.org/genproto v0.0.0-20210105202744-fe13368bc0e1 // indirect
+	gitlab.com/elixxir/comms v0.0.4-0.20220222212253-41a1a0067369
+	gitlab.com/elixxir/crypto v0.0.7-0.20220222212142-d3303373ee78
+	gitlab.com/elixxir/ekv v0.1.6
+	gitlab.com/elixxir/primitives v0.0.3-0.20220222212109-d412a6e46623
+	gitlab.com/xx_network/comms v0.0.4-0.20220222212058-5a37737af57e
+	gitlab.com/xx_network/crypto v0.0.5-0.20220222212031-750f7e8a01f4
+	gitlab.com/xx_network/primitives v0.0.4-0.20220222211843-901fa4a2d72b
+	golang.org/x/crypto v0.0.0-20220128200615-198e4374d7ed
+	golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2
 	google.golang.org/grpc v1.42.0
 	google.golang.org/protobuf v1.27.1
+)
+
+require (
+	github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00 // indirect
+	github.com/magiconair/properties v1.8.4 // indirect
+	github.com/mitchellh/mapstructure v1.4.0 // indirect
+	github.com/pelletier/go-toml v1.8.1 // indirect
+	github.com/smartystreets/assertions v1.0.1 // indirect
+	github.com/spf13/afero v1.5.1 // indirect
+	github.com/spf13/cast v1.3.1 // indirect
+	google.golang.org/genproto v0.0.0-20210105202744-fe13368bc0e1 // indirect
 	gopkg.in/ini.v1 v1.62.0 // indirect
 	gopkg.in/yaml.v2 v2.4.0 // indirect
 )
diff --git a/go.sum b/go.sum
index f331b2abbba23eafdf7369c14a046d90de1dada3..5cecc075ac7209f3664ffc22c85e679008a5edfb 100644
--- a/go.sum
+++ b/go.sum
@@ -51,6 +51,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
 github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
+github.com/elliotchance/orderedmap v1.4.0 h1:wZtfeEONCbx6in1CZyE6bELEt/vFayMvsxqI5SgsR+A=
+github.com/elliotchance/orderedmap v1.4.0/go.mod h1:wsDwEaX5jEoyhbs7x93zk2H/qv0zwuhg4inXhDkYqys=
 github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
 github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
 github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
@@ -270,36 +272,34 @@ github.com/zeebo/pcg v1.0.0 h1:dt+dx+HvX8g7Un32rY9XWoYnd0NmKmrIzpHF7qiTDj0=
 github.com/zeebo/pcg v1.0.0/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
 gitlab.com/elixxir/bloomfilter v0.0.0-20200930191214-10e9ac31b228 h1:Gi6rj4mAlK0BJIk1HIzBVMjWNjIUfstrsXC2VqLYPcA=
 gitlab.com/elixxir/bloomfilter v0.0.0-20200930191214-10e9ac31b228/go.mod h1:H6jztdm0k+wEV2QGK/KYA+MY9nj9Zzatux/qIvDDv3k=
-gitlab.com/elixxir/comms v0.0.4-0.20220104174855-044783c5c1e6 h1:MaiS3Mdhjwc3aNKonKJf9FSgnShwXQ6FnAI1QENDi7o=
-gitlab.com/elixxir/comms v0.0.4-0.20220104174855-044783c5c1e6/go.mod h1:r4xZgd+DPDujHTJZ6Y8+JXM2Ae2rZHa6rFggOPxs/Gc=
+gitlab.com/elixxir/comms v0.0.4-0.20220222212253-41a1a0067369 h1:Bk5T3unbs3cjEqzxCirb1IlcTOMAEVQLltgsrVs1/cw=
+gitlab.com/elixxir/comms v0.0.4-0.20220222212253-41a1a0067369/go.mod h1:AligJKSltFDPe/rqE2EZBfWCMSrae0zUo7scsXoyMPE=
 gitlab.com/elixxir/crypto v0.0.0-20200804182833-984246dea2c4/go.mod h1:ucm9SFKJo+K0N2GwRRpaNr+tKXMIOVWzmyUD0SbOu2c=
 gitlab.com/elixxir/crypto v0.0.3/go.mod h1:ZNgBOblhYToR4m8tj4cMvJ9UsJAUKq+p0gCp07WQmhA=
-gitlab.com/elixxir/crypto v0.0.7-0.20211230230452-bca020488964/go.mod h1:fexaw14nwGMlT6vL9eIJ1ixgiomyAp88hSHl0Yx0/xU=
-gitlab.com/elixxir/crypto v0.0.7-0.20220104174238-dbd761b30553 h1:BPwepGZspxgiY4QMUwVFMgVIEs8ziuMcT/uXmPQQ9Gs=
-gitlab.com/elixxir/crypto v0.0.7-0.20220104174238-dbd761b30553/go.mod h1:fexaw14nwGMlT6vL9eIJ1ixgiomyAp88hSHl0Yx0/xU=
-gitlab.com/elixxir/ekv v0.1.5 h1:R8M1PA5zRU1HVnTyrtwybdABh7gUJSCvt1JZwUSeTzk=
-gitlab.com/elixxir/ekv v0.1.5/go.mod h1:e6WPUt97taFZe5PFLPb1Dupk7tqmDCTQu1kkstqJvw4=
+gitlab.com/elixxir/crypto v0.0.7-0.20220222212142-d3303373ee78 h1:MvZ0UwyhCSuNUcmHT905oadu7XYT2WSz+QD3Rjcgg00=
+gitlab.com/elixxir/crypto v0.0.7-0.20220222212142-d3303373ee78/go.mod h1:bPD4FmnnaDFLxn+d4YDWZhVnevWXArKwOMMza4MU5uQ=
+gitlab.com/elixxir/ekv v0.1.6 h1:M2hUSNhH/ChxDd+s8xBqSEKgoPtmE6hOEBqQ73KbN6A=
+gitlab.com/elixxir/ekv v0.1.6/go.mod h1:e6WPUt97taFZe5PFLPb1Dupk7tqmDCTQu1kkstqJvw4=
 gitlab.com/elixxir/primitives v0.0.0-20200731184040-494269b53b4d/go.mod h1:OQgUZq7SjnE0b+8+iIAT2eqQF+2IFHn73tOo+aV11mg=
 gitlab.com/elixxir/primitives v0.0.0-20200804170709-a1896d262cd9/go.mod h1:p0VelQda72OzoUckr1O+vPW0AiFe0nyKQ6gYcmFSuF8=
 gitlab.com/elixxir/primitives v0.0.0-20200804182913-788f47bded40/go.mod h1:tzdFFvb1ESmuTCOl1z6+yf6oAICDxH2NPUemVgoNLxc=
 gitlab.com/elixxir/primitives v0.0.1/go.mod h1:kNp47yPqja2lHSiS4DddTvFpB/4D9dB2YKnw5c+LJCE=
-gitlab.com/elixxir/primitives v0.0.3-0.20211230224340-fc0905d8776e/go.mod h1:zA+1Lp9fGPo6pl1QxtMoNPLeZJ1O5m4kcH7HNxePQnQ=
-gitlab.com/elixxir/primitives v0.0.3-0.20220104173924-275cb9d7834f h1:zg3oYk+a6Wmq9tGRwka3GjJR1XRZIVCsOMpBGxtF2yc=
-gitlab.com/elixxir/primitives v0.0.3-0.20220104173924-275cb9d7834f/go.mod h1:zA+1Lp9fGPo6pl1QxtMoNPLeZJ1O5m4kcH7HNxePQnQ=
+gitlab.com/elixxir/primitives v0.0.3-0.20220222212109-d412a6e46623 h1:NzJ06KdJd3fVJee0QvGhNr3CO+Ki8Ea1PeakZsm+rZM=
+gitlab.com/elixxir/primitives v0.0.3-0.20220222212109-d412a6e46623/go.mod h1:MtFIyJUQn9P7djzVlBpEYkPNnnWFTjZvw89swoXY+QM=
 gitlab.com/xx_network/comms v0.0.0-20200805174823-841427dd5023/go.mod h1:owEcxTRl7gsoM8c3RQ5KAm5GstxrJp5tn+6JfQ4z5Hw=
-gitlab.com/xx_network/comms v0.0.4-0.20211227194445-c099754b3cda h1:oWl8TuAgdx/6J9lwdH8wYGSb4W6D5TG1AZkzbA8EzbE=
-gitlab.com/xx_network/comms v0.0.4-0.20211227194445-c099754b3cda/go.mod h1:5arueRMa2MNa6dALnfJwyZOhqhV53Gqc+tlHRz+Ycjw=
+gitlab.com/xx_network/comms v0.0.4-0.20220222212058-5a37737af57e h1:PrQoTQoA6be4J+Lr/AclebS5Gz0Zm/TYC5b44qWa1PU=
+gitlab.com/xx_network/comms v0.0.4-0.20220222212058-5a37737af57e/go.mod h1:isHnwem0v4rTcwwHP455FhVlFyPcHkHiVz+N3s/uCSI=
 gitlab.com/xx_network/crypto v0.0.3/go.mod h1:DF2HYvvCw9wkBybXcXAgQMzX+MiGbFPjwt3t17VRqRE=
 gitlab.com/xx_network/crypto v0.0.4/go.mod h1:+lcQEy+Th4eswFgQDwT0EXKp4AXrlubxalwQFH5O0Mk=
-gitlab.com/xx_network/crypto v0.0.5-0.20211227194420-f311e8920467 h1:LkZtWBYrM2e7QRf5aaBAcy7s7CpYGhAqgXRFVCdBRy4=
-gitlab.com/xx_network/crypto v0.0.5-0.20211227194420-f311e8920467/go.mod h1:c+x0w3Xk6QZe5w2Redn5SiaBpqAhgNSfwBr0JGa/yyo=
+gitlab.com/xx_network/crypto v0.0.5-0.20220222212031-750f7e8a01f4 h1:95dZDMn/hpLNwsgZO9eyQgGKaSDyh6F6+WygqZIciww=
+gitlab.com/xx_network/crypto v0.0.5-0.20220222212031-750f7e8a01f4/go.mod h1:6apvsoHCQJDjO0J4E3uhR3yO9tTz/Mq5be5rjB3tQPU=
 gitlab.com/xx_network/primitives v0.0.0-20200803231956-9b192c57ea7c/go.mod h1:wtdCMr7DPePz9qwctNoAUzZtbOSHSedcK++3Df3psjA=
 gitlab.com/xx_network/primitives v0.0.0-20200804183002-f99f7a7284da/go.mod h1:OK9xevzWCaPO7b1wiluVJGk7R5ZsuC7pHY5hteZFQug=
 gitlab.com/xx_network/primitives v0.0.2/go.mod h1:cs0QlFpdMDI6lAo61lDRH2JZz+3aVkHy+QogOB6F/qc=
-gitlab.com/xx_network/primitives v0.0.4-0.20211222205802-03e9d7d835b0 h1:IHHb59DJEKk02HgfxddqK2ilvkRMAUdPIBFn8rmjjIg=
-gitlab.com/xx_network/primitives v0.0.4-0.20211222205802-03e9d7d835b0/go.mod h1:9imZHvYwNFobxueSvVtHneZLk9wTK7HQTzxPm+zhFhE=
-gitlab.com/xx_network/ring v0.0.3-0.20210527191221-ce3f170aabd5 h1:FY+4Rh1Q2rgLyv10aKJjhWApuKRCR/054XhreudfAvw=
-gitlab.com/xx_network/ring v0.0.3-0.20210527191221-ce3f170aabd5/go.mod h1:aLzpP2TiZTQut/PVHR40EJAomzugDdHXetbieRClXIM=
+gitlab.com/xx_network/primitives v0.0.4-0.20220222211843-901fa4a2d72b h1:shZZ3xZNNKYVpEp4Mqu/G9+ZR+J8QA8mmPk/4Cit8+Y=
+gitlab.com/xx_network/primitives v0.0.4-0.20220222211843-901fa4a2d72b/go.mod h1:9imZHvYwNFobxueSvVtHneZLk9wTK7HQTzxPm+zhFhE=
+gitlab.com/xx_network/ring v0.0.3-0.20220222211904-da613960ad93 h1:eJZrXqHsMmmejEPWw8gNAt0I8CGAMNO/7C339Zco3TM=
+gitlab.com/xx_network/ring v0.0.3-0.20220222211904-da613960ad93/go.mod h1:aLzpP2TiZTQut/PVHR40EJAomzugDdHXetbieRClXIM=
 go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
 go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
 go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
@@ -320,8 +320,8 @@ golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPh
 golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
 golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg=
-golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/crypto v0.0.0-20220128200615-198e4374d7ed h1:YoWVYYAfvQ4ddHv3OKmIvX7NCAhFGTj62VP2l2kfBbA=
+golang.org/x/crypto v0.0.0-20220128200615-198e4374d7ed/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -357,8 +357,9 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
 golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
 golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
-golang.org/x/net v0.0.0-20210525063256-abc453219eb5 h1:wjuX4b5yYQnEQHzd+CBcrcC6OVR2J1CN6mUy0oSxIPo=
 golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE=
+golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
diff --git a/groupChat/send.go b/groupChat/send.go
index 868d420134b6ebb79c0687f58648311af6445559..03c513ccc032ab28778dfb94bb44d05c1362355e 100644
--- a/groupChat/send.go
+++ b/groupChat/send.go
@@ -37,44 +37,55 @@ const (
 
 // Send sends a message to all group members using Client.SendManyCMIX. The
 // send fails if the message is too long.
-func (m *Manager) Send(groupID *id.ID, message []byte) (id.Round, time.Time,
+func (m *Manager) Send(groupID *id.ID, message []byte) (id.Round, time.Time, group.MessageID,
 	error) {
 	// Get the current time stripped of the monotonic clock
 	timeNow := netTime.Now().Round(0)
 
 	// Create a cMix message for each group member
-	messages, err := m.createMessages(groupID, message, timeNow)
+	messages, msgID, err := m.createMessages(groupID, message, timeNow)
 	if err != nil {
-		return 0, time.Time{}, errors.Errorf(newCmixMsgErr, err)
+		return 0, time.Time{}, group.MessageID{}, errors.Errorf(newCmixMsgErr, err)
 	}
 
 	param := params.GetDefaultCMIX()
 	param.IdentityPreimage = groupID[:]
+	param.DebugTag = "group.Message"
 
 	rid, _, err := m.net.SendManyCMIX(messages, param)
 	if err != nil {
-		return 0, time.Time{},
+		return 0, time.Time{}, group.MessageID{},
 			errors.Errorf(sendManyCmixErr, m.gs.GetUser().ID, groupID, err)
 	}
 
 	jww.DEBUG.Printf("Sent message to %d members in group %s at %s.",
 		len(messages), groupID, timeNow)
 
-	return rid, timeNow, nil
+	return rid, timeNow, msgID, nil
 }
 
 // createMessages generates a list of cMix messages and a list of corresponding
 // recipient IDs.
 func (m *Manager) createMessages(groupID *id.ID, msg []byte, timestamp time.Time) (
-	[]message.TargetedCmixMessage, error) {
+	[]message.TargetedCmixMessage, group.MessageID, error) {
+
+	//make the message ID
+	cmixMsg := format.NewMessage(m.store.Cmix().GetGroup().GetP().ByteLen())
+	_, intlMsg, err := newMessageParts(cmixMsg.ContentsSize())
+	if err != nil {
+		return nil, group.MessageID{},errors.WithMessage(err,"Failed to make message parts for message ID")
+	}
+	messageID := group.NewMessageID(groupID, setInternalPayload(intlMsg, timestamp, m.gs.GetUser().ID, msg))
 
 	g, exists := m.gs.Get(groupID)
 	if !exists {
-		return []message.TargetedCmixMessage{},
+		return []message.TargetedCmixMessage{}, group.MessageID{},
 			errors.Errorf(newNoGroupErr, groupID)
 	}
 
-	return m.newMessages(g, msg, timestamp)
+	NewMessages, err := m.newMessages(g, msg, timestamp)
+
+	return NewMessages, messageID, err
 }
 
 // newMessages is a private function that allows the passing in of a timestamp
diff --git a/groupChat/sendRequests.go b/groupChat/sendRequests.go
index edcecc77f1b5516a9ed75d9481738210374b88e4..555fd2c5e0cfc891ba8cf37877e6dc72353aebf3 100644
--- a/groupChat/sendRequests.go
+++ b/groupChat/sendRequests.go
@@ -117,7 +117,18 @@ func (m Manager) sendRequest(memberID *id.ID, request []byte) ([]id.Round, error
 		MessageType: message.GroupCreationRequest,
 	}
 
-	rounds, _, _, err := m.net.SendE2E(sendMsg, params.GetDefaultE2E(), nil)
+
+	recipent, err := m.store.E2e().GetPartner(memberID)
+	if err!=nil{
+		return nil, errors.WithMessagef(err,"Failed to send request to %s " +
+			"because e2e relationship could not be found", memberID)
+	}
+
+	p := params.GetDefaultE2E()
+	p.IdentityPreimage = recipent.GetGroupRequestPreimage()
+	p.DebugTag = "group.Request"
+
+	rounds, _, _, err := m.net.SendE2E(sendMsg, p, nil)
 	if err != nil {
 		return nil, errors.Errorf(sendE2eErr, memberID, err)
 	}
diff --git a/groupChat/sendRequests_test.go b/groupChat/sendRequests_test.go
index 9c22c13fed92f6b986a6f71db63d6f9aea3a230a..9f2b9c19f2eb2c65dd51d3f66e3dc512cfe991b3 100644
--- a/groupChat/sendRequests_test.go
+++ b/groupChat/sendRequests_test.go
@@ -9,8 +9,13 @@ package groupChat
 
 import (
 	"fmt"
+	"github.com/cloudflare/circl/dh/sidh"
 	"github.com/golang/protobuf/proto"
 	"gitlab.com/elixxir/client/interfaces/message"
+	"gitlab.com/elixxir/client/interfaces/params"
+	util "gitlab.com/elixxir/client/storage/utility"
+	"gitlab.com/elixxir/crypto/diffieHellman"
+	"gitlab.com/xx_network/crypto/csprng"
 	"gitlab.com/xx_network/primitives/id"
 	"math/rand"
 	"reflect"
@@ -33,11 +38,29 @@ func TestManager_ResendRequest(t *testing.T) {
 		Created:     g.Created.UnixNano(),
 	}
 
+	for i := range g.Members{
+		grp := m.store.E2e().GetGroup()
+		dhKey := grp.NewInt(int64(i + 42))
+		pubKey := diffieHellman.GeneratePublicKey(dhKey, grp)
+		p := params.GetDefaultE2ESessionParams()
+		rng := csprng.NewSystemRNG()
+		_, mySidhPriv := util.GenerateSIDHKeyPair(
+			sidh.KeyVariantSidhA, rng)
+		theirSidhPub, _ := util.GenerateSIDHKeyPair(
+			sidh.KeyVariantSidhB, rng)
+		err := m.store.E2e().AddPartner(g.Members[i].ID, pubKey, dhKey,
+			mySidhPriv, theirSidhPub, p, p)
+		if err != nil {
+			t.Errorf("Failed to add partner #%d %s: %+v", i, g.Members[i].ID, err)
+		}
+	}
+
 	_, status, err := m.ResendRequest(g.ID)
 	if err != nil {
 		t.Errorf("ResendRequest() returned an error: %+v", err)
 	}
 
+
 	if status != AllSent {
 		t.Errorf("ResendRequest() failed to return the expected status."+
 			"\nexpected: %s\nreceived: %s", AllSent, status)
@@ -112,6 +135,23 @@ func TestManager_sendRequests(t *testing.T) {
 		Created:     g.Created.UnixNano(),
 	}
 
+	for i := range g.Members{
+		grp := m.store.E2e().GetGroup()
+		dhKey := grp.NewInt(int64(i + 42))
+		pubKey := diffieHellman.GeneratePublicKey(dhKey, grp)
+		p := params.GetDefaultE2ESessionParams()
+		rng := csprng.NewSystemRNG()
+		_, mySidhPriv := util.GenerateSIDHKeyPair(
+			sidh.KeyVariantSidhA, rng)
+		theirSidhPub, _ := util.GenerateSIDHKeyPair(
+			sidh.KeyVariantSidhB, rng)
+		err := m.store.E2e().AddPartner(g.Members[i].ID, pubKey, dhKey,
+			mySidhPriv, theirSidhPub, p, p)
+		if err != nil {
+			t.Errorf("Failed to add partner #%d %s: %+v", i, g.Members[i].ID, err)
+		}
+	}
+
 	_, status, err := m.sendRequests(g)
 	if err != nil {
 		t.Errorf("sendRequests() returned an error: %+v", err)
@@ -195,6 +235,23 @@ func TestManager_sendRequests_SendPartialSent(t *testing.T) {
 	expectedErr := fmt.Sprintf(sendRequestPartialErr, (len(g.Members)-1)/2,
 		len(g.Members)-1, "")
 
+	for i := range g.Members{
+		grp := m.store.E2e().GetGroup()
+		dhKey := grp.NewInt(int64(i + 42))
+		pubKey := diffieHellman.GeneratePublicKey(dhKey, grp)
+		p := params.GetDefaultE2ESessionParams()
+		rng := csprng.NewSystemRNG()
+		_, mySidhPriv := util.GenerateSIDHKeyPair(
+			sidh.KeyVariantSidhA, rng)
+		theirSidhPub, _ := util.GenerateSIDHKeyPair(
+			sidh.KeyVariantSidhB, rng)
+		err := m.store.E2e().AddPartner(g.Members[i].ID, pubKey, dhKey,
+			mySidhPriv, theirSidhPub, p, p)
+		if err != nil {
+			t.Errorf("Failed to add partner #%d %s: %+v", i, g.Members[i].ID, err)
+		}
+	}
+
 	_, status, err := m.sendRequests(g)
 	if err == nil || !strings.Contains(err.Error(), expectedErr) {
 		t.Errorf("sendRequests() failed to return the expected error."+
@@ -217,6 +274,23 @@ func TestManager_sendRequest(t *testing.T) {
 	prng := rand.New(rand.NewSource(42))
 	m, g := newTestManagerWithStore(prng, 10, 0, nil, nil, t)
 
+	for i := range g.Members{
+		grp := m.store.E2e().GetGroup()
+		dhKey := grp.NewInt(int64(i + 42))
+		pubKey := diffieHellman.GeneratePublicKey(dhKey, grp)
+		p := params.GetDefaultE2ESessionParams()
+		rng := csprng.NewSystemRNG()
+		_, mySidhPriv := util.GenerateSIDHKeyPair(
+			sidh.KeyVariantSidhA, rng)
+		theirSidhPub, _ := util.GenerateSIDHKeyPair(
+			sidh.KeyVariantSidhB, rng)
+		err := m.store.E2e().AddPartner(g.Members[i].ID, pubKey, dhKey,
+			mySidhPriv, theirSidhPub, p, p)
+		if err != nil {
+			t.Errorf("Failed to add partner #%d %s: %+v", i, g.Members[i].ID, err)
+		}
+	}
+
 	expected := message.Send{
 		Recipient:   g.Members[0].ID,
 		Payload:     []byte("request message"),
@@ -241,7 +315,25 @@ func TestManager_sendRequest_SendE2eError(t *testing.T) {
 	m, _ := newTestManagerWithStore(prng, 10, 1, nil, nil, t)
 	expectedErr := strings.SplitN(sendE2eErr, "%", 2)[0]
 
-	_, err := m.sendRequest(id.NewIdFromString("memberID", id.User, t), nil)
+	recipientID := id.NewIdFromString("memberID", id.User, t)
+
+	grp := m.store.E2e().GetGroup()
+	dhKey := grp.NewInt(int64(42))
+	pubKey := diffieHellman.GeneratePublicKey(dhKey, grp)
+	p := params.GetDefaultE2ESessionParams()
+	rng := csprng.NewSystemRNG()
+	_, mySidhPriv := util.GenerateSIDHKeyPair(
+		sidh.KeyVariantSidhA, rng)
+	theirSidhPub, _ := util.GenerateSIDHKeyPair(
+		sidh.KeyVariantSidhB, rng)
+	err := m.store.E2e().AddPartner(recipientID, pubKey, dhKey,
+		mySidhPriv, theirSidhPub, p, p)
+	if err != nil {
+		t.Errorf("Failed to add partner %s: %+v", recipientID, err)
+	}
+
+
+	_, err = m.sendRequest(recipientID, nil)
 	if err == nil || !strings.Contains(err.Error(), expectedErr) {
 		t.Errorf("sendRequest() failed to return the expected error."+
 			"\nexpected: %s\nreceived: %+v", expectedErr, err)
diff --git a/groupChat/send_test.go b/groupChat/send_test.go
index 8fd29ba8c60719ef6f0ca2eff8c1d39049ef1c42..2d6940c3a69dbe3b894481e738d6fbb1d7974e45 100644
--- a/groupChat/send_test.go
+++ b/groupChat/send_test.go
@@ -30,7 +30,7 @@ func TestManager_Send(t *testing.T) {
 	messageBytes := []byte("Group chat message.")
 	sender := m.gs.GetUser().DeepCopy()
 
-	_, _, err := m.Send(g.ID, messageBytes)
+	_, _, _, err := m.Send(g.ID, messageBytes)
 	if err != nil {
 		t.Errorf("Send() returned an error: %+v", err)
 	}
@@ -110,7 +110,7 @@ func TestManager_Send_CmixMessageError(t *testing.T) {
 	expectedErr := strings.SplitN(newCmixMsgErr, "%", 2)[0]
 
 	// Send message
-	_, _, err := m.Send(g.ID, make([]byte, 400))
+	_, _, _, err := m.Send(g.ID, make([]byte, 400))
 	if err == nil || !strings.Contains(err.Error(), expectedErr) {
 		t.Errorf("Send() failed to return the expected error."+
 			"\nexpected: %s\nreceived: %+v", expectedErr, err)
@@ -125,7 +125,7 @@ func TestManager_Send_SendManyCMIXError(t *testing.T) {
 	expectedErr := strings.SplitN(sendManyCmixErr, "%", 2)[0]
 
 	// Send message
-	_, _, err := m.Send(g.ID, []byte("message"))
+	_, _, _, err := m.Send(g.ID, []byte("message"))
 	if err == nil || !strings.Contains(err.Error(), expectedErr) {
 		t.Errorf("Send() failed to return the expected error."+
 			"\nexpected: %s\nreceived: %+v", expectedErr, err)
@@ -145,7 +145,7 @@ func TestManager_createMessages(t *testing.T) {
 
 	testMsg := []byte("Test group message.")
 	sender := m.gs.GetUser()
-	messages, err := m.createMessages(g.ID, testMsg, netTime.Now())
+	messages, _, err := m.createMessages(g.ID, testMsg, netTime.Now())
 	if err != nil {
 		t.Errorf("createMessages() returned an error: %+v", err)
 	}
@@ -205,7 +205,7 @@ func TestManager_createMessages_InvalidGroupIdError(t *testing.T) {
 	m, _ := newTestManagerWithStore(prng, 10, 0, nil, nil, t)
 
 	// Read message and make sure the error is expected
-	_, err := m.createMessages(
+	_, _, err := m.createMessages(
 		id.NewIdFromString("invalidID", id.Group, t), nil, time.Time{})
 	if err == nil || !strings.Contains(err.Error(), expectedErr) {
 		t.Errorf("createMessages() did not return the expected error."+
diff --git a/interfaces/auth.go b/interfaces/auth.go
index bb3cae74d0a37f0fda2c34e8ce0623af6f8a789f..4ce22fba789b45a5616a640f0409a054f6a05ecb 100644
--- a/interfaces/auth.go
+++ b/interfaces/auth.go
@@ -12,7 +12,7 @@ import (
 	"gitlab.com/xx_network/primitives/id"
 )
 
-type RequestCallback func(requestor contact.Contact, message string)
+type RequestCallback func(requestor contact.Contact)
 type ConfirmCallback func(partner contact.Contact)
 
 type Auth interface {
@@ -42,4 +42,6 @@ type Auth interface {
 	AddSpecificConfirmCallback(id *id.ID, cb ConfirmCallback)
 	// Removes a specific callback to be used on auth confirm.
 	RemoveSpecificConfirmCallback(id *id.ID)
+	//Replays all pending received requests over tha callbacks
+	ReplayRequests()
 }
diff --git a/interfaces/message/type.go b/interfaces/message/type.go
index 4a5115a162e562e04c6e2577d7a80d64589d8b4f..4c444628f46b06a80d12d43485f3c8bf2243ed45 100644
--- a/interfaces/message/type.go
+++ b/interfaces/message/type.go
@@ -23,8 +23,8 @@ const (
 	// non Cmix messages will be ignored
 	Raw Type = 1
 
-	//General text message, contains human readable text
-	Text Type = 2
+	//Type of message sent by the xx messenger
+	XxMessage Type = 2
 
 	/*User Discovery message types*/
 	//Message structures defined in the UD package
diff --git a/interfaces/params/CMIX.go b/interfaces/params/CMIX.go
index 5e772d0360a94350ff6b99b6db33d22934e5410d..db2bfa655906d3b07d98ca54ea1f1539cd2fb9ed 100644
--- a/interfaces/params/CMIX.go
+++ b/interfaces/params/CMIX.go
@@ -27,6 +27,10 @@ type CMIX struct {
 	// an alternate identity preimage to use on send. If not set, the default
 	// for the sending identity will be used
 	IdentityPreimage []byte
+
+	// Tag which prints with sending logs to help localize the source
+	// All internal sends are tagged, so the default tag is "External"
+	DebugTag string
 }
 
 func GetDefaultCMIX() CMIX {
@@ -35,6 +39,7 @@ func GetDefaultCMIX() CMIX {
 		Timeout:     25 * time.Second,
 		RetryDelay:  1 * time.Second,
 		SendTimeout: 3 * time.Second,
+		DebugTag: 	 "External",
 	}
 }
 
diff --git a/interfaces/params/message.go b/interfaces/params/message.go
index fbf9779829b939145cf7bc1277fa79b5617b826a..27a8ebd7d626445cc4c2ace03772d5dbbb6105ed 100644
--- a/interfaces/params/message.go
+++ b/interfaces/params/message.go
@@ -16,6 +16,7 @@ type Messages struct {
 	MessageReceptionWorkerPoolSize uint
 	MaxChecksGarbledMessage        uint
 	GarbledMessageWait             time.Duration
+	RealtimeOnly 					bool
 }
 
 func GetDefaultMessage() Messages {
@@ -24,5 +25,6 @@ func GetDefaultMessage() Messages {
 		MessageReceptionWorkerPoolSize: 4,
 		MaxChecksGarbledMessage:        10,
 		GarbledMessageWait:             15 * time.Minute,
+		RealtimeOnly: 				    false,
 	}
 }
diff --git a/interfaces/params/network.go b/interfaces/params/network.go
index d6f9ef1b01d6f493e8060febdb5e968ddb7f0a5c..7e3b6f4ab3f72f664af8e5b7e9cc5d226950a37f 100644
--- a/interfaces/params/network.go
+++ b/interfaces/params/network.go
@@ -34,6 +34,10 @@ type Network struct {
 	// Determines if the state of every round processed is tracked in ram.
 	// This is very memory intensive and is primarily used for debugging
 	VerboseRoundTracking bool
+	//disables all attempts to pick up dropped or missed messages
+	RealtimeOnly bool
+	// Resends auth requests up the stack if received multiple times
+	ReplayRequests bool
 
 	Rounds
 	Messages
@@ -46,7 +50,7 @@ func GetDefaultNetwork() Network {
 	n := Network{
 		TrackNetworkPeriod:        100 * time.Millisecond,
 		MaxCheckedRounds:          500,
-		RegNodesBufferLen:         500,
+		RegNodesBufferLen:         1000,
 		NetworkHealthTimeout:      30 * time.Second,
 		E2EParams:                 GetDefaultE2ESessionParams(),
 		ParallelNodeRegistrations: 20,
@@ -54,6 +58,8 @@ func GetDefaultNetwork() Network {
 		FastPolling:               true,
 		BlacklistedNodes:          make([]string, 0),
 		VerboseRoundTracking:      false,
+		RealtimeOnly:              false,
+		ReplayRequests:            true,
 	}
 	n.Rounds = GetDefaultRounds()
 	n.Messages = GetDefaultMessage()
@@ -65,6 +71,13 @@ func (n Network) Marshal() ([]byte, error) {
 	return json.Marshal(n)
 }
 
+func (n Network) SetRealtimeOnlyAll()Network {
+	n.RealtimeOnly = true
+	n.Rounds.RealtimeOnly = true
+	n.Messages.RealtimeOnly = true
+	return n
+}
+
 // Obtain default Network parameters, or override with given parameters if set
 func GetNetworkParameters(params string) (Network, error) {
 	p := GetDefaultNetwork()
diff --git a/interfaces/params/rounds.go b/interfaces/params/rounds.go
index 40bd41bfbd80a6cef2ecf30d8a1c645782f7b305..75e4270987ab2b5d8ea1c4edb6e49efb9f32a863 100644
--- a/interfaces/params/rounds.go
+++ b/interfaces/params/rounds.go
@@ -43,6 +43,9 @@ type Rounds struct {
 	// Duration to wait before sending on a round times out and a new round is
 	// tried
 	SendTimeout time.Duration
+
+	//disables all attempts to pick up dropped or missed messages
+	RealtimeOnly bool
 }
 
 func GetDefaultRounds() Rounds {
@@ -58,5 +61,6 @@ func GetDefaultRounds() Rounds {
 		UncheckRoundPeriod:         20 * time.Second,
 		ForceMessagePickupRetry:    false,
 		SendTimeout:                1 * time.Second,
+		RealtimeOnly: false,
 	}
 }
diff --git a/interfaces/preimage/types.go b/interfaces/preimage/types.go
index 0b87ef34c47bc1574a94813ebb77ebb0155a67d8..bd6226e623594561ef0338210af8824c1b789f27 100644
--- a/interfaces/preimage/types.go
+++ b/interfaces/preimage/types.go
@@ -8,4 +8,5 @@ const (
 	E2e     = "e2e"
 	Group   = "group"
 	EndFT   = "endFT"
+	GroupRq = "groupRq"
 )
diff --git a/keyExchange/confirm.go b/keyExchange/confirm.go
index d754acc1da040efed8e55227d623c0cc5c414342..4c21f77db86638f24a50fcf65b65001e71e081c9 100644
--- a/keyExchange/confirm.go
+++ b/keyExchange/confirm.go
@@ -61,7 +61,7 @@ func handleConfirm(sess *storage.Session, confirmation message.Receive) {
 	confirmedSession := partner.GetSendSession(confimedSessionID)
 	if confirmedSession == nil {
 		jww.ERROR.Printf("[REKEY] Failed to find confirmed session %s from "+
-			"partner %s: %s", confimedSessionID, confirmation.Sender, err)
+			"partner %s", confimedSessionID, confirmation.Sender)
 		return
 	}
 
diff --git a/keyExchange/rekey.go b/keyExchange/rekey.go
index 773a8544c5a1d614f8b5ab1bbaab14035d3488f3..04bb31413af965f621884a01ab0f557c632ec504 100644
--- a/keyExchange/rekey.go
+++ b/keyExchange/rekey.go
@@ -8,6 +8,7 @@
 package keyExchange
 
 import (
+	"fmt"
 	"github.com/golang/protobuf/proto"
 	"github.com/pkg/errors"
 	jww "github.com/spf13/jwalterweatherman"
@@ -24,7 +25,6 @@ import (
 	"gitlab.com/elixxir/crypto/diffieHellman"
 	"gitlab.com/elixxir/primitives/states"
 	"time"
-	"fmt"
 )
 
 func CheckKeyExchanges(instance *network.Instance, sendE2E interfaces.SendE2E,
@@ -121,6 +121,7 @@ func negotiate(instance *network.Instance, sendE2E interfaces.SendE2E,
 	e2eParams := params.GetDefaultE2E()
 	e2eParams.Type = params.KeyExchange
 	e2eParams.IdentityPreimage = rekeyPreimage
+	e2eParams.DebugTag = "kx.Request"
 
 	rounds, msgID, _, err := sendE2E(m, e2eParams, stop)
 	// If the send fails, returns the error so it can be handled. The caller
diff --git a/keyExchange/trigger.go b/keyExchange/trigger.go
index 9e3bf66a3a7b596478f35be48cb3e88415c184c6..fcdb71aa93fc535ac879696ebe36a86d6045de87 100644
--- a/keyExchange/trigger.go
+++ b/keyExchange/trigger.go
@@ -125,6 +125,7 @@ func handleTrigger(sess *storage.Session, net interfaces.NetworkManager,
 	//send the message under the key exchange
 	e2eParams := params.GetDefaultE2E()
 	e2eParams.IdentityPreimage = partner.GetSilentPreimage()
+	e2eParams.DebugTag = "kx.Confirm"
 
 	// store in critical messages buffer first to ensure it is resent if the
 	// send fails
diff --git a/network/ephemeral/tracker.go b/network/ephemeral/tracker.go
index 5c2a23b4f750b372bd237895c7d7b664707ccd2e..e848c23c473b90170b740fc31cd5734bf746e7ad 100644
--- a/network/ephemeral/tracker.go
+++ b/network/ephemeral/tracker.go
@@ -78,7 +78,7 @@ func track(session *storage.Session, addrSpace *AddressSpace, ourId *id.ID, stop
 
 		// Generates the IDs since the last track
 		protoIds, err := ephemeral.GetIdsByRange(
-			ourId, uint(addressSize), lastCheck, now.Sub(lastCheck))
+			ourId, uint(addressSize), lastCheck, now.Add(validityGracePeriod).Sub(lastCheck))
 
 		jww.DEBUG.Printf("Now: %s, LastCheck: %s, Different: %s",
 			now, lastCheck, now.Sub(lastCheck))
diff --git a/network/ephemeral/tracker_test.go b/network/ephemeral/tracker_test.go
index 47b8f034766c45c003f9f4997a599cf1ad22cfbd..4a946a3fc66c31071cf6b8ba31bc8e913ef73bfd 100644
--- a/network/ephemeral/tracker_test.go
+++ b/network/ephemeral/tracker_test.go
@@ -107,7 +107,7 @@ func setupInstance(instance interfaces.NetworkManager) error {
 	if err = signature.SignRsa(ri, testCert); err != nil {
 		return errors.Errorf("Failed to sign round info: %+v", err)
 	}
-	if err = instance.GetInstance().RoundUpdate(ri); err != nil {
+	if _, err = instance.GetInstance().RoundUpdate(ri); err != nil {
 		return errors.Errorf("Failed to RoundUpdate from from file: %+v", err)
 	}
 
@@ -117,7 +117,7 @@ func setupInstance(instance interfaces.NetworkManager) error {
 	if err = signature.SignRsa(ri, testCert); err != nil {
 		return errors.Errorf("Failed to sign round info: %+v", err)
 	}
-	if err = instance.GetInstance().RoundUpdate(ri); err != nil {
+	if _, err = instance.GetInstance().RoundUpdate(ri); err != nil {
 		return errors.Errorf("Failed to RoundUpdate from from file: %v", err)
 	}
 
diff --git a/network/follow.go b/network/follow.go
index e25f5ff6cd43857aa7e5fd4cd5856f2a2eaa5172..6f98a33b50127d78f10e703b1c75081d02357d0f 100644
--- a/network/follow.go
+++ b/network/follow.go
@@ -41,7 +41,14 @@ import (
 	"time"
 )
 
-const debugTrackPeriod = 1 * time.Minute
+const (
+	debugTrackPeriod = 1 * time.Minute
+
+	// Estimate the number of rounds per second in the network. Will need updated someday
+	// in order to correctly determine how far back to search rounds for messages
+	// as the network continues to grow, otherwise message drops occur.
+	estimatedRoundsPerSecond = 5
+)
 
 //comms interface makes testing easier
 type followNetworkComms interface {
@@ -143,7 +150,7 @@ func (m *manager) follow(report interfaces.ClientErrorReport, rng csprng.Source,
 	result, err := m.GetSender().SendToAny(func(host *connect.Host) (interface{}, error) {
 		jww.DEBUG.Printf("Executing poll for %v(%s) range: %s-%s(%s) from %s",
 			identity.EphId.Int64(), identity.Source, identity.StartValid,
-			identity.EndValid, identity.StartValid.Sub(identity.EndValid), host.GetId())
+			identity.EndValid, identity.EndValid.Sub(identity.StartValid), host.GetId())
 		return comms.SendPoll(host, &pollReq)
 	}, stop)
 
@@ -217,23 +224,12 @@ func (m *manager) follow(report interfaces.ClientErrorReport, rng csprng.Source,
 	}
 
 	// Update the address space size
-	// todo: this is a fix for incompatibility with the live network
-	// remove once the live network has been pushed to
 	if len(m.Instance.GetPartialNdf().Get().AddressSpace) != 0 {
 		m.addrSpace.Update(m.Instance.GetPartialNdf().Get().AddressSpace[0].Size)
-	} else {
-		m.addrSpace.Update(18)
 	}
 
-	// NOTE: this updates rounds and updates the tracking of the health of the
-	// network
+	// NOTE: this updates rounds and updates the tracking of the health of the network
 	if pollResp.Updates != nil {
-		err = m.Instance.RoundUpdates(pollResp.Updates)
-		if err != nil {
-			jww.ERROR.Printf("%+v", err)
-			return
-		}
-
 		// TODO: ClientErr needs to know the source of the error and it doesn't yet
 		// Iterate over ClientErrors for each RoundUpdate
 		for _, update := range pollResp.Updates {
@@ -259,19 +255,11 @@ func (m *manager) follow(report interfaces.ClientErrorReport, rng csprng.Source,
 						return
 					}
 
+					// Mutate the update to indicate failure due to a ClientError
 					// FIXME: Should be able to trigger proper type of round event
 					// FIXME: without mutating the RoundInfo. Signature also needs verified
 					// FIXME: before keys are deleted
 					update.State = uint32(states.FAILED)
-					rnd, err := m.Instance.GetWrappedRound(id.Round(update.ID))
-					if err != nil {
-						jww.ERROR.Printf("Failed to report client error: "+
-							"Could not get round for event triggering: "+
-							"Unable to get round %d from instance: %+v",
-							id.Round(update.ID), err)
-						break
-					}
-					m.Instance.GetRoundEvents().TriggerRoundEvent(rnd)
 
 					// delete all existing keys and trigger a re-registration with the relevant Node
 					m.Session.Cmix().Remove(nid)
@@ -280,6 +268,13 @@ func (m *manager) follow(report interfaces.ClientErrorReport, rng csprng.Source,
 			}
 		}
 
+		// Trigger RoundEvents for all polled updates, including modified rounds with ClientErrors
+		err = m.Instance.RoundUpdates(pollResp.Updates)
+		if err != nil {
+			jww.ERROR.Printf("%+v", err)
+			return
+		}
+
 		newestTS := uint64(0)
 		for i := 0; i < len(pollResp.Updates[len(pollResp.Updates)-1].Timestamps); i++ {
 			if pollResp.Updates[len(pollResp.Updates)-1].Timestamps[i] != 0 {
@@ -308,18 +303,9 @@ func (m *manager) follow(report interfaces.ClientErrorReport, rng csprng.Source,
 		return
 	}
 
-	//get the range fo filters which are valid for the identity
-	filtersStart, filtersEnd, outOfBounds := rounds.ValidFilterRange(identity, pollResp.Filters)
-
-	//check if there are any valid filters returned
-	if outOfBounds {
-		jww.WARN.Printf("No filters processed, none in valid range")
-		return
-	}
-
 	//prepare the filter objects for processing
-	filterList := make([]*rounds.RemoteFilter, 0, filtersEnd-filtersStart)
-	for i := filtersStart; i < filtersEnd; i++ {
+	filterList := make([]*rounds.RemoteFilter, 0, len(pollResp.Filters.Filters))
+	for i := range pollResp.Filters.Filters {
 		if len(pollResp.Filters.Filters[i].Filter) != 0 {
 			filterList = append(filterList, rounds.NewRemoteFilter(pollResp.Filters.Filters[i]))
 		}
@@ -340,25 +326,42 @@ func (m *manager) follow(report interfaces.ClientErrorReport, rng csprng.Source,
 	// tracked round if it is behind
 	earliestTrackedRound := id.Round(pollResp.EarliestRound)
 	m.SetFakeEarliestRound(earliestTrackedRound)
-	updated, old, _ := identity.ER.Set(earliestTrackedRound)
+	updatedEarliestRound, old, _ := identity.ER.Set(earliestTrackedRound)
+
+	// If there was no registered rounds for the identity
 	if old == 0 {
-		if gwRoundsState.GetLastChecked() > id.Round(m.param.KnownRoundsThreshold) {
-			updated = gwRoundsState.GetLastChecked() - id.Round(m.param.KnownRoundsThreshold)
+		lastCheckedRound := gwRoundsState.GetLastChecked()
+		// Approximate the earliest possible round that messages could be received on this ID
+		// by using an estimate of how many rounds the network runs per second
+		roundsDelta := uint(time.Now().Sub(identity.StartValid) / time.Second * estimatedRoundsPerSecond)
+		if roundsDelta < m.param.KnownRoundsThreshold {
+			roundsDelta = m.param.KnownRoundsThreshold
+		}
+		if id.Round(roundsDelta) > lastCheckedRound {
+			// Handles edge case for new networks to prevent starting at negative rounds
+			updatedEarliestRound = 1
 		} else {
-			updated = 1
+			updatedEarliestRound = lastCheckedRound - id.Round(roundsDelta)
+			earliestFilterRound := filterList[0].FirstRound() // Length of filterList always > 0
+			// If the network appears to be moving faster than our estimate, causing
+			// earliestFilterRound to be lower, we will instead use the earliestFilterRound
+			// which will ensure messages are not dropped as long as contacted gateway has all data
+			if updatedEarliestRound > earliestFilterRound {
+				updatedEarliestRound = earliestFilterRound
+			}
 		}
-		identity.ER.Set(updated)
+		identity.ER.Set(updatedEarliestRound)
 	}
 
 	// loop through all rounds the client does not know about and the gateway
 	// does, checking the bloom filter for the user to see if there are
 	// messages for the user (bloom not implemented yet)
 	//threshold is the earliest round that will not be excluded from earliest remaining
-	earliestRemaining, roundsWithMessages, roundsUnknown := gwRoundsState.RangeUnchecked(updated,
+	earliestRemaining, roundsWithMessages, roundsUnknown := gwRoundsState.RangeUnchecked(updatedEarliestRound,
 		m.param.KnownRoundsThreshold, roundChecker)
 	jww.DEBUG.Printf("Processed RangeUnchecked, Oldest: %d, firstUnchecked: %d, "+
 		"last Checked: %d, threshold: %d, NewEarliestRemaning: %d, NumWithMessages: %d, "+
-		"NumUnknown: %d", updated, gwRoundsState.GetFirstUnchecked(), gwRoundsState.GetLastChecked(),
+		"NumUnknown: %d", updatedEarliestRound, gwRoundsState.GetFirstUnchecked(), gwRoundsState.GetLastChecked(),
 		m.param.KnownRoundsThreshold, earliestRemaining, len(roundsWithMessages), len(roundsUnknown))
 
 	_, _, changed := identity.ER.Set(earliestRemaining)
@@ -367,12 +370,17 @@ func (m *manager) follow(report interfaces.ClientErrorReport, rng csprng.Source,
 		jww.DEBUG.Printf("New Earliest Remaining: %d, Gateways last checked: %d", earliestRemaining, gwRoundsState.GetLastChecked())
 	}
 
-	roundsWithMessages2 := identity.UR.Iterate(func(rid id.Round) bool {
-		if gwRoundsState.Checked(rid) {
-			return rounds.Checker(rid, filterList, identity.CR)
-		}
-		return false
-	}, roundsUnknown, abandon)
+	var roundsWithMessages2 []id.Round
+
+	if !m.param.RealtimeOnly{
+		roundsWithMessages2 = identity.UR.Iterate(func(rid id.Round) bool {
+			if gwRoundsState.Checked(rid) {
+				return rounds.Checker(rid, filterList, identity.CR)
+			}
+			return false
+		}, roundsUnknown, abandon)
+	}
+
 
 	for _, rid := range roundsWithMessages {
 		//denote that the round has been looked at in the tracking store
@@ -393,8 +401,8 @@ func (m *manager) follow(report interfaces.ClientErrorReport, rng csprng.Source,
 	}
 
 	if m.verboseRounds != nil {
-		trackingStart := updated
-		if uint(earliestRemaining-updated) > m.param.KnownRoundsThreshold {
+		trackingStart := updatedEarliestRound
+		if uint(earliestRemaining-updatedEarliestRound) > m.param.KnownRoundsThreshold {
 			trackingStart = earliestRemaining - id.Round(m.param.KnownRoundsThreshold)
 		}
 		jww.DEBUG.Printf("Rounds tracked: %v to %v", trackingStart, earliestRemaining)
diff --git a/network/gateway/hostPool.go b/network/gateway/hostPool.go
index 5bb0b82044058f8c51f055482cdd00a1b6bf37d9..43ae1481b9dcb01f732b12e6d6234fbc29acf5ff 100644
--- a/network/gateway/hostPool.go
+++ b/network/gateway/hostPool.go
@@ -578,7 +578,7 @@ func (h *HostPool) updateConns() error {
 		return errors.Errorf("Unable to convert new NDF to set: %+v", err)
 	}
 
-	// Filter out gateway IDs
+	// Filter out unwanted gateway IDs
 	newMap = h.getFilter()(newMap, h.ndf)
 
 	// Keep track of the old NDF set
@@ -588,7 +588,7 @@ func (h *HostPool) updateConns() error {
 
 	// Handle adding Gateways
 	for gwId, ndfIdx := range newMap {
-		if _, ok := oldMap[gwId]; !ok {
+		if _, ok := oldMap[gwId]; !ok && gwId.GetType() == id.Gateway {
 			// If GwId in newMap is not in ndfMap, add the Gateway
 			h.addGateway(gwId.DeepCopy(), ndfIdx)
 		}
@@ -596,7 +596,7 @@ func (h *HostPool) updateConns() error {
 
 	// Handle removing Gateways
 	for gwId := range oldMap {
-		if _, ok := newMap[gwId]; !ok {
+		if _, ok := newMap[gwId]; !ok && gwId.GetType() == id.Gateway {
 			// If GwId in ndfMap is not in newMap, remove the Gateway
 			h.removeGateway(gwId.DeepCopy())
 		}
diff --git a/network/gateway/sender.go b/network/gateway/sender.go
index d09decce432779a13271667851159580dbc6f824..b3b029f369bd91ada4f023f5fd77bb336e682177 100644
--- a/network/gateway/sender.go
+++ b/network/gateway/sender.go
@@ -153,7 +153,7 @@ func (s *Sender) SendToPreferred(targets []*id.ID, sendFunc sendToPreferredFunc,
 		for targetIdx := range proxies {
 			// Return an error if the timeout duration is reached
 			if netTime.Since(startTime) > timeout {
-				return nil, errors.Errorf("iterating over target's procies "+
+				return nil, errors.Errorf("iterating over target's proxies "+
 					"timed out after %s", timeout)
 			}
 
diff --git a/network/manager.go b/network/manager.go
index c968102e2f5b04269c123c5d1b624ae7a865678a..bba264c6b63263a196d4bfc135f1db42649948ec 100644
--- a/network/manager.go
+++ b/network/manager.go
@@ -105,6 +105,7 @@ func NewManager(session *storage.Session, switchboard *switchboard.Switchboard,
 		events:        events,
 		earliestRound: &earliest,
 	}
+	m.addrSpace.Update(18)
 
 	if params.VerboseRoundTracking {
 		m.verboseRounds = NewRoundTracker()
diff --git a/network/message/garbled.go b/network/message/garbled.go
index 9388f4dcfb2c42f3f9c106cc949c893207451ced..b2449ca9f0d79c8d1b5cdd7a8e9cd9db1ae2863f 100644
--- a/network/message/garbled.go
+++ b/network/message/garbled.go
@@ -45,6 +45,7 @@ func (m *Manager) processGarbledMessages(stop *stoppable.Single) {
 			stop.ToStopped()
 			return
 		case <-m.triggerGarbled:
+			jww.INFO.Printf("[GARBLE] Checking Garbled messages")
 			m.handleGarbledMessages()
 		}
 	}
@@ -57,65 +58,72 @@ func (m *Manager) handleGarbledMessages() {
 	var failedMsgs []format.Message
 	//try to decrypt every garbled message, excising those who's counts are too high
 	for grbldMsg, count, timestamp, has := garbledMsgs.Next(); has; grbldMsg, count, timestamp, has = garbledMsgs.Next() {
-		fingerprint := grbldMsg.GetKeyFP()
-		// Check if the key is there, process it if it is
-		if key, isE2E := e2eKv.PopKey(fingerprint); isE2E {
-			jww.INFO.Printf("[GARBLE] Check E2E for %s, KEYFP: %s",
-				grbldMsg.Digest(), grbldMsg.GetKeyFP())
-			// Decrypt encrypted message
-			msg, err := key.Decrypt(grbldMsg)
-			if err == nil {
-				// get the sender
-				sender := key.GetSession().GetPartner()
-				//remove from the buffer if decryption is successful
-				garbledMsgs.Remove(grbldMsg)
+		//if it exists, check against all in the list
+		grbldContents := grbldMsg.GetContents()
+		identity := m.Session.GetUser().ReceptionID
+		_, forMe, _ := m.Session.GetEdge().Check(identity, grbldMsg.GetIdentityFP(), grbldContents)
+		if forMe {
+			fingerprint := grbldMsg.GetKeyFP()
+			// Check if the key is there, process it if it is
+			if key, isE2E := e2eKv.PopKey(fingerprint); isE2E {
+				jww.INFO.Printf("[GARBLE] Check E2E for %s, KEYFP: %s",
+					grbldMsg.Digest(), grbldMsg.GetKeyFP())
+				// Decrypt encrypted message
+				msg, err := key.Decrypt(grbldMsg)
+				if err == nil {
+					// get the sender
+					sender := key.GetSession().GetPartner()
+					//remove from the buffer if decryption is successful
+					garbledMsgs.Remove(grbldMsg)
 
-				jww.INFO.Printf("[GARBLE] message decoded as E2E from "+
-					"%s, msgDigest: %s", sender, grbldMsg.Digest())
+					jww.INFO.Printf("[GARBLE] message decoded as E2E from "+
+						"%s, msgDigest: %s", sender, grbldMsg.Digest())
 
-				//handle the successfully decrypted message
-				xxMsg, ok := m.partitioner.HandlePartition(sender, message.E2E,
-					msg.GetContents(),
-					key.GetSession().GetRelationshipFingerprint())
-				if ok {
-					m.Switchboard.Speak(xxMsg)
-					continue
-				}
-			}
-		} else {
-			// todo: figure out how to get the ephermal reception id in here.
-			// we have the raw data, but do not know what address space was
-			// used int he round
-			// todo: figure out how to get the round id, the recipient id, and the round timestamp
-			/*
-				ephid, err := ephemeral.Marshal(garbledMsg.GetEphemeralRID())
-				if err!=nil{
-					jww.WARN.Printf("failed to get the ephemeral id for a garbled " +
-						"message, clearing the message: %+v", err)
-					garbledMsgs.Remove(garbledMsg)
-					continue
+					//handle the successfully decrypted message
+					xxMsg, ok := m.partitioner.HandlePartition(sender, message.E2E,
+						msg.GetContents(),
+						key.GetSession().GetRelationshipFingerprint())
+					if ok {
+						m.Switchboard.Speak(xxMsg)
+						continue
+					}
 				}
+			} else {
+				// todo: figure out how to get the ephermal reception id in here.
+				// we have the raw data, but do not know what address space was
+				// used int he round
+				// todo: figure out how to get the round id, the recipient id, and the round timestamp
+				/*
+					ephid, err := ephemeral.Marshal(garbledMsg.GetEphemeralRID())
+					if err!=nil{
+						jww.WARN.Printf("failed to get the ephemeral id for a garbled " +
+							"message, clearing the message: %+v", err)
+						garbledMsgs.Remove(garbledMsg)
+						continue
+					}
 
-				ephid.Clear(m.)*/
+					ephid.Clear(m.)*/
 
-			raw := message.Receive{
-				Payload:        grbldMsg.Marshal(),
-				MessageType:    message.Raw,
-				Sender:         &id.ID{},
-				EphemeralID:    ephemeral.Id{},
-				Timestamp:      time.Time{},
-				Encryption:     message.None,
-				RecipientID:    &id.ID{},
-				RoundId:        0,
-				RoundTimestamp: time.Time{},
+				raw := message.Receive{
+					Payload:        grbldMsg.Marshal(),
+					MessageType:    message.Raw,
+					Sender:         &id.ID{},
+					EphemeralID:    ephemeral.Id{},
+					Timestamp:      time.Time{},
+					Encryption:     message.None,
+					RecipientID:    &id.ID{},
+					RoundId:        0,
+					RoundTimestamp: time.Time{},
+				}
+				im := fmt.Sprintf("[GARBLE] RAW Message reprocessed: keyFP: %v, "+
+					"msgDigest: %s", grbldMsg.GetKeyFP(), grbldMsg.Digest())
+				jww.INFO.Print(im)
+				m.Internal.Events.Report(1, "MessageReception", "Garbled", im)
+				m.Session.GetGarbledMessages().Add(grbldMsg)
+				m.Switchboard.Speak(raw)
 			}
-			im := fmt.Sprintf("[GARBLE] RAW Message reprecessed: keyFP: %v, "+
-				"msgDigest: %s", grbldMsg.GetKeyFP(), grbldMsg.Digest())
-			jww.INFO.Print(im)
-			m.Internal.Events.Report(1, "MessageReception", "Garbled", im)
-			m.Session.GetGarbledMessages().Add(grbldMsg)
-			m.Switchboard.Speak(raw)
 		}
+
 		// fail the message if any part of the decryption fails,
 		// unless it is the last attempts and has been in the buffer long
 		// enough, in which case remove it
diff --git a/network/message/garbled_test.go b/network/message/garbled_test.go
index 8656eda570f80f0a7f9333cbaa6f82b6ea80594e..4a0f7eda9cb50fe7e7ed61aee72040d3756edb10 100644
--- a/network/message/garbled_test.go
+++ b/network/message/garbled_test.go
@@ -11,10 +11,12 @@ import (
 	"gitlab.com/elixxir/client/network/message/parse"
 	"gitlab.com/elixxir/client/stoppable"
 	"gitlab.com/elixxir/client/storage"
+	"gitlab.com/elixxir/client/storage/edge"
 	util "gitlab.com/elixxir/client/storage/utility"
 	"gitlab.com/elixxir/client/switchboard"
 	"gitlab.com/elixxir/comms/client"
 	"gitlab.com/elixxir/crypto/fastRNG"
+	"gitlab.com/elixxir/crypto/fingerprint"
 	"gitlab.com/elixxir/primitives/format"
 	"gitlab.com/xx_network/comms/connect"
 	"gitlab.com/xx_network/crypto/csprng"
@@ -104,6 +106,13 @@ func TestManager_CheckGarbledMessages(t *testing.T) {
 		t.FailNow()
 	}
 
+	preimage := edge.Preimage{
+		Data:   []byte{0},
+		Type:   "test",
+		Source: nil,
+	}
+	m.Session.GetEdge().Add(preimage, sess2.GetUser().ReceptionID)
+
 	err = sess2.E2e().AddPartner(sess1.GetUser().TransmissionID,
 		sess1.E2e().GetDHPublicKey(), sess2.E2e().GetDHPrivateKey(),
 		mySIDHPubKey, partnerSIDHPrivKey,
@@ -143,6 +152,7 @@ func TestManager_CheckGarbledMessages(t *testing.T) {
 	copy(fmp.Timestamp, ts)
 	msg.SetContents(fmp.Bytes())
 	encryptedMsg := key.Encrypt(msg)
+	msg.SetIdentityFP(fingerprint.IdentityFP( msg.GetContents(), preimage.Data))
 	i.Session.GetGarbledMessages().Add(encryptedMsg)
 
 	stop := stoppable.NewSingle("stop")
diff --git a/network/message/handler.go b/network/message/handler.go
index b7574f4cf8636b67aaa37ef7330058dd037de2f6..7aa63e6ad85293b57d716d9bdf894736a3fd4bed 100644
--- a/network/message/handler.go
+++ b/network/message/handler.go
@@ -54,23 +54,27 @@ func (m *Manager) handleMessage(ecrMsg format.Message, bundle Bundle, edge *edge
 	var relationshipFingerprint []byte
 
 	//if it exists, check against all in the list
-	has, forMe, _ := m.Session.GetEdge().Check(identity.Source, ecrMsg.GetIdentityFP(), ecrMsg.GetContents())
+	ecrMsgContents := ecrMsg.GetContents()
+	has, forMe, _ := m.Session.GetEdge().Check(identity.Source, ecrMsg.GetIdentityFP(), ecrMsgContents)
 	if !has {
 		jww.INFO.Printf("checking backup %v", preimage.MakeDefault(identity.Source))
 		//if it doesnt exist, check against the default fingerprint for the identity
 		forMe = fingerprint2.CheckIdentityFP(ecrMsg.GetIdentityFP(),
-			ecrMsg.GetContents(), preimage.MakeDefault(identity.Source))
+			ecrMsgContents, preimage.MakeDefault(identity.Source))
 	}
 
 	if !forMe {
 		if jww.GetLogThreshold() == jww.LevelTrace {
-			expectedFP := fingerprint2.IdentityFP(ecrMsg.GetContents(),
+			expectedFP := fingerprint2.IdentityFP(ecrMsgContents,
 				preimage.MakeDefault(identity.Source))
 			jww.TRACE.Printf("Message for %d (%s) failed identity "+
 				"check: %v (expected-default) vs %v (received)", identity.EphId,
 				identity.Source, expectedFP, ecrMsg.GetIdentityFP())
 		}
-
+		im := fmt.Sprintf("Garbled/RAW Message: keyFP: %v, round: %d"+
+			"msgDigest: %s, not determined to be for client", ecrMsg.GetKeyFP(), bundle.Round, ecrMsg.Digest())
+		m.Internal.Events.Report(1, "MessageReception", "Garbled", im)
+		m.Session.GetGarbledMessages().Add(ecrMsg)
 		return
 	}
 
@@ -103,7 +107,7 @@ func (m *Manager) handleMessage(ecrMsg format.Message, bundle Bundle, edge *edge
 		msg = ecrMsg
 		encTy = message.None
 	} else {
-		// if it doesnt match any form of encrypted, hear it as a raw message
+		// if it doesn't match any form of encrypted, hear it as a raw message
 		// and add it to garbled messages to be handled later
 		msg = ecrMsg
 		raw := message.Receive{
@@ -117,7 +121,7 @@ func (m *Manager) handleMessage(ecrMsg format.Message, bundle Bundle, edge *edge
 			RoundId:        id.Round(bundle.RoundInfo.ID),
 			RoundTimestamp: time.Unix(0, int64(bundle.RoundInfo.Timestamps[states.QUEUED])),
 		}
-		im := fmt.Sprintf("Garbled/RAW Message: keyFP: %v, round: %d"+
+		im := fmt.Sprintf("Received message of type Garbled/RAW: keyFP: %v, round: %d, "+
 			"msgDigest: %s", msg.GetKeyFP(), bundle.Round, msg.Digest())
 		jww.INFO.Print(im)
 		m.Internal.Events.Report(1, "MessageReception", "Garbled", im)
@@ -126,14 +130,12 @@ func (m *Manager) handleMessage(ecrMsg format.Message, bundle Bundle, edge *edge
 		return
 	}
 
-
-
 	// Process the decrypted/unencrypted message partition, to see if
 	// we get a full message
 	xxMsg, ok := m.partitioner.HandlePartition(sender, encTy, msg.GetContents(),
 		relationshipFingerprint)
 
-	im := fmt.Sprintf("Received message of ecr type %s and msg type " +
+	im := fmt.Sprintf("Received message of ecr type %s and msg type "+
 		"%d from %s in round %d,msgDigest: %s, keyFP: %v", encTy,
 		xxMsg.MessageType, sender, bundle.Round, msgDigest, msg.GetKeyFP())
 	jww.INFO.Print(im)
diff --git a/network/message/parse/firstMessagePart_test.go b/network/message/parse/firstMessagePart_test.go
index 2411dd4a6896453f08d2fd02ae2dd15946f0f76c..9902790552e45add0d651b5db38e3e277173b39e 100644
--- a/network/message/parse/firstMessagePart_test.go
+++ b/network/message/parse/firstMessagePart_test.go
@@ -36,7 +36,7 @@ var efmp = firstMessagePart{
 // Test that newFirstMessagePart returns a correctly made firstMessagePart
 func TestNewFirstMessagePart(t *testing.T) {
 	fmp := newFirstMessagePart(
-		message.Text,
+		message.XxMessage,
 		1077,
 		2,
 		time.Unix(1609786229, 0).UTC(),
@@ -67,8 +67,8 @@ func TestFirstMessagePartFromBytes(t *testing.T) {
 
 // Test that GetType returns the correct type for a firstMessagePart
 func TestFirstMessagePart_GetType(t *testing.T) {
-	if efmp.GetType() != message.Text {
-		t.Errorf("Got %v, expected %v", efmp.GetType(), message.Text)
+	if efmp.GetType() != message.XxMessage {
+		t.Errorf("Got %v, expected %v", efmp.GetType(), message.XxMessage)
 	}
 }
 
diff --git a/network/message/parse/partition_test.go b/network/message/parse/partition_test.go
index 9351a1e77b7e80dfc74938fefe01203dd4f0b705..7c063f2d222d9171416a7d0366325dd464da7475 100644
--- a/network/message/parse/partition_test.go
+++ b/network/message/parse/partition_test.go
@@ -66,7 +66,7 @@ func TestPartitioner_Partition(t *testing.T) {
 	storeSession := storage.InitTestingSession(t)
 	p := NewPartitioner(len(ipsumTestStr), storeSession)
 
-	_, _, err := p.Partition(&id.DummyUser, message.Text,
+	_, _, err := p.Partition(&id.DummyUser, message.XxMessage,
 		netTime.Now(), []byte(ipsumTestStr))
 	if err != nil {
 		t.Error(err)
@@ -94,7 +94,7 @@ func TestPartitioner_HandleFirstPartition(t *testing.T) {
 	storeSession := storage.InitTestingSession(t)
 	p := NewPartitioner(len(ipsumTestStr), storeSession)
 
-	m := newFirstMessagePart(message.Text, 1107, 1, netTime.Now(), []byte(ipsumTestStr))
+	m := newFirstMessagePart(message.XxMessage, 1107, 1, netTime.Now(), []byte(ipsumTestStr))
 
 	_, _ = p.HandlePartition(
 		&id.DummyUser,
diff --git a/network/message/sendCmix.go b/network/message/sendCmix.go
index 173fb1f8c2bbb87eb56d48472a737aff950300f9..a7db6d51f80e38bad648ccff338dc68409d6cb05 100644
--- a/network/message/sendCmix.go
+++ b/network/message/sendCmix.go
@@ -27,6 +27,8 @@ import (
 	"gitlab.com/xx_network/primitives/id"
 	"gitlab.com/xx_network/primitives/id/ephemeral"
 	"gitlab.com/xx_network/primitives/netTime"
+	"gitlab.com/xx_network/primitives/rateLimiting"
+	"strings"
 	"time"
 )
 
@@ -85,8 +87,8 @@ func sendCmixHelper(sender *gateway.Sender, msg format.Message,
 		attempted = excludedRounds.NewSet()
 	}
 
-	jww.INFO.Printf("Looking for round to send cMix message to %s "+
-		"(msgDigest: %s)", recipient, msg.Digest())
+	jww.INFO.Printf("[SendCMIX-%s]Looking for round to send cMix message to %s "+
+		"(msgDigest: %s)", cmixParams.DebugTag, recipient, msg.Digest())
 
 	stream := rng.GetStream()
 	defer stream.Close()
@@ -98,18 +100,18 @@ func sendCmixHelper(sender *gateway.Sender, msg format.Message,
 
 	for numRoundTries := uint(0); numRoundTries < cmixParams.RoundTries; numRoundTries++ {
 		elapsed := netTime.Since(timeStart)
-		jww.TRACE.Printf("[SendCMIX] try %d, elapsed: %s",
-			numRoundTries, elapsed)
+		jww.TRACE.Printf("[SendCMIX-%s] try %d, elapsed: %s",
+			cmixParams.DebugTag, numRoundTries, elapsed)
 
 		if elapsed > cmixParams.Timeout {
-			jww.INFO.Printf("No rounds to send to %s (msgDigest: %s) "+
-				"were found before timeout %s", recipient, msg.Digest(),
+			jww.INFO.Printf("[SendCMIX-%s] No rounds to send to %s (msgDigest: %s) "+
+				"were found before timeout %s", cmixParams.DebugTag, recipient, msg.Digest(),
 				cmixParams.Timeout)
 			return 0, ephemeral.Id{}, errors.New("Sending cmix message timed out")
 		}
 		if numRoundTries > 0 {
-			jww.INFO.Printf("Attempt %d to find round to send message "+
-				"to %s (msgDigest: %s)", numRoundTries+1, recipient,
+			jww.INFO.Printf("[SendCMIX-%s] Attempt %d to find round to send message "+
+				"to %s (msgDigest: %s)", cmixParams.DebugTag, numRoundTries+1, recipient,
 				msg.Digest())
 		}
 
@@ -117,13 +119,14 @@ func sendCmixHelper(sender *gateway.Sender, msg format.Message,
 		remainingTime := cmixParams.Timeout - elapsed
 		bestRound, err := instance.GetWaitingRounds().GetUpcomingRealtime(remainingTime, attempted, sendTimeBuffer)
 		if err != nil {
-			jww.WARN.Printf("Failed to GetUpcomingRealtime (msgDigest: %s): %+v", msg.Digest(), err)
+			jww.WARN.Printf("[SendCMIX-%s] Failed to GetUpcomingRealtime "+
+				"(msgDigest: %s): %+v", cmixParams.DebugTag, msg.Digest(), err)
 		}
 		if bestRound == nil {
-			jww.WARN.Printf("Best round on send is nil")
+			jww.WARN.Printf("[SendCMIX-%s], Best round on send is nil", cmixParams.DebugTag)
 			continue
 		}
-		jww.DEBUG.Printf("[sendCMIX] bestRound: %v", bestRound)
+		jww.TRACE.Printf("[SendCMIX-%s] bestRound: %v", cmixParams.DebugTag, bestRound)
 
 		// add the round on to the list of attempted, so it is not tried again
 		attempted.Insert(bestRound.GetRoundId())
@@ -138,19 +141,21 @@ func sendCmixHelper(sender *gateway.Sender, msg format.Message,
 			}
 		}
 		if containsBlacklisted {
-			jww.WARN.Printf("Round %d contains blacklisted node, skipping...", bestRound.ID)
+			jww.WARN.Printf("[SendCMIX-%s]Round %d contains blacklisted node, "+
+				"skipping...", cmixParams.DebugTag, bestRound.ID)
 			continue
 		}
 
 		// Retrieve host and key information from round
 		firstGateway, roundKeys, err := processRound(instance, session, nodeRegistration, bestRound, recipient.String(), msg.Digest())
 		if err != nil {
-			jww.WARN.Printf("SendCmix failed to process round (will retry): %v", err)
+			jww.WARN.Printf("[SendCMIX-%s]SendCmix failed to process round"+
+				" (will retry): %v", cmixParams.DebugTag, err)
 			continue
 		}
 
-		jww.DEBUG.Printf("[sendCMIX] round %v processed, firstGW: %s",
-			bestRound, firstGateway)
+		jww.TRACE.Printf("[SendCMIX-%s]round %v processed, firstGW: %s",
+			cmixParams.DebugTag, bestRound, firstGateway)
 
 		// Build the messages to send
 
@@ -160,9 +165,9 @@ func sendCmixHelper(sender *gateway.Sender, msg format.Message,
 			return 0, ephemeral.Id{}, err
 		}
 
-		jww.INFO.Printf("[sendCMIX] Sending to EphID %d (%s), "+
+		jww.INFO.Printf("[SendCMIX-%s] Sending to EphID %d (%s), "+
 			"on round %d (msgDigest: %s, ecrMsgDigest: %s) "+
-			"via gateway %s",
+			"via gateway %s", cmixParams.DebugTag,
 			ephID.Int64(), recipient, bestRound.ID, msg.Digest(),
 			encMsg.Digest(), firstGateway.String())
 
@@ -171,7 +176,7 @@ func sendCmixHelper(sender *gateway.Sender, msg format.Message,
 			timeout time.Duration) (interface{}, error) {
 			wrappedMsg.Target = target.Marshal()
 
-			jww.TRACE.Printf("[sendCMIX] sendFunc %s", host)
+			jww.TRACE.Printf("[SendCMIX-%s]sendFunc %s", cmixParams.DebugTag, host)
 			timeout = calculateSendTimeout(bestRound, maxTimeout)
 			// Use the smaller of the two timeout durations
 			calculatedTimeout := calculateSendTimeout(bestRound, maxTimeout)
@@ -182,25 +187,26 @@ func sendCmixHelper(sender *gateway.Sender, msg format.Message,
 			//send the message
 			result, err := comms.SendPutMessage(host, wrappedMsg,
 				timeout)
-			jww.TRACE.Printf("[sendCMIX] sendFunc %s putmsg", host)
+			jww.TRACE.Printf("[SendCMIX-%s]sendFunc %s putmsg", cmixParams.DebugTag, host)
 
 			if err != nil {
 				// fixme: should we provide as a slice the whole topology?
 				err := handlePutMessageError(firstGateway,
 					instance, session, nodeRegistration,
 					recipient.String(), bestRound, err)
-				jww.TRACE.Printf("[sendCMIX] sendFunc %s err %+v",
-					host, err)
+				jww.TRACE.Printf("[SendCMIX-%s] sendFunc %s err %+v",
+					cmixParams.DebugTag, host, err)
 				return result, errors.WithMessagef(err,
 					"SendCmix %s", unrecoverableError)
 			}
 			return result, err
 		}
-		jww.DEBUG.Printf("[sendCMIX] sendToPreferred %s", firstGateway)
+		jww.TRACE.Printf("[SendCMIX-%s] sendToPreferred %s",
+			cmixParams.DebugTag, firstGateway)
 		result, err := sender.SendToPreferred(
 			[]*id.ID{firstGateway}, sendFunc, stop, cmixParams.SendTimeout)
-		jww.DEBUG.Printf("[sendCMIX] sendToPreferred %s returned",
-			firstGateway)
+		jww.DEBUG.Printf("[SendCMIX-%s] sendToPreferred %s returned",
+			cmixParams.DebugTag, firstGateway)
 
 		// Exit if the thread has been stopped
 		if stoppable.CheckErr(err) {
@@ -209,8 +215,15 @@ func sendCmixHelper(sender *gateway.Sender, msg format.Message,
 
 		// if the comm errors or the message fails to send, continue retrying.
 		if err != nil {
-			jww.ERROR.Printf("SendCmix failed to send to EphID %d (%s) on "+
-				"round %d, trying a new round: %+v", ephID.Int64(), recipient,
+			if strings.Contains(err.Error(), rateLimiting.ClientRateLimitErr) {
+				jww.ERROR.Printf("[SendCMIX-%s] SendCmix failed to send to EphID %d (%s) on "+
+					"round %d: %+v", cmixParams.DebugTag, ephID.Int64(), recipient,
+					bestRound.ID, err)
+				return 0, ephemeral.Id{}, err
+			}
+
+			jww.ERROR.Printf("[SendCMIX-%s] SendCmix failed to send to EphID %d (%s) on "+
+				"round %d, trying a new round: %+v", cmixParams.DebugTag, ephID.Int64(), recipient,
 				bestRound.ID, err)
 			continue
 		}
@@ -218,9 +231,9 @@ func sendCmixHelper(sender *gateway.Sender, msg format.Message,
 		// Return if it sends properly
 		gwSlotResp := result.(*pb.GatewaySlotResponse)
 		if gwSlotResp.Accepted {
-			m := fmt.Sprintf("Successfully sent to EphID %v "+
+			m := fmt.Sprintf("[SendCMIX-%s] Successfully sent to EphID %v "+
 				"(source: %s) in round %d (msgDigest: %s), "+
-				"elapsed: %s numRoundTries: %d", ephID.Int64(),
+				"elapsed: %s numRoundTries: %d", cmixParams.DebugTag, ephID.Int64(),
 				recipient, bestRound.ID, msg.Digest(),
 				elapsed, numRoundTries)
 			jww.INFO.Print(m)
@@ -228,9 +241,9 @@ func sendCmixHelper(sender *gateway.Sender, msg format.Message,
 			onSend(1, session)
 			return id.Round(bestRound.ID), ephID, nil
 		} else {
-			jww.FATAL.Panicf("Gateway %s returned no error, but failed "+
+			jww.FATAL.Panicf("[SendCMIX-%s] Gateway %s returned no error, but failed "+
 				"to accept message when sending to EphID %d (%s) on round %d",
-				firstGateway, ephID.Int64(), recipient, bestRound.ID)
+				cmixParams.DebugTag, firstGateway, ephID.Int64(), recipient, bestRound.ID)
 		}
 
 	}
diff --git a/network/message/sendCmixUtils.go b/network/message/sendCmixUtils.go
index 15674cd2f0de004d306e44ebc9e1280845fedfa5..ae2344415338685af084a81744e941691854415c 100644
--- a/network/message/sendCmixUtils.go
+++ b/network/message/sendCmixUtils.go
@@ -154,12 +154,11 @@ func buildSlotMessage(msg format.Message, recipient *id.ID, target *id.ID,
 	}
 
 	// Set the identity fingerprint
+
 	ifp := fingerprint.IdentityFP(msg.GetContents(), preimage)
 
 	msg.SetIdentityFP(ifp)
 
-	jww.INFO.Printf(" Sending to %s with preimage %v, ifp: %v, contents: %v", recipient, preimage, ifp, msg.GetContents())
-
 	// Encrypt the message
 	salt := make([]byte, 32)
 	_, err = stream.Read(salt)
diff --git a/network/message/sendCmix_test.go b/network/message/sendCmix_test.go
index 984e5cd2d01d4f4e687b72f6e8217c9440c980cd..8ef85711c6d333d909b5f633994f47a4cc7caf46 100644
--- a/network/message/sendCmix_test.go
+++ b/network/message/sendCmix_test.go
@@ -90,7 +90,7 @@ func Test_attemptSendCmix(t *testing.T) {
 		t.Errorf("Failed to load a key for testing: %v", err)
 	}
 	rnd := ds.NewRound(ri, pubKey, nil)
-	inst.GetWaitingRounds().Insert(rnd)
+	inst.GetWaitingRounds().Insert([]*ds.Round{rnd}, nil)
 	i := internal.Internal{
 		Session:          sess1,
 		Switchboard:      sw,
diff --git a/network/message/sendManyCmix.go b/network/message/sendManyCmix.go
index 58edff8076be9e365f0c4873281c78141136f68c..29e766edd90738a3e0f89afa8832c48f109b2f68 100644
--- a/network/message/sendManyCmix.go
+++ b/network/message/sendManyCmix.go
@@ -75,22 +75,22 @@ func sendManyCmixHelper(sender *gateway.Sender,
 
 	recipientString, msgDigests := messageListToStrings(msgs)
 
-	jww.INFO.Printf("Looking for round to send cMix messages to [%s] "+
-		"(msgDigest: %s)", recipientString, msgDigests)
+	jww.INFO.Printf("[SendManyCMIX-%s]Looking for round to send cMix messages to [%s] "+
+		"(msgDigest: %s)", param.DebugTag, recipientString, msgDigests)
 
 	for numRoundTries := uint(0); numRoundTries < param.RoundTries; numRoundTries++ {
 		elapsed := netTime.Since(timeStart)
 
 		if elapsed > param.Timeout {
-			jww.INFO.Printf("No rounds to send to %s (msgDigest: %s) were found "+
-				"before timeout %s", recipientString, msgDigests, param.Timeout)
+			jww.INFO.Printf("[SendManyCMIX-%s]No rounds to send to %s (msgDigest: %s) were found "+
+				"before timeout %s", param.DebugTag, recipientString, msgDigests, param.Timeout)
 			return 0, []ephemeral.Id{},
 				errors.New("sending cMix message timed out")
 		}
 
 		if numRoundTries > 0 {
-			jww.INFO.Printf("Attempt %d to find round to send message to %s "+
-				"(msgDigest: %s)", numRoundTries+1, recipientString, msgDigests)
+			jww.INFO.Printf("[SendManyCMIX-%s]Attempt %d to find round to send message to %s "+
+				"(msgDigest: %s)", param.DebugTag, numRoundTries+1, recipientString, msgDigests)
 		}
 
 		remainingTime := param.Timeout - elapsed
@@ -116,8 +116,8 @@ func sendManyCmixHelper(sender *gateway.Sender,
 			}
 		}
 		if containsBlacklisted {
-			jww.WARN.Printf("Round %d contains blacklisted node, skipping...",
-				bestRound.ID)
+			jww.WARN.Printf("[SendManyCMIX-%s]Round %d contains blacklisted node, skipping...",
+				param.DebugTag, bestRound.ID)
 			continue
 		}
 
@@ -125,9 +125,9 @@ func sendManyCmixHelper(sender *gateway.Sender,
 		firstGateway, roundKeys, err := processRound(instance, session,
 			nodeRegistration, bestRound, recipientString, msgDigests)
 		if err != nil {
-			jww.INFO.Printf("error processing round: %v", err)
-			jww.WARN.Printf("SendManyCMIX failed to process round %d "+
-				"(will retry): %+v", bestRound.ID, err)
+			jww.INFO.Printf("[SendManyCMIX-%s]error processing round: %v", param.DebugTag, err)
+			jww.WARN.Printf("[SendManyCMIX-%s]SendManyCMIX failed to process round %d "+
+				"(will retry): %+v", param.DebugTag, bestRound.ID, err)
 			continue
 		}
 
@@ -142,7 +142,7 @@ func sendManyCmixHelper(sender *gateway.Sender,
 				bestRound, roundKeys, param)
 			if err != nil {
 				stream.Close()
-				jww.INFO.Printf("error building slot received: %v", err)
+				jww.INFO.Printf("[SendManyCMIX-%s]error building slot received: %v", param.DebugTag, err)
 				return 0, []ephemeral.Id{}, errors.Errorf("failed to build "+
 					"slot message for %s: %+v", msg.Recipient, err)
 			}
@@ -154,8 +154,8 @@ func sendManyCmixHelper(sender *gateway.Sender,
 		ephemeralIDsString := ephemeralIdListToString(ephemeralIDs)
 		encMsgsDigest := messagesToDigestString(encMsgs)
 
-		jww.INFO.Printf("Sending to EphIDs [%s] (%s) on round %d, "+
-			"(msgDigest: %s, ecrMsgDigest: %s) via gateway %s",
+		jww.INFO.Printf("[SendManyCMIX-%s]Sending to EphIDs [%s] (%s) on round %d, "+
+			"(msgDigest: %s, ecrMsgDigest: %s) via gateway %s", param.DebugTag,
 			ephemeralIDsString, recipientString, bestRound.ID, msgDigests,
 			encMsgsDigest, firstGateway)
 
@@ -198,10 +198,10 @@ func sendManyCmixHelper(sender *gateway.Sender,
 		// If the comm errors or the message fails to send, continue retrying
 		if err != nil {
 			if !strings.Contains(err.Error(), unrecoverableError) {
-				jww.ERROR.Printf("SendManyCMIX failed to send to EphIDs [%s] "+
+				jww.ERROR.Printf("[SendManyCMIX-%s]SendManyCMIX failed to send to EphIDs [%s] "+
 					"(sources: %s) on round %d, trying a new round %+v",
-					ephemeralIDsString, recipientString, bestRound.ID, err)
-				jww.INFO.Printf("error received, continuing: %v", err)
+					param.DebugTag, ephemeralIDsString, recipientString, bestRound.ID, err)
+				jww.INFO.Printf("[SendManyCMIX-%s]error received, continuing: %v", param.DebugTag, err)
 				continue
 			}
 			jww.INFO.Printf("error received: %v", err)
@@ -211,8 +211,8 @@ func sendManyCmixHelper(sender *gateway.Sender,
 		// Return if it sends properly
 		gwSlotResp := result.(*pb.GatewaySlotResponse)
 		if gwSlotResp.Accepted {
-			m := fmt.Sprintf("Successfully sent to EphIDs %s (sources: [%s]) "+
-				"in round %d", ephemeralIDsString, recipientString, bestRound.ID)
+			m := fmt.Sprintf("[SendManyCMIX-%s]Successfully sent to EphIDs %s (sources: [%s]) "+
+				"in round %d", param.DebugTag, ephemeralIDsString, recipientString, bestRound.ID)
 			jww.INFO.Print(m)
 			events.Report(1, "MessageSendMany", "Metric", m)
 			onSend(uint32(len(msgs)), session)
diff --git a/network/message/sendManyCmix_test.go b/network/message/sendManyCmix_test.go
index 476004edd18f481692d70794b3c22d3d8fa79b17..7ac3b99c3e0e121d8478a6162333741cfb411a29 100644
--- a/network/message/sendManyCmix_test.go
+++ b/network/message/sendManyCmix_test.go
@@ -90,7 +90,7 @@ func Test_attemptSendManyCmix(t *testing.T) {
 		t.Errorf("Failed to load a key for testing: %v", err)
 	}
 	rnd := ds.NewRound(ri, pubKey, nil)
-	inst.GetWaitingRounds().Insert(rnd)
+	inst.GetWaitingRounds().Insert([]*ds.Round{rnd}, nil)
 	i := internal.Internal{
 		Session:          sess1,
 		Switchboard:      sw,
diff --git a/network/node/register.go b/network/node/register.go
index 3e19eb6750aa7bd040e12625ef1cf3bb0852ed0a..4336575dee390d0b948b914595d9bb15cfafd5bd 100644
--- a/network/node/register.go
+++ b/network/node/register.go
@@ -272,8 +272,8 @@ func requestKey(sender *gateway.Sender, comms RegisterNodeCommsInterface,
 	// Verify the HMAC
 	h.Reset()
 	if !registration.VerifyClientHMAC(sessionKey.Bytes(), keyResponse.EncryptedClientKey,
-		h, keyResponse.EncryptedClientKeyHMAC) {
-		return nil, nil, 0, errors.WithMessagef(err, "Failed to verify client HMAC")
+		opts.Hash.New, keyResponse.EncryptedClientKeyHMAC) {
+		return nil, nil, 0, errors.New("Failed to verify client HMAC")
 	}
 
 	// Decrypt the client key
diff --git a/network/rounds/check.go b/network/rounds/check.go
index 3b9eb2d2849400d2fea2227373839cbc0def44e6..295a63f0abdb607660c0e6bc6865e7d026d608f6 100644
--- a/network/rounds/check.go
+++ b/network/rounds/check.go
@@ -56,6 +56,11 @@ func serializeRound(roundId id.Round) []byte {
 func (m *Manager) GetMessagesFromRound(roundID id.Round, identity reception.IdentityUse) {
 	ri, err := m.Instance.GetRound(roundID)
 	if err != nil || m.params.ForceHistoricalRounds {
+		if m.params.RealtimeOnly {
+			jww.WARN.Printf("Skipping round %d because it is not in ram and we are realtime only mode",
+				roundID)
+			return
+		}
 		if m.params.ForceHistoricalRounds {
 			jww.WARN.Printf("Forcing use of historical rounds for round ID %d.",
 				roundID)
@@ -80,11 +85,14 @@ func (m *Manager) GetMessagesFromRound(roundID id.Round, identity reception.Iden
 			"up messages via in ram lookup", roundID, identity.EphId.Int64(),
 			identity.Source)
 		//store the round as an unretreived round
-		err = m.Session.UncheckedRounds().AddRound(roundID, ri,
-			identity.Source, identity.EphId)
-		if err != nil {
-			jww.FATAL.Panicf("Failed to denote Unchecked Round for round %d", roundID)
+		if !m.params.RealtimeOnly {
+			err = m.Session.UncheckedRounds().AddRound(roundID, ri,
+				identity.Source, identity.EphId)
+			if err != nil {
+				jww.FATAL.Panicf("Failed to denote Unchecked Round for round %d", roundID)
+			}
 		}
+
 		// If found, send to Message Retrieval Workers
 		m.lookupRoundMessages <- roundLookup{
 			roundInfo: ri,
diff --git a/network/rounds/manager.go b/network/rounds/manager.go
index c695cd7a8c78be622769a4388a669eda5757b304..f220583de8de98ab706b48db3a2b24d6a6168078 100644
--- a/network/rounds/manager.go
+++ b/network/rounds/manager.go
@@ -58,9 +58,12 @@ func (m *Manager) StartProcessors() stoppable.Stoppable {
 	}
 
 	// Start the periodic unchecked round worker
-	stopper := stoppable.NewSingle("UncheckRound")
-	go m.processUncheckedRounds(m.params.UncheckRoundPeriod, backOffTable, stopper)
-	multi.Add(stopper)
+	if !m.params.RealtimeOnly{
+		stopper := stoppable.NewSingle("UncheckRound")
+		go m.processUncheckedRounds(m.params.UncheckRoundPeriod, backOffTable, stopper)
+		multi.Add(stopper)
+	}
+
 
 	return multi
 }
diff --git a/network/rounds/remoteFilters.go b/network/rounds/remoteFilters.go
index 7a434be073e51118d22adae682c27d2e3ad83b50..6753c4110716e83d08b55eb2f585b4e82ada1180 100644
--- a/network/rounds/remoteFilters.go
+++ b/network/rounds/remoteFilters.go
@@ -11,7 +11,6 @@ import (
 	jww "github.com/spf13/jwalterweatherman"
 	bloom "gitlab.com/elixxir/bloomfilter"
 	"gitlab.com/elixxir/client/interfaces"
-	"gitlab.com/elixxir/client/storage/reception"
 	"gitlab.com/elixxir/comms/mixmessages"
 	"gitlab.com/xx_network/primitives/id"
 )
@@ -48,37 +47,3 @@ func (rf *RemoteFilter) FirstRound() id.Round {
 func (rf *RemoteFilter) LastRound() id.Round {
 	return id.Round(rf.data.FirstRound + uint64(rf.data.RoundRange))
 }
-
-// ValidFilterRange calculates which of the returned filters are valid for the identity
-func ValidFilterRange(identity reception.IdentityUse, filters *mixmessages.ClientBlooms) (startIdx int, endIdx int, outOfBounds bool) {
-	outOfBounds = false
-
-	firstElementTS := filters.FirstTimestamp
-
-	identityStart := identity.StartValid.UnixNano()
-	identityEnd := identity.EndValid.UnixNano()
-
-	startIdx = int((identityStart - firstElementTS) / filters.Period)
-	if startIdx < 0 {
-		startIdx = 0
-	}
-
-	if startIdx > len(filters.Filters)-1 {
-		outOfBounds = true
-		return startIdx, endIdx, outOfBounds
-	}
-
-	endIdx = int((identityEnd - firstElementTS) / filters.Period)
-	if endIdx < 0 {
-		outOfBounds = true
-		return startIdx, endIdx, outOfBounds
-	}
-
-	if endIdx > len(filters.Filters)-1 {
-		endIdx = len(filters.Filters) - 1
-	}
-
-	// Add 1 to the end index so that it follows Go's convention; the last index
-	// is exclusive to the range
-	return startIdx, endIdx + 1, outOfBounds
-}
diff --git a/network/rounds/remoteFilters_test.go b/network/rounds/remoteFilters_test.go
index a04d7f51c7f580402744fef7a5b1f128d5f23332..51d26973d9f024142ed89b04e57d8f7117992268 100644
--- a/network/rounds/remoteFilters_test.go
+++ b/network/rounds/remoteFilters_test.go
@@ -11,15 +11,12 @@ import (
 	jww "github.com/spf13/jwalterweatherman"
 	bloom "gitlab.com/elixxir/bloomfilter"
 	"gitlab.com/elixxir/client/interfaces"
-	"gitlab.com/elixxir/client/storage/reception"
 	"gitlab.com/elixxir/comms/mixmessages"
 	"gitlab.com/xx_network/comms/connect"
 	"gitlab.com/xx_network/primitives/id"
-	"gitlab.com/xx_network/primitives/id/ephemeral"
 	"os"
 	"reflect"
 	"testing"
-	"time"
 )
 
 func TestMain(m *testing.M) {
@@ -101,113 +98,4 @@ func TestRemoteFilter_FirstLastRound(t *testing.T) {
 			"\n\tExpected: %v\n\tReceived: %v", receivedLastRound, firstRound+uint64(roundRange))
 	}
 
-}
-
-// In bounds test
-func TestValidFilterRange(t *testing.T) {
-	firstRound := uint64(25)
-	roundRange := uint32(75)
-	testFilter, err := bloom.InitByParameters(interfaces.BloomFilterSize,
-		interfaces.BloomFilterHashes)
-	if err != nil {
-		t.Fatalf("GetFilter error: "+
-			"Cannot initialize bloom filter for setup: %v", err)
-	}
-
-	data, err := testFilter.MarshalBinary()
-	if err != nil {
-		t.Fatalf("GetFilter error: "+
-			"Cannot marshal filter for setup: %v", err)
-	}
-
-	// Construct an in bounds value
-	expectedEphID := ephemeral.Id{1, 2, 3, 4, 5, 6, 7, 8}
-	requestGateway := id.NewIdFromString(ReturningGateway, id.Gateway, t)
-	iu := reception.IdentityUse{
-		Identity: reception.Identity{
-			EphId:      expectedEphID,
-			Source:     requestGateway,
-			StartValid: time.Now().Add(-12 * time.Hour),
-			EndValid:   time.Now().Add(24 * time.Hour),
-		},
-	}
-
-	bloomFilter := &mixmessages.ClientBloom{
-		Filter:     data,
-		FirstRound: firstRound,
-		RoundRange: roundRange,
-	}
-
-	// Fix for test on Windows machines: provides extra buffer between
-	// time.Now() for the reception.Identity and the mixmessages.ClientBlooms
-	time.Sleep(time.Millisecond)
-
-	msg := &mixmessages.ClientBlooms{
-		Period:         int64(12 * time.Hour),
-		FirstTimestamp: time.Now().UnixNano(),
-		Filters:        []*mixmessages.ClientBloom{bloomFilter},
-	}
-
-	start, end, outOfBounds := ValidFilterRange(iu, msg)
-	if outOfBounds {
-		t.Errorf("ValidFilterRange error: " +
-			"Range should not be out of bounds")
-	}
-
-	if start != 0 && end != 1 {
-		t.Errorf("ValidFilterRange error: "+
-			"Unexpected indices returned. "+
-			"\n\tExpected start: %v\n\tReceived start: %v"+
-			"\n\tExpected end: %v\n\tReceived end: %v", 0, start, 1, end)
-	}
-
-}
-
-// out of bounds test
-func TestValidFilterRange_OutBounds(t *testing.T) {
-	firstRound := uint64(25)
-	roundRange := uint32(75)
-	testFilter, err := bloom.InitByParameters(interfaces.BloomFilterSize,
-		interfaces.BloomFilterHashes)
-	if err != nil {
-		t.Fatalf("GetFilter error: "+
-			"Cannot initialize bloom filter for setup: %v", err)
-	}
-
-	data, err := testFilter.MarshalBinary()
-	if err != nil {
-		t.Fatalf("GetFilter error: "+
-			"Cannot marshal filter for setup: %v", err)
-	}
-
-	// Construct an in bounds value
-	expectedEphID := ephemeral.Id{1, 2, 3, 4, 5, 6, 7, 8}
-	requestGateway := id.NewIdFromString(ReturningGateway, id.Gateway, t)
-	iu := reception.IdentityUse{
-		Identity: reception.Identity{
-			EphId:      expectedEphID,
-			Source:     requestGateway,
-			StartValid: time.Now().Add(-24 * time.Hour),
-			EndValid:   time.Now().Add(-36 * time.Hour),
-		},
-	}
-
-	bloomFilter := &mixmessages.ClientBloom{
-		Filter:     data,
-		FirstRound: firstRound,
-		RoundRange: roundRange,
-	}
-
-	msg := &mixmessages.ClientBlooms{
-		Period:         int64(12 * time.Hour),
-		FirstTimestamp: time.Now().UnixNano(),
-		Filters:        []*mixmessages.ClientBloom{bloomFilter},
-	}
-
-	_, _, outOfBounds := ValidFilterRange(iu, msg)
-	if !outOfBounds {
-		t.Errorf("ValidFilterRange error: " +
-			"Range should be out of bounds")
-	}
-
-}
+}
\ No newline at end of file
diff --git a/network/rounds/retrieve.go b/network/rounds/retrieve.go
index 1db833b43ccf1b415efe44b1d0f10aefe3aa2bed..00f6675375243ef4025ad86f87751b578f185c1c 100644
--- a/network/rounds/retrieve.go
+++ b/network/rounds/retrieve.go
@@ -49,12 +49,15 @@ func (m *Manager) processMessageRetrieval(comms messageRetrievalComms,
 		case rl := <-m.lookupRoundMessages:
 			ri := rl.roundInfo
 			jww.DEBUG.Printf("Checking for messages in round %d", ri.ID)
-			err := m.Session.UncheckedRounds().AddRound(id.Round(ri.ID), ri,
-				rl.identity.Source, rl.identity.EphId)
-			if err != nil {
-				jww.FATAL.Panicf("Failed to denote Unchecked Round for round %d", id.Round(ri.ID))
+			if !m.params.RealtimeOnly{
+				err := m.Session.UncheckedRounds().AddRound(id.Round(ri.ID), ri,
+					rl.identity.Source, rl.identity.EphId)
+				if err != nil {
+					jww.FATAL.Panicf("Failed to denote Unchecked Round for round %d", id.Round(ri.ID))
+				}
 			}
 
+
 			// Convert gateways in round to proper ID format
 			gwIds := make([]*id.ID, len(ri.Topology))
 			for i, idBytes := range ri.Topology {
@@ -73,7 +76,7 @@ func (m *Manager) processMessageRetrieval(comms messageRetrievalComms,
 			// messages first, randomize other members of the team
 			var rndBytes [32]byte
 			stream := m.Rng.GetStream()
-			_, err = stream.Read(rndBytes[:])
+			_, err := stream.Read(rndBytes[:])
 			stream.Close()
 			if err != nil {
 				jww.FATAL.Panicf("Failed to randomize shuffle in round %d "+
@@ -129,12 +132,15 @@ func (m *Manager) processMessageRetrieval(comms messageRetrievalComms,
 				m.messageBundles <- bundle
 
 				jww.DEBUG.Printf("Removing round %d from unchecked store", ri.ID)
-				err = m.Session.UncheckedRounds().Remove(id.Round(ri.ID), rl.identity.Source, rl.identity.EphId)
-				if err != nil {
-					jww.ERROR.Printf("Could not remove round %d "+
-						"from unchecked rounds store: %v", ri.ID, err)
+				if !m.params.RealtimeOnly{
+					err = m.Session.UncheckedRounds().Remove(id.Round(ri.ID), rl.identity.Source, rl.identity.EphId)
+					if err != nil {
+						jww.ERROR.Printf("Could not remove round %d "+
+							"from unchecked rounds store: %v", ri.ID, err)
+					}
 				}
 
+
 			}
 
 		}
@@ -190,12 +196,14 @@ func (m *Manager) getMessagesFromGateway(roundID id.Round,
 			" in round %d. This happening every once in a while is normal,"+
 			" but can be indicative of a problem if it is consistent",
 			m.TransmissionID, roundID)
-
-		err = m.Session.UncheckedRounds().Remove(roundID, identity.Source, identity.EphId)
-		if err != nil {
-			jww.FATAL.Panicf("Failed to remove round %d: %+v", roundID, err)
+		if m.params.RealtimeOnly{
+			err = m.Session.UncheckedRounds().Remove(roundID, identity.Source, identity.EphId)
+			if err != nil {
+				jww.ERROR.Printf("Failed to remove round %d: %+v", roundID, err)
+			}
 		}
 
+
 		return message.Bundle{}, nil
 	}
 
diff --git a/network/rounds/unchecked.go b/network/rounds/unchecked.go
index 0618a95c75d07faa2bdc030348bb0c27b68fca84..a73bf6eb24eef97f0fa47a693d045fcd18a38bae 100644
--- a/network/rounds/unchecked.go
+++ b/network/rounds/unchecked.go
@@ -115,8 +115,8 @@ func (m *Manager) processUncheckedRounds(checkInterval time.Duration, backoffTab
 func isRoundCheckDue(tries uint64, ts time.Time, backoffTable [cappedTries]time.Duration) bool {
 	now := netTime.Now()
 
-	if tries > cappedTries {
-		tries = cappedTries
+	if tries >= uint64(len(backoffTable)) {
+		tries = uint64(len(backoffTable)) - 1
 	}
 	roundCheckTime := ts.Add(backoffTable[tries])
 
diff --git a/single/reception.go b/single/reception.go
index e1aa8e08d3341e6bf3bdb4d5354d6e4cf90dfda6..b4aa68f8341da6dba735639883239fcc1d0a97da 100644
--- a/single/reception.go
+++ b/single/reception.go
@@ -33,47 +33,48 @@ func (m *Manager) receiveTransmissionHandler(rawMessages chan message.Receive,
 		case msg := <-rawMessages:
 			jww.TRACE.Printf("Received CMIX message; checking if it is a " +
 				"single-use transmission.")
-
-			// Check if message is a single-use transmit message
-			cmixMsg, err := format.Unmarshal(msg.Payload)
-			if err != nil {
-				jww.ERROR.Printf("Could not unmarshal msg: %s",
-					err.Error())
-				continue
-			}
-			if fp != cmixMsg.GetKeyFP() {
-				// If the verification fails, then ignore the message as it is
-				// likely garbled or for a different protocol
-				jww.TRACE.Print("Failed to read single-use CMIX message: " +
-					"fingerprint verification failed.")
-				continue
-			}
-
-			// Denote that the message is not garbled
-			jww.DEBUG.Printf("Received single-use transmission message.")
-			m.store.GetGarbledMessages().Remove(cmixMsg)
-
-			// Handle message
-			payload, c, err := m.processTransmission(cmixMsg, fp)
-			if err != nil {
-				jww.WARN.Printf("Failed to read single-use CMIX message: %+v",
-					err)
-				continue
-			}
-			jww.DEBUG.Printf("Successfully processed single-use transmission message.")
-
-			// Lookup the registered callback for the message's tag fingerprint
-			callback, err := m.callbackMap.getCallback(c.tagFP)
-			if err != nil {
-				jww.WARN.Printf("Failed to find module to pass single-use "+
-					"payload: %+v", err)
-				continue
-			}
-
-			jww.DEBUG.Printf("Calling single-use callback with tag "+
-				"fingerprint %s.", c.tagFP)
-
-			go callback(payload, c)
+			go func(m *Manager, msg message.Receive) {
+				// Check if message is a single-use transmit message
+				cmixMsg, err := format.Unmarshal(msg.Payload)
+				if err != nil {
+					jww.ERROR.Printf("Could not unmarshal msg: %s",
+						err.Error())
+					return
+				}
+				if fp != cmixMsg.GetKeyFP() {
+					// If the verification fails, then ignore the message as it is
+					// likely garbled or for a different protocol
+					jww.TRACE.Print("Failed to read single-use CMIX message: " +
+						"fingerprint verification failed.")
+					return
+				}
+
+				// Denote that the message is not garbled
+				jww.DEBUG.Printf("Received single-use transmission message.")
+				m.store.GetGarbledMessages().Remove(cmixMsg)
+
+				// Handle message
+				payload, c, err := m.processTransmission(cmixMsg, fp)
+				if err != nil {
+					jww.WARN.Printf("Failed to read single-use CMIX message: %+v",
+						err)
+					return
+				}
+				jww.DEBUG.Printf("Successfully processed single-use transmission message.")
+
+				// Lookup the registered callback for the message's tag fingerprint
+				callback, err := m.callbackMap.getCallback(c.tagFP)
+				if err != nil {
+					jww.WARN.Printf("Failed to find module to pass single-use "+
+						"payload: %+v", err)
+					return
+				}
+
+				jww.DEBUG.Printf("Calling single-use callback with tag "+
+					"fingerprint %s.", c.tagFP)
+
+				callback(payload, c)
+			}(m, msg)
 		}
 	}
 }
@@ -113,11 +114,9 @@ func (m *Manager) processTransmission(msg format.Message,
 	c := NewContact(payload.GetRID(transmitMsg.GetPubKey(grp)),
 		transmitMsg.GetPubKey(grp), dhKey, payload.GetTagFP(), payload.GetMaxParts())
 
-	jww.INFO.Printf("generated by singe use receiver reception id for single use: %s, "+
-		"ephId: %v, pubkey: %x, msg: %s",
-		c.partner, "unknown:",
-		transmitMsg.GetPubKey(grp).Bytes(),
-		msg)
+	jww.INFO.Printf("Generated by singe use receiver reception id for single use: %s, "+
+		"ephId: %v, pubkey: %x",
+		c.partner, "unknown:", transmitMsg.GetPubKey(grp).Bytes())
 
 	return payload.GetContents(), c, nil
 }
diff --git a/single/response.go b/single/response.go
index a5ab3cdb1baee704b214ff694b783c10182471ac..4486690e78e29185443d9bfd1642be78b2064b39 100644
--- a/single/response.go
+++ b/single/response.go
@@ -74,7 +74,9 @@ func (m *Manager) respondSingleUse(partner Contact, payload []byte,
 		go func() {
 			defer wg.Done()
 			// Send Message
-			round, ephID, err := m.net.SendCMIX(cmixMsgFunc, partner.partner, params.GetDefaultCMIX())
+			p := params.GetDefaultCMIX()
+			p.DebugTag = "single.Response"
+			round, ephID, err := m.net.SendCMIX(cmixMsgFunc, partner.partner, p)
 			if err != nil {
 				jww.ERROR.Printf("Failed to send single-use response CMIX "+
 					"message part %d: %+v", j, err)
diff --git a/single/transmission.go b/single/transmission.go
index 30f2f6c90a3cc5e5e8f6ece6cb459fc15ec1aa57..436420a7f3711d7bbb4a5271c207779f64bd8704 100644
--- a/single/transmission.go
+++ b/single/transmission.go
@@ -13,7 +13,6 @@ import (
 	jww "github.com/spf13/jwalterweatherman"
 	"gitlab.com/elixxir/client/interfaces"
 	"gitlab.com/elixxir/client/interfaces/params"
-	"gitlab.com/elixxir/client/interfaces/utility"
 	"gitlab.com/elixxir/client/storage/reception"
 	ds "gitlab.com/elixxir/comms/network/dataStructures"
 	contact2 "gitlab.com/elixxir/crypto/contact"
@@ -51,8 +50,7 @@ func (m *Manager) TransmitSingleUse(partner contact2.Contact, payload []byte,
 	rngReader := m.rng.GetStream()
 	defer rngReader.Close()
 
-	return m.transmitSingleUse(partner, payload, tag, maxMsgs, rngReader,
-		callback, timeout, m.net.GetInstance().GetRoundEvents())
+	return m.transmitSingleUse(partner, payload, tag, maxMsgs, rngReader, callback, timeout)
 }
 
 // roundEvents interface allows custom round events to be passed in for testing.
@@ -63,23 +61,24 @@ type roundEvents interface {
 
 // transmitSingleUse has the fields passed in for easier testing.
 func (m *Manager) transmitSingleUse(partner contact2.Contact, payload []byte,
-	tag string, MaxMsgs uint8, rng io.Reader, callback ReplyComm, timeout time.Duration, roundEvents roundEvents) error {
+	tag string, MaxMsgs uint8, rng io.Reader, callback ReplyComm, timeout time.Duration) error {
 
 	// Get ephemeral ID address space size; this blocks until the address space
 	// size is set for the first time
 	addressSize := m.net.GetAddressSize()
+	timeStart := netTime.Now()
 
 	// Create new CMIX message containing the transmission payload
 	cmixMsg, dhKey, rid, ephID, err := m.makeTransmitCmixMessage(partner,
-		payload, tag, MaxMsgs, addressSize, timeout, netTime.Now(), rng)
+		payload, tag, MaxMsgs, addressSize, timeout, timeStart, rng)
 	if err != nil {
 		return errors.Errorf("failed to create new CMIX message: %+v", err)
 	}
 
+	startValid := timeStart.Add(-2 * timeout)
+	endValid := timeStart.Add(2 * timeout)
 	jww.DEBUG.Printf("Created single-use transmission CMIX message with new ID "+
-		"%s and ephemeral ID %d", rid, ephID.Int64())
-
-	timeStart := netTime.Now()
+		"%s and EphID %d (Valid %s - %s)", rid, ephID.Int64(), startValid.String(), endValid.String())
 
 	// Add message state to map
 	quitChan, quit, err := m.p.addState(rid, dhKey, MaxMsgs, callback, timeout)
@@ -92,10 +91,10 @@ func (m *Manager) transmitSingleUse(partner contact2.Contact, payload []byte,
 		EphId:       ephID,
 		Source:      rid,
 		AddressSize: addressSize,
-		End:         timeStart.Add(2 * timeout),
+		End:         endValid,
 		ExtraChecks: interfaces.DefaultExtraChecks,
-		StartValid:  timeStart.Add(-2 * timeout),
-		EndValid:    timeStart.Add(2 * timeout),
+		StartValid:  startValid,
+		EndValid:    endValid,
 		Ephemeral:   true,
 	})
 	if err != nil {
@@ -116,11 +115,13 @@ func (m *Manager) transmitSingleUse(partner contact2.Contact, payload []byte,
 		// Send Message
 		jww.DEBUG.Printf("Sending single-use transmission CMIX "+
 			"message to %s.", partner.ID)
-		round, _, err := m.net.SendCMIX(cmixMsg, partner.ID, params.GetDefaultCMIX())
+		p := params.GetDefaultCMIX()
+		p.DebugTag = "single.Transmit"
+		round, _, err := m.net.SendCMIX(cmixMsg, partner.ID, p)
 		if err != nil {
 			errorString := fmt.Sprintf("failed to send single-use transmission "+
 				"CMIX message: %+v", err)
-			jww.ERROR.Print(errorString)
+			jww.ERROR.Printf(errorString)
 
 			// Exit the state timeout handler, delete the state from map, and
 			// return an error on the callback
@@ -137,40 +138,9 @@ func (m *Manager) transmitSingleUse(partner contact2.Contact, payload []byte,
 				"message because the timeout handler quit.")
 			return
 		}
-
-		// Update the timeout for the elapsed time
-		roundEventTimeout := timeout - netTime.Since(timeStart) - time.Millisecond
-
-		// Check message delivery
-		sendResults := make(chan ds.EventReturn, 1)
-		roundEvents.AddRoundEventChan(round, sendResults, roundEventTimeout,
-			states.COMPLETED, states.FAILED)
-
-		im := fmt.Sprintf("Sent single-use transmission CMIX "+
+		jww.DEBUG.Printf("Sent single-use transmission CMIX "+
 			"message to %s and ephemeral ID %d on round %d.",
 			partner.ID, ephID.Int64(), round)
-		jww.DEBUG.Print(im)
-		if m.client != nil {
-			m.client.ReportEvent(1, "SingleUse", "MessageSend", im)
-		}
-
-		// Wait until the result tracking responds
-		success, numRoundFail, numTimeOut := utility.TrackResults(sendResults, 1)
-		if !success {
-			errorString := fmt.Sprintf("failed to send single-use transmission "+
-				"message: %d round failures, %d round event time outs.",
-				numRoundFail, numTimeOut)
-			jww.ERROR.Print(errorString)
-
-			// Exit the state timeout handler, delete the state from map, and
-			// return an error on the callback
-			quitChan <- struct{}{}
-			m.p.Lock()
-			delete(m.p.singleUse, *rid)
-			m.p.Unlock()
-			go callback(nil, errors.New(errorString))
-		}
-		jww.DEBUG.Print("Tracked single-use transmission message round.")
 	}()
 
 	return nil
diff --git a/single/transmission_test.go b/single/transmission_test.go
index 548cefa7c0be402eda3671278a4234e6fb610672..15c557e60292021edab2252bd05ec10e5553d6a7 100644
--- a/single/transmission_test.go
+++ b/single/transmission_test.go
@@ -26,7 +26,7 @@ func TestManager_GetMaxTransmissionPayloadSize(t *testing.T) {
 	m := newTestManager(0, false, t)
 	cmixPrimeSize := m.store.Cmix().GetGroup().GetP().ByteLen()
 	e2ePrimeSize := m.store.E2e().GetGroup().GetP().ByteLen()
-	expectedSize := 2*cmixPrimeSize - e2ePrimeSize - format.KeyFPLen - format.MacLen - format.RecipientIDLen - transmitPlMinSize - transmitMessageVersionSize-1
+	expectedSize := 2*cmixPrimeSize - e2ePrimeSize - format.KeyFPLen - format.MacLen - format.RecipientIDLen - transmitPlMinSize - transmitMessageVersionSize - 1
 	testSize := m.GetMaxTransmissionPayloadSize()
 
 	if expectedSize != testSize {
@@ -50,8 +50,7 @@ func TestManager_transmitSingleUse(t *testing.T) {
 	callback, callbackChan := createReplyComm()
 	timeout := 15 * time.Millisecond
 
-	err := m.transmitSingleUse(partner, payload, tag, maxMsgs, prng,
-		callback, timeout, newTestRoundEvents(false))
+	err := m.transmitSingleUse(partner, payload, tag, maxMsgs, prng, callback, timeout)
 	if err != nil {
 		t.Errorf("transmitSingleUse() returned an error: %+v", err)
 	}
@@ -93,7 +92,7 @@ func TestManager_transmitSingleUse_QuitChanError(t *testing.T) {
 	timeout := 15 * time.Millisecond
 
 	err := m.transmitSingleUse(partner, []byte{}, "testTag", 9,
-		rand.New(rand.NewSource(42)), callback, timeout, newTestRoundEvents(false))
+		rand.New(rand.NewSource(42)), callback, timeout)
 	if err != nil {
 		t.Errorf("transmitSingleUse() returned an error: %+v", err)
 	}
@@ -125,7 +124,7 @@ func TestManager_transmitSingleUse_AddIdentityError(t *testing.T) {
 	callback, callbackChan := createReplyComm()
 
 	err := m.transmitSingleUse(partner, []byte{}, "testTag", 9,
-		rand.New(rand.NewSource(42)), callback, timeout, newTestRoundEvents(false))
+		rand.New(rand.NewSource(42)), callback, timeout)
 	if err != nil {
 		t.Errorf("transmitSingleUse() returned an error: %+v", err)
 	}
@@ -158,7 +157,7 @@ func TestManager_transmitSingleUse_SendCMIXError(t *testing.T) {
 	timeout := 15 * time.Millisecond
 
 	err := m.transmitSingleUse(partner, []byte{}, "testTag", 9,
-		rand.New(rand.NewSource(42)), callback, timeout, newTestRoundEvents(false))
+		rand.New(rand.NewSource(42)), callback, timeout)
 	if err != nil {
 		t.Errorf("transmitSingleUse() returned an error: %+v", err)
 	}
@@ -182,7 +181,7 @@ func TestManager_transmitSingleUse_MakeTransmitCmixMessageError(t *testing.T) {
 	prng := rand.New(rand.NewSource(42))
 	payload := make([]byte, m.store.Cmix().GetGroup().GetP().ByteLen())
 
-	err := m.transmitSingleUse(contact2.Contact{}, payload, "", 0, prng, nil, 0, nil)
+	err := m.transmitSingleUse(contact2.Contact{}, payload, "", 0, prng, nil, 0)
 	if err == nil {
 		t.Error("transmitSingleUse() did not return an error when the payload " +
 			"is too large.")
@@ -212,7 +211,7 @@ func TestManager_transmitSingleUse_AddStateError(t *testing.T) {
 	m.p.singleUse[*rid] = newState(dhKey, maxMsgs, nil)
 
 	err = m.transmitSingleUse(partner, payload, tag, maxMsgs,
-		rand.New(rand.NewSource(42)), callback, timeout, nil)
+		rand.New(rand.NewSource(42)), callback, timeout)
 	if !check(err, "failed to add pending state") {
 		t.Errorf("transmitSingleUse() failed to error when on adding state "+
 			"when the state already exists: %+v", err)
@@ -232,8 +231,7 @@ func TestManager_transmitSingleUse_RoundTimeoutError(t *testing.T) {
 	callback, callbackChan := createReplyComm()
 	timeout := 15 * time.Millisecond
 
-	err := m.transmitSingleUse(partner, payload, "testTag", 8, prng, callback,
-		timeout, newTestRoundEvents(true))
+	err := m.transmitSingleUse(partner, payload, "testTag", 8, prng, callback, timeout)
 	if err != nil {
 		t.Errorf("transmitSingleUse() returned an error: %+v", err)
 	}
@@ -242,7 +240,7 @@ func TestManager_transmitSingleUse_RoundTimeoutError(t *testing.T) {
 
 	select {
 	case results := <-callbackChan:
-		if results.payload != nil || !check(results.err, "round failures") {
+		if results.payload != nil || !check(results.err, "timed out") {
 			t.Errorf("Callback did not return the correct error when it "+
 				"should have timed out.\npayload: %+v\nerror:   %+v",
 				results.payload, results.err)
diff --git a/storage/auth/store.go b/storage/auth/store.go
index addd3d138499ae09cfc5e9dc5d221d79d95d060a..fb9aa2b04be8875bae32de4c74752db3dd80ddee 100644
--- a/storage/auth/store.go
+++ b/storage/auth/store.go
@@ -261,6 +261,20 @@ func (s *Store) AddReceived(c contact.Contact, key *sidh.PublicKey) error {
 	return nil
 }
 
+// GetAllReceived returns all pending received contact requests from storage.
+func (s *Store) GetAllReceived() []contact.Contact {
+	s.mux.RLock()
+	defer s.mux.RUnlock()
+	cList := make([]contact.Contact, 0, len(s.requests))
+	for key := range s.requests {
+		r := s.requests[key]
+		if r.rt == Receive {
+			cList = append(cList, *r.receive)
+		}
+	}
+	return cList
+}
+
 // GetFingerprint can return either a private key or a sentRequest if the
 // fingerprint is found. If it returns a sentRequest, then it takes the lock to
 // ensure there is only one operator at a time. The user of the API must release
@@ -389,7 +403,7 @@ func (s *Store) Done(partner *id.ID) {
 	r.mux.Unlock()
 }
 
-// delete is one of two calls after using a request. This one is to be used when
+// Delete is one of two calls after using a request. This one is to be used when
 // the use is unsuccessful. It deletes all references to the request associated
 // with the passed partner, if it exists. It will allow any thread waiting on
 // access to continue. They should fail due to the deletion of the structure.
@@ -404,16 +418,9 @@ func (s *Store) Delete(partner *id.ID) error {
 
 	switch r.rt {
 	case Sent:
-		delete(s.fingerprints, r.sent.fingerprint)
-		if err := r.sent.delete(); err != nil {
-			jww.FATAL.Panicf("Failed to delete sent request: %+v", err)
-		}
-
+		s.deleteSentRequest(r)
 	case Receive:
-		if err := util.DeleteContact(s.kv, r.receive.ID); err != nil {
-			jww.FATAL.Panicf("Failed to delete recieved request "+
-				"contact: %+v", err)
-		}
+		s.deleteReceiveRequest(r)
 	}
 
 	delete(s.requests, *partner)
@@ -424,3 +431,91 @@ func (s *Store) Delete(partner *id.ID) error {
 
 	return nil
 }
+
+// DeleteAllRequests clears the request map and all associated storage objects
+// containing request data.
+func (s *Store) DeleteAllRequests() error {
+	s.mux.Lock()
+	defer s.mux.Unlock()
+
+	for partnerId, req := range s.requests {
+		switch req.rt {
+		case Sent:
+			s.deleteSentRequest(req)
+			delete(s.requests, partnerId)
+		case Receive:
+			s.deleteReceiveRequest(req)
+			delete(s.requests, partnerId)
+		}
+
+	}
+
+	if err := s.save(); err != nil {
+		jww.FATAL.Panicf("Failed to store updated request map after "+
+			"deleting all requests: %+v", err)
+	}
+
+	return nil
+}
+
+// DeleteSentRequests deletes all Sent requests from Store.
+func (s *Store) DeleteSentRequests() error {
+	s.mux.Lock()
+	defer s.mux.Unlock()
+
+	for partnerId, req := range s.requests {
+		switch req.rt {
+		case Sent:
+			s.deleteSentRequest(req)
+			delete(s.requests, partnerId)
+		case Receive:
+			continue
+		}
+	}
+
+	if err := s.save(); err != nil {
+		jww.FATAL.Panicf("Failed to store updated request map after "+
+			"deleting all sent requests: %+v", err)
+	}
+
+	return nil
+}
+
+// DeleteReceiveRequests deletes all Receive requests from Store.
+func (s *Store) DeleteReceiveRequests() error {
+	s.mux.Lock()
+	defer s.mux.Unlock()
+
+	for partnerId, req := range s.requests {
+		switch req.rt {
+		case Sent:
+			continue
+		case Receive:
+			s.deleteReceiveRequest(req)
+			delete(s.requests, partnerId)
+		}
+	}
+
+	if err := s.save(); err != nil {
+		jww.FATAL.Panicf("Failed to store updated request map after "+
+			"deleting all receive requests: %+v", err)
+	}
+
+	return nil
+}
+
+// deleteSentRequest is a helper function which deletes a Sent request from storage.
+func (s *Store) deleteSentRequest(r *request) {
+	delete(s.fingerprints, r.sent.fingerprint)
+	if err := r.sent.delete(); err != nil {
+		jww.FATAL.Panicf("Failed to delete sent request: %+v", err)
+	}
+}
+
+// deleteReceiveRequest is a helper function which deletes a Receive request from storage.
+func (s *Store) deleteReceiveRequest(r *request) {
+	if err := util.DeleteContact(s.kv, r.receive.ID); err != nil {
+		jww.FATAL.Panicf("Failed to delete recieved request "+
+			"contact: %+v", err)
+	}
+}
diff --git a/storage/auth/store_test.go b/storage/auth/store_test.go
index b1c4bb92b95c98eadc23db6e66d1ab5e353efc51..f1f10414836eb6c37596593322d112dbcfcc9394 100644
--- a/storage/auth/store_test.go
+++ b/storage/auth/store_test.go
@@ -8,6 +8,7 @@
 package auth
 
 import (
+	"bytes"
 	"github.com/cloudflare/circl/dh/sidh"
 	sidhinterface "gitlab.com/elixxir/client/interfaces/sidh"
 	util "gitlab.com/elixxir/client/storage/utility"
@@ -23,6 +24,7 @@ import (
 	"io"
 	"math/rand"
 	"reflect"
+	"sort"
 	"sync"
 	"testing"
 )
@@ -764,6 +766,309 @@ func TestStore_Delete_RequestNotInMap(t *testing.T) {
 	}
 }
 
+// Unit test of Store.GetAllReceived.
+func TestStore_GetAllReceived(t *testing.T) {
+	s, _, _ := makeTestStore(t)
+	numReceived := 10
+
+	expectContactList := make([]contact.Contact, 0, numReceived)
+	// Add multiple received contact requests
+	for i := 0; i < numReceived; i++ {
+		c := contact.Contact{ID: id.NewIdFromUInt(rand.Uint64(), id.User, t)}
+		rng := csprng.NewSystemRNG()
+		_, sidhPubKey := genSidhAKeys(rng)
+
+		if err := s.AddReceived(c, sidhPubKey); err != nil {
+			t.Fatalf("AddReceived() returned an error: %+v", err)
+		}
+
+		expectContactList = append(expectContactList, c)
+	}
+
+	// Check that GetAllReceived returns all contacts
+	receivedContactList := s.GetAllReceived()
+	if len(receivedContactList) != numReceived {
+		t.Errorf("GetAllReceived did not return expected amount of contacts."+
+			"\nExpected: %d"+
+			"\nReceived: %d", numReceived, len(receivedContactList))
+	}
+
+	// Sort expected and received lists so that they are in the same order
+	// since extraction from a map does not maintain order
+	sort.Slice(expectContactList, func(i, j int) bool {
+		return bytes.Compare(expectContactList[i].ID.Bytes(), expectContactList[j].ID.Bytes()) == -1
+	})
+	sort.Slice(receivedContactList, func(i, j int) bool {
+		return bytes.Compare(receivedContactList[i].ID.Bytes(), receivedContactList[j].ID.Bytes()) == -1
+	})
+
+	// Check validity of contacts
+	if !reflect.DeepEqual(expectContactList, receivedContactList) {
+		t.Errorf("GetAllReceived did not return expected contact list."+
+			"\nExpected: %+v"+
+			"\nReceived: %+v", expectContactList, receivedContactList)
+	}
+
+}
+
+// Tests that Store.GetAllReceived returns an empty list when there are no
+// received requests.
+func TestStore_GetAllReceived_EmptyList(t *testing.T) {
+	s, _, _ := makeTestStore(t)
+
+	// Check that GetAllReceived returns all contacts
+	receivedContactList := s.GetAllReceived()
+	if len(receivedContactList) != 0 {
+		t.Errorf("GetAllReceived did not return expected amount of contacts."+
+			"\nExpected: %d"+
+			"\nReceived: %d", 0, len(receivedContactList))
+	}
+
+	// Add Sent and Receive requests
+	for i := 0; i < 10; i++ {
+		partnerID := id.NewIdFromUInt(rand.Uint64(), id.User, t)
+		rng := csprng.NewSystemRNG()
+		sidhPrivKey, sidhPubKey := genSidhAKeys(rng)
+		sr := &SentRequest{
+			kv:                      s.kv,
+			partner:                 partnerID,
+			partnerHistoricalPubKey: s.grp.NewInt(1),
+			myPrivKey:               s.grp.NewInt(2),
+			myPubKey:                s.grp.NewInt(3),
+			mySidHPrivKeyA:          sidhPrivKey,
+			mySidHPubKeyA:           sidhPubKey,
+			fingerprint:             format.Fingerprint{5},
+		}
+		if err := s.AddSent(sr.partner, sr.partnerHistoricalPubKey,
+			sr.myPrivKey, sr.myPubKey, sr.mySidHPrivKeyA,
+			sr.mySidHPubKeyA, sr.fingerprint); err != nil {
+			t.Fatalf("AddSent() returned an error: %+v", err)
+		}
+	}
+
+	// Check that GetAllReceived returns all contacts
+	receivedContactList = s.GetAllReceived()
+	if len(receivedContactList) != 0 {
+		t.Errorf("GetAllReceived did not return expected amount of contacts. "+
+			"It may be pulling from Sent Requests."+
+			"\nExpected: %d"+
+			"\nReceived: %d", 0, len(receivedContactList))
+	}
+
+}
+
+// Tests that Store.GetAllReceived returns only Sent requests when there
+// are both Sent and Receive requests in Store.
+func TestStore_GetAllReceived_MixSentReceived(t *testing.T) {
+	s, _, _ := makeTestStore(t)
+	numReceived := 10
+
+	// Add multiple received contact requests
+	for i := 0; i < numReceived; i++ {
+		// Add received request
+		c := contact.Contact{ID: id.NewIdFromUInt(rand.Uint64(), id.User, t)}
+		rng := csprng.NewSystemRNG()
+		_, sidhPubKey := genSidhAKeys(rng)
+
+		if err := s.AddReceived(c, sidhPubKey); err != nil {
+			t.Fatalf("AddReceived() returned an error: %+v", err)
+		}
+
+		// Add sent request
+		partnerID := id.NewIdFromUInt(rand.Uint64(), id.User, t)
+		sidhPrivKey, sidhPubKey := genSidhAKeys(rng)
+		sr := &SentRequest{
+			kv:                      s.kv,
+			partner:                 partnerID,
+			partnerHistoricalPubKey: s.grp.NewInt(1),
+			myPrivKey:               s.grp.NewInt(2),
+			myPubKey:                s.grp.NewInt(3),
+			mySidHPrivKeyA:          sidhPrivKey,
+			mySidHPubKeyA:           sidhPubKey,
+			fingerprint:             format.Fingerprint{5},
+		}
+		if err := s.AddSent(sr.partner, sr.partnerHistoricalPubKey,
+			sr.myPrivKey, sr.myPubKey, sr.mySidHPrivKeyA,
+			sr.mySidHPubKeyA, sr.fingerprint); err != nil {
+			t.Fatalf("AddSent() returned an error: %+v", err)
+		}
+	}
+
+	// Check that GetAllReceived returns all contacts
+	receivedContactList := s.GetAllReceived()
+	if len(receivedContactList) != numReceived {
+		t.Errorf("GetAllReceived did not return expected amount of contacts. "+
+			"It may be pulling from Sent Requests."+
+			"\nExpected: %d"+
+			"\nReceived: %d", numReceived, len(receivedContactList))
+	}
+
+}
+
+// Unit test.
+func TestStore_DeleteReceiveRequests(t *testing.T) {
+	s, _, _ := makeTestStore(t)
+	c := contact.Contact{ID: id.NewIdFromUInt(rand.Uint64(), id.User, t)}
+	rng := csprng.NewSystemRNG()
+	_, sidhPubKey := genSidhAKeys(rng)
+	if err := s.AddReceived(c, sidhPubKey); err != nil {
+		t.Fatalf("AddReceived() returned an error: %+v", err)
+	}
+	if _, _, err := s.GetReceivedRequest(c.ID); err != nil {
+		t.Fatalf("GetReceivedRequest() returned an error: %+v", err)
+	}
+
+	err := s.DeleteReceiveRequests()
+	if err != nil {
+		t.Fatalf("DeleteReceiveRequests returned an error: %+v", err)
+	}
+
+	if s.requests[*c.ID] != nil {
+		t.Errorf("delete() failed to delete request for user %s.", c.ID)
+	}
+}
+
+// Unit test.
+func TestStore_DeleteSentRequests(t *testing.T) {
+	s, _, _ := makeTestStore(t)
+	partnerID := id.NewIdFromUInt(rand.Uint64(), id.User, t)
+	rng := csprng.NewSystemRNG()
+	sidhPrivKey, sidhPubKey := genSidhAKeys(rng)
+	sr := &SentRequest{
+		kv:                      s.kv,
+		partner:                 partnerID,
+		partnerHistoricalPubKey: s.grp.NewInt(1),
+		myPrivKey:               s.grp.NewInt(2),
+		myPubKey:                s.grp.NewInt(3),
+		mySidHPrivKeyA:          sidhPrivKey,
+		mySidHPubKeyA:           sidhPubKey,
+		fingerprint:             format.Fingerprint{5},
+	}
+	if err := s.AddSent(sr.partner, sr.partnerHistoricalPubKey,
+		sr.myPrivKey, sr.myPubKey, sr.mySidHPrivKeyA,
+		sr.mySidHPubKeyA, sr.fingerprint); err != nil {
+		t.Fatalf("AddSent() returned an error: %+v", err)
+	}
+
+	err := s.DeleteSentRequests()
+	if err != nil {
+		t.Fatalf("DeleteSentRequests returned an error: %+v", err)
+	}
+
+	if s.requests[*sr.partner] != nil {
+		t.Errorf("delete() failed to delete request for user %s.",
+			sr.partner)
+	}
+
+	if _, exists := s.fingerprints[sr.fingerprint]; exists {
+		t.Errorf("delete() failed to delete fingerprint for fp %v.",
+			sr.fingerprint)
+	}
+}
+
+// Tests that DeleteSentRequests does not affect receive requests in map
+func TestStore_DeleteSentRequests_ReceiveInMap(t *testing.T) {
+	s, _, _ := makeTestStore(t)
+	c := contact.Contact{ID: id.NewIdFromUInt(rand.Uint64(), id.User, t)}
+	rng := csprng.NewSystemRNG()
+	_, sidhPubKey := genSidhAKeys(rng)
+	if err := s.AddReceived(c, sidhPubKey); err != nil {
+		t.Fatalf("AddReceived() returned an error: %+v", err)
+	}
+
+	err := s.DeleteSentRequests()
+	if err != nil {
+		t.Fatalf("DeleteSentRequests returned an error: %+v", err)
+	}
+
+	if s.requests[*c.ID] == nil {
+		t.Fatalf("DeleteSentRequests removes receive requests!")
+	}
+
+}
+
+// Tests that DeleteReceiveRequests does not affect sent requests in map
+func TestStore_DeleteReceiveRequests_SentInMap(t *testing.T) {
+	s, _, _ := makeTestStore(t)
+	partnerID := id.NewIdFromUInt(rand.Uint64(), id.User, t)
+	rng := csprng.NewSystemRNG()
+	sidhPrivKey, sidhPubKey := genSidhAKeys(rng)
+	sr := &SentRequest{
+		kv:                      s.kv,
+		partner:                 partnerID,
+		partnerHistoricalPubKey: s.grp.NewInt(1),
+		myPrivKey:               s.grp.NewInt(2),
+		myPubKey:                s.grp.NewInt(3),
+		mySidHPrivKeyA:          sidhPrivKey,
+		mySidHPubKeyA:           sidhPubKey,
+		fingerprint:             format.Fingerprint{5},
+	}
+	if err := s.AddSent(sr.partner, sr.partnerHistoricalPubKey,
+		sr.myPrivKey, sr.myPubKey, sr.mySidHPrivKeyA,
+		sr.mySidHPubKeyA, sr.fingerprint); err != nil {
+		t.Fatalf("AddSent() returned an error: %+v", err)
+	}
+
+	err := s.DeleteReceiveRequests()
+	if err != nil {
+		t.Fatalf("DeleteSentRequests returned an error: %+v", err)
+	}
+
+	if s.requests[*partnerID] == nil {
+		t.Fatalf("DeleteReceiveRequests removes sent requests!")
+	}
+
+}
+
+// Unit test.
+func TestStore_DeleteAllRequests(t *testing.T) {
+	s, _, _ := makeTestStore(t)
+	partnerID := id.NewIdFromUInt(rand.Uint64(), id.User, t)
+	rng := csprng.NewSystemRNG()
+	sidhPrivKey, sidhPubKey := genSidhAKeys(rng)
+	sr := &SentRequest{
+		kv:                      s.kv,
+		partner:                 partnerID,
+		partnerHistoricalPubKey: s.grp.NewInt(1),
+		myPrivKey:               s.grp.NewInt(2),
+		myPubKey:                s.grp.NewInt(3),
+		mySidHPrivKeyA:          sidhPrivKey,
+		mySidHPubKeyA:           sidhPubKey,
+		fingerprint:             format.Fingerprint{5},
+	}
+	if err := s.AddSent(sr.partner, sr.partnerHistoricalPubKey,
+		sr.myPrivKey, sr.myPubKey, sr.mySidHPrivKeyA,
+		sr.mySidHPubKeyA, sr.fingerprint); err != nil {
+		t.Fatalf("AddSent() returned an error: %+v", err)
+	}
+
+	c := contact.Contact{ID: id.NewIdFromUInt(rand.Uint64(), id.User, t)}
+	_, sidhPubKey = genSidhAKeys(rng)
+	if err := s.AddReceived(c, sidhPubKey); err != nil {
+		t.Fatalf("AddReceived() returned an error: %+v", err)
+	}
+
+	err := s.DeleteAllRequests()
+	if err != nil {
+		t.Fatalf("DeleteAllRequests returned an error: %+v", err)
+	}
+
+	if s.requests[*sr.partner] != nil {
+		t.Errorf("delete() failed to delete request for user %s.",
+			sr.partner)
+	}
+
+	if _, exists := s.fingerprints[sr.fingerprint]; exists {
+		t.Errorf("delete() failed to delete fingerprint for fp %v.",
+			sr.fingerprint)
+	}
+
+	if s.requests[*c.ID] != nil {
+		t.Errorf("delete() failed to delete request for user %s.", c.ID)
+	}
+
+}
+
 func makeTestStore(t *testing.T) (*Store, *versioned.KV, []*cyclic.Int) {
 	kv := versioned.NewKV(make(ekv.Memstore))
 	grp := cyclic.NewGroup(large.NewInt(173), large.NewInt(0))
diff --git a/storage/e2e/manager.go b/storage/e2e/manager.go
index d8ec60e65209a217e45bcc28d2387ce6eadd1d9d..f6c000f78ff002e3a3c82db143b8e783ca8a497e 100644
--- a/storage/e2e/manager.go
+++ b/storage/e2e/manager.go
@@ -292,3 +292,9 @@ func (m *Manager) GetSilentPreimage() []byte {
 func (m *Manager) GetFileTransferPreimage() []byte {
 	return preimage.Generate(m.GetRelationshipFingerprintBytes(), preimage.EndFT)
 }
+
+// GetGroupRequestPreimage returns a hash of the unique
+// fingerprint for group requests received from this user.
+func (m *Manager) GetGroupRequestPreimage() []byte {
+	return preimage.Generate(m.GetRelationshipFingerprintBytes(), preimage.GroupRq)
+}
\ No newline at end of file
diff --git a/storage/fileTransfer/fileMessage.go b/storage/fileTransfer/fileMessage.go
new file mode 100644
index 0000000000000000000000000000000000000000..03269326bbeab891e4678c888994fc35ea1026a6
--- /dev/null
+++ b/storage/fileTransfer/fileMessage.go
@@ -0,0 +1,119 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright © 2020 xx network SEZC                                          //
+//                                                                           //
+// Use of this source code is governed by a license that can be found in the //
+// LICENSE file                                                              //
+///////////////////////////////////////////////////////////////////////////////
+
+package fileTransfer
+
+import (
+	"encoding/binary"
+	"github.com/pkg/errors"
+)
+
+// Size constants.
+const (
+	partNumLen = 2          // The length of the part number in bytes
+	FmMinSize  = partNumLen // Minimum size for the PartMessage
+)
+
+// Error messages.
+const (
+	newFmSizeErr       = "size of external payload (%d) must be greater than %d"
+	unmarshalFmSizeErr = "size of passed in bytes (%d) must be greater than %d"
+	setFileFmErr       = "length of part bytes (%d) must be smaller than maximum payload size %d"
+)
+
+/*
++-----------------------------------------+
+|          CMIX Message Contents          |
++---------+-------------+-----------------+
+| Padding | Part Number |    File Data    |
+| 8 bytes |   2 bytes   | remaining space |
++---------+-------------+-----------------+
+*/
+
+// PartMessage contains part of the data being transferred and 256-bit nonce
+// that is used as a nonce.
+type PartMessage struct {
+	data    []byte // Serial of all contents
+	partNum []byte // The part number of the file
+	part    []byte // File part data
+}
+
+// NewPartMessage generates a new part message that fits into the specified
+// external payload size. An error is returned if the external payload size is
+// too small to fit the part message.
+func NewPartMessage(externalPayloadSize int) (PartMessage, error) {
+	if externalPayloadSize < FmMinSize {
+		return PartMessage{},
+			errors.Errorf(newFmSizeErr, externalPayloadSize, FmMinSize)
+	}
+
+	return MapPartMessage(make([]byte, externalPayloadSize)), nil
+}
+
+// MapPartMessage maps the data to the components of a PartMessage. It is mapped
+// by reference; a copy is not made.
+func MapPartMessage(data []byte) PartMessage {
+	return PartMessage{
+		data:    data,
+		partNum: data[:partNumLen],
+		part:    data[partNumLen:],
+	}
+}
+
+// UnmarshalPartMessage converts the bytes into a PartMessage. An error is
+// returned if the size of the data is too small for a PartMessage.
+func UnmarshalPartMessage(b []byte) (PartMessage, error) {
+	if len(b) < FmMinSize {
+		return PartMessage{},
+			errors.Errorf(unmarshalFmSizeErr, len(b), FmMinSize)
+	}
+
+	return MapPartMessage(b), nil
+}
+
+// Marshal returns the byte representation of the PartMessage.
+func (m PartMessage) Marshal() []byte {
+	b := make([]byte, len(m.data))
+	copy(b,m.data)
+	return b
+}
+
+// GetPartNum returns the file part number.
+func (m PartMessage) GetPartNum() uint16 {
+	return binary.LittleEndian.Uint16(m.partNum)
+}
+
+// SetPartNum sets the file part number.
+func (m PartMessage) SetPartNum(num uint16) {
+	b := make([]byte, partNumLen)
+	binary.LittleEndian.PutUint16(b, num)
+	copy(m.partNum, b)
+}
+
+// GetPart returns the file part data from the message.
+func (m PartMessage) GetPart() []byte {
+	b := make([]byte, len(m.part))
+	copy(b,m.part)
+	return b
+}
+
+// SetPart sets the PartMessage part to the given bytes. An error is returned if
+// the size of the provided part data is too large to store.
+func (m PartMessage) SetPart(b []byte) error {
+	if len(b) > len(m.part) {
+		return errors.Errorf(setFileFmErr, len(b), len(m.part))
+	}
+
+	copy(m.part, b)
+
+	return nil
+}
+
+// GetPartSize returns the number of bytes available to store part data.
+func (m PartMessage) GetPartSize() int {
+	return len(m.part)
+}
diff --git a/storage/fileTransfer/fileMessage_test.go b/storage/fileTransfer/fileMessage_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..ad565931e6d44d60f887552e9cb4c1a954fe5cf0
--- /dev/null
+++ b/storage/fileTransfer/fileMessage_test.go
@@ -0,0 +1,241 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright © 2020 xx network SEZC                                          //
+//                                                                           //
+// Use of this source code is governed by a license that can be found in the //
+// LICENSE file                                                              //
+///////////////////////////////////////////////////////////////////////////////
+
+package fileTransfer
+
+import (
+	"bytes"
+	"encoding/binary"
+	"fmt"
+	"math/rand"
+	"testing"
+)
+
+// Tests that NewPartMessage returns a PartMessage of the expected size.
+func Test_newPartMessage(t *testing.T) {
+	externalPayloadSize := 256
+
+	fm, err := NewPartMessage(externalPayloadSize)
+	if err != nil {
+		t.Errorf("NewPartMessage returned an error: %+v", err)
+	}
+
+	if len(fm.data) != externalPayloadSize {
+		t.Errorf("Size of PartMessage data does not match payload size."+
+			"\nexpected: %d\nreceived: %d", externalPayloadSize, len(fm.data))
+	}
+}
+
+// Error path: tests that NewPartMessage returns the expected error when the
+// external payload size is too small.
+func Test_newPartMessage_SmallPayloadSizeError(t *testing.T) {
+	externalPayloadSize := FmMinSize - 1
+	expectedErr := fmt.Sprintf(newFmSizeErr, externalPayloadSize, FmMinSize)
+
+	_, err := NewPartMessage(externalPayloadSize)
+	if err == nil || err.Error() != expectedErr {
+		t.Errorf("NewPartMessage did not return the expected error when the "+
+			"given external payload size is too small."+
+			"\nexpected: %s\nreceived: %+v", expectedErr, err)
+	}
+}
+
+// Tests that MapPartMessage maps the data to the correct parts of the
+// PartMessage.
+func Test_mapPartMessage(t *testing.T) {
+	// Generate expected values
+	_, expectedData, expectedPartNum, expectedFile :=
+		newRandomFileMessage()
+
+	fm := MapPartMessage(expectedData)
+
+	if !bytes.Equal(expectedData, fm.data) {
+		t.Errorf("Incorrect data.\nexpected: %q\nreceived: %q",
+			expectedData, fm.data)
+	}
+
+	if !bytes.Equal(expectedPartNum, fm.partNum) {
+		t.Errorf("Incorrect part number.\nexpected: %q\nreceived: %q",
+			expectedPartNum, fm.partNum)
+	}
+
+	if !bytes.Equal(expectedFile, fm.part) {
+		t.Errorf("Incorrect part data.\nexpected: %q\nreceived: %q",
+			expectedFile, fm.part)
+	}
+
+}
+
+// Tests that UnmarshalPartMessage returns a PartMessage with the expected
+// values.
+func Test_unmarshalPartMessage(t *testing.T) {
+	// Generate expected values
+	_, expectedData, expectedPartNumb, expectedFile :=
+		newRandomFileMessage()
+
+	fm, err := UnmarshalPartMessage(expectedData)
+	if err != nil {
+		t.Errorf("UnmarshalPartMessage return an error: %+v", err)
+	}
+
+	if !bytes.Equal(expectedData, fm.data) {
+		t.Errorf("Incorrect data.\nexpected: %q\nreceived: %q",
+			expectedData, fm.data)
+	}
+
+	if !bytes.Equal(expectedPartNumb, fm.partNum) {
+		t.Errorf("Incorrect part number.\nexpected: %q\nreceived: %q",
+			expectedPartNumb, fm.partNum)
+	}
+
+	if !bytes.Equal(expectedFile, fm.part) {
+		t.Errorf("Incorrect part data.\nexpected: %q\nreceived: %q",
+			expectedFile, fm.part)
+	}
+}
+
+// Error path: tests that UnmarshalPartMessage returns the expected error when
+// the provided data is too small to be unmarshalled into a PartMessage.
+func Test_unmarshalPartMessage_SizeError(t *testing.T) {
+	data := make([]byte, FmMinSize-1)
+	expectedErr := fmt.Sprintf(unmarshalFmSizeErr, len(data), FmMinSize)
+
+	_, err := UnmarshalPartMessage(data)
+	if err == nil || err.Error() != expectedErr {
+		t.Errorf("UnmarshalPartMessage did not return the expected error when "+
+			"the given bytes are too small to be a PartMessage."+
+			"\nexpected: %s\nreceived: %+v", expectedErr, err)
+	}
+}
+
+// Tests that PartMessage.Marshal returns the correct data.
+func Test_fileMessage_marshal(t *testing.T) {
+	fm, expectedData, _, _ := newRandomFileMessage()
+
+	data := fm.Marshal()
+
+	if !bytes.Equal(expectedData, data) {
+		t.Errorf("Marshalled data does not match expected."+
+			"\nexpected: %q\nreceived: %q", expectedData, data)
+	}
+}
+
+// Tests that PartMessage.GetPartNum returns the correct part number.
+func Test_fileMessage_getPartNum(t *testing.T) {
+	fm, _, expectedPartNum, _ := newRandomFileMessage()
+
+	partNum := fm.GetPartNum()
+	expected := binary.LittleEndian.Uint16(expectedPartNum)
+
+	if expected != partNum {
+		t.Errorf("Part number does not match expected."+
+			"\nexpected: %d\nreceived: %d", expected, partNum)
+	}
+}
+
+// Tests that PartMessage.SetPartNum sets the correct part number.
+func Test_fileMessage_setPartNum(t *testing.T) {
+	fm, err := NewPartMessage(256)
+	if err != nil {
+		t.Errorf("Failed to create new PartMessage: %+v", err)
+	}
+
+	expectedPartNum := make([]byte, partNumLen)
+	rand.New(rand.NewSource(42)).Read(expectedPartNum)
+	expected := binary.LittleEndian.Uint16(expectedPartNum)
+
+	fm.SetPartNum(expected)
+
+	if expected != fm.GetPartNum() {
+		t.Errorf("Failed to set correct part number.\nexpected: %d\nreceived: %d",
+			expected, fm.GetPartNum())
+	}
+}
+
+// Tests that PartMessage.GetPart returns the correct part data.
+func Test_fileMessage_getFile(t *testing.T) {
+	fm, _, _, expectedFile := newRandomFileMessage()
+
+	file := fm.GetPart()
+
+	if !bytes.Equal(expectedFile, file) {
+		t.Errorf("File data does not match expected."+
+			"\nexpected: %q\nreceived: %q", expectedFile, file)
+	}
+}
+
+// Tests that PartMessage.SetPart sets the correct part data.
+func Test_fileMessage_setFile(t *testing.T) {
+	fm, err := NewPartMessage(256)
+	if err != nil {
+		t.Errorf("Failed to create new PartMessage: %+v", err)
+	}
+
+	fileData := make([]byte, 64)
+	rand.New(rand.NewSource(42)).Read(fileData)
+	expectedFile := make([]byte, fm.GetPartSize())
+	copy(expectedFile, fileData)
+
+	err = fm.SetPart(expectedFile)
+	if err != nil {
+		t.Errorf("SetPart returned an error: %+v", err)
+	}
+
+	if !bytes.Equal(expectedFile, fm.GetPart()) {
+		t.Errorf("Failed to set correct part data.\nexpected: %q\nreceived: %q",
+			expectedFile, fm.GetPart())
+	}
+}
+
+// Error path: tests that PartMessage.SetPart returns the expected error when
+// the provided part data is too large for the message.
+func Test_fileMessage_setFile_FileTooLargeError(t *testing.T) {
+	fm, err := NewPartMessage(FmMinSize + 1)
+	if err != nil {
+		t.Errorf("Failed to create new PartMessage: %+v", err)
+	}
+
+	expectedErr := fmt.Sprintf(setFileFmErr, fm.GetPartSize()+1, fm.GetPartSize())
+
+	err = fm.SetPart(make([]byte, fm.GetPartSize()+1))
+	if err == nil || err.Error() != expectedErr {
+		t.Errorf("SetPart did not return the expected error when the given "+
+			"part data is too large to fit in the PartMessage."+
+			"\nexpected: %s\nreceived: %+v", expectedErr, err)
+	}
+}
+
+// Tests that PartMessage.GetPartSize returns the expected available space for
+// the part data.
+func Test_fileMessage_getFileSize(t *testing.T) {
+	expectedSize := 256
+
+	fm, err := NewPartMessage(FmMinSize + expectedSize)
+	if err != nil {
+		t.Errorf("Failed to create new PartMessage: %+v", err)
+	}
+
+	if expectedSize != fm.GetPartSize() {
+		t.Errorf("File size incorrect.\nexpected: %d\nreceived: %d",
+			expectedSize, fm.GetPartSize())
+	}
+}
+
+// newRandomFileMessage generates a new PartMessage filled with random data and
+// return the PartMessage and its individual parts.
+func newRandomFileMessage() (PartMessage, []byte, []byte, []byte) {
+	prng := rand.New(rand.NewSource(42))
+	partNum := make([]byte, partNumLen)
+	prng.Read(partNum)
+	part := make([]byte, 64)
+	prng.Read(part)
+	data := append(partNum, part...)
+
+	fm := MapPartMessage(data)
+
+	return fm, data, partNum, part
+}
diff --git a/storage/fileTransfer/partStore_test.go b/storage/fileTransfer/partStore_test.go
index 5e3eabae208b88c8bfed5c2131727939c7c448e7..597fea4b20a6871cf44310e97cbd3c4f06c8aa59 100644
--- a/storage/fileTransfer/partStore_test.go
+++ b/storage/fileTransfer/partStore_test.go
@@ -12,6 +12,7 @@ import (
 	"encoding/binary"
 	"gitlab.com/elixxir/client/storage/versioned"
 	"gitlab.com/elixxir/ekv"
+	"gitlab.com/elixxir/primitives/format"
 	"io"
 	"math/rand"
 	"reflect"
@@ -521,7 +522,10 @@ func Test_makePartsKey_consistency(t *testing.T) {
 func newRandomPartStore(numParts uint16, kv *versioned.KV, prng io.Reader,
 	t *testing.T) (*partStore, []byte) {
 
-	partSize := 64
+	cmixMsg := format.NewMessage(format.MinimumPrimeSize)
+
+	partData, _ := NewPartMessage(cmixMsg.ContentsSize())
+	partSize := partData.GetPartSize()
 
 	ps, err := newPartStore(kv, numParts)
 	if err != nil {
diff --git a/storage/fileTransfer/receiveFileTransfers.go b/storage/fileTransfer/receiveFileTransfers.go
index ac9f262e6b2f6920fad1cf17a0453e3f89ace8c2..0c7bffdc6881e96fd79026deb58c98195b05dfbf 100644
--- a/storage/fileTransfer/receiveFileTransfers.go
+++ b/storage/fileTransfer/receiveFileTransfers.go
@@ -35,7 +35,7 @@ const (
 	getReceivedTransferErr    = "received transfer with ID %s not found"
 	addTransferNewIdErr       = "could not generate new transfer ID: %+v"
 	noFingerprintErr          = "no part found with fingerprint %s"
-	addPartErr                = "failed to add part number %d/%d to transfer %s: %+v"
+	addPartErr                = "failed to add part to transfer %s: %+v"
 	deleteReceivedTransferErr = "failed to delete received transfer with ID %s from store: %+v"
 )
 
@@ -176,17 +176,18 @@ func (rft *ReceivedFileTransfersStore) DeleteTransfer(tid ftCrypto.TransferID) e
 // transfer that the part was added to so that a progress callback can be
 // called. Returns the transfer ID so that it can be used for logging. Also
 // returns of the transfer is complete after adding the part.
-func (rft *ReceivedFileTransfersStore) AddPart(encryptedPart, padding,
-	mac []byte, partNum uint16, fp format.Fingerprint) (*ReceivedTransfer,
+func (rft *ReceivedFileTransfersStore) AddPart(cmixMsg format.Message) (*ReceivedTransfer,
 	ftCrypto.TransferID, bool, error) {
 	rft.mux.Lock()
 	defer rft.mux.Unlock()
 
+	keyfp := cmixMsg.GetKeyFP()
+
 	// Lookup the part info for the given fingerprint
-	info, exists := rft.info[fp]
+	info, exists := rft.info[cmixMsg.GetKeyFP()]
 	if !exists {
 		return nil, ftCrypto.TransferID{}, false,
-			errors.Errorf(noFingerprintErr, fp)
+			errors.Errorf(noFingerprintErr, keyfp)
 	}
 
 	// Lookup the transfer with the ID in the part info
@@ -197,15 +198,14 @@ func (rft *ReceivedFileTransfersStore) AddPart(encryptedPart, padding,
 	}
 
 	// Add the part to the transfer
-	completed, err := transfer.AddPart(
-		encryptedPart, padding, mac, partNum, info.fpNum)
+	completed, err := transfer.AddPart(cmixMsg, info.fpNum)
 	if err != nil {
 		return transfer, info.id, false, errors.Errorf(
-			addPartErr, partNum, transfer.numParts, info.id, err)
+			addPartErr, info.id, err)
 	}
 
 	// Remove the part info from the map
-	delete(rft.info, fp)
+	delete(rft.info, keyfp)
 
 	return transfer, info.id, completed, nil
 }
diff --git a/storage/fileTransfer/receiveFileTransfers_test.go b/storage/fileTransfer/receiveFileTransfers_test.go
index 0c048246023069538f20870b89a8a2dbb60d1d47..4fd245ec4d902d99f8389a8a4a1a6393c95d1432 100644
--- a/storage/fileTransfer/receiveFileTransfers_test.go
+++ b/storage/fileTransfer/receiveFileTransfers_test.go
@@ -345,14 +345,26 @@ func TestReceivedFileTransfersStore_AddPart(t *testing.T) {
 	}
 
 	// Create encrypted part
+
+	cmixMsg := format.NewMessage(format.MinimumPrimeSize)
+
 	expectedData := []byte("test")
+
 	partNum, fpNum := uint16(1), uint16(1)
-	encryptedPart, mac, padding := newEncryptedPartData(
-		key, expectedData, fpNum, t)
+
+	partData, _ := NewPartMessage(cmixMsg.ContentsSize())
+	partData.SetPartNum(partNum)
+	_ = partData.SetPart(expectedData)
+
 	fp := ftCrypto.GenerateFingerprint(key, fpNum)
+	encryptedPart, mac, err := ftCrypto.EncryptPart(key, partData.Marshal(), fpNum, fp)
+
+	cmixMsg.SetKeyFP(fp)
+	cmixMsg.SetContents(encryptedPart)
+	cmixMsg.SetMac(mac)
 
 	// Add encrypted part
-	rt, _, _, err := rft.AddPart(encryptedPart, padding, mac, partNum, fp)
+	rt, _, _, err := rft.AddPart(cmixMsg)
 	if err != nil {
 		t.Errorf("AddPart returned an error: %+v", err)
 	}
@@ -373,9 +385,9 @@ func TestReceivedFileTransfersStore_AddPart(t *testing.T) {
 
 	// Check that the correct part was stored
 	receivedPart := expectedRT.receivedParts.parts[partNum]
-	if !bytes.Equal(receivedPart, expectedData) {
+	if !bytes.Equal(receivedPart[:len(expectedData)], expectedData) {
 		t.Errorf("Part in memory is not expected."+
-			"\nexpected: %q\nreceived: %q", expectedData, receivedPart)
+			"\nexpected: %q\nreceived: %q", expectedData, receivedPart[:len(expectedData)])
 	}
 }
 
@@ -391,9 +403,12 @@ func TestReceivedFileTransfersStore_AddPart_NoFingerprintError(t *testing.T) {
 	// Create encrypted part
 	fp := format.NewFingerprint([]byte("invalidTransferKey"))
 
+	msg := format.NewMessage(1000)
+	msg.SetKeyFP(fp)
+
 	// Add encrypted part
 	expectedErr := fmt.Sprintf(noFingerprintErr, fp)
-	_, _, _, err = rft.AddPart([]byte{}, []byte{}, []byte{}, 0, fp)
+	_, _, _, err = rft.AddPart(msg)
 	if err == nil || err.Error() != expectedErr {
 		t.Errorf("AddPart did not return the expected error when no part for "+
 			"the fingerprint exists.\nexpected: %s\nreceived: %+v",
@@ -425,9 +440,12 @@ func TestReceivedFileTransfersStore_AddPart_NoTransferError(t *testing.T) {
 	invalidTid, _ := ftCrypto.NewTransferID(prng)
 	rft.info[fp].id = invalidTid
 
+	msg := format.NewMessage(1000)
+	msg.SetKeyFP(fp)
+
 	// Add encrypted part
 	expectedErr := fmt.Sprintf(getReceivedTransferErr, invalidTid)
-	_, _, _, err = rft.AddPart([]byte{}, []byte{}, []byte{}, 0, fp)
+	_, _, _, err = rft.AddPart(msg)
 	if err == nil || err.Error() != expectedErr {
 		t.Errorf("AddPart did not return the expected error when no transfer "+
 			"for the ID exists.\nexpected: %s\nreceived: %+v", expectedErr, err)
@@ -455,14 +473,24 @@ func TestReceivedFileTransfersStore_AddPart_AddPartError(t *testing.T) {
 
 	// Create encrypted part
 	partNum, fpNum := uint16(1), uint16(1)
-	encryptedPart := []byte("invalidPart")
-	mac = []byte("invalidMAC")
-	padding := make([]byte, 24)
+	part := []byte("invalidPart")
+	mac = make([]byte, format.MacLen)
 	fp := ftCrypto.GenerateFingerprint(key, fpNum)
 
 	// Add encrypted part
-	expectedErr := fmt.Sprintf(addPartErr, partNum, numParts, tid, "")
-	_, _, _, err = rft.AddPart(encryptedPart, padding, mac, partNum, fp)
+	expectedErr := fmt.Sprintf(addPartErr, tid, "")
+
+	cmixMsg := format.NewMessage(format.MinimumPrimeSize)
+
+	partData, _ := NewPartMessage(cmixMsg.ContentsSize())
+	partData.SetPartNum(partNum)
+	_ = partData.SetPart(part)
+
+	cmixMsg.SetKeyFP(fp)
+	cmixMsg.SetContents(partData.Marshal())
+	cmixMsg.SetMac(mac)
+
+	_, _, _, err = rft.AddPart(cmixMsg)
 	if err == nil || !strings.Contains(err.Error(), expectedErr) {
 		t.Errorf("AddPart did not return the expected error when the "+
 			"encrypted part, padding, and MAC are invalid."+
diff --git a/storage/fileTransfer/receiveTransfer.go b/storage/fileTransfer/receiveTransfer.go
index df4bad66163245c9593bfbbc949d73ccf04b5bc3..b3e387a750d4cb6ba398c28195add01ec5b5763e 100644
--- a/storage/fileTransfer/receiveTransfer.go
+++ b/storage/fileTransfer/receiveTransfer.go
@@ -16,6 +16,7 @@ import (
 	"gitlab.com/elixxir/client/storage/utility"
 	"gitlab.com/elixxir/client/storage/versioned"
 	ftCrypto "gitlab.com/elixxir/crypto/fileTransfer"
+	"gitlab.com/elixxir/primitives/format"
 	"gitlab.com/xx_network/primitives/netTime"
 	"sync"
 	"time"
@@ -276,20 +277,25 @@ func (rt *ReceivedTransfer) AddProgressCB(
 // AddPart decrypts an encrypted file part, adds it to the list of received
 // parts and marks its fingerprint as used. Returns true if the part added was
 // the last in the transfer.
-func (rt *ReceivedTransfer) AddPart(encryptedPart, padding, mac []byte, partNum,
+func (rt *ReceivedTransfer) AddPart(cmixMsg format.Message,
 	fpNum uint16) (bool, error) {
 	rt.mux.Lock()
 	defer rt.mux.Unlock()
 
 	// Decrypt the encrypted file part
-	decryptedPart, err := ftCrypto.DecryptPart(
-		rt.key, encryptedPart, padding, mac, fpNum)
+	decryptedPart, err := ftCrypto.DecryptPart(rt.key,
+		cmixMsg.GetContents(), cmixMsg.GetMac(), fpNum, cmixMsg.GetKeyFP())
+	if err != nil {
+		return false, err
+	}
+
+	part, err := UnmarshalPartMessage(decryptedPart)
 	if err != nil {
 		return false, err
 	}
 
 	// Add the part to the list of parts
-	err = rt.receivedParts.addPart(decryptedPart, partNum)
+	err = rt.receivedParts.addPart(part.GetPart(), part.GetPartNum())
 	if err != nil {
 		return false, err
 	}
@@ -298,7 +304,7 @@ func (rt *ReceivedTransfer) AddPart(encryptedPart, padding, mac []byte, partNum,
 	rt.fpVector.Use(uint32(fpNum))
 
 	// Mark part as received
-	rt.receivedStatus.Use(uint32(partNum))
+	rt.receivedStatus.Use(uint32(part.GetPartNum()))
 
 	if rt.receivedStatus.GetNumUsed() >= uint32(rt.numParts) {
 		return true, nil
diff --git a/storage/fileTransfer/receiveTransfer_test.go b/storage/fileTransfer/receiveTransfer_test.go
index 7c0382ec2f038899e9f1d047b087e958e0795abc..281bb373822ee0642a6c02c07c814d402cd3d174 100644
--- a/storage/fileTransfer/receiveTransfer_test.go
+++ b/storage/fileTransfer/receiveTransfer_test.go
@@ -16,6 +16,7 @@ import (
 	"gitlab.com/elixxir/client/storage/versioned"
 	ftCrypto "gitlab.com/elixxir/crypto/fileTransfer"
 	"gitlab.com/elixxir/ekv"
+	"gitlab.com/elixxir/primitives/format"
 	"reflect"
 	"strings"
 	"sync"
@@ -538,13 +539,25 @@ func TestReceivedTransfer_AddPart(t *testing.T) {
 	_, rt, _ := newEmptyReceivedTransfer(16, 20, kv, t)
 
 	// Create encrypted part
+	cmixMsg := format.NewMessage(format.MinimumPrimeSize)
+
 	expectedData := []byte("test")
+
 	partNum, fpNum := uint16(1), uint16(1)
-	encryptedPart, mac, padding := newEncryptedPartData(
-		rt.key, expectedData, fpNum, t)
+
+	partData, _ := NewPartMessage(cmixMsg.ContentsSize())
+	partData.SetPartNum(partNum)
+	_ = partData.SetPart(expectedData)
+
+	fp := ftCrypto.GenerateFingerprint(rt.key, fpNum)
+	encryptedPart, mac, err := ftCrypto.EncryptPart(rt.key, partData.Marshal(), fpNum, fp)
+
+	cmixMsg.SetKeyFP(fp)
+	cmixMsg.SetContents(encryptedPart)
+	cmixMsg.SetMac(mac)
 
 	// Add encrypted part
-	complete, err := rt.AddPart(encryptedPart, padding, mac, partNum, fpNum)
+	complete, err := rt.AddPart(cmixMsg,fpNum)
 	if err != nil {
 		t.Errorf("AddPart returned an error: %+v", err)
 	}
@@ -556,9 +569,9 @@ func TestReceivedTransfer_AddPart(t *testing.T) {
 	receivedData, exists := rt.receivedParts.parts[partNum]
 	if !exists {
 		t.Errorf("Part #%d not found in part map.", partNum)
-	} else if !bytes.Equal(expectedData, receivedData) {
+	} else if !bytes.Equal(expectedData, receivedData[:len(expectedData)]) {
 		t.Fatalf("Part data in list does not match expected."+
-			"\nexpected: %+v\nreceived: %+v", expectedData, receivedData)
+			"\nexpected: %+v\nreceived: %+v", expectedData, receivedData[:len(expectedData)])
 	}
 
 	// Check that the fingerprint vector has correct values
@@ -594,14 +607,27 @@ func TestReceivedTransfer_AddPart_DecryptPartError(t *testing.T) {
 	_, rt, _ := newEmptyReceivedTransfer(16, 20, kv, t)
 
 	// Create encrypted part
-	Data := []byte("test")
+	cmixMsg := format.NewMessage(format.MinimumPrimeSize)
+
+	expectedData := []byte("test")
+
 	partNum, fpNum := uint16(1), uint16(1)
-	encryptedPart, _, padding := newEncryptedPartData(rt.key, Data, fpNum, t)
-	mac := []byte("invalidMAC")
+
+	partData, _ := NewPartMessage(cmixMsg.ContentsSize())
+	partData.SetPartNum(partNum)
+	_ = partData.SetPart(expectedData)
+
+	fp := ftCrypto.GenerateFingerprint(rt.key, fpNum)
+	encryptedPart, _, err := ftCrypto.EncryptPart(rt.key, partData.Marshal(), fpNum, fp)
+	badMac := make([]byte, format.MacLen)
+
+	cmixMsg.SetKeyFP(fp)
+	cmixMsg.SetContents(encryptedPart)
+	cmixMsg.SetMac(badMac)
 
 	// Add encrypted part
 	expectedErr := "reconstructed MAC from decrypting does not match MAC from sender"
-	_, err := rt.AddPart(encryptedPart, padding, mac, partNum, fpNum)
+	_, err = rt.AddPart(cmixMsg, fpNum)
 	if err == nil || err.Error() != expectedErr {
 		t.Errorf("AddPart did not return the expected error when the MAC is "+
 			"invalid.\nexpected: %s\nreceived: %+v", expectedErr, err)
@@ -670,11 +696,22 @@ func Test_loadReceivedTransfer(t *testing.T) {
 	// Create encrypted part
 	expectedData := []byte("test")
 	partNum, fpNum := uint16(1), uint16(1)
-	encryptedPart, mac, padding := newEncryptedPartData(
-		expectedRT.key, expectedData, fpNum, t)
+
+	cmixMsg := format.NewMessage(format.MinimumPrimeSize)
+
+	partData, _ := NewPartMessage(cmixMsg.ContentsSize())
+	partData.SetPartNum(partNum)
+	_ = partData.SetPart(expectedData)
+
+	fp := ftCrypto.GenerateFingerprint(expectedRT.key, fpNum)
+	encryptedPart, mac, err := ftCrypto.EncryptPart(expectedRT.key, partData.Marshal(), fpNum, fp)
+
+	cmixMsg.SetKeyFP(fp)
+	cmixMsg.SetContents(encryptedPart)
+	cmixMsg.SetMac(mac)
 
 	// Add encrypted part
-	_, err := expectedRT.AddPart(encryptedPart, padding, mac, partNum, fpNum)
+	_, err = expectedRT.AddPart(cmixMsg, fpNum)
 	if err != nil {
 		t.Errorf("Failed to add test part: %+v", err)
 	}
@@ -712,12 +749,25 @@ func Test_loadReceivedTransfer_LoadFpVectorError(t *testing.T) {
 	tid, rt, _ := newRandomReceivedTransfer(16, 20, kv, t)
 
 	// Create encrypted part
+
 	data := []byte("test")
 	partNum, fpNum := uint16(1), uint16(1)
-	encryptedPart, mac, padding := newEncryptedPartData(rt.key, data, fpNum, t)
+
+	cmixMsg := format.NewMessage(format.MinimumPrimeSize)
+
+	partData, _ := NewPartMessage(cmixMsg.ContentsSize())
+	partData.SetPartNum(partNum)
+	_ = partData.SetPart(data)
+
+	fp := ftCrypto.GenerateFingerprint(rt.key, fpNum)
+	encryptedPart, mac, err := ftCrypto.EncryptPart(rt.key, partData.Marshal(), fpNum, fp)
+
+	cmixMsg.SetKeyFP(fp)
+	cmixMsg.SetContents(encryptedPart)
+	cmixMsg.SetMac(mac)
 
 	// Add encrypted part
-	_, err := rt.AddPart(encryptedPart, padding, mac, partNum, fpNum)
+	_, err = rt.AddPart(cmixMsg, fpNum)
 	if err != nil {
 		t.Errorf("Failed to add test part: %+v", err)
 	}
@@ -746,10 +796,22 @@ func Test_loadReceivedTransfer_LoadPartStoreError(t *testing.T) {
 	// Create encrypted part
 	data := []byte("test")
 	partNum, fpNum := uint16(1), uint16(1)
-	encryptedPart, mac, padding := newEncryptedPartData(rt.key, data, fpNum, t)
+
+	cmixMsg := format.NewMessage(format.MinimumPrimeSize)
+
+	partData, _ := NewPartMessage(cmixMsg.ContentsSize())
+	partData.SetPartNum(partNum)
+	_ = partData.SetPart(data)
+
+	fp := ftCrypto.GenerateFingerprint(rt.key, fpNum)
+	encryptedPart, mac, err := ftCrypto.EncryptPart(rt.key, partData.Marshal(), fpNum, fp)
+
+	cmixMsg.SetKeyFP(fp)
+	cmixMsg.SetContents(encryptedPart)
+	cmixMsg.SetMac(mac)
 
 	// Add encrypted part
-	_, err := rt.AddPart(encryptedPart, padding, mac, partNum, fpNum)
+	_, err = rt.AddPart(cmixMsg, fpNum)
 	if err != nil {
 		t.Errorf("Failed to add test part: %+v", err)
 	}
@@ -778,10 +840,22 @@ func Test_loadReceivedTransfer_LoadReceivedVectorError(t *testing.T) {
 	// Create encrypted part
 	data := []byte("test")
 	partNum, fpNum := uint16(1), uint16(1)
-	encryptedPart, mac, padding := newEncryptedPartData(rt.key, data, fpNum, t)
+
+	cmixMsg := format.NewMessage(format.MinimumPrimeSize)
+
+	partData, _ := NewPartMessage(cmixMsg.ContentsSize())
+	partData.SetPartNum(partNum)
+	_ = partData.SetPart(data)
+
+	fp := ftCrypto.GenerateFingerprint(rt.key, fpNum)
+	encryptedPart, mac, err := ftCrypto.EncryptPart(rt.key, partData.Marshal(), fpNum, fp)
+
+	cmixMsg.SetKeyFP(fp)
+	cmixMsg.SetContents(encryptedPart)
+	cmixMsg.SetMac(mac)
 
 	// Add encrypted part
-	_, err := rt.AddPart(encryptedPart, padding, mac, partNum, fpNum)
+	_, err = rt.AddPart(cmixMsg, fpNum)
 	if err != nil {
 		t.Errorf("Failed to add test part: %+v", err)
 	}
@@ -871,11 +945,22 @@ func TestReceivedTransfer_delete(t *testing.T) {
 	// Create encrypted part
 	expectedData := []byte("test")
 	partNum, fpNum := uint16(1), uint16(1)
-	encryptedPart, mac, padding := newEncryptedPartData(
-		rt.key, expectedData, fpNum, t)
+
+	cmixMsg := format.NewMessage(format.MinimumPrimeSize)
+
+	partData, _ := NewPartMessage(cmixMsg.ContentsSize())
+	partData.SetPartNum(partNum)
+	_ = partData.SetPart(expectedData)
+
+	fp := ftCrypto.GenerateFingerprint(rt.key, fpNum)
+	encryptedPart, mac, err := ftCrypto.EncryptPart(rt.key, partData.Marshal(), fpNum, fp)
+
+	cmixMsg.SetKeyFP(fp)
+	cmixMsg.SetContents(encryptedPart)
+	cmixMsg.SetMac(mac)
 
 	// Add encrypted part
-	_, err := rt.AddPart(encryptedPart, padding, mac, partNum, fpNum)
+	_, err = rt.AddPart(cmixMsg, fpNum)
 	if err != nil {
 		t.Fatalf("Failed to add test part: %+v", err)
 	}
@@ -1030,8 +1115,20 @@ func newRandomReceivedTransfer(numParts, numFps uint16, kv *versioned.KV,
 	}
 
 	for partNum, part := range parts.parts {
-		encryptedPart, mac, padding := newEncryptedPartData(key, part, partNum, t)
-		_, err := rt.AddPart(encryptedPart, padding, mac, partNum, partNum)
+		cmixMsg := format.NewMessage(format.MinimumPrimeSize)
+
+		partData, _ := NewPartMessage(cmixMsg.ContentsSize())
+		partData.SetPartNum(partNum)
+		_ = partData.SetPart(part)
+
+		fp := ftCrypto.GenerateFingerprint(rt.key, partNum)
+		encryptedPart, mac, err := ftCrypto.EncryptPart(rt.key, partData.Marshal(), partNum, fp)
+
+		cmixMsg.SetKeyFP(fp)
+		cmixMsg.SetContents(encryptedPart)
+		cmixMsg.SetMac(mac)
+
+		_, err = rt.AddPart(cmixMsg, partNum)
 		if err != nil {
 			t.Errorf("Failed to add part #%d: %+v", partNum, err)
 		}
@@ -1059,18 +1156,4 @@ func newEmptyReceivedTransfer(numParts, numFps uint16, kv *versioned.KV,
 	}
 
 	return tid, rt, fileData
-}
-
-// newEncryptedPartData encrypts the part data and returns the encrypted part
-// its MAC, and its padding.
-func newEncryptedPartData(key ftCrypto.TransferKey, part []byte, fpNum uint16,
-	t *testing.T) ([]byte, []byte, []byte) {
-	// Create encrypted part
-	prng := NewPrng(42)
-	encPart, mac, padding, err := ftCrypto.EncryptPart(key, part, fpNum, prng)
-	if err != nil {
-		t.Fatalf("Failed to encrypt data: %+v", err)
-	}
-
-	return encPart, mac, padding
-}
+}
\ No newline at end of file
diff --git a/storage/fileTransfer/sentFileTransfers.go b/storage/fileTransfer/sentFileTransfers.go
index 485eb67d062eca8ccb75f9b53bda262d64996eb3..e672ce184fa4674a47b94c89bb47abdc2b420eb8 100644
--- a/storage/fileTransfer/sentFileTransfers.go
+++ b/storage/fileTransfer/sentFileTransfers.go
@@ -193,11 +193,6 @@ func (sft *SentFileTransfersStore) GetUnsentPartsAndSentRounds() (
 	sentRounds := map[id.Round][]ftCrypto.TransferID{}
 
 	for tid, st := range sft.transfers {
-		// Get list of round IDs that transfers have in-progress rounds on
-		for _, rid := range st.GetSentRounds() {
-			sentRounds[rid] = append(sentRounds[rid], tid)
-		}
-
 		// Get list of unsent part numbers for each transfer
 		stUnsentParts, err := st.GetUnsentPartNums()
 		if err != nil {
@@ -206,6 +201,11 @@ func (sft *SentFileTransfersStore) GetUnsentPartsAndSentRounds() (
 		if len(stUnsentParts) > 0 {
 			unsentParts[tid] = stUnsentParts
 		}
+
+		// Get list of round IDs that transfers have in-progress rounds on
+		for _, rid := range st.GetSentRounds() {
+			sentRounds[rid] = append(sentRounds[rid], tid)
+		}
 	}
 
 	return unsentParts, sentRounds, nil
diff --git a/storage/fileTransfer/sentPartTracker_test.go b/storage/fileTransfer/sentPartTracker_test.go
index 8d5a476ed542563b7524e9f3da9d668680f9ff22..af50953b889415c3c11bf4969199ee6d318408fb 100644
--- a/storage/fileTransfer/sentPartTracker_test.go
+++ b/storage/fileTransfer/sentPartTracker_test.go
@@ -56,21 +56,20 @@ func Test_sentPartTracker_GetPartStatus(t *testing.T) {
 
 		switch partStatuses[partNum] {
 		case interfaces.FpSent:
-			err := st.partStats.Set(partNum, uint8(interfaces.FpSent))
+			err := st.partStats.Set(partNum, inProgress)
 			if err != nil {
-				t.Errorf("Failed to set part %d to %s: %+v",
-					partNum, interfaces.FpSent, err)
+				t.Errorf(
+					"Failed to set part %d to in-progress: %+v", partNum, err)
 			}
 		case interfaces.FpArrived:
-			err := st.partStats.Set(partNum, uint8(interfaces.FpSent))
+			err := st.partStats.Set(partNum, inProgress)
 			if err != nil {
-				t.Errorf("Failed to set part %d to %s: %+v",
-					partNum, interfaces.FpSent, err)
+				t.Errorf(
+					"Failed to set part %d to in-progress: %+v", partNum, err)
 			}
-			err = st.partStats.Set(partNum, uint8(interfaces.FpArrived))
+			err = st.partStats.Set(partNum, finished)
 			if err != nil {
-				t.Errorf("Failed to set part %d to %s: %+v",
-					partNum, interfaces.FpArrived, err)
+				t.Errorf("Failed to set part %d to finished: %+v", partNum, err)
 			}
 		}
 	}
diff --git a/storage/fileTransfer/sentTransfer.go b/storage/fileTransfer/sentTransfer.go
index a91a59847fee21466f0bff6a80a49ad01b67c82d..cdffaaeca6807c4025b9bf90925130e6be9cbcb5 100644
--- a/storage/fileTransfer/sentTransfer.go
+++ b/storage/fileTransfer/sentTransfer.go
@@ -17,7 +17,6 @@ import (
 	"gitlab.com/elixxir/client/storage/versioned"
 	ftCrypto "gitlab.com/elixxir/crypto/fileTransfer"
 	"gitlab.com/elixxir/primitives/format"
-	"gitlab.com/xx_network/crypto/csprng"
 	"gitlab.com/xx_network/primitives/id"
 	"gitlab.com/xx_network/primitives/netTime"
 	"sync"
@@ -91,6 +90,14 @@ const (
 // been used.
 var MaxRetriesErr = errors.New(maxRetriesErr)
 
+// States for parts in the partStats MultiStateVector.
+const (
+	unsent = iota
+	inProgress
+	finished
+	numStates // The number of part states (for initialisation of the vector)
+)
+
 // sentTransferStateMap prevents illegal state changes for part statuses.
 var sentTransferStateMap = [][]bool{
 	{false, true, false},
@@ -188,8 +195,8 @@ func NewSentTransfer(recipient *id.ID, tid ftCrypto.TransferID,
 	}
 
 	// Create new MultiStateVector for storing part statuses
-	st.partStats, err = utility.NewMultiStateVector(
-		st.numParts, 3, sentTransferStateMap, sentPartStatsVectorKey, st.kv)
+	st.partStats, err = utility.NewMultiStateVector(st.numParts, numStates,
+		sentTransferStateMap, sentPartStatsVectorKey, st.kv)
 	if err != nil {
 		return nil, errors.Errorf(newSentPartStatusVectorErr, err)
 	}
@@ -236,8 +243,8 @@ func (st *SentTransfer) ReInit(numFps uint16,
 	}
 
 	// Overwrite new part status MultiStateVector
-	st.partStats, err = utility.NewMultiStateVector(
-		st.numParts, 3, sentTransferStateMap, sentPartStatsVectorKey, st.kv)
+	st.partStats, err = utility.NewMultiStateVector(st.numParts, numStates,
+		sentTransferStateMap, sentPartStatsVectorKey, st.kv)
 	if err != nil {
 		return errors.Errorf(reInitSentPartStatusVectorErr, err)
 	}
@@ -314,7 +321,7 @@ func (st *SentTransfer) IsPartInProgress(partNum uint16) (bool, error) {
 	if err != nil {
 		return false, errors.Errorf(getStatusErr, partNum, err)
 	}
-	return status == 1, nil
+	return status == inProgress, nil
 }
 
 // IsPartFinished returns true if the part has successfully arrived. Returns
@@ -325,7 +332,7 @@ func (st *SentTransfer) IsPartFinished(partNum uint16) (bool, error) {
 	if err != nil {
 		return false, errors.Errorf(getStatusErr, partNum, err)
 	}
-	return status == 2, nil
+	return status == finished, nil
 }
 
 // GetProgress returns the current progress of the transfer. Completed is true
@@ -345,8 +352,8 @@ func (st *SentTransfer) GetProgress() (completed bool, sent, arrived,
 // getProgress is the thread-unsafe helper function for GetProgress.
 func (st *SentTransfer) getProgress() (completed bool, sent, arrived,
 	total uint16, t interfaces.FilePartTracker) {
-	arrived, _ = st.partStats.GetCount(2)
-	sent, _ = st.partStats.GetCount(1)
+	arrived, _ = st.partStats.GetCount(finished)
+	sent, _ = st.partStats.GetCount(inProgress)
 	total = st.numParts
 
 	if sent == 0 && arrived == total {
@@ -419,31 +426,44 @@ func (st *SentTransfer) AddProgressCB(cb interfaces.SentProgressCallback,
 
 // GetEncryptedPart gets the specified part, encrypts it, and returns the
 // encrypted part along with its MAC, padding, and fingerprint.
-func (st *SentTransfer) GetEncryptedPart(partNum uint16, partSize int,
-	rng csprng.Source) (encPart, mac, padding []byte, fp format.Fingerprint,
-	err error) {
+func (st *SentTransfer) GetEncryptedPart(partNum uint16, contentsSize int) (encPart, mac []byte,
+	fp format.Fingerprint, err error) {
 	st.mux.Lock()
 	defer st.mux.Unlock()
 
+	// Create new empty file part message of size equal to the available payload
+	// size in the cMix message
+	partMsg, err := NewPartMessage(contentsSize)
+	if err != nil {
+		return nil, nil, format.Fingerprint{}, err
+	}
+
+	partMsg.SetPartNum(partNum)
+
 	// Lookup part
 	part, exists := st.sentParts.getPart(partNum)
 	if !exists {
-		return nil, nil, nil, format.Fingerprint{},
+		return nil, nil, format.Fingerprint{},
 			errors.Errorf(noPartNumErr, partNum)
 	}
 
+	if err = partMsg.SetPart(part); err != nil{
+		return nil, nil, format.Fingerprint{},
+			err
+	}
+
 	// If all fingerprints have been used but parts still remain, then change
 	// the status to stopping and return an error specifying that all the
 	// retries have been used
 	if st.fpVector.GetNumAvailable() < 1 {
 		st.status = Stopping
-		return nil, nil, nil, format.Fingerprint{}, MaxRetriesErr
+		return nil, nil, format.Fingerprint{}, MaxRetriesErr
 	}
 
 	// Get next unused fingerprint number and mark it as used
 	nextKey, err := st.fpVector.Next()
 	if err != nil {
-		return nil, nil, nil, format.Fingerprint{},
+		return nil, nil, format.Fingerprint{},
 			errors.Errorf(fingerprintErr, err)
 	}
 	fpNum := uint16(nextKey)
@@ -452,16 +472,13 @@ func (st *SentTransfer) GetEncryptedPart(partNum uint16, partSize int,
 	fp = ftCrypto.GenerateFingerprint(st.key, fpNum)
 
 	// Encrypt the file part and generate the file part MAC and padding (nonce)
-	maxLengthPart := make([]byte, partSize)
-	copy(maxLengthPart, part)
-	encPart, mac, padding, err = ftCrypto.EncryptPart(
-		st.key, maxLengthPart, fpNum, rng)
+	encPart, mac, err = ftCrypto.EncryptPart(st.key, partMsg.Marshal(), fpNum, fp)
 	if err != nil {
-		return nil, nil, nil, format.Fingerprint{},
+		return nil, nil, format.Fingerprint{},
 			errors.Errorf(encryptPartErr, partNum, err)
 	}
 
-	return encPart, mac, padding, fp, err
+	return encPart, mac, fp, err
 }
 
 // SetInProgress adds the specified file part numbers to the in-progress
@@ -476,7 +493,7 @@ func (st *SentTransfer) SetInProgress(rid id.Round, partNums ...uint16) (bool,
 	_, exists := st.inProgressTransfers.getPartNums(rid)
 
 	// Set parts as in-progress in part status vector
-	err := st.partStats.SetMany(partNums, 1)
+	err := st.partStats.SetMany(partNums, inProgress)
 	if err != nil {
 		return false, err
 	}
@@ -503,8 +520,16 @@ func (st *SentTransfer) UnsetInProgress(rid id.Round) ([]uint16, error) {
 	// Get the list of part numbers to be removed from list
 	partNums, _ := st.inProgressTransfers.getPartNums(rid)
 
+	// The part status is set in partStats before the parts and round ID so that
+	// in the event of recovery after a crash, the parts will be resent on a new
+	// round and the parts in the inProgressTransfers will be left until deleted
+	// with the rest of the storage on transfer completion. The side effect is
+	// that on recovery, the status of the round will be looked up again and the
+	// progress callback will be called for an event that has already been
+	// called on the callback.
+
 	// Set parts as unsent in part status vector
-	err := st.partStats.SetMany(partNums, 0)
+	err := st.partStats.SetMany(partNums, unsent)
 	if err != nil {
 		return nil, err
 	}
@@ -539,7 +564,7 @@ func (st *SentTransfer) FinishTransfer(rid id.Round) (bool, error) {
 	}
 
 	// Set parts as finished in part status vector
-	err = st.partStats.SetMany(partNums, 2)
+	err = st.partStats.SetMany(partNums, finished)
 	if err != nil {
 		return false, err
 	}
@@ -561,7 +586,7 @@ func (st *SentTransfer) GetUnsentPartNums() ([]uint16, error) {
 	defer st.mux.RUnlock()
 
 	// Get list of parts with a status of unsent
-	unsentPartNums, err := st.partStats.GetKeys(0)
+	unsentPartNums, err := st.partStats.GetKeys(unsent)
 	if err != nil {
 		return nil, errors.Errorf(getUnsentPartsErr, err)
 	}
diff --git a/storage/fileTransfer/sentTransfer_test.go b/storage/fileTransfer/sentTransfer_test.go
index 79415befe0632d48092d75f078969f28f86f5693..033992bf84b42aa4bf54431ac50e3c3c0b132998 100644
--- a/storage/fileTransfer/sentTransfer_test.go
+++ b/storage/fileTransfer/sentTransfer_test.go
@@ -803,7 +803,6 @@ func TestSentTransfer_AddProgressCB(t *testing.T) {
 func TestSentTransfer_GetEncryptedPart(t *testing.T) {
 	kv := versioned.NewKV(make(ekv.Memstore))
 	_, st := newRandomSentTransfer(16, 24, kv, t)
-	prng := NewPrng(42)
 
 	// Create and fill fingerprint map used to check fingerprint validity
 	// The first item in the uint16 slice is the fingerprint number and the
@@ -816,7 +815,7 @@ func TestSentTransfer_GetEncryptedPart(t *testing.T) {
 	for i := uint16(0); i < st.numFps; i++ {
 		partNum := i % st.numParts
 
-		encPart, mac, padding, fp, err := st.GetEncryptedPart(partNum, 16, prng)
+		encPart, mac, fp, err := st.GetEncryptedPart(partNum, 18)
 		if err != nil {
 			t.Fatalf("GetEncryptedPart returned an error for part number "+
 				"%d (%d): %+v", partNum, i, err)
@@ -836,17 +835,24 @@ func TestSentTransfer_GetEncryptedPart(t *testing.T) {
 		}
 
 		// Attempt to decrypt the part
-		part, err := ftCrypto.DecryptPart(st.key, encPart, padding, mac, fpNum[0])
+		partMarshaled, err := ftCrypto.DecryptPart(st.key, encPart, mac, fpNum[0], fp)
 		if err != nil {
 			t.Errorf("Failed to decrypt file part number %d (%d): %+v",
 				partNum, i, err)
 		}
 
+		partMsg, _ := UnmarshalPartMessage(partMarshaled)
+
 		// Make sure the decrypted part matches the original
 		expectedPart, _ := st.sentParts.getPart(i % st.numParts)
-		if !bytes.Equal(expectedPart, part) {
+		if !bytes.Equal(expectedPart, partMsg.GetPart()) {
 			t.Errorf("Decyrpted part number %d does not match expected (%d)."+
-				"\nexpected: %+v\nreceived: %+v", partNum, i, expectedPart, part)
+				"\nexpected: %+v\nreceived: %+v", partNum, i, expectedPart, partMsg.GetPart())
+		}
+
+		if partMsg.GetPartNum()!=i % st.numParts{
+			t.Errorf("Number of part did not match, expected: %d, " +
+				"received: %d", i % st.numParts, partMsg.GetPartNum())
 		}
 	}
 }
@@ -856,12 +862,11 @@ func TestSentTransfer_GetEncryptedPart(t *testing.T) {
 func TestSentTransfer_GetEncryptedPart_NoPartError(t *testing.T) {
 	kv := versioned.NewKV(make(ekv.Memstore))
 	_, st := newRandomSentTransfer(16, 24, kv, t)
-	prng := NewPrng(42)
 
 	partNum := st.numParts + 1
 	expectedErr := fmt.Sprintf(noPartNumErr, partNum)
 
-	_, _, _, _, err := st.GetEncryptedPart(partNum, 16, prng)
+	_, _, _, err := st.GetEncryptedPart(partNum, 16)
 	if err == nil || err.Error() != expectedErr {
 		t.Errorf("GetEncryptedPart did not return the expected error for a "+
 			"nonexistent part number %d.\nexpected: %s\nreceived: %+v",
@@ -874,12 +879,11 @@ func TestSentTransfer_GetEncryptedPart_NoPartError(t *testing.T) {
 func TestSentTransfer_GetEncryptedPart_NoFingerprintsError(t *testing.T) {
 	kv := versioned.NewKV(make(ekv.Memstore))
 	_, st := newRandomSentTransfer(16, 24, kv, t)
-	prng := NewPrng(42)
 
 	// Use up all the fingerprints
 	for i := uint16(0); i < st.numFps; i++ {
 		partNum := i % st.numParts
-		_, _, _, _, err := st.GetEncryptedPart(partNum, 16, prng)
+		_, _, _, err := st.GetEncryptedPart(partNum, 18)
 		if err != nil {
 			t.Errorf("Error when encyrpting part number %d (%d): %+v",
 				partNum, i, err)
@@ -887,7 +891,7 @@ func TestSentTransfer_GetEncryptedPart_NoFingerprintsError(t *testing.T) {
 	}
 
 	// Try to encrypt without any fingerprints
-	_, _, _, _, err := st.GetEncryptedPart(5, 16, prng)
+	_, _, _, err := st.GetEncryptedPart(5, 18)
 	if err != MaxRetriesErr {
 		t.Errorf("GetEncryptedPart did not return MaxRetriesErr when all "+
 			"fingerprints have been used.\nexpected: %s\nreceived: %+v",
@@ -895,32 +899,6 @@ func TestSentTransfer_GetEncryptedPart_NoFingerprintsError(t *testing.T) {
 	}
 }
 
-// Error path: tests that SentTransfer.GetEncryptedPart returns the expected
-// error when encrypting the part fails due to a PRNG error.
-func TestSentTransfer_GetEncryptedPart_EncryptPartError(t *testing.T) {
-	kv := versioned.NewKV(make(ekv.Memstore))
-	_, st := newRandomSentTransfer(16, 24, kv, t)
-	prng := NewPrngErr()
-
-	// Create and fill fingerprint map used to check fingerprint validity
-	// The first item in the uint16 slice is the fingerprint number and the
-	// second item is the number of times it has been used
-	fpMap := make(map[format.Fingerprint][]uint16, st.numFps)
-	for num, fp := range ftCrypto.GenerateFingerprints(st.key, st.numFps) {
-		fpMap[fp] = []uint16{uint16(num), 0}
-	}
-
-	partNum := uint16(0)
-	expectedErr := fmt.Sprintf(encryptPartErr, partNum, "")
-
-	_, _, _, _, err := st.GetEncryptedPart(partNum, 16, prng)
-	if err == nil || !strings.Contains(err.Error(), expectedErr) {
-		t.Errorf("GetEncryptedPart did not return the expected error when "+
-			"the PRNG should have errored.\nexpected: %s\nreceived: %+v",
-			expectedErr, err)
-	}
-}
-
 // Tests that SentTransfer.SetInProgress correctly adds the part numbers for the
 // given round ID to the in-progress map and sets the correct parts as
 // in-progress in the state vector.
@@ -962,15 +940,15 @@ func TestSentTransfer_SetInProgress(t *testing.T) {
 
 	// Check that the part numbers were set on the in-progress status vector
 	for i, partNum := range expectedPartNums {
-		if status, _ := st.partStats.Get(partNum); status != 1 {
-			t.Errorf("Part number %d not marked as used in status vector (%d).",
-				partNum, i)
+		if status, _ := st.partStats.Get(partNum); status != inProgress {
+			t.Errorf("Part number %d not marked as in-progress in status "+
+				"vector (%d).", partNum, i)
 		}
 	}
 
 	// Check that the correct number of parts were marked as in-progress in the
 	// status vector
-	count, _ := st.partStats.GetCount(1)
+	count, _ := st.partStats.GetCount(inProgress)
 	if int(count) != len(expectedPartNums) {
 		t.Errorf("Incorrect number of parts marked as in-progress."+
 			"\nexpected: %d\nreceived: %d", len(expectedPartNums), count)
@@ -989,7 +967,7 @@ func TestSentTransfer_SetInProgress(t *testing.T) {
 	}
 
 	// Check that the number of parts were marked as in-progress is unchanged
-	count, _ = st.partStats.GetCount(1)
+	count, _ = st.partStats.GetCount(inProgress)
 	if int(count) != len(expectedPartNums2)+len(expectedPartNums) {
 		t.Errorf("Incorrect number of parts marked as in-progress."+
 			"\nexpected: %d\nreceived: %d",
@@ -1075,10 +1053,10 @@ func TestSentTransfer_UnsetInProgress(t *testing.T) {
 	}
 
 	// Check that there are no set parts in the in-progress status vector
-	status, _ := st.partStats.Get(1)
-	if status != 0 {
+	status, _ := st.partStats.Get(inProgress)
+	if status != unsent {
 		t.Errorf("Failed to unset all parts in the in-progress vector."+
-			"\nexpected: %d\nreceived: %d", 0, status)
+			"\nexpected: %d\nreceived: %d", unsent, status)
 	}
 }
 
@@ -1134,7 +1112,7 @@ func TestSentTransfer_FinishTransfer(t *testing.T) {
 	}
 
 	// Check that there are no set parts in the in-progress status vector
-	count, _ := st.partStats.GetCount(1)
+	count, _ := st.partStats.GetCount(inProgress)
 	if count != 0 {
 		t.Errorf("Failed to unset all parts in the in-progress vector."+
 			"\nexpected: %d\nreceived: %d", 0, count)
@@ -1142,17 +1120,16 @@ func TestSentTransfer_FinishTransfer(t *testing.T) {
 
 	// Check that the part numbers were set on the finished status vector
 	for i, partNum := range expectedPartNums {
-
-		status, _ := st.partStats.Get(1)
-		if status != 2 {
-			t.Errorf("Part number %d not marked as used in status vector (%d).",
-				partNum, i)
+		status, _ := st.partStats.Get(inProgress)
+		if status != finished {
+			t.Errorf("Part number %d not marked as finished in status vector "+
+				"(%d).", partNum, i)
 		}
 	}
 
 	// Check that the correct number of parts were marked as finished in the
 	// status vector
-	count, _ = st.partStats.GetCount(2)
+	count, _ = st.partStats.GetCount(finished)
 	if int(count) != len(expectedPartNums) {
 		t.Errorf("Incorrect number of parts marked as finished."+
 			"\nexpected: %d\nreceived: %d", len(expectedPartNums), count)
diff --git a/storage/partition/multiPartMessage_test.go b/storage/partition/multiPartMessage_test.go
index 752d53272bb121f566e2917d746087020f68d14b..0fa14f3294eef6efa0ff1362e0eca7d1f97864fa 100644
--- a/storage/partition/multiPartMessage_test.go
+++ b/storage/partition/multiPartMessage_test.go
@@ -203,7 +203,7 @@ func TestMultiPartMessage_IsComplete(t *testing.T) {
 		t.Error("IsComplete() returned true when NumParts == 0.")
 	}
 
-	mpm.AddFirst(message.Text, partNums[0], 75, netTime.Now(), netTime.Now(), parts[0])
+	mpm.AddFirst(message.XxMessage, partNums[0], 75, netTime.Now(), netTime.Now(), parts[0])
 	for i := range partNums {
 		if i > 0 {
 			mpm.Add(partNums[i], parts[i])
diff --git a/storage/partition/store_test.go b/storage/partition/store_test.go
index 4e3d221a159be0f1fd73664c1d6d6a9e7b5097aa..0b05232ca20380f5c066dfa31405dea0833a778f 100644
--- a/storage/partition/store_test.go
+++ b/storage/partition/store_test.go
@@ -41,7 +41,7 @@ func TestStore_AddFirst(t *testing.T) {
 	s := New(versioned.NewKV(ekv.Memstore{}))
 
 	msg, complete := s.AddFirst(id.NewIdFromString("User", id.User, t),
-		message.Text, 5, 0, 1, netTime.Now(), netTime.Now(), part,
+		message.XxMessage, 5, 0, 1, netTime.Now(), netTime.Now(), part,
 		[]byte{0})
 
 	if !complete {
@@ -61,7 +61,7 @@ func TestStore_Add(t *testing.T) {
 	s := New(versioned.NewKV(ekv.Memstore{}))
 
 	msg, complete := s.AddFirst(id.NewIdFromString("User", id.User, t),
-		message.Text, 5, 0, 2, netTime.Now(), netTime.Now(), part1,
+		message.XxMessage, 5, 0, 2, netTime.Now(), netTime.Now(), part1,
 		[]byte{0})
 
 	if complete {
@@ -92,7 +92,7 @@ func TestStore_ClearMessages(t *testing.T) {
 	messageId1 := uint64(5)
 	oldTimestamp := netTime.Now().Add(-2 * clearPartitionThreshold)
 	s.AddFirst(partner1,
-		message.Text, messageId1, 0, 2, netTime.Now(),
+		message.XxMessage, messageId1, 0, 2, netTime.Now(),
 		oldTimestamp, part1,
 		[]byte{0})
 	s.Add(partner1, messageId1, 1, part2, []byte{0})
@@ -100,7 +100,7 @@ func TestStore_ClearMessages(t *testing.T) {
 	partner2 := id.NewIdFromString("User1", id.User, t)
 	messageId2 := uint64(6)
 	newTimestamp := netTime.Now()
-	s.AddFirst(partner2, message.Text, messageId2, 0, 2, netTime.Now(),
+	s.AddFirst(partner2, message.XxMessage, messageId2, 0, 2, netTime.Now(),
 		newTimestamp, part1,
 		[]byte{0})
 
diff --git a/storage/session.go b/storage/session.go
index e54ab01fbc4a2002bac5ffa445d906ffd8710186..d11e11fc6697edd4e52d3550c6de2d6677a37bd1 100644
--- a/storage/session.go
+++ b/storage/session.go
@@ -486,6 +486,18 @@ func InitTestingSession(i interface{}) *Session {
 
 	s.hostList = hostList.NewStore(s.kv)
 
+	privKeys := make([]*cyclic.Int, 10)
+	pubKeys := make([]*cyclic.Int, 10)
+	for i := range privKeys {
+		privKeys[i] = cmixGrp.NewInt(5)
+		pubKeys[i] = cmixGrp.ExpG(privKeys[i], cmixGrp.NewInt(1))
+	}
+
+	s.auth, err = auth.NewStore(s.kv, cmixGrp, privKeys)
+	if err != nil {
+		jww.FATAL.Panicf("Failed to create auth store: %v", err)
+	}
+
 	s.edgeCheck, err = edge.NewStore(s.kv, uid)
 	if err != nil {
 		jww.FATAL.Panicf("Failed to create new edge Store: %+v", err)
diff --git a/storage/utility/meteredCmixMessageBuffer.go b/storage/utility/meteredCmixMessageBuffer.go
index ed1920ff42f2cf0f122549584ef11d480d43ebbb..9060dbb7ea2424f87879032371ee25944ff64851 100644
--- a/storage/utility/meteredCmixMessageBuffer.go
+++ b/storage/utility/meteredCmixMessageBuffer.go
@@ -114,6 +114,10 @@ func LoadMeteredCmixMessageBuffer(kv *versioned.KV, key string) (*MeteredCmixMes
 }
 
 func (mcmb *MeteredCmixMessageBuffer) Add(m format.Message) {
+	if m.GetPrimeByteLen()==0{
+		jww.FATAL.Panicf("Cannot handle a metered " +
+			"cmix message with a length of 0")
+	}
 	msg := meteredCmixMessage{
 		M:         m.Marshal(),
 		Count:     0,
diff --git a/storage/utility/multiStateVector.go b/storage/utility/multiStateVector.go
index 0a882ada564d7044ae1a5b42e914b1b311692146..77cf24ad1435c6c83ce431a13080a4d9d27cae68 100644
--- a/storage/utility/multiStateVector.go
+++ b/storage/utility/multiStateVector.go
@@ -43,7 +43,7 @@ const (
 	saveSetStateErr = "failed to save MultiStateVector after setting key %d state to %d: %+v"
 
 	// MultiStateVector.SetMany
-	setManyStateErr     = "failed to set state of key %d (%d/%d): %+v"
+	setManyStateErr     = "failed to set state of key %d (%d of %d): %+v"
 	saveManySetStateErr = "failed to save MultiStateVector after setting keys %d state to %d: %+v"
 
 	// MultiStateVector.set
diff --git a/ud/manager.go b/ud/manager.go
index 1b9de113a558afc451e2acabfb5dabde096a4f88..b271c0d90a635e3927728a9337b2902bb8a58be4 100644
--- a/ud/manager.go
+++ b/ud/manager.go
@@ -42,9 +42,20 @@ type Manager struct {
 	single SingleInterface
 	myID   *id.ID
 
+	// alternate User discovery service to circumvent production
+	alternativeUd *alternateUd
+
 	registered *uint32
 }
 
+// alternateUd is an alternative user discovery service.
+// This is used for testing, so client can avoid using
+// the production server.
+type alternateUd struct {
+	host     *connect.Host
+	dhPubKey []byte
+}
+
 // NewManager builds a new user discovery manager. It requires that an updated
 // NDF is available and will error if one is not.
 func NewManager(client *api.Client, single *single.Manager) (*Manager, error) {
@@ -94,11 +105,60 @@ func NewManager(client *api.Client, single *single.Manager) (*Manager, error) {
 	return m, nil
 }
 
+// SetAlternativeUserDiscovery sets the alternativeUd object within manager.
+// Once set, any user discovery operation will go through the alternative
+// user discovery service.
+// To undo this operation, use UnsetAlternativeUserDiscovery.
+func (m *Manager) SetAlternativeUserDiscovery(altCert, altAddress, contactFile []byte) error {
+	params := connect.GetDefaultHostParams()
+	params.AuthEnabled = false
+
+	udIdBytes, dhPubKey, err := contact.ReadContactFromFile(contactFile)
+	if err != nil {
+		return err
+	}
+
+	udID, err := id.Unmarshal(udIdBytes)
+	if err != nil {
+		return err
+	}
+
+	// Add a new host and return it if it does not already exist
+	host, err := m.comms.AddHost(udID, string(altAddress),
+		altCert, params)
+	if err != nil {
+		return errors.WithMessage(err, "User Discovery host object could "+
+			"not be constructed.")
+	}
+
+	m.alternativeUd = &alternateUd{
+		host:     host,
+		dhPubKey: dhPubKey,
+	}
+
+	return nil
+}
+
+// UnsetAlternativeUserDiscovery clears out the information from
+// the Manager object.
+func (m *Manager) UnsetAlternativeUserDiscovery() error {
+	if m.alternativeUd == nil {
+		return errors.New("Alternative User Discovery is already unset.")
+	}
+
+	m.alternativeUd = nil
+	return nil
+}
+
 // getHost returns the current UD host for the UD ID found in the NDF. If the
 // host does not exist, then it is added and returned
 func (m *Manager) getHost() (*connect.Host, error) {
-	netDef := m.net.GetInstance().GetPartialNdf().Get()
+	// Return alternative User discovery service if it has been set
+	if m.alternativeUd != nil {
+		return m.alternativeUd.host, nil
+	}
 
+	netDef := m.net.GetInstance().GetPartialNdf().Get()
 	// Unmarshal UD ID from the NDF
 	udID, err := id.Unmarshal(netDef.UDB.ID)
 	if err != nil {
@@ -113,6 +173,7 @@ func (m *Manager) getHost() (*connect.Host, error) {
 
 	params := connect.GetDefaultHostParams()
 	params.AuthEnabled = false
+	params.SendTimeout = 20 * time.Second
 
 	// Add a new host and return it if it does not already exist
 	host, err = m.comms.AddHost(udID, netDef.UDB.Address,
@@ -127,6 +188,23 @@ func (m *Manager) getHost() (*connect.Host, error) {
 
 // getContact returns the contact for UD as retrieved from the NDF.
 func (m *Manager) getContact() (contact.Contact, error) {
+	// Return alternative User discovery contact if set
+	if m.alternativeUd != nil {
+		// Unmarshal UD DH public key
+		alternativeDhPubKey := m.storage.E2e().GetGroup().NewInt(1)
+		if err := alternativeDhPubKey.UnmarshalJSON(m.alternativeUd.dhPubKey); err != nil {
+			return contact.Contact{},
+				errors.WithMessage(err, "Failed to unmarshal UD DH public key.")
+		}
+
+		return contact.Contact{
+			ID:             m.alternativeUd.host.GetId(),
+			DhPubKey:       alternativeDhPubKey,
+			OwnershipProof: nil,
+			Facts:          nil,
+		}, nil
+	}
+
 	netDef := m.net.GetInstance().GetPartialNdf().Get()
 
 	// Unmarshal UD ID from the NDF
diff --git a/ud/manager_test.go b/ud/manager_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..1dcb6c273df1c6ec7ed92980b15fc8990cfab01a
--- /dev/null
+++ b/ud/manager_test.go
@@ -0,0 +1,83 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright © 2020 xx network SEZC                                          //
+//                                                                           //
+// Use of this source code is governed by a license that can be found in the //
+// LICENSE file                                                              //
+///////////////////////////////////////////////////////////////////////////////
+
+package ud
+
+import (
+	"gitlab.com/elixxir/comms/client"
+	"gitlab.com/xx_network/crypto/csprng"
+	"gitlab.com/xx_network/crypto/signature/rsa"
+	"testing"
+)
+
+var testCert = `-----BEGIN CERTIFICATE-----
+MIIF4DCCA8igAwIBAgIUegUvihtQooWNIzsNqj6lucXn6g8wDQYJKoZIhvcNAQEL
+BQAwgYwxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTESMBAGA1UEBwwJQ2xhcmVt
+b250MRAwDgYDVQQKDAdFbGl4eGlyMRQwEgYDVQQLDAtEZXZlbG9wbWVudDETMBEG
+A1UEAwwKZWxpeHhpci5pbzEfMB0GCSqGSIb3DQEJARYQYWRtaW5AZWxpeHhpci5p
+bzAeFw0yMTExMzAxODMwMTdaFw0zMTExMjgxODMwMTdaMIGMMQswCQYDVQQGEwJV
+UzELMAkGA1UECAwCQ0ExEjAQBgNVBAcMCUNsYXJlbW9udDEQMA4GA1UECgwHRWxp
+eHhpcjEUMBIGA1UECwwLRGV2ZWxvcG1lbnQxEzARBgNVBAMMCmVsaXh4aXIuaW8x
+HzAdBgkqhkiG9w0BCQEWEGFkbWluQGVsaXh4aXIuaW8wggIiMA0GCSqGSIb3DQEB
+AQUAA4ICDwAwggIKAoICAQCckGabzUitkySleveyD9Yrxrpj50FiGkOvwkmgN1jF
+9r5StN3otiU5tebderkjD82mVqB781czRA9vPqAggbw1ZdAyQPTvDPTj7rmzkByq
+QIkdZBMshV/zX1z8oXoNB9bzZlUFVF4HTY3dEytAJONJRkGGAw4FTa/wCkWsITiT
+mKvkP3ciKgz7s8uMyZzZpj9ElBphK9Nbwt83v/IOgTqDmn5qDBnHtoLw4roKJkC8
+00GF4ZUhlVSQC3oFWOCu6tvSUVCBCTUzVKYJLmCnoilmiE/8nCOU0VOivtsx88f5
+9RSPfePUk8u5CRmgThwOpxb0CAO0gd+sY1YJrn+FaW+dSR8OkM3bFuTq7fz9CEkS
+XFfUwbJL+HzT0ZuSA3FupTIExyDmM/5dF8lC0RB3j4FNQF+H+j5Kso86e83xnXPI
+e+IKKIYa/LVdW24kYRuBDpoONN5KS/F+F/5PzOzH9Swdt07J9b7z1dzWcLnKGtkN
+WVsZ7Ue6cuI2zOEWqF1OEr9FladgORcdVBoF/WlsA63C2c1J0tjXqqcl/27GmqGW
+gvhaA8Jkm20qLCEhxQ2JzrBdk/X/lCZdP/7A5TxnLqSBq8xxMuLJlZZbUG8U/BT9
+sHF5mXZyiucMjTEU7qHMR2UGNFot8TQ7ZXntIApa2NlB/qX2qI5D13PoXI9Hnyxa
+8wIDAQABozgwNjAVBgNVHREEDjAMggplbGl4eGlyLmlvMB0GA1UdDgQWBBQimFud
+gCzDVFD3Xz68zOAebDN6YDANBgkqhkiG9w0BAQsFAAOCAgEAccsH9JIyFZdytGxC
+/6qjSHPgV23ZGmW7alg+GyEATBIAN187Du4Lj6cLbox5nqLdZgYzizVop32JQAHv
+N1QPKjViOOkLaJprSUuRULa5kJ5fe+XfMoyhISI4mtJXXbMwl/PbOaDSdeDjl0ZO
+auQggWslyv8ZOkfcbC6goEtAxljNZ01zY1ofSKUj+fBw9Lmomql6GAt7NuubANs4
+9mSjXwD27EZf3Aqaaju7gX1APW2O03/q4hDqhrGW14sN0gFt751ddPuPr5COGzCS
+c3Xg2HqMpXx//FU4qHrZYzwv8SuGSshlCxGJpWku9LVwci1Kxi4LyZgTm6/xY4kB
+5fsZf6C2yAZnkIJ8bEYr0Up4KzG1lNskU69uMv+d7W2+4Ie3Evf3HdYad/WeUskG
+tc6LKY6B2NX3RMVkQt0ftsDaWsktnR8VBXVZSBVYVEQu318rKvYRdOwZJn339obI
+jyMZC/3D721e5Anj/EqHpc3I9Yn3jRKw1xc8kpNLg/JIAibub8JYyDvT1gO4xjBO
++6EWOBFgDAsf7bSP2xQn1pQFWcA/sY1MnRsWeENmKNrkLXffP+8l1tEcijN+KCSF
+ek1mr+qBwSaNV9TA+RXVhvqd3DEKPPJ1WhfxP1K81RdUESvHOV/4kdwnSahDyao0
+EnretBzQkeKeBwoB2u6NTiOmUjk=
+-----END CERTIFICATE-----
+`
+
+var testContact = `<xxc(2)LF2ccT+sdqh0AIKlFFeDOJdnxzbQQYhGStgxhOXmijIDkAZiB9kZo+Dl3bRSbBi5pXZ82rOu2IQXz9+5sspChvoccZqgC/dXGhlesmiNy/EbKxWtptTF4tcNyQxtnmCXg1p/HwKey4G2XDekTw86lq6Lpmj72jozvRWlQisqvWz/5deiPaeFGKDKC0OrrDFnIib7WnKqdYt4XyTKdmObnmbvdCbliZq0zBl7J40qKy5FypYXGlZjStIm0R1qtD4XHMZMsrMJEGxdM55zJdSzknXbR8MNahUrGMyUOTivXLHzojYLht0gFQifKMVWhrDjUoVQV43KOLPmdBwY/2Kc5KvVloDeuDXYY0i7tD63gNIp9JA3gJQUJymDdwqbS13riT1DMHHkdTzKEyGdHS+v2l7AVSlJBiTKuyM00FBNuXhhIcFR7ONFCf8cRPOPPBx3Q6iHNsvsca3KPNhwOJBgaQvHSkjIMsudiR954QbwG9rbi2vxVobIgWYMl5j6vlBS/9rfbE/uLdTEQZfNsLKDCIVCCI4I1bYZxZrDLPrfXTrN6W0sCLE7a/kRBQAAAgA7+LwJqiv9O1ogLnS4TYkSEg==xxc>`
+
+func TestManager_SetAlternativeUserDiscovery(t *testing.T) {
+	isReg := uint32(1)
+
+	// Create a new Private Key to use for signing the Fact
+	rng := csprng.NewSystemRNG()
+	cpk, err := rsa.GenerateKey(rng, 2048)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	comms, err := client.NewClientComms(nil, nil, nil, nil)
+	if err != nil {
+		t.Errorf("Failed to start client comms: %+v", err)
+	}
+
+	// Create our Manager object
+	m := Manager{
+		comms:      comms,
+		net:        newTestNetworkManager(t),
+		privKey:    cpk,
+		registered: &isReg,
+	}
+
+	altAddr := "0.0.0.0:11420"
+	err = m.SetAlternativeUserDiscovery([]byte(testCert), []byte(altAddr), []byte(testContact))
+	if err != nil {
+		t.Fatalf("Unexpected error in SetAlternativeUserDiscovery: %v", err)
+	}
+}