diff --git a/bindings/authenticatedConnection.go b/bindings/authenticatedConnection.go
index 33aac039b7bea7ff91e5f021103e22442bc615ba..3cd2fcd009b7d47160e9002b60b4844ff1d43ade 100644
--- a/bindings/authenticatedConnection.go
+++ b/bindings/authenticatedConnection.go
@@ -34,7 +34,6 @@ func (_ *AuthenticatedConnection) IsAuthenticated() bool {
 // ConnectWithAuthentication is called by the client (i.e., the one establishing
 // connection with the server). Once a connect.Connection has been established
 // with the server, it then authenticates their identity to the server.
-// accepts a marshalled ReceptionIdentity and contact.Contact object
 func (c *Cmix) ConnectWithAuthentication(e2eId int, recipientContact,
 	e2eParamsJSON []byte) (*AuthenticatedConnection, error) {
 	if len(e2eParamsJSON) == 0 {
diff --git a/bindings/backup.go b/bindings/backup.go
index 2c5c24560184fe5d1f121e05b8df5de98ce1fc23..b7756fccb06b3df2b25f6ab5da3aa06f7d1e6313 100644
--- a/bindings/backup.go
+++ b/bindings/backup.go
@@ -55,7 +55,7 @@ type UpdateBackupFunc interface {
 // backup. Users of this function should delete the storage directory on error.
 // Users of this function should call LoadCmix as normal once this call succeeds.
 //
-// Params
+// Parameters:
 //  - ndfJSON - JSON of the NDF.
 //  - storageDir - directory for the storage files.
 //  - sessionPassword - password to decrypt the data in the storageDir.
@@ -92,10 +92,11 @@ func NewCmixFromBackup(ndfJSON, storageDir, backupPassphrase string,
 
 // InitializeBackup creates a bindings-layer Backup object.
 //
-// Params
+// Parameters:
 //  - e2eID - ID of the E2e object in the e2e tracker.
 //  - udID - ID of the UserDiscovery object in the ud tracker.
-//  - backupPassPhrase - backup passphrase provided by the user. Used to decrypt backup.
+//  - backupPassPhrase - backup passphrase provided by the user. Used to decrypt
+//    backup.
 //  - cb - the callback to be called when a backup is triggered.
 func InitializeBackup(e2eID, udID int, backupPassPhrase string,
 	cb UpdateBackupFunc) (*Backup, error) {
@@ -129,7 +130,7 @@ func InitializeBackup(e2eID, udID int, backupPassPhrase string,
 // To start the backup for the first time or to use a new password, use
 // InitializeBackup.
 //
-// Params
+// Parameters:
 //  - e2eID - ID of the E2e object in the e2e tracker.
 //  - udID - ID of the UserDiscovery object in the ud tracker.
 //  - cb - the callback to be called when a backup is triggered.
diff --git a/bindings/broadcast.go b/bindings/broadcast.go
index 62cce68bc271509b19f92aa4c44dac6f2c9ff7d9..6cea173b9000ede941c7c640727912c21f6e8cb0 100644
--- a/bindings/broadcast.go
+++ b/bindings/broadcast.go
@@ -19,18 +19,21 @@ import (
 	"gitlab.com/xx_network/primitives/id/ephemeral"
 )
 
-// Channel is a bindings-level struct encapsulating the broadcast.Channel client object.
+// Channel is a bindings-level struct encapsulating the broadcast.Channel client
+// object.
 type Channel struct {
 	ch broadcast.Channel
 }
 
-// ChannelDef is the bindings representation of an elixxir/crypto broadcast.Channel object.
+// ChannelDef is the bindings representation of an elixxir/crypto
+// broadcast.Channel object.
 //
 // Example JSON:
-//  {"Name": "My broadcast channel",
-//   "Description":"A broadcast channel for me to test things",
-//   "Salt":"gpUqW7N22sffMXsvPLE7BA==",
-//   "PubKey":"LS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tCk1DZ0NJUUN2YkZVckJKRFpqT3Y0Y0MvUHZZdXNvQkFtUTFkb3Znb044aHRuUjA2T3F3SURBUUFCCi0tLS0tRU5EIFJTQSBQVUJMSUMgS0VZLS0tLS0="
+//  {
+//    "Name": "My broadcast channel",
+//    "Description": "A broadcast channel for me to test things",
+//    "Salt": "gpUqW7N22sffMXsvPLE7BA==",
+//    "PubKey": "LS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tCk1DZ0NJUUN2YkZVckJKRFpqT3Y0Y0MvUHZZdXNvQkFtUTFkb3Znb044aHRuUjA2T3F3SURBUUFCCi0tLS0tRU5EIFJTQSBQVUJMSUMgS0VZLS0tLS0="
 //  }
 type ChannelDef struct {
 	Name        string
@@ -51,7 +54,8 @@ type BroadcastMessage struct {
 	Payload []byte
 }
 
-// BroadcastReport is the bindings representation of the info on how a broadcast message was sent
+// BroadcastReport is the bindings representation of the info on how a broadcast
+// message was sent
 //
 // Example JSON:
 //  {"RoundID":42,
@@ -72,7 +76,8 @@ type BroadcastListener interface {
 	Callback([]byte, error)
 }
 
-// NewBroadcastChannel creates a bindings-layer broadcast channel & starts listening for new messages
+// NewBroadcastChannel creates a bindings-layer broadcast channel and starts
+// listening for new messages.
 //
 // Parameters:
 //  - cmixId - internal ID of cmix
@@ -112,8 +117,8 @@ func NewBroadcastChannel(cmixId int, channelDefinition []byte) (*Channel, error)
 	return &Channel{ch: ch}, nil
 }
 
-// Listen registers a BroadcastListener for a given method.
-// This allows users to handle incoming broadcast messages.
+// Listen registers a BroadcastListener for a given method. This allows users to
+// handle incoming broadcast messages.
 //
 // Parameters:
 //  - l - BroadcastListener object
@@ -139,7 +144,8 @@ func (c *Channel) Listen(l BroadcastListener, method int) error {
 //
 // Returns:
 //  - []byte - the JSON marshalled bytes of the BroadcastReport object, which
-//    can be passed into WaitForRoundResult to see if the broadcast succeeded.
+//    can be passed into Cmix.WaitForRoundResult to see if the broadcast
+//    succeeded.
 func (c *Channel) Broadcast(payload []byte) ([]byte, error) {
 	rid, eid, err := c.ch.Broadcast(payload, cmix.GetDefaultCMIXParams())
 	if err != nil {
@@ -172,17 +178,20 @@ func (c *Channel) BroadcastAsymmetric(payload, pk []byte) ([]byte, error) {
 	})
 }
 
-// MaxPayloadSize returns the maximum possible payload size which can be broadcast.
+// MaxPayloadSize returns the maximum possible payload size which can be
+// broadcast.
 func (c *Channel) MaxPayloadSize() int {
 	return c.ch.MaxPayloadSize()
 }
 
-// MaxAsymmetricPayloadSize returns the maximum possible payload size which can be broadcast.
+// MaxAsymmetricPayloadSize returns the maximum possible payload size which can
+// be broadcast.
 func (c *Channel) MaxAsymmetricPayloadSize() int {
 	return c.ch.MaxAsymmetricPayloadSize()
 }
 
-// Get returns the result of calling json.Marshal on a ChannelDef based on the underlying crypto broadcast.Channel.
+// Get returns the result of calling json.Marshal on a ChannelDef based on the
+// underlying crypto broadcast.Channel.
 func (c *Channel) Get() ([]byte, error) {
 	def := c.ch.Get()
 	return json.Marshal(&ChannelDef{
diff --git a/bindings/cmix.go b/bindings/cmix.go
index 4b9d66cf31598178e56e387d429b390cb91356c4..71d5b1c64e19277498922c345edac1ab38997698 100644
--- a/bindings/cmix.go
+++ b/bindings/cmix.go
@@ -35,10 +35,10 @@ type Cmix struct {
 	id  int
 }
 
-// NewCmix creates user storage, generates keys, connects, and registers
-// with the network. Note that this does not register a username/identity, but
-// merely creates a new cryptographic identity for adding such information
-// at a later date.
+// NewCmix creates user storage, generates keys, connects, and registers with
+// the network. Note that this does not register a username/identity, but merely
+// creates a new cryptographic identity for adding such information at a later
+// date.
 //
 // Users of this function should delete the storage directory on error.
 func NewCmix(ndfJSON, storageDir string, password []byte, registrationCode string) error {
@@ -49,8 +49,9 @@ func NewCmix(ndfJSON, storageDir string, password []byte, registrationCode strin
 	return nil
 }
 
-// LoadCmix will load an existing user storage from the storageDir using the password.
-// This will fail if the user storage does not exist or the password is incorrect.
+// LoadCmix will load an existing user storage from the storageDir using the
+// password. This will fail if the user storage does not exist or the password
+// is incorrect.
 //
 // The password is passed as a byte array so that it can be cleared from memory
 // and stored as securely as possible using the MemGuard library.
@@ -82,6 +83,10 @@ func (c *Cmix) GetID() int {
 	return c.id
 }
 
+////////////////////////////////////////////////////////////////////////////////
+// cMix Tracker                                                               //
+////////////////////////////////////////////////////////////////////////////////
+
 // cmixTracker is a singleton used to keep track of extant Cmix objects,
 // preventing race conditions created by passing it over the bindings.
 type cmixTracker struct {
@@ -90,8 +95,8 @@ type cmixTracker struct {
 	mux     sync.RWMutex
 }
 
-// make creates a Cmix from a xxdk.Cmix, assigns it a unique ID,and adds it to
-// the cmixTracker.
+// make creates a Cmix from a [xxdk.Cmix], assigns it a unique ID, and adds it
+// to the cmixTracker.
 func (ct *cmixTracker) make(c *xxdk.Cmix) *Cmix {
 	ct.mux.Lock()
 	defer ct.mux.Unlock()
diff --git a/bindings/connect.go b/bindings/connect.go
index c097d0cb3a23c9366ff6057a690c2b92845bc2aa..912ee6ffa9744e12a5b44ae626a198cb6e9bb36e 100644
--- a/bindings/connect.go
+++ b/bindings/connect.go
@@ -48,7 +48,7 @@ func (c *Connection) GetId() int {
 // Parameters:
 //  - e2eId - ID of the E2E object in the e2e tracker
 //  - recipientContact - marshalled contact.Contact object
-//  - myIdentity - marshalled ReceptionIdentity object
+//  - e2eParamsJSON - JSON marshalled byte of xxdk.E2EParams object
 func (c *Cmix) Connect(e2eId int, recipientContact, e2eParamsJSON []byte) (
 	*Connection, error) {
 	if len(e2eParamsJSON) == 0 {
@@ -83,7 +83,7 @@ func (c *Cmix) Connect(e2eId int, recipientContact, e2eParamsJSON []byte) (
 //
 // Returns:
 //  - []byte - the JSON marshalled bytes of the E2ESendReport object, which can
-//    be passed into WaitForRoundResult to see if the send succeeded.
+//    be passed into Cmix.WaitForRoundResult to see if the send succeeded.
 func (c *Connection) SendE2E(mt int, payload []byte) ([]byte, error) {
 	rounds, mid, ts, err := c.connection.SendE2E(catalog.MessageType(mt), payload,
 		c.params.Base)
diff --git a/bindings/delivery.go b/bindings/delivery.go
index 7d549ec7d397da9654ff5be8fccdd7122a6c3c2f..39df199c799b395cae42e0743debca49375a46c0 100644
--- a/bindings/delivery.go
+++ b/bindings/delivery.go
@@ -70,19 +70,21 @@ type MessageDeliveryCallback interface {
 	EventCallback(delivered, timedOut bool, roundResults []byte)
 }
 
-// WaitForRoundResult allows the caller to get notified if the rounds a
-// message was sent in successfully completed. Under the hood, this uses an API
-// that uses the internal round data, network historical round lookup, and
-// waiting on network events to determine what has (or will) occur.
-//
-// The callbacks will return at timeoutMS if no state update occurs.
+// WaitForRoundResult allows the caller to get notified if the rounds a message
+// was sent in successfully completed. Under the hood, this uses an API that
+// uses the internal round data, network historical round lookup, and waiting on
+// network events to determine what has (or will) occur.
 //
 // This function takes the marshaled send report to ensure a memory leak does
 // not occur as a result of both sides of the bindings holding a reference to
 // the same pointer.
 //
-// roundList is a JSON marshalled RoundsList or any JSON marshalled send report
-// that inherits a RoundsList object.
+// Parameters:
+//  - roundList - JSON marshalled bytes of RoundsList or JSON of any send report
+//    that inherits a [bindings.RoundsList] object
+//  - mdc - callback that adheres to the MessageDeliveryCallback interface
+//  - timeoutMS - timeout when the callback will return if no state update
+//    occurs, in milliseconds
 func (c *Cmix) WaitForRoundResult(
 	roundList []byte, mdc MessageDeliveryCallback, timeoutMS int) error {
 	jww.INFO.Printf("WaitForRoundResult(%s, _, %d)", roundList, timeoutMS)
diff --git a/bindings/dummy.go b/bindings/dummy.go
new file mode 100644
index 0000000000000000000000000000000000000000..6d6ee3c391c3edfb6812347f702d16bbc65dc443
--- /dev/null
+++ b/bindings/dummy.go
@@ -0,0 +1,86 @@
+////////////////////////////////////////////////////////////////////////////////
+// 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/elixxir/client/dummy"
+	"time"
+)
+
+// DummyTraffic is the bindings-layer dummy (or "cover") traffic manager. T
+// The manager can be used to set and get the status of the thread responsible for
+// sending dummy messages.
+type DummyTraffic struct {
+	m *dummy.Manager
+}
+
+// NewDummyTrafficManager creates a DummyTraffic manager and initialises the
+// dummy traffic sending thread. Note that the manager does not start sending
+// dummy traffic until true is passed into DummyTraffic.SetStatus. The time
+// duration between each sending operation and the amount of messages sent each
+// interval are randomly generated values with bounds defined by the given
+// parameters below.
+//
+// Parameters:
+//  - cmixId - a Cmix object ID in the tracker.
+//  - maxNumMessages - the upper bound of the random number of messages sent
+//    each sending cycle.
+//  - avgSendDeltaMS - the average duration, in milliseconds, to wait between
+//    sends.
+//  - randomRangeMS - the upper bound of the interval between sending cycles, in
+//    milliseconds. Sends occur every avgSendDeltaMS +/- a random duration with
+//    an upper bound of randomRangeMS.
+func NewDummyTrafficManager(cmixId, maxNumMessages, avgSendDeltaMS,
+	randomRangeMS int) (*DummyTraffic, error) {
+
+	// Get user from singleton
+	net, err := cmixTrackerSingleton.get(cmixId)
+	if err != nil {
+		return nil, err
+	}
+
+	avgSendDelta := time.Duration(avgSendDeltaMS) * time.Millisecond
+	randomRange := time.Duration(randomRangeMS) * time.Millisecond
+
+	m := dummy.NewManager(
+		maxNumMessages, avgSendDelta, randomRange, net.api)
+
+	return &DummyTraffic{m}, net.api.AddService(m.StartDummyTraffic)
+}
+
+// SetStatus sets the state of the DummyTraffic manager's send thread by passing
+// in a boolean parameter. There may be a small delay in between this call and
+// the status of the sending thread to change accordingly. For example, passing
+// false into this call while the sending thread is currently sending messages
+// will not cancel nor halt the sending operation, but will pause the thread
+// once that operation has completed.
+//
+// Parameters:
+//  - status - Input should be true if you want to send dummy messages and false
+//    if you want to pause dummy messages.
+//
+// Returns:
+//  - error - if the DummyTraffic.SetStatus is called too frequently, causing
+//    the internal status channel to fill.
+func (dt *DummyTraffic) SetStatus(status bool) error {
+	return dt.m.SetStatus(status)
+}
+
+// GetStatus returns the current state of the DummyTraffic manager's sending
+// thread. Note that this function does not return the status set by the most
+// recent call to SetStatus. Instead, this call returns the current status of
+// the sending thread. This is due to the small delay that may occur between
+// calling SetStatus and the sending thread taking into effect that status
+// change.
+//
+// Returns:
+//   - bool - Returns true if sending thread is sending dummy messages and false
+//     if sending thread is paused/stopped and is not sending dummy messages.
+func (dt *DummyTraffic) GetStatus() bool {
+	return dt.m.GetStatus()
+}
diff --git a/bindings/e2e.go b/bindings/e2e.go
index 22438e73df9db897e20b785b871d78aadba73307..22bc2b7249c48ffff3aff0ce88102175282fd024 100644
--- a/bindings/e2e.go
+++ b/bindings/e2e.go
@@ -26,20 +26,20 @@ var e2eTrackerSingleton = &e2eTracker{
 	count:   0,
 }
 
-// E2e wraps the xxdk.E2e, implementing additional functions
-// to support the bindings E2e interface.
+// E2e wraps the xxdk.E2e, implementing additional functions to support the
+// bindings E2e interface.
 type E2e struct {
 	api *xxdk.E2e
 	id  int
 }
 
-// GetID returns the e2eTracker ID for the E2e object.
+// GetID returns the ID for this E2e in the e2eTracker.
 func (e *E2e) GetID() int {
 	return e.id
 }
 
 // Login creates and returns a new E2e object and adds it to the
-// e2eTrackerSingleton. identity should be created via
+// e2eTrackerSingleton. Identity should be created via
 // Cmix.MakeReceptionIdentity and passed in here. If callbacks is left nil, a
 // default auth.Callbacks will be used.
 func Login(cmixId int, callbacks AuthCallbacks, identity,
@@ -80,7 +80,7 @@ func Login(cmixId int, callbacks AuthCallbacks, identity,
 }
 
 // LoginEphemeral creates and returns a new ephemeral E2e object and adds it to
-// the e2eTrackerSingleton. identity should be created via
+// the e2eTrackerSingleton. Identity should be created via
 // Cmix.MakeReceptionIdentity or Cmix.MakeLegacyReceptionIdentity and passed in
 // here. If callbacks is left nil, a default auth.Callbacks will be used.
 func LoginEphemeral(cmixId int, callbacks AuthCallbacks, identity,
@@ -126,13 +126,15 @@ func (e *E2e) GetContact() []byte {
 	return e.api.GetReceptionIdentity().GetContact().Marshal()
 }
 
-// GetUdAddressFromNdf retrieve the User Discovery's network address fom the NDF.
+// GetUdAddressFromNdf retrieve the User Discovery's network address fom the
+// NDF.
 func (e *E2e) GetUdAddressFromNdf() string {
 	return e.api.GetCmix().GetInstance().GetPartialNdf().
 		Get().UDB.Address
 }
 
-// GetUdCertFromNdf retrieves the User Discovery's TLS certificate from the NDF.
+// GetUdCertFromNdf retrieves the User Discovery's TLS certificate (in PEM
+// format) from the NDF.
 func (e *E2e) GetUdCertFromNdf() []byte {
 	return []byte(e.api.GetCmix().GetInstance().GetPartialNdf().Get().UDB.Cert)
 }
@@ -143,19 +145,26 @@ func (e *E2e) GetUdCertFromNdf() []byte {
 // Returns
 //  - []byte - A byte marshalled contact.Contact.
 func (e *E2e) GetUdContactFromNdf() ([]byte, error) {
-	udIdData := e.api.GetCmix().GetInstance().GetPartialNdf().Get().UDB.ID
+	// Retrieve data from E2e
+	netDef := e.api.GetCmix().GetInstance().GetPartialNdf().Get()
+	e2eGroup := e.api.GetE2E().GetGroup()
+
+	// Unmarshal UD ID
+	udIdData := netDef.UDB.ID
 	udId, err := id.Unmarshal(udIdData)
 	if err != nil {
 		return nil, err
 	}
 
-	udDhPubKeyData := e.api.GetCmix().GetInstance().GetPartialNdf().Get().UDB.DhPubKey
-	udDhPubKey := e.api.GetE2E().GetGroup().NewInt(1)
+	// Unmarshal DH pub key
+	udDhPubKeyData := netDef.UDB.DhPubKey
+	udDhPubKey := e2eGroup.NewInt(1)
 	err = udDhPubKey.UnmarshalJSON(udDhPubKeyData)
 	if err != nil {
 		return nil, err
 	}
 
+	// Construct contact
 	udContact := contact.Contact{
 		ID:       udId,
 		DhPubKey: udDhPubKey,
@@ -178,11 +187,11 @@ type authCallback struct {
 }
 
 // convertAuthCallbacks turns an auth.Callbacks into an AuthCallbacks.
-func convertAuthCallbacks(requestor contact.Contact,
+func convertAuthCallbacks(requester contact.Contact,
 	receptionID receptionID.EphemeralIdentity, round rounds.Round) (
 	contact []byte, receptionId []byte, ephemeralId int64, roundId int64) {
 
-	contact = requestor.Marshal()
+	contact = requester.Marshal()
 	receptionId = receptionID.Source.Marshal()
 	ephemeralId = int64(receptionID.EphId.UInt64())
 	roundId = int64(round.ID)
@@ -207,6 +216,10 @@ func (a *authCallback) Reset(partner contact.Contact,
 	a.bindingsCbs.Reset(convertAuthCallbacks(partner, receptionID, round))
 }
 
+////////////////////////////////////////////////////////////////////////////////
+// E2E Tracker                                                                //
+////////////////////////////////////////////////////////////////////////////////
+
 // e2eTracker is a singleton used to keep track of extant E2e objects,
 // preventing race conditions created by passing it over the bindings.
 type e2eTracker struct {
@@ -222,15 +235,15 @@ func (ct *e2eTracker) make(c *xxdk.E2e) *E2e {
 	ct.mux.Lock()
 	defer ct.mux.Unlock()
 
-	id := ct.count
+	e2eID := ct.count
 	ct.count++
 
-	ct.tracked[id] = &E2e{
+	ct.tracked[e2eID] = &E2e{
 		api: c,
-		id:  id,
+		id:  e2eID,
 	}
 
-	return ct.tracked[id]
+	return ct.tracked[e2eID]
 }
 
 // get an E2e from the e2eTracker given its ID.
diff --git a/bindings/e2eAuth.go b/bindings/e2eAuth.go
index b2277eb3e3c77ae54ab933dae3b23c9edee9ef25..cee5f04f4d0e1fafcaecc8c65ff5cdb56e3c9209 100644
--- a/bindings/e2eAuth.go
+++ b/bindings/e2eAuth.go
@@ -35,12 +35,12 @@ import (
 //
 // Parameters:
 //  - partnerContact - the marshalled bytes of the contact.Contact object.
-//  - myFacts - stringified list of fact.FactList.
+//  - factsListJson - the JSON marshalled bytes of [fact.FactList].
 //
 // Returns:
 //  - int64 - ID of the round (convert to uint64)
 func (e *E2e) Request(partnerContact, factsListJson []byte) (int64, error) {
-	var factsList []Fact
+	var factsList fact.FactList
 	err := json.Unmarshal(factsListJson, &factsList)
 	if err != nil {
 		return 0, err
@@ -51,15 +51,7 @@ func (e *E2e) Request(partnerContact, factsListJson []byte) (int64, error) {
 		return 0, err
 	}
 
-	myFacts := fact.FactList{}
-	for _, f := range factsList {
-		myFacts = append(myFacts, fact.Fact{
-			Fact: f.Fact,
-			T:    fact.FactType(f.Type),
-		})
-	}
-
-	roundID, err := e.api.GetAuth().Request(partner, myFacts)
+	roundID, err := e.api.GetAuth().Request(partner, factsList)
 
 	return int64(roundID), err
 }
@@ -179,8 +171,7 @@ func (e *E2e) DeleteSentRequests() error {
 	return e.api.GetAuth().DeleteSentRequests()
 }
 
-// DeleteReceiveRequests clears all received requests from auth
-// storage.
+// DeleteReceiveRequests clears all received requests from auth storage.
 func (e *E2e) DeleteReceiveRequests() error {
 	return e.api.GetAuth().DeleteReceiveRequests()
 }
diff --git a/bindings/e2eHandler.go b/bindings/e2eHandler.go
index 83ca76d62341b62162fa97d19be63b5537c517d2..b71d6646d85eec5cea9a4450979d75003e20b9d2 100644
--- a/bindings/e2eHandler.go
+++ b/bindings/e2eHandler.go
@@ -24,9 +24,11 @@ import (
 // SendE2E.
 //
 // Example E2ESendReport:
-//  {"Rounds":[1,5,9],
+//  {
+//   "Rounds":[1,5,9],
 //   "MessageID":"51Yy47uZbP0o2Y9B/kkreDLTB6opUol3M3mYiY2dcdQ=",
-//   "Timestamp":1653582683183384000}
+//   "Timestamp":1653582683183384000
+//  }
 type E2ESendReport struct {
 	RoundsList
 	MessageID []byte
@@ -117,7 +119,7 @@ func (e *E2e) RemoveService(tag string) error {
 //
 // Returns:
 //  - []byte - the JSON marshalled bytes of the E2ESendReport object, which can
-//    be passed into WaitForRoundResult to see if the send succeeded.
+//    be passed into Cmix.WaitForRoundResult to see if the send succeeded.
 func (e *E2e) SendE2E(messageType int, recipientId, payload,
 	e2eParams []byte) ([]byte, error) {
 	// Note that specifically these are the Base params from xxdk.E2EParams
@@ -166,11 +168,9 @@ func (e *E2e) AddService(tag string, processor Processor) error {
 //  - messageType - message type from the sender you want to listen for.
 //  - newListener: A provider for a callback to hear a message.
 //    Do not pass nil to this.
-func (e *E2e) RegisterListener(senderID []byte,
-	messageType int,
-	newListener Listener) error {
-	jww.INFO.Printf("RegisterListener(%v, %d)", senderID,
-		messageType)
+func (e *E2e) RegisterListener(
+	senderID []byte, messageType int, newListener Listener) error {
+	jww.INFO.Printf("RegisterListener(%v, %d)", senderID, messageType)
 
 	// Convert senderID to id.Id object
 	var uid *id.ID
diff --git a/bindings/errors.go b/bindings/errors.go
index 32c945c80c219626623b39ce12e3f299c1f73cef..c5f4edce5a03fd31ce40ca5c15c3e689fe16e174 100644
--- a/bindings/errors.go
+++ b/bindings/errors.go
@@ -46,14 +46,14 @@ const (
 	UnrecognizedMessage = UnrecognizedCode + "Unrecognized error from XX backend, please report"
 )
 
-// CreateUserFriendlyErrorMessage will convert the passed in error string
-// to an error string that is user-friendly if a substring match is
-// found to a common error. Common errors is a map which can be updated
-// using UpdateCommonErrors. If the error is not common, some simple parsing
-// is done on the error message to make it more user-accessible, removing
-// backend specific jargon.
+// CreateUserFriendlyErrorMessage will convert the passed in error string to an
+// error string that is user-friendly if a substring match is found to a
+// common error. Common errors is a map that can be updated using
+// UpdateCommonErrors. If the error is not common, some simple parsing is done
+// on the error message to make it more user-accessible, removing backend
+// specific jargon.
 //
-// Parameters
+// Parameters:
 //   - errStr - an error returned from the backend.
 //
 // Returns
@@ -97,16 +97,18 @@ func CreateUserFriendlyErrorMessage(errStr string) string {
 	return fmt.Sprintf("%s: %v", UnrecognizedCode, errStr)
 }
 
-// UpdateCommonErrors updates the internal error mapping DB. This internal database
-// maps errors returned from the backend to user-friendly error messages.
+// UpdateCommonErrors updates the internal error mapping database. This internal
+// database maps errors returned from the backend to user-friendly error
+// messages.
 //
-// Parameters
+// Parameters:
 //  - jsonFile - contents of a JSON file whose format conforms to the example below.
+//
 // Example Input:
-//   {
-//  	"Failed to Unmarshal Conversation": "Could not retrieve conversation",
-//  	"Failed to unmarshal SentRequestMap": "Failed to pull up friend requests",
-//  	"cannot create username when network is not health": "Cannot create username, unable to connect to network",
+//  {
+//    "Failed to Unmarshal Conversation": "Could not retrieve conversation",
+//    "Failed to unmarshal SentRequestMap": "Failed to pull up friend requests",
+//    "cannot create username when network is not health": "Cannot create username, unable to connect to network",
 //  }
 func UpdateCommonErrors(jsonFile string) error {
 	errorMux.Lock()
diff --git a/bindings/fileTransfer.go b/bindings/fileTransfer.go
index b0182ed6e2cb9c018a54ee716946e60cbb74f328..79a7d6bc7cbda08f0cc9b85341a82692bb834362 100644
--- a/bindings/fileTransfer.go
+++ b/bindings/fileTransfer.go
@@ -179,7 +179,6 @@ func InitFileTransfer(e2eID int, receiveFileCallback ReceiveFileCallback,
 // Parameters:
 //  - payload - JSON marshalled FileSend
 //  - recipientID - marshalled recipient id.ID
-//  - paramsJSON - JSON marshalled e2e.Params
 //  - retry - number of retries allowed
 //  - callback - callback that reports file sending progress
 //  - period - duration to wait between progress callbacks triggering
diff --git a/bindings/follow.go b/bindings/follow.go
index 482bbd9cd83ab254ee2677a494d8e720df67c88d..6267a34125388a681827437d7a1e1cea1aa1c8dd 100644
--- a/bindings/follow.go
+++ b/bindings/follow.go
@@ -27,28 +27,28 @@ import (
 //
 // Threads Started:
 //   - Network Follower (/network/follow.go)
-//   	tracks the network events and hands them off to workers for handling.
+//     tracks the network events and hands them off to workers for handling.
 //   - Historical Round Retrieval (/network/rounds/historical.go)
-// 		retrieves data about rounds that are too old to be stored by the client.
+//     retrieves data about rounds that are too old to be stored by the client.
 //	 - Message Retrieval Worker Group (/network/rounds/retrieve.go)
-//		requests all messages in a given round from the gateway of the last
-//		nodes.
+//	   requests all messages in a given round from the gateway of the last
+//	   nodes.
 //	 - Message Handling Worker Group (/network/message/handle.go)
-//		decrypts and partitions messages when signals via the Switchboard.
+//	   decrypts and partitions messages when signals via the Switchboard.
 //	 - Health Tracker (/network/health),
-//		via the network instance, tracks the state of the network.
+//	   via the network instance, tracks the state of the network.
 //	 - Garbled Messages (/network/message/garbled.go)
-//		can be signaled to check all recent messages that could be decoded. It
-//		uses a message store on disk for persistence.
+//	   can be signaled to check all recent messages that could be decoded. It
+//	   uses a message store on disk for persistence.
 //	 - Critical Messages (/network/message/critical.go)
-//		ensures all protocol layer mandatory messages are sent. It uses a
-//		message store on disk for persistence.
+//	   ensures all protocol layer mandatory messages are sent. It uses a message
+//	   store on disk for persistence.
 //	 - KeyExchange Trigger (/keyExchange/trigger.go)
-//		responds to sent rekeys and executes them.
+//	   responds to sent rekeys and executes them.
 //   - KeyExchange Confirm (/keyExchange/confirm.go)
-//		responds to confirmations of successful rekey operations.
+//	   responds to confirmations of successful rekey operations.
 //   - Auth Callback (/auth/callback.go)
-//      handles both auth confirm and requests.
+//     handles both auth confirm and requests.
 func (c *Cmix) StartNetworkFollower(timeoutMS int) error {
 	timeout := time.Duration(timeoutMS) * time.Millisecond
 	return c.api.StartNetworkFollower(timeout)
@@ -58,7 +58,7 @@ func (c *Cmix) StartNetworkFollower(timeoutMS int) error {
 // an error if the follower is in the wrong state to stop or if it fails to stop
 // it.
 //
-// if the network follower is running and this fails, the Cmix object will
+// If the network follower is running and this fails, the Cmix object will
 // most likely be in an unrecoverable state and need to be trashed.
 func (c *Cmix) StopNetworkFollower() error {
 	if err := c.api.StopNetworkFollower(); err != nil {
@@ -84,11 +84,9 @@ func (c *Cmix) WaitForNetwork(timeoutMS int) bool {
 
 // NetworkFollowerStatus gets the state of the network follower. It returns a
 // status with the following values:
-//
-// Status:
-//  - Stopped  - 0
-//  - Running  - 2000
-//  - Stopping - 3000
+//  Stopped  - 0
+//  Running  - 2000
+//  Stopping - 3000
 func (c *Cmix) NetworkFollowerStatus() int {
 	return int(c.api.NetworkFollowerStatus())
 }
@@ -103,10 +101,11 @@ type NodeRegistrationReport struct {
 // GetNodeRegistrationStatus returns the current state of node registration.
 //
 // Returns:
-//  - []bye - A marshalled NodeRegistrationReport containing the number of
-//    nodes the user is registered with and the number of nodes present in the NDF.
-//  - An error if it cannot get the node registration status. The most likely cause
-//    is that the network is unhealthy.
+//  - []byte - A marshalled NodeRegistrationReport containing the number of
+//    nodes the user is registered with and the number of nodes present in the
+//    NDF.
+//  - An error if it cannot get the node registration status. The most likely
+//    cause is that the network is unhealthy.
 func (c *Cmix) GetNodeRegistrationStatus() ([]byte, error) {
 	numNodesRegistered, numNodes, err := c.api.GetNodeRegistrationStatus()
 	if err != nil {
diff --git a/bindings/group.go b/bindings/group.go
index 00951c269e43843fd3bd05609f1bddc9562cb844..709a969091b8bbddd5a2d242e8ddab0e51312e9a 100644
--- a/bindings/group.go
+++ b/bindings/group.go
@@ -22,7 +22,7 @@ import (
 )
 
 ////////////////////////////////////////////////////////////////////////////////
-// Group Singleton Tracker                                               //
+// Group Singleton Tracker                                                    //
 ////////////////////////////////////////////////////////////////////////////////
 
 // groupTrackerSingleton is used to track Group objects so that they can be
@@ -129,16 +129,16 @@ func NewGroupChat(e2eID int,
 //    IDs of members the user wants to add to the group.
 //  - message - the initial message sent to all members in the group. This is an
 //    optional parameter and may be nil.
-//  - tag - the name of the group decided by the creator. This is an optional
+//  - name - the name of the group decided by the creator. This is an optional
 //    parameter and may be nil. If nil the group will be assigned the default
 //    name.
 //
 // Returns:
 //  - []byte - the JSON marshalled bytes of the GroupReport object, which can be
-//    passed into WaitForRoundResult to see if the group request message send
-//    succeeded.
-func (g *GroupChat) MakeGroup(membershipBytes []byte, message, name []byte) (
-	[]byte, error) {
+//    passed into Cmix.WaitForRoundResult to see if the group request message
+//    send succeeded.
+func (g *GroupChat) MakeGroup(
+	membershipBytes, message, name []byte) ([]byte, error) {
 
 	// Unmarshal membership list into a list of []*id.Id
 	var members []*id.ID
@@ -246,17 +246,16 @@ func (g *GroupChat) LeaveGroup(groupId []byte) error {
 // Send is the bindings-level function for sending to a group.
 //
 // Parameters:
-//  - groupId - the byte data representing a group ID.
-//    This can be pulled from a marshalled GroupReport.
+//  - groupId - the byte data representing a group ID. This can be pulled from
+//    marshalled GroupReport.
 //  - message - the message that the user wishes to send to the group.
 //  - tag - the tag associated with the message. This tag may be empty.
 //
 // Returns:
 //  - []byte - the JSON marshalled bytes of the GroupSendReport object, which
-//    can be passed into WaitForRoundResult to see if the group message send
-//    succeeded.
-func (g *GroupChat) Send(groupId,
-	message []byte, tag string) ([]byte, error) {
+//    can be passed into Cmix.WaitForRoundResult to see if the group message
+//    send succeeded.
+func (g *GroupChat) Send(groupId, message []byte, tag string) ([]byte, error) {
 	groupID, err := id.Unmarshal(groupId)
 	if err != nil {
 		return nil, errors.Errorf("Failed to unmarshal group ID: %+v", err)
@@ -291,7 +290,7 @@ func (g *GroupChat) GetGroups() ([]byte, error) {
 //
 // Parameters:
 //  - groupId - The byte data representing a group ID (a byte marshalled id.ID).
-//              This can be pulled from a marshalled GroupReport.
+//    This can be pulled from a marshalled GroupReport.
 // Returns:
 //  - Group - The bindings-layer representation of a group.
 func (g *GroupChat) GetGroup(groupId []byte) (*Group, error) {
@@ -317,7 +316,7 @@ func (g *GroupChat) NumGroups() int {
 }
 
 ////////////////////////////////////////////////////////////////////////////////
-// Group Structure
+// Group Structure                                                            //
 ////////////////////////////////////////////////////////////////////////////////
 
 // Group structure contains the identifying and membership information of a
@@ -332,12 +331,13 @@ func (g *Group) GetName() []byte {
 	return g.g.Name
 }
 
-// GetID return the 33-byte unique group ID. This represents the id.ID object
+// GetID return the 33-byte unique group ID. This represents the id.ID object.
 func (g *Group) GetID() []byte {
 	return g.g.ID.Bytes()
 }
 
-// GetTrackedID returns the tracked ID of the Group object. This is used by the backend tracker.
+// GetTrackedID returns the tracked ID of the Group object. This is used by the
+// backend tracker.
 func (g *Group) GetTrackedID() int {
 	return g.id
 }
@@ -375,9 +375,9 @@ func (g *Group) Serialize() []byte {
 	return g.g.Serialize()
 }
 
-//////////////////////////////////////////////////////////////////////////////////
-// Callbacks
-//////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+// Callbacks                                                                  //
+////////////////////////////////////////////////////////////////////////////////
 
 // GroupRequest is a bindings-layer interface that handles a group reception.
 //
diff --git a/bindings/identity.go b/bindings/identity.go
index f1b56803184de8491f3c93ad43d89008f6227bd4..e3aa4abdc2362f80c7d12f103cea39e55bfc93e4 100644
--- a/bindings/identity.go
+++ b/bindings/identity.go
@@ -26,16 +26,18 @@ import (
 //   "RSAPrivatePem":"LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcFFJQkFBS0NBUUVBNU15dTdhYjBJOS9UL1BFUUxtd2x3ejZHV3FjMUNYemVIVXhoVEc4bmg1WWRWSXMxCmJ2THpBVjNOMDJxdXN6K2s4TVFEWjBtejMzdkswUmhPczZIY0NUSFdzTEpXRkE5WWpzWWlCRi9qTDd1bmd1ckIKL2tvK1JJSnNrWGFWaEZaazRGdERoRXhTNWY4RnR0Qmk1NmNLZmdJQlVKT3ozZi9qQllTMkxzMlJ6cWV5YXM3SApjV2RaME9TclBTT3BiYlViU1FPbS9LWnlweGZHU21yZ2oxRUZuU1dZZ2xGZTdUOTRPbHF5MG14QTV5clVXbHorCk9sK3hHbXpCNUp4WUFSMU9oMFQrQTk4RWMrTUZHNm43L1MraDdzRDgybGRnVnJmbStFTzRCdmFKeTRESGZGMWgKNnp6QnVnY25NUVFGc0dLeDFYWC9COTVMdUpPVjdyeXlDbzZGbHdJREFRQUJBb0lCQVFDaUh6OGNlcDZvQk9RTAphUzBVRitHeU5VMnlVcVRNTWtTWThoUkh1c09CMmFheXoybHZVb3RLUHBPbjZRSWRWVTJrcE4vY2dtY0lSb2x5CkhBMDRUOHJBWVNaRlVqaVlRajkzKzRFREpJYXd2Z0YyVEs1bFoyb3oxVTdreStncU82V0RMR2Z0Q0wvODVQWEIKa210aXhnUXpRV3g1RWcvemtHdm03eURBalQxeDloNytsRjJwNFlBam5kT2xTS0dmQjFZeTR1RXBQd0kwc1lWdgpKQWc0MEFxbllZUmt4emJPbmQxWGNjdEJFN2Z1VDdrWXhoeSs3WXYrUTJwVy9BYmh6NGlHOEY1MW9GMGZwV0czCmlISDhsVXZFTkp2SUZEVHZ0UEpESlFZalBRN3lUbGlGZUdrMXZUQkcyQkpQNExzVzhpbDZOeUFuRktaY1hOQ24KeHVCendiSlJBb0dCQVBUK0dGTVJGRHRHZVl6NmwzZmg3UjJ0MlhrMysvUmpvR3BDUWREWDhYNERqR1pVd1RGVQpOS2tQTTNjS29ia2RBYlBDb3FpL0tOOVBibk9QVlZ3R3JkSE9vSnNibFVHYmJGamFTUzJQMFZnNUVhTC9rT2dUCmxMMUdoVFpIUWk1VUlMM0p4M1Z3T0ZRQ3RQOU1UQlQ0UEQvcEFLbDg3VTJXN3JTY1dGV1ZGbFNkQW9HQkFPOFUKVmhHWkRpVGFKTWVtSGZIdVYrNmtzaUlsam9aUVVzeGpmTGNMZ2NjV2RmTHBqS0ZWTzJNN3NqcEJEZ0w4NmFnegorVk14ZkQzZ1l0SmNWN01aMVcwNlZ6TlNVTHh3a1dRY1hXUWdDaXc5elpyYlhCUmZRNUVjMFBlblVoWWVwVzF5CkpkTC8rSlpQeDJxSzVrQytiWU5EdmxlNWdpcjlDSGVzTlR5enVyckRBb0dCQUl0cTJnN1RaazhCSVFUUVNrZ24Kb3BkRUtzRW4wZExXcXlBdENtVTlyaWpHL2l2eHlXczMveXZDQWNpWm5VVEp0QUZISHVlbXVTeXplQ2g5QmRkegoyWkRPNUdqQVBxVHlQS3NudFlNZkY4UDczZ1NES1VSWWVFbHFDejdET0c5QzRzcitPK3FoN1B3cCtqUmFoK1ZiCkNuWllNMDlBVDQ3YStJYUJmbWRkaXpLbEFvR0JBSmo1dkRDNmJIQnNISWlhNUNJL1RZaG5YWXUzMkVCYytQM0sKMHF3VThzOCtzZTNpUHBla2Y4RjVHd3RuUU4zc2tsMk1GQWFGYldmeVFZazBpUEVTb0p1cGJzNXA1enNNRkJ1bwpncUZrVnQ0RUZhRDJweTVwM2tQbDJsZjhlZXVwWkZScGE0WmRQdVIrMjZ4eWYrNEJhdlZJeld3NFNPL1V4Q3crCnhqbTNEczRkQW9HQWREL0VOa1BjU004c1BCM3JSWW9MQ2twcUV2U0MzbVZSbjNJd3c1WFAwcDRRVndhRmR1ckMKYUhtSE1EekNrNEUvb0haQVhFdGZ2S2tRaUI4MXVYM2c1aVo4amdYUVhXUHRteTVIcVVhcWJYUTlENkxWc3B0egpKL3R4SWJLMXp5c1o2bk9IY1VoUUwyVVF6SlBBRThZNDdjYzVzTThEN3kwZjJ0QURTQUZNMmN3PQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQ==",
 //   "Salt":"4kk02v0NIcGtlobZ/xkxqWz8uH/ams/gjvQm14QT0dI=",
 //   "DHKeyPrivate":"eyJWYWx1ZSI6NDU2MDgzOTEzMjA0OTIyODA5Njg2MDI3MzQ0MzM3OTA0MzAyODYwMjM2NDk2NDM5NDI4NTcxMTMwNDMzOTQwMzgyMTIyMjY4OTQzNTMyMjIyMzc1MTkzNTEzMjU4MjA4MDA0NTczMDY4MjEwNzg2NDI5NjA1MjA0OTA3MjI2ODI5OTc3NTczMDkxODY0NTY3NDExMDExNjQxNCwiRmluZ2VycHJpbnQiOjE2ODAxNTQxNTExMjMzMDk4MzYzfQ=="
-//  }
+//   "E2eGrp": "eyJnZW4iOiIyIiwicHJpbWUiOiJlMmVlOTgzZDAzMWRjMWRiNmYxYTdhNjdkZjBlOWE4ZTU1NjFkYjhlOGQ0OTQxMzM5NGMwNDliN2E4YWNjZWRjMjk4NzA4ZjEyMTk1MWQ5Y2Y5MjBlYzVkMTQ2NzI3YWE0YWU1MzViMDkyMmM2ODhiNTViM2RkMmFlZGY2YzAxYzk0NzY0ZGFiOTM3OTM1YWE4M2JlMzZlNjc3NjA3MTNhYjQ0YTYzMzdjMjBlNzg2MTU3NWU3NDVkMzFmOGI5ZTlhZDg0MTIxMThjNjJhM2UyZTI5ZGY0NmIwODY0ZDBjOTUxYzM5NGE1Y2JiZGM2YWRjNzE4ZGQyYTNlMDQxMDIzZGJiNWFiMjNlYmI0NzQyZGU5YzE2ODdiNWIzNGZhNDhjMzUyMTYzMmM0YTUzMGU4ZmZiMWJjNTFkYWRkZjQ1M2IwYjI3MTdjMmJjNjY2OWVkNzZiNGJkZDVjOWZmNTU4ZTg4ZjI2ZTU3ODUzMDJiZWRiY2EyM2VhYzVhY2U5MjA5NmVlOGE2MDY0MmZiNjFlOGYzZDI0OTkwYjhjYjEyZWU0NDhlZWY3OGUxODRjNzI0MmRkMTYxYzc3MzhmMzJiZjI5YTg0MTY5ODk3ODgyNWI0MTExYjRiYzNlMWUxOTg0NTUwOTU5NTgzMzNkNzc2ZDhiMmJlZWVkM2ExYTFhMjIxYTZlMzdlNjY0YTY0YjgzOTgxYzQ2ZmZkZGMxYTQ1ZTNkNTIxMWFhZjhiZmJjMDcyNzY4YzRmNTBkN2Q3ODAzZDJkNGYyNzhkZTgwMTRhNDczMjM2MzFkN2UwNjRkZTgxYzBjNmJmYTQzZWYwZTY5OTg4NjBmMTM5MGI1ZDNmZWFjYWYxNjk2MDE1Y2I3OWMzZjljMmQ5M2Q5NjExMjBjZDBlNWYxMmNiYjY4N2VhYjA0NTI0MWY5Njc4OWMzOGU4OWQ3OTYxMzhlNjMxOWJlNjJlMzVkODdiMTA0OGNhMjhiZTM4OWI1NzVlOTk0ZGNhNzU1NDcxNTg0YTA5ZWM3MjM3NDJkYzM1ODczODQ3YWVmNDlmNjZlNDM4NzMifQ=="
+// }
 type ReceptionIdentity struct {
 	ID            []byte // User ID (base64)
 	RSAPrivatePem []byte // RSA Private key (PEM format)
 	Salt          []byte // Salt for identity (base64)
 	DHKeyPrivate  []byte // DH Private key
+	E2eGrp        []byte
 }
 
 // StoreReceptionIdentity stores the given identity in Cmix storage with the
-// given key.  This is the ideal way to securely store identities, as the caller
+// given key. This is the ideal way to securely store identities, as the caller
 // of this function is only required to store the given key separately rather
 // than the keying material.
 func StoreReceptionIdentity(key string, identity []byte, cmixId int) error {
@@ -77,7 +79,8 @@ func (c *Cmix) MakeReceptionIdentity() ([]byte, error) {
 }
 
 // MakeLegacyReceptionIdentity generates the legacy identity for receiving
-// messages.
+// messages. As with all legacy calls, this should primarily be used
+// for the xx messenger team.
 func (c *Cmix) MakeLegacyReceptionIdentity() ([]byte, error) {
 	ident, err := xxdk.MakeLegacyReceptionIdentity(c.api)
 	if err != nil {
@@ -90,17 +93,23 @@ func (c *Cmix) MakeLegacyReceptionIdentity() ([]byte, error) {
 // GetReceptionRegistrationValidationSignature returns the signature provided by
 // the xx network.
 func (c *Cmix) GetReceptionRegistrationValidationSignature() []byte {
-	return c.api.GetStorage().GetReceptionRegistrationValidationSignature()
+	regSig := c.api.GetStorage().GetReceptionRegistrationValidationSignature()
+	return regSig
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // Contact Functions                                                          //
 ////////////////////////////////////////////////////////////////////////////////
 
-// GetIDFromContact accepts a marshalled contact.Contact object and returns a
-// marshalled id.ID object.
-func GetIDFromContact(marshaled []byte) ([]byte, error) {
-	cnt, err := contact.Unmarshal(marshaled)
+// GetIDFromContact returns the ID in the [contact.Contact] object.
+//
+// Parameters:
+//  - marshaledContact - JSON marshalled bytes of [contact.Contact]
+//
+// Returns:
+//  - []byte - bytes of the [id.ID] object
+func GetIDFromContact(marshaledContact []byte) ([]byte, error) {
+	cnt, err := contact.Unmarshal(marshaledContact)
 	if err != nil {
 		return nil, err
 	}
@@ -108,10 +117,16 @@ func GetIDFromContact(marshaled []byte) ([]byte, error) {
 	return cnt.ID.Marshal(), nil
 }
 
-// GetPubkeyFromContact accepts a marshalled contact.Contact object and returns
-// a JSON marshalled large.Int DH public key.
-func GetPubkeyFromContact(marshaled []byte) ([]byte, error) {
-	cnt, err := contact.Unmarshal(marshaled)
+// GetPubkeyFromContact returns the DH public key in the [contact.Contact]
+// object.
+//
+// Parameters:
+//  - marshaledContact - JSON marshalled bytes of [contact.Contact]
+//
+// Returns:
+//  - []byte - JSON marshalled bytes of the [cyclic.Int] object
+func GetPubkeyFromContact(marshaledContact []byte) ([]byte, error) {
+	cnt, err := contact.Unmarshal(marshaledContact)
 	if err != nil {
 		return nil, err
 	}
@@ -123,67 +138,44 @@ func GetPubkeyFromContact(marshaled []byte) ([]byte, error) {
 // Fact Functions                                                             //
 ////////////////////////////////////////////////////////////////////////////////
 
-// Fact is an internal fact type for use in the bindings layer.
-//
-// JSON example:
-//  {
-//   "Fact": "Zezima",
-//   "Type": 0
-//  }
-type Fact struct {
-	Fact string
-	Type int
-}
-
 // SetFactsOnContact replaces the facts on the contact with the passed in facts
 // pass in empty facts in order to clear the facts.
 //
 // Parameters:
-//  - marshaled - JSON marshalled contact.Contact object
-//  - facts - JSON marshalled Fact object.
-func SetFactsOnContact(marshaled []byte, facts []byte) ([]byte, error) {
-	cnt, err := contact.Unmarshal(marshaled)
+//  - marshaledContact - the JSON marshalled bytes of [contact.Contact]
+//  - factListJSON - the JSON marshalled bytes of [fact.FactList]
+//
+// Returns:
+//  - []byte - marshalled bytes of the modified [contact.Contact]
+func SetFactsOnContact(marshaledContact []byte, factListJSON []byte) ([]byte, error) {
+	cnt, err := contact.Unmarshal(marshaledContact)
 	if err != nil {
 		return nil, err
 	}
 
-	factsList := make([]Fact, 0)
-	err = json.Unmarshal(facts, &factsList)
+	var factsList fact.FactList
+	err = json.Unmarshal(factListJSON, &factsList)
 	if err != nil {
 		return nil, err
 	}
 
-	realFactList := make(fact.FactList, 0, len(factsList))
-	for i := range factsList {
-		realFactList = append(realFactList, fact.Fact{
-			Fact: factsList[i].Fact,
-			T:    fact.FactType(factsList[i].Type),
-		})
-	}
+	cnt.Facts = factsList
 
-	cnt.Facts = realFactList
 	return cnt.Marshal(), nil
 }
 
-// GetFactsFromContact accepts a marshalled contact.Contact object and returns
-// its marshalled list of Fact objects.
-func GetFactsFromContact(marshaled []byte) ([]byte, error) {
-	cnt, err := contact.Unmarshal(marshaled)
+// GetFactsFromContact returns the fact list in the [contact.Contact] object.
+//
+// Parameters:
+//  - marshaledContact - the JSON marshalled bytes of [contact.Contact]
+//
+// Returns:
+//  - []byte - the JSON marshalled bytes of [fact.FactList]
+func GetFactsFromContact(marshaledContact []byte) ([]byte, error) {
+	cnt, err := contact.Unmarshal(marshaledContact)
 	if err != nil {
 		return nil, err
 	}
 
-	factsList := make([]Fact, len(cnt.Facts))
-	for i := range cnt.Facts {
-		factsList = append(factsList, Fact{
-			Fact: cnt.Facts[i].Fact,
-			Type: int(cnt.Facts[i].T),
-		})
-	}
-
-	factsListMarshaled, err := json.Marshal(&factsList)
-	if err != nil {
-		return nil, err
-	}
-	return factsListMarshaled, nil
+	return json.Marshal(&cnt.Facts)
 }
diff --git a/bindings/identity_test.go b/bindings/identity_test.go
index 662f1f5c000021def40a17a50d9c9646b8a78779..3dda17691bc63fc1e049ff14cccbbe7888d55146 100644
--- a/bindings/identity_test.go
+++ b/bindings/identity_test.go
@@ -29,34 +29,21 @@ func TestIdentity_JSON(t *testing.T) {
 	dhpk := dh.GeneratePrivateKey(64, grp, rng)
 	dhpkJson, _ := dhpk.MarshalJSON()
 	op := make([]byte, 64)
+	e2eGrp, _ := getGroup().MarshalJSON()
 	_, _ = rng.Read(op)
 	identity := ReceptionIdentity{
 		ID:            uid.Marshal(),
 		RSAPrivatePem: rsa.CreatePrivateKeyPem(pk),
 		Salt:          salt,
 		DHKeyPrivate:  dhpkJson,
+		E2eGrp:        e2eGrp,
 	}
+
 	im, _ := json.Marshal(identity)
 	t.Log("Marshalled ReceptionIdentity object")
 	t.Log(string(im))
 }
 
-func TestFacts_JSON(t *testing.T) {
-	fl := []Fact{
-		{
-			Fact: "Zezima",
-			Type: 0,
-		},
-		{
-			Fact: "Zezima@osrs.org",
-			Type: 2,
-		},
-	}
-	flm, _ := json.Marshal(fl)
-	t.Log("Marshalled []Fact")
-	t.Log(string(flm))
-}
-
 func getGroup() *cyclic.Group {
 	return cyclic.NewGroup(
 		large.NewIntFromString("E2EE983D031DC1DB6F1A7A67DF0E9A8E5561DB8E8D4941"+
diff --git a/bindings/logging.go b/bindings/logging.go
index 6783d4b546f293862863110a85b16791dbf88baa..a4b1df6ae671f808ce5594f5ab1ae0e204d81cfc 100644
--- a/bindings/logging.go
+++ b/bindings/logging.go
@@ -10,7 +10,6 @@
 package bindings
 
 import (
-	"fmt"
 	"log"
 
 	"github.com/pkg/errors"
@@ -34,7 +33,7 @@ import (
 // The default log level without updates is INFO.
 func LogLevel(level int) error {
 	if level < 0 || level > 6 {
-		return errors.New(fmt.Sprintf("log level is not valid: log level: %d", level))
+		return errors.Errorf("log level is not valid: log level: %d", level)
 	}
 
 	threshold := jww.Threshold(level)
diff --git a/bindings/notifications.go b/bindings/notifications.go
new file mode 100644
index 0000000000000000000000000000000000000000..bbc79ca79c028ceac33a5ba1e972119f904aa4ba
--- /dev/null
+++ b/bindings/notifications.go
@@ -0,0 +1,97 @@
+////////////////////////////////////////////////////////////////////////////////
+// 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
+
+// FIXME: This is the old NotificationsForMe code that needs to be fixed
+/*
+type NotificationForMeReport struct {
+	ForMe  bool
+	Type   string
+	Source []byte
+}
+
+type ManyNotificationForMeReport struct {
+	Many []*NotificationForMeReport
+}
+
+// NotificationsForMe Check if a notification received is for me
+// It returns a NotificationForMeReport which contains a ForMe bool stating if it is for the caller,
+// a Type, and a source. These are as follows:
+//	TYPE       	SOURCE				DESCRIPTION
+// 	"default"	recipient user ID	A message with no association
+//	"request"	sender user ID		A channel request has been received
+//	"reset"	    sender user ID		A channel reset has been received
+//	"confirm"	sender user ID		A channel request has been accepted
+//	"silent"	sender user ID		A message which should not be notified on
+//	"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
+	if err := json.Unmarshal([]byte(preimages), &preimageList); err != nil {
+		return nil, errors.WithMessagef(err, "Failed to unmarshal the " +
+			"preimages list, cannot check if notification is for me")
+	}
+
+	list, err := notifications.DecodeNotificationsCSV(notifCSV)
+	if err != nil {
+		return nil, err
+	}
+
+	notifList := make([]*NotificationForMeReport, len(list))
+
+	for i, notifData := range list {
+		notifList[i] = &NotificationForMeReport{
+			ForMe:  false,
+			Type:   "",
+			Source: nil,
+		}
+		// check if any preimages match with the passed in data
+		for _, preimage := range preimageList {
+			if fingerprint.CheckIdentityFpFromMessageHash(notifData.IdentityFP, notifData.MessageHash, preimage.Data) {
+				notifList[i] = &NotificationForMeReport{
+					ForMe:  true,
+					Type:   preimage.Type,
+					Source: preimage.Source,
+				}
+				break
+			}
+		}
+	}
+
+	return &ManyNotificationForMeReport{notifList}, nil
+}*/
+
+// RegisterForNotifications allows a client to register for push notifications.
+// The token is a firebase messaging token.
+//
+// Parameters:
+//  - e2eId - ID of the E2E object in the E2E tracker
+func RegisterForNotifications(e2eId int, token string) error {
+	user, err := e2eTrackerSingleton.get(e2eId)
+	if err != nil {
+		return err
+	}
+
+	return user.api.RegisterForNotifications(token)
+}
+
+// UnregisterForNotifications turns off notifications for this client.
+//
+// Parameters:
+//  - e2eId - ID of the E2E object in the E2E tracker
+func UnregisterForNotifications(e2eId int) error {
+	user, err := e2eTrackerSingleton.get(e2eId)
+	if err != nil {
+		return err
+	}
+
+	return user.api.UnregisterForNotifications()
+}
diff --git a/bindings/params.go b/bindings/params.go
index 2b0cdd18c82d16c2a7a3f0b5570532d26bf87b23..151811be715db3ce2d612173b6d2b45bcfd84417 100644
--- a/bindings/params.go
+++ b/bindings/params.go
@@ -66,8 +66,8 @@ func GetDefaultSingleUseParams() []byte {
 }
 
 // GetDefaultE2eFileTransferParams returns a JSON serialized object with all the
-// e2e file transfer parameters and their default values. Call this function and modify
-// the JSON to change single use settings.
+// E2E file transfer parameters and their default values. Call this function and
+// modify the JSON to change single use settings.
 func GetDefaultE2eFileTransferParams() []byte {
 	defaultParams := e2eFileTransfer.DefaultParams()
 	data, err := defaultParams.MarshalJSON()
diff --git a/bindings/restlike.go b/bindings/restlike.go
index e3c87e1d8279f60833ae91a8e49f32597fcf8352..2fe52c8aa5df83a31f5f49be27e16c2d384705f0 100644
--- a/bindings/restlike.go
+++ b/bindings/restlike.go
@@ -107,7 +107,7 @@ func RestlikeRequest(
 //
 // Returns:
 //  - []byte - JSON marshalled RestlikeMessage
-func RestlikeRequestAuth(cmixId int, authConnectionID int, request,
+func RestlikeRequestAuth(cmixId, authConnectionID int, request,
 	e2eParamsJSON []byte) ([]byte, error) {
 	if len(e2eParamsJSON) == 0 {
 		jww.WARN.Printf("restlike params unspecified, using defaults")
diff --git a/bindings/single.go b/bindings/single.go
index bb1efe57b0821fb1f1445c0fbf06ae7ce8ec666a..a3bacbce28c6db129ae233ea0c684ad9b90a9817 100644
--- a/bindings/single.go
+++ b/bindings/single.go
@@ -62,14 +62,14 @@ func TransmitSingleUse(e2eID int, recipient []byte, tag string, payload,
 	}
 	sr := SingleUseSendReport{
 		EphID:       eid.EphId.Int64(),
-		ReceptionID: eid.Source.Marshal(),
+		ReceptionID: eid.Source,
 		RoundsList:  makeRoundsList(rids...),
 	}
 	return json.Marshal(sr)
 }
 
-// Listen starts a single-use listener on a given tag using the passed in e2e object
-// and SingleUseCallback func.
+// Listen starts a single-use listener on a given tag using the passed in E2e
+// object and SingleUseCallback func.
 //
 // Parameters:
 //  - e2eID - ID of the e2e object in the tracker
@@ -85,11 +85,11 @@ func Listen(e2eID int, tag string, cb SingleUseCallback) (Stopper, error) {
 	}
 
 	suListener := singleUseListener{scb: cb}
-	dhpk, err := e2eCl.api.GetReceptionIdentity().GetDHKeyPrivate()
+	dhPk, err := e2eCl.api.GetReceptionIdentity().GetDHKeyPrivate()
 	if err != nil {
 		return nil, err
 	}
-	l := single.Listen(tag, e2eCl.api.GetReceptionIdentity().ID, dhpk,
+	l := single.Listen(tag, e2eCl.api.GetReceptionIdentity().ID, dhPk,
 		e2eCl.api.GetCmix(), e2eCl.api.GetStorage().GetE2EGroup(), suListener)
 	return &stopper{l: l}, nil
 }
@@ -102,12 +102,12 @@ func Listen(e2eID int, tag string, cb SingleUseCallback) (Stopper, error) {
 // JSON example:
 //  {
 //   "Rounds":[1,5,9],
-//   "EphID":{"EphId":[0,0,0,0,0,0,3,89],
-//   "Source":"emV6aW1hAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD"}
+//   "EphID":1655533,
+//   "ReceptionID":"emV6aW1hAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD"}
 //  }
 type SingleUseSendReport struct {
 	RoundsList
-	ReceptionID []byte
+	ReceptionID *id.ID
 	EphID       int64
 }
 
@@ -119,14 +119,14 @@ type SingleUseSendReport struct {
 //  {
 //   "Rounds":[1,5,9],
 //   "Payload":"rSuPD35ELWwm5KTR9ViKIz/r1YGRgXIl5792SF8o8piZzN6sT4Liq4rUU/nfOPvQEjbfWNh/NYxdJ72VctDnWw==",
-//   "ReceptionID":{"EphId":[0,0,0,0,0,0,3,89],
-//   "Source":"emV6aW1hAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD"},
-//   "Err":null
+//   "EphID":1655533,
+//   "ReceptionID":"emV6aW1hAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD"},
+//   "Err":"",
 //  }
 type SingleUseResponseReport struct {
 	RoundsList
 	Payload     []byte
-	ReceptionID []byte
+	ReceptionID *id.ID
 	EphID       int64
 	Err         error
 }
@@ -139,22 +139,23 @@ type SingleUseResponseReport struct {
 //   "Rounds":[1,5,9],
 //   "Payload":"rSuPD35ELWwm5KTR9ViKIz/r1YGRgXIl5792SF8o8piZzN6sT4Liq4rUU/nfOPvQEjbfWNh/NYxdJ72VctDnWw==",
 //   "Partner":"emV6aW1hAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD",
-//   "EphID":{"EphId":[0,0,0,0,0,0,3,89],
-//   "Source":"emV6aW1hAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD"}
+//   "EphID":1655533,
+//   "ReceptionID":"emV6aW1hAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD"}
 //  }
 type SingleUseCallbackReport struct {
 	RoundsList
 	Payload     []byte
 	Partner     *id.ID
 	EphID       int64
-	ReceptionID []byte
+	ReceptionID *id.ID
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // Function Types                                                             //
 ////////////////////////////////////////////////////////////////////////////////
 
-// Stopper is a public interface returned by Listen, allowing users to stop the registered listener.
+// Stopper is a public interface returned by Listen, allowing users to stop the
+// registered listener.
 type Stopper interface {
 	Stop()
 }
@@ -164,8 +165,8 @@ type Stopper interface {
 //
 // Parameters:
 //  - callbackReport - the JSON marshalled bytes of the SingleUseCallbackReport
-//    object, which can be passed into WaitForRoundResult to see if the send
-//    succeeded.
+//    object, which can be passed into Cmix.WaitForRoundResult to see if the
+//    send operation succeeded.
 type SingleUseCallback interface {
 	Callback(callbackReport []byte, err error)
 }
@@ -175,8 +176,8 @@ type SingleUseCallback interface {
 //
 // Parameters:
 //  - callbackReport - the JSON marshalled bytes of the SingleUseResponseReport
-//    object, which can be passed into WaitForRoundResult to see if the send
-//    succeeded.
+//    object, which can be passed into Cmix.WaitForRoundResult to see if the
+//    send operation succeeded.
 type SingleUseResponse interface {
 	Callback(responseReport []byte, err error)
 }
@@ -209,7 +210,7 @@ func (sl singleUseListener) Callback(
 		RoundsList:  makeRoundsList(rids...),
 		Partner:     req.GetPartner(),
 		EphID:       eid.EphId.Int64(),
-		ReceptionID: eid.Source.Marshal(),
+		ReceptionID: eid.Source,
 	}
 
 	sl.scb.Callback(json.Marshal(scr))
@@ -245,7 +246,7 @@ func (sr singleUseResponse) Callback(payload []byte,
 	}
 	sendReport := SingleUseResponseReport{
 		RoundsList:  makeRoundsList(rids...),
-		ReceptionID: receptionID.Source.Marshal(),
+		ReceptionID: receptionID.Source,
 		EphID:       receptionID.EphId.Int64(),
 		Payload:     payload,
 		Err:         err,
diff --git a/bindings/single_test.go b/bindings/single_test.go
index a262a40738afe4ce73fc565ddaf259663f9d3a2e..c5424809add94e375336da1badd3a1a00696c36e 100644
--- a/bindings/single_test.go
+++ b/bindings/single_test.go
@@ -36,7 +36,7 @@ func TestSingleUseJsonMarshals(t *testing.T) {
 	sendReport := SingleUseSendReport{
 		RoundsList:  rl,
 		EphID:       ephId.EphId.Int64(),
-		ReceptionID: ephId.Source.Marshal(),
+		ReceptionID: ephId.Source,
 	}
 	srm, err := json.Marshal(sendReport)
 	if err != nil {
@@ -48,7 +48,7 @@ func TestSingleUseJsonMarshals(t *testing.T) {
 	responseReport := SingleUseResponseReport{
 		RoundsList:  rl,
 		Payload:     payload,
-		ReceptionID: ephId.Source.Marshal(),
+		ReceptionID: ephId.Source,
 		EphID:       ephId.EphId.Int64(),
 		Err:         nil,
 	}
@@ -64,7 +64,7 @@ func TestSingleUseJsonMarshals(t *testing.T) {
 		Payload:     payload,
 		Partner:     rid,
 		EphID:       ephId.EphId.Int64(),
-		ReceptionID: ephId.Source.Marshal(),
+		ReceptionID: ephId.Source,
 	}
 	crm, err := json.Marshal(callbackReport)
 	if err != nil {
diff --git a/bindings/ud.go b/bindings/ud.go
index 6ce8f8b3af073057e210675ff3203ef5ba364746..3a3f21a0d43a4905ba5645d40cd38f4e82ed4854 100644
--- a/bindings/ud.go
+++ b/bindings/ud.go
@@ -105,34 +105,77 @@ type UdNetworkStatus interface {
 // Manager functions                                                          //
 ////////////////////////////////////////////////////////////////////////////////
 
-// NewOrLoadUd loads an existing Manager from storage or creates a
-// new one if there is no extant storage information. Parameters need be provided
-// to specify how to connect to the User Discovery service. These parameters may be used
-// to contact either the UD server hosted by the xx network team or a custom
-// third-party operated server. For the former, all the information may be pulled from the
-// NDF using the bindings.
+// IsRegisteredWithUD is a function which checks the internal state
+// files to determine if a user has registered with UD in the past.
+//
+// Parameters:
+//  - e2eID -  REQUIRED. The tracked e2e object ID. This can be retrieved using [E2e.GetID].
+//
+// Returns:
+//   - bool - A boolean representing true if the user has been registered with UD already
+//            or false if it has not been registered already.
+//  - error - An error should only be returned if the internal tracker failed to retrieve an
+//            E2e object given the e2eId. If an error was returned, the registration state check
+//            was not performed properly, and the boolean returned should be ignored.
+func IsRegisteredWithUD(e2eId int) (bool, error) {
+
+	// Get user from singleton
+	user, err := e2eTrackerSingleton.get(e2eId)
+	if err != nil {
+		return false, err
+	}
+
+	return ud.IsRegistered(user.api.GetStorage().GetKV()), nil
+}
+
+// NewOrLoadUd loads an existing UserDiscovery from storage or creates a new
+// UserDiscovery if there is no storage data. Regardless of storage state,
+// the UserDiscovery object returned will be registered with the
+// User Discovery service. If the user is not already registered, a call
+// to register will occur internally. If the user is already registered,
+// this call will simply load state and return to you a UserDiscovery object.
+// Some parameters are required for registering with the service, but are not required
+// if the user is already registered. These will be noted in the parameters section as
+// "SEMI-REQUIRED".
+//
+// Certain parameters are required every call to this function. These parameters are listed below
+// as "REQUIRED". For example, parameters need be provided to specify how to connect to the
+// User Discovery service. These parameters specifically may be used to contact either the UD
+// server hosted by the xx network team or a custom third-party operated server. For the former,
+// all the information may be fetched from the NDF using the bindings. These fetch
+// methods are detailed in the parameters section.
 //
 // Params
-//  - e2eID - e2e object ID in the tracker
-//  - follower - network follower func wrapped in UdNetworkStatus
-//  - username - the username the user wants to register with UD.
-//    If the user is already registered, this field may be blank
-//  - networkValidationSig is a signature provided by the network (i.e. the client registrar).
-//    This may be nil, however UD may return an error in some cases (e.g. in a production level
-//    environment).
-//  - cert is the TLS certificate for the UD server this call will connect with.
-//    You may use the UD server run by the xx network team by using E2e.GetUdCertFromNdf.
-//  - contactFile is the data within a marshalled contact.Contact. This represents the
+//  - e2eID -  REQUIRED. The tracked e2e object ID. This is returned by [E2e.GetID].
+//  - follower - REQUIRED. Network follower function. This will check if the network
+//    follower is running.
+//  - username - SEMI-REQUIRED. The username the user wants to register with UD.
+//    If the user is already registered, this field may be blank. If the user is not
+//    already registered, these field must be populated with a username that meets the
+//    requirements of the UD service. For example, in the xx network's UD service,
+//    the username must not be registered by another user.
+//  - registrationValidationSignature - SEMI-REQUIRED. A signature provided by the xx network
+//    (i.e. the client registrar). If the user is not already registered, this field is required
+//    in order to register with the xx network. This may be nil if the user is already registered
+//    or connecting to a third-party UD service unassociated with the xx network.
+//  - cert - REQUIRED. The TLS certificate for the UD server this call will connect with.
+//    If this is nil, you may not contact the UD server hosted by the xx network.
+//    Third-party services may vary.
+//    You may use the UD server run by the xx network team by using [E2e.GetUdCertFromNdf].
+//  - contactFile - REQUIRED. The data within a marshalled [contact.Contact]. This represents the
 //    contact file of the server this call will connect with.
-//    You may use the UD server run by the xx network team by using E2e.GetUdContactFromNdf.
-//  - address is the IP address of the UD server this call will connect with.
-//    You may use the UD server run by the xx network team by using E2e.GetUdAddressFromNdf.
+//    If this is nil, you may not contact the UD server hosted by the xx network.
+//    Third-party services may vary.
+//    You may use the UD server run by the xx network team by using [E2e.GetUdContactFromNdf].
+//  - address - REQUIRED. The IP address of the UD server this call will connect with.
+//    You may use the UD server run by the xx network team by using [E2e.GetUdAddressFromNdf].
+//    If this is nil, you may not contact the UD server hosted by the xx network.
+//    Third-party services may vary.
 //
 // Returns
 //  - A Manager object which is registered to the specified UD service.
-func NewOrLoadUd(e2eID int, follower UdNetworkStatus,
-	username string, registrationValidationSignature,
-	cert, contactFile []byte, address string) (
+func NewOrLoadUd(e2eID int, follower UdNetworkStatus, username string,
+	registrationValidationSignature, cert, contactFile []byte, address string) (
 	*UserDiscovery, error) {
 
 	// Get user from singleton
@@ -165,17 +208,24 @@ func NewOrLoadUd(e2eID int, follower UdNetworkStatus,
 // Parameters:
 //  - e2eID - e2e object ID in the tracker
 //  - follower - network follower func wrapped in UdNetworkStatus
-//  - emailFactJson - nullable JSON marshalled email fact.Fact
-//  - phoneFactJson - nullable JSON marshalled phone fact.Fact
-//  - cert is the TLS certificate for the UD server this call will connect with.
-//    You may use the UD server run by the xx network team by using E2e.GetUdCertFromNdf.
-//  - contactFile is the data within a marshalled contact.Contact. This represents the
-//    contact file of the server this call will connect with.
-//    You may use the UD server run by the xx network team by using E2e.GetUdContactFromNdf.
-//  - address is the IP address of the UD server this call will connect with.
-//    You may use the UD server run by the xx network team by using E2e.GetUdAddressFromNdf.
-func NewUdManagerFromBackup(e2eID int, follower UdNetworkStatus, emailFactJson,
-	phoneFactJson []byte, cert, contactFile []byte, address string) (*UserDiscovery, error) {
+//  - username - The username this user registered with initially. This should
+//               not be nullable, and be JSON marshalled as retrieved from
+//               UserDiscovery.GetFacts().
+//  - emailFactJson - nullable JSON marshalled email [fact.Fact]
+//  - phoneFactJson - nullable JSON marshalled phone [fact.Fact]
+//  - cert - the TLS certificate for the UD server this call will connect with.
+//    You may use the UD server run by the xx network team by using
+//    E2e.GetUdCertFromNdf.
+//  - contactFile - the data within a marshalled contact.Contact. This
+//    represents the contact file of the server this call will connect with. You
+//    may use the UD server run by the xx network team by using
+//    E2e.GetUdContactFromNdf.
+//  - address - the IP address of the UD server this call will connect with. You
+//    may use the UD server run by the xx network team by using
+//    E2e.GetUdAddressFromNdf.
+func NewUdManagerFromBackup(e2eID int, follower UdNetworkStatus,
+	usernameJson, emailFactJson, phoneFactJson,
+	cert, contactFile []byte, address string) (*UserDiscovery, error) {
 
 	// Get user from singleton
 	user, err := e2eTrackerSingleton.get(e2eID)
@@ -183,7 +233,9 @@ func NewUdManagerFromBackup(e2eID int, follower UdNetworkStatus, emailFactJson,
 		return nil, err
 	}
 
-	var email, phone fact.Fact
+	var email, phone, username fact.Fact
+
+	// Parse email if non-nil
 	if emailFactJson != nil {
 		err = json.Unmarshal(emailFactJson, &email)
 		if err != nil {
@@ -191,6 +243,7 @@ func NewUdManagerFromBackup(e2eID int, follower UdNetworkStatus, emailFactJson,
 		}
 	}
 
+	// Parse phone if non-nil
 	if phoneFactJson != nil {
 		err = json.Unmarshal(phoneFactJson, &phone)
 		if err != nil {
@@ -198,13 +251,19 @@ func NewUdManagerFromBackup(e2eID int, follower UdNetworkStatus, emailFactJson,
 		}
 	}
 
+	// Parse username
+	err = json.Unmarshal(usernameJson, &username)
+	if err != nil {
+		return nil, err
+	}
+
 	UdNetworkStatusFn := func() xxdk.Status {
 		return xxdk.Status(follower.UdNetworkStatus())
 	}
 
 	u, err := ud.NewManagerFromBackup(
 		user.api, user.api.GetComms(), UdNetworkStatusFn,
-		email, phone,
+		username, email, phone,
 		cert, contactFile, address)
 	if err != nil {
 		return nil, err
@@ -213,7 +272,7 @@ func NewUdManagerFromBackup(e2eID int, follower UdNetworkStatus, emailFactJson,
 	return udTrackerSingleton.make(u), nil
 }
 
-// GetFacts returns a JSON marshalled list of fact.Fact objects that exist
+// GetFacts returns a JSON marshalled list of [fact.Fact] objects that exist
 // within the Store's registeredFacts map.
 func (ud *UserDiscovery) GetFacts() []byte {
 	jsonData, err := json.Marshal(ud.api.GetFacts())
@@ -229,9 +288,9 @@ func (ud *UserDiscovery) GetContact() ([]byte, error) {
 	return ud.api.GetContact().Marshal(), nil
 }
 
-// ConfirmFact confirms a fact first registered via AddFact. The confirmation ID
-// comes from AddFact while the code will come over the associated
-// communications system.
+// ConfirmFact confirms a fact first registered via SendRegisterFact. The
+// confirmation ID comes from SendRegisterFact while the code will come over the
+// associated communications system.
 func (ud *UserDiscovery) ConfirmFact(confirmationID, code string) error {
 	return ud.api.ConfirmFact(confirmationID, code)
 }
@@ -246,7 +305,7 @@ func (ud *UserDiscovery) ConfirmFact(confirmationID, code string) error {
 // along with the code to finalize the fact.
 //
 // Parameters:
-//  - factJson - a JSON marshalled fact.Fact
+//  - factJson - a JSON marshalled [fact.Fact]
 func (ud *UserDiscovery) SendRegisterFact(factJson []byte) (string, error) {
 	var f fact.Fact
 	err := json.Unmarshal(factJson, &f)
@@ -262,7 +321,7 @@ func (ud *UserDiscovery) SendRegisterFact(factJson []byte) (string, error) {
 // be associated with this user.
 //
 // Parameters:
-//  - factJson - a JSON marshalled fact.Fact
+//  - factJson - a JSON marshalled [fact.Fact]
 func (ud *UserDiscovery) PermanentDeleteAccount(factJson []byte) error {
 	var f fact.Fact
 	err := json.Unmarshal(factJson, &f)
@@ -277,7 +336,7 @@ func (ud *UserDiscovery) PermanentDeleteAccount(factJson []byte) error {
 // passed in is not UD service does not associate this fact with this user.
 //
 // Parameters:
-//  - factJson - a JSON marshalled fact.Fact
+//  - factJson - a JSON marshalled [fact.Fact]
 func (ud *UserDiscovery) RemoveFact(factJson []byte) error {
 	var f fact.Fact
 	err := json.Unmarshal(factJson, &f)
@@ -309,13 +368,14 @@ type UdLookupCallback interface {
 // Parameters:
 //  - e2eID - e2e object ID in the tracker
 //  - udContact - the marshalled bytes of the contact.Contact object
-//  - lookupId - the marshalled bytes of the id.ID object for the user
-//    that LookupUD will look up.
+//  - lookupId - the marshalled bytes of the id.ID object for the user that
+//    LookupUD will look up.
 //  - singleRequestParams - the JSON marshalled bytes of single.RequestParams
 //
 // Returns:
 //  - []byte - the JSON marshalled bytes of the SingleUseSendReport object,
-//    which can be passed into WaitForRoundResult to see if the send succeeded.
+//    which can be passed into Cmix.WaitForRoundResult to see if the send
+//    succeeded.
 func LookupUD(e2eID int, udContact []byte, cb UdLookupCallback,
 	lookupId []byte, singleRequestParamsJSON []byte) ([]byte, error) {
 
@@ -352,7 +412,7 @@ func LookupUD(e2eID int, udContact []byte, cb UdLookupCallback,
 
 	sr := SingleUseSendReport{
 		EphID:       eid.EphId.Int64(),
-		ReceptionID: eid.Source.Marshal(),
+		ReceptionID: eid.Source,
 		RoundsList:  makeRoundsList(rids...),
 	}
 
@@ -369,8 +429,15 @@ func LookupUD(e2eID int, udContact []byte, cb UdLookupCallback,
 //
 // Parameters:
 //  - contactListJSON - the JSON marshalled bytes of []contact.Contact, or nil
-//    if an error occurs
-//  - err - any errors that occurred in the search
+//    if an error occurs.
+//
+//   JSON Example:
+//   {
+//  	"<xxc(2)F8dL9EC6gy+RMJuk3R+Au6eGExo02Wfio5cacjBcJRwDEgB7Ugdw/BAr6RkCABkWAFV1c2VybmFtZTA7c4LzV05sG+DMt+rFB0NIJg==xxc>",
+//  	"<xxc(2)eMhAi/pYkW5jCmvKE5ZaTglQb+fTo1D8NxVitr5CCFADEgB7Ugdw/BAr6RoCABkWAFV1c2VybmFtZTE7fElAa7z3IcrYrrkwNjMS2w==xxc>",
+//  	"<xxc(2)d7RJTu61Vy1lDThDMn8rYIiKSe1uXA/RCvvcIhq5Yg4DEgB7Ugdw/BAr6RsCABkWAFV1c2VybmFtZTI7N3XWrxIUpR29atpFMkcR6A==xxc>"
+//	}
+//  - err - any errors that occurred in the search.
 type UdSearchCallback interface {
 	Callback(contactListJSON []byte, err error)
 }
@@ -385,14 +452,15 @@ type UdSearchCallback interface {
 //  - e2eID - e2e object ID in the tracker
 //  - udContact - the marshalled bytes of the contact.Contact for the user
 //    discovery server
-//  - factListJSON - the JSON marshalled bytes of fact.FactList
+//  - factListJSON - the JSON marshalled bytes of [fact.FactList]
 //  - singleRequestParams - the JSON marshalled bytes of single.RequestParams
 //
 // Returns:
 //  - []byte - the JSON marshalled bytes of the SingleUseSendReport object,
-//    which can be passed into WaitForRoundResult to see if the send succeeded.
+//    which can be passed into Cmix.WaitForRoundResult to see if the send
+//    operation succeeded.
 func SearchUD(e2eID int, udContact []byte, cb UdSearchCallback,
-	factListJSON []byte, singleRequestParamsJSON []byte) ([]byte, error) {
+	factListJSON, singleRequestParamsJSON []byte) ([]byte, error) {
 
 	// Get user from singleton
 	user, err := e2eTrackerSingleton.get(e2eID)
@@ -418,7 +486,20 @@ func SearchUD(e2eID int, udContact []byte, cb UdSearchCallback,
 	}
 
 	callback := func(contactList []contact.Contact, err error) {
-		contactListJSON, err2 := json.Marshal(contactList)
+		marshaledContactList := make([][]byte, 0)
+		// fixme: it may be wiser to change this callback interface
+		//   to simply do the work below when parsing the response from UD.
+		//   that would change ud/search.go in two places:
+		//    - searchCallback
+		//    - parseContacts
+		//  I avoid doing that as it changes interfaces w/o approval
+		for i := range contactList {
+			con := contactList[i]
+			marshaledContactList = append(
+				marshaledContactList, con.Marshal())
+		}
+
+		contactListJSON, err2 := json.Marshal(marshaledContactList)
 		if err2 != nil {
 			jww.FATAL.Panicf(
 				"Failed to marshal list of contact.Contact: %+v", err2)
@@ -434,7 +515,7 @@ func SearchUD(e2eID int, udContact []byte, cb UdSearchCallback,
 
 	sr := SingleUseSendReport{
 		EphID:       eid.EphId.Int64(),
-		ReceptionID: eid.Source.Marshal(),
+		ReceptionID: eid.Source,
 		RoundsList:  makeRoundsList(rids...),
 	}
 
diff --git a/cmd/flags.go b/cmd/flags.go
index 93b5d505d77f4846f80d1cda6b32bfa29a0ad3cf..edd40259a56eb227c267811c125ffab746ec0f67 100644
--- a/cmd/flags.go
+++ b/cmd/flags.go
@@ -80,7 +80,9 @@ const (
 	// Misc
 	sendIdFlag       = "sendid"
 	profileCpuFlag   = "profile-cpu"
+	profileMemFlag   = "profile-mem"
 	userIdPrefixFlag = "userid-prefix"
+	legacyFlag       = "legacy"
 
 	///////////////// Broadcast subcommand flags //////////////////////////////
 	broadcastNameFlag        = "name"
diff --git a/cmd/init.go b/cmd/init.go
index e78ef22bb78387d64210e9d7365bc76fa765fd94..00294f2c4a79d6bd38ce9c330df5b86466a608c3 100644
--- a/cmd/init.go
+++ b/cmd/init.go
@@ -45,16 +45,27 @@ var initCmd = &cobra.Command{
 			jww.FATAL.Panicf("%+v", err)
 		}
 
-		identity, err := xxdk.MakeReceptionIdentity(net)
+		// Generate identity
+		var identity xxdk.ReceptionIdentity
+		if viper.GetBool(legacyFlag) {
+			identity, err = xxdk.MakeLegacyReceptionIdentity(net)
+		} else {
+			identity, err = xxdk.MakeReceptionIdentity(net)
+
+		}
+
+		// Panic if conditional branch fails
 		if err != nil {
 			jww.FATAL.Panicf("%+v", err)
 		}
 
+		// Store identity
 		err = xxdk.StoreReceptionIdentity(identityStorageKey, identity, net)
 		if err != nil {
 			jww.FATAL.Panicf("%+v", err)
 		}
 
+		// Write contact to file
 		jww.INFO.Printf("User: %s", identity.ID)
 		writeContact(identity.GetContact())
 
@@ -68,6 +79,11 @@ func init() {
 		"Desired prefix of userID to brute force when running init command. Prepend (?i) for case-insensitive. Only Base64 characters are valid.")
 	bindFlagHelper(userIdPrefixFlag, initCmd)
 
+	initCmd.Flags().BoolP(legacyFlag, "", false,
+		"Generates a legacy identity if set. "+
+			"If this flag is absent, a standard identity will be generated.")
+	bindFlagHelper(legacyFlag, initCmd)
+
 	rootCmd.AddCommand(initCmd)
 }
 
diff --git a/cmd/root.go b/cmd/root.go
index 837b68e1af7d13f8476844c3bb0f525be4bcbd63..3766d7cb53360d19598066788aaa42726c10aa2b 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -17,7 +17,9 @@ import (
 	"io/ioutil"
 	"log"
 	"os"
-	"runtime/pprof"
+
+	"github.com/pkg/profile"
+
 	"strconv"
 	"strings"
 	"sync"
@@ -58,13 +60,17 @@ var rootCmd = &cobra.Command{
 	Short: "Runs a client for cMix anonymous communication platform",
 	Args:  cobra.NoArgs,
 	Run: func(cmd *cobra.Command, args []string) {
-		profileOut := viper.GetString(profileCpuFlag)
-		if profileOut != "" {
-			f, err := os.Create(profileOut)
-			if err != nil {
-				jww.FATAL.Panicf("%+v", err)
-			}
-			pprof.StartCPUProfile(f)
+		cpuProfileOut := viper.GetString(profileCpuFlag)
+		if cpuProfileOut != "" {
+			defer profile.Start(profile.CPUProfile,
+				profile.ProfilePath(cpuProfileOut),
+				profile.NoShutdownHook).Stop()
+		}
+		memProfileOut := viper.GetString(profileMemFlag)
+		if memProfileOut != "" {
+			defer profile.Start(profile.MemProfile,
+				profile.ProfilePath(memProfileOut),
+				profile.NoShutdownHook).Stop()
 		}
 
 		cmixParams, e2eParams := initParams()
@@ -382,9 +388,6 @@ var rootCmd = &cobra.Command{
 				"Failed to cleanly close threads: %+v\n",
 				err)
 		}
-		if profileOut != "" {
-			pprof.StopCPUProfile()
-		}
 		jww.INFO.Printf("Client exiting!")
 	},
 }
@@ -1101,6 +1104,10 @@ func init() {
 		"Enable cpu profiling to this file")
 	viper.BindPFlag(profileCpuFlag, rootCmd.Flags().Lookup(profileCpuFlag))
 
+	rootCmd.Flags().String(profileMemFlag, "",
+		"Enable memory profiling to this file")
+	viper.BindPFlag(profileMemFlag, rootCmd.Flags().Lookup(profileMemFlag))
+
 	// Proto user flags
 	rootCmd.Flags().String(protoUserPathFlag, "",
 		"Path to proto user JSON file containing cryptographic primitives "+
diff --git a/cmix/client.go b/cmix/client.go
index d164b4d2a60620176fe7fee12c5a26e95e351c99..d2dcee014faa90f9a1328db5bbd6ac1232703100 100644
--- a/cmix/client.go
+++ b/cmix/client.go
@@ -251,6 +251,9 @@ func (c *client) Follow(report ClientErrorReport) (stoppable.Stoppable, error) {
 	// Start the processes for the identity handler
 	multi.Add(c.Tracker.StartProcesses())
 
+	//Start the critical processing thread
+	multi.Add(c.crit.startProcessies())
+
 	return multi, nil
 }
 
diff --git a/cmix/critical.go b/cmix/critical.go
index 03ebfc7b56429b0fd0e8f588fa5ca2fe2e809491..719e6d9f0eff0774bdf083f01f07034a58ab6127 100644
--- a/cmix/critical.go
+++ b/cmix/critical.go
@@ -1,6 +1,8 @@
 package cmix
 
 import (
+	"time"
+
 	jww "github.com/spf13/jwalterweatherman"
 	"gitlab.com/elixxir/client/cmix/health"
 	"gitlab.com/elixxir/client/stoppable"
@@ -10,7 +12,6 @@ import (
 	"gitlab.com/elixxir/primitives/states"
 	"gitlab.com/xx_network/primitives/id"
 	"gitlab.com/xx_network/primitives/id/ephemeral"
-	"time"
 )
 
 const criticalRawMessagesKey = "RawCriticalMessages"
@@ -58,6 +59,12 @@ func newCritical(kv *versioned.KV, hm health.Monitor,
 	return c
 }
 
+func (c *critical) startProcessies() *stoppable.Single {
+	stop := stoppable.NewSingle("criticalStopper")
+	go c.runCriticalMessages(stop)
+	return stop
+}
+
 func (c *critical) runCriticalMessages(stop *stoppable.Single) {
 	for {
 		select {
diff --git a/cmix/identity/tracker.go b/cmix/identity/tracker.go
index 961aac77627f0f5ef15ed35b839c2c5eaafa9b4b..8d9f8a6d2a168d27147f9fe280f2d777e0b9935c 100644
--- a/cmix/identity/tracker.go
+++ b/cmix/identity/tracker.go
@@ -55,7 +55,7 @@ type Tracker interface {
 }
 
 type manager struct {
-	tracked        []TrackedID
+	tracked        []*TrackedID
 	ephemeral      *receptionID.Store
 	session        storage.Session
 	newIdentity    chan TrackedID
@@ -76,7 +76,7 @@ type TrackedID struct {
 func NewOrLoadTracker(session storage.Session, addrSpace address.Space) *manager {
 	// Initialization
 	t := &manager{
-		tracked:        make([]TrackedID, 0),
+		tracked:        make([]*TrackedID, 0),
 		session:        session,
 		newIdentity:    make(chan TrackedID, trackedIDChanSize),
 		deleteIdentity: make(chan *id.ID, deleteIDChanSize),
@@ -92,7 +92,7 @@ func NewOrLoadTracker(session storage.Session, addrSpace address.Space) *manager
 			jww.WARN.Printf("No tracked identities found, creating a new " +
 				"tracked identity from legacy stored timestamp.")
 
-			t.tracked = append(t.tracked, TrackedID{
+			t.tracked = append(t.tracked, &TrackedID{
 				// Make the next generation now so a generation triggers on
 				// first run
 				NextGeneration: netTime.Now(),
@@ -155,7 +155,7 @@ func (t *manager) GetIdentity(get *id.ID) (TrackedID, error) {
 	defer t.mux.Unlock()
 	for i := range t.tracked {
 		if get.Cmp(t.tracked[i].Source) {
-			return t.tracked[i], nil
+			return *t.tracked[i], nil
 		}
 	}
 	return TrackedID{}, errors.Errorf("could not find id %s", get)
@@ -201,7 +201,7 @@ func (t *manager) track(stop *stoppable.Single) {
 			if !isOld {
 				jww.DEBUG.Printf("Tracking new identity %s", newIdentity.Source)
 				// Otherwise, add it to the list and run
-				t.tracked = append(t.tracked, newIdentity)
+				t.tracked = append(t.tracked, &newIdentity)
 			}
 
 			t.save()
@@ -236,7 +236,8 @@ func (t *manager) processIdentities(addressSize uint8) time.Time {
 	nextEvent := netTime.Now().Add(time.Duration(ephemeral.Period))
 
 	// Loop through every tracked ID and see if any operations are needed
-	for i, inQuestion := range t.tracked {
+	for i := range t.tracked {
+		inQuestion := t.tracked[i]
 		// Generate new ephemeral if is time for it
 		if netTime.Now().After(inQuestion.NextGeneration) {
 			nextGeneration := t.generateIdentitiesOverRange(inQuestion, addressSize)
@@ -267,7 +268,7 @@ func (t *manager) processIdentities(addressSize uint8) time.Time {
 
 	// Process any deletions
 	if len(toRemove) > 0 {
-		newTracked := make([]TrackedID, 0, len(t.tracked))
+		newTracked := make([]*TrackedID, 0, len(t.tracked))
 		for i := range t.tracked {
 			if _, remove := toRemove[i]; !remove {
 				newTracked = append(newTracked, t.tracked[i])
@@ -305,7 +306,7 @@ func unmarshalTimestamp(lastTimestampObj *versioned.Object) (time.Time, error) {
 
 // generateIdentitiesOverRange generates and adds all not yet existing ephemeral Ids
 // and returns the timestamp of the next generation for the given TrackedID
-func (t *manager) generateIdentitiesOverRange(inQuestion TrackedID,
+func (t *manager) generateIdentitiesOverRange(inQuestion *TrackedID,
 	addressSize uint8) time.Time {
 	// Ensure that ephemeral IDs will not be generated after the
 	// identity is invalid
@@ -374,7 +375,7 @@ func (t *manager) save() {
 
 	for i := range t.tracked {
 		if t.tracked[i].Persistent {
-			persistent = append(persistent, t.tracked[i])
+			persistent = append(persistent, *t.tracked[i])
 		}
 	}
 
diff --git a/cmix/identity/tracker_test.go b/cmix/identity/tracker_test.go
index d16f64b5526f30e2d9719974ad1f82560b7f6b1f..dc9a895764c996abf1cf947c92139f2b7a23c71e 100644
--- a/cmix/identity/tracker_test.go
+++ b/cmix/identity/tracker_test.go
@@ -67,7 +67,7 @@ func TestManager_processIdentities(t *testing.T) {
 	addrSpace.UpdateAddressSpace(18)
 	session := storage.InitTestingSession(t)
 	m := &manager{
-		tracked:        make([]TrackedID, 0),
+		tracked:        make([]*TrackedID, 0),
 		session:        session,
 		newIdentity:    make(chan TrackedID, trackedIDChanSize),
 		deleteIdentity: make(chan *id.ID, deleteIDChanSize),
@@ -79,7 +79,7 @@ func TestManager_processIdentities(t *testing.T) {
 	// Add some expired test IDs
 	testId := id.NewIdFromUInt(0, id.User, t)
 	validUntil := netTime.Now().Add(time.Minute)
-	m.tracked = append(m.tracked, TrackedID{
+	m.tracked = append(m.tracked, &TrackedID{
 		NextGeneration: netTime.Now(),
 		LastGeneration: time.Time{},
 		Source:         testId,
diff --git a/connect/authenticated.go b/connect/authenticated.go
index 6a311978d78333ec9709151e7528c7da7f55657f..c45489fbc78ab87ab1115ab6da02d2b0fe5167f4 100644
--- a/connect/authenticated.go
+++ b/connect/authenticated.go
@@ -67,7 +67,7 @@ func ConnectWithAuthentication(recipient contact.Contact, user *xxdk.E2e,
 
 	// Build the authenticated connection and return
 	identity := user.GetReceptionIdentity()
-	privKey, err := identity.GetRSAPrivatePem()
+	privKey, err := identity.GetRSAPrivateKey()
 	if err != nil {
 		return nil, err
 	}
diff --git a/dummy/manager.go b/dummy/manager.go
index 4832f3ef505ea37114c2f4d202c82604ddd7eab0..7bc4847134c5b95ebb776f81d04199ad0862878c 100644
--- a/dummy/manager.go
+++ b/dummy/manager.go
@@ -12,7 +12,7 @@ package dummy
 
 import (
 	"github.com/pkg/errors"
-	"gitlab.com/elixxir/client/interfaces"
+	"gitlab.com/elixxir/client/cmix"
 	"gitlab.com/elixxir/client/stoppable"
 	"gitlab.com/elixxir/client/storage"
 	"gitlab.com/elixxir/client/xxdk"
@@ -21,20 +21,25 @@ import (
 	"time"
 )
 
+// Manager related thread handling constants.
 const (
+	// The name of the Manager's stoppable.Stoppable
 	dummyTrafficStoppableName = "DummyTraffic"
-	statusChanLen             = 100
+
+	// The amount of statuses in queue that can be placed
+	// by Manager.SetStatus.
+	statusChanLen = 100
 )
 
-// Thread status.
+// The thread status values.
 const (
-	notStarted uint32 = iota
-	running
-	paused
-	stopped
+	notStarted uint32 = iota // Sending thread has not been started
+	running                  // Sending thread is currently operating
+	paused                   // Sending thread is temporarily halted.
+	stopped                  // Sending thread is halted.
 )
 
-// Error messages.
+// Error messages for Manager.
 const (
 	setStatusErr = "Failed to change status of dummy traffic send thread to %t: channel full"
 )
@@ -56,27 +61,41 @@ type Manager struct {
 	// Pauses/Resumes the dummy send thread when triggered
 	statusChan chan bool
 
-	// Cmix interfaces
-	net            *xxdk.Cmix
-	store          *storage.Session
-	networkManager interfaces.NetworkManager
-	rng            *fastRNG.StreamGenerator
+	// Interfaces
+	net   cmix.Client
+	store storage.Session
+
+	// Generates
+	rng *fastRNG.StreamGenerator
 }
 
-// NewManager creates a new dummy Manager with the specified average send delta
-// and the range used for generating random durations.
-func NewManager(maxNumMessages int, avgSendDelta, randomRange time.Duration,
-	net *xxdk.Cmix, manager interfaces.NetworkManager) *Manager {
-	clientStorage := net.GetStorage()
-	return newManager(maxNumMessages, avgSendDelta, randomRange, net,
-		&clientStorage, manager, net.GetRng())
+// NewManager creates a Manager object and initialises the
+// dummy traffic sending thread. Note that the Manager does not start sending dummy
+// traffic until `True` is passed into Manager.SetStatus. The time duration
+// between each sending operation and the amount of messages sent each interval
+// are randomly generated values with bounds defined by the
+// given parameters below.
+//
+// Params:
+//  - maxNumMessages - the upper bound of the random number of messages sent
+//    each sending cycle.
+//  - avgSendDeltaMS - the average duration, in milliseconds, to wait
+//    between sends.
+//  - randomRangeMS - the upper bound of the interval between sending cycles,
+//    in milliseconds. Sends occur every avgSendDeltaMS +/- a random duration
+//    with an upper bound of randomRangeMS.
+func NewManager(maxNumMessages int,
+	avgSendDelta, randomRange time.Duration,
+	net *xxdk.Cmix) *Manager {
+
+	return newManager(maxNumMessages, avgSendDelta, randomRange, net.GetCmix(),
+		net.GetStorage(), net.GetRng())
 }
 
 // newManager builds a new dummy Manager from fields explicitly passed in. This
-// function is a helper function for NewManager to make it easier to test.
+// function is a helper function for NewManager.
 func newManager(maxNumMessages int, avgSendDelta, randomRange time.Duration,
-	net *xxdk.Cmix, store *storage.Session, networkManager interfaces.NetworkManager,
-	rng *fastRNG.StreamGenerator) *Manager {
+	net cmix.Client, store storage.Session, rng *fastRNG.StreamGenerator) *Manager {
 	return &Manager{
 		maxNumMessages: maxNumMessages,
 		avgSendDelta:   avgSendDelta,
@@ -85,13 +104,12 @@ func newManager(maxNumMessages int, avgSendDelta, randomRange time.Duration,
 		statusChan:     make(chan bool, statusChanLen),
 		net:            net,
 		store:          store,
-		networkManager: networkManager,
 		rng:            rng,
 	}
 }
 
 // StartDummyTraffic starts the process of sending dummy traffic. This function
-// matches the xxdk.Service type.
+// adheres to xxdk.Service.
 func (m *Manager) StartDummyTraffic() (stoppable.Stoppable, error) {
 	stop := stoppable.NewSingle(dummyTrafficStoppableName)
 	go m.sendThread(stop)
@@ -99,13 +117,19 @@ func (m *Manager) StartDummyTraffic() (stoppable.Stoppable, error) {
 	return stop, nil
 }
 
-// SetStatus sets the state of the dummy traffic send thread, which determines
-// if the thread is running or paused. The possible statuses are:
-//  true  = send thread is sending dummy messages
-//  false = send thread is paused/stopped and not sending dummy messages
-// Returns an error if the channel is full.
-// Note that this function cannot change the status of the send thread if it has
-// yet to be started via StartDummyTraffic or if it has been stopped.
+// SetStatus sets the state of the dummy traffic send thread by passing in
+// a boolean parameter. There may be a small delay in between this call
+// and the status of the sending thread to change accordingly. For example,
+// passing False into this call while the sending thread is currently sending messages
+// will not cancel nor halt the sending operation, but will pause the thread once that
+// operation has completed.
+//
+// Params:
+//  - boolean - Input should be true if you want to send dummy messages.
+//  			Input should be false if you want to pause dummy messages.
+// Returns:
+//  - error - if the Manager.SetStatus is called too frequently, causing the
+//    internal status channel to fill.
 func (m *Manager) SetStatus(status bool) error {
 	select {
 	case m.statusChan <- status:
@@ -115,13 +139,16 @@ func (m *Manager) SetStatus(status bool) error {
 	}
 }
 
-// GetStatus returns the current state of the dummy traffic send thread. It has
-// the following return values:
-//  true  = send thread is sending dummy messages
-//  false = send thread is paused/stopped and not sending dummy messages
-// Note that this function does not return the status set by SetStatus directly;
-// it returns the current status of the send thread, which means any call to
-// SetStatus will have a small delay before it is returned by GetStatus.
+// GetStatus returns the current state of the Manager's sending thread.
+// Note that this function does not return the status set by the most recent call to
+// SetStatus. Instead, this call returns the current status of the sending thread.
+// This is due to the small delay that may occur between calling SetStatus and the
+// sending thread taking into effect that status change.
+//
+// Returns:
+//   - boolean - Returns true if sending thread is sending dummy messages.
+//  	         Returns false if sending thread is paused/stopped and is
+// 	             not sending dummy messages.
 func (m *Manager) GetStatus() bool {
 	switch atomic.LoadUint32(&m.status) {
 	case running:
diff --git a/dummy/manager_test.go b/dummy/manager_test.go
index 6a49bcc6a597ebf476ee8bbfb8f45a79045a4488..5ce9cdaee7792078681a22255e53a8039677f041 100644
--- a/dummy/manager_test.go
+++ b/dummy/manager_test.go
@@ -27,7 +27,7 @@ func Test_newManager(t *testing.T) {
 	}
 
 	received := newManager(expected.maxNumMessages, expected.avgSendDelta,
-		expected.randomRange, nil, nil, nil, nil)
+		expected.randomRange, nil, nil, nil)
 
 	if statusChanLen != cap(received.statusChan) {
 		t.Errorf("Capacity of status channel unexpected."+
@@ -45,7 +45,7 @@ func Test_newManager(t *testing.T) {
 // Tests that Manager.StartDummyTraffic sends dummy messages and that it stops
 // when the stoppable is closed.
 func TestManager_StartDummyTraffic(t *testing.T) {
-	m := newTestManager(10, 50*time.Millisecond, 10*time.Millisecond, false, t)
+	m := newTestManager(10, 50*time.Millisecond, 10*time.Millisecond, t)
 
 	err := m.SetStatus(true)
 	if err != nil {
@@ -59,7 +59,7 @@ func TestManager_StartDummyTraffic(t *testing.T) {
 
 	msgChan := make(chan bool)
 	go func() {
-		for m.networkManager.(*testNetworkManager).GetMsgListLen() == 0 {
+		for m.net.(*mockCmix).GetMsgListLen() == 0 {
 			time.Sleep(5 * time.Millisecond)
 		}
 		msgChan <- true
@@ -71,7 +71,7 @@ func TestManager_StartDummyTraffic(t *testing.T) {
 		t.Errorf("Timed out after %s waiting for messages to be sent.",
 			3*m.avgSendDelta)
 	case <-msgChan:
-		numReceived += m.networkManager.(*testNetworkManager).GetMsgListLen()
+		numReceived += m.net.(*mockCmix).GetMsgListLen()
 	}
 
 	err = stop.Close()
@@ -86,7 +86,7 @@ func TestManager_StartDummyTraffic(t *testing.T) {
 
 	msgChan = make(chan bool)
 	go func() {
-		for m.networkManager.(*testNetworkManager).GetMsgListLen() == numReceived {
+		for m.net.(*mockCmix).GetMsgListLen() == numReceived {
 			time.Sleep(5 * time.Millisecond)
 		}
 		msgChan <- true
@@ -104,7 +104,7 @@ func TestManager_StartDummyTraffic(t *testing.T) {
 // can be called multiple times with the same status without it affecting
 // anything. Also tests that the thread quits even when paused.
 func TestManager_SetStatus(t *testing.T) {
-	m := newTestManager(10, 50*time.Millisecond, 10*time.Millisecond, false, t)
+	m := newTestManager(10, 50*time.Millisecond, 10*time.Millisecond, t)
 
 	err := m.SetStatus(false)
 	if err != nil {
@@ -118,10 +118,10 @@ func TestManager_SetStatus(t *testing.T) {
 	go func() {
 		var numReceived int
 		for i := 0; i < 2; i++ {
-			for m.networkManager.(*testNetworkManager).GetMsgListLen() == numReceived {
+			for m.net.(*mockCmix).GetMsgListLen() == numReceived {
 				time.Sleep(5 * time.Millisecond)
 			}
-			numReceived = m.networkManager.(*testNetworkManager).GetMsgListLen()
+			numReceived = m.net.(*mockCmix).GetMsgListLen()
 			msgChan <- true
 		}
 	}()
@@ -161,7 +161,7 @@ func TestManager_SetStatus(t *testing.T) {
 		t.Errorf("Timed out after %s waiting for messages to be sent.",
 			3*m.avgSendDelta)
 	case <-msgChan:
-		numReceived += m.networkManager.(*testNetworkManager).GetMsgListLen()
+		numReceived += m.net.(*mockCmix).GetMsgListLen()
 	}
 
 	// Setting status to true multiple times does not interrupt sending
@@ -177,10 +177,10 @@ func TestManager_SetStatus(t *testing.T) {
 		t.Errorf("Timed out after %s waiting for messages to be sent.",
 			3*m.avgSendDelta)
 	case <-msgChan:
-		if m.networkManager.(*testNetworkManager).GetMsgListLen() <= numReceived {
+		if m.net.(*mockCmix).GetMsgListLen() <= numReceived {
 			t.Errorf("Failed to receive second send."+
 				"\nmessages on last receive: %d\nmessages on this receive: %d",
-				numReceived, m.networkManager.(*testNetworkManager).GetMsgListLen())
+				numReceived, m.net.(*mockCmix).GetMsgListLen())
 		}
 	}
 
@@ -213,7 +213,7 @@ func TestManager_SetStatus(t *testing.T) {
 // Error path: tests that Manager.SetStatus returns an error if the status
 // cannot be set.
 func TestManager_SetStatus_ChannelError(t *testing.T) {
-	m := newTestManager(10, 50*time.Millisecond, 10*time.Millisecond, false, t)
+	m := newTestManager(10, 50*time.Millisecond, 10*time.Millisecond, t)
 
 	// Send the max number of status changes on the channel
 	for i := 0; i < statusChanLen; i++ {
@@ -236,7 +236,7 @@ func TestManager_SetStatus_ChannelError(t *testing.T) {
 // Tests that Manager.GetStatus gets the correct status before the send thread
 // starts, while sending, while paused, and after it is stopped.
 func TestManager_GetStatus(t *testing.T) {
-	m := newTestManager(10, 50*time.Millisecond, 10*time.Millisecond, false, t)
+	m := newTestManager(10, 50*time.Millisecond, 10*time.Millisecond, t)
 
 	err := m.SetStatus(false)
 	if err != nil {
@@ -254,10 +254,10 @@ func TestManager_GetStatus(t *testing.T) {
 	go func() {
 		var numReceived int
 		for i := 0; i < 2; i++ {
-			for m.networkManager.(*testNetworkManager).GetMsgListLen() == numReceived {
+			for m.net.(*mockCmix).GetMsgListLen() == numReceived {
 				time.Sleep(5 * time.Millisecond)
 			}
-			numReceived = m.networkManager.(*testNetworkManager).GetMsgListLen()
+			numReceived = m.net.(*mockCmix).GetMsgListLen()
 			msgChan <- true
 		}
 	}()
@@ -292,7 +292,7 @@ func TestManager_GetStatus(t *testing.T) {
 		t.Errorf("Timed out after %s waiting for messages to be sent.",
 			3*m.avgSendDelta)
 	case <-msgChan:
-		numReceived += m.networkManager.(*testNetworkManager).GetMsgListLen()
+		numReceived += m.net.(*mockCmix).GetMsgListLen()
 	}
 
 	// Setting status to true multiple times does not interrupt sending
@@ -311,10 +311,10 @@ func TestManager_GetStatus(t *testing.T) {
 		t.Errorf("Timed out after %s waiting for messages to be sent.",
 			3*m.avgSendDelta)
 	case <-msgChan:
-		if m.networkManager.(*testNetworkManager).GetMsgListLen() <= numReceived {
+		if m.net.(*mockCmix).GetMsgListLen() <= numReceived {
 			t.Errorf("Failed to receive second send."+
 				"\nmessages on last receive: %d\nmessages on this receive: %d",
-				numReceived, m.networkManager.(*testNetworkManager).GetMsgListLen())
+				numReceived, m.net.(*mockCmix).GetMsgListLen())
 		}
 	}
 
diff --git a/dummy/mockCmix_test.go b/dummy/mockCmix_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..f8211be97a777d23f59458416075154dd055e067
--- /dev/null
+++ b/dummy/mockCmix_test.go
@@ -0,0 +1,219 @@
+////////////////////////////////////////////////////////////////////////////////
+// Copyright © 2020 xx network SEZC                                           //
+//                                                                            //
+// Use of this source code is governed by a license that can be found in the  //
+// LICENSE file                                                               //
+////////////////////////////////////////////////////////////////////////////////
+
+package dummy
+
+import (
+	"gitlab.com/elixxir/client/cmix"
+	"gitlab.com/elixxir/client/cmix/gateway"
+	"gitlab.com/elixxir/client/cmix/identity"
+	"gitlab.com/elixxir/client/cmix/message"
+	"gitlab.com/elixxir/client/cmix/rounds"
+	"gitlab.com/elixxir/client/stoppable"
+	"gitlab.com/elixxir/comms/network"
+	"gitlab.com/elixxir/primitives/format"
+	"gitlab.com/xx_network/comms/connect"
+	"gitlab.com/xx_network/primitives/id"
+	"gitlab.com/xx_network/primitives/id/ephemeral"
+	"sync"
+	"time"
+)
+
+// mockCmix is a testing structure that adheres to cmix.Client.
+type mockCmix struct {
+	messages map[id.ID]format.Message
+	sync.RWMutex
+	payloadSize int
+}
+
+func newMockCmix(payloadSize int) cmix.Client {
+
+	return &mockCmix{
+		messages:    make(map[id.ID]format.Message),
+		payloadSize: payloadSize,
+	}
+}
+
+func (m *mockCmix) Send(recipient *id.ID, fingerprint format.Fingerprint, service message.Service, payload, mac []byte, cmixParams cmix.CMIXParams) (id.Round, ephemeral.Id, error) {
+	m.Lock()
+	defer m.Unlock()
+	m.messages[*recipient] = generateMessage(m.payloadSize, fingerprint, service, payload, mac)
+
+	return 0, ephemeral.Id{}, nil
+}
+
+func (m *mockCmix) GetMsgListLen() int {
+	m.RLock()
+	defer m.RUnlock()
+	return len(m.messages)
+}
+
+func (m *mockCmix) GetMsgList() map[id.ID]format.Message {
+	m.RLock()
+	defer m.RUnlock()
+	return m.messages
+}
+
+func (m mockCmix) Follow(report cmix.ClientErrorReport) (stoppable.Stoppable, error) {
+	//TODO implement me
+	panic("implement me")
+}
+
+func (m mockCmix) GetMaxMessageLength() int {
+	//TODO implement me
+	panic("implement me")
+}
+
+func (m *mockCmix) SendMany(messages []cmix.TargetedCmixMessage, p cmix.CMIXParams) (id.Round, []ephemeral.Id, error) {
+	//TODO implement me
+	panic("implement me")
+}
+
+func (m *mockCmix) AddIdentity(id *id.ID, validUntil time.Time, persistent bool) {
+	//TODO implement me
+	panic("implement me")
+}
+
+func (m *mockCmix) RemoveIdentity(id *id.ID) {
+	//TODO implement me
+	panic("implement me")
+}
+
+func (m mockCmix) GetIdentity(get *id.ID) (identity.TrackedID, error) {
+	//TODO implement me
+	panic("implement me")
+}
+
+func (m mockCmix) AddFingerprint(identity *id.ID, fingerprint format.Fingerprint, mp message.Processor) error {
+	//TODO implement me
+	panic("implement me")
+}
+
+func (m mockCmix) DeleteFingerprint(identity *id.ID, fingerprint format.Fingerprint) {
+	//TODO implement me
+	panic("implement me")
+}
+
+func (m mockCmix) DeleteClientFingerprints(identity *id.ID) {
+	//TODO implement me
+	panic("implement me")
+}
+
+func (m mockCmix) AddService(clientID *id.ID, newService message.Service, response message.Processor) {
+	//TODO implement me
+	panic("implement me")
+}
+
+func (m mockCmix) DeleteService(clientID *id.ID, toDelete message.Service, processor message.Processor) {
+	//TODO implement me
+	panic("implement me")
+}
+
+func (m mockCmix) DeleteClientService(clientID *id.ID) {
+	//TODO implement me
+	panic("implement me")
+}
+
+func (m mockCmix) TrackServices(tracker message.ServicesTracker) {
+	//TODO implement me
+	panic("implement me")
+}
+
+func (m mockCmix) CheckInProgressMessages() {
+	//TODO implement me
+	panic("implement me")
+}
+
+func (m mockCmix) IsHealthy() bool {
+	//TODO implement me
+	panic("implement me")
+}
+
+func (m mockCmix) WasHealthy() bool {
+	//TODO implement me
+	panic("implement me")
+}
+
+func (m mockCmix) AddHealthCallback(f func(bool)) uint64 {
+	//TODO implement me
+	panic("implement me")
+}
+
+func (m mockCmix) RemoveHealthCallback(u uint64) {
+	//TODO implement me
+	panic("implement me")
+}
+
+func (m mockCmix) HasNode(nid *id.ID) bool {
+	//TODO implement me
+	panic("implement me")
+}
+
+func (m mockCmix) NumRegisteredNodes() int {
+	//TODO implement me
+	panic("implement me")
+}
+
+func (m mockCmix) TriggerNodeRegistration(nid *id.ID) {
+	//TODO implement me
+	panic("implement me")
+}
+
+func (m mockCmix) GetRoundResults(timeout time.Duration, roundCallback cmix.RoundEventCallback, roundList ...id.Round) error {
+	//TODO implement me
+	panic("implement me")
+}
+
+func (m mockCmix) LookupHistoricalRound(rid id.Round, callback rounds.RoundResultCallback) error {
+	//TODO implement me
+	panic("implement me")
+}
+
+func (m mockCmix) SendToAny(sendFunc func(host *connect.Host) (interface{}, error), stop *stoppable.Single) (interface{}, error) {
+	//TODO implement me
+	panic("implement me")
+}
+
+func (m mockCmix) SendToPreferred(targets []*id.ID, sendFunc gateway.SendToPreferredFunc, stop *stoppable.Single, timeout time.Duration) (interface{}, error) {
+	//TODO implement me
+	panic("implement me")
+}
+
+func (m mockCmix) SetGatewayFilter(f gateway.Filter) {
+	//TODO implement me
+	panic("implement me")
+}
+
+func (m mockCmix) GetHostParams() connect.HostParams {
+	//TODO implement me
+	panic("implement me")
+}
+
+func (m mockCmix) GetAddressSpace() uint8 {
+	//TODO implement me
+	panic("implement me")
+}
+
+func (m mockCmix) RegisterAddressSpaceNotification(tag string) (chan uint8, error) {
+	//TODO implement me
+	panic("implement me")
+}
+
+func (m mockCmix) UnregisterAddressSpaceNotification(tag string) {
+	//TODO implement me
+	panic("implement me")
+}
+
+func (m mockCmix) GetInstance() *network.Instance {
+	//TODO implement me
+	panic("implement me")
+}
+
+func (m mockCmix) GetVerboseRounds() string {
+	//TODO implement me
+	panic("implement me")
+}
diff --git a/dummy/random.go b/dummy/random.go
index 8c8a87a6cc0328f136f2444125cde62c92972a0b..4190336b55ed461c4264ad259340876bc676afc7 100644
--- a/dummy/random.go
+++ b/dummy/random.go
@@ -10,42 +10,77 @@ package dummy
 import (
 	"encoding/binary"
 	"github.com/pkg/errors"
+	"gitlab.com/elixxir/client/cmix/message"
 	"gitlab.com/elixxir/primitives/format"
 	"gitlab.com/xx_network/crypto/csprng"
+	"gitlab.com/xx_network/primitives/id"
 	"time"
 ) // Error messages.
 
+// Error constants for Manager.newRandomCmixMessage and it's helper functions..
 const (
 	payloadSizeRngErr = "failed to generate random payload size: %+v"
+	payloadRngErr     = "failed to generate random payload: %+v"
+	fingerprintRngErr = "failed to generate random fingerprint: %+v"
+	macRngErr         = "failed to generate random MAC: %+v"
+	recipientRngErr   = "failed to generate random recipient: %+v"
 )
 
-// intRng returns, as an int, a non-negative, non-zero random number in [1, n)
-// from the csprng.Source.
-func intRng(n int, rng csprng.Source) (int, error) {
-	v, err := csprng.Generate(8, rng)
+// newRandomCmixMessage returns random format.Message data.
+//
+// Returns in order a:
+//  - Recipient (id.ID)
+//  - Message fingerprint (format.Fingerprint)
+//  - Message service (message.Service)
+//  - Payload ([]byte)
+//  - MAC ([]byte)
+//  - Error if there was an issue randomly generating any of the above data.
+//    The error will specify which of the above failed to be randomly generated.
+func (m *Manager) newRandomCmixMessage(rng csprng.Source) (
+	recipient *id.ID, fingerprint format.Fingerprint,
+	service message.Service,
+	payload, mac []byte, err error) {
+
+	// Generate random recipient
+	recipient, err = id.NewRandomID(rng, id.User)
 	if err != nil {
-		return 0, err
+		return nil, format.Fingerprint{}, message.Service{}, nil, nil,
+			errors.Errorf(recipientRngErr, err)
 	}
 
-	return int(binary.LittleEndian.Uint64(v)%uint64(n-1)) + 1, nil
-}
+	// Generate random message payload
+	payloadSize := m.store.GetCmixGroup().GetP().ByteLen()
+	payload, err = newRandomPayload(payloadSize, rng)
+	if err != nil {
+		return nil, format.Fingerprint{}, message.Service{}, nil, nil,
+			errors.Errorf(payloadRngErr, err)
+	}
 
-// durationRng returns a duration that is the base duration plus or minus a
-// random duration of max randomRange.
-func durationRng(base, randomRange time.Duration, rng csprng.Source) (
-	time.Duration, error) {
-	delta, err := intRng(int(2*randomRange), rng)
+	// Generate random fingerprint
+	fingerprint, err = newRandomFingerprint(rng)
 	if err != nil {
-		return 0, err
+		return nil, format.Fingerprint{}, message.Service{}, nil, nil,
+			errors.Errorf(fingerprintRngErr, err)
 	}
 
-	return base + randomRange - time.Duration(delta), nil
+	// Generate random MAC
+	mac, err = newRandomMAC(rng)
+	if err != nil {
+		return nil, format.Fingerprint{}, message.Service{}, nil, nil,
+			errors.Errorf(macRngErr, err)
+	}
+
+	// Generate random service
+	service = message.GetRandomService(rng)
+
+	return
 }
 
-// newRandomPayload generates a random payload of a random length.
+// newRandomPayload generates a random payload of a random length
+// within the maxPayloadSize.
 func newRandomPayload(maxPayloadSize int, rng csprng.Source) ([]byte, error) {
 	// Generate random payload size
-	randomPayloadSize, err := intRng(maxPayloadSize, rng)
+	randomPayloadSize, err := randomInt(maxPayloadSize, rng)
 	if err != nil {
 		return nil, errors.Errorf(payloadSizeRngErr, err)
 	}
@@ -86,3 +121,32 @@ func newRandomMAC(rng csprng.Source) ([]byte, error) {
 
 	return mac, nil
 }
+
+//////////////////////////////////////////////////////////////////////////////////
+// Miscellaneous
+//////////////////////////////////////////////////////////////////////////////////
+
+// randomDuration returns a duration that is the base duration plus or minus a
+// random duration of max randomRange.
+func randomDuration(base, randomRange time.Duration, rng csprng.Source) (
+	time.Duration, error) {
+
+	// Generate a random duration
+	delta, err := randomInt(int(2*randomRange), rng)
+	if err != nil {
+		return 0, err
+	}
+
+	return base + randomRange - time.Duration(delta), nil
+}
+
+// randomInt returns, as an int, a non-negative, non-zero random number in [1, n)
+// from the csprng.Source.
+func randomInt(n int, rng csprng.Source) (int, error) {
+	v, err := csprng.Generate(8, rng)
+	if err != nil {
+		return 0, err
+	}
+
+	return int(binary.LittleEndian.Uint64(v)%uint64(n-1)) + 1, nil
+}
diff --git a/dummy/random_test.go b/dummy/random_test.go
index 661986a0416993e211209d009a023c451dd3ff60..3303425c884da1febf5ddecc9b3850b8221e1e33 100644
--- a/dummy/random_test.go
+++ b/dummy/random_test.go
@@ -13,7 +13,7 @@ import (
 	"time"
 )
 
-// Consistency test: tests that intRng returns the expected int when using a
+// Consistency test: tests that randomInt returns the expected int when using a
 // PRNG and that the result is not larger than the max.
 func Test_intRng_Consistency(t *testing.T) {
 	expectedInts := []int{15, 1, 35, 13, 42, 52, 57, 3, 48}
@@ -22,9 +22,9 @@ func Test_intRng_Consistency(t *testing.T) {
 	max := 64
 
 	for i, expected := range expectedInts {
-		v, err := intRng(max, prng)
+		v, err := randomInt(max, prng)
 		if err != nil {
-			t.Errorf("intRng returned an error (%d): %+v", i, err)
+			t.Errorf("randomInt returned an error (%d): %+v", i, err)
 		}
 
 		if v != expected {
@@ -40,7 +40,7 @@ func Test_intRng_Consistency(t *testing.T) {
 	}
 }
 
-// Consistency test: tests that durationRng returns the expected int when using
+// Consistency test: tests that randomDuration returns the expected int when using
 // a PRNG and that the result is within the allowed range.
 func Test_durationRng_Consistency(t *testing.T) {
 	expectedDurations := []time.Duration{
@@ -52,9 +52,9 @@ func Test_durationRng_Consistency(t *testing.T) {
 	base, randomRange := time.Minute, 15*time.Second
 
 	for i, expected := range expectedDurations {
-		v, err := durationRng(base, randomRange, prng)
+		v, err := randomDuration(base, randomRange, prng)
 		if err != nil {
-			t.Errorf("durationRng returned an error (%d): %+v", i, err)
+			t.Errorf("randomDuration returned an error (%d): %+v", i, err)
 		}
 
 		if v != expected {
diff --git a/dummy/send.go b/dummy/send.go
index 84271b67ea4310bf91af057a58ffe2256520460a..06561c941434643f6f77af8105f78323b4d3c47b 100644
--- a/dummy/send.go
+++ b/dummy/send.go
@@ -8,192 +8,137 @@
 package dummy
 
 import (
+	"gitlab.com/elixxir/client/cmix"
+	"gitlab.com/xx_network/crypto/csprng"
 	"sync"
 	"sync/atomic"
 	"time"
 
 	"github.com/pkg/errors"
 	jww "github.com/spf13/jwalterweatherman"
-	"gitlab.com/elixxir/client/cmix"
 	"gitlab.com/elixxir/client/stoppable"
-	"gitlab.com/elixxir/primitives/format"
-	"gitlab.com/xx_network/crypto/csprng"
-	"gitlab.com/xx_network/primitives/id"
 )
 
-// Error messages.
+// Error messages for the Manager.sendThread and its helper functions.
 const (
-	numMsgsRngErr     = "failed to generate random number of messages to send: %+v"
-	payloadRngErr     = "failed to generate random payload: %+v"
-	recipientRngErr   = "failed to generate random recipient: %+v"
-	fingerprintRngErr = "failed to generate random fingerprint: %+v"
-	macRngErr         = "failed to generate random MAC: %+v"
+	numMsgsRngErr = "failed to generate random number of messages to send: %+v"
 )
 
 // sendThread is a thread that sends the dummy messages at random intervals.
 func (m *Manager) sendThread(stop *stoppable.Single) {
-	jww.DEBUG.Print("Starting dummy traffic sending thread.")
+	jww.INFO.Print("Starting dummy traffic sending thread.")
 
 	nextSendChan := make(<-chan time.Time)
 	nextSendChanPtr := &(nextSendChan)
 
 	for {
 		select {
-		case <-stop.Quit():
-			m.stopSendThread(stop)
-			return
 		case status := <-m.statusChan:
 			if status {
 				atomic.StoreUint32(&m.status, running)
-				nextSendChanPtr = &(m.randomTimer().C)
+				// Generate random duration
+				rng := m.rng.GetStream()
+				duration, err := randomDuration(m.avgSendDelta, m.randomRange, rng)
+				if err != nil {
+					rng.Close()
+					jww.FATAL.Panicf("Failed to generate random sending interval: %+v", err)
+				}
+				rng.Close()
+
+				// Create timer
+				nextSendChanPtr = &(time.NewTimer(duration).C)
+
 			} else {
 				atomic.StoreUint32(&m.status, paused)
 				nextSendChan = make(<-chan time.Time)
 				nextSendChanPtr = &nextSendChan
 			}
 		case <-*nextSendChanPtr:
-			nextSendChanPtr = &(m.randomTimer().C)
-
-			go func() {
-				// get list of random messages and recipients
-				rng := m.rng.GetStream()
-				msgs, err := m.newRandomMessages(rng)
-				if err != nil {
-					jww.FATAL.Panicf("Failed to generate dummy messages: %+v", err)
-				}
+			// Generate random duration
+			rng := m.rng.GetStream()
+			duration, err := randomDuration(m.avgSendDelta, m.randomRange, rng)
+			if err != nil {
 				rng.Close()
+				jww.FATAL.Panicf("Failed to generate random sending interval: %+v", err)
+			}
+			rng.Close()
+
+			// Create timer
+			nextSendChanPtr = &(time.NewTimer(duration).C)
 
-				err = m.sendMessages(msgs)
+			// Send messages
+			go func() {
+				err := m.sendMessages()
 				if err != nil {
-					jww.FATAL.Panicf("Failed to send dummy messages: %+v", err)
+					jww.ERROR.Printf("Failed to send dummy messages: %+v", err)
 				}
 			}()
+		case <-stop.Quit():
+			m.stopSendThread(stop)
+			return
 
 		}
 	}
 }
 
-// stopSendThread is triggered when the stoppable is triggered. It prints a
-// debug message, sets the thread status to stopped, and sets the status of the
-// stoppable to stopped.
-func (m *Manager) stopSendThread(stop *stoppable.Single) {
-	jww.DEBUG.Print(
-		"Stopping dummy traffic sending thread: stoppable triggered")
-	atomic.StoreUint32(&m.status, stopped)
-	stop.ToStopped()
-}
-
 // sendMessages generates and sends random messages.
-func (m *Manager) sendMessages(msgs map[id.ID]format.Message) error {
-	var sent, i int64
+func (m *Manager) sendMessages() error {
+	var sent int64
 	var wg sync.WaitGroup
 
-	for recipient, msg := range msgs {
-		wg.Add(1)
+	// Randomly generate amount of messages to send
+	rng := m.rng.GetStream()
+	defer rng.Close()
+	numMessages, err := randomInt(m.maxNumMessages+1, rng)
+	if err != nil {
+		return errors.Errorf(numMsgsRngErr, err)
+	}
 
-		go func(i int64, recipient id.ID, msg format.Message) {
+	for i := 0; i < numMessages; i++ {
+		wg.Add(1)
+		go func(localIndex, totalMessages int) {
 			defer wg.Done()
 
-			// Fill the preimage with random data to ensure it is not repeatable
-			p := cmix.GetDefaultParams()
-			// FIXME: these fields no longer available
-			//        through these params objects
-			// p.IdentityPreimage = make([]byte, 32)
-			// rng := m.rng.GetStream()
-			// if _, err := rng.Read(p.IdentityPreimage); err != nil {
-			// 	jww.FATAL.Panicf("Failed to generate data for random identity "+
-			// 		"preimage in e2e send: %+v", err)
-			// }
-			// rng.Close()
-			// p.DebugTag = "dummy"
-			_, _, err := m.networkManager.SendCMIX(msg, &recipient, p)
+			err = m.sendMessage(localIndex, totalMessages, rng)
 			if err != nil {
-				jww.WARN.Printf("Failed to send dummy message %d/%d via "+
-					"Send: %+v", i, len(msgs), err)
-			} else {
-				atomic.AddInt64(&sent, 1)
+				jww.ERROR.Printf("Failed to send message %d/%d: %+v",
+					localIndex, numMessages, err)
 			}
-		}(i, recipient, msg)
-
-		i++
+			// Add to counter of successful sends
+			atomic.AddInt64(&sent, 1)
+		}(i, numMessages)
 	}
 
 	wg.Wait()
-
-	jww.INFO.Printf("Sent %d/%d dummy messages.", sent, len(msgs))
-
+	jww.INFO.Printf("Sent %d/%d dummy messages.", sent, numMessages)
 	return nil
 }
 
-// newRandomMessages returns a map of a random recipients and random messages of
-// a randomly generated length in [1, Manager.maxNumMessages].
-func (m *Manager) newRandomMessages(rng csprng.Source) (
-	map[id.ID]format.Message, error) {
-	numMessages, err := intRng(m.maxNumMessages+1, rng)
-	if err != nil {
-		return nil, errors.Errorf(numMsgsRngErr, err)
-	}
-
-	msgs := make(map[id.ID]format.Message, numMessages)
-
-	for i := 0; i < numMessages; i++ {
-		// Generate random recipient
-		recipient, err := id.NewRandomID(rng, id.User)
-		if err != nil {
-			return nil, errors.Errorf(recipientRngErr, err)
-		}
-
-		msgs[*recipient], err = m.newRandomCmixMessage(rng)
-		if err != nil {
-			return nil, err
-		}
-	}
-
-	return msgs, nil
-}
-
-// newRandomCmixMessage returns a new cMix message filled with a randomly
-// generated payload, fingerprint, and MAC.
-func (m *Manager) newRandomCmixMessage(rng csprng.Source) (format.Message, error) {
-	// Create new empty cMix message
-	clientStorage := *m.store
-	cMixMsg := format.NewMessage(clientStorage.GetCmixGroup().GetP().ByteLen())
-
-	// Generate random message
-	randomMsg, err := newRandomPayload(cMixMsg.ContentsSize(), rng)
+// sendMessage is a helper function which generates a sends a single random format.Message
+// to a random recipient.
+func (m *Manager) sendMessage(index, totalMessages int, rng csprng.Source) error {
+	// Generate message data
+	recipient, fp, service, payload, mac, err := m.newRandomCmixMessage(rng)
 	if err != nil {
-		return format.Message{}, errors.Errorf(payloadRngErr, err)
+		return errors.Errorf("Failed to create random data: %+v", err)
 	}
 
-	// Generate random fingerprint
-	fingerprint, err := newRandomFingerprint(rng)
+	// Send message
+	p := cmix.GetDefaultCMIXParams()
+	_, _, err = m.net.Send(recipient, fp, service, payload, mac, p)
 	if err != nil {
-		return format.Message{}, errors.Errorf(fingerprintRngErr, err)
+		return errors.Errorf("Failed to send message: %+v", err)
 	}
 
-	// Generate random MAC
-	mac, err := newRandomMAC(rng)
-	if err != nil {
-		return format.Message{}, errors.Errorf(macRngErr, err)
-	}
-
-	// Set contents, fingerprint, and MAC, of the cMix message
-	cMixMsg.SetContents(randomMsg)
-	cMixMsg.SetKeyFP(fingerprint)
-	cMixMsg.SetMac(mac)
-
-	return cMixMsg, nil
+	return nil
 }
 
-// randomTimer generates a timer that will trigger after a random duration.
-func (m *Manager) randomTimer() *time.Timer {
-	rng := m.rng.GetStream()
-
-	duration, err := durationRng(m.avgSendDelta, m.randomRange, rng)
-	if err != nil {
-		jww.FATAL.Panicf("Failed to generate random duration to wait to send "+
-			"dummy messages: %+v", err)
-	}
-
-	return time.NewTimer(duration)
+// stopSendThread is triggered when the stoppable is triggered. It prints a
+// debug message, sets the thread status to stopped, and sets the status of the
+// stoppable to stopped.
+func (m *Manager) stopSendThread(stop *stoppable.Single) {
+	jww.DEBUG.Print(
+		"Stopping dummy traffic sending thread: stoppable triggered")
+	atomic.StoreUint32(&m.status, stopped)
+	stop.ToStopped()
 }
diff --git a/dummy/send_test.go b/dummy/send_test.go
index 9af8ee8796e4d76faa7d45188c60db271d772335..ef776b17ba9084fe07d34ab453ed890ab9b9f0f4 100644
--- a/dummy/send_test.go
+++ b/dummy/send_test.go
@@ -9,7 +9,6 @@ package dummy
 
 import (
 	"bytes"
-	"encoding/base64"
 	"gitlab.com/elixxir/client/stoppable"
 	"gitlab.com/elixxir/primitives/format"
 	"gitlab.com/xx_network/primitives/id"
@@ -21,7 +20,7 @@ import (
 
 // Tests that Manager.sendThread sends multiple sets of messages.
 func TestManager_sendThread(t *testing.T) {
-	m := newTestManager(10, 50*time.Millisecond, 10*time.Millisecond, false, t)
+	m := newTestManager(10, 50*time.Millisecond, 10*time.Millisecond, t)
 
 	stop := stoppable.NewSingle("sendThreadTest")
 	go m.sendThread(stop)
@@ -40,10 +39,10 @@ func TestManager_sendThread(t *testing.T) {
 	go func() {
 		var numReceived int
 		for i := 0; i < 2; i++ {
-			for m.networkManager.(*testNetworkManager).GetMsgListLen() == numReceived {
+			for m.net.(*mockCmix).GetMsgListLen() == numReceived {
 				time.Sleep(5 * time.Millisecond)
 			}
-			numReceived = m.networkManager.(*testNetworkManager).GetMsgListLen()
+			numReceived = m.net.(*mockCmix).GetMsgListLen()
 			msgChan <- true
 		}
 	}()
@@ -54,7 +53,7 @@ func TestManager_sendThread(t *testing.T) {
 		t.Errorf("Timed out after %s waiting for messages to be sent.",
 			3*m.avgSendDelta)
 	case <-msgChan:
-		numReceived += m.networkManager.(*testNetworkManager).GetMsgListLen()
+		numReceived += m.net.(*mockCmix).GetMsgListLen()
 	}
 
 	select {
@@ -62,10 +61,10 @@ func TestManager_sendThread(t *testing.T) {
 		t.Errorf("Timed out after %s waiting for messages to be sent.",
 			3*m.avgSendDelta)
 	case <-msgChan:
-		if m.networkManager.(*testNetworkManager).GetMsgListLen() <= numReceived {
+		if m.net.(*mockCmix).GetMsgListLen() <= numReceived {
 			t.Errorf("Failed to receive second send."+
 				"\nmessages on last receive: %d\nmessages on this receive: %d",
-				numReceived, m.networkManager.(*testNetworkManager).GetMsgListLen())
+				numReceived, m.net.(*mockCmix).GetMsgListLen())
 		}
 	}
 
@@ -86,36 +85,37 @@ func TestManager_sendThread(t *testing.T) {
 
 }
 
-// Tests that Manager.sendMessages sends all the messages with the correct
-// recipient.
-func TestManager_sendMessages(t *testing.T) {
-	m := newTestManager(100, 0, 0, false, t)
-	prng := NewPrng(42)
+// Tests that sendMessage generates random message data using pseudo-RNGs.
+func TestManager_sendMessage(t *testing.T) {
+	m := newTestManager(100, 0, 0, t)
+
+	// Generate two identical RNGs, one for generating expected data (newRandomCmixMessage)
+	// and one for received data (sendMessage)
+	prngOne := NewPrng(42)
+	prngTwo := NewPrng(42)
 
 	// Generate map of recipients and messages
 	msgs := make(map[id.ID]format.Message, m.maxNumMessages)
 	for i := 0; i < m.maxNumMessages; i++ {
-		recipient, err := id.NewRandomID(prng, id.User)
+		// Generate random data
+		recipient, fp, service, payload, mac, err := m.newRandomCmixMessage(prngOne)
 		if err != nil {
-			t.Errorf("Failed to generate random recipient ID (%d): %+v", i, err)
+			t.Fatalf("Failed to generate random cMix message (%d): %+v", i, err)
 		}
 
-		msg, err := m.newRandomCmixMessage(prng)
+		payloadSize := m.store.GetCmixGroup().GetP().ByteLen()
+		msgs[*recipient] = generateMessage(payloadSize, fp, service, payload, mac)
+
+		// Send the messages
+		err = m.sendMessage(i, m.maxNumMessages, prngTwo)
 		if err != nil {
-			t.Errorf("Failed to generate random cMix message (%d): %+v", i, err)
+			t.Errorf("sendMessages returned an error: %+v", err)
 		}
 
-		msgs[*recipient] = msg
-	}
-
-	// Send the messages
-	err := m.sendMessages(msgs)
-	if err != nil {
-		t.Errorf("sendMessages returned an error: %+v", err)
 	}
 
 	// get sent messages
-	receivedMsgs := m.networkManager.(*testNetworkManager).GetMsgList()
+	receivedMsgs := m.net.(*mockCmix).GetMsgList()
 
 	// Test that all messages were received
 	if len(receivedMsgs) != len(msgs) {
@@ -127,60 +127,46 @@ func TestManager_sendMessages(t *testing.T) {
 	for recipient, msg := range msgs {
 		receivedMsg, exists := receivedMsgs[recipient]
 		if !exists {
-			t.Errorf("Failed to receive message from %s: %+v", &recipient, msg)
-		} else if !reflect.DeepEqual(msg, receivedMsg) {
+			t.Errorf("Failed to receive message from %s: %+v", &recipient, msg.Marshal())
+		} else if !reflect.DeepEqual(msg.Marshal(), receivedMsg.Marshal()) {
+			// In mockCmix.Send, we map recipientId to the passed fingerprint.
 			t.Errorf("Received unexpected message for recipient %s."+
 				"\nexpected: %+v\nreceived: %+v", &recipient, msg, receivedMsg)
 		}
 	}
 }
 
-// Tests that Manager.newRandomMessages creates a non-empty map of messages and
-// that each message is unique.
-func TestManager_newRandomMessages(t *testing.T) {
-	m := newTestManager(10, 0, 0, false, t)
+// Tests that newRandomCmixMessage generates cMix message data with
+// populated recipient, payload, fingerprint, and MAC.
+func TestManager_newRandomCmixMessage(t *testing.T) {
+	m := newTestManager(0, 0, 0, t)
 	prng := NewPrng(42)
 
-	msgMap, err := m.newRandomMessages(prng)
+	// Generate data
+	recipient, fp, _, payload, mac, err := m.newRandomCmixMessage(prng)
 	if err != nil {
-		t.Errorf("newRandomMessages returned an error: %+v", err)
-	}
-
-	if len(msgMap) == 0 {
-		t.Error("Message map is empty.")
+		t.Fatalf("newRandomCmixMessage returned an error: %+v", err)
 	}
 
-	marshalledMsgs := make(map[string]format.Message, len(msgMap))
-	for _, msg := range msgMap {
-		msgString := base64.StdEncoding.EncodeToString(msg.Marshal())
-		if _, exists := marshalledMsgs[msgString]; exists {
-			t.Errorf("Message not unique.")
-		} else {
-			marshalledMsgs[msgString] = msg
-		}
+	// Check that recipient is not empty data
+	if bytes.Equal(recipient.Bytes(), make([]byte, id.ArrIDLen)) {
+		t.Errorf("Recipient ID not set")
 	}
-}
-
-// Tests that Manager.newRandomCmixMessage generates a cMix message with
-// populated contents, fingerprint, and MAC.
-func TestManager_newRandomCmixMessage(t *testing.T) {
-	m := newTestManager(0, 0, 0, false, t)
-	prng := NewPrng(42)
 
-	cMixMsg, err := m.newRandomCmixMessage(prng)
-	if err != nil {
-		t.Errorf("newRandomCmixMessage returned an error: %+v", err)
-	}
-
-	if bytes.Equal(cMixMsg.GetContents(), make([]byte, len(cMixMsg.GetContents()))) {
+	// Check that payload is not empty data
+	payloadSize := m.store.GetCmixGroup().GetP().ByteLen()
+	if bytes.Equal(payload, make([]byte, payloadSize)) {
 		t.Error("cMix message contents not set.")
 	}
 
-	if cMixMsg.GetKeyFP() == (format.Fingerprint{}) {
+	// Check that fingerprint is not empty data
+	if fp == (format.Fingerprint{}) {
 		t.Error("cMix message fingerprint not set.")
 	}
 
-	if bytes.Equal(cMixMsg.GetMac(), make([]byte, format.MacLen)) {
+	// Check that mac is not empty data
+	if bytes.Equal(mac, make([]byte, format.MacLen)) {
 		t.Error("cMix message MAC not set.")
 	}
+
 }
diff --git a/dummy/utils_test.go b/dummy/utils_test.go
index bc838731f5f42b345c4fbdfd849750b4551c0ef5..a3780867a513a6c497157bd17ccfceef89f2a5ec 100644
--- a/dummy/utils_test.go
+++ b/dummy/utils_test.go
@@ -8,27 +8,13 @@
 package dummy
 
 import (
-	"github.com/pkg/errors"
-	"gitlab.com/elixxir/client/cmix"
-	"gitlab.com/elixxir/client/cmix/gateway"
 	"gitlab.com/elixxir/client/cmix/message"
-	"gitlab.com/elixxir/client/event"
-	"gitlab.com/elixxir/client/interfaces"
-	"gitlab.com/elixxir/client/stoppable"
 	"gitlab.com/elixxir/client/storage"
-	"gitlab.com/elixxir/comms/mixmessages"
-	"gitlab.com/elixxir/comms/network"
-	"gitlab.com/elixxir/crypto/e2e"
 	"gitlab.com/elixxir/crypto/fastRNG"
 	"gitlab.com/elixxir/primitives/format"
-	"gitlab.com/xx_network/comms/connect"
 	"gitlab.com/xx_network/crypto/csprng"
-	"gitlab.com/xx_network/primitives/id"
-	"gitlab.com/xx_network/primitives/id/ephemeral"
-	"gitlab.com/xx_network/primitives/ndf"
 	"io"
 	"math/rand"
-	"sync"
 	"testing"
 	"time"
 )
@@ -51,201 +37,35 @@ func (s *Prng) SetSeed([]byte) error       { return nil }
 // newTestManager creates a new Manager that has groups stored for testing. One
 // of the groups in the list is also returned.
 func newTestManager(maxNumMessages int, avgSendDelta, randomRange time.Duration,
-	sendErr bool, t *testing.T) *Manager {
+	t *testing.T) *Manager {
 	store := storage.InitTestingSession(t)
+	payloadSize := store.GetCmixGroup().GetP().ByteLen()
 	m := &Manager{
 		maxNumMessages: maxNumMessages,
 		avgSendDelta:   avgSendDelta,
 		randomRange:    randomRange,
 		statusChan:     make(chan bool, statusChanLen),
-		store:          &store,
-		networkManager: newTestNetworkManager(sendErr, t),
+		store:          store,
+		net:            newMockCmix(payloadSize),
 		rng:            fastRNG.NewStreamGenerator(1000, 10, csprng.NewSystemRNG),
 	}
 
 	return m
 }
 
-// ////////////////////////////////////////////////////////////////////////////////
-// // Test Network State                                                       //
-// ////////////////////////////////////////////////////////////////////////////////
-
-// // testNetworkManager is a test implementation of NetworkManager interface.
-type testNetworkManager struct {
-	instance *network.Instance
-	messages map[id.ID]format.Message
-	sendErr  bool
-	sync.RWMutex
-}
-
-func newTestNetworkManager(sendErr bool, t *testing.T) interfaces.NetworkManager {
-	instanceComms := &connect.ProtoComms{
-		Manager: connect.NewManagerTesting(t),
-	}
-
-	thisInstance, err := network.NewInstanceTesting(instanceComms, getNDF(),
-		getNDF(), nil, nil, t)
-	if err != nil {
-		t.Fatalf("Failed to create new test instance: %v", err)
-	}
-
-	return &testNetworkManager{
-		instance: thisInstance,
-		messages: make(map[id.ID]format.Message),
-		sendErr:  sendErr,
-	}
-}
-
-func (tnm *testNetworkManager) GetMsgListLen() int {
-	tnm.RLock()
-	defer tnm.RUnlock()
-	return len(tnm.messages)
-}
-
-func (tnm *testNetworkManager) GetMsgList() map[id.ID]format.Message {
-	tnm.RLock()
-	defer tnm.RUnlock()
-	return tnm.messages
-}
-
-func (tnm *testNetworkManager) GetMsg(recipient id.ID) format.Message {
-	tnm.RLock()
-	defer tnm.RUnlock()
-	return tnm.messages[recipient]
-}
-
-// TEST
-func (tnm *testNetworkManager) SendE2E() (
-	[]id.Round, e2e.MessageID, time.Time, error) {
-	return nil, e2e.MessageID{}, time.Time{}, nil
-}
-
-// TEST
-func (tnm *testNetworkManager) SendUnsafe() ([]id.Round, error) {
-	return []id.Round{}, nil
-}
-
-func (tnm *testNetworkManager) SendCMIX(message format.Message,
-	recipient *id.ID, _ cmix.Params) (id.Round, ephemeral.Id, error) {
-	tnm.Lock()
-	defer tnm.Unlock()
-
-	if tnm.sendErr {
-		return 0, ephemeral.Id{}, errors.New("Send error")
-	}
+// generateMessage is a utility function which generates a format.Message
+// given message data.
+func generateMessage(payloadSize int,
+	fingerprint format.Fingerprint,
+	service message.Service,
+	payload, mac []byte) format.Message {
 
-	tnm.messages[*recipient] = message
+	// Build message. Will panic if inputs are not correct.
+	msg := format.NewMessage(payloadSize)
+	msg.SetContents(payload)
+	msg.SetKeyFP(fingerprint)
+	msg.SetSIH(service.Hash(msg.GetContents()))
+	msg.SetMac(mac)
 
-	return 0, ephemeral.Id{}, nil
-}
-
-func (tnm *testNetworkManager) SendManyCMIX([]cmix.TargetedCmixMessage, cmix.Params) (
-	id.Round, []ephemeral.Id, error) {
-	return 0, nil, nil
-}
-
-type dummyEventMgr struct{}
-
-func (d *dummyEventMgr) Report(int, string, string, string) {}
-func (tnm *testNetworkManager) GetEventManager() event.Reporter {
-	return &dummyEventMgr{}
-}
-
-func (tnm *testNetworkManager) GetInstance() *network.Instance             { return tnm.instance }
-func (tnm *testNetworkManager) GetAddressSpace() uint8                     { return 0 }
-func (tnm *testNetworkManager) GetHostParams() connect.HostParams          { return connect.HostParams{} }
-func (tnm *testNetworkManager) GetHealthTracker() interfaces.HealthTracker { return nil }
-func (tnm *testNetworkManager) Follow(interfaces.ClientErrorReport) (stoppable.Stoppable, error) {
-	return nil, nil
-}
-func (tnm *testNetworkManager) CheckGarbledMessages()        {}
-func (tnm *testNetworkManager) CheckInProgressMessages()     {}
-func (tnm *testNetworkManager) InProgressRegistrations() int { return 0 }
-func (tnm *testNetworkManager) GetSender() *gateway.Sender   { return nil }
-func (tnm *testNetworkManager) GetAddressSize() uint8        { return 0 }
-func (tnm *testNetworkManager) RegisterAddressSizeNotification(string) (chan uint8, error) {
-	return nil, nil
-}
-func (tnm *testNetworkManager) UnregisterAddressSizeNotification(string) {}
-func (tnm *testNetworkManager) SetPoolFilter(gateway.Filter)             {}
-func (tnm *testNetworkManager) GetVerboseRounds() string                 { return "" }
-func (tnm *testNetworkManager) HasNode(*id.ID) bool                      { return false }
-func (tnm *testNetworkManager) LookupHistoricalRound(id.Round, func(*mixmessages.RoundInfo, bool)) error {
-	return nil
-}
-func (tnm *testNetworkManager) NumRegisteredNodes() int { return 0 }
-func (tnm *testNetworkManager) RegisterAddressSpaceNotification(string) (chan uint8, error) {
-	return nil, nil
-}
-func (tnm *testNetworkManager) SendToAny(func(*connect.Host) (interface{}, error), *stoppable.Single) (interface{}, error) {
-	return nil, nil
-}
-func (tnm *testNetworkManager) SendToPreferred([]*id.ID, func(*connect.Host, *id.ID, time.Duration) (interface{}, error), *stoppable.Single, time.Duration) (interface{}, error) {
-	return nil, nil
-}
-func (tnm *testNetworkManager) SetGatewayFilter(func(map[id.ID]int, *ndf.NetworkDefinition) map[id.ID]int) {
-}
-func (tnm *testNetworkManager) TrackServices(message.ServicesTracker)     {}
-func (tnm *testNetworkManager) TriggerNodeRegistration(*id.ID)            {}
-func (tnm *testNetworkManager) UnregisterAddressSpaceNotification(string) {}
-
-func (tnm *testNetworkManager) AddFingerprint(*id.ID, format.Fingerprint, message.Processor) error {
-	return nil
-}
-func (tnm *testNetworkManager) DeleteFingerprint(*id.ID, format.Fingerprint) {}
-func (tnm *testNetworkManager) DeleteClientFingerprints(*id.ID)              {}
-
-func (tnm *testNetworkManager) AddIdentity(*id.ID, time.Time, bool) error { return nil }
-func (tnm *testNetworkManager) RemoveIdentity(*id.ID)                     {}
-
-func (tnm *testNetworkManager) AddTrigger(*id.ID, message.Service, message.Processor) {}
-func (tnm *testNetworkManager) DeleteTrigger(*id.ID, interfaces.Preimage, message.Processor) error {
-	return nil
-}
-func (tnm *testNetworkManager) DeleteClientTriggers(*id.ID) {}
-
-// ////////////////////////////////////////////////////////////////////////////////
-// // NDF Primes                                                                 //
-// ////////////////////////////////////////////////////////////////////////////////
-
-func getNDF() *ndf.NetworkDefinition {
-	return &ndf.NetworkDefinition{
-		E2E: ndf.Group{
-			Prime: "E2EE983D031DC1DB6F1A7A67DF0E9A8E5561DB8E8D49413394C049B7A" +
-				"8ACCEDC298708F121951D9CF920EC5D146727AA4AE535B0922C688B55B3D" +
-				"D2AEDF6C01C94764DAB937935AA83BE36E67760713AB44A6337C20E78615" +
-				"75E745D31F8B9E9AD8412118C62A3E2E29DF46B0864D0C951C394A5CBBDC" +
-				"6ADC718DD2A3E041023DBB5AB23EBB4742DE9C1687B5B34FA48C3521632C" +
-				"4A530E8FFB1BC51DADDF453B0B2717C2BC6669ED76B4BDD5C9FF558E88F2" +
-				"6E5785302BEDBCA23EAC5ACE92096EE8A60642FB61E8F3D24990B8CB12EE" +
-				"448EEF78E184C7242DD161C7738F32BF29A841698978825B4111B4BC3E1E" +
-				"198455095958333D776D8B2BEEED3A1A1A221A6E37E664A64B83981C46FF" +
-				"DDC1A45E3D5211AAF8BFBC072768C4F50D7D7803D2D4F278DE8014A47323" +
-				"631D7E064DE81C0C6BFA43EF0E6998860F1390B5D3FEACAF1696015CB79C" +
-				"3F9C2D93D961120CD0E5F12CBB687EAB045241F96789C38E89D796138E63" +
-				"19BE62E35D87B1048CA28BE389B575E994DCA755471584A09EC723742DC3" +
-				"5873847AEF49F66E43873",
-			Generator: "2",
-		},
-		CMIX: ndf.Group{
-			Prime: "9DB6FB5951B66BB6FE1E140F1D2CE5502374161FD6538DF1648218642" +
-				"F0B5C48C8F7A41AADFA187324B87674FA1822B00F1ECF8136943D7C55757" +
-				"264E5A1A44FFE012E9936E00C1D3E9310B01C7D179805D3058B2A9F4BB6F" +
-				"9716BFE6117C6B5B3CC4D9BE341104AD4A80AD6C94E005F4B993E14F091E" +
-				"B51743BF33050C38DE235567E1B34C3D6A5C0CEAA1A0F368213C3D19843D" +
-				"0B4B09DCB9FC72D39C8DE41F1BF14D4BB4563CA28371621CAD3324B6A2D3" +
-				"92145BEBFAC748805236F5CA2FE92B871CD8F9C36D3292B5509CA8CAA77A" +
-				"2ADFC7BFD77DDA6F71125A7456FEA153E433256A2261C6A06ED3693797E7" +
-				"995FAD5AABBCFBE3EDA2741E375404AE25B",
-			Generator: "5C7FF6B06F8F143FE8288433493E4769C4D988ACE5BE25A0E2480" +
-				"9670716C613D7B0CEE6932F8FAA7C44D2CB24523DA53FBE4F6EC3595892D" +
-				"1AA58C4328A06C46A15662E7EAA703A1DECF8BBB2D05DBE2EB956C142A33" +
-				"8661D10461C0D135472085057F3494309FFA73C611F78B32ADBB5740C361" +
-				"C9F35BE90997DB2014E2EF5AA61782F52ABEB8BD6432C4DD097BC5423B28" +
-				"5DAFB60DC364E8161F4A2A35ACA3A10B1C4D203CC76A470A33AFDCBDD929" +
-				"59859ABD8B56E1725252D78EAC66E71BA9AE3F1DD2487199874393CD4D83" +
-				"2186800654760E1E34C09E4D155179F9EC0DC4473F996BDCE6EED1CABED8" +
-				"B6F116F7AD9CF505DF0F998E34AB27514B0FFE7",
-		},
-	}
+	return msg
 }
diff --git a/go.sum b/go.sum
index 3898f13b2d9af37a8e82268407d8423e1badb09a..9ddad24c184a4ed3b3074e6cf902a8ea1e2e9352 100644
--- a/go.sum
+++ b/go.sum
@@ -337,6 +337,8 @@ github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
 github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/profile v1.6.0 h1:hUDfIISABYI59DyeB3OTay/HxSRwTQ8rB/H83k6r5dM=
+github.com/pkg/profile v1.6.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18=
 github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
diff --git a/interfaces/networkManager.go b/interfaces/networkManager.go
deleted file mode 100644
index 2139e9597c6a4cdc51714e47308d6aa91d3481c5..0000000000000000000000000000000000000000
--- a/interfaces/networkManager.go
+++ /dev/null
@@ -1,244 +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 interfaces
-
-import (
-	"time"
-
-	"gitlab.com/elixxir/comms/network"
-	"gitlab.com/xx_network/comms/connect"
-	"gitlab.com/xx_network/primitives/ndf"
-
-	"gitlab.com/elixxir/client/cmix"
-	"gitlab.com/elixxir/client/cmix/message"
-	"gitlab.com/elixxir/client/stoppable"
-	"gitlab.com/elixxir/comms/mixmessages"
-	"gitlab.com/elixxir/primitives/format"
-	"gitlab.com/xx_network/primitives/id"
-	"gitlab.com/xx_network/primitives/id/ephemeral"
-)
-
-type NetworkManager interface {
-	// Follow starts the tracking of the network in a new thread.
-	// Errors that occur are reported on the ClientErrorReport function if
-	// passed. The returned stopable can be used to stop the follower.
-	// Only one follower may run at a time.
-	Follow(report ClientErrorReport) (stoppable.Stoppable, error)
-
-	/*===Sending==========================================================*/
-
-	// SendCMIX sends a "raw" CMIX message payload to the provided
-	// recipient.  Returns the round ID of the round the payload
-	// was sent or an error if it fails.
-	SendCMIX(message format.Message, recipient *id.ID, p cmix.Params) (
-		id.Round, ephemeral.Id, error)
-
-	// SendManyCMIX sends many "raw" cMix message payloads to each
-	// of the provided recipients. Used to send messages in group
-	// chats. Metadata is NOT as well protected with this call and
-	// can leak data about yourself. Should be replaced with
-	// multiple uses of SendCmix in most cases. Returns the round
-	// ID of the round the payload was sent or an error if it
-	// fails.
-	// WARNING: Potentially Unsafe
-	SendManyCMIX(messages []cmix.TargetedCmixMessage, p cmix.Params) (
-		id.Round, []ephemeral.Id, error)
-
-	/*===Message Reception================================================*/
-	/* Identities are all network identities which the client is currently
-	trying to pick up message on. An identity must be added
-	to receive messages, fake ones will be used to poll the network
-	if none are present. On creation of the network handler, the identity in
-	session storage will be automatically added*/
-
-	// AddIdentity adds an identity to be tracked
-	// If persistent is false, the identity will not be stored to disk and
-	// will be dropped on reload.
-	AddIdentity(id *id.ID, validUntil time.Time, persistent bool) error
-	// RemoveIdentity removes a currently tracked identity.
-	RemoveIdentity(id *id.ID)
-
-	/* Fingerprints are the primary mechanism of identifying a
-	picked up message over cMix. They are a unique one time use
-	255 bit vector generally associated with a specific encryption
-	key, but can be used for an alternative protocol.When
-	registering a fingerprint, a MessageProcessor is registered to
-	handle the message.*/
-
-	// AddFingerprint - Adds a fingerprint which will be handled by a
-	// specific processor for messages received by the given identity
-	AddFingerprint(identity *id.ID, fingerprint format.Fingerprint,
-		mp message.Processor) error
-
-	// DeleteFingerprint deletes a single fingerprint associated
-	// with the given identity if it exists
-
-	DeleteFingerprint(identity *id.ID, fingerprint format.Fingerprint)
-	// DeleteClientFingerprints deletes al fingerprint associated
-	// with the given identity if it exists
-	DeleteClientFingerprints(identity *id.ID)
-
-	/* trigger - predefined hash based tags appended to all cMix messages
-	which, though trial hashing, are used to determine if a message applies
-	to this client
-
-	Triggers are used for 2 purposes - They can be processed by the
-	notifications system, or can be used to implement custom non fingerprint
-	processing of payloads. I.E. key negotiation, broadcast negotiation
-
-	A tag is appended to the message of the format tag =
-	H(H(messageContents), preimage) and trial hashing is used to
-	determine if a message adheres to a tag.
-
-	WARNING: If a preimage is known by an adversary, they can
-	determine which messages are for the client on reception
-	(which is normally hidden due to collision between ephemeral
-	IDs.
-
-	Due to the extra overhead of trial hashing, triggers are
-	processed after fingerprints.  If a fingerprint match occurs
-	on the message, triggers will not be handled.
-
-	Triggers are address to the session. When starting a new
-	client, all triggers must be re-added before
-	StartNetworkFollower is called.
-	*/
-
-	// AddTrigger - Adds a trigger which can call a message
-	// handing function or be used for notifications. Multiple
-	// triggers can be registered for the same preimage.
-	//   preimage - the preimage which is triggered on
-	//   type - a descriptive string of the trigger. Generally
-	//          used in notifications
-	//   source - a byte buffer of related data. Generally used in
-	//            notifications.
-	//     Example: Sender ID
-	AddTrigger(identity *id.ID, newTrigger message.Service,
-		response message.Processor)
-
-	// DeleteTrigger - If only a single response is associated with the
-	// preimage, the entire preimage is removed. If there is more than one
-	// response, only the given response is removed if nil is passed in for
-	// response, all triggers for the preimage will be removed
-	DeleteTrigger(identity *id.ID, preimage Preimage,
-		response message.Processor) error
-
-	// DeleteClientTriggers - deletes all triggers assoseated with
-	// the given identity
-	DeleteClientTriggers(identity *id.ID)
-
-	// TrackServices - Registers a callback which will get called
-	// every time triggers change.
-	// It will receive the triggers list every time it is modified.
-	// Will only get callbacks while the Network Follower is running.
-	// Multiple trackTriggers can be registered
-	TrackServices(message.ServicesTracker)
-
-	/* In inProcess */
-	// it is possible to receive a message over cMix before the
-	// fingerprints or triggers are registered. As a result, when
-	// handling fails, messages are put in the inProcess que for a
-	// set number of retries.
-
-	// CheckInProgressMessages - retry processing all messages in check in
-	// progress messages. Call this after adding fingerprints or triggers
-	//while the follower is running.
-	CheckInProgressMessages()
-
-	/*===Nodes============================================================*/
-	/* Keys must be registed with nodes in order to send messages
-	throug them.  this process is in general automatically handled
-	by the Network Manager*/
-
-	// HasNode can be used to determine if a keying relationship
-	// exists with a node.
-	HasNode(nid *id.ID) bool
-
-	// NumRegisteredNodes Returns the total number of nodes we have a keying
-	// relationship with
-	NumRegisteredNodes() int
-
-	// TriggerNodeRegistration triggers the generation of a keying
-	// relationship with a given node
-	TriggerNodeRegistration(nid *id.ID)
-
-	/*===Historical Rounds================================================*/
-	/* A complete set of round info is not kept on the client, and sometimes
-	the network will need to be queried to get round info. Historical rounds
-	is the system internal to the Network Manager to do this.
-	It can be used externally as well.*/
-
-	// LookupHistoricalRound - looks up the passed historical round on the
-	// network
-	LookupHistoricalRound(rid id.Round,
-		callback func(info *mixmessages.RoundInfo,
-		success bool)) error
-
-	/*===Sender===========================================================*/
-	/* The sender handles sending comms to the network. It tracks
-	connections to gateways and handles proxying to gateways for
-	targeted comms. It can be used externally to contact gateway
-	directly, bypassing the majority of the network package*/
-
-	// SendToAny can be used to send the comm to any gateway in the network.
-	SendToAny(sendFunc func(host *connect.Host) (interface{}, error),
-		stop *stoppable.Single) (interface{}, error)
-
-	// SendToPreferred sends to a specific gateway, doing so through another
-	// gateway as a proxy if not directly connected.
-	SendToPreferred(targets []*id.ID, sendFunc func(host *connect.Host,
-		target *id.ID, timeout time.Duration) (interface{}, error),
-		stop *stoppable.Single, timeout time.Duration) (interface{},
-		error)
-
-	// SetGatewayFilter sets a function which will be used to
-	// filter gateways before connecting.
-	SetGatewayFilter(f func(map[id.ID]int,
-		*ndf.NetworkDefinition) map[id.ID]int)
-
-	// GetHostParams - returns the host params used when
-	// connectign to gateways
-	GetHostParams() connect.HostParams
-
-	/*===Address Space====================================================*/
-	// The network compasses identities into a smaller address
-	// space to cause collisions and hide the actual recipient of
-	// messages. These functions allow for the tracking of this
-	// addresses space. In general, address space issues are
-	// completely handled by the network package
-
-	// GetAddressSpace GetAddressSize returns the current address
-	// size of IDs. Blocks until an address size is known.
-	GetAddressSpace() uint8
-
-	// RegisterAddressSpaceNotification returns a channel that
-	// will trigger for every address space size update. The
-	// provided tag is the unique ID for the channel. Returns an
-	// error if the tag is already used.
-	RegisterAddressSpaceNotification(tag string) (chan uint8, error)
-
-	// UnregisterAddressSpaceNotification stops broadcasting
-	// address space size updates on the channel with the
-	// specified tag.
-	UnregisterAddressSpaceNotification(tag string)
-
-	/*===Accessors========================================================*/
-
-	// GetInstance returns the network instance object, which tracks the
-	// state of the network
-	GetInstance() *network.Instance
-
-	// GetHealthTracker returns the health tracker, which using a polling or
-	// event api lets you determine if network following is functioning
-	GetHealthTracker() HealthTracker
-
-	// GetVerboseRounds returns stringification of verbose round info
-	GetVerboseRounds() string
-}
-
-type Preimage [32]byte
diff --git a/registration/register.go b/registration/register.go
index 57b6cf8f2003b0323d96d9b53aff9dc6e0f1ac2c..97c1f3d6d9ef121ca3d4efa8e71c6070d85614a0 100644
--- a/registration/register.go
+++ b/registration/register.go
@@ -62,14 +62,6 @@ func register(comms registrationMessageSender, host *connect.Host,
 			"reception confirmation message")
 	}
 
-	transmissionConfirmation := &pb.ClientRegistrationConfirmation{}
-	err = proto.Unmarshal(response.GetClientReceptionConfirmation().
-		ClientRegistrationConfirmation, transmissionConfirmation)
-	if err != nil {
-		return nil, nil, 0, errors.WithMessage(err, "Failed to unmarshal "+
-			"transmission confirmation message")
-	}
-
 	// Verify reception signature
 	receptionSignature := response.GetClientReceptionConfirmation().
 		GetRegistrarSignature().Signature
@@ -80,6 +72,15 @@ func register(comms registrationMessageSender, host *connect.Host,
 		return nil, nil, 0, errors.WithMessage(err, "Failed to verify reception signature")
 	}
 
+	// Unmarshal transmission confirmation
+	transmissionConfirmation := &pb.ClientRegistrationConfirmation{}
+	err = proto.Unmarshal(response.GetClientTransmissionConfirmation().
+		ClientRegistrationConfirmation, transmissionConfirmation)
+	if err != nil {
+		return nil, nil, 0, errors.WithMessage(err, "Failed to unmarshal "+
+			"transmission confirmation message")
+	}
+
 	// Verify transmission signature
 	transmissionSignature := response.GetClientTransmissionConfirmation().
 		GetRegistrarSignature().Signature
diff --git a/storage/versioned/kv.go b/storage/versioned/kv.go
index 2b8b7ae2129d5a67fdf576297a4318e229c91ff8..13f05e82c7449282c669453eefbff1a6bb03ad46 100644
--- a/storage/versioned/kv.go
+++ b/storage/versioned/kv.go
@@ -164,3 +164,9 @@ func (v *KV) GetFullKey(key string, version uint64) string {
 func (v *KV) makeKey(key string, version uint64) string {
 	return fmt.Sprintf("%s%s_%d", v.prefix, key, version)
 }
+
+// Exists returns false if the error indicates the element doesn't
+// exist.
+func (v *KV) Exists(err error) bool {
+	return ekv.Exists(err)
+}
diff --git a/ud/addFact.go b/ud/addFact.go
index 719e7d137ab1f9c3e5ce4d414c9db08876d668a4..2022b82e03482dbbe5d172ff6fc95a533c1d87d2 100644
--- a/ud/addFact.go
+++ b/ud/addFact.go
@@ -40,7 +40,7 @@ func (m *Manager) addFact(inFact fact.Fact, myId *id.ID,
 	fHash := factID.Fingerprint(f)
 
 	// Sign our inFact for putting into the request
-	privKey, err := m.user.GetReceptionIdentity().GetRSAPrivatePem()
+	privKey, err := m.user.GetReceptionIdentity().GetRSAPrivateKey()
 	if err != nil {
 		return "", err
 	}
diff --git a/ud/confirmFact.go b/ud/confirmFact.go
index a55da37566945a03b5c0a276f92b7e30ec220f4d..1d77f357c8a1a53f7b8ed5bf6899ce696c2def01 100644
--- a/ud/confirmFact.go
+++ b/ud/confirmFact.go
@@ -6,8 +6,8 @@ import (
 	pb "gitlab.com/elixxir/comms/mixmessages"
 )
 
-// ConfirmFact confirms a fact first registered via AddFact. The
-// confirmation ID comes from AddFact while the code will come over the
+// ConfirmFact confirms a fact first registered via SendRegisterFact. The
+// confirmation ID comes from SendRegisterFact while the code will come over the
 // associated communications system.
 func (m *Manager) ConfirmFact(confirmationID, code string) error {
 	jww.INFO.Printf("ud.ConfirmFact(%s, %s)", confirmationID, code)
diff --git a/ud/manager.go b/ud/manager.go
index 0a4247fb809d876b4c0659639827bc2929ba3b83..d431da78c9f961ac7112923203eb54f51dae9564 100644
--- a/ud/manager.go
+++ b/ud/manager.go
@@ -66,18 +66,34 @@ func NewOrLoad(user udE2e, comms Comms, follower udNetworkStatus,
 
 	jww.INFO.Println("ud.NewOrLoad()")
 
-	// Construct manager
-	m, err := loadOrNewManager(user, comms, follower)
-	if err != nil {
-		return nil, err
+	if follower() != xxdk.Running {
+		return nil, errors.New(
+			"cannot start UD Manager when network follower is not running.")
+	}
+
+	// Initialize manager
+	m := &Manager{
+		user:  user,
+		comms: comms,
 	}
 
 	// Set user discovery
-	err = m.setUserDiscovery(cert, contactFile, address)
+	err := m.setUserDiscovery(cert, contactFile, address)
 	if err != nil {
 		return nil, err
 	}
 
+	// Initialize store
+	m.store, err = store.NewOrLoadStore(m.getKv())
+	if err != nil {
+		return nil, errors.Errorf("Failed to initialize store: %v", err)
+	}
+
+	// If already registered, return
+	if IsRegistered(m.getKv()) {
+		return m, nil
+	}
+
 	// Register manager
 	rng := m.getRng().GetStream()
 	defer rng.Close()
@@ -86,6 +102,13 @@ func NewOrLoad(user udE2e, comms Comms, follower udNetworkStatus,
 		return nil, err
 	}
 
+	usernameFact, err := fact.NewFact(fact.Username, username)
+	if err != nil {
+		return nil, err
+	}
+
+	err = m.store.StoreUsername(usernameFact)
+
 	return m, nil
 }
 
@@ -111,7 +134,8 @@ func NewOrLoad(user udE2e, comms Comms, follower udNetworkStatus,
 // Returns
 //  - A Manager object which is registered to the specified UD service.
 func NewManagerFromBackup(user udE2e, comms Comms, follower udNetworkStatus,
-	email, phone fact.Fact, cert, contactFile []byte, address string) (*Manager, error) {
+	username, email, phone fact.Fact,
+	cert, contactFile []byte, address string) (*Manager, error) {
 	jww.INFO.Println("ud.NewManagerFromBackup()")
 	if follower() != xxdk.Running {
 		return nil, errors.New(
@@ -133,7 +157,7 @@ func NewManagerFromBackup(user udE2e, comms Comms, follower udNetworkStatus,
 	}
 
 	// Put any passed in missing facts into store
-	err = m.store.BackUpMissingFacts(email, phone)
+	err = m.store.BackUpMissingFacts(username, email, phone)
 	if err != nil {
 		return nil, errors.WithMessage(err, "Failed to restore UD store "+
 			"from backup")
@@ -164,7 +188,7 @@ func InitStoreFromBackup(kv *versioned.KV,
 	}
 
 	// Put any passed in missing facts into store
-	err = udStore.BackUpMissingFacts(email, phone)
+	err = udStore.BackUpMissingFacts(username, email, phone)
 	if err != nil {
 		return errors.WithMessage(err, "Failed to restore UD store "+
 			"from backup")
@@ -197,41 +221,6 @@ func (m *Manager) GetContact() contact.Contact {
 	return m.ud.contact
 }
 
-// loadOrNewManager is a helper function which loads from storage or
-// creates a new Manager object.
-func loadOrNewManager(user udE2e, comms Comms,
-	follower udNetworkStatus) (*Manager, error) {
-	if follower() != xxdk.Running {
-		return nil, errors.New(
-			"cannot start UD Manager when network follower is not running.")
-	}
-
-	// Initialize manager
-	m := &Manager{
-		user:  user,
-		comms: comms,
-	}
-
-	if m.isRegistered() {
-		// Load manager if already registered
-		var err error
-		m.store, err = store.NewOrLoadStore(m.getKv())
-		if err != nil {
-			return nil, errors.Errorf("Failed to initialize store: %v", err)
-		}
-		return m, nil
-	}
-
-	// Initialize store
-	var err error
-	m.store, err = store.NewOrLoadStore(m.getKv())
-	if err != nil {
-		return nil, errors.Errorf("Failed to initialize store: %v", err)
-	}
-
-	return m, nil
-}
-
 ////////////////////////////////////////////////////////////////////////////////
 // Internal Getters                                                           //
 ////////////////////////////////////////////////////////////////////////////////
@@ -242,7 +231,7 @@ func (m *Manager) getCmix() udCmix {
 	return m.user.GetCmix()
 }
 
-// getKv returns a versioned.KV used for isRegistered and setRegistered.
+// getKv returns a versioned.KV used for IsRegistered and setRegistered.
 // This is separated from store operations as store's kv
 // has a different prefix which breaks backwards compatibility.
 func (m *Manager) getKv() *versioned.KV {
diff --git a/ud/register.go b/ud/register.go
index 495d50da5e1fb51dd0e9d0e9d0211547d3314946..48074cf1ac491fb5496d1160d1ef7cda54138edb 100644
--- a/ud/register.go
+++ b/ud/register.go
@@ -19,7 +19,7 @@ func (m *Manager) register(username string, networkSignature []byte,
 
 	// Retrieve data used for registration
 	identity := m.user.GetReceptionIdentity()
-	privKey, err := identity.GetRSAPrivatePem()
+	privKey, err := identity.GetRSAPrivateKey()
 	if err != nil {
 		return err
 	}
diff --git a/ud/registered.go b/ud/registered.go
index 06dba95886e3d92717ae9d8143fbd3ca8bd3189c..77742fc17923e1b0a30c03622db40e797e76ec36 100644
--- a/ud/registered.go
+++ b/ud/registered.go
@@ -11,10 +11,10 @@ import (
 const isRegisteredKey = "isRegisteredKey"
 const isRegisteredVersion = 0
 
-// isRegistered loads from storage if the user is registered with user
+// IsRegistered loads from storage if the user is registered with user
 // discovery.
-func (m *Manager) isRegistered() bool {
-	_, err := m.getKv().Get(isRegisteredKey, isRegisteredVersion)
+func IsRegistered(kv *versioned.KV) bool {
+	_, err := kv.Get(isRegisteredKey, isRegisteredVersion)
 	if err != nil {
 		return false
 	}
diff --git a/ud/remove.go b/ud/remove.go
index ebb809c0022ab0bbc81237c092c106bcbf946bd2..691d9792f4468093ed2468b654b829ec60353c35 100644
--- a/ud/remove.go
+++ b/ud/remove.go
@@ -40,7 +40,7 @@ func (m *Manager) removeFact(f fact.Fact,
 
 	// Sign our inFact for putting into the request
 	identity := m.user.GetReceptionIdentity()
-	privKey, err := identity.GetRSAPrivatePem()
+	privKey, err := identity.GetRSAPrivateKey()
 	if err != nil {
 		return err
 	}
@@ -78,7 +78,7 @@ func (m *Manager) PermanentDeleteAccount(f fact.Fact) error {
 			"a username. Cannot remove fact %q", f.Fact))
 	}
 	identity := m.user.GetReceptionIdentity()
-	privKey, err := identity.GetRSAPrivatePem()
+	privKey, err := identity.GetRSAPrivateKey()
 	if err != nil {
 		return err
 	}
diff --git a/ud/store/facts.go b/ud/store/facts.go
index 202d8814bf5748ad5c7f8bf77c68ca26344fa842..d0ac3112616de9c9336555ea9126ec059df8ff1d 100644
--- a/ud/store/facts.go
+++ b/ud/store/facts.go
@@ -42,6 +42,42 @@ func (s *Store) RestoreFromBackUp(backupData fact.FactList) error {
 	return s.save()
 }
 
+// StoreUsername forces the storage of a username fact.Fact into the
+// Store's confirmedFacts map. The passed in fact.Fact must be of
+// type fact.Username or this will not store the username.
+func (s *Store) StoreUsername(f fact.Fact) error {
+	s.mux.Lock()
+	defer s.mux.Unlock()
+
+	if f.T != fact.Username {
+		return errors.Errorf("Fact (%s) is not of type username", f.Stringify())
+	}
+
+	s.confirmedFacts[f] = struct{}{}
+
+	return s.saveUnconfirmedFacts()
+}
+
+// GetUsername retrieves the username from the Store object.
+// If it is not directly in the Store's username field, it is
+// searched for in the map.
+func (s *Store) GetUsername() (string, error) {
+	s.mux.RLock()
+	defer s.mux.RUnlock()
+
+	// todo: refactor this in the future so that
+	//  it's an O(1) lookup (place this object in another map
+	//  or have it's own field)
+	for f := range s.confirmedFacts {
+		if f.T == fact.Username {
+			return f.Fact, nil
+		}
+	}
+
+	return "", errors.New("Could not find username in store")
+
+}
+
 // StoreUnconfirmedFact stores a fact that has been added to UD but has not been
 // confirmed by the user. It is keyed on the confirmation ID given by UD.
 func (s *Store) StoreUnconfirmedFact(confirmationId string, f fact.Fact) error {
@@ -84,11 +120,11 @@ func (s *Store) ConfirmFact(confirmationId string) error {
 // If you attempt to back up a fact type that has already been backed up,
 // an error will be returned and nothing will be backed up.
 // Otherwise, it adds the fact and returns whether the Store saved successfully.
-func (s *Store) BackUpMissingFacts(email, phone fact.Fact) error {
+func (s *Store) BackUpMissingFacts(username, email, phone fact.Fact) error {
 	s.mux.Lock()
 	defer s.mux.Unlock()
 
-	modifiedEmail, modifiedPhone := false, false
+	modified := false
 
 	// Handle email if it is not zero (empty string)
 	if !isFactZero(email) {
@@ -102,10 +138,12 @@ func (s *Store) BackUpMissingFacts(email, phone fact.Fact) error {
 			// If an email exists in memory, return an error
 			return errors.Errorf(factTypeExistsErr, email, fact.Email)
 		} else {
-			modifiedEmail = true
+			s.confirmedFacts[email] = struct{}{}
+			modified = true
 		}
 	}
 
+	// Handle phone if it is not an empty string
 	if !isFactZero(phone) {
 		// check if fact is expected type
 		if phone.T != fact.Phone {
@@ -117,19 +155,24 @@ func (s *Store) BackUpMissingFacts(email, phone fact.Fact) error {
 			// If a phone exists in memory, return an error
 			return errors.Errorf(factTypeExistsErr, phone, fact.Phone)
 		} else {
-			modifiedPhone = true
+			s.confirmedFacts[phone] = struct{}{}
+			modified = true
 		}
 	}
 
-	if modifiedPhone || modifiedEmail {
-		if modifiedEmail {
-			s.confirmedFacts[email] = struct{}{}
-		}
-
-		if modifiedPhone {
-			s.confirmedFacts[phone] = struct{}{}
+	if !isFactZero(username) {
+		// Check if fact type is already in map. You should not be able to
+		// overwrite your username.
+		if isFactTypeInMap(fact.Username, s.confirmedFacts) {
+			// If a username exists in memory, return an error
+			return errors.Errorf(factTypeExistsErr, username, fact.Username)
+		} else {
+			s.confirmedFacts[username] = struct{}{}
+			modified = true
 		}
+	}
 
+	if modified {
 		return s.saveConfirmedFacts()
 	}
 
diff --git a/ud/store/facts_test.go b/ud/store/facts_test.go
index 332b5289da335390d76df7f41adbb49f2bd2d975..7cdac5fd81667f6da119a0ce2fc6333bd8eef895 100644
--- a/ud/store/facts_test.go
+++ b/ud/store/facts_test.go
@@ -203,7 +203,12 @@ func TestStore_BackUpMissingFacts(t *testing.T) {
 		T:    fact.Phone,
 	}
 
-	err = expectedStore.BackUpMissingFacts(email, phone)
+	username := fact.Fact{
+		Fact: "admin",
+		T:    fact.Username,
+	}
+
+	err = expectedStore.BackUpMissingFacts(username, email, phone)
 	if err != nil {
 		t.Fatalf("BackUpMissingFacts() produced an error: %v", err)
 	}
@@ -238,18 +243,23 @@ func TestStore_BackUpMissingFacts_DuplicateFactType(t *testing.T) {
 		T:    fact.Phone,
 	}
 
-	err = expectedStore.BackUpMissingFacts(email, phone)
+	username := fact.Fact{
+		Fact: "admin",
+		T:    fact.Username,
+	}
+
+	err = expectedStore.BackUpMissingFacts(username, email, phone)
 	if err != nil {
 		t.Fatalf("BackUpMissingFacts() produced an error: %v", err)
 	}
 
-	err = expectedStore.BackUpMissingFacts(email, fact.Fact{})
+	err = expectedStore.BackUpMissingFacts(username, email, fact.Fact{})
 	if err == nil {
 		t.Fatalf("BackUpMissingFacts() should not allow backing up an "+
 			"email when an email has already been backed up: %v", err)
 	}
 
-	err = expectedStore.BackUpMissingFacts(fact.Fact{}, phone)
+	err = expectedStore.BackUpMissingFacts(username, fact.Fact{}, phone)
 	if err == nil {
 		t.Fatalf("BackUpMissingFacts() should not allow backing up a "+
 			"phone number when a phone number has already been backed up: %v", err)
@@ -272,7 +282,12 @@ func TestStore_GetFacts(t *testing.T) {
 
 	emptyFact := fact.Fact{}
 
-	err = testStore.BackUpMissingFacts(emailFact, emptyFact)
+	username := fact.Fact{
+		Fact: "admin",
+		T:    fact.Username,
+	}
+
+	err = testStore.BackUpMissingFacts(username, emailFact, emptyFact)
 	if err != nil {
 		t.Fatalf("Faild to add fact %v: %v", emailFact, err)
 	}
@@ -282,12 +297,12 @@ func TestStore_GetFacts(t *testing.T) {
 		T:    fact.Phone,
 	}
 
-	err = testStore.BackUpMissingFacts(emptyFact, phoneFact)
+	err = testStore.BackUpMissingFacts(emptyFact, emptyFact, phoneFact)
 	if err != nil {
 		t.Fatalf("Faild to add fact %v: %v", phoneFact, err)
 	}
 
-	expectedFacts := []fact.Fact{emailFact, phoneFact}
+	expectedFacts := []fact.Fact{username, emailFact, phoneFact}
 
 	receivedFacts := testStore.GetFacts()
 
@@ -318,10 +333,14 @@ func TestStore_GetFactStrings(t *testing.T) {
 		Fact: "josh@elixxir.io",
 		T:    fact.Email,
 	}
+	username := fact.Fact{
+		Fact: "admin",
+		T:    fact.Username,
+	}
 
 	emptyFact := fact.Fact{}
 
-	err = testStore.BackUpMissingFacts(emailFact, emptyFact)
+	err = testStore.BackUpMissingFacts(username, emailFact, emptyFact)
 	if err != nil {
 		t.Fatalf("Faild to add fact %v: %v", emailFact, err)
 	}
@@ -331,12 +350,12 @@ func TestStore_GetFactStrings(t *testing.T) {
 		T:    fact.Phone,
 	}
 
-	err = testStore.BackUpMissingFacts(emptyFact, phoneFact)
+	err = testStore.BackUpMissingFacts(emptyFact, emptyFact, phoneFact)
 	if err != nil {
 		t.Fatalf("Faild to add fact %v: %v", phoneFact, err)
 	}
 
-	expectedFacts := []string{emailFact.Stringify(), phoneFact.Stringify()}
+	expectedFacts := []string{username.Stringify(), emailFact.Stringify(), phoneFact.Stringify()}
 
 	receivedFacts := testStore.GetStringifiedFacts()
 	sort.SliceStable(receivedFacts, func(i, j int) bool {
diff --git a/ud/ud.go b/ud/ud.go
index 5fb27e619de9d5196b24022b35277f1f26c37837..fd4633483eb34b52a9106a00cbc3b8c342bec5fd 100644
--- a/ud/ud.go
+++ b/ud/ud.go
@@ -4,7 +4,6 @@ import (
 	"github.com/pkg/errors"
 	"gitlab.com/elixxir/crypto/contact"
 	"gitlab.com/xx_network/comms/connect"
-	"gitlab.com/xx_network/primitives/id"
 	"time"
 )
 
@@ -23,35 +22,26 @@ func (m *Manager) setUserDiscovery(cert,
 	params.AuthEnabled = false
 	params.SendTimeout = 20 * time.Second
 
-	udIdBytes, dhPubKeyBytes, err := contact.ReadContactFromFile(contactFile)
-	if err != nil {
-		return err
-	}
-
-	udID, err := id.Unmarshal(udIdBytes)
+	// Unmarshal the new contact
+	con, err := contact.Unmarshal(contactFile)
 	if err != nil {
 		return err
 	}
 
 	// Add a new host and return it if it does not already exist
-	host, err := m.comms.AddHost(udID, address,
+	host, err := m.comms.AddHost(con.ID, address,
 		cert, params)
 	if err != nil {
 		return errors.WithMessage(err, "User Discovery host object could "+
 			"not be constructed.")
 	}
 
-	dhPubKey := m.user.GetE2E().GetGroup().NewInt(1)
-	err = dhPubKey.UnmarshalJSON(dhPubKeyBytes)
-	if err != nil {
-		return err
-	}
-
+	// Set the user discovery object within the manager
 	m.ud = &userDiscovery{
 		host: host,
 		contact: contact.Contact{
-			ID:       udID,
-			DhPubKey: dhPubKey,
+			ID:       con.ID,
+			DhPubKey: con.DhPubKey,
 		},
 	}
 
diff --git a/xxdk/cmix.go b/xxdk/cmix.go
index 539c2610d55f23f3b26ed7aad70e0f563148cc39..a6a715d575e35422a7c224a8b78a5b579aa7a284 100644
--- a/xxdk/cmix.go
+++ b/xxdk/cmix.go
@@ -341,28 +341,28 @@ func (c *Cmix) GetErrorsChannel() <-chan interfaces.ClientError {
 //
 // Threads Started:
 //   - Network Follower (/network/follow.go)
-//   	tracks the network events and hands them off to workers for handling.
+//     tracks the network events and hands them off to workers for handling.
 //   - Historical Round Retrieval (/network/rounds/historical.go)
-// 		retrieves data about rounds that are too old to be stored by the client.
+// 	   retrieves data about rounds that are too old to be stored by the client.
 //	 - Message Retrieval Worker Group (/network/rounds/retrieve.go)
-//		requests all messages in a given round from the gateway of the last
-//		nodes.
+//	   requests all messages in a given round from the gateway of the last
+//	   nodes.
 //	 - Message Handling Worker Group (/network/message/handle.go)
-//		decrypts and partitions messages when signals via the Switchboard.
+//	   decrypts and partitions messages when signals via the Switchboard.
 //	 - Health Tracker (/network/health),
-//		via the network instance, tracks the state of the network.
+//	   via the network instance, tracks the state of the network.
 //	 - Garbled Messages (/network/message/garbled.go)
-//		can be signaled to check all recent messages that could be decoded. It
-//		uses a message store on disk for persistence.
+//	   can be signaled to check all recent messages that could be decoded. It
+//	   uses a message store on disk for persistence.
 //	 - Critical Messages (/network/message/critical.go)
-//		ensures all protocol layer mandatory messages are sent. It uses a
-//		message store on disk for persistence.
+//	   ensures all protocol layer mandatory messages are sent. It uses a
+//	   message store on disk for persistence.
 //	 - KeyExchange Trigger (/keyExchange/trigger.go)
-//		responds to sent rekeys and executes them.
+//	   responds to sent rekeys and executes them.
 //   - KeyExchange Confirm (/keyExchange/confirm.go)
-//		responds to confirmations of successful rekey operations.
+//	   responds to confirmations of successful rekey operations.
 //   - Auth Callback (/auth/callback.go)
-//      handles both auth confirm and requests.
+//     handles both auth confirm and requests.
 func (c *Cmix) StartNetworkFollower(timeout time.Duration) error {
 	jww.INFO.Printf(
 		"StartNetworkFollower() \n\tTransmissionID: %s \n\tReceptionID: %s",
@@ -375,7 +375,7 @@ func (c *Cmix) StartNetworkFollower(timeout time.Duration) error {
 // an error if the follower is in the wrong state to stop or if it fails to stop
 // it.
 //
-// if the network follower is running and this fails, the client object will
+// If the network follower is running and this fails, the client object will
 // most likely be in an unrecoverable state and need to be trashed.
 func (c *Cmix) StopNetworkFollower() error {
 	jww.INFO.Printf("StopNetworkFollower()")
diff --git a/xxdk/e2e.go b/xxdk/e2e.go
index 4c2b9e22ca2e800e77489f69690092e13f6db6bc..cd0d7f00c2931b192d20ba6206e4b21067e6740f 100644
--- a/xxdk/e2e.go
+++ b/xxdk/e2e.go
@@ -102,7 +102,7 @@ func loginLegacy(net *Cmix, callbacks AuthCallbacks,
 		return nil, err
 	}
 
-	rsaKey, err := identity.GetRSAPrivatePem()
+	rsaKey, err := identity.GetRSAPrivateKey()
 	if err != nil {
 		return nil, err
 	}
@@ -116,7 +116,7 @@ func login(net *Cmix, callbacks AuthCallbacks, identity ReceptionIdentity,
 	kv *versioned.KV, params E2EParams) (m *E2e, err error) {
 
 	// Verify the passed-in ReceptionIdentity matches its properties
-	privatePem, err := identity.GetRSAPrivatePem()
+	privatePem, err := identity.GetRSAPrivateKey()
 	if err != nil {
 		return nil, err
 	}
@@ -246,7 +246,7 @@ func (m *E2e) ConstructProtoUserFile() ([]byte, error) {
 
 	transIdentity := m.Cmix.GetTransmissionIdentity()
 	receptionIdentity := m.GetReceptionIdentity()
-	privatePem, err := receptionIdentity.GetRSAPrivatePem()
+	privatePem, err := receptionIdentity.GetRSAPrivateKey()
 	if err != nil {
 		return nil, err
 	}
diff --git a/xxdk/identity.go b/xxdk/identity.go
index 9d646785fd71c713398036fd4dc8ae66ecbbc447..1b2dd605dfa9f6bd63daeff49967dcdcd9fb459e 100644
--- a/xxdk/identity.go
+++ b/xxdk/identity.go
@@ -80,8 +80,8 @@ func (r ReceptionIdentity) GetDHKeyPrivate() (*cyclic.Int, error) {
 	return dhKeyPriv, err
 }
 
-// GetRSAPrivatePem returns the RSAPrivatePem.
-func (r ReceptionIdentity) GetRSAPrivatePem() (*rsa.PrivateKey, error) {
+// GetRSAPrivateKey returns the RSAPrivatePem.
+func (r ReceptionIdentity) GetRSAPrivateKey() (*rsa.PrivateKey, error) {
 	return rsa.LoadPrivateKeyFromPem(r.RSAPrivatePem)
 }