diff --git a/api/client.go b/api/client.go
index 8801e2bff28eb03c9f990db04fb7632c0cd272a8..dae5be40555fba6de9f258006932238e8367c4a8 100644
--- a/api/client.go
+++ b/api/client.go
@@ -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,22 @@ 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.Nodes[0].ID)
+	if err != nil {
+		return err
+	}
+	transmitGateway.SetType(id.Gateway)
+	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)
+	udbID, err := id.Unmarshal(cl.ndf.UDB.ID)
+	if err != nil {
+		return err
+	}
+	bots.InitBots(cl.session, cl.receptionManager, cl.topology, udbID, transmissionHost)
 	// Initialize Rekey listeners
 	rekey.InitRekey(cl.session, cl.receptionManager, cl.topology, cl.rekeyChan)
 	return nil
@@ -325,8 +333,12 @@ 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.Nodes[len(cl.ndf.Nodes)-1].ID)
+	if err != nil {
+		return err
+	}
+	receptionGateway.SetType(id.Gateway)
+	receptionHost, ok := cl.receptionManager.Comms.GetHost(receptionGateway)
 	if !ok {
 		return errors.New("Failed to retrieve host for transmission")
 	}
@@ -349,8 +361,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.Nodes[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 +393,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 +409,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 +483,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 +515,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 312ae5813c9007331c566978e7d7659dbb3a87c0..9f9dd4ef62756bc38a7cafda896528a4327e9904 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.Nodes[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.Nodes[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 {
@@ -617,7 +626,6 @@ func TestClient_LogoutHappyPath(t *testing.T) {
 		t.Fail()
 	}
 
-
 	//Test that the things that should not be nil are not nil
 	if tc.ndf == nil {
 		t.Logf("NDF should not be set to nil")
@@ -643,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 {
@@ -701,12 +710,13 @@ func TestClient_LogoutTimeout(t *testing.T) {
 }
 
 // Test that if we logout we can logback in.
-func TestClient_LogoutAndLoginAgain(t *testing.T){
+func TestClient_LogoutAndLoginAgain(t *testing.T) {
 	//Initialize a client
 	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 {
@@ -744,14 +754,14 @@ func TestClient_LogoutAndLoginAgain(t *testing.T){
 	}
 
 	_, err = tc.Login("")
-	if err != nil{
+	if err != nil {
 		t.Logf("Login failed %+v", err)
 		t.Fail()
 	}
 
-	if newId != initialId{
+	if newId != initialId {
 		t.Logf("Failed to log user back in to original session")
 		t.Fail()
 	}
 
-}
\ No newline at end of file
+}
diff --git a/api/connect.go b/api/connect.go
index 5feebdb09b2262f04e04a205ce7558b1d92ba126..9e9676fd76f2eee4380e5dbfab23122f7d290748 100644
--- a/api/connect.go
+++ b/api/connect.go
@@ -24,7 +24,10 @@ 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)
+		uid := new(id.ID)
+		copy(uid[:], "client")
+		uid.SetType(id.User)
+		cl.receptionManager, err = io.NewReceptionManager(cl.rekeyChan, uid, nil, nil, nil)
 		if err != nil {
 			return errors.Wrap(err, "Failed to create reception manager")
 		}
@@ -66,7 +69,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",
@@ -79,9 +82,13 @@ func addNotificationBotHost(rm *io.ReceptionManager, definition *ndf.NetworkDefi
 // builds a circuit for all the node's in the definition
 func BuildNodeTopology(definition *ndf.NetworkDefinition) *connect.Circuit {
 	//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 {
+			panic(err)
+		}
 	}
 
 	return connect.NewCircuit(nodeIDs)
@@ -148,8 +155,12 @@ 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.Nodes[i].ID)
+		if err != nil {
+			globals.Log.ERROR.Panic(err)
+		}
+		gwID.SetType(id.Gateway)
+		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 +174,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 +191,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..fbb5e2fb3c2c61418963f61bb7f48c0ecb5afba2 100644
--- a/api/mockserver.go
+++ b/api/mockserver.go
@@ -37,15 +37,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
 }
 
@@ -276,13 +276,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
 }
@@ -315,7 +315,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)
@@ -337,7 +337,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 253153c39aae15a3116fce667d6434084db871d2..590ca2fe42ec57042c10029c7b83e4ea571ffb02 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{
@@ -71,7 +71,8 @@ func TestClient_StartMessageReceiver_MultipleMessages(t *testing.T) {
 			Address: string(fmtAddress(GWErrorPort + i)),
 		}
 		testDef.Gateways = append(testDef.Gateways, gw)
-		GWErrComms[i] = gateway.StartGateway("testGateway", gw.Address,
+		gwID := id.NewIdFromString("testGateway", id.Gateway, t)
+		GWErrComms[i] = gateway.StartGateway(gwID, gw.Address,
 			&GatewayHandlerMultipleMessages{}, nil, nil)
 
 	}
@@ -163,7 +164,7 @@ func TestRegister_ValidPrecannedRegCodeReturnsZeroID(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)
 	}
 	disconnectServers()
@@ -254,8 +255,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(1, 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 +328,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,
 		},
@@ -347,7 +348,7 @@ func TestSend(t *testing.T) {
 		t.Errorf("Error sending message: %v", err)
 	}
 
-	err = client.Logout(100* time.Millisecond)
+	err = client.Logout(100 * time.Millisecond)
 
 	if err != nil {
 		t.Errorf("Logout failed: %v", err)
@@ -447,10 +448,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,8 +514,10 @@ 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.Node)
+	RegComms = registration.StartRegistrationServer(regId, def.Registration.Address, &RegHandler, nil, nil)
 	def.Gateways = make([]ndf.Gateway, 0)
 
 	//Start up gateways
@@ -524,10 +528,13 @@ func startServers() {
 		}
 
 		def.Gateways = append(def.Gateways, gw)
-		GWComms[i] = gateway.StartGateway("testGateway", gw.Address, handler, nil, nil)
+		gwID := new(id.ID)
+		copy(regId[:], "testGateway")
+		regId.SetType(id.Gateway)
+		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..528dedea53517c15747b5d1868b050aca488f6d4 100644
--- a/api/private.go
+++ b/api/private.go
@@ -20,8 +20,8 @@ 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"
 )
@@ -30,13 +30,13 @@ 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 +82,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 +110,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 +127,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())
 	}
@@ -163,7 +163,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
@@ -188,7 +188,7 @@ func (cl *Client) confirmNonce(UID, nonce []byte,
 		},
 	}
 
