diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index ec567c930ca36acff12a23f223f1bcc45dd5e4d6..939c429d956d80ba7afb9d650f9b4cacf6d4df3d 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -4,7 +4,7 @@ variables:
   REPO_DIR: gitlab.com/elixxir
   REPO_NAME: client
   DOCKER_IMAGE: elixxirlabs/cuda-go:latest
-  MIN_CODE_COVERAGE: "75"
+  MIN_CODE_COVERAGE: "74"
 
 before_script:
   ##
@@ -21,7 +21,7 @@ before_script:
   - ssh-keyscan -t rsa gitlab.com > ~/.ssh/known_hosts
   - git config --global url."git@gitlab.com:".insteadOf "https://gitlab.com/"
   - export PATH=$HOME/go/bin:$PATH
-  - export GOPRIVATE=gitlab.com/elixxir/*
+  - export GOPRIVATE=gitlab.com/elixxir/*,gitlab.com/xx_network/*
 
 
 stages:
diff --git a/Makefile b/Makefile
index a2a6eef59836c1a6ee6de3b4447ca40ae9a943d9..b8d85b2d4b167ff0347162df66cc69dda12755e3 100644
--- a/Makefile
+++ b/Makefile
@@ -1,11 +1,18 @@
-.PHONY: update master release setup update_master update_release build
+.PHONY: update master release setup update_master update_release build clean version
 
 setup:
 	git config --global --add url."git@gitlab.com:".insteadOf "https://gitlab.com/"
 
-update:
+version:
+	go run main.go generate
+	sed -i.bak 's/package\ cmd/package\ globals/g' version_vars.go
+	mv version_vars.go globals/version_vars.go
+
+clean:
 	rm -rf vendor/
 	go mod vendor
+
+update:
 	-GOFLAGS="" go get -u all
 
 build:
@@ -16,12 +23,14 @@ update_release:
 	GOFLAGS="" go get -u gitlab.com/elixxir/primitives@release
 	GOFLAGS="" go get -u gitlab.com/elixxir/crypto@release
 	GOFLAGS="" go get -u gitlab.com/elixxir/comms@release
+	GOFLAGS="" go get -u gitlab.com/xx_network/comms@release
 
 update_master:
 	GOFLAGS="" go get -u gitlab.com/elixxir/primitives@master
 	GOFLAGS="" go get -u gitlab.com/elixxir/crypto@master
 	GOFLAGS="" go get -u gitlab.com/elixxir/comms@master
+	GOFLAGS="" go get -u gitlab.com/xx_network/comms@master
 
-master: update update_master build
+master: clean update_master build version
 
-release: update update_release build
+release: clean update_release build version
diff --git a/api/client.go b/api/client.go
index b5639a701870805f53a884949d3cc5306f83f61e..820c3457a9e3ffc7b5627b867580592cd8df483e 100644
--- a/api/client.go
+++ b/api/client.go
@@ -23,7 +23,6 @@ import (
 	"gitlab.com/elixxir/client/parse"
 	"gitlab.com/elixxir/client/rekey"
 	"gitlab.com/elixxir/client/user"
-	"gitlab.com/elixxir/comms/connect"
 	"gitlab.com/elixxir/crypto/cyclic"
 	"gitlab.com/elixxir/crypto/large"
 	"gitlab.com/elixxir/crypto/signature/rsa"
@@ -31,6 +30,7 @@ import (
 	"gitlab.com/elixxir/primitives/id"
 	"gitlab.com/elixxir/primitives/ndf"
 	"gitlab.com/elixxir/primitives/switchboard"
+	"gitlab.com/xx_network/comms/connect"
 	goio "io"
 	"strings"
 	"testing"
@@ -121,7 +121,7 @@ func newClient(s globals.Storage, locA, locB string, ndfJSON *ndf.NetworkDefinit
 }
 
 // LoadSession loads the session object for the UID
-func (cl *Client) Login(password string) (*id.User, error) {
+func (cl *Client) Login(password string) (*id.ID, error) {
 
 	var session user.Session
 	var err error
@@ -158,7 +158,7 @@ func (cl *Client) Login(password string) (*id.User, error) {
 	}
 
 	cl.session = session
-	newRm, err := io.NewReceptionManager(cl.rekeyChan, cl.session.GetCurrentUser().User.String(),
+	newRm, err := io.NewReceptionManager(cl.rekeyChan, cl.session.GetCurrentUser().User,
 		rsa.CreatePrivateKeyPem(cl.session.GetRSAPrivateKey()),
 		rsa.CreatePublicKeyPem(cl.session.GetRSAPublicKey()),
 		cl.session.GetSalt())
@@ -306,14 +306,19 @@ var sessionFileError = errors.New("Session file cannot be loaded and " +
 	"is possibly corrupt. Please contact support@xxmessenger.io")
 
 func (cl *Client) InitListeners() error {
-	transmitGateway := id.NewNodeFromBytes(cl.ndf.Nodes[0].ID).NewGateway()
-	transmissionHost, ok := cl.receptionManager.Comms.GetHost(transmitGateway.String())
+	transmitGateway, err := id.Unmarshal(cl.ndf.Gateways[0].ID)
+	if err != nil {
+		globals.Log.DEBUG.Printf("%s: Gateways are: %+v", err.Error(),
+			cl.ndf.Gateways)
+		return err
+	}
+	transmissionHost, ok := cl.receptionManager.Comms.GetHost(transmitGateway)
 	if !ok {
 		return errors.New("Failed to retrieve host for transmission")
 	}
 
 	// Initialize UDB and nickname "bot" stuff here
-	bots.InitBots(cl.session, cl.receptionManager, cl.topology, id.NewUserFromBytes(cl.ndf.UDB.ID), transmissionHost)
+	bots.InitBots(cl.session, cl.receptionManager, cl.topology, transmissionHost)
 	// Initialize Rekey listeners
 	rekey.InitRekey(cl.session, cl.receptionManager, cl.topology, transmissionHost, cl.rekeyChan)
 	return nil
@@ -325,8 +330,11 @@ func (cl *Client) StartMessageReceiver(callback func(error)) error {
 	pollWaitTimeMillis := 500 * time.Millisecond
 	// TODO Don't start the message receiver if it's already started.
 	// Should be a pretty rare occurrence except perhaps for mobile.
-	receptionGateway := id.NewNodeFromBytes(cl.ndf.Nodes[len(cl.ndf.Nodes)-1].ID).NewGateway()
-	receptionHost, ok := cl.receptionManager.Comms.GetHost(receptionGateway.String())
+	receptionGateway, err := id.Unmarshal(cl.ndf.Gateways[len(cl.ndf.Gateways)-1].ID)
+	if err != nil {
+		return err
+	}
+	receptionHost, ok := cl.receptionManager.Comms.GetHost(receptionGateway)
 	if !ok {
 		return errors.New("Failed to retrieve host for transmission")
 	}
@@ -349,8 +357,12 @@ func (cl *Client) StartMessageReceiver(callback func(error)) error {
 
 // Default send function, can be overridden for testing
 func (cl *Client) Send(message parse.MessageInterface) error {
-	transmitGateway := id.NewNodeFromBytes(cl.ndf.Nodes[0].ID).NewGateway()
-	host, ok := cl.receptionManager.Comms.GetHost(transmitGateway.String())
+	transmitGateway, err := id.Unmarshal(cl.ndf.Gateways[0].ID)
+	if err != nil {
+		return err
+	}
+	transmitGateway.SetType(id.Gateway)
+	host, ok := cl.receptionManager.Comms.GetHost(transmitGateway)
 	if !ok {
 		return errors.New("Failed to retrieve host for transmission")
 	}
@@ -377,7 +389,7 @@ func (cl *Client) SetRateLimiting(limit uint32) {
 	cl.receptionManager.SetRateLimit(time.Duration(limit) * time.Millisecond)
 }
 
-func (cl *Client) Listen(user *id.User, messageType int32, newListener switchboard.Listener) string {
+func (cl *Client) Listen(user *id.ID, messageType int32, newListener switchboard.Listener) string {
 	listenerId := cl.session.GetSwitchboard().
 		Register(user, messageType, newListener)
 	globals.Log.INFO.Printf("Listening now: user %v, message type %v, id %v",
@@ -393,7 +405,7 @@ func (cl *Client) GetSwitchboard() *switchboard.Switchboard {
 	return cl.session.GetSwitchboard()
 }
 
-func (cl *Client) GetCurrentUser() *id.User {
+func (cl *Client) GetCurrentUser() *id.ID {
 	return cl.session.GetCurrentUser().User
 }
 
@@ -467,7 +479,7 @@ type NickLookupCallback interface {
 	Callback(nick string, err error)
 }
 
-func (cl *Client) DeleteUser(u *id.User) (string, error) {
+func (cl *Client) DeleteUser(u *id.ID) (string, error) {
 
 	//delete from session
 	v, err1 := cl.session.DeleteContact(u)
@@ -499,7 +511,7 @@ func (cl *Client) DeleteUser(u *id.User) (string, error) {
 // Nickname lookup API
 // Non-blocking, once the API call completes, the callback function
 // passed as argument is called
-func (cl *Client) LookupNick(user *id.User,
+func (cl *Client) LookupNick(user *id.ID,
 	cb NickLookupCallback) {
 	go func() {
 		nick, err := bots.LookupNick(user)
diff --git a/api/client_test.go b/api/client_test.go
index b77ea2c6fe1709a1b115a8e8a8166c35df98b2cd..bebd23da966c191ed8c45900a0626f25c54b4932 100644
--- a/api/client_test.go
+++ b/api/client_test.go
@@ -128,8 +128,8 @@ func TestParse(t *testing.T) {
 	ms := parse.Message{}
 	ms.Body = []byte{0, 1, 2}
 	ms.MessageType = int32(cmixproto.Type_NO_TYPE)
-	ms.Receiver = id.ZeroID
-	ms.Sender = id.ZeroID
+	ms.Receiver = &id.ZeroUser
+	ms.Sender = &id.ZeroUser
 
 	messagePacked := ms.Pack()
 
@@ -158,8 +158,8 @@ func TestRegisterUserE2E(t *testing.T) {
 
 	rng := csprng.NewSystemRNG()
 	cmixGrp, e2eGrp := getGroups()
-	userID := id.NewUserFromUint(18, t)
-	partner := id.NewUserFromUint(14, t)
+	userID := id.NewIdFromUInt(18, id.User, t)
+	partner := id.NewIdFromUInt(14, id.User, t)
 
 	myPrivKeyCyclic := e2eGrp.RandomCoprime(e2eGrp.NewMaxInt())
 	myPubKeyCyclic := e2eGrp.ExpG(myPrivKeyCyclic, e2eGrp.NewMaxInt())
@@ -247,8 +247,8 @@ func TestRegisterUserE2E_CheckAllKeys(t *testing.T) {
 	}
 
 	cmixGrp, e2eGrp := getGroups()
-	userID := id.NewUserFromUint(18, t)
-	partner := id.NewUserFromUint(14, t)
+	userID := id.NewIdFromUInt(18, id.User, t)
+	partner := id.NewIdFromUInt(14, id.User, t)
 
 	rng := csprng.NewSystemRNG()
 	myPrivKeyCyclic := e2eGrp.RandomCoprime(e2eGrp.NewMaxInt())
@@ -401,7 +401,7 @@ func TestClient_precannedRegister(t *testing.T) {
 		t.Error(err)
 	}
 
-	_, _, _, err = testClient.precannedRegister("UAV6IWD6")
+	_, _, _, err = testClient.precannedRegister("WTROXJ33")
 	if err != nil {
 		t.Errorf("Error during precannedRegister: %+v", err)
 	}
@@ -428,7 +428,7 @@ func TestClient_sendRegistrationMessage(t *testing.T) {
 	privateKeyRSA, _ := rsa.GenerateKey(rng, TestKeySize)
 	publicKeyRSA := rsa.PublicKey{PublicKey: privateKeyRSA.PublicKey}
 
-	_, err = testClient.sendRegistrationMessage("UAV6IWD6", &publicKeyRSA)
+	_, err = testClient.sendRegistrationMessage("WTROXJ33", &publicKeyRSA)
 	if err != nil {
 		t.Errorf("Error during sendRegistrationMessage: %+v", err)
 	}
@@ -462,7 +462,11 @@ func TestClient_requestNonce(t *testing.T) {
 		t.Errorf("Unable to generate salt! %s", err)
 	}
 
-	gwID := id.NewNodeFromBytes(testClient.ndf.Nodes[0].ID).NewGateway()
+	gwID, err := id.Unmarshal(testClient.ndf.Gateways[0].ID)
+	if err != nil {
+		t.Fatal(err)
+	}
+	gwID.SetType(id.Gateway)
 	_, _, err = testClient.requestNonce(salt, []byte("test"), publicKeyDH, &publicKeyRSA, privateKeyRSA, gwID)
 	if err != nil {
 		t.Errorf("Error during requestNonce: %+v", err)
@@ -485,7 +489,11 @@ func TestClient_confirmNonce(t *testing.T) {
 	}
 	rng := csprng.NewSystemRNG()
 	privateKeyRSA, _ := rsa.GenerateKey(rng, TestKeySize)
-	gwID := id.NewNodeFromBytes(testClient.ndf.Nodes[0].ID).NewGateway()
+	gwID, err := id.Unmarshal(testClient.ndf.Gateways[0].ID)
+	if err != nil {
+		t.Fatal(err)
+	}
+	gwID.SetType(id.Gateway)
 	err = testClient.confirmNonce([]byte("user"), []byte("test"), privateKeyRSA, gwID)
 	if err != nil {
 		t.Errorf("Error during confirmNonce: %+v", err)
@@ -567,7 +575,8 @@ func TestClient_LogoutHappyPath(t *testing.T) {
 	d := DummyStorage{LocationA: "Blah", StoreA: []byte{'a', 'b', 'c'}}
 	tc, _ := NewClient(&d, "", "", def)
 
-	tc.receptionManager, _ = io.NewReceptionManager(tc.rekeyChan, "kk", nil, nil, nil)
+	uid := id.NewIdFromString("kk", id.User, t)
+	tc.receptionManager, _ = io.NewReceptionManager(tc.rekeyChan, uid, nil, nil, nil)
 
 	err := tc.InitNetwork()
 	if err != nil {
@@ -642,7 +651,8 @@ func TestClient_LogoutTimeout(t *testing.T) {
 	d := DummyStorage{LocationA: "Blah", StoreA: []byte{'a', 'b', 'c'}}
 	tc, _ := NewClient(&d, "", "", def)
 
-	tc.receptionManager, _ = io.NewReceptionManager(tc.rekeyChan, "kk", nil, nil, nil)
+	uid := id.NewIdFromString("kk", id.User, t)
+	tc.receptionManager, _ = io.NewReceptionManager(tc.rekeyChan, uid, nil, nil, nil)
 
 	err := tc.InitNetwork()
 	if err != nil {
@@ -705,7 +715,8 @@ func TestClient_LogoutAndLoginAgain(t *testing.T) {
 	storage := globals.RamStorage{}
 	tc, initialId := NewClient(&storage, "", "", def)
 
-	tc.receptionManager, _ = io.NewReceptionManager(tc.rekeyChan, "kk", nil, nil, nil)
+	uid := id.NewIdFromString("kk", id.User, t)
+	tc.receptionManager, _ = io.NewReceptionManager(tc.rekeyChan, uid, nil, nil, nil)
 
 	err := tc.InitNetwork()
 	if err != nil {
diff --git a/api/connect.go b/api/connect.go
index 5feebdb09b2262f04e04a205ce7558b1d92ba126..578ee3e05f6250bf69fc5d40ad8085317eb7131a 100644
--- a/api/connect.go
+++ b/api/connect.go
@@ -11,9 +11,10 @@ import (
 	"github.com/pkg/errors"
 	"gitlab.com/elixxir/client/globals"
 	"gitlab.com/elixxir/client/io"
-	"gitlab.com/elixxir/comms/connect"
 	"gitlab.com/elixxir/primitives/id"
 	"gitlab.com/elixxir/primitives/ndf"
+	"gitlab.com/elixxir/primitives/version"
+	"gitlab.com/xx_network/comms/connect"
 )
 
 var ErrNoPermissioning = errors.New("No Permissioning In NDF")
@@ -24,7 +25,8 @@ var ErrNoPermissioning = errors.New("No Permissioning In NDF")
 func (cl *Client) InitNetwork() error {
 	var err error
 	if cl.receptionManager == nil {
-		cl.receptionManager, err = io.NewReceptionManager(cl.rekeyChan, "client", nil, nil, nil)
+		// Start reception manager with a dummy user, so we can connect to things
+		cl.receptionManager, err = io.NewReceptionManager(cl.rekeyChan, &id.DummyUser, nil, nil, nil)
 		if err != nil {
 			return errors.Wrap(err, "Failed to create reception manager")
 		}
@@ -53,7 +55,10 @@ func (cl *Client) InitNetwork() error {
 	}
 
 	// InitNetwork to nodes
-	cl.topology = BuildNodeTopology(cl.ndf)
+	cl.topology, err = BuildNodeTopology(cl.ndf)
+	if err != nil {
+		return err
+	}
 
 	err = addNotificationBotHost(cl.receptionManager, cl.ndf)
 	if err != nil {
@@ -66,7 +71,7 @@ func (cl *Client) InitNetwork() error {
 // AddNotificationBotHost adds notification bot as a host within the reception manager
 func addNotificationBotHost(rm *io.ReceptionManager, definition *ndf.NetworkDefinition) error {
 
-	err := addHost(rm, id.NOTIFICATION_BOT, definition.Notification.Address,
+	err := addHost(rm, &id.NotificationBot, definition.Notification.Address,
 		definition.Notification.TlsCertificate, false, true)
 	if err != nil {
 		return errors.Errorf("Failed to connect to notification bot at %+v",
@@ -77,14 +82,18 @@ func addNotificationBotHost(rm *io.ReceptionManager, definition *ndf.NetworkDefi
 
 // BuildNodeTopology is a helper function which goes through the ndf and
 // builds a circuit for all the node's in the definition
-func BuildNodeTopology(definition *ndf.NetworkDefinition) *connect.Circuit {
+func BuildNodeTopology(definition *ndf.NetworkDefinition) (*connect.Circuit, error) {
 	//build the topology
-	nodeIDs := make([]*id.Node, len(definition.Nodes))
+	nodeIDs := make([]*id.ID, len(definition.Nodes))
+	var err error
 	for i, node := range definition.Nodes {
-		nodeIDs[i] = id.NewNodeFromBytes(node.ID)
+		nodeIDs[i], err = id.Unmarshal(node.ID)
+		if err != nil {
+			return nil, err
+		}
 	}
 
-	return connect.NewCircuit(nodeIDs)
+	return connect.NewCircuit(nodeIDs), nil
 }
 
 // DisableTls disables tls for communications
@@ -119,10 +128,19 @@ func (cl *Client) setupPermissioning() error {
 	// Only check the version if we got a remote version
 	// The remote version won't have been populated if we didn't connect to permissioning
 	if cl.GetRegistrationVersion() != "" {
-		ok, err := globals.CheckVersion(cl.GetRegistrationVersion())
+		// Parse client version
+		clientVersion, err := version.ParseVersion(globals.SEMVER)
+		if err != nil {
+			return err
+		}
+
+		// Parse the permissioning version
+		regVersion, err := version.ParseVersion(cl.GetRegistrationVersion())
 		if err != nil {
 			return err
 		}
+
+		ok := version.IsCompatible(regVersion, clientVersion)
 		if !ok {
 			return errors.Errorf("Couldn't connect to gateways: Versions"+
 				" incompatible; Local version: %v; remote version: %v", globals.SEMVER,
@@ -148,8 +166,18 @@ func addGatewayHosts(rm *io.ReceptionManager, definition *ndf.NetworkDefinition)
 	// connect to all gateways
 	var errs error = nil
 	for i, gateway := range definition.Gateways {
-		gwID := id.NewNodeFromBytes(definition.Nodes[i].ID).NewGateway()
-		err := addHost(rm, gwID.String(), gateway.Address, gateway.TlsCertificate, false, false)
+		gwID, err := id.Unmarshal(definition.Gateways[i].ID)
+		if err != nil {
+			err = errors.Errorf("Failed to unmarshal gateway ID %s at index %v: %+v",
+				definition.Gateways[i].ID, i, err)
+			if errs != nil {
+				errs = err
+			} else {
+				errs = errors.Wrap(errs, err.Error())
+			}
+			continue
+		}
+		err = addHost(rm, gwID, gateway.Address, gateway.TlsCertificate, false, false)
 		if err != nil {
 			err = errors.Errorf("Failed to create host for gateway %s at %s: %+v",
 				gwID.String(), gateway.Address, err)
@@ -163,7 +191,7 @@ func addGatewayHosts(rm *io.ReceptionManager, definition *ndf.NetworkDefinition)
 	return errs
 }
 
-func addHost(rm *io.ReceptionManager, id, address, cert string, disableTimeout, enableAuth bool) error {
+func addHost(rm *io.ReceptionManager, id *id.ID, address, cert string, disableTimeout, enableAuth bool) error {
 	var creds []byte
 	if cert != "" && rm.Tls {
 		creds = []byte(cert)
@@ -180,7 +208,7 @@ func addHost(rm *io.ReceptionManager, id, address, cert string, disableTimeout,
 // to permissioning is needed
 func addPermissioningHost(rm *io.ReceptionManager, definition *ndf.NetworkDefinition) error {
 	if definition.Registration.Address != "" {
-		err := addHost(rm, PermissioningAddrID, definition.Registration.Address,
+		err := addHost(rm, &id.Permissioning, definition.Registration.Address,
 			definition.Registration.TlsCertificate, false, false)
 		if err != nil {
 			return errors.New(fmt.Sprintf(
diff --git a/api/mockserver.go b/api/mockserver.go
index c77de57be32e75d6b8c73fbb5c4d1d2a49cde249..ca0d86e8e580d6c0890331111940f2e68bfea0df 100644
--- a/api/mockserver.go
+++ b/api/mockserver.go
@@ -14,7 +14,6 @@ import (
 	"gitlab.com/elixxir/client/cmixproto"
 	"gitlab.com/elixxir/client/globals"
 	"gitlab.com/elixxir/client/parse"
-	"gitlab.com/elixxir/comms/connect"
 	pb "gitlab.com/elixxir/comms/mixmessages"
 	"gitlab.com/elixxir/crypto/cyclic"
 	"gitlab.com/elixxir/crypto/e2e"
@@ -22,6 +21,8 @@ import (
 	"gitlab.com/elixxir/primitives/format"
 	"gitlab.com/elixxir/primitives/id"
 	"gitlab.com/elixxir/primitives/ndf"
+	"gitlab.com/xx_network/comms/connect"
+	"gitlab.com/xx_network/comms/messages"
 	"sync"
 	"time"
 )
@@ -37,15 +38,15 @@ const BatchSize = 10
 // easy to use from Go
 type APIMessage struct {
 	Payload     []byte
-	SenderID    *id.User
-	RecipientID *id.User
+	SenderID    *id.ID
+	RecipientID *id.ID
 }
 
-func (m APIMessage) GetSender() *id.User {
+func (m APIMessage) GetSender() *id.ID {
 	return m.SenderID
 }
 
-func (m APIMessage) GetRecipient() *id.User {
+func (m APIMessage) GetRecipient() *id.ID {
 	return m.RecipientID
 }
 
@@ -141,7 +142,7 @@ type MockRegistration struct {
 	//LastReceivedMessage pb.CmixMessage
 }
 
-func (s *MockRegistration) RegisterNode(ID []byte,
+func (s *MockRegistration) RegisterNode(ID *id.ID,
 	NodeTLSCert, GatewayTLSCert, RegistrationCode, Addr, Addr2 string) error {
 	return nil
 }
@@ -153,7 +154,7 @@ func (s *MockRegistration) PollNdf(clientNdfHash []byte, auth *connect.Auth) ([]
 	return ndfJson, nil
 }
 
-func (s *MockRegistration) Poll(*pb.PermissioningPoll, *connect.Auth) (*pb.PermissionPollResponse, error) {
+func (s *MockRegistration) Poll(*pb.PermissioningPoll, *connect.Auth, string) (*pb.PermissionPollResponse, error) {
 	return nil, nil
 }
 
@@ -167,6 +168,10 @@ func (s *MockRegistration) GetCurrentClientVersion() (version string, err error)
 	return globals.SEMVER, nil
 }
 
+func (i *MockRegistration) CheckRegistration(msg *pb.RegisteredNodeCheck) (*pb.RegisteredNodeConfirmation, error) {
+	return nil, nil
+}
+
 //registration handler for getUpdatedNDF error case
 type MockPermNdfErrorCase struct {
 }
@@ -265,7 +270,7 @@ type GatewayHandler struct {
 	LastReceivedMessage pb.Slot
 }
 
-func (m *GatewayHandler) PollForNotifications(auth *connect.Auth) ([]string, error) {
+func (m *GatewayHandler) PollForNotifications(auth *connect.Auth) ([]*id.ID, error) {
 
 	return nil, nil
 }
@@ -276,13 +281,13 @@ func (m *GatewayHandler) Poll(*pb.GatewayPoll) (*pb.GatewayPollResponse, error)
 
 // Returns message contents for MessageID, or a null/randomized message
 // if that ID does not exist of the same size as a regular message
-func (m *GatewayHandler) GetMessage(userId *id.User,
+func (m *GatewayHandler) GetMessage(userId *id.ID,
 	msgId, ipaddr string) (*pb.Slot, error) {
 	return &pb.Slot{}, nil
 }
 
 // Return any MessageIDs in the globals for this User
-func (m *GatewayHandler) CheckMessages(userId *id.User,
+func (m *GatewayHandler) CheckMessages(userId *id.ID,
 	messageID, ipAddress string) ([]string, error) {
 	return make([]string, 0), nil
 }
@@ -296,7 +301,7 @@ func (m *GatewayHandler) PutMessage(msg *pb.Slot, ipaddr string) error {
 
 func (m *GatewayHandler) ConfirmNonce(message *pb.RequestRegistrationConfirmation, ipaddr string) (*pb.RegistrationConfirmation, error) {
 	regConfirmation := &pb.RegistrationConfirmation{
-		ClientSignedByServer: &pb.RSASignature{},
+		ClientSignedByServer: &messages.RSASignature{},
 	}
 
 	return regConfirmation, nil
@@ -315,7 +320,7 @@ type GatewayHandlerMultipleMessages struct {
 	LastReceivedMessage []pb.Slot
 }
 
-func (m *GatewayHandlerMultipleMessages) GetMessage(userId *id.User,
+func (m *GatewayHandlerMultipleMessages) GetMessage(userId *id.ID,
 	msgId, ipaddr string) (*pb.Slot, error) {
 	msg := []byte("Hello")
 	payload, err := e2e.Pad(msg, format.PayloadLen)
@@ -328,7 +333,7 @@ func (m *GatewayHandlerMultipleMessages) GetMessage(userId *id.User,
 	}, nil
 }
 
-func (m *GatewayHandlerMultipleMessages) PollForNotifications(auth *connect.Auth) ([]string, error) {
+func (m *GatewayHandlerMultipleMessages) PollForNotifications(auth *connect.Auth) ([]*id.ID, error) {
 	return nil, nil
 }
 
@@ -337,7 +342,7 @@ func (s *GatewayHandlerMultipleMessages) Poll(*pb.GatewayPoll) (*pb.GatewayPollR
 }
 
 // Return any MessageIDs in the globals for this User
-func (m *GatewayHandlerMultipleMessages) CheckMessages(userId *id.User,
+func (m *GatewayHandlerMultipleMessages) CheckMessages(userId *id.ID,
 	messageID, ipaddr string) ([]string, error) {
 	msgs := []string{"a", "b", "c", "d", "e", "f", "g"}
 	return msgs, nil
diff --git a/api/mockserver_test.go b/api/mockserver_test.go
index a407aa3b8b18e1ebaa1e55b60721d850b0e5fe3f..86a26bf01b7db1641ea79003bdfbb33e6782a86f 100644
--- a/api/mockserver_test.go
+++ b/api/mockserver_test.go
@@ -38,7 +38,7 @@ var RegComms *registration.Comms
 var NDFErrorReg = MockPermNdfErrorCase{}
 var ErrorDef *ndf.NetworkDefinition
 
-const ValidRegCode = "UAV6IWD6"
+const ValidRegCode = "WTROXJ33"
 const InvalidRegCode = "INVALID_REG_CODE_"
 
 var RegGWHandlers [3]*GatewayHandler = [NumGWs]*GatewayHandler{
@@ -67,11 +67,13 @@ func TestClient_StartMessageReceiver_MultipleMessages(t *testing.T) {
 	// Initialize client with dummy storage
 	testDef := getNDF()
 	for i := 0; i < NumNodes; i++ {
+		gwID := id.NewIdFromString("testGateway", id.Gateway, t)
 		gw := ndf.Gateway{
 			Address: string(fmtAddress(GWErrorPort + i)),
+			ID:      gwID.Marshal(),
 		}
 		testDef.Gateways = append(testDef.Gateways, gw)
-		GWErrComms[i] = gateway.StartGateway("testGateway", gw.Address,
+		GWErrComms[i] = gateway.StartGateway(gwID, gw.Address,
 			&GatewayHandlerMultipleMessages{}, nil, nil)
 
 	}
@@ -163,7 +165,7 @@ func TestRegister_ValidPrecannedRegCodeReturnsZeroID(t *testing.T) {
 		t.Errorf("Registration failed: %s", err.Error())
 	}
 
-	if *regRes == *id.ZeroID {
+	if regRes.Cmp(&id.ZeroUser) {
 		t.Errorf("Invalid registration number received: %v", *regRes)
 	}
 	disconnectServers()
@@ -254,8 +256,8 @@ func TestRegister_DeletedUserReturnsErr(t *testing.T) {
 	}
 
 	// ...
-	tempUser, _ := user.Users.GetUser(id.NewUserFromUint(5, t))
-	user.Users.DeleteUser(id.NewUserFromUint(5, t))
+	tempUser, _ := user.Users.GetUser(id.NewIdFromUInt(5, id.User, t))
+	user.Users.DeleteUser(id.NewIdFromUInt(5, id.User, t))
 	err = client.GenerateKeys(nil, "")
 	if err != nil {
 		t.Errorf("Could not generate Keys: %+v", err)
@@ -327,7 +329,7 @@ func TestSend(t *testing.T) {
 	// Test send with invalid sender ID
 	err = client.Send(
 		APIMessage{
-			SenderID:    id.NewUserFromUint(12, t),
+			SenderID:    id.NewIdFromUInt(12, id.User, t),
 			Payload:     []byte("test"),
 			RecipientID: userID,
 		},
@@ -447,10 +449,11 @@ func testMainWrapper(m *testing.M) int {
 	}
 
 	for i := 0; i < NumNodes; i++ {
-		nIdBytes := make([]byte, id.NodeIdLen)
-		nIdBytes[0] = byte(i)
+		nId := new(id.ID)
+		nId[0] = byte(i)
+		nId.SetType(id.Node)
 		n := ndf.Node{
-			ID: nIdBytes,
+			ID: nId[:],
 		}
 		def.Nodes = append(def.Nodes, n)
 		ErrorDef.Nodes = append(ErrorDef.Nodes, n)
@@ -512,22 +515,28 @@ func getNDF() *ndf.NetworkDefinition {
 }
 
 func startServers() {
-	//func StartRegistrationServer(id, localServer string, handler Handler, certPEMblock, keyPEMblock []byte) *Comms {
-	RegComms = registration.StartRegistrationServer("testServer", def.Registration.Address, &RegHandler, nil, nil)
+	regId := new(id.ID)
+	copy(regId[:], "testServer")
+	regId.SetType(id.Generic)
+	RegComms = registration.StartRegistrationServer(regId, def.Registration.Address, &RegHandler, nil, nil)
 	def.Gateways = make([]ndf.Gateway, 0)
 
 	//Start up gateways
 	for i, handler := range RegGWHandlers {
 
+		gwID := new(id.ID)
+		copy(gwID[:], "testGateway")
+		gwID.SetType(id.Gateway)
 		gw := ndf.Gateway{
 			Address: fmtAddress(GWsStartPort + i),
+			ID:      gwID.Marshal(),
 		}
 
 		def.Gateways = append(def.Gateways, gw)
-		GWComms[i] = gateway.StartGateway("testGateway", gw.Address, handler, nil, nil)
+		GWComms[i] = gateway.StartGateway(gwID, gw.Address, handler, nil, nil)
 	}
 
-	NotificationBotComms = notificationBot.StartNotificationBot(id.NOTIFICATION_BOT, def.Notification.Address, &NotificationBotHandler, nil, nil)
+	NotificationBotComms = notificationBot.StartNotificationBot(&id.NotificationBot, def.Notification.Address, &NotificationBotHandler, nil, nil)
 
 }
 
diff --git a/api/notifications.go b/api/notifications.go
index 663daa315379e88496d186f027377978dda0c80f..5d27bf73d22387c378b66f738478dfb927c6ba0f 100644
--- a/api/notifications.go
+++ b/api/notifications.go
@@ -10,7 +10,7 @@ import (
 // is registering for notifications
 func (cl *Client) RegisterForNotifications(notificationToken []byte) error {
 	// Pull the host from the manage
-	notificationBotHost, ok := cl.receptionManager.Comms.GetHost(id.NOTIFICATION_BOT)
+	notificationBotHost, ok := cl.receptionManager.Comms.GetHost(&id.NotificationBot)
 	if !ok {
 		return errors.New("Failed to retrieve host for notification bot")
 	}
@@ -34,7 +34,7 @@ func (cl *Client) RegisterForNotifications(notificationToken []byte) error {
 // no longer wants to be registered for notifications
 func (cl *Client) UnregisterForNotifications() error {
 	// Pull the host from the manage
-	notificationBotHost, ok := cl.receptionManager.Comms.GetHost(id.NOTIFICATION_BOT)
+	notificationBotHost, ok := cl.receptionManager.Comms.GetHost(&id.NotificationBot)
 	if !ok {
 		return errors.New("Failed to retrieve host for notification bot")
 	}
diff --git a/api/private.go b/api/private.go
index 2e6dcb6a9e950a3d6e8a5fc69dc9aecb62dfaa05..bc76f0f36d6f02080c16698ceab5c73a6f6b7caa 100644
--- a/api/private.go
+++ b/api/private.go
@@ -20,23 +20,24 @@ import (
 	"gitlab.com/elixxir/crypto/diffieHellman"
 	"gitlab.com/elixxir/crypto/e2e"
 	"gitlab.com/elixxir/crypto/large"
-	"gitlab.com/elixxir/crypto/registration"
 	"gitlab.com/elixxir/crypto/signature/rsa"
+	"gitlab.com/elixxir/crypto/xx"
 	"gitlab.com/elixxir/primitives/id"
 	"gitlab.com/elixxir/primitives/ndf"
+	"gitlab.com/xx_network/comms/messages"
 )
 
 const PermissioningAddrID = "Permissioning"
 
 // precannedRegister is a helper function for Register
 // It handles the precanned registration case
-func (cl *Client) precannedRegister(registrationCode string) (*user.User, *id.User, map[id.Node]user.NodeKeys, error) {
+func (cl *Client) precannedRegister(registrationCode string) (*user.User, *id.ID, map[id.ID]user.NodeKeys, error) {
 	var successLook bool
-	var UID *id.User
+	var UID *id.ID
 	var u *user.User
 	var err error
 
-	nk := make(map[id.Node]user.NodeKeys)
+	nk := make(map[id.ID]user.NodeKeys)
 
 	UID, successLook = user.Users.LookupUser(registrationCode)
 
@@ -82,7 +83,7 @@ func (cl *Client) sendRegistrationMessage(registrationCode string,
 
 	regValidationSignature := make([]byte, 0)
 	// Send registration code and public key to RegistrationServer
-	host, ok := cl.receptionManager.Comms.GetHost(PermissioningAddrID)
+	host, ok := cl.receptionManager.Comms.GetHost(&id.Permissioning)
 	if !ok {
 		return nil, errors.New("Failed to find permissioning host")
 	}
@@ -110,7 +111,7 @@ func (cl *Client) sendRegistrationMessage(registrationCode string,
 // Returns nonce if successful
 func (cl *Client) requestNonce(salt, regHash []byte,
 	publicKeyDH *cyclic.Int, publicKeyRSA *rsa.PublicKey,
-	privateKeyRSA *rsa.PrivateKey, gwID *id.Gateway) ([]byte, []byte, error) {
+	privateKeyRSA *rsa.PrivateKey, gwID *id.ID) ([]byte, []byte, error) {
 	dhPub := publicKeyDH.Bytes()
 	sha := crypto.SHA256
 	opts := rsa.NewDefaultOptions()
@@ -127,7 +128,7 @@ func (cl *Client) requestNonce(salt, regHash []byte,
 	}
 
 	// Send signed public key and salt for UserID to Server
-	host, ok := cl.receptionManager.Comms.GetHost(gwID.String())
+	host, ok := cl.receptionManager.Comms.GetHost(gwID)
 	if !ok {
 		return nil, nil, errors.Errorf("Failed to find host with ID %s", gwID.String())
 	}
@@ -137,11 +138,11 @@ func (cl *Client) requestNonce(salt, regHash []byte,
 			&pb.NonceRequest{
 				Salt:            salt,
 				ClientRSAPubKey: string(rsa.CreatePublicKeyPem(publicKeyRSA)),
-				ClientSignedByServer: &pb.RSASignature{
+				ClientSignedByServer: &messages.RSASignature{
 					Signature: regHash,
 				},
 				ClientDHPubKey: publicKeyDH.Bytes(),
-				RequestSignature: &pb.RSASignature{
+				RequestSignature: &messages.RSASignature{
 					Signature: signed,
 				},
 			}) // TODO: modify this to return server DH
@@ -163,7 +164,7 @@ func (cl *Client) requestNonce(salt, regHash []byte,
 // It signs a nonce and sends it for confirmation
 // Returns nil if successful, error otherwise
 func (cl *Client) confirmNonce(UID, nonce []byte,
-	privateKeyRSA *rsa.PrivateKey, gwID *id.Gateway) error {
+	privateKeyRSA *rsa.PrivateKey, gwID *id.ID) error {
 	sha := crypto.SHA256
 	opts := rsa.NewDefaultOptions()
 	opts.Hash = sha
@@ -183,12 +184,12 @@ func (cl *Client) confirmNonce(UID, nonce []byte,
 	// TODO: This returns a receipt that can be used to speed up registration
 	msg := &pb.RequestRegistrationConfirmation{
 		UserID: UID,
-		NonceSignedByClient: &pb.RSASignature{
+		NonceSignedByClient: &messages.RSASignature{
 			Signature: sig,
 		},
 	}
 
-	host, ok := cl.receptionManager.Comms.GetHost(gwID.String())
+	host, ok := cl.receptionManager.Comms.GetHost(gwID)
 	if !ok {
 		return errors.Errorf("Failed to find host with ID %s", gwID.String())
 	}
@@ -207,7 +208,7 @@ func (cl *Client) confirmNonce(UID, nonce []byte,
 	return nil
 }
 
-func (cl *Client) registerUserE2E(partnerID *id.User,
+func (cl *Client) registerUserE2E(partnerID *id.ID,
 	partnerPubKey []byte) error {
 
 	// Check that the returned user is valid
@@ -303,7 +304,7 @@ func (cl *Client) GenerateKeys(rsaPrivKey *rsa.PrivateKey,
 	cl.session = user.NewSession(cl.storage, usr, pubKey, privKey, cmixPubKey,
 		cmixPrivKey, e2ePubKey, e2ePrivKey, salt, cmixGrp, e2eGrp, password)
 
-	newRm, err := io.NewReceptionManager(cl.rekeyChan, cl.session.GetCurrentUser().User.String(),
+	newRm, err := io.NewReceptionManager(cl.rekeyChan, cl.session.GetCurrentUser().User,
 		rsa.CreatePrivateKeyPem(privKey), rsa.CreatePublicKeyPem(pubKey), salt)
 	if err != nil {
 		return errors.Wrap(err, "Failed to create new reception manager")
@@ -392,7 +393,7 @@ func generateE2eKeys(cmixGrp, e2eGrp *cyclic.Group) (e2ePrivateKey, e2ePublicKey
 
 //generateUserInformation serves as a helper function for RegisterUser.
 // It generates a salt s.t. it can create a user and their ID
-func generateUserInformation(publicKeyRSA *rsa.PublicKey) ([]byte, *id.User, *user.User, error) {
+func generateUserInformation(publicKeyRSA *rsa.PublicKey) ([]byte, *id.ID, *user.User, error) {
 	//Generate salt for UserID
 	salt := make([]byte, SaltSize)
 	_, err := csprng.NewSystemRNG().Read(salt)
@@ -402,7 +403,10 @@ func generateUserInformation(publicKeyRSA *rsa.PublicKey) ([]byte, *id.User, *us
 	}
 
 	//Generate UserID by hashing salt and public key
-	userId := registration.GenUserID(publicKeyRSA, salt)
+	userId, err := xx.NewID(publicKeyRSA, salt, id.User)
+	if err != nil {
+		return nil, nil, nil, err
+	}
 
 	usr := user.Users.NewUser(userId, "")
 	user.Users.UpsertUser(usr)
diff --git a/api/register.go b/api/register.go
index afbefd0ad10d5f27783712877328913fb0314b5a..921738be22150f4fa06a187a0f375a622ce2aaa7 100644
--- a/api/register.go
+++ b/api/register.go
@@ -24,11 +24,11 @@ import (
 	"time"
 )
 
-const SaltSize = 256
+const SaltSize = 32
 
 //RegisterWithPermissioning registers the user and returns the User ID.
 // Returns an error if registration fails.
-func (cl *Client) RegisterWithPermissioning(preCan bool, registrationCode string) (*id.User, error) {
+func (cl *Client) RegisterWithPermissioning(preCan bool, registrationCode string) (*id.ID, error) {
 	//Check the regState is in proper state for registration
 	if cl.session.GetRegState() != user.KeyGenComplete {
 		return nil, errors.Errorf("Attempting to register before key generation!")
@@ -45,11 +45,11 @@ func (cl *Client) RegisterWithPermissioning(preCan bool, registrationCode string
 		// Either perform a precanned registration for a precanned user
 		cl.opStatus(globals.REG_PRECAN)
 		globals.Log.INFO.Printf("Registering precanned user...")
-		var nodeKeyMap map[id.Node]user.NodeKeys
+		var nodeKeyMap map[id.ID]user.NodeKeys
 		usr, UID, nodeKeyMap, err = cl.precannedRegister(registrationCode)
 		if err != nil {
 			globals.Log.ERROR.Printf("Unable to complete precanned registration: %+v", err)
-			return id.ZeroID, err
+			return &id.ZeroUser, err
 		}
 
 		//overwrite the user object
@@ -70,7 +70,7 @@ func (cl *Client) RegisterWithPermissioning(preCan bool, registrationCode string
 		regValidationSignature, err = cl.registerWithPermissioning(registrationCode, cl.session.GetRSAPublicKey())
 		if err != nil {
 			globals.Log.INFO.Printf(err.Error())
-			return id.ZeroID, err
+			return &id.ZeroUser, err
 		}
 		//update the session with the registration
 		err = cl.session.RegisterPermissioningSignature(regValidationSignature)
@@ -217,9 +217,12 @@ func (cl *Client) RegisterWithNodes() error {
 
 	for i := range cl.ndf.Gateways {
 		localI := i
-		nodeID := *id.NewNodeFromBytes(cl.ndf.Nodes[i].ID)
+		nodeID, err := id.Unmarshal(cl.ndf.Nodes[i].ID)
+		if err != nil {
+			return err
+		}
 		//Register with node if the node has not been registered with already
-		if _, ok := registeredNodes[nodeID]; !ok {
+		if _, ok := registeredNodes[*nodeID]; !ok {
 			wg.Add(1)
 			newRegistrations = true
 			go func() {
@@ -264,12 +267,16 @@ func (cl *Client) RegisterWithNodes() error {
 
 //registerWithNode serves as a helper for RegisterWithNodes
 // It registers a user with a specific in the client's ndf.
-func (cl *Client) registerWithNode(index int, salt, registrationValidationSignature []byte, UID *id.User,
+func (cl *Client) registerWithNode(index int, salt, registrationValidationSignature []byte, UID *id.ID,
 	publicKeyRSA *rsa.PublicKey, privateKeyRSA *rsa.PrivateKey,
 	cmixPublicKeyDH, cmixPrivateKeyDH *cyclic.Int,
 	cmixGrp *cyclic.Group, errorChan chan error) {
 
-	gatewayID := id.NewNodeFromBytes(cl.ndf.Nodes[index].ID).NewGateway()
+	gatewayID, err := id.Unmarshal(cl.ndf.Gateways[index].ID)
+	if err != nil {
+		errorChan <- err
+		return
+	}
 
 	// Initialise blake2b hash for transmission keys and sha256 for reception
 	// keys
diff --git a/api/register_test.go b/api/register_test.go
index b36b0f93c39b0fa4e793f52594730f47ec0a98a1..5c0ab24a56d6029c89c04e737dfc2e39dc4641eb 100644
--- a/api/register_test.go
+++ b/api/register_test.go
@@ -9,9 +9,8 @@ import (
 	"crypto/sha256"
 	"gitlab.com/elixxir/client/globals"
 	"gitlab.com/elixxir/client/user"
-	"gitlab.com/elixxir/comms/connect"
 	"gitlab.com/elixxir/primitives/id"
-	"reflect"
+	"gitlab.com/xx_network/comms/connect"
 	"testing"
 )
 
@@ -33,7 +32,7 @@ func TestRegistrationGob(t *testing.T) {
 	}
 
 	// populate a gob in the store
-	_, err = testClient.RegisterWithPermissioning(true, "UAV6IWD6")
+	_, err = testClient.RegisterWithPermissioning(true, "WTROXJ33")
 	if err != nil {
 		t.Error(err)
 	}
@@ -76,7 +75,7 @@ func TestClient_Register(t *testing.T) {
 	}
 
 	// populate a gob in the store
-	_, err = testClient.RegisterWithPermissioning(true, "UAV6IWD6")
+	_, err = testClient.RegisterWithPermissioning(true, "WTROXJ33")
 	if err != nil {
 		t.Error(err)
 	}
@@ -102,10 +101,10 @@ func TestClient_Register(t *testing.T) {
 //Verify the user from the session make in the registration above matches expected user
 func VerifyRegisterGobUser(session user.Session, t *testing.T) {
 
-	expectedUser := id.NewUserFromUint(5, t)
+	expectedUser := id.NewIdFromUInt(5, id.User, t)
 
-	if reflect.DeepEqual(session.GetCurrentUser().User, &expectedUser) {
-		t.Errorf("Incorrect User ID; \n   expected %q \n   recieved: %q",
+	if !session.GetCurrentUser().User.Cmp(expectedUser) {
+		t.Errorf("Incorrect User ID; \n   expected: %q \n   recieved: %q",
 			expectedUser, session.GetCurrentUser().User)
 	}
 }
@@ -153,7 +152,7 @@ func TestRegister_ValidRegParams___(t *testing.T) {
 		t.Errorf("Registration failed: %s", err.Error())
 	}
 
-	if *regRes == *id.ZeroID {
+	if *regRes == *&id.ZeroUser {
 		t.Errorf("Invalid registration number received: %+v", *regRes)
 	}
 	err = client.RegisterWithNodes()
diff --git a/bindings/client.go b/bindings/client.go
index 746843b824a3c0e42c8f192adbc92d93ab91ef77..480a55fe8ec810be1e112cb91265b90f8e99d13f 100644
--- a/bindings/client.go
+++ b/bindings/client.go
@@ -35,12 +35,15 @@ type Client struct {
 // messages sent from all users.
 // If you pass the zero type (just zero) to Listen() you will hear messages of
 // all types.
-func (cl *Client) Listen(userId []byte, messageType int32, newListener Listener) string {
-	typedUserId := id.NewUserFromBytes(userId)
+func (cl *Client) Listen(userId []byte, messageType int32, newListener Listener) (string, error) {
+	typedUserId, err := id.Unmarshal(userId)
+	if err != nil {
+		return "", err
+	}
 
 	listener := &listenerProxy{proxy: newListener}
 
-	return cl.client.Listen(typedUserId, messageType, listener)
+	return cl.client.Listen(typedUserId, messageType, listener), nil
 }
 
 // Pass the listener handle that Listen() returned to delete the listener
@@ -132,7 +135,7 @@ func (cl *Client) RegisterWithPermissioning(preCan bool, registrationCode string
 	UID, err := cl.client.RegisterWithPermissioning(preCan, registrationCode)
 
 	if err != nil {
-		return id.ZeroID[:], err
+		return id.ZeroUser[:], err
 	}
 
 	return UID[:], nil
@@ -285,8 +288,14 @@ func (cl *Client) Send(m Message, encrypt bool) (int64, error) {
 		"MessageTye: %v", m.GetSender(), m.GetPayload(),
 		m.GetRecipient(), m.GetMessageType())
 
-	sender := id.NewUserFromBytes(m.GetSender())
-	recipient := id.NewUserFromBytes(m.GetRecipient())
+	sender, err := id.Unmarshal(m.GetSender())
+	if err != nil {
+		return 0, err
+	}
+	recipient, err := id.Unmarshal(m.GetRecipient())
+	if err != nil {
+		return 0, err
+	}
 
 	var cryptoType parse.CryptoType
 	if encrypt {
@@ -370,7 +379,10 @@ func (cl *Client) SearchForUser(username string,
 func (cl *Client) DeleteContact(uid []byte) (string, error) {
 	globals.Log.INFO.Printf("Binding call: DeleteContact()\n"+
 		"   uid: %v\n", uid)
-	u := id.NewUserFromBytes(uid)
+	u, err := id.Unmarshal(uid)
+	if err != nil {
+		return "", err
+	}
 
 	return cl.client.DeleteUser(u)
 }
@@ -379,10 +391,14 @@ func (cl *Client) DeleteContact(uid []byte) (string, error) {
 // Non-blocking, once the API call completes, the callback function
 // passed as argument is called
 func (cl *Client) LookupNick(user []byte,
-	cb NickLookupCallback) {
+	cb NickLookupCallback) error {
 	proxy := &nickCallbackProxy{cb}
-	userID := id.NewUserFromBytes(user)
+	userID, err := id.Unmarshal(user)
+	if err != nil {
+		return err
+	}
 	cl.client.LookupNick(userID, proxy)
+	return nil
 }
 
 // Parses a passed message.  Allows a message to be parsed using the internal parser
diff --git a/bindings/client_test.go b/bindings/client_test.go
index d578702bd67f978fbfc4ea34c139556086720b7b..f59ee64034c0b680752c0133cd9adf84eb00eca0 100644
--- a/bindings/client_test.go
+++ b/bindings/client_test.go
@@ -17,13 +17,13 @@ import (
 	"gitlab.com/elixxir/client/globals"
 	"gitlab.com/elixxir/client/parse"
 	"gitlab.com/elixxir/client/user"
-	"gitlab.com/elixxir/comms/connect"
 	"gitlab.com/elixxir/comms/gateway"
 	pb "gitlab.com/elixxir/comms/mixmessages"
 	"gitlab.com/elixxir/comms/registration"
 	"gitlab.com/elixxir/crypto/signature/rsa"
 	"gitlab.com/elixxir/primitives/id"
 	"gitlab.com/elixxir/primitives/ndf"
+	"gitlab.com/xx_network/comms/connect"
 	"math/rand"
 	"os"
 	"reflect"
@@ -36,7 +36,7 @@ const NumNodes = 3
 const NumGWs = NumNodes
 const GWsStartPort = 7950
 const RegPort = 5100
-const ValidRegCode = "UAV6IWD6"
+const ValidRegCode = "WTROXJ33"
 
 var RegHandler = MockRegistration{}
 var RegComms *registration.Comms
@@ -52,7 +52,7 @@ func (i *MockRegistration) RegisterUser(registrationCode, test string) (hash []b
 	return nil, nil
 }
 
-func (i *MockRegistration) RegisterNode(ID []byte, ServerAddr, ServerTlsCert,
+func (i *MockRegistration) RegisterNode(ID *id.ID, ServerAddr, ServerTlsCert,
 	GatewayAddr, GatewayTlsCert, RegistrationCode string) error {
 	return nil
 }
@@ -68,7 +68,11 @@ func (i *MockRegistration) PollNdf(clientNdfHash []byte,
 	return ndfJson, nil
 }
 
-func (i *MockRegistration) Poll(*pb.PermissioningPoll, *connect.Auth) (*pb.PermissionPollResponse, error) {
+func (i *MockRegistration) Poll(*pb.PermissioningPoll, *connect.Auth, string) (*pb.PermissionPollResponse, error) {
+	return nil, nil
+}
+
+func (i *MockRegistration) CheckRegistration(msg *pb.RegisteredNodeCheck) (*pb.RegisteredNodeConfirmation, error) {
 	return nil, nil
 }
 
@@ -358,12 +362,14 @@ func TestClient_Send(t *testing.T) {
 		t.Errorf("Could not start message reception: %+v", err)
 	}
 
+	receiverID := id.NewIdFromBytes(userID, t)
+	receiverID.SetType(id.User)
 	// Test send with invalid sender ID
 	_, err = testClient.Send(
 		mockMesssage{
-			Sender:    id.NewUserFromUint(12, t),
+			Sender:    id.NewIdFromUInt(12, id.User, t),
 			TypedBody: parse.TypedBody{Body: []byte("test")},
-			Receiver:  id.NewUserFromBytes(userID),
+			Receiver:  receiverID,
 		}, false)
 
 	if err != nil {
@@ -375,7 +381,7 @@ func TestClient_Send(t *testing.T) {
 	// Test send with valid inputs
 	_, err = testClient.Send(
 		mockMesssage{
-			Sender:    id.NewUserFromBytes(userID),
+			Sender:    id.NewIdFromBytes(userID, t),
 			TypedBody: parse.TypedBody{Body: []byte("test")},
 			Receiver:  testClient.client.GetCurrentUser(),
 		}, false)
@@ -468,13 +474,13 @@ func TestListen(t *testing.T) {
 	}
 
 	listener := MockListener(false)
-	client.Listen(id.ZeroID[:], int32(cmixproto.Type_NO_TYPE), &listener)
+	client.Listen(id.ZeroUser[:], int32(cmixproto.Type_NO_TYPE), &listener)
 	client.client.GetSwitchboard().Speak(&parse.Message{
 		TypedBody: parse.TypedBody{
 			MessageType: 0,
 			Body:        []byte("stuff"),
 		},
-		Sender:   id.ZeroID,
+		Sender:   &id.ZeroUser,
 		Receiver: client.client.GetCurrentUser(),
 	})
 	time.Sleep(time.Second)
@@ -513,15 +519,18 @@ func TestStopListening(t *testing.T) {
 	}
 
 	listener := MockListener(false)
-	handle := client.Listen(id.ZeroID[:], int32(cmixproto.Type_NO_TYPE), &listener)
+	handle, err := client.Listen(id.ZeroUser[:], int32(cmixproto.Type_NO_TYPE), &listener)
+	if err != nil {
+		t.Fatal(err)
+	}
 	client.StopListening(handle)
 	client.client.GetSwitchboard().Speak(&parse.Message{
 		TypedBody: parse.TypedBody{
 			MessageType: 0,
 			Body:        []byte("stuff"),
 		},
-		Sender:   id.ZeroID,
-		Receiver: id.ZeroID,
+		Sender:   &id.ZeroUser,
+		Receiver: &id.ZeroUser,
 	})
 	if listener {
 		t.Error("Message was received after we stopped listening for it")
@@ -551,8 +560,8 @@ func TestParse(t *testing.T) {
 	ms := parse.Message{}
 	ms.Body = []byte{0, 1, 2}
 	ms.MessageType = int32(cmixproto.Type_NO_TYPE)
-	ms.Receiver = id.ZeroID
-	ms.Sender = id.ZeroID
+	ms.Receiver = &id.ZeroUser
+	ms.Sender = &id.ZeroUser
 
 	messagePacked := ms.Pack()
 
@@ -604,20 +613,29 @@ func testMainWrapper(m *testing.M) int {
 	def = getNDF()
 
 	// Initialize permissioning server
+	// TODO(nan) We shouldn't need to start registration servers twice, right?
 	pAddr := def.Registration.Address
-	RegComms = registration.StartRegistrationServer("testRegServer", pAddr, &RegHandler, nil, nil)
+	regId := new(id.ID)
+	copy(regId[:], "testRegServer")
+	regId.SetType(id.Generic)
+	RegComms = registration.StartRegistrationServer(regId, pAddr,
+		&RegHandler, nil, nil)
 
 	// Start mock gateways used by registration and defer their shutdown (may not be needed)
 	//the ports used are colliding between tests in GoLand when running full suite, this is a dumb fix
 	bump := rand.Intn(10) * 10
 	for i := 0; i < NumGWs; i++ {
+		gwId := new(id.ID)
+		copy(gwId[:], "testGateway")
+		gwId.SetType(id.Gateway)
 
 		gw := ndf.Gateway{
+			ID:      gwId.Marshal(),
 			Address: fmtAddress(GWsStartPort + i + bump),
 		}
 
 		def.Gateways = append(def.Gateways, gw)
-		GWComms[i] = gateway.StartGateway("testGateway", gw.Address,
+		GWComms[i] = gateway.StartGateway(gwId, gw.Address,
 			gateway.NewImplementation(), nil, nil)
 	}
 
@@ -625,14 +643,15 @@ func testMainWrapper(m *testing.M) int {
 	def.Registration = ndf.Registration{
 		Address: fmtAddress(RegPort),
 	}
-	RegComms = registration.StartRegistrationServer("testRegServer", def.Registration.Address,
+	RegComms = registration.StartRegistrationServer(regId, def.Registration.Address,
 		&RegHandler, nil, nil)
 
 	for i := 0; i < NumNodes; i++ {
-		nIdBytes := make([]byte, id.NodeIdLen)
-		nIdBytes[0] = byte(i)
+		nId := new(id.ID)
+		nId[0] = byte(i)
+		nId.SetType(id.Node)
 		n := ndf.Node{
-			ID: nIdBytes,
+			ID: nId[:],
 		}
 		def.Nodes = append(def.Nodes, n)
 	}
@@ -749,8 +768,8 @@ type mockMesssage struct {
 	parse.TypedBody
 	// The crypto type is inferred from the message's contents
 	InferredType parse.CryptoType
-	Sender       *id.User
-	Receiver     *id.User
+	Sender       *id.ID
+	Receiver     *id.ID
 	Nonce        []byte
 	Timestamp    time.Time
 }
diff --git a/bots/bots.go b/bots/bots.go
index b40537aa4beba5ad0f60caa04fff2a2ab57e600b..6cf0bdc3cee88b91aa0bc11a279cf0789a3a39ce 100644
--- a/bots/bots.go
+++ b/bots/bots.go
@@ -6,9 +6,9 @@ import (
 	"gitlab.com/elixxir/client/io"
 	"gitlab.com/elixxir/client/parse"
 	"gitlab.com/elixxir/client/user"
-	"gitlab.com/elixxir/comms/connect"
 	"gitlab.com/elixxir/primitives/id"
 	"gitlab.com/elixxir/primitives/switchboard"
+	"gitlab.com/xx_network/comms/connect"
 )
 
 var session user.Session
@@ -16,9 +16,6 @@ var topology *connect.Circuit
 var comms io.Communications
 var transmissionHost *connect.Host
 
-// UdbID is the ID of the user discovery bot, which is always 3
-var UdbID *id.User
-
 type channelResponseListener chan string
 
 func (l *channelResponseListener) Hear(msg switchboard.Item, isHeardElsewhere bool, i ...interface{}) {
@@ -50,9 +47,7 @@ func (l *nickReqListener) Hear(msg switchboard.Item, isHeardElsewhere bool, i ..
 var nicknameRequestListener nickReqListener
 
 // InitBots is called internally by the Login API
-func InitBots(s user.Session, m io.Communications, top *connect.Circuit, udbID *id.User, host *connect.Host) {
-	UdbID = udbID
-
+func InitBots(s user.Session, m io.Communications, top *connect.Circuit, host *connect.Host) {
 	// FIXME: these all need to be used in non-blocking threads if we are
 	// going to do it this way...
 	msgBufSize := 100
@@ -70,30 +65,30 @@ func InitBots(s user.Session, m io.Communications, top *connect.Circuit, udbID *
 
 	l := session.GetSwitchboard()
 
-	l.Register(UdbID, int32(cmixproto.Type_UDB_PUSH_KEY_RESPONSE),
+	l.Register(&id.UDB, int32(cmixproto.Type_UDB_PUSH_KEY_RESPONSE),
 		&pushKeyResponseListener)
-	l.Register(UdbID, int32(cmixproto.Type_UDB_GET_KEY_RESPONSE),
+	l.Register(&id.UDB, int32(cmixproto.Type_UDB_GET_KEY_RESPONSE),
 		&getKeyResponseListener)
-	l.Register(UdbID, int32(cmixproto.Type_UDB_REGISTER_RESPONSE),
+	l.Register(&id.UDB, int32(cmixproto.Type_UDB_REGISTER_RESPONSE),
 		&registerResponseListener)
-	l.Register(UdbID, int32(cmixproto.Type_UDB_SEARCH_RESPONSE),
+	l.Register(&id.UDB, int32(cmixproto.Type_UDB_SEARCH_RESPONSE),
 		&searchResponseListener)
-	l.Register(id.ZeroID,
+	l.Register(&id.ZeroUser,
 		int32(cmixproto.Type_NICKNAME_REQUEST), &nicknameRequestListener)
-	l.Register(id.ZeroID,
+	l.Register(&id.ZeroUser,
 		int32(cmixproto.Type_NICKNAME_RESPONSE), &nicknameResponseListener)
 }
 
 // sendCommand sends a command to the udb. This doesn't block.
 // Callers that need to wait on a response should implement waiting with a
 // listener.
-func sendCommand(botID *id.User, command []byte) error {
+func sendCommand(botID *id.ID, command []byte) error {
 	return comms.SendMessage(session, topology, botID,
 		parse.Unencrypted, command, transmissionHost)
 }
 
 // Nickname Lookup function
-func LookupNick(user *id.User) (string, error) {
+func LookupNick(user *id.ID) (string, error) {
 	globals.Log.DEBUG.Printf("Sending nickname request to user %v", *user)
 	msg := parse.Pack(&parse.TypedBody{
 		MessageType: int32(cmixproto.Type_NICKNAME_REQUEST),
diff --git a/bots/bots_test.go b/bots/bots_test.go
index c6628b29a78cb425298a2c25b60df832a40f9b52..6f0c7edba4e04504b780cf8039028940a7e25986 100644
--- a/bots/bots_test.go
+++ b/bots/bots_test.go
@@ -9,6 +9,7 @@ package bots
 
 import (
 	"encoding/base64"
+	"encoding/binary"
 	"fmt"
 	"github.com/pkg/errors"
 	jww "github.com/spf13/jwalterweatherman"
@@ -16,11 +17,11 @@ import (
 	"gitlab.com/elixxir/client/globals"
 	"gitlab.com/elixxir/client/parse"
 	"gitlab.com/elixxir/client/user"
-	"gitlab.com/elixxir/comms/connect"
 	"gitlab.com/elixxir/crypto/cyclic"
 	"gitlab.com/elixxir/crypto/large"
 	"gitlab.com/elixxir/primitives/format"
 	"gitlab.com/elixxir/primitives/id"
+	"gitlab.com/xx_network/comms/connect"
 	"os"
 	"strings"
 	"testing"
@@ -36,7 +37,7 @@ type dummyMessaging struct {
 // SendMessage to the server
 func (d *dummyMessaging) SendMessage(sess user.Session,
 	topology *connect.Circuit,
-	recipientID *id.User,
+	recipientID *id.ID,
 	cryptoType parse.CryptoType,
 	message []byte, transmissionHost *connect.Host) error {
 	jww.INFO.Printf("Sending: %s", string(message))
@@ -46,7 +47,7 @@ func (d *dummyMessaging) SendMessage(sess user.Session,
 // SendMessage without partitions to the server
 func (d *dummyMessaging) SendMessageNoPartition(sess user.Session,
 	topology *connect.Circuit,
-	recipientID *id.User,
+	recipientID *id.ID,
 	cryptoType parse.CryptoType,
 	message []byte, transmissionHost *connect.Host) error {
 	jww.INFO.Printf("Sending: %s", string(message))
@@ -64,9 +65,11 @@ var pubKey []byte
 
 func TestMain(m *testing.M) {
 	u := &user.User{
-		User:     id.NewUserFromUints(&[4]uint64{0, 0, 0, 18}),
+		User:     new(id.ID),
 		Username: "Bernie",
 	}
+	binary.BigEndian.PutUint64(u.User[:], 18)
+	u.User.SetType(id.User)
 
 	cmixGrp, e2eGrp := getGroups()
 
@@ -78,9 +81,11 @@ func TestMain(m *testing.M) {
 		listener: ListenCh,
 	}
 	h := connect.Host{}
-	topology := connect.NewCircuit([]*id.Node{id.NewNodeFromBytes(make([]byte, id.NodeIdLen))})
+	nodeID := new(id.ID)
+	nodeID.SetType(id.Node)
+	topology := connect.NewCircuit([]*id.ID{nodeID})
 
-	InitBots(fakeSession, fakeComm, topology, id.NewUserFromBytes([]byte("testid")), &h)
+	InitBots(fakeSession, fakeComm, topology, &h)
 
 	// Make the reception channels buffered for this test
 	// which overwrites the channels registered in InitBots
@@ -121,9 +126,14 @@ func TestRegister(t *testing.T) {
 // TestSearch smoke tests the search function
 func TestSearch(t *testing.T) {
 	publicKeyString := base64.StdEncoding.EncodeToString(pubKey)
+	//uid := id.NewIdFromUInt(26, id.User, t)
+	//serRetUid := base64.StdEncoding.EncodeToString(uid[:])
+	//result, _ := base64.StdEncoding.DecodeString(serRetUid)
+	//t.Fatal(serRetUid)
+	//t.Fatal(len(result))
 
 	// Send response messages from fake UDB in advance
-	searchResponseListener <- "blah@elixxir.io FOUND UR69db14ZyicpZVqJ1HFC5rk9UZ8817aV6+VHmrJpGc= AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABo= 8oKh7TYG4KxQcBAymoXPBHSD/uga9pX3Mn/jKhvcD8M="
+	searchResponseListener <- "blah@elixxir.io FOUND UR69db14ZyicpZVqJ1HFC5rk9UZ8817aV6+VHmrJpGc= AAAAAAAAABoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD 8oKh7TYG4KxQcBAymoXPBHSD/uga9pX3Mn/jKhvcD8M="
 	getKeyResponseListener <- fmt.Sprintf("GETKEY %s %s", keyFingerprint,
 		publicKeyString)
 
@@ -136,8 +146,8 @@ func TestSearch(t *testing.T) {
 	if err != nil {
 		t.Errorf("Error on Search: %s", err.Error())
 	}
-	if *searchedUser != *id.NewUserFromUint(26, t) {
-		t.Errorf("Search did not return user ID 26! returned %v", string(searchedUser.Bytes()))
+	if !searchedUser.Cmp(id.NewIdFromUInt(26, id.User, t)) {
+		t.Errorf("Search did not return user ID 26! returned %s", searchedUser)
 	}
 	//Test the timeout capabilities
 	searchedUser, _, err = Search("EMAIL", "blah@elixxir.io", dummySearchState, 1*time.Millisecond)
@@ -190,7 +200,7 @@ type errorMessaging struct{}
 // SendMessage that just errors out
 func (e *errorMessaging) SendMessage(sess user.Session,
 	topology *connect.Circuit,
-	recipientID *id.User,
+	recipientID *id.ID,
 	cryptoType parse.CryptoType,
 	message []byte, transmissionHost *connect.Host) error {
 	return errors.New("This is an error")
@@ -199,7 +209,7 @@ func (e *errorMessaging) SendMessage(sess user.Session,
 // SendMessage no partition that just errors out
 func (e *errorMessaging) SendMessageNoPartition(sess user.Session,
 	topology *connect.Circuit,
-	recipientID *id.User,
+	recipientID *id.ID,
 	cryptoType parse.CryptoType,
 	message []byte, transmissionHost *connect.Host) error {
 	return errors.New("This is an error")
diff --git a/bots/userDiscovery.go b/bots/userDiscovery.go
index 35cd3970051e04839b5fb406536147f804d18b90..d84976c955d24e3814939419a7d3c8541dae07fc 100644
--- a/bots/userDiscovery.go
+++ b/bots/userDiscovery.go
@@ -46,7 +46,7 @@ func Register(valueType, value string, publicKey []byte, regStatus func(int), ti
 
 	regStatus(globals.UDB_REG_PUSHKEY)
 	// push key and error if it already exists
-	err = pushKey(UdbID, keyFP, publicKey)
+	err = pushKey(keyFP, publicKey)
 
 	if err != nil {
 		return errors.Wrap(err, "Could not PUSHKEY")
@@ -84,7 +84,7 @@ func Register(valueType, value string, publicKey []byte, regStatus func(int), ti
 
 	// Send register command
 	// Send register command
-	err = sendCommand(UdbID, msgBody)
+	err = sendCommand(&id.UDB, msgBody)
 	if err != nil {
 		return errors.Wrap(err, "Could not Push User")
 	}
@@ -114,7 +114,7 @@ func Register(valueType, value string, publicKey []byte, regStatus func(int), ti
 // Search returns a userID and public key based on the search criteria
 // it accepts a valueType of EMAIL and value of an e-mail address, and
 // returns a map of userid -> public key
-func Search(valueType, value string, searchStatus func(int), timeout time.Duration) (*id.User, []byte, error) {
+func Search(valueType, value string, searchStatus func(int), timeout time.Duration) (*id.ID, []byte, error) {
 	globals.Log.DEBUG.Printf("Running search for %v, %v", valueType, value)
 
 	searchTimeout := time.NewTimer(timeout)
@@ -133,7 +133,7 @@ func Search(valueType, value string, searchStatus func(int), timeout time.Durati
 		MessageType: int32(cmixproto.Type_UDB_SEARCH),
 		Body:        []byte(fmt.Sprintf("%s %s", valueType, value)),
 	})
-	err = sendCommand(UdbID, msgBody)
+	err = sendCommand(&id.UDB, msgBody)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -160,9 +160,9 @@ func Search(valueType, value string, searchStatus func(int), timeout time.Durati
 	}
 
 	// While search returns more than 1 result, we only process the first
-	cMixUID, keyFP := parseSearch(response)
-	if *cMixUID == *id.ZeroID {
-		return nil, nil, fmt.Errorf("%s", keyFP)
+	cMixUID, keyFP, err := parseSearch(response)
+	if err != nil {
+		return nil, nil, err
 	}
 
 	searchStatus(globals.UDB_SEARCH_GETKEY)
@@ -172,7 +172,7 @@ func Search(valueType, value string, searchStatus func(int), timeout time.Durati
 		MessageType: int32(cmixproto.Type_UDB_GET_KEY),
 		Body:        []byte(keyFP),
 	})
-	err = sendCommand(UdbID, msgBody)
+	err = sendCommand(&id.UDB, msgBody)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -221,20 +221,23 @@ func hashAndEncode(s string) (string, error) {
 
 // parseSearch parses the responses from SEARCH. It returns the user's id and
 // the user's public key fingerprint
-func parseSearch(msg string) (*id.User, string) {
+func parseSearch(msg string) (*id.ID, string, error) {
 	globals.Log.DEBUG.Printf("Parsing search response: %v", msg)
 	resParts := strings.Split(msg, " ")
 	if len(resParts) != 5 {
-		return id.ZeroID, fmt.Sprintf("Invalid response from search: %s", msg)
+		return &id.ZeroUser, "", errors.WithMessagef(errors.New("Invalid response from search"), ": %s", msg)
 	}
 
 	cMixUIDBytes, err := base64.StdEncoding.DecodeString(resParts[3])
 	if err != nil {
-		return id.ZeroID, fmt.Sprintf("Couldn't parse search cMix UID: %s", msg)
+		return &id.ZeroUser, "", errors.WithMessagef(errors.New("Couldn't parse search cMix UID"), ": %s", msg)
+	}
+	cMixUID, err := id.Unmarshal(cMixUIDBytes)
+	if err != nil {
+		return &id.ZeroUser, "", err
 	}
-	cMixUID := id.NewUserFromBytes(cMixUIDBytes)
 
-	return cMixUID, resParts[4]
+	return cMixUID, resParts[4], nil
 }
 
 // parseGetKey parses the responses from GETKEY. It returns the
@@ -254,14 +257,14 @@ func parseGetKey(msg string) []byte {
 }
 
 // pushKey uploads the users' public key
-func pushKey(udbID *id.User, keyFP string, publicKey []byte) error {
+func pushKey(keyFP string, publicKey []byte) error {
 	publicKeyString := base64.StdEncoding.EncodeToString(publicKey)
-	globals.Log.DEBUG.Printf("Running pushkey for %q, %v, %v", *udbID, keyFP,
+	globals.Log.DEBUG.Printf("Running pushkey for %v, %v, %v", id.UDB, keyFP,
 		publicKeyString)
 
 	pushKeyMsg := fmt.Sprintf("%s %s", keyFP, publicKeyString)
 
-	return sendCommand(udbID, parse.Pack(&parse.TypedBody{
+	return sendCommand(&id.UDB, parse.Pack(&parse.TypedBody{
 		MessageType: int32(cmixproto.Type_UDB_PUSH_KEY),
 		Body:        []byte(pushKeyMsg),
 	}))
diff --git a/cmd/root.go b/cmd/root.go
index cb96a47e6beb53eab7f7a32220b25040a8f4bdc2..c61e825a24dcb4eae57cae5fd6c61eca64955142 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -9,18 +9,17 @@ package cmd
 
 import (
 	"encoding/base64"
+	"encoding/binary"
 	"fmt"
 	"github.com/golang/protobuf/proto"
 	"github.com/spf13/cobra"
 	jww "github.com/spf13/jwalterweatherman"
 	"github.com/spf13/viper"
 	"gitlab.com/elixxir/client/api"
-	"gitlab.com/elixxir/client/bots"
 	"gitlab.com/elixxir/client/cmixproto"
 	"gitlab.com/elixxir/client/globals"
 	"gitlab.com/elixxir/client/parse"
 	"gitlab.com/elixxir/client/user"
-	"gitlab.com/elixxir/crypto/large"
 	"gitlab.com/elixxir/crypto/signature/rsa"
 	"gitlab.com/elixxir/primitives/id"
 	"gitlab.com/elixxir/primitives/switchboard"
@@ -69,7 +68,7 @@ func Execute() {
 	}
 }
 
-func sessionInitialization() (*id.User, string, *api.Client) {
+func sessionInitialization() (*id.ID, string, *api.Client) {
 	var err error
 	register := false
 
@@ -111,7 +110,7 @@ func sessionInitialization() (*id.User, string, *api.Client) {
 		if err != nil {
 			globals.Log.ERROR.Printf("Could Not Initialize Ram Storage: %s\n",
 				err.Error())
-			return id.ZeroID, "", nil
+			return &id.ZeroUser, "", nil
 		}
 		globals.Log.INFO.Println("Initialized Ram Storage")
 		register = true
@@ -141,14 +140,14 @@ func sessionInitialization() (*id.User, string, *api.Client) {
 				//Fail if any other error is received
 				globals.Log.ERROR.Printf("Error with file paths: %s %s",
 					err1, err2)
-				return id.ZeroID, "", nil
+				return &id.ZeroUser, "", nil
 			}
 		}
 		//Initialize client with OS Storage
 		client, err = api.NewClient(nil, sessionA, sessionB, ndfJSON)
 		if err != nil {
 			globals.Log.ERROR.Printf("Could Not Initialize OS Storage: %s\n", err.Error())
-			return id.ZeroID, "", nil
+			return &id.ZeroUser, "", nil
 		}
 		globals.Log.INFO.Println("Initialized OS Storage")
 
@@ -169,7 +168,7 @@ func sessionInitialization() (*id.User, string, *api.Client) {
 		// No gateways in config file or passed via command line
 		globals.Log.ERROR.Printf("Error: No gateway specified! Add to" +
 			" configuration file or pass via command line using -g!\n")
-		return id.ZeroID, "", nil
+		return &id.ZeroUser, "", nil
 	}*/
 
 	if noTLS {
@@ -185,7 +184,7 @@ func sessionInitialization() (*id.User, string, *api.Client) {
 	client.SetRateLimiting(rateLimiting)
 
 	// Holds the User ID
-	var uid *id.User
+	var uid *id.ID
 
 	// Register a new user if requested
 	if register {
@@ -195,7 +194,10 @@ func sessionInitialization() (*id.User, string, *api.Client) {
 		// If precanned user, use generated code instead
 		if userId != 0 {
 			precanned = true
-			regCode = id.NewUserFromUints(&[4]uint64{0, 0, 0, userId}).RegistrationCode()
+			uid := new(id.ID)
+			binary.BigEndian.PutUint64(uid[:], userId)
+			uid.SetType(id.User)
+			regCode = user.RegistrationCode(uid)
 		}
 
 		globals.Log.INFO.Printf("Building keys...")
@@ -224,10 +226,10 @@ func sessionInitialization() (*id.User, string, *api.Client) {
 		globals.Log.INFO.Printf("Attempting to register with code %s...", regCode)
 
 		errRegister := fmt.Errorf("")
-		uid = client.GetCurrentUser()
+
 		//Attempt to register user with same keys until a success occurs
 		for errRegister != nil {
-			_, errRegister = client.RegisterWithPermissioning(userId != 0, regCode)
+			_, errRegister = client.RegisterWithPermissioning(precanned, regCode)
 			if errRegister != nil {
 				globals.Log.FATAL.Panicf("Could Not Register User: %s",
 					errRegister.Error())
@@ -240,6 +242,8 @@ func sessionInitialization() (*id.User, string, *api.Client) {
 				err.Error())
 		}
 
+		uid = client.GetCurrentUser()
+
 		userbase64 := base64.StdEncoding.EncodeToString(uid[:])
 		globals.Log.INFO.Printf("Registered as user (uid, the var) %v", uid)
 		globals.Log.INFO.Printf("Registered as user (userID, the global) %v", userId)
@@ -248,7 +252,9 @@ func sessionInitialization() (*id.User, string, *api.Client) {
 	} else {
 		// hack for session persisting with cmd line
 		// doesn't support non pre canned users
-		uid = id.NewUserFromUints(&[4]uint64{0, 0, 0, userId})
+		uid := new(id.ID)
+		binary.BigEndian.PutUint64(uid[:], userId)
+		uid.SetType(id.User)
 		globals.Log.INFO.Printf("Skipped Registration, user: %v", uid)
 	}
 
@@ -319,7 +325,7 @@ func (l *FallbackListener) Hear(item switchboard.Item, isHeardElsewhere bool, i
 		}
 		atomic.AddInt64(&l.MessagesReceived, 1)
 		globals.Log.INFO.Printf("Message of type %v from %q, %v received with fallback: %s\n",
-			message.MessageType, *message.Sender, senderNick,
+			message.MessageType, printIDNice(message.Sender), senderNick,
 			string(message.Body))
 	}
 }
@@ -341,7 +347,7 @@ func (l *TextListener) Hear(item switchboard.Item, isHeardElsewhere bool, i ...i
 	sender, ok := user.Users.GetUser(message.Sender)
 	var senderNick string
 	if !ok {
-		globals.Log.INFO.Printf("First message from sender %v", message.Sender)
+		globals.Log.INFO.Printf("First message from sender %v", printIDNice(message.Sender))
 		u := user.Users.NewUser(message.Sender, base64.StdEncoding.EncodeToString(message.Sender[:]))
 		user.Users.UpsertUser(u)
 		senderNick = u.Username
@@ -349,7 +355,7 @@ func (l *TextListener) Hear(item switchboard.Item, isHeardElsewhere bool, i ...i
 		senderNick = sender.Username
 	}
 	logMsg := fmt.Sprintf("Message from %v, %v Received: %s\n",
-		large.NewIntFromBytes(message.Sender[:]).Text(10),
+		printIDNice(message.Sender),
 		senderNick, result.Message)
 	globals.Log.INFO.Printf("%s -- Timestamp: %s\n", logMsg,
 		message.Timestamp.String())
@@ -406,11 +412,11 @@ var rootCmd = &cobra.Command{
 		// the integration test
 		// Normal text messages
 		text := TextListener{}
-		client.Listen(id.ZeroID, int32(cmixproto.Type_TEXT_MESSAGE),
+		client.Listen(&id.ZeroUser, int32(cmixproto.Type_TEXT_MESSAGE),
 			&text)
 		// All other messages
 		fallback := FallbackListener{}
-		client.Listen(id.ZeroID, int32(cmixproto.Type_NO_TYPE),
+		client.Listen(&id.ZeroUser, int32(cmixproto.Type_NO_TYPE),
 			&fallback)
 
 		// Log the user in, for now using the first gateway specified
@@ -422,7 +428,7 @@ var rootCmd = &cobra.Command{
 
 		err = client.InitListeners()
 		if err != nil {
-			globals.Log.FATAL.Panicf("Could not initialize receivers: %s\n", err)
+			globals.Log.FATAL.Panicf("Could not initialize receivers: %+v\n", err)
 		}
 
 		err = client.StartMessageReceiver(cb)
@@ -447,7 +453,7 @@ var rootCmd = &cobra.Command{
 			cryptoType = parse.E2E
 		}
 
-		var recipientId *id.User
+		var recipientId *id.ID
 
 		if destinationUserId != 0 && destinationUserIDBase64 != "" {
 			globals.Log.FATAL.Panicf("Two destiantions set for the message, can only have one")
@@ -460,10 +466,15 @@ var rootCmd = &cobra.Command{
 			if err != nil {
 				globals.Log.FATAL.Panic("Could not decode the destination user ID")
 			}
-			recipientId = id.NewUserFromBytes(recipientIdBytes)
-
+			recipientId, err = id.Unmarshal(recipientIdBytes)
+			if err != nil {
+				// Destination user ID must be 33 bytes and include the id type
+				globals.Log.FATAL.Panicf("Could not unmarshal destination user ID: %v", err)
+			}
 		} else {
-			recipientId = id.NewUserFromUints(&[4]uint64{0, 0, 0, destinationUserId})
+			recipientId = new(id.ID)
+			binary.BigEndian.PutUint64(recipientId[:], destinationUserId)
+			recipientId.SetType(id.User)
 		}
 
 		if message != "" {
@@ -475,7 +486,7 @@ var rootCmd = &cobra.Command{
 			}
 
 			// Handle sending to UDB
-			if *recipientId == *bots.UdbID {
+			if recipientId.Cmp(&id.UDB) {
 				parseUdbMessage(message, client)
 			} else {
 				// Handle sending to any other destination
@@ -484,10 +495,7 @@ var rootCmd = &cobra.Command{
 				for i := uint(0); i < messageCnt; i++ {
 					logMsg := fmt.Sprintf(
 						"Sending Message to "+
-							"%s, %v: %s\n",
-						large.NewIntFromBytes(
-							recipientId[:]).Text(
-							10),
+							"%s, %v: %s\n", printIDNice(recipientId),
 						recipientNick, message)
 					globals.Log.INFO.Printf(logMsg)
 					fmt.Printf(logMsg)
@@ -551,10 +559,9 @@ var rootCmd = &cobra.Command{
 
 		if searchForUser != "" {
 			foundUser := <-udbLister.(*userSearcher).foundUserChan
-			if isValidUser(foundUser) {
-				userIDBase64 := base64.StdEncoding.EncodeToString(foundUser)
+			if isValid, uid := isValidUser(foundUser); isValid {
 				globals.Log.INFO.Printf("Found User %s at ID: %s",
-					searchForUser, userIDBase64)
+					searchForUser, printIDNice(uid))
 			} else {
 				globals.Log.INFO.Printf("Found User %s is invalid", searchForUser)
 			}
@@ -578,16 +585,21 @@ var rootCmd = &cobra.Command{
 	},
 }
 
-func isValidUser(usr []byte) bool {
-	if len(usr) != id.UserLen {
-		return false
+func isValidUser(usr []byte) (bool, *id.ID) {
+	if len(usr) != id.ArrIDLen {
+		return false, nil
 	}
 	for _, b := range usr {
 		if b != 0 {
-			return true
+			uid, err := id.Unmarshal(usr)
+			if err != nil {
+				globals.Log.WARN.Printf("Could not unmarshal user: %s", err)
+				return false, nil
+			}
+			return true, uid
 		}
 	}
-	return false
+	return false, nil
 }
 
 // init is the initialization function for Cobra which defines commands
@@ -703,3 +715,34 @@ func init() {
 
 // initConfig reads in config file and ENV variables if set.
 func initConfig() {}
+
+// returns a simple numerical id if the user is a precanned user, otherwise
+// returns the normal string of the userID
+func printIDNice(uid *id.ID) string {
+
+	for index, puid := range precannedIDList {
+		if uid.Cmp(puid) {
+			return strconv.Itoa(index + 1)
+		}
+	}
+
+	return uid.String()
+}
+
+// build a list of precanned ids to use for comparision for nicer user id output
+var precannedIDList = buildPrecannedIDList()
+
+func buildPrecannedIDList() []*id.ID {
+
+	idList := make([]*id.ID, 40)
+
+	for i := 0; i < 40; i++ {
+		uid := new(id.ID)
+		binary.BigEndian.PutUint64(uid[:], uint64(i+1))
+		uid.SetType(id.User)
+		idList[i] = uid
+	}
+
+	return idList
+}
+
diff --git a/cmd/udb.go b/cmd/udb.go
index 3b234d6e06ff687e219ccfff9c4c22e4ab1a1e83..ed39e5d8be21880cc3b3540c64d214c8f6e87b12 100644
--- a/cmd/udb.go
+++ b/cmd/udb.go
@@ -22,8 +22,12 @@ func (cs callbackSearch) Callback(userID, pubKey []byte, err error) {
 	} else if len(pubKey) == 0 {
 		globals.Log.INFO.Printf("Public Key returned is empty\n")
 	} else {
+		userID, err := id.Unmarshal(userID)
+		if err != nil {
+			globals.Log.ERROR.Printf("Malformed user ID from successful UDB search: %v", err)
+		}
 		globals.Log.INFO.Printf("UDB search successful. Returned user %v\n",
-			*id.NewUserFromBytes(userID))
+			userID)
 	}
 }
 
diff --git a/cmd/version.go b/cmd/version.go
index 1555261dad8260e5e020855e618913007b478341..364bd0f0856c10212984bac2abdc64d38f57cd84 100644
--- a/cmd/version.go
+++ b/cmd/version.go
@@ -16,7 +16,7 @@ import (
 )
 
 // Change this value to set the version for this build
-const currentVersion = "1.3.0"
+const currentVersion = "1.4.0"
 
 func printVersion() {
 	fmt.Printf("Elixxir Client v%s -- %s\n\n", globals.SEMVER, globals.GITVERSION)
diff --git a/crypto/encrypt.go b/crypto/encrypt.go
index 7cfe6fe504820f8943b49507a286e1af567a2ac2..7686521401474733e45bc47b508e90501d4ef8ea 100644
--- a/crypto/encrypt.go
+++ b/crypto/encrypt.go
@@ -9,12 +9,12 @@ package crypto
 import (
 	"gitlab.com/elixxir/client/globals"
 	"gitlab.com/elixxir/client/user"
-	"gitlab.com/elixxir/comms/connect"
 	"gitlab.com/elixxir/crypto/cmix"
 	"gitlab.com/elixxir/crypto/cyclic"
 	"gitlab.com/elixxir/crypto/e2e"
 	"gitlab.com/elixxir/crypto/hash"
 	"gitlab.com/elixxir/primitives/format"
+	"gitlab.com/xx_network/comms/connect"
 )
 
 // CMIX Encrypt performs the encryption
diff --git a/crypto/encryptdecrypt_test.go b/crypto/encryptdecrypt_test.go
index b94d74a3d5b720ca85259f8e9eaf1fcbbd3a31c3..7cc9ad2c852126e10e63b560486946d7864f3066 100644
--- a/crypto/encryptdecrypt_test.go
+++ b/crypto/encryptdecrypt_test.go
@@ -8,8 +8,8 @@ package crypto
 
 import (
 	"bytes"
+	"encoding/binary"
 	"gitlab.com/elixxir/client/user"
-	"gitlab.com/elixxir/comms/connect"
 	pb "gitlab.com/elixxir/comms/mixmessages"
 	"gitlab.com/elixxir/crypto/cmix"
 	"gitlab.com/elixxir/crypto/cyclic"
@@ -17,6 +17,7 @@ import (
 	"gitlab.com/elixxir/crypto/large"
 	"gitlab.com/elixxir/primitives/format"
 	"gitlab.com/elixxir/primitives/id"
+	"gitlab.com/xx_network/comms/connect"
 	"golang.org/x/crypto/blake2b"
 	"os"
 	"testing"
@@ -40,16 +41,21 @@ func setup() {
 
 	user.InitUserRegistry(cmixGrp)
 
-	UID := id.NewUserFromUints(&[4]uint64{0, 0, 0, 18})
-	u, _ := user.Users.GetUser(UID)
+	UID := new(id.ID)
+	binary.BigEndian.PutUint64(UID[:], 18)
+	UID.SetType(id.User)
+	u, ok := user.Users.GetUser(UID)
+	if !ok {
+		panic("Didn't get user 18 from registry")
+	}
 
-	var nodeSlice []*id.Node
+	var nodeSlice []*id.ID
 
 	//build topology
 	for i := 0; i < numNodes; i++ {
-		nodeBytes := make([]byte, id.NodeIdLen)
-		nodeBytes[0] = byte(i)
-		nodeId := id.NewNodeFromBytes(nodeBytes)
+		nodeId := new(id.ID)
+		nodeId[0] = byte(i)
+		nodeId.SetType(id.Node)
 		nodeSlice = append(nodeSlice, nodeId)
 	}
 
@@ -93,8 +99,8 @@ func TestMain(m *testing.M) {
 func TestFullEncryptDecrypt(t *testing.T) {
 	cmixGrp, e2eGrp := getGroups()
 
-	sender := id.NewUserFromUint(38, t)
-	recipient := id.NewUserFromUint(29, t)
+	sender := id.NewIdFromUInt(38, id.User, t)
+	recipient := id.NewIdFromUInt(29, id.User, t)
 	msg := format.NewMessage()
 	msg.SetRecipient(recipient)
 	msgPayload := []byte("help me, i'm stuck in an" +
@@ -141,9 +147,14 @@ func TestFullEncryptDecrypt(t *testing.T) {
 		t.Errorf("E2EDecrypt returned error: %v", err.Error())
 	}
 
-	if *decMsg.GetRecipient() != *recipient {
+	decryptedRecipient, err := decMsg.GetRecipient()
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if !decryptedRecipient.Cmp(recipient) {
 		t.Errorf("Recipient differed from expected: Got %q, expected %q",
-			decMsg.GetRecipient(), sender)
+			decryptedRecipient, sender)
 	}
 	if !bytes.Equal(decMsg.Contents.GetRightAligned(), msgPayload) {
 		t.Errorf("Decrypted payload differed from expected: Got %q, "+
@@ -155,8 +166,8 @@ func TestFullEncryptDecrypt(t *testing.T) {
 // to be sent occupies the whole payload structure, i.e. 256 bytes
 func TestFullEncryptDecrypt_Unsafe(t *testing.T) {
 	cmixGrp, e2eGrp := getGroups()
-	sender := id.NewUserFromUint(38, t)
-	recipient := id.NewUserFromUint(29, t)
+	sender := id.NewIdFromUInt(38, id.User, t)
+	recipient := id.NewIdFromUInt(29, id.User, t)
 	msg := format.NewMessage()
 	msg.SetRecipient(recipient)
 	msgPayload := []byte(
@@ -212,9 +223,14 @@ func TestFullEncryptDecrypt_Unsafe(t *testing.T) {
 		t.Errorf("E2EDecryptUnsafe returned error: %v", err.Error())
 	}
 
-	if *decMsg.GetRecipient() != *recipient {
+	decryptedRecipient, err := decMsg.GetRecipient()
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if !decryptedRecipient.Cmp(recipient) {
 		t.Errorf("Recipient differed from expected: Got %q, expected %q",
-			decMsg.GetRecipient(), sender)
+			decryptedRecipient, sender)
 	}
 	if !bytes.Equal(decMsg.Contents.Get(), msgPayload[:format.ContentsLen]) {
 		t.Errorf("Decrypted payload differed from expected: Got %q, "+
@@ -225,7 +241,7 @@ func TestFullEncryptDecrypt_Unsafe(t *testing.T) {
 // Test that E2EEncrypt panics if the payload is too big (can't be padded)
 func TestE2EEncrypt_Panic(t *testing.T) {
 	_, e2eGrp := getGroups()
-	recipient := id.NewUserFromUint(29, t)
+	recipient := id.NewIdFromUInt(29, id.User, t)
 	msg := format.NewMessage()
 	msg.SetRecipient(recipient)
 	msgPayload := []byte("help me, i'm stuck in an" +
@@ -258,7 +274,7 @@ func TestE2EEncrypt_Panic(t *testing.T) {
 // Test that E2EDecrypt and E2EDecryptUnsafe handle errors correctly
 func TestE2EDecrypt_Errors(t *testing.T) {
 	_, e2eGrp := getGroups()
-	recipient := id.NewUserFromUint(29, t)
+	recipient := id.NewIdFromUInt(29, id.User, t)
 	msg := format.NewMessage()
 	msg.SetRecipient(recipient)
 	msgPayload := []byte("help me, i'm stuck in an EnterpriseTextLabelDescriptorSetPipelineStateFactoryBeanFactory ")
diff --git a/globals/checkVersion.go b/globals/checkVersion.go
deleted file mode 100644
index a9517bacdf154906be194753f66c82904837bf88..0000000000000000000000000000000000000000
--- a/globals/checkVersion.go
+++ /dev/null
@@ -1,87 +0,0 @@
-////////////////////////////////////////////////////////////////////////////////
-// Copyright © 2020 Privategrity Corporation                                   /
-//                                                                             /
-// All rights reserved.                                                        /
-////////////////////////////////////////////////////////////////////////////////
-
-package globals
-
-import (
-	"github.com/pkg/errors"
-	"strconv"
-	"strings"
-)
-
-type clientVersion struct {
-	major int
-	minor int
-	patch string
-}
-
-func (v *clientVersion) String() string {
-	return strconv.Itoa(v.major) + "." + strconv.Itoa(v.minor) + "." + v.patch
-}
-
-func parseClientVersion(versionString string) (*clientVersion, error) {
-	versions := strings.SplitN(versionString, ".", 3)
-	if len(versions) != 3 {
-		return nil, errors.New("Client version string must contain a major, minor, and patch version separated by \".\"")
-	}
-	major, err := strconv.Atoi(versions[0])
-	if err != nil {
-		return nil, errors.New("Major client version couldn't be parsed as integer")
-	}
-	minor, err := strconv.Atoi(versions[1])
-	if err != nil {
-		return nil, errors.New("Minor client version couldn't be parsed as integer")
-	}
-	return &clientVersion{
-		major: major,
-		minor: minor,
-		patch: versions[2],
-	}, nil
-}
-
-// Handle client version check
-// Example valid version strings:
-// 0.1.0
-// 1.3.0-ff81cdae
-// Major and minor versions should both be numbers, and patch versions can be
-// anything, but they must be present
-// receiver is the version from the registration server
-func (v *clientVersion) isCompatible(theirVersion *clientVersion) bool {
-	// Compare major version: must be equal to be deemed compatible
-	if theirVersion.major != v.major {
-		return false
-	}
-	// Compare minor version: our version must be greater than or equal to their version to be deemed compatible
-	if theirVersion.minor > v.minor {
-		return false
-	}
-	// Patch versions aren't supposed to affect compatibility, so they're ignored for the check
-
-	return true
-}
-
-// Parse both versions and ensure they're compatible
-// Ours is the local version of the client library, theirs is the version on the
-// registration server
-func checkVersion(ours string, theirs string) (ok bool, err error) {
-	theirVersion, err := parseClientVersion(theirs)
-	if err != nil {
-		return false, errors.Wrapf(err,
-			"Error parsing permissioning's version (%v)", theirs)
-	}
-	ourVersion, err := parseClientVersion(ours)
-	if err != nil {
-		return false, errors.Wrapf(err,
-			"Error parsing our version (%v)", ours)
-	}
-	return ourVersion.isCompatible(theirVersion), nil
-}
-
-// Utility method, returns whether the local version and remote version are
-// compatible
-func CheckVersion(ver string) (bool, error) { // again, version stuff, move to globals
-	return checkVersion(SEMVER, ver)
-}
diff --git a/globals/checkVersion_test.go b/globals/checkVersion_test.go
deleted file mode 100644
index d59999671f96237b6c92393e081ff81ba4027fc0..0000000000000000000000000000000000000000
--- a/globals/checkVersion_test.go
+++ /dev/null
@@ -1,179 +0,0 @@
-////////////////////////////////////////////////////////////////////////////////
-// Copyright © 2019 Privategrity Corporation                                   /
-//                                                                             /
-// All rights reserved.                                                        /
-////////////////////////////////////////////////////////////////////////////////
-
-package globals
-
-import (
-	"testing"
-)
-
-func TestParseClientVersion_Success(t *testing.T) {
-	version, err := parseClientVersion("1.2.3456")
-	expectedVersion := clientVersion{
-		major: 1,
-		minor: 2,
-		patch: "3456",
-	}
-	if err != nil {
-		t.Error(err)
-	}
-	if version.minor != expectedVersion.minor {
-		t.Errorf("Expected %+v for minor version, got %+v",
-			expectedVersion.minor, version.minor)
-	}
-	if version.major != expectedVersion.major {
-		t.Errorf("Expected %+v for major version, got %+v",
-			expectedVersion.major, version.major)
-	}
-	if version.patch != expectedVersion.patch {
-		t.Errorf("Expected %+v for patch version, got %+v",
-			expectedVersion.patch, version.patch)
-	}
-}
-
-func TestParseClientVersion_Failure(t *testing.T) {
-	_, err := parseClientVersion("")
-	if err == nil {
-		t.Error("Expected error for empty version string")
-	}
-	_, err = parseClientVersion("0")
-	if err == nil {
-		t.Error("Expected error for version string with one number")
-	}
-	_, err = parseClientVersion("0.0")
-	if err == nil {
-		t.Error("Expected error for version string with two numbers")
-	}
-	_, err = parseClientVersion("a.4.0")
-	if err == nil {
-		t.Error("Expected error for version string with non-numeric major version")
-	}
-	_, err = parseClientVersion("4.a.0")
-	if err == nil {
-		t.Error("Expected error for version string with non-numeric minor version")
-	}
-}
-
-// If the registration version starts with zeroes, anything with major version 0
-// should be compatible, with any (positive) minor version and any patch
-func TestClientVersion_IsCompatible_Zero(t *testing.T) {
-	theirVersion := &clientVersion{
-		major: 0,
-		minor: 0,
-		patch: "stuff",
-	}
-	ourVersion_compatible := &clientVersion{
-		major: 0,
-		minor: 1,
-		patch: "even more stuff",
-	}
-	if !ourVersion_compatible.isCompatible(theirVersion) {
-		t.Errorf("Our version %v should have been compatible with their version %v",
-			ourVersion_compatible, theirVersion)
-	}
-	ourVersion_incompatible := &clientVersion{
-		major: 1,
-		minor: 0,
-		patch: "other stuff",
-	}
-	if ourVersion_incompatible.isCompatible(theirVersion) {
-		t.Errorf("Our version %v shouldn't have been compatible with their version %v",
-			ourVersion_incompatible, theirVersion)
-	}
-}
-
-// If the registration version is a real version (non-zero), the boundaries
-// of compatibility should be enforced. That is, the major version should be the
-// same, and the client's minor version should be greater than or equal to the
-// registration server's minor version to be deemed compatible.
-func TestClientVersion_IsCompatible_Nonzero(t *testing.T) {
-	theirVersion := &clientVersion{
-		major: 1,
-		minor: 4,
-		patch: "51",
-	}
-	ourVersion_compatible := []*clientVersion{{
-		major: 1,
-		minor: 4,
-		patch: "50",
-	}, {
-		major: 1,
-		minor: 4,
-		patch: "52",
-	}, {
-		major: 1,
-		minor: 4,
-		patch: "51",
-	}, {
-		major: 1,
-		minor: 10,
-		patch: "50",
-	}, {
-		major: 1,
-		minor: 10,
-		patch: "52",
-	}, {
-		major: 1,
-		minor: 10,
-		patch: "51",
-	}}
-	for i := 0; i < len(ourVersion_compatible); i++ {
-		if !ourVersion_compatible[i].isCompatible(theirVersion) {
-			t.Errorf("Versions (ours) %v (and theirs) %v were incorrectly incompatible at index %v.",
-				ourVersion_compatible[i], theirVersion, i)
-		}
-	}
-
-	ourVersion_incompatible := []*clientVersion{{
-		major: 0,
-		minor: 0,
-		patch: "0",
-	}, {
-		major: 2,
-		minor: 0,
-		patch: "1",
-	}, {
-		major: 1,
-		minor: 3,
-		patch: "9",
-	}, {
-		major: 2,
-		minor: 5,
-		patch: "9",
-	}}
-	for i := 0; i < len(ourVersion_incompatible); i++ {
-		if ourVersion_incompatible[i].isCompatible(theirVersion) {
-			t.Errorf("Versions (ours) %v (and theirs) %v were incorrectly compatible at index %v.",
-				ourVersion_incompatible[i], theirVersion, i)
-		}
-	}
-}
-
-func TestClientVersion_String(t *testing.T) {
-	cv := clientVersion{5, 10, "test"}
-
-	testString := "5.10.test"
-
-	if cv.String() != testString {
-		t.Errorf("String() did not return the correct string"+
-			"\n\texpected: %s\n\treceived: %s", cv.String(), testString)
-	}
-}
-
-func TestCommManager_CheckVersion(t *testing.T) {
-	_, err := checkVersion("ours", "theirs")
-
-	if err == nil {
-		t.Errorf("checkVersion() did not return an error"+
-			"\n\t%v", err)
-	}
-	_, err = checkVersion("ours", "1.2.3456")
-
-	if err == nil {
-		t.Errorf("checkVersion() did not return an error"+
-			"\n\t%v", err)
-	}
-}
diff --git a/globals/storage_test.go b/globals/storage_test.go
index 23cbf077c49c528a3038b8e5e85cbd5a29f0d9a3..96abc9532bd258097ffd73da5c4430def403fe5d 100644
--- a/globals/storage_test.go
+++ b/globals/storage_test.go
@@ -132,3 +132,13 @@ func TestRamStorage_GetLocation(t *testing.T) {
 			a, b)
 	}
 }
+
+func Test_dsLoadHelper_LocError(t *testing.T) {
+	testLoc := "~a/test"
+
+	result := dsLoadHelper(testLoc)
+
+	if result != nil {
+		t.Errorf("dsLoadHelper() did not error on invalid path.")
+	}
+}
diff --git a/globals/version_vars.go b/globals/version_vars.go
index 03a571a0a00133fa71fb81ea137cd7814d60babe..adb16b83a9a4d441e85e282929bd7d9d59a0409e 100644
--- a/globals/version_vars.go
+++ b/globals/version_vars.go
@@ -1,17 +1,17 @@
 // Code generated by go generate; DO NOT EDIT.
 // This file was generated by robots at
-// 2020-05-12 21:02:57.275479486 +0000 UTC m=+0.003920070
+// 2020-07-30 15:27:18.7064291 -0700 PDT m=+0.065083001
 package globals
 
-const GITVERSION = `daeebfe disable stdout on the global jww logger`
-const SEMVER = "1.3.0"
+const GITVERSION = `4ef845a Merge branch 'hotfix/integrateekv' into 'release'`
+const SEMVER = "1.4.0"
 const DEPENDENCIES = `module gitlab.com/elixxir/client
 
 go 1.13
 
 require (
 	github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3
-	github.com/golang/protobuf v1.4.0
+	github.com/golang/protobuf v1.4.2
 	github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00 // indirect
 	github.com/pelletier/go-toml v1.6.0 // indirect
 	github.com/pkg/errors v0.9.1
@@ -22,10 +22,12 @@ require (
 	github.com/spf13/jwalterweatherman v1.1.0
 	github.com/spf13/pflag v1.0.5 // indirect
 	github.com/spf13/viper v1.6.2
-	gitlab.com/elixxir/comms v0.0.0-20200415204952-6d63dd94a0ea
-	gitlab.com/elixxir/crypto v0.0.0-20200410231849-90e859940f5d
-	gitlab.com/elixxir/primitives v0.0.0-20200410231944-a57d71d577c9
-	golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59
+	gitlab.com/elixxir/comms v0.0.0-20200730220537-60dbe58afe94
+	gitlab.com/elixxir/crypto v0.0.0-20200707005343-97f868cbd930
+	gitlab.com/elixxir/ekv v0.0.0-20200729182028-159355ea5842
+	gitlab.com/elixxir/primitives v0.0.0-20200706165052-9fe7a4fb99a3
+	gitlab.com/xx_network/comms v0.0.0-20200730220144-eea32e8b696d
+	golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899
 	gopkg.in/ini.v1 v1.52.0 // indirect
 )
 `
diff --git a/go.mod b/go.mod
index 66bd3d8f52c113ae6b159c607bc9083ec9911673..f483175c4086c6414c15f9cd2f863fc10c680e0e 100644
--- a/go.mod
+++ b/go.mod
@@ -4,7 +4,7 @@ go 1.13
 
 require (
 	github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3
-	github.com/golang/protobuf v1.4.1
+	github.com/golang/protobuf v1.4.2
 	github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00 // indirect
 	github.com/pelletier/go-toml v1.6.0 // indirect
 	github.com/pkg/errors v0.9.1
@@ -15,9 +15,11 @@ require (
 	github.com/spf13/jwalterweatherman v1.1.0
 	github.com/spf13/pflag v1.0.5 // indirect
 	github.com/spf13/viper v1.6.2
-	gitlab.com/elixxir/comms v0.0.0-20200513163847-4975a4118ac6
-	gitlab.com/elixxir/crypto v0.0.0-20200513163659-38b6079db0b2
-	gitlab.com/elixxir/primitives v0.0.0-20200513162412-ef77445c0ab7
-	golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37
+	gitlab.com/elixxir/comms v0.0.0-20200730220537-60dbe58afe94
+	gitlab.com/elixxir/crypto v0.0.0-20200707005343-97f868cbd930
+	gitlab.com/elixxir/ekv v0.0.0-20200729182028-159355ea5842
+	gitlab.com/elixxir/primitives v0.0.0-20200706165052-9fe7a4fb99a3
+	gitlab.com/xx_network/comms v0.0.0-20200730220144-eea32e8b696d
+	golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899
 	gopkg.in/ini.v1 v1.52.0 // indirect
 )
diff --git a/go.sum b/go.sum
index b54504a99cf65ac325e04fe1b1d731412f212dd7..0222d5381d5434a6707b9200622d02e3c7197de4 100644
--- a/go.sum
+++ b/go.sum
@@ -1,4 +1,5 @@
 cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
 github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
@@ -18,6 +19,7 @@ github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfc
 github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
 github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
 github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
@@ -45,7 +47,6 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
 github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
-github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
 github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
 github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
 github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
@@ -54,12 +55,18 @@ github.com/golang/protobuf v1.4.0 h1:oOuy+ugB+P/kBdUnG5QaMXSIyJ1q38wWSojYCb3z5VQ
 github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
 github.com/golang/protobuf v1.4.1 h1:ZFgWrT+bLgsYPirOnRfKLYJLvssAegOj/hgyMFdJZe0=
 github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
+github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
+github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
 github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
 github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
 github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
 github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
+github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
+github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00 h1:l5lAOZEym3oK3SQ2HBHWsJUfbNBiTXJDeW2QDxw9AQ0=
 github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
 github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
 github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
@@ -67,8 +74,10 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf
 github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
 github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
 github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
+github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
 github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
 github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
+github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
 github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
 github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
 github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
@@ -76,9 +85,9 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o
 github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
-github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
 github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
 github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
 github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
@@ -89,6 +98,7 @@ github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrk
 github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
 github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
 github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
+github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
 github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
 github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
 github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
@@ -97,6 +107,7 @@ github.com/pelletier/go-toml v1.6.0/go.mod h1:5N711Q9dKgbdkxHL+MEfF31hpT7l0S0s/t
 github.com/pkg/errors v0.8.0/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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
 github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
@@ -113,7 +124,9 @@ github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD
 github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
 github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
 github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
+github.com/smartystreets/assertions v1.0.1 h1:voD4ITNjPL5jjBfgR/r8fPIIBrliWrWHeiJApdr3r4w=
 github.com/smartystreets/assertions v1.0.1/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM=
+github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
 github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
 github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
 github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
@@ -137,39 +150,40 @@ github.com/spf13/viper v1.6.2/go.mod h1:t3iDnF5Jlj76alVNuyFBk5oUMCvsrkbvZK0WQdfD
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
-github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
 github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
 github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
 github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
 github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
 github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
 github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
-gitlab.com/elixxir/comms v0.0.0-20200415204952-6d63dd94a0ea h1:LVQuXjq26hjnuw2isxSpYHFBql80VLnhcrNQ+IJ4QH0=
-gitlab.com/elixxir/comms v0.0.0-20200415204952-6d63dd94a0ea/go.mod h1:EeRHiSIfVYqTaMZGb6eE2kz1Rw5zGXuuBow4Pi2VFg0=
-gitlab.com/elixxir/comms v0.0.0-20200513163847-4975a4118ac6 h1:k+loYgTu4fN12Dmypu8OSZ8mQCBUxJQI29In5gaDHo4=
-gitlab.com/elixxir/comms v0.0.0-20200513163847-4975a4118ac6/go.mod h1:zuYjqakP4b5rYtgeAra5SAHV87FRym9eEa8dwDVdNuU=
-gitlab.com/elixxir/crypto v0.0.0-20200410231849-90e859940f5d h1:+g7tGMO3g20Su3pdTJg30n5XhEGZ3avEd4ccN33CtdU=
-gitlab.com/elixxir/crypto v0.0.0-20200410231849-90e859940f5d/go.mod h1:QPClJr3F90ejz6iHaCZuhexytd6PP97dDnt93iRCTDo=
-gitlab.com/elixxir/crypto v0.0.0-20200513163659-38b6079db0b2 h1:dqWdpifXovXXk2egHeh3FQFqB3IlcIurL+A+ziFFbU0=
-gitlab.com/elixxir/crypto v0.0.0-20200513163659-38b6079db0b2/go.mod h1:9BCEQtgddrCHt/QONWD+nU1n6iZ+qfc89GnzSVHA97I=
-gitlab.com/elixxir/primitives v0.0.0-20200218211222-4193179f359c/go.mod h1:REJMcwIcyxh74VSHqy4S9yYiaEsQYObOPglRExDpk14=
-gitlab.com/elixxir/primitives v0.0.0-20200410231944-a57d71d577c9 h1:KP/BQqOrLcoCah24VHzQBY8EbQHvBcsrhz/Yjs6vn4Y=
-gitlab.com/elixxir/primitives v0.0.0-20200410231944-a57d71d577c9/go.mod h1:nqC90Tt1jLDd2e35hVWCmTynTXBBEt3UfaSsIhHevmc=
-gitlab.com/elixxir/primitives v0.0.0-20200513162412-ef77445c0ab7 h1:XY7jcsaqkM60c0TFrPyxfPam1hlM1oYGq/wlCk1GKUM=
-gitlab.com/elixxir/primitives v0.0.0-20200513162412-ef77445c0ab7/go.mod h1:uc1rmOIN/Cg6WsTtIz7vu/ezxHqqFRiDEvBeFM/tMjM=
+gitlab.com/elixxir/comms v0.0.0-20200707210150-b8ebd0951d23/go.mod h1:OsWMZ1O/R9fOkm+PoHnR3rkXfFtipGoPs73FuKuurHY=
+gitlab.com/elixxir/comms v0.0.0-20200730220537-60dbe58afe94 h1:AY3Nm9QXAh8jRUyKdX0jsay4A4j4z7/ofJgJKbvIFmc=
+gitlab.com/elixxir/comms v0.0.0-20200730220537-60dbe58afe94/go.mod h1:rlB+AA0mNMRq9GIR587cNw5RX4Cr8VOiVpZkFTTcukQ=
+gitlab.com/elixxir/crypto v0.0.0-20200707005343-97f868cbd930 h1:9qzfwyR12OYgn3j30qcHZHHVfWshWnH54lcAHppEROQ=
+gitlab.com/elixxir/crypto v0.0.0-20200707005343-97f868cbd930/go.mod h1:LHBAaEf48a0/AjU118rjoworH0LgXifhAqmNX3ZRvME=
+gitlab.com/elixxir/ekv v0.0.0-20200729182028-159355ea5842 h1:m1zDQ6UadpuMnV7nvnyR+DUXE3AisRnVjajTb1xZE4c=
+gitlab.com/elixxir/ekv v0.0.0-20200729182028-159355ea5842/go.mod h1:bXY0kgbV5BHYda4YY5/hiG5bjimGK+R3PYub5yM9C/s=
+gitlab.com/elixxir/primitives v0.0.0-20200706165052-9fe7a4fb99a3 h1:GTfflZBNLeBq3UApYog0J3+hytdkoRsDduGQji2wyEU=
+gitlab.com/elixxir/primitives v0.0.0-20200706165052-9fe7a4fb99a3/go.mod h1:OQgUZq7SjnE0b+8+iIAT2eqQF+2IFHn73tOo+aV11mg=
+gitlab.com/xx_network/comms v0.0.0-20200730220144-eea32e8b696d h1:P4ceawldsxuwQ6LZHnUnemtiOJI9/QquYi8QkFuUbtI=
+gitlab.com/xx_network/comms v0.0.0-20200730220144-eea32e8b696d/go.mod h1:76OCijGBxYOBV5Kt7z6K7vNg3n9I57aCQMmI8GTpoEM=
 go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
 go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
 go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
 go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
 golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20200117160349-530e935923ad/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59 h1:3zb4D3T4G8jdExgVU/95+vQXfpEPiMdCaZgmGVxjNHM=
-golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37 h1:cg5LA/zNPRzIXIWSCxQW10Rvpy94aQh3LT/ShoCpkHw=
 golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20200707235045-ab33eee955e0 h1:eIYIE7EC5/Wv5Kbz8bJPaq+TN3kq3W8S+LSm62vM0DY=
+golang.org/x/crypto v0.0.0-20200707235045-ab33eee955e0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899 h1:DZhuSZLsGlFL4CmhA8BcRA0mnthyA/nZ00AqCUo7vHg=
+golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
@@ -182,10 +196,10 @@ golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73r
 golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
-golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0=
-golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0 h1:Jcxah/M+oLZ/R4/z5RzfPzGbPXnVDPkEDtf2JnuxN+U=
-golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200513185701-a91f0712d120 h1:EZ3cVSzKOlJxAd8e8YAJ7no8nNypTxexh/YE/xW3ZEY=
+golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU=
+golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -198,16 +212,16 @@ golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5h
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200331124033-c3d80250170d h1:nc5K6ox/4lTFbMVSL9WRR81ixkcwXThoiF6yf+R9scA=
-golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f h1:mOhmO9WsBaJCNmaZHPtHs9wOcdqdKCjF6OPJlmDM3KI=
-golang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200519105757-fe76b779f299 h1:DYfZAGf2WMFjMxbgTjaC+2HC7NkNAQs+6Q8b9WEB/F4=
+golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae h1:Ih9Yo4hSPImZOpfGuA4bR/ORKTAbhZo2AbWNRCnevdo=
+golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -216,24 +230,26 @@ golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3
 golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
 google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
 google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
-google.golang.org/genproto v0.0.0-20200309141739-5b75447e413d h1:VQ0pz4dAUaMWcQLM7tGY8Nk691kSrlGPyF5nSgAIw2g=
-google.golang.org/genproto v0.0.0-20200309141739-5b75447e413d/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200428115010-c45acf45369a h1:ykRcNp3dotYGpAEIYeWCGaefklVjVy/rnSvM3zNh6j8=
-google.golang.org/genproto v0.0.0-20200428115010-c45acf45369a/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200514193133-8feb7f20f2a2 h1:RwW6+LxyOQJ7oeoZ76GIJlwt/O0J5cN2fk+q/jK27kQ=
+google.golang.org/genproto v0.0.0-20200514193133-8feb7f20f2a2/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
+google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
+google.golang.org/genproto v0.0.0-20200709005830-7a2ca40e9dc3 h1:JwLN1jVnmIsfE4HkDVe2AblFAbo0Z+4cjteDSOnv6oE=
+google.golang.org/genproto v0.0.0-20200709005830-7a2ca40e9dc3/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
 google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
 google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
 google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
-google.golang.org/grpc v1.27.1 h1:zvIju4sqAGvwKspUQOhwnpcqSbzi7/H6QomNNjTL4sk=
-google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
 google.golang.org/grpc v1.29.1 h1:EC2SB8S04d2r73uptxphDSUG+kTKVgjRPF+N3xpxRB4=
 google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
+google.golang.org/grpc v1.30.0 h1:M5a8xTlYTxwMn5ZFkwhRabsygDY5G8TYLyQDBxJNAxE=
+google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
 google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
 google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
 google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@@ -242,10 +258,16 @@ google.golang.org/protobuf v1.21.0 h1:qdOKuR/EIArgaWNjetjgTzgVTAZ+S/WXVrq9HW9zim
 google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
 google.golang.org/protobuf v1.22.0 h1:cJv5/xdbk1NnMPR1VP9+HU6gupuG9MLBoH1r6RHZ2MY=
 google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
+google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
+google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
+google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
 gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
 gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
 gopkg.in/ini.v1 v1.52.0 h1:j+Lt/M1oPPejkniCg1TkWE2J3Eh1oZTsHSXzMTzUXn4=
@@ -255,8 +277,10 @@ gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bl
 gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
-gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
+gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
+gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
diff --git a/io/collate.go b/io/collate.go
index 0f4ec1e49cdbe270b03538719e8ee94412b613a9..c2d74635ed915576f807767f4f17d065e5b928aa 100644
--- a/io/collate.go
+++ b/io/collate.go
@@ -47,16 +47,19 @@ func NewCollator() *Collator {
 // TODO this takes too many types. i should split it up.
 // This method returns a byte slice with the assembled message if it's
 // received a completed message.
-func (mb *Collator) AddMessage(message *format.Message, sender *id.User,
-	timeout time.Duration) *parse.Message {
+func (mb *Collator) AddMessage(message *format.Message, sender *id.ID,
+	timeout time.Duration) (*parse.Message, error) {
 
 	payload := message.Contents.GetRightAligned()
-	recipient := message.GetRecipient()
+	recipient, err := message.GetRecipient()
+	if err != nil {
+		return nil, err
+	}
 
 	//get the time
 	timestamp := time.Time{}
 
-	err := timestamp.UnmarshalBinary(message.GetTimestamp()[:len(message.GetTimestamp())-1])
+	err = timestamp.UnmarshalBinary(message.GetTimestamp()[:len(message.GetTimestamp())-1])
 
 	if err != nil {
 		globals.Log.WARN.Printf("Failed to parse timestamp for message %v: %+v",
@@ -65,15 +68,16 @@ func (mb *Collator) AddMessage(message *format.Message, sender *id.User,
 
 	partition, err := parse.ValidatePartition(payload)
 
-	if err == nil {
+	if err != nil {
+		return nil, errors.WithMessage(err, "Received an invalid partition: ")
+	} else {
 		if partition.MaxIndex == 0 {
 			//this is the only part of the message. we should take the fast
 			//path and skip putting it in the map
 			typedBody, err := parse.Parse(partition.Body)
 			// Log an error if the message is malformed and return nothing
 			if err != nil {
-				globals.Log.ERROR.Printf("Malformed message received")
-				return nil
+				return nil, errors.WithMessage(err, "Malformed message received")
 			}
 
 			msg := parse.Message{
@@ -84,7 +88,7 @@ func (mb *Collator) AddMessage(message *format.Message, sender *id.User,
 				Timestamp:    timestamp,
 			}
 
-			return &msg
+			return &msg, nil
 		} else {
 			// assemble the map key into a new chunk of memory
 			var key PendingMessageKey
@@ -95,6 +99,7 @@ func (mb *Collator) AddMessage(message *format.Message, sender *id.User,
 			copy(key[:], keyHash[:PendingMessageKeyLen])
 
 			mb.mux.Lock()
+			defer mb.mux.Unlock()
 			message, ok := mb.pendingMessages[key]
 			if !ok {
 				// this is a multi-part message we haven't seen before.
@@ -113,11 +118,11 @@ func (mb *Collator) AddMessage(message *format.Message, sender *id.User,
 				// TODO vary timeout depending on number of messages?
 				time.AfterFunc(timeout, func() {
 					mb.mux.Lock()
+					defer mb.mux.Unlock()
 					_, ok := mb.pendingMessages[key]
 					if ok {
 						delete(mb.pendingMessages, key)
 					}
-					mb.mux.Unlock()
 				})
 			} else {
 				// append to array for this key
@@ -129,17 +134,13 @@ func (mb *Collator) AddMessage(message *format.Message, sender *id.User,
 				fullMsg, err := parse.Assemble(message.parts)
 				if err != nil {
 					delete(mb.pendingMessages, key)
-					mb.mux.Unlock()
-					globals.Log.ERROR.Printf("Malformed message: Padding error, %v", err.Error())
-					return nil
+					return nil, errors.WithMessage(err, "Malformed message: padding error, ")
 				}
 				typedBody, err := parse.Parse(fullMsg)
 				// Log an error if the message is malformed and return nothing
 				if err != nil {
 					delete(mb.pendingMessages, key)
-					mb.mux.Unlock()
-					globals.Log.ERROR.Printf("Malformed message Received")
-					return nil
+					return nil, errors.WithMessage(err, "Malformed message received")
 				}
 
 				msg := parse.Message{
@@ -151,16 +152,13 @@ func (mb *Collator) AddMessage(message *format.Message, sender *id.User,
 				}
 
 				delete(mb.pendingMessages, key)
-				mb.mux.Unlock()
-				return &msg
+				return &msg, nil
+			} else {
+				// need more parts
+				return nil, nil
 			}
-			mb.mux.Unlock()
 		}
-	} else {
-		globals.Log.ERROR.Printf("Received an invalid partition: %v\n", err.Error())
 	}
-	globals.Log.DEBUG.Printf("Message collator: %v", mb.dump())
-	return nil
 }
 
 // Debug: dump all messages that are currently in the map
diff --git a/io/collate_test.go b/io/collate_test.go
index 6bf301fb530803bc1586639ab798bb07b4584220..274497f2519a478511de3c8f24ceffd1d2bb5195 100644
--- a/io/collate_test.go
+++ b/io/collate_test.go
@@ -19,7 +19,7 @@ import (
 
 func TestCollator_AddMessage(t *testing.T) {
 
-	uid := id.NewUserFromUint(69, t)
+	uid := id.NewIdFromUInt(69, id.User, t)
 
 	collator := &Collator{
 		pendingMessages: make(map[PendingMessageKey]*multiPartMessage),
@@ -44,10 +44,13 @@ func TestCollator_AddMessage(t *testing.T) {
 		for j := range partitions {
 
 			fm := format.NewMessage()
-			fm.SetRecipient(id.NewUserFromUint(6, t))
+			fm.SetRecipient(id.NewIdFromUInt(6, id.User, t))
 			fm.Contents.SetRightAligned(partitions[j])
 
-			result = collator.AddMessage(fm, uid, time.Minute)
+			result, err = collator.AddMessage(fm, uid, time.Minute)
+			if err != nil {
+				t.Fatal(err)
+			}
 		}
 
 		typedBody, err := parse.Parse(bodies[i])
@@ -66,7 +69,7 @@ func TestCollator_AddMessage(t *testing.T) {
 
 func TestCollator_AddMessage_Timeout(t *testing.T) {
 
-	uid := id.NewUserFromUint(69, t)
+	uid := id.NewIdFromUInt(69, id.User, t)
 
 	collator := &Collator{
 		pendingMessages: make(map[PendingMessageKey]*multiPartMessage),
@@ -84,10 +87,13 @@ func TestCollator_AddMessage_Timeout(t *testing.T) {
 		nowBytes, _ := now.MarshalBinary()
 		nowBytes = append(nowBytes, make([]byte, format.TimestampLen-len(nowBytes))...)
 		fm.SetTimestamp(nowBytes)
-		fm.SetRecipient(id.NewUserFromUint(6, t))
+		fm.SetRecipient(id.NewIdFromUInt(6, id.User, t))
 		fm.Contents.SetRightAligned(partitions[i])
 
-		result = collator.AddMessage(fm, uid, 80*time.Millisecond)
+		result, err = collator.AddMessage(fm, uid, 80*time.Millisecond)
+		if err != nil {
+			t.Fatal(err)
+		}
 		if result != nil {
 			t.Error("Got a result from collator when it should be timing out" +
 				" submessages")
diff --git a/io/interface.go b/io/interface.go
index 1aaf3ce3b57bec5b6ba485da40fe69e906c0e591..3352147ee359249751df963abe00487a6fd579e1 100644
--- a/io/interface.go
+++ b/io/interface.go
@@ -9,8 +9,8 @@ package io
 import (
 	"gitlab.com/elixxir/client/parse"
 	"gitlab.com/elixxir/client/user"
-	"gitlab.com/elixxir/comms/connect"
 	"gitlab.com/elixxir/primitives/id"
+	"gitlab.com/xx_network/comms/connect"
 	"time"
 )
 
@@ -20,12 +20,12 @@ type Communications interface {
 
 	// TODO(nen) Can we get rid of the crypto type param here?
 	SendMessage(session user.Session, topology *connect.Circuit,
-		recipientID *id.User, cryptoType parse.CryptoType, message []byte,
+		recipientID *id.ID, cryptoType parse.CryptoType, message []byte,
 		transmissionHost *connect.Host) error
 	// SendMessage without partitions to the server
 	// This is used to send rekey messages
 	SendMessageNoPartition(session user.Session, topology *connect.Circuit,
-		recipientID *id.User, cryptoType parse.CryptoType, message []byte,
+		recipientID *id.ID, cryptoType parse.CryptoType, message []byte,
 		transmissionHost *connect.Host) error
 	// MessageReceiver thread to get new messages
 	MessageReceiver(session user.Session, delay time.Duration,
diff --git a/io/receive.go b/io/receive.go
index 49c1cddd7f888bc3785a27aa64eeb0adbec425ad..ef5c5dbcba7c66037efa0d67935e762916c3cd07 100644
--- a/io/receive.go
+++ b/io/receive.go
@@ -14,12 +14,12 @@ import (
 	"gitlab.com/elixxir/client/globals"
 	"gitlab.com/elixxir/client/parse"
 	"gitlab.com/elixxir/client/user"
-	"gitlab.com/elixxir/comms/connect"
 	pb "gitlab.com/elixxir/comms/mixmessages"
 	"gitlab.com/elixxir/crypto/e2e"
 	"gitlab.com/elixxir/primitives/format"
 	"gitlab.com/elixxir/primitives/id"
 	"gitlab.com/elixxir/primitives/switchboard"
+	"gitlab.com/xx_network/comms/connect"
 	"strings"
 	"time"
 )
@@ -100,8 +100,11 @@ func (rm *ReceptionManager) MessageReceiver(session user.Session, delay time.Dur
 			if decryptedMessages != nil {
 				for i := range decryptedMessages {
 					// TODO Handle messages that do not need partitioning
-					assembledMessage := rm.collator.AddMessage(decryptedMessages[i],
+					assembledMessage, err := rm.collator.AddMessage(decryptedMessages[i],
 						senders[i], time.Minute)
+					if err != nil {
+						go callback(err)
+					}
 					if assembledMessage != nil {
 						// we got a fully assembled message. let's broadcast it
 						broadcastMessageReception(assembledMessage, session.GetSwitchboard())
@@ -113,7 +116,7 @@ func (rm *ReceptionManager) MessageReceiver(session user.Session, delay time.Dur
 }
 
 func handleE2EReceiving(session user.Session,
-	message *format.Message) (*id.User, bool, error) {
+	message *format.Message) (*id.ID, bool, error) {
 	keyFingerprint := message.GetKeyFP()
 
 	// Lookup reception key
@@ -249,11 +252,11 @@ func (rm *ReceptionManager) receiveMessagesFromGateway(session user.Session,
 }
 
 func (rm *ReceptionManager) decryptMessages(session user.Session,
-	encryptedMessages []*format.Message) ([]*format.Message, []*id.User,
+	encryptedMessages []*format.Message) ([]*format.Message, []*id.ID,
 	[]*format.Message) {
 
 	messages := make([]*format.Message, len(encryptedMessages))
-	senders := make([]*id.User, len(encryptedMessages))
+	senders := make([]*id.ID, len(encryptedMessages))
 	messagesSendersLoc := 0
 
 	garbledMessages := make([]*format.Message, len(encryptedMessages))
@@ -263,7 +266,7 @@ func (rm *ReceptionManager) decryptMessages(session user.Session,
 		var err error = nil
 		var rekey bool
 		var unpadded []byte
-		var sender *id.User
+		var sender *id.ID
 		garbled := false
 
 		// If message is E2E, handle decryption
@@ -275,7 +278,7 @@ func (rm *ReceptionManager) decryptMessages(session user.Session,
 			}
 
 			keyFP := msg.AssociatedData.GetKeyFP()
-			sender = id.NewUserFromBytes(keyFP[:])
+			sender, err = makeUserID(keyFP[:])
 		} else {
 			sender, rekey, err = handleE2EReceiving(session, msg)
 
@@ -311,6 +314,17 @@ func broadcastMessageReception(message *parse.Message,
 	listeners.Speak(message)
 }
 
+// Put a sender ID in a byte slice and set its type to user
+func makeUserID(senderID []byte) (*id.ID, error) {
+	senderIDBytes := make([]byte, id.ArrIDLen)
+	copy(senderIDBytes, senderID[:])
+	userID, err := id.Unmarshal(senderIDBytes)
+	if userID != nil {
+		userID.SetType(id.User)
+	}
+	return userID, err
+}
+
 // skipErrChecker checks checks if the error is fatal or should be ignored
 func skipErrChecker(err error) bool {
 	if strings.Contains(err.Error(), "Could not find any message IDs for this user") {
diff --git a/io/receptionManager.go b/io/receptionManager.go
index 53b70ee3ba7fb8949f6db4fe25785afe14b53b40..467ab0ebd069c22dcad1600d90e5e9967afd1205 100644
--- a/io/receptionManager.go
+++ b/io/receptionManager.go
@@ -13,12 +13,11 @@ import (
 	"github.com/pkg/errors"
 	"gitlab.com/elixxir/client/parse"
 	"gitlab.com/elixxir/comms/client"
+	"gitlab.com/elixxir/primitives/id"
 	"sync"
 	"time"
 )
 
-const PermissioningAddrID = "Permissioning"
-
 type ConnAddr string
 
 func (a ConnAddr) String() string {
@@ -51,7 +50,7 @@ type ReceptionManager struct {
 }
 
 // Build a new reception manager object using inputted key fields
-func NewReceptionManager(rekeyChan chan struct{}, uid string, privKey, pubKey, salt []byte) (*ReceptionManager, error) {
+func NewReceptionManager(rekeyChan chan struct{}, uid *id.ID, privKey, pubKey, salt []byte) (*ReceptionManager, error) {
 	comms, err := client.NewClientComms(uid, pubKey, privKey, salt)
 	if err != nil {
 		return nil, errors.Wrap(err, "Failed to get client comms using constructor: %+v")
@@ -74,9 +73,9 @@ func NewReceptionManager(rekeyChan chan struct{}, uid string, privKey, pubKey, s
 // Connects to the permissioning server, if we know about it, to get the latest
 // version from it
 func (rm *ReceptionManager) GetRemoteVersion() (string, error) {
-	permissioningHost, ok := rm.Comms.GetHost(PermissioningAddrID)
+	permissioningHost, ok := rm.Comms.GetHost(&id.Permissioning)
 	if !ok {
-		return "", errors.Errorf("Failed to find permissioning host with id %s", PermissioningAddrID)
+		return "", errors.Errorf("Failed to find permissioning host with id %s", id.Permissioning)
 	}
 	registrationVersion, err := rm.Comms.
 		SendGetCurrentClientVersionMessage(permissioningHost)
diff --git a/io/send.go b/io/send.go
index 1d0821e9e94814ade332ce1995bcca33963904a4..f4d5fc97507ce718c6bdf66704facc797746a143 100644
--- a/io/send.go
+++ b/io/send.go
@@ -15,13 +15,13 @@ import (
 	"gitlab.com/elixxir/client/keyStore"
 	"gitlab.com/elixxir/client/parse"
 	"gitlab.com/elixxir/client/user"
-	"gitlab.com/elixxir/comms/connect"
 	pb "gitlab.com/elixxir/comms/mixmessages"
 	"gitlab.com/elixxir/crypto/cmix"
 	"gitlab.com/elixxir/crypto/csprng"
 	"gitlab.com/elixxir/crypto/e2e"
 	"gitlab.com/elixxir/primitives/format"
 	"gitlab.com/elixxir/primitives/id"
+	"gitlab.com/xx_network/comms/connect"
 	"time"
 )
 
@@ -31,7 +31,7 @@ import (
 // TODO This method would be cleaner if it took a parse.Message (particularly
 // w.r.t. generating message IDs for multi-part messages.)
 func (rm *ReceptionManager) SendMessage(session user.Session, topology *connect.Circuit,
-	recipientID *id.User, cryptoType parse.CryptoType,
+	recipientID *id.ID, cryptoType parse.CryptoType,
 	message []byte, transmissionHost *connect.Host) error {
 	// FIXME: We should really bring the plaintext parts of the NewMessage logic
 	// into this module, then have an EncryptedMessage type that is sent to/from
@@ -77,7 +77,7 @@ func (rm *ReceptionManager) SendMessage(session user.Session, topology *connect.
 // This function will be needed for example to send a Rekey
 // message, where a new public key will take up the whole message
 func (rm *ReceptionManager) SendMessageNoPartition(session user.Session,
-	topology *connect.Circuit, recipientID *id.User, cryptoType parse.CryptoType,
+	topology *connect.Circuit, recipientID *id.ID, cryptoType parse.CryptoType,
 	message []byte, transmissionHost *connect.Host) error {
 	size := len(message)
 	if size > format.TotalLen {
@@ -132,14 +132,14 @@ func (rm *ReceptionManager) send(session user.Session, topology *connect.Circuit
 		}
 		message.Contents.Set(padded)
 		e2e.SetUnencrypted(message)
-		message.SetKeyFP(*format.NewFingerprint(session.GetCurrentUser().User.Bytes()))
+		message.SetKeyFP(*format.NewFingerprint(session.GetCurrentUser().User.Marshal()[:32]))
 	}
 	// CMIX Encryption
 	salt := cmix.NewSalt(csprng.Source(&csprng.SystemRNG{}), 32)
 	encMsg, kmacs := crypto.CMIXEncrypt(session, topology, salt, message)
 
 	msgPacket := &pb.Slot{
-		SenderID: session.GetCurrentUser().User.Bytes(),
+		SenderID: session.GetCurrentUser().User.Marshal(),
 		PayloadA: encMsg.GetPayloadA(),
 		PayloadB: encMsg.GetPayloadB(),
 		Salt:     salt,
@@ -152,7 +152,10 @@ func (rm *ReceptionManager) send(session user.Session, topology *connect.Circuit
 func handleE2ESending(session user.Session,
 	message *format.Message,
 	rekey bool) {
-	recipientID := message.GetRecipient()
+	recipientID, err := message.GetRecipient()
+	if err != nil {
+		globals.Log.ERROR.Panic(err)
+	}
 
 	var key *keyStore.E2EKey
 	var action keyStore.Action
diff --git a/keyStore/keyManager.go b/keyStore/keyManager.go
index d6d58634521ac62aea3e8c8865a971cd7ca4043e..a6938702fa624f69145c159f74a883ae46da1e7e 100644
--- a/keyStore/keyManager.go
+++ b/keyStore/keyManager.go
@@ -34,7 +34,7 @@ type KeyManager struct {
 	pubKey *cyclic.Int
 
 	// Designates end-to-end partner
-	partner *id.User
+	partner *id.ID
 
 	// True if key manager tracks send keys, false if receive keys
 	sendOrRecv bool
@@ -76,7 +76,7 @@ type KeyManager struct {
 // All internal states are forced to 0 for safety purposes
 func NewManager(baseKey *cyclic.Int,
 	privKey *cyclic.Int, pubKey *cyclic.Int,
-	partner *id.User, sendOrRecv bool,
+	partner *id.ID, sendOrRecv bool,
 	numKeys uint32, ttl uint16, numReKeys uint16) *KeyManager {
 
 	km := new(KeyManager)
@@ -117,7 +117,7 @@ func (km *KeyManager) GetPubKey() *cyclic.Int {
 }
 
 // Get the partner ID from the Key Manager
-func (km *KeyManager) GetPartner() *id.User {
+func (km *KeyManager) GetPartner() *id.ID {
 	return km.partner
 }
 
@@ -263,7 +263,7 @@ func (km *KeyManager) checkRecvStateBit(rekey bool, keyNum uint32) bool {
 // E2E relationship is established, and also to generate all previously
 // unused keys based on KeyManager state, when reloading an user session
 // The function returns modifications that need to be independently made to the keystore.
-func (km *KeyManager) GenerateKeys(grp *cyclic.Group, userID *id.User) []*E2EKey {
+func (km *KeyManager) GenerateKeys(grp *cyclic.Group, userID *id.ID) []*E2EKey {
 	var recE2EKeys []*E2EKey
 
 	if km.sendOrRecv {
@@ -538,6 +538,12 @@ func (km *KeyManager) GobDecode(in []byte) error {
 		return err
 	}
 
+	partner, err := id.Unmarshal(s.Partner)
+	if err != nil {
+		return err
+	}
+	km.partner = partner
+
 	// Convert decoded bytes and put into key manager structure
 	km.baseKey = new(cyclic.Int)
 	err = km.baseKey.GobDecode(s.BaseKey)
@@ -566,7 +572,6 @@ func (km *KeyManager) GobDecode(in []byte) error {
 		km.sendOrRecv = false
 	}
 
-	km.partner = id.NewUserFromBytes(s.Partner)
 	km.sendState = new(uint64)
 	*km.sendState = binary.BigEndian.Uint64(s.State)
 	km.ttl = binary.BigEndian.Uint16(s.TTL)
diff --git a/keyStore/keyManager_test.go b/keyStore/keyManager_test.go
index 72fc65533ac1ed3588724a9aba4ea8ab7fefa1ff..3851e03e3e978539768f1d759ada70c3017b5ca0 100644
--- a/keyStore/keyManager_test.go
+++ b/keyStore/keyManager_test.go
@@ -47,7 +47,7 @@ func initGroup() *cyclic.Group {
 func TestKeyManager_New(t *testing.T) {
 	grp := cyclic.NewGroup(large.NewInt(107), large.NewInt(2))
 	baseKey := grp.NewInt(57)
-	partner := id.NewUserFromUint(14, t)
+	partner := id.NewIdFromUInt(14, id.User, t)
 
 	km := NewManager(baseKey, nil, nil,
 		partner, true, 12, 10, 10)
@@ -63,7 +63,7 @@ func TestKeyManager_GetBaseKey(t *testing.T) {
 	baseKey := grp.NewInt(57)
 	privKey := grp.NewInt(5)
 	pubKey := grp.NewInt(42)
-	partner := id.NewUserFromUint(14, t)
+	partner := id.NewIdFromUInt(14, id.User, t)
 
 	km := NewManager(baseKey, privKey, pubKey,
 		partner, true, 12, 10, 10)
@@ -83,7 +83,7 @@ func TestKeyManager_GetPrivKey(t *testing.T) {
 	baseKey := grp.NewInt(57)
 	privKey := grp.NewInt(5)
 	pubKey := grp.NewInt(42)
-	partner := id.NewUserFromUint(14, t)
+	partner := id.NewIdFromUInt(14, id.User, t)
 
 	km := NewManager(baseKey, privKey, pubKey,
 		partner, true, 12, 10, 10)
@@ -103,7 +103,7 @@ func TestKeyManager_GetPubKey(t *testing.T) {
 	baseKey := grp.NewInt(57)
 	privKey := grp.NewInt(5)
 	pubKey := grp.NewInt(42)
-	partner := id.NewUserFromUint(14, t)
+	partner := id.NewIdFromUInt(14, id.User, t)
 
 	km := NewManager(baseKey, privKey, pubKey,
 		partner, true, 12, 10, 10)
@@ -123,7 +123,7 @@ func TestKeyManager_GetPartner(t *testing.T) {
 	baseKey := grp.NewInt(57)
 	privKey := grp.NewInt(5)
 	pubKey := grp.NewInt(42)
-	partner := id.NewUserFromUint(14, t)
+	partner := id.NewIdFromUInt(14, id.User, t)
 
 	km := NewManager(baseKey, privKey, pubKey,
 		partner, true, 12, 10, 10)
@@ -141,7 +141,7 @@ func TestKeyManager_GetPartner(t *testing.T) {
 func TestKeyManager_Rekey(t *testing.T) {
 	grp := cyclic.NewGroup(large.NewInt(107), large.NewInt(2))
 	baseKey := grp.NewInt(57)
-	partner := id.NewUserFromUint(14, t)
+	partner := id.NewIdFromUInt(14, id.User, t)
 
 	km := NewManager(baseKey, nil, nil,
 		partner, true, 12, 10, 10)
@@ -166,7 +166,7 @@ func TestKeyManager_Rekey(t *testing.T) {
 func TestKeyManager_Purge(t *testing.T) {
 	grp := cyclic.NewGroup(large.NewInt(107), large.NewInt(2))
 	baseKey := grp.NewInt(57)
-	partner := id.NewUserFromUint(14, t)
+	partner := id.NewIdFromUInt(14, id.User, t)
 
 	km := NewManager(baseKey, nil, nil,
 		partner, true, 12, 10, 10)
@@ -198,7 +198,7 @@ func TestKeyManager_Purge(t *testing.T) {
 func TestKeyManager_UpdateRecvState(t *testing.T) {
 	grp := cyclic.NewGroup(large.NewInt(107), large.NewInt(2))
 	baseKey := grp.NewInt(57)
-	partner := id.NewUserFromUint(14, t)
+	partner := id.NewIdFromUInt(14, id.User, t)
 
 	km := NewManager(baseKey, nil, nil,
 		partner, false, 12, 10, 10)
@@ -230,8 +230,8 @@ func TestKeyManager_UpdateRecvState(t *testing.T) {
 func TestKeyManager_GenerateKeys(t *testing.T) {
 	grp := initGroup()
 	baseKey := grp.NewInt(57)
-	partner := id.NewUserFromUint(14, t)
-	userID := id.NewUserFromUint(18, t)
+	partner := id.NewIdFromUInt(14, id.User, t)
+	userID := id.NewIdFromUInt(18, id.User, t)
 
 	ks := NewStore()
 	kmSend := NewManager(baseKey, nil, nil,
@@ -291,8 +291,8 @@ func TestKeyManager_GenerateKeys(t *testing.T) {
 func TestKeyManager_Destroy(t *testing.T) {
 	grp := initGroup()
 	baseKey := grp.NewInt(57)
-	partner := id.NewUserFromUint(14, t)
-	userID := id.NewUserFromUint(18, t)
+	partner := id.NewIdFromUInt(14, id.User, t)
+	userID := id.NewIdFromUInt(18, id.User, t)
 
 	ks := NewStore()
 	km := NewManager(baseKey, nil, nil,
@@ -365,7 +365,7 @@ func TestKeyManager_GobSimple(t *testing.T) {
 	baseKey := grp.NewInt(57)
 	privKey := grp.NewInt(5)
 	pubKey := grp.NewInt(42)
-	partner := id.NewUserFromUint(14, t)
+	partner := id.NewIdFromUInt(14, id.User, t)
 
 	var byteBuf bytes.Buffer
 
@@ -471,8 +471,8 @@ func TestKeyManager_Gob(t *testing.T) {
 	baseKey := grp.NewInt(57)
 	privKey := grp.NewInt(5)
 	pubKey := grp.NewInt(42)
-	partner := id.NewUserFromUint(14, t)
-	userID := id.NewUserFromUint(18, t)
+	partner := id.NewIdFromUInt(14, id.User, t)
+	userID := id.NewIdFromUInt(18, id.User, t)
 
 	ks := NewStore()
 	km := NewManager(baseKey, privKey, pubKey,
diff --git a/keyStore/keyStore.go b/keyStore/keyStore.go
index a08a9938d450955d735427245cae614d8f4ab96c..8256cbde527b7025ea1842974b6c62df2b9de427 100644
--- a/keyStore/keyStore.go
+++ b/keyStore/keyStore.go
@@ -18,12 +18,12 @@ type keyManMap sync.Map
 type inKeyMap sync.Map
 
 // Stores a KeyManager entry for given user
-func (m *keyManMap) Store(user *id.User, km *KeyManager) {
+func (m *keyManMap) Store(user *id.ID, km *KeyManager) {
 	(*sync.Map)(m).Store(*user, km)
 }
 
 // Loads a KeyManager entry for given user
-func (m *keyManMap) Load(user *id.User) *KeyManager {
+func (m *keyManMap) Load(user *id.ID) *KeyManager {
 	val, ok := (*sync.Map)(m).Load(*user)
 	if !ok {
 		return nil
@@ -33,7 +33,7 @@ func (m *keyManMap) Load(user *id.User) *KeyManager {
 }
 
 // Deletes a KeyManager entry for given user
-func (m *keyManMap) Delete(user *id.User) {
+func (m *keyManMap) Delete(user *id.ID) {
 	(*sync.Map)(m).Delete(*user)
 }
 
@@ -50,10 +50,10 @@ func (m *keyManMap) values() []*KeyManager {
 
 // Internal helper function to get a list of all keys
 // contained in a KeyManMap
-func (m *keyManMap) keys() []id.User {
-	keyList := make([]id.User, 0)
+func (m *keyManMap) keys() []id.ID {
+	keyList := make([]id.ID, 0)
 	(*sync.Map)(m).Range(func(key, value interface{}) bool {
-		keyList = append(keyList, key.(id.User))
+		keyList = append(keyList, key.(id.ID))
 		return true
 	})
 	return keyList
@@ -114,7 +114,7 @@ type KeyStore struct {
 	params *KeyParams
 
 	// Transmission Keys map
-	// Maps id.User to *KeyManager
+	// Maps id.ID to *KeyManager
 	sendKeyManagers *keyManMap
 
 	// Reception Keys map
@@ -122,7 +122,7 @@ type KeyStore struct {
 	receptionKeys *inKeyMap
 
 	// Reception Key Managers map
-	recvKeyManagers map[id.User]*ReceptionKeyManagerBuffer
+	recvKeyManagers map[id.ID]*ReceptionKeyManagerBuffer
 
 	lock sync.Mutex
 }
@@ -140,11 +140,11 @@ func NewStore() *KeyStore {
 	}
 	ks.sendKeyManagers = new(keyManMap)
 	ks.receptionKeys = new(inKeyMap)
-	ks.recvKeyManagers = make(map[id.User]*ReceptionKeyManagerBuffer)
+	ks.recvKeyManagers = make(map[id.ID]*ReceptionKeyManagerBuffer)
 	return ks
 }
 
-func (ks *KeyStore) DeleteContactKeys(id *id.User) error {
+func (ks *KeyStore) DeleteContactKeys(id *id.ID) error {
 	ks.lock.Lock()
 	defer ks.lock.Unlock()
 
@@ -177,18 +177,18 @@ func (ks *KeyStore) AddSendManager(km *KeyManager) {
 
 // Get a Send KeyManager from respective map in KeyStore
 // based on partner ID
-func (ks *KeyStore) GetSendManager(partner *id.User) *KeyManager {
+func (ks *KeyStore) GetSendManager(partner *id.ID) *KeyManager {
 	return ks.sendKeyManagers.Load(partner)
 }
 
 // GetPartners returns the list of partners we have keys for
-func (ks *KeyStore) GetPartners() []id.User {
+func (ks *KeyStore) GetPartners() []id.ID {
 	return ks.sendKeyManagers.keys()
 }
 
 // Delete a Send KeyManager from respective map in KeyStore
 // based on partner ID
-func (ks *KeyStore) DeleteSendManager(partner *id.User) {
+func (ks *KeyStore) DeleteSendManager(partner *id.ID) {
 	ks.sendKeyManagers.Delete(partner)
 }
 
@@ -225,14 +225,14 @@ func (ks *KeyStore) AddRecvManager(km *KeyManager) {
 
 // Gets the Key manager at the current location on the ReceptionKeyManagerBuffer
 // based on partner ID
-func (ks *KeyStore) GetRecvManager(partner *id.User) *KeyManager {
+func (ks *KeyStore) GetRecvManager(partner *id.ID) *KeyManager {
 	ks.lock.Lock()
 	defer ks.lock.Unlock()
 	return ks.recvKeyManagers[*partner].getCurrentReceptionKeyManager()
 }
 
 // Delete a Receive KeyManager based on partner ID from respective map in KeyStore
-func (ks *KeyStore) DeleteRecvManager(partner *id.User) {
+func (ks *KeyStore) DeleteRecvManager(partner *id.ID) {
 	ks.lock.Lock()
 	defer ks.lock.Unlock()
 	delete(ks.recvKeyManagers, *partner)
@@ -316,7 +316,7 @@ func (ks *KeyStore) GobDecode(in []byte) error {
 // ReconstructKeys loops through all key managers and
 // calls GenerateKeys on each of them, in order to rebuild
 // the key maps
-func (ks *KeyStore) ReconstructKeys(grp *cyclic.Group, userID *id.User) {
+func (ks *KeyStore) ReconstructKeys(grp *cyclic.Group, userID *id.ID) {
 
 	kmList := ks.sendKeyManagers.values()
 	for _, km := range kmList {
diff --git a/keyStore/keyStore_test.go b/keyStore/keyStore_test.go
index 35077a3817b679626172ea162f827fa1424e40c3..ee334ff3ad7147cb1d6ba2f02423f4f5f1705b4a 100644
--- a/keyStore/keyStore_test.go
+++ b/keyStore/keyStore_test.go
@@ -40,8 +40,8 @@ func TestKeyStore_Gob(t *testing.T) {
 	baseKey := grp.NewInt(57)
 	privKey := grp.NewInt(5)
 	pubKey := grp.NewInt(42)
-	partner := id.NewUserFromUint(14, t)
-	userID := id.NewUserFromUint(18, t)
+	partner := id.NewIdFromUInt(14, id.User, t)
+	userID := id.NewIdFromUInt(18, id.User, t)
 
 	ks := NewStore()
 	km := NewManager(baseKey, privKey, pubKey,
@@ -126,8 +126,8 @@ func TestKeyStore_DeleteContactKeys(t *testing.T) {
 	baseKey := grp.NewInt(57)
 	privKey := grp.NewInt(5)
 	pubKey := grp.NewInt(42)
-	partner := id.NewUserFromUint(14, t)
-	userID := id.NewUserFromUint(18, t)
+	partner := id.NewIdFromUInt(14, id.User, t)
+	userID := id.NewIdFromUInt(18, id.User, t)
 
 	ks := NewStore()
 	km := NewManager(baseKey, privKey, pubKey,
diff --git a/keyStore/recieveKeyManagerBuffer_test.go b/keyStore/recieveKeyManagerBuffer_test.go
index 18b7bdbe02569cd56d0c492f2e068588f3e6a45a..a9caec7d1dba38af6a53253a26b64b518df0688b 100644
--- a/keyStore/recieveKeyManagerBuffer_test.go
+++ b/keyStore/recieveKeyManagerBuffer_test.go
@@ -13,8 +13,8 @@ func TestPush(t *testing.T) {
 
 	grp := initGroup()
 	baseKey := grp.NewInt(57)
-	partner := id.NewUserFromUint(14, t)
-	userID := id.NewUserFromUint(18, t)
+	partner := id.NewIdFromUInt(14, id.User, t)
+	userID := id.NewIdFromUInt(18, id.User, t)
 
 	//Generate twice the amount of keymanagers so we can test the circularness of the buffer as well
 	kmArray := []KeyManager{}
@@ -110,8 +110,8 @@ func TestReceptionKeyManagerBuffer_Gob(t *testing.T) {
 	aBuffer := NewReceptionKeyManagerBuffer()
 	grp := initGroup()
 	baseKey := grp.NewInt(57)
-	partner := id.NewUserFromUint(14, t)
-	userID := id.NewUserFromUint(18, t)
+	partner := id.NewIdFromUInt(14, id.User, t)
+	userID := id.NewIdFromUInt(18, id.User, t)
 
 	newKm := *NewManager(baseKey, nil,
 		nil, partner,
diff --git a/keyStore/rekeyManager.go b/keyStore/rekeyManager.go
index f83ace69887ad9fd5fb2a6db5a87e07ca5cb26ef..cfb0442141a2ac3f93c871d1d35decb953e4afb0 100644
--- a/keyStore/rekeyManager.go
+++ b/keyStore/rekeyManager.go
@@ -29,51 +29,51 @@ func (k *RekeyKeys) RotateKeysIfReady() {
 }
 
 type RekeyManager struct {
-	Ctxs map[id.User]*RekeyContext
-	Keys map[id.User]*RekeyKeys
+	Ctxs map[id.ID]*RekeyContext
+	Keys map[id.ID]*RekeyKeys
 	lock sync.Mutex
 }
 
 func NewRekeyManager() *RekeyManager {
 	return &RekeyManager{
-		Ctxs: make(map[id.User]*RekeyContext),
-		Keys: make(map[id.User]*RekeyKeys),
+		Ctxs: make(map[id.ID]*RekeyContext),
+		Keys: make(map[id.ID]*RekeyKeys),
 	}
 }
 
-func (rkm *RekeyManager) AddCtx(partner *id.User,
+func (rkm *RekeyManager) AddCtx(partner *id.ID,
 	ctx *RekeyContext) {
 	rkm.lock.Lock()
 	defer rkm.lock.Unlock()
 	rkm.Ctxs[*partner] = ctx
 }
 
-func (rkm *RekeyManager) GetCtx(partner *id.User) *RekeyContext {
+func (rkm *RekeyManager) GetCtx(partner *id.ID) *RekeyContext {
 	rkm.lock.Lock()
 	defer rkm.lock.Unlock()
 	return rkm.Ctxs[*partner]
 }
 
-func (rkm *RekeyManager) DeleteCtx(partner *id.User) {
+func (rkm *RekeyManager) DeleteCtx(partner *id.ID) {
 	rkm.lock.Lock()
 	defer rkm.lock.Unlock()
 	delete(rkm.Ctxs, *partner)
 }
 
-func (rkm *RekeyManager) AddKeys(partner *id.User,
+func (rkm *RekeyManager) AddKeys(partner *id.ID,
 	keys *RekeyKeys) {
 	rkm.lock.Lock()
 	defer rkm.lock.Unlock()
 	rkm.Keys[*partner] = keys
 }
 
-func (rkm *RekeyManager) GetKeys(partner *id.User) *RekeyKeys {
+func (rkm *RekeyManager) GetKeys(partner *id.ID) *RekeyKeys {
 	rkm.lock.Lock()
 	defer rkm.lock.Unlock()
 	return rkm.Keys[*partner]
 }
 
-func (rkm *RekeyManager) DeleteKeys(partner *id.User) {
+func (rkm *RekeyManager) DeleteKeys(partner *id.ID) {
 	rkm.lock.Lock()
 	defer rkm.lock.Unlock()
 	delete(rkm.Keys, *partner)
diff --git a/keyStore/rekeyManager_test.go b/keyStore/rekeyManager_test.go
index 648b0d6a08d23f80c7c7daaa15a27d77602ea329..87072da6f788bbc5c7e6aae38fea5e7e0c8d6b96 100644
--- a/keyStore/rekeyManager_test.go
+++ b/keyStore/rekeyManager_test.go
@@ -22,8 +22,8 @@ func TestRekeyManager_Ctx(t *testing.T) {
 	baseKey := grp.NewInt(57)
 	privKey := grp.NewInt(5)
 	pubKey := grp.NewInt(42)
-	partner := id.NewUserFromUint(14, t)
-	userID := id.NewUserFromUint(18, t)
+	partner := id.NewIdFromUInt(14, id.User, t)
+	userID := id.NewIdFromUInt(18, id.User, t)
 	rkm := NewRekeyManager()
 
 	val := &RekeyContext{
@@ -70,8 +70,8 @@ func TestRekeyManager_Keys(t *testing.T) {
 	grp := cyclic.NewGroup(large.NewInt(107), large.NewInt(2))
 	privKey := grp.NewInt(5)
 	pubKey := grp.NewInt(42)
-	partner := id.NewUserFromUint(14, t)
-	userID := id.NewUserFromUint(18, t)
+	partner := id.NewIdFromUInt(14, id.User, t)
+	userID := id.NewIdFromUInt(18, id.User, t)
 	rkm := NewRekeyManager()
 
 	val := &RekeyKeys{
diff --git a/parse/message.go b/parse/message.go
index 898d7e5e77244eccecfcf7cd96dd803cefeb84df..9e626dc2c524e9660275ee74854769b0852cd689 100644
--- a/parse/message.go
+++ b/parse/message.go
@@ -21,8 +21,8 @@ type Message struct {
 	TypedBody
 	// The crypto type is inferred from the message's contents
 	InferredType CryptoType
-	Sender       *id.User
-	Receiver     *id.User
+	Sender       *id.ID
+	Receiver     *id.ID
 	Nonce        []byte
 	Timestamp    time.Time
 }
@@ -46,12 +46,12 @@ func (ct CryptoType) String() string {
 type MessageInterface interface {
 	// Returns the message's sender ID
 	// (uint64) BigEndian serialized into a byte slice
-	GetSender() *id.User
+	GetSender() *id.ID
 	// Returns the message payload, without packed type
 	GetPayload() []byte
 	// Returns the message's recipient ID
 	// (uint64) BigEndian serialized into a byte slice
-	GetRecipient() *id.User
+	GetRecipient() *id.ID
 	// Return the message's inner type
 	GetMessageType() int32
 	// Returns the message's outer type
@@ -81,11 +81,11 @@ func (m Message) Hash() MessageHash {
 	return mh
 }
 
-func (m *Message) GetSender() *id.User {
+func (m *Message) GetSender() *id.ID {
 	return m.Sender
 }
 
-func (m *Message) GetRecipient() *id.User {
+func (m *Message) GetRecipient() *id.ID {
 	return m.Receiver
 }
 
diff --git a/parse/message_test.go b/parse/message_test.go
index 92746eff493349b1fa6a4d0c3e252040aeabce91..15933f84cfa7c526a2b4cf1dd33af1c63eca751f 100644
--- a/parse/message_test.go
+++ b/parse/message_test.go
@@ -18,8 +18,8 @@ func TestMessage_Hash(t *testing.T) {
 	m := Message{}
 	m.MessageType = 0
 	m.Body = []byte{0, 0}
-	m.Sender = id.ZeroID
-	m.Receiver = id.ZeroID
+	m.Sender = &id.ZeroUser
+	m.Receiver = &id.ZeroUser
 	m.Nonce = []byte{0, 0}
 
 	baseHash := m.Hash()
@@ -44,7 +44,7 @@ func TestMessage_Hash(t *testing.T) {
 
 	m.Body = []byte{0, 0}
 
-	newID := id.NewUserFromUint(1, t)
+	newID := id.NewIdFromUInt(1, id.User, t)
 	oldID := m.Sender
 	m.Sender = newID
 
diff --git a/rekey/rekey.go b/rekey/rekey.go
index 710cd6e658a4f2e9d37fc30620648eb88c7a2699..b013ad245f9b5d53994e4466756d81eb525ef831 100644
--- a/rekey/rekey.go
+++ b/rekey/rekey.go
@@ -9,7 +9,6 @@ import (
 	"gitlab.com/elixxir/client/keyStore"
 	"gitlab.com/elixxir/client/parse"
 	"gitlab.com/elixxir/client/user"
-	"gitlab.com/elixxir/comms/connect"
 	"gitlab.com/elixxir/crypto/cyclic"
 	"gitlab.com/elixxir/crypto/diffieHellman"
 	"gitlab.com/elixxir/crypto/e2e"
@@ -17,6 +16,7 @@ import (
 	"gitlab.com/elixxir/primitives/format"
 	"gitlab.com/elixxir/primitives/id"
 	"gitlab.com/elixxir/primitives/switchboard"
+	"gitlab.com/xx_network/comms/connect"
 )
 
 var session user.Session
@@ -110,10 +110,10 @@ func InitRekey(s user.Session, m io.Communications, t *connect.Circuit, host *co
 	//  messages that have a type that includes the outer type if that's not
 	//  possible
 	// in short, switchboard should be the package that includes outer
-	l.Register(id.ZeroID,
+	l.Register(&id.ZeroUser,
 		int32(cmixproto.Type_NO_TYPE),
 		&rekeyList)
-	l.Register(id.ZeroID,
+	l.Register(&id.ZeroUser,
 		int32(cmixproto.Type_REKEY_CONFIRM),
 		&rekeyConfirmList)
 }
@@ -127,7 +127,7 @@ const (
 	RekeyConfirm
 )
 
-func rekeyProcess(rt rekeyType, partner *id.User, data []byte) error {
+func rekeyProcess(rt rekeyType, partner *id.ID, data []byte) error {
 	rkm := session.GetRekeyManager()
 	e2egrp := session.GetE2EGroup()
 
diff --git a/rekey/rekey_test.go b/rekey/rekey_test.go
index 9c217871d430bdc524c957b7f3152e15874f424a..cd9019f5f82024a89e7fb1da845745effdbfd892 100644
--- a/rekey/rekey_test.go
+++ b/rekey/rekey_test.go
@@ -2,13 +2,13 @@ package rekey
 
 import (
 	"bytes"
+	"encoding/binary"
 	"fmt"
 	"gitlab.com/elixxir/client/cmixproto"
 	"gitlab.com/elixxir/client/globals"
 	"gitlab.com/elixxir/client/keyStore"
 	"gitlab.com/elixxir/client/parse"
 	"gitlab.com/elixxir/client/user"
-	"gitlab.com/elixxir/comms/connect"
 	"gitlab.com/elixxir/crypto/csprng"
 	"gitlab.com/elixxir/crypto/cyclic"
 	"gitlab.com/elixxir/crypto/diffieHellman"
@@ -17,6 +17,7 @@ import (
 	"gitlab.com/elixxir/crypto/large"
 	"gitlab.com/elixxir/crypto/signature/rsa"
 	"gitlab.com/elixxir/primitives/id"
+	"gitlab.com/xx_network/comms/connect"
 	"os"
 	"testing"
 	"time"
@@ -31,7 +32,7 @@ type dummyMessaging struct {
 // SendMessage to the server
 func (d *dummyMessaging) SendMessage(sess user.Session,
 	topology *connect.Circuit,
-	recipientID *id.User,
+	recipientID *id.ID,
 	cryptoType parse.CryptoType,
 	message []byte, transmissionHost *connect.Host) error {
 	d.listener <- message
@@ -41,7 +42,7 @@ func (d *dummyMessaging) SendMessage(sess user.Session,
 // SendMessage without partitions to the server
 func (d *dummyMessaging) SendMessageNoPartition(sess user.Session,
 	topology *connect.Circuit,
-	recipientID *id.User,
+	recipientID *id.ID,
 	cryptoType parse.CryptoType,
 	message []byte, transmissionHost *connect.Host) error {
 	d.listener <- message
@@ -59,14 +60,18 @@ func TestMain(m *testing.M) {
 	user.InitUserRegistry(grp)
 	rng := csprng.NewSystemRNG()
 	u := &user.User{
-		User:     id.NewUserFromUints(&[4]uint64{0, 0, 0, 18}),
+		User:     new(id.ID),
 		Username: "Bernie",
 	}
+	binary.BigEndian.PutUint64(u.User[:], 18)
+	u.User.SetType(id.User)
 	myPrivKeyCyclicCMIX := grp.RandomCoprime(grp.NewMaxInt())
 	myPubKeyCyclicCMIX := grp.ExpG(myPrivKeyCyclicCMIX, grp.NewInt(1))
 	myPrivKeyCyclicE2E := e2eGrp.RandomCoprime(e2eGrp.NewMaxInt())
 	myPubKeyCyclicE2E := e2eGrp.ExpG(myPrivKeyCyclicE2E, e2eGrp.NewInt(1))
-	partnerID := id.NewUserFromUints(&[4]uint64{0, 0, 0, 12})
+	partnerID := new(id.ID)
+	binary.BigEndian.PutUint64(partnerID[:], 12)
+	partnerID.SetType(id.User)
 
 	partnerPubKeyCyclic := e2eGrp.RandomCoprime(e2eGrp.NewMaxInt())
 
@@ -83,7 +88,9 @@ func TestMain(m *testing.M) {
 	}
 
 	rekeyChan2 := make(chan struct{}, 50)
-	InitRekey(session, fakeComm, connect.NewCircuit([]*id.Node{id.NewNodeFromBytes(make([]byte, id.NodeIdLen))}), nil, rekeyChan2)
+	nodeID := new(id.ID)
+	nodeID.SetType(id.Node)
+	InitRekey(session, fakeComm, connect.NewCircuit([]*id.ID{nodeID}), nil, rekeyChan2)
 
 	// Create E2E relationship with partner
 	// Generate baseKey
@@ -129,7 +136,9 @@ func TestMain(m *testing.M) {
 
 // Test RekeyTrigger
 func TestRekeyTrigger(t *testing.T) {
-	partnerID := id.NewUserFromUints(&[4]uint64{0, 0, 0, 12})
+	partnerID := new(id.ID)
+	binary.BigEndian.PutUint64(partnerID[:], 12)
+	partnerID.SetType(id.User)
 	km := session.GetKeyStore().GetRecvManager(partnerID)
 	partnerPubKey := km.GetPubKey()
 	// Test receiving a RekeyTrigger message
@@ -185,7 +194,9 @@ func TestRekeyTrigger(t *testing.T) {
 
 // Test RekeyConfirm
 func TestRekeyConfirm(t *testing.T) {
-	partnerID := id.NewUserFromUints(&[4]uint64{0, 0, 0, 12})
+	partnerID := new(id.ID)
+	binary.BigEndian.PutUint64(partnerID[:], 12)
+	partnerID.SetType(id.User)
 	rekeyCtx := session.GetRekeyManager().GetCtx(partnerID)
 	baseKey := rekeyCtx.BaseKey
 	// Test receiving a RekeyConfirm message with wrong H(baseKey)
@@ -253,7 +264,9 @@ func TestRekeyConfirm(t *testing.T) {
 
 // Test Rekey
 func TestRekey(t *testing.T) {
-	partnerID := id.NewUserFromUints(&[4]uint64{0, 0, 0, 12})
+	partnerID := new(id.ID)
+	binary.BigEndian.PutUint64(partnerID[:], 12)
+	partnerID.SetType(id.User)
 	km := session.GetKeyStore().GetSendManager(partnerID)
 	// Generate new partner public key
 	_, grp := getGroups()
@@ -312,7 +325,9 @@ func TestRekey(t *testing.T) {
 
 // Test Rekey errors
 func TestRekey_Errors(t *testing.T) {
-	partnerID := id.NewUserFromUints(&[4]uint64{0, 0, 0, 12})
+	partnerID := new(id.ID)
+	binary.BigEndian.PutUint64(partnerID[:], 12)
+	partnerID.SetType(id.User)
 	km := session.GetKeyStore().GetRecvManager(partnerID)
 	partnerPubKey := km.GetPubKey()
 	// Delete RekeyKeys so that RekeyTrigger and rekey error out
diff --git a/storage/versionedkv.go b/storage/versionedkv.go
new file mode 100644
index 0000000000000000000000000000000000000000..edd315658655e27a0c4a278ed9781615db5ce9d0
--- /dev/null
+++ b/storage/versionedkv.go
@@ -0,0 +1,121 @@
+package storage
+
+import (
+	"encoding/json"
+	"fmt"
+	"gitlab.com/elixxir/ekv"
+	"strconv"
+	"strings"
+)
+
+// MakeKeyPrefix provides a helper with a data type and a version
+// TODO: We might need a separator string here, or a fixed number of
+//       digits available to the version string
+//  Otherwise version 10 could be mistaken for version 1! Bad news
+//  For now, let's hope a semicolon won't be part of the rest of the key
+//  It's not in base64, so maybe it will be fine
+func MakeKeyPrefix(dataType string, version uint64) string {
+	return dataType + strconv.FormatUint(version, 10) + ";"
+}
+
+// VersionedObject is used by VersionedKeyValue to keep track of
+// versioning and time of storage
+type VersionedObject struct {
+	// Used to determine version upgrade, if any
+	Version uint64
+
+	// Marshal to/from time.Time using Time.MarshalText and
+	// Time.UnmarshalText
+	Timestamp []byte
+
+	// Serialized version of original object
+	Data []byte
+}
+
+// Unmarshal deserializes a VersionedObject from a byte slice. It's used to
+// make these storable in a KeyValue.
+// VersionedObject exports all fields and they have simple types, so
+// json.Unmarshal works fine.
+func (v *VersionedObject) Unmarshal(data []byte) error {
+	return json.Unmarshal(data, v)
+}
+
+// Marshal serializes a VersionedObject into a byte slice. It's used to
+// make these storable in a KeyValue.
+// VersionedObject exports all fields and they have simple types, so
+// json.Marshal works fine.
+func (v *VersionedObject) Marshal() []byte {
+	d, err := json.Marshal(v)
+	// Not being to marshal this simple object means something is really
+	// wrong
+	if err != nil {
+		panic(fmt.Sprintf("Could not marshal: %+v", v))
+	}
+	return d
+}
+
+// Upgrade functions must be of this type
+type upgrade func(key string, oldObject *VersionedObject) (*VersionedObject,
+	error)
+
+// VersionedKV stores versioned data and upgrade functions
+type VersionedKV struct {
+	upgradeTable map[string]upgrade
+	data         ekv.KeyValue
+}
+
+// Create a versioned key/value store backed by something implementing KeyValue
+func NewVersionedKV(data ekv.KeyValue) *VersionedKV {
+	newKV := new(VersionedKV)
+	// Add new upgrade functions to this upgrade table
+	newKV.upgradeTable = make(map[string]upgrade)
+	// All upgrade functions should upgrade to the latest version. You can
+	// call older upgrade functions if you need to. Upgrade functions don't
+	// change the key or store the upgraded version of the data in the
+	// key/value store. There's no mechanism built in for this -- users
+	// should always make the key prefix before calling Set, and if they
+	// want the upgraded data persisted they should call Set with the
+	// upgraded data.
+	newKV.upgradeTable[MakeKeyPrefix("test", 0)] = func(key string,
+		oldObject *VersionedObject) (*VersionedObject, error) {
+		return &VersionedObject{
+			Version: 1,
+			// Upgrade functions don't need to update the timestamp
+			Timestamp: oldObject.Timestamp,
+			Data: []byte("this object was upgraded from v0" +
+				" to v1"),
+		}, nil
+	}
+	newKV.data = data
+	return newKV
+}
+
+// Get gets and upgrades data stored in the key/value store
+// Make sure to inspect the version returned in the versioned object
+func (v *VersionedKV) Get(key string) (*VersionedObject, error) {
+	// Get raw data
+	result := VersionedObject{}
+	err := v.data.Get(key, &result)
+	if err != nil {
+		return nil, err
+	}
+	// If the key starts with a version tag that we can find in the table,
+	// we should call that function to upgrade it
+	for version, upgrade := range v.upgradeTable {
+		if strings.HasPrefix(key, version) {
+			// We should run this upgrade function
+			// The user of this function must update the key
+			// based on the version returned in this
+			// versioned object!
+			return upgrade(key, &result)
+		}
+	}
+	return &result, nil
+}
+
+// Set upserts new data into the storage
+// When calling this, you are responsible for prefixing the key with the correct
+// type and version! Call MakeKeyPrefix() to do so.
+func (v *VersionedKV) Set(key string, object *VersionedObject) error {
+	return v.data.Set(key, object)
+}
diff --git a/storage/versionedkv_test.go b/storage/versionedkv_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..4a7ccb8754bd6b70df058bc2a3c20707f9c2a5e4
--- /dev/null
+++ b/storage/versionedkv_test.go
@@ -0,0 +1,146 @@
+package storage
+
+import (
+	"bytes"
+	"gitlab.com/elixxir/ekv"
+	"reflect"
+	"testing"
+	"time"
+)
+
+// Shows that all fields can be serialized/deserialized correctly using json
+func TestVersionedObject_MarshalUnmarshal(t *testing.T) {
+	sometime, err := time.Date(1, 2, 3, 4, 5, 6, 7, time.UTC).MarshalText()
+	if err != nil {
+		// Should never happen
+		t.Fatal(err)
+	}
+
+	original := VersionedObject{
+		Version:   8,
+		Timestamp: sometime,
+		Data:      []byte("original text"),
+	}
+
+	marshalled := original.Marshal()
+
+	unmarshalled := VersionedObject{}
+	err = unmarshalled.Unmarshal(marshalled)
+	if err != nil {
+		// Should never happen
+		t.Fatal(err)
+	}
+
+	if !reflect.DeepEqual(original, unmarshalled) {
+		t.Error("Original and serialized/deserialized objects not equal")
+	}
+	t.Logf("%+v", unmarshalled)
+}
+
+// VersionedKV Get should call the upgrade function when it's available
+func TestVersionedKV_Get_Err(t *testing.T) {
+	kv := make(ekv.Memstore)
+	vkv := NewVersionedKV(kv)
+	key := MakeKeyPrefix("test", 0) + "12345"
+	result, err := vkv.Get(key)
+	if err == nil {
+		t.Error("Getting a key that didn't exist should have returned an error")
+	}
+	if result != nil {
+		t.Error("Getting a key that didn't exist shouldn't have returned data")
+	}
+}
+
+// Test versioned KV upgrade path
+func TestVersionedKV_Get_Upgrade(t *testing.T) {
+	// Set up a dummy KV with the required data
+	kv := make(ekv.Memstore)
+	vkv := NewVersionedKV(kv)
+	key := MakeKeyPrefix("test", 0) + "12345"
+	now := time.Now()
+	nowText, err := now.MarshalText()
+	if err != nil {
+		//Should never happen
+		t.Fatal(err)
+	}
+	original := VersionedObject{
+		Version:   0,
+		Timestamp: nowText,
+		Data:      []byte("not upgraded"),
+	}
+	originalSerialized := original.Marshal()
+	if err != nil {
+		t.Fatal(err)
+	}
+	kv[key] = originalSerialized
+
+	result, err := vkv.Get(key)
+	if err != nil {
+		t.Fatalf("Error getting something that should have been in: %v", err)
+	}
+	if !bytes.Equal(result.Data, []byte("this object was upgraded from v0 to v1")) {
+		t.Errorf("upgrade should have overwritten data. result data: %q", result.Data)
+	}
+}
+
+// Test Get without upgrade path
+func TestVersionedKV_Get(t *testing.T) {
+	// Set up a dummy KV with the required data
+	kv := make(ekv.Memstore)
+	vkv := NewVersionedKV(kv)
+	originalVersion := uint64(1)
+	key := MakeKeyPrefix("test", originalVersion) + "12345"
+	now := time.Now()
+	nowText, err := now.MarshalText()
+	if err != nil {
+		//Should never happen
+		t.Fatal(err)
+	}
+	original := VersionedObject{
+		Version:   originalVersion,
+		Timestamp: nowText,
+		Data:      []byte("not upgraded"),
+	}
+	originalSerialized := original.Marshal()
+	if err != nil {
+		t.Fatal(err)
+	}
+	kv[key] = originalSerialized
+
+	result, err := vkv.Get(key)
+	if err != nil {
+		t.Fatalf("Error getting something that should have been in: %v", err)
+	}
+	if !bytes.Equal(result.Data, []byte("not upgraded")) {
+		t.Errorf("upgrade should not have overwritten data. result data: %q", result.Data)
+	}
+}
+
+// Test that Set puts data in the store
+func TestVersionedKV_Set(t *testing.T) {
+	kv := make(ekv.Memstore)
+	vkv := NewVersionedKV(kv)
+	originalVersion := uint64(1)
+	key := MakeKeyPrefix("test", originalVersion) + "12345"
+	now := time.Now()
+	nowText, err := now.MarshalText()
+	if err != nil {
+		//Should never happen
+		t.Fatal(err)
+	}
+	original := VersionedObject{
+		Version:   originalVersion,
+		Timestamp: nowText,
+		Data:      []byte("not upgraded"),
+	}
+	err = vkv.Set(key, &original)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	// Store should now have data in it at that key
+	_, ok := kv[key]
+	if !ok {
+		t.Error("data store didn't have anything in the key")
+	}
+}
diff --git a/user/regCode.go b/user/regCode.go
new file mode 100644
index 0000000000000000000000000000000000000000..65094ad616ef680f8ea7a193d505c45e00cc1dc8
--- /dev/null
+++ b/user/regCode.go
@@ -0,0 +1,21 @@
+package user
+
+import (
+	"encoding/base32"
+	"gitlab.com/elixxir/primitives/id"
+	"golang.org/x/crypto/blake2b"
+)
+
+const RegCodeLen = 5
+
+func RegistrationCode(id *id.ID) string {
+	return base32.StdEncoding.EncodeToString(userHash(id))
+}
+
+func userHash(id *id.ID) []byte {
+	h, _ := blake2b.New256(nil)
+	h.Write(id.Marshal())
+	huid := h.Sum(nil)
+	huid = huid[len(huid)-RegCodeLen:]
+	return huid
+}
diff --git a/user/session.go b/user/session.go
index 325db8c2db4c2eb13658903d8e69b67859f42a85..04665d6579c272144b08d222d7d7e911c882881b 100644
--- a/user/session.go
+++ b/user/session.go
@@ -17,12 +17,12 @@ import (
 	"github.com/pkg/errors"
 	"gitlab.com/elixxir/client/globals"
 	"gitlab.com/elixxir/client/keyStore"
-	"gitlab.com/elixxir/comms/connect"
 	"gitlab.com/elixxir/crypto/cyclic"
 	"gitlab.com/elixxir/crypto/signature/rsa"
 	"gitlab.com/elixxir/primitives/format"
 	"gitlab.com/elixxir/primitives/id"
 	"gitlab.com/elixxir/primitives/switchboard"
+	"gitlab.com/xx_network/comms/connect"
 	"io"
 	"sync"
 	"sync/atomic"
@@ -36,7 +36,7 @@ var ErrQuery = errors.New("element not in map")
 type Session interface {
 	GetCurrentUser() (currentUser *User)
 	GetNodeKeys(topology *connect.Circuit) []NodeKeys
-	PushNodeKey(id *id.Node, key NodeKeys)
+	PushNodeKey(id *id.ID, key NodeKeys)
 	GetRSAPrivateKey() *rsa.PrivateKey
 	GetRSAPublicKey() *rsa.PublicKey
 	GetCMIXDHPrivateKey() *cyclic.Int
@@ -60,7 +60,7 @@ type Session interface {
 	UnlockStorage()
 	GetSessionData() ([]byte, error)
 	GetRegistrationValidationSignature() []byte
-	GetNodes() map[id.Node]int
+	GetNodes() map[id.ID]int
 	AppendGarbledMessage(messages ...*format.Message)
 	PopGarbledMessages() []*format.Message
 	GetSalt() []byte
@@ -68,9 +68,9 @@ type Session interface {
 	GetRegState() uint32
 	ChangeUsername(string) error
 	StorageIsEmpty() bool
-	GetContactByValue(string) (*id.User, []byte)
-	StoreContactByValue(string, *id.User, []byte)
-	DeleteContact(*id.User) (string, error)
+	GetContactByValue(string) (*id.ID, []byte)
+	StoreContactByValue(string, *id.ID, []byte)
+	DeleteContact(*id.ID) (string, error)
 	GetSessionLocation() uint8
 	LoadEncryptedSession(store globals.Storage) ([]byte, error)
 	RegisterPermissioningSignature(sig []byte) error
@@ -96,7 +96,7 @@ func NewSession(store globals.Storage,
 	regState := uint32(KeyGenComplete)
 	// With an underlying Session data structure
 	return Session(&SessionObj{
-		NodeKeys:            make(map[id.Node]NodeKeys),
+		NodeKeys:            make(map[id.ID]NodeKeys),
 		CurrentUser:         u,
 		RSAPublicKey:        publicKeyRSA,
 		RSAPrivateKey:       privateKeyRSA,
@@ -173,7 +173,7 @@ func LoadSession(store globals.Storage, password string) (Session, error) {
 	session.password = password
 
 	if session.NodeKeys == nil {
-		session.NodeKeys = make(map[id.Node]NodeKeys)
+		session.NodeKeys = make(map[id.ID]NodeKeys)
 	}
 
 	return &session, nil
@@ -246,7 +246,7 @@ type SessionObj struct {
 	// Currently authenticated user
 	CurrentUser *User
 
-	NodeKeys         map[id.Node]NodeKeys
+	NodeKeys         map[id.ID]NodeKeys
 	RSAPrivateKey    *rsa.PrivateKey
 	RSAPublicKey     *rsa.PublicKey
 	CMIXDHPrivateKey *cyclic.Int
@@ -325,7 +325,7 @@ func (s *SessionObj) LoadEncryptedSession(store globals.Storage) ([]byte, error)
 }
 
 type SearchedUserRecord struct {
-	Id id.User
+	Id id.ID
 	Pk []byte
 }
 
@@ -348,10 +348,10 @@ func (s *SessionObj) SetLastMessageID(id string) {
 	s.UnlockStorage()
 }
 
-func (s *SessionObj) GetNodes() map[id.Node]int {
+func (s *SessionObj) GetNodes() map[id.ID]int {
 	s.LockStorage()
 	defer s.UnlockStorage()
-	nodes := make(map[id.Node]int, 0)
+	nodes := make(map[id.ID]int, 0)
 	for node := range s.NodeKeys {
 		nodes[node] = 1
 	}
@@ -379,7 +379,7 @@ func (s *SessionObj) GetNodeKeys(topology *connect.Circuit) []NodeKeys {
 	return keys
 }
 
-func (s *SessionObj) PushNodeKey(id *id.Node, key NodeKeys) {
+func (s *SessionObj) PushNodeKey(id *id.ID, key NodeKeys) {
 	s.LockStorage()
 	defer s.UnlockStorage()
 
@@ -731,7 +731,7 @@ func (s *SessionObj) PopGarbledMessages() []*format.Message {
 	return tempBuffer
 }
 
-func (s *SessionObj) GetContactByValue(v string) (*id.User, []byte) {
+func (s *SessionObj) GetContactByValue(v string) (*id.ID, []byte) {
 	s.LockStorage()
 	defer s.UnlockStorage()
 	u, ok := s.ContactsByValue[v]
@@ -741,7 +741,7 @@ func (s *SessionObj) GetContactByValue(v string) (*id.User, []byte) {
 	return &(u.Id), u.Pk
 }
 
-func (s *SessionObj) StoreContactByValue(v string, uid *id.User, pk []byte) {
+func (s *SessionObj) StoreContactByValue(v string, uid *id.ID, pk []byte) {
 	s.LockStorage()
 	defer s.UnlockStorage()
 	u, ok := s.ContactsByValue[v]
@@ -756,7 +756,7 @@ func (s *SessionObj) StoreContactByValue(v string, uid *id.User, pk []byte) {
 	}
 }
 
-func (s *SessionObj) DeleteContact(uid *id.User) (string, error) {
+func (s *SessionObj) DeleteContact(uid *id.ID) (string, error) {
 	s.LockStorage()
 	defer s.UnlockStorage()
 
diff --git a/user/session_test.go b/user/session_test.go
index 59068c9a7fa83d9668bb9502ce85eed2a30d42b3..ddf7201574ec3eb66bd06c5bb87c8932d4ccd5e5 100644
--- a/user/session_test.go
+++ b/user/session_test.go
@@ -11,12 +11,12 @@ import (
 	"crypto/sha256"
 	"encoding/gob"
 	"gitlab.com/elixxir/client/globals"
-	"gitlab.com/elixxir/comms/connect"
 	"gitlab.com/elixxir/crypto/cyclic"
 	"gitlab.com/elixxir/crypto/large"
 	"gitlab.com/elixxir/crypto/signature/rsa"
 	"gitlab.com/elixxir/primitives/format"
 	"gitlab.com/elixxir/primitives/id"
+	"gitlab.com/xx_network/comms/connect"
 	"math/rand"
 	"reflect"
 	"testing"
@@ -34,14 +34,14 @@ func TestUserSession(t *testing.T) {
 	// sure that the gob contains the user ID
 	UID := uint64(65)
 
-	u.User = id.NewUserFromUint(UID, t)
+	u.User = id.NewIdFromUInt(UID, id.User, t)
 	u.Username = "Mario"
 
 	grp := cyclic.NewGroup(large.NewInt(107), large.NewInt(2))
 
-	nodeID := id.NewNodeFromUInt(1, t)
+	nodeID := id.NewIdFromUInt(1, id.Node, t)
 
-	topology := connect.NewCircuit([]*id.Node{nodeID})
+	topology := connect.NewCircuit([]*id.ID{nodeID})
 
 	// Storage
 	storage := &globals.RamStorage{}
@@ -220,12 +220,12 @@ func TestSessionObj_DeleteContact(t *testing.T) {
 	// sure that the gob contains the user ID
 	UID := uint64(65)
 
-	u.User = id.NewUserFromUint(UID, t)
+	u.User = id.NewIdFromUInt(UID, id.User, t)
 	u.Username = "Mario"
 
 	grp := cyclic.NewGroup(large.NewInt(107), large.NewInt(2))
 
-	nodeID := id.NewNodeFromUInt(1, t)
+	nodeID := id.NewIdFromUInt(1, id.Node, t)
 
 	// Storage
 	storage := &globals.RamStorage{}
@@ -261,9 +261,10 @@ func TestSessionObj_DeleteContact(t *testing.T) {
 		ReceptionKey:    grp.NewInt(2),
 	})
 
-	ses.StoreContactByValue("test", id.NewUserFromBytes([]byte("test")), []byte("test"))
+	testContact := id.NewIdFromString("test", id.User, t)
+	ses.StoreContactByValue("test", testContact, []byte("test"))
 
-	_, err = ses.DeleteContact(id.NewUserFromBytes([]byte("test")))
+	_, err = ses.DeleteContact(testContact)
 	if err != nil {
 		t.Errorf("Failed to delete contact: %+v", err)
 	}
@@ -271,7 +272,7 @@ func TestSessionObj_DeleteContact(t *testing.T) {
 
 func TestGetPubKey(t *testing.T) {
 	u := new(User)
-	UID := id.NewUserFromUint(1, t)
+	UID := id.NewIdFromUInt(1, id.User, t)
 
 	u.User = UID
 	u.Username = "Mario"
@@ -304,7 +305,7 @@ func TestGetPubKey(t *testing.T) {
 			err.Error())
 	}
 
-	ses.PushNodeKey(id.NewNodeFromUInt(1, t), NodeKeys{
+	ses.PushNodeKey(id.NewIdFromUInt(1, id.Node, t), NodeKeys{
 		TransmissionKey: grp.NewInt(2),
 		ReceptionKey:    grp.NewInt(2),
 	})
@@ -323,12 +324,12 @@ func TestSessionObj_StorageIsEmpty(t *testing.T) {
 	// sure that the gob contains the user ID
 	UID := uint64(65)
 
-	u.User = id.NewUserFromUint(UID, t)
+	u.User = id.NewIdFromUInt(UID, id.User, t)
 	u.Username = "Mario"
 
 	grp := cyclic.NewGroup(large.NewInt(107), large.NewInt(2))
 
-	nodeID := id.NewNodeFromUInt(1, t)
+	nodeID := id.NewIdFromUInt(1, id.Node, t)
 	// Storage
 	storage := &globals.RamStorage{}
 
@@ -381,12 +382,12 @@ func TestSessionObj_GetContactByValue(t *testing.T) {
 	// sure that the gob contains the user ID
 	UID := uint64(65)
 
-	u.User = id.NewUserFromUint(UID, t)
+	u.User = id.NewIdFromUInt(UID, id.User, t)
 	u.Username = "Mario"
 
 	grp := cyclic.NewGroup(large.NewInt(107), large.NewInt(2))
 
-	nodeID := id.NewNodeFromUInt(1, t)
+	nodeID := id.NewIdFromUInt(1, id.Node, t)
 
 	// Storage
 	storage := &globals.RamStorage{}
@@ -420,7 +421,9 @@ func TestSessionObj_GetContactByValue(t *testing.T) {
 		ReceptionKey:    grp.NewInt(2),
 	})
 
-	ses.StoreContactByValue("value", id.NewUserFromBytes([]byte("test")), []byte("test"))
+	userId := id.NewIdFromBytes([]byte("test"), t)
+
+	ses.StoreContactByValue("value", userId, []byte("test"))
 
 	observedUser, observedPk := ses.GetContactByValue("value")
 
@@ -429,7 +432,7 @@ func TestSessionObj_GetContactByValue(t *testing.T) {
 			"Expected: %+v\n\tRecieved: %+v", privateKey.PublicKey.N.Bytes(), observedPk)
 	}
 
-	if !observedUser.Cmp(id.NewUserFromBytes([]byte("test"))) {
+	if !observedUser.Cmp(userId) {
 		t.Errorf("Failed to retrieve user using GetContactByValue;"+
 			"Expected: %+v\n\tRecieved: %+v", u.User, observedUser)
 	}
@@ -437,7 +440,7 @@ func TestSessionObj_GetContactByValue(t *testing.T) {
 
 func TestGetPrivKey(t *testing.T) {
 	u := new(User)
-	UID := id.NewUserFromUint(1, t)
+	UID := id.NewIdFromUInt(1, id.User, t)
 
 	u.User = UID
 	u.Username = "Mario"
@@ -470,7 +473,7 @@ func TestGetPrivKey(t *testing.T) {
 			err.Error())
 	}
 
-	ses.PushNodeKey(id.NewNodeFromUInt(1, t), NodeKeys{
+	ses.PushNodeKey(id.NewIdFromUInt(1, id.Node, t), NodeKeys{
 		TransmissionKey: grp.NewInt(2),
 		ReceptionKey:    grp.NewInt(2),
 	})
@@ -637,7 +640,7 @@ func GenerateTestMessages(size int) []*format.Message {
 // Happy path
 func TestConvertSessionV1toV2(t *testing.T) {
 	u := new(User)
-	UID := id.NewUserFromUint(1, t)
+	UID := id.NewIdFromUInt(1, id.Node, t)
 
 	u.User = UID
 	u.Username = "Bernie"
diff --git a/user/sessionv1.go b/user/sessionv1.go
index 0496913de013df8972cc45d0c5fb7dc88e785bb5..3f470aa558447c8edf0aad156db45e6a23d52d21 100644
--- a/user/sessionv1.go
+++ b/user/sessionv1.go
@@ -20,7 +20,7 @@ type SessionObjV1 struct {
 	// Currently authenticated user
 	CurrentUser *UserV1
 
-	Keys             map[id.Node]NodeKeys
+	Keys             map[id.ID]NodeKeys
 	RSAPrivateKey    *rsa.PrivateKey
 	RSAPublicKey     *rsa.PublicKey
 	CMIXDHPrivateKey *cyclic.Int
@@ -73,7 +73,7 @@ type SessionObjV1 struct {
 
 // Struct representing a User in the system
 type UserV1 struct {
-	User  *id.User
+	User  *id.ID
 	Nick  string
 	Email string
 }
diff --git a/user/user.go b/user/user.go
index 209f039c94751cb6a11d8d8b42ba877d6fd4daac..45e0bc65cf265247df30e08c830ee87d958f006c 100644
--- a/user/user.go
+++ b/user/user.go
@@ -8,6 +8,7 @@ package user
 
 import (
 	"crypto/sha256"
+	"encoding/binary"
 	"gitlab.com/elixxir/client/globals"
 	"gitlab.com/elixxir/crypto/cyclic"
 	"gitlab.com/elixxir/primitives/id"
@@ -31,25 +32,25 @@ func InitUserRegistry(grp *cyclic.Group) {
 
 // Interface for User Registry operations
 type Registry interface {
-	NewUser(id *id.User, nickname string) *User
-	DeleteUser(id *id.User)
-	GetUser(id *id.User) (user *User, ok bool)
+	NewUser(id *id.ID, nickname string) *User
+	DeleteUser(id *id.ID)
+	GetUser(id *id.ID) (user *User, ok bool)
 	UpsertUser(user *User)
 	CountUsers() int
-	LookupUser(hid string) (uid *id.User, ok bool)
-	LookupKeys(uid *id.User) (*NodeKeys, bool)
+	LookupUser(hid string) (uid *id.ID, ok bool)
+	LookupKeys(uid *id.ID) (*NodeKeys, bool)
 }
 
 type UserMap struct {
 	// Map acting as the User Registry containing User -> ID mapping
-	userCollection map[id.User]*User
+	userCollection map[id.ID]*User
 	// Increments sequentially for User.ID values
 	idCounter uint64
 	// Temporary map acting as a lookup table for demo user registration codes
 	// Key type is string because keys must implement == and []byte doesn't
-	userLookup map[string]*id.User
+	userLookup map[string]*id.ID
 	//Temporary placed to store the keys for each user
-	keysLookup map[id.User]*NodeKeys
+	keysLookup map[id.ID]*NodeKeys
 }
 
 // newRegistry creates a new Registry interface
@@ -57,21 +58,22 @@ func newRegistry(grp *cyclic.Group) Registry {
 	if len(DemoChannelNames) > 10 || len(DemoUserNicks) > 30 {
 		globals.Log.ERROR.Print("Not enough demo users have been hardcoded.")
 	}
-	userUserIdMap := make(map[id.User]*User)
-	userRegCodeMap := make(map[string]*id.User)
-	nk := make(map[id.User]*NodeKeys)
+	userUserIdMap := make(map[id.ID]*User)
+	userRegCodeMap := make(map[string]*id.ID)
+	nk := make(map[id.ID]*NodeKeys)
 
 	// Deterministically create NumDemoUsers users
 	// TODO Replace this with real user registration/discovery
 	for i := uint64(1); i <= NumDemoUsers; i++ {
-		currentID := id.NewUserFromUints(&[4]uint64{0, 0, 0, i})
+		currentID := new(id.ID)
+		binary.BigEndian.PutUint64(currentID[:], i)
+		currentID.SetType(id.User)
 		newUsr := new(User)
 		nodeKey := new(NodeKeys)
 
 		// Generate user parameters
 		newUsr.User = currentID
 		newUsr.Precan = true
-		currentID.RegistrationCode()
 		// TODO We need a better way to generate base/recursive keys
 		h := sha256.New()
 		h.Write([]byte(string(40000 + i)))
@@ -83,23 +85,27 @@ func newRegistry(grp *cyclic.Group) Registry {
 		// Add user to collection and lookup table
 		userUserIdMap[*newUsr.User] = newUsr
 		// Detect collisions in the registration code
-		if _, ok := userRegCodeMap[newUsr.User.RegistrationCode()]; ok {
+		if _, ok := userRegCodeMap[RegistrationCode(newUsr.User)]; ok {
 			globals.Log.ERROR.Printf(
 				"Collision in demo user list creation at %v. "+
 					"Please fix ASAP (include more bits to the reg code.", i)
 		}
-		userRegCodeMap[newUsr.User.RegistrationCode()] = newUsr.User
+		userRegCodeMap[RegistrationCode(newUsr.User)] = newUsr.User
 		nk[*newUsr.User] = nodeKey
 	}
 
 	// Channels have been hardcoded to users starting with 31
 	for i := 0; i < len(DemoUserNicks); i++ {
-		currentID := id.NewUserFromUints(&[4]uint64{0, 0, 0, uint64(i) + 1})
+		currentID := new(id.ID)
+		binary.BigEndian.PutUint64(currentID[:], uint64(i)+1)
+		currentID.SetType(id.User)
 		userUserIdMap[*currentID].Username = DemoUserNicks[i]
 	}
 
 	for i := 0; i < len(DemoChannelNames); i++ {
-		currentID := id.NewUserFromUints(&[4]uint64{0, 0, 0, uint64(i) + 31})
+		currentID := new(id.ID)
+		binary.BigEndian.PutUint64(currentID[:], uint64(i)+31)
+		currentID.SetType(id.User)
 		userUserIdMap[*currentID].Username = DemoChannelNames[i]
 	}
 
@@ -112,7 +118,7 @@ func newRegistry(grp *cyclic.Group) Registry {
 
 // Struct representing a User in the system
 type User struct {
-	User     *id.User
+	User     *id.ID
 	Username string
 	Precan   bool
 }
@@ -130,20 +136,20 @@ func (u *User) DeepCopy() *User {
 }
 
 // NewUser creates a new User object with default fields and given address.
-func (m *UserMap) NewUser(id *id.User, username string) *User {
+func (m *UserMap) NewUser(id *id.ID, username string) *User {
 	return &User{User: id, Username: username}
 }
 
 // GetUser returns a user with the given ID from userCollection
 // and a boolean for whether the user exists
-func (m *UserMap) GetUser(id *id.User) (user *User, ok bool) {
+func (m *UserMap) GetUser(id *id.ID) (user *User, ok bool) {
 	user, ok = m.userCollection[*id]
 	user = user.DeepCopy()
 	return
 }
 
 // DeleteContactKeys deletes a user with the given ID from userCollection.
-func (m *UserMap) DeleteUser(id *id.User) {
+func (m *UserMap) DeleteUser(id *id.ID) {
 	// If key does not exist, do nothing
 	delete(m.userCollection, *id)
 }
@@ -160,13 +166,13 @@ func (m *UserMap) CountUsers() int {
 }
 
 // LookupUser returns the user id corresponding to the demo registration code
-func (m *UserMap) LookupUser(hid string) (*id.User, bool) {
+func (m *UserMap) LookupUser(hid string) (*id.ID, bool) {
 	uid, ok := m.userLookup[hid]
 	return uid, ok
 }
 
 // LookupKeys returns the keys for the given user from the temporary key map
-func (m *UserMap) LookupKeys(uid *id.User) (*NodeKeys, bool) {
+func (m *UserMap) LookupKeys(uid *id.ID) (*NodeKeys, bool) {
 	nk, t := m.keysLookup[*uid]
 	return nk, t
 }
diff --git a/user/user_test.go b/user/user_test.go
index fd2a0cfe78c38c60de115d574128805049495cb4..52d303e432c7c0a686b30406db2b130a7b46ce73 100644
--- a/user/user_test.go
+++ b/user/user_test.go
@@ -24,16 +24,16 @@ func TestUserRegistry(t *testing.T) {
 	}
 	// Test the integration of the LookupUser, UserHash and GetUser functions
 	for i := 0; i < len(DemoUserNicks); i++ {
-		currentID := id.NewUserFromUint(uint64(i+1), t)
-		reg, ok := Users.LookupUser(currentID.RegistrationCode())
+		currentID := id.NewIdFromUInt(uint64(i+1), id.User, t)
+		reg, ok := Users.LookupUser(RegistrationCode(currentID))
 		if !ok {
 			t.Errorf("Couldn't lookup user %q with code %v", *currentID,
-				currentID.RegistrationCode())
+				RegistrationCode(currentID))
 		}
 		usr, ok := Users.GetUser(reg)
 		if !ok {
-			t.Logf("Reg codes of both: %v, %v", reg.RegistrationCode(),
-				currentID.RegistrationCode())
+			t.Logf("Reg codes of both: %v, %v", RegistrationCode(reg),
+				RegistrationCode(currentID))
 			t.Errorf("Couldn't get user %q corresponding to user %q",
 				*reg, *currentID)
 		}
@@ -43,7 +43,7 @@ func TestUserRegistry(t *testing.T) {
 		}
 	}
 	// Test the NewUser function
-	newID := id.NewUserFromUint(2002, t)
+	newID := id.NewIdFromUInt(2002, id.User, t)
 	usr := Users.NewUser(newID, "Will I am")
 
 	if usr.Username != "Will I am" {
@@ -71,7 +71,7 @@ func TestUserRegistry(t *testing.T) {
 	grp := InitGroup()
 
 	// Test LookupKeys
-	keys, suc := Users.LookupKeys(id.NewUserFromUint(1, t))
+	keys, suc := Users.LookupKeys(id.NewIdFromUInt(1, id.User, t))
 	if !suc {
 		t.Errorf("LookupKeys failed to find a valid user.")
 	}
@@ -85,9 +85,9 @@ func TestUserRegistry(t *testing.T) {
 	}
 
 	// Test delete user
-	Users.DeleteUser(id.NewUserFromUint(2, t))
+	Users.DeleteUser(id.NewIdFromUInt(2, id.User, t))
 
-	_, ok := Users.GetUser(id.NewUserFromUint(2, t))
+	_, ok := Users.GetUser(id.NewIdFromUInt(2, id.User, t))
 	if ok {
 		t.Errorf("User %v has not been deleted succesfully!", usr.Username)
 	}
@@ -97,8 +97,8 @@ func TestUserRegistry(t *testing.T) {
 // the first several users
 func TestPrintRegCodes(t *testing.T) {
 	for i := 1; i <= NumDemoUsers; i++ {
-		currentID := id.NewUserFromUint(uint64(i), t)
-		t.Logf("%v:\t%v", i, currentID.RegistrationCode())
+		currentID := id.NewIdFromUInt(uint64(i), id.User, t)
+		t.Logf("%v:\t%v", i, RegistrationCode(currentID))
 	}
 }
 
diff --git a/version_vars.go.bak b/version_vars.go.bak
new file mode 100644
index 0000000000000000000000000000000000000000..67620a0b3f330cea5051e878a2c07ceea175125d
--- /dev/null
+++ b/version_vars.go.bak
@@ -0,0 +1,33 @@
+// Code generated by go generate; DO NOT EDIT.
+// This file was generated by robots at
+// 2020-07-30 15:27:18.7064291 -0700 PDT m=+0.065083001
+package cmd
+
+const GITVERSION = `4ef845a Merge branch 'hotfix/integrateekv' into 'release'`
+const SEMVER = "1.4.0"
+const DEPENDENCIES = `module gitlab.com/elixxir/client
+
+go 1.13
+
+require (
+	github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3
+	github.com/golang/protobuf v1.4.2
+	github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00 // indirect
+	github.com/pelletier/go-toml v1.6.0 // indirect
+	github.com/pkg/errors v0.9.1
+	github.com/smartystreets/assertions v1.0.1 // indirect
+	github.com/spf13/afero v1.2.2 // indirect
+	github.com/spf13/cast v1.3.1 // indirect
+	github.com/spf13/cobra v1.0.0
+	github.com/spf13/jwalterweatherman v1.1.0
+	github.com/spf13/pflag v1.0.5 // indirect
+	github.com/spf13/viper v1.6.2
+	gitlab.com/elixxir/comms v0.0.0-20200730220537-60dbe58afe94
+	gitlab.com/elixxir/crypto v0.0.0-20200707005343-97f868cbd930
+	gitlab.com/elixxir/ekv v0.0.0-20200729182028-159355ea5842
+	gitlab.com/elixxir/primitives v0.0.0-20200706165052-9fe7a4fb99a3
+	gitlab.com/xx_network/comms v0.0.0-20200730220144-eea32e8b696d
+	golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899
+	gopkg.in/ini.v1 v1.52.0 // indirect
+)
+`