-	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 +207,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 +303,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 +392,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 +402,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..8b538ee0518d848db37605d9a9575f1643a39ca3 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 {
+			panic(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.Nodes[index].ID)
+	if err != nil {
+		globals.Log.ERROR.Panic(err)
+	}
+	gatewayID.SetType(id.Gateway)
 
 	// 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..f53dca59aaffbcf23d8dd9c1156efac166d6e0f2 100644
--- a/api/register_test.go
+++ b/api/register_test.go
@@ -11,7 +11,6 @@ import (
 	"gitlab.com/elixxir/client/user"
 	"gitlab.com/elixxir/comms/connect"
 	"gitlab.com/elixxir/primitives/id"
-	"reflect"
 	"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..0b020651146605c62acd581efa876288c28f8ce1 100644
--- a/bindings/client_test.go
+++ b/bindings/client_test.go
@@ -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
@@ -358,12 +358,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 +377,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 +470,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 +515,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 +556,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,8 +609,13 @@ 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
@@ -617,7 +627,10 @@ func testMainWrapper(m *testing.M) int {
 		}
 
 		def.Gateways = append(def.Gateways, gw)
-		GWComms[i] = gateway.StartGateway("testGateway", gw.Address,
+		gwId := new(id.ID)
+		copy(gwId[:], "testGateway")
+		gwId.SetType(id.Gateway)
+		GWComms[i] = gateway.StartGateway(gwId, gw.Address,
 			gateway.NewImplementation(), nil, nil)
 	}
 
@@ -625,14 +638,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 +763,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..dc372fdd442c485e05ea286e756636986ff7eadc 100644
--- a/bots/bots.go
+++ b/bots/bots.go
@@ -17,7 +17,7 @@ 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
+var UdbID *id.ID
 
 type channelResponseListener chan string
 
@@ -50,7 +50,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) {
+func InitBots(s user.Session, m io.Communications, top *connect.Circuit, udbID *id.ID, host *connect.Host) {
 	UdbID = udbID
 
 	// FIXME: these all need to be used in non-blocking threads if we are
@@ -78,22 +78,22 @@ func InitBots(s user.Session, m io.Communications, top *connect.Circuit, udbID *
 		&registerResponseListener)
 	l.Register(UdbID, 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..d0c74ab10875eafdff93b08b3790e8ce589292d6 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"
@@ -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,13 @@ 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)
+	botID := new(id.ID)
+	copy(botID[:], "testid")
+	InitBots(fakeSession, fakeComm, topology, botID, &h)
 
 	// Make the reception channels buffered for this test
 	// which overwrites the channels registered in InitBots
@@ -121,9 +128,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 +148,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 +202,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 +211,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..47e8eaf3daaf4f4109b2411ed3258a01e5b61eda 100644
--- a/bots/userDiscovery.go
+++ b/bots/userDiscovery.go
@@ -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)
@@ -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)
@@ -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,7 +257,7 @@ func parseGetKey(msg string) []byte {
 }
 
 // pushKey uploads the users' public key
-func pushKey(udbID *id.User, keyFP string, publicKey []byte) error {
+func pushKey(udbID *id.ID, keyFP string, publicKey []byte) error {
 	publicKeyString := base64.StdEncoding.EncodeToString(publicKey)
 	globals.Log.DEBUG.Printf("Running pushkey for %q, %v, %v", *udbID, keyFP,
 		publicKeyString)
diff --git a/cmd/root.go b/cmd/root.go
index 2c222736725c2c6bb0a07a22f983c639bae21085..97d0403b998216be2a9b64f33f5de136085974ec 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -9,6 +9,7 @@ package cmd
 
 import (
 	"encoding/base64"
+	"encoding/binary"
 	"fmt"
 	"github.com/golang/protobuf/proto"
 	"github.com/spf13/cobra"
@@ -68,7 +69,7 @@ func Execute() {
 	}
 }
 
-func sessionInitialization() (*id.User, string, *api.Client) {
+func sessionInitialization() (*id.ID, string, *api.Client) {
 	var err error
 	register := false
 
@@ -110,7 +111,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
@@ -140,14 +141,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")
 
@@ -168,7 +169,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 {
@@ -184,7 +185,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 {
@@ -194,7 +195,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...")
@@ -247,7 +251,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)
 	}
 
@@ -403,11 +409,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
@@ -441,7 +447,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")
@@ -454,10 +460,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 != "" {
@@ -566,7 +577,7 @@ var rootCmd = &cobra.Command{
 }
 
 func isValidUser(usr []byte) bool {
-	if len(usr) != id.UserLen {
+	if len(usr) != id.ArrIDLen {
 		return false
 	}
 	for _, b := range usr {
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/crypto/encryptdecrypt_test.go b/crypto/encryptdecrypt_test.go
index b94d74a3d5b720ca85259f8e9eaf1fcbbd3a31c3..6e899bfe3f1fe8902e4f24339ed95ad115788518 100644
--- a/crypto/encryptdecrypt_test.go
+++ b/crypto/encryptdecrypt_test.go
@@ -8,6 +8,7 @@ package crypto
 
 import (
 	"bytes"
+	"encoding/binary"
 	"gitlab.com/elixxir/client/user"
 	"gitlab.com/elixxir/comms/connect"
 	pb "gitlab.com/elixxir/comms/mixmessages"
@@ -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/go.mod b/go.mod
index 00b8371aa4bb7a0298c679607bcf63b3790ae09b..48f3a544cb5497c4cd1b2ac033d6f8377c60b869 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.0
+	github.com/golang/protobuf v1.4.1
 	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,9 @@ 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-20200505183321-919cc2af795a
+	gitlab.com/elixxir/crypto v0.0.0-20200504184505-e210a219cbd9
+	gitlab.com/elixxir/primitives v0.0.0-20200504203122-2adfdf770891
+	golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79
 	gopkg.in/ini.v1 v1.52.0 // indirect
 )
diff --git a/go.sum b/go.sum
index 9372f25ba6e5c36043b6972afecbdeb77122c02c..1ffd5fe8068b484337702a903ee9113ce61424de 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=
@@ -9,6 +10,7 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce
 github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
 github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
 github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
 github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
 github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
@@ -17,10 +19,13 @@ 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=
+github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
 github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
 github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
 github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
@@ -40,19 +45,22 @@ 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=
 github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
 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/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/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=
@@ -60,8 +68,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=
@@ -72,6 +82,7 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
 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=
@@ -82,6 +93,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=
@@ -90,6 +102,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=
@@ -106,7 +119,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=
@@ -130,7 +145,7 @@ 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/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
 github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
@@ -138,23 +153,22 @@ github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1
 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/crypto v0.0.0-20200410231849-90e859940f5d h1:+g7tGMO3g20Su3pdTJg30n5XhEGZ3avEd4ccN33CtdU=
-gitlab.com/elixxir/crypto v0.0.0-20200410231849-90e859940f5d/go.mod h1:QPClJr3F90ejz6iHaCZuhexytd6PP97dDnt93iRCTDo=
-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/comms v0.0.0-20200505183321-919cc2af795a h1:TL7YbcarLWNdFnOtpZ6kEx4nfQalv5uUcpQgIs+KuO0=
+gitlab.com/elixxir/comms v0.0.0-20200505183321-919cc2af795a/go.mod h1:mvCvziyu5Vp4Flg9VQ343y9fHZh96wLHysLFjoVF6rU=
+gitlab.com/elixxir/crypto v0.0.0-20200504184505-e210a219cbd9 h1:KS0GMn9qw5nSwSp9RTGDMoBqesOBujcBaL8NPOGN3zg=
+gitlab.com/elixxir/crypto v0.0.0-20200504184505-e210a219cbd9/go.mod h1:QFHKOPIscWJTc8l7CEJ7guMtVDt7wbNqFTw1HsKac5M=
+gitlab.com/elixxir/primitives v0.0.0-20200429161225-0baf426936d7/go.mod h1:w4DuFgnCQZbBpBCFMAwG232qOQY5ALKOUBzCQihtNqI=
+gitlab.com/elixxir/primitives v0.0.0-20200504203122-2adfdf770891 h1:IgRd90gMBOg8YPLdCSMzqaFnsR7YP8J3Lr8hhD2+tsg=
+gitlab.com/elixxir/primitives v0.0.0-20200504203122-2adfdf770891/go.mod h1:w4DuFgnCQZbBpBCFMAwG232qOQY5ALKOUBzCQihtNqI=
 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-20200422194213-44a606286825/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79 h1:IaQbIIB2X/Mp/DKctl6ROxz1KyMlKp4uyvL6+kQ7C88=
+golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79/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=
@@ -167,8 +181,8 @@ 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-20200505041828-1ed23360d12c h1:zJ0mtu4jCalhKg6Oaukv6iIkb+cOvDrajDH9DH46Q4M=
+golang.org/x/net v0.0.0-20200505041828-1ed23360d12c/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
 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=
@@ -180,10 +194,10 @@ golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5h
 golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 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-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-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-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200501145240-bc7a7d42d5c3 h1:5B6i6EAiSYyejWfvc5Rc9BbI3rzIsrrXfAQBWnYfn+w=
+golang.org/x/sys v0.0.0-20200501145240-bc7a7d42d5c3/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=
@@ -195,29 +209,35 @@ 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-20200430143042-b979b6f78d84 h1:pSLkPbrjnPyLDYUO2VM9mDLqo2V6CFBY84lFSZAfoi4=
+google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
 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/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=
 google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
 google.golang.org/protobuf v1.21.0 h1:qdOKuR/EIArgaWNjetjgTzgVTAZ+S/WXVrq9HW9zimw=
 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=
 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=
@@ -227,7 +247,6 @@ 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=
 honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
diff --git a/io/collate.go b/io/collate.go
index 0f4ec1e49cdbe270b03538719e8ee94412b613a9..5ca49382c3ad89455f151d5d2550ec9b2669a50b 100644
--- a/io/collate.go
+++ b/io/collate.go
@@ -47,16 +47,20 @@ 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,
+func (mb *Collator) AddMessage(message *format.Message, sender *id.ID,
 	timeout time.Duration) *parse.Message {
 
 	payload := message.Contents.GetRightAligned()
-	recipient := message.GetRecipient()
+	recipient, err := message.GetRecipient()
+	// Is this the right way to handle the error? It should never happen, right?
+	if err != nil {
+		globals.Log.ERROR.Panic(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",
diff --git a/io/collate_test.go b/io/collate_test.go
index 6bf301fb530803bc1586639ab798bb07b4584220..269db27b33a773b1ee03e68e20bc1e161799f52e 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,7 +44,7 @@ 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)
@@ -66,7 +66,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,7 +84,7 @@ 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)
diff --git a/io/interface.go b/io/interface.go
index 1aaf3ce3b57bec5b6ba485da40fe69e906c0e591..c0e0673e7bde79e83486efe9c87ace3de7a8b102 100644
--- a/io/interface.go
+++ b/io/interface.go
@@ -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..6110ea60fdedee9851e0f9e3f45ba81647eca404 100644
--- a/io/receive.go
+++ b/io/receive.go
@@ -113,7 +113,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 +249,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 +263,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 +275,7 @@ func (rm *ReceptionManager) decryptMessages(session user.Session,
 			}
 
 			keyFP := msg.AssociatedData.GetKeyFP()
-			sender = id.NewUserFromBytes(keyFP[:])
+			sender, err = id.Unmarshal(keyFP[:])
 		} else {
 			sender, rekey, err = handleE2EReceiving(session, msg)
 
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..d123f946960ace5f71ae4a984a02536cc4b68e3e 100644
--- a/io/send.go
+++ b/io/send.go
@@ -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..19ab1167b8d87599bef952b978577383c3ce30ce 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 {
@@ -566,7 +566,10 @@ func (km *KeyManager) GobDecode(in []byte) error {
 		km.sendOrRecv = false
 	}
 
-	km.partner = id.NewUserFromBytes(s.Partner)
+	km.partner, err = id.Unmarshal(s.Partner)
+	if err != nil {
+		return err
+	}
 	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 674723f744e31107b0487d6732c5f3fd4bb19a67..99ac6e7ca10294a3bfa00cdb69e403e50bccc906 100644
--- a/rekey/rekey.go
+++ b/rekey/rekey.go
@@ -108,10 +108,10 @@ func InitRekey(s user.Session, m io.Communications, t *connect.Circuit, rekeyCha
 	//  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)
 }
@@ -125,7 +125,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 c0767b8ea56c36aebbd87dff228abbf77f324019..6b9c67b97ff288bde5ee5df8df58ce3bb90498a9 100644
--- a/rekey/rekey_test.go
+++ b/rekey/rekey_test.go
@@ -2,6 +2,7 @@ package rekey
 
 import (
 	"bytes"
+	"encoding/binary"
 	"fmt"
 	"gitlab.com/elixxir/client/cmixproto"
 	"gitlab.com/elixxir/client/globals"
@@ -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))}), rekeyChan2)
+	nodeID := new(id.ID)
+	nodeID.SetType(id.Node)
+	InitRekey(session, fakeComm, connect.NewCircuit([]*id.ID{nodeID}), 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/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..342bffb3afaf5f8d8e471acbc384ce28994c1f0a 100644
--- a/user/session.go
+++ b/user/session.go
@@ -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..eb6c9aaa681555ed75d3678cfc3c501ed6395989 100644
--- a/user/session_test.go
+++ b/user/session_test.go
@@ -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,11 @@ func TestSessionObj_DeleteContact(t *testing.T) {
 		ReceptionKey:    grp.NewInt(2),
 	})
 
-	ses.StoreContactByValue("test", id.NewUserFromBytes([]byte("test")), []byte("test"))
+	testContact := id.NewIdFromBytes([]byte("test"), t)
+	testContact.SetType(id.User)
+	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 +273,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 +306,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 +325,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 +383,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 +422,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 +433,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 +441,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 +474,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 +641,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))
 	}
 }