diff --git a/bindings/ud.go b/bindings/ud.go
index df4d3c5bf2fa1ed27bad1ee66b7c9146862c4da9..a8dbb9e39cd692c4bec358ca2fbb50b8faacef80 100644
--- a/bindings/ud.go
+++ b/bindings/ud.go
@@ -96,7 +96,7 @@ func NewUserDiscoveryFromBackup(client *Client,
 		jww.WARN.Printf("Loading manager without a registered phone number")
 	}
 
-	m, err := ud.NewManagerFromBackup(&client.api, single, emailFact, phoneFact)
+	m, err := ud.NewManagerFromBackup(&client.api, single, phoneFact)
 	if err != nil {
 		return nil, errors.WithMessage(err, "Failed to create User Discovery Manager")
 	} else {
diff --git a/ud/addFact.go b/ud/addFact.go
index d67fe0961a609a3707e731bfe388618ac07ea0b7..5139b0490060c2baaa1e0b0854733d727ba7b17b 100644
--- a/ud/addFact.go
+++ b/ud/addFact.go
@@ -21,7 +21,7 @@ import (
 // called along with the code to finalize the fact.
 func (m *Manager) SendRegisterFact(f fact.Fact) (string, error) {
 	jww.INFO.Printf("ud.SendRegisterFact(%s)", f.Stringify())
-	return m.addFact(f, m.myID, m.comms)
+	return m.addFact(f, m.e2e.GetReceptionID(), m.comms)
 }
 
 func (m *Manager) addFact(inFact fact.Fact, myId *id.ID, aFC addFactComms) (string, error) {
@@ -42,7 +42,8 @@ func (m *Manager) addFact(inFact fact.Fact, myId *id.ID, aFC addFactComms) (stri
 	fHash := factID.Fingerprint(f)
 
 	// Sign our inFact for putting into the request
-	fSig, err := rsa.Sign(rand.Reader, m.privKey, hash.CMixHash, fHash, nil)
+	privKey := m.user.PortableUserInfo().ReceptionRSA
+	fSig, err := rsa.Sign(rand.Reader, privKey, hash.CMixHash, fHash, nil)
 	if err != nil {
 		return "", err
 	}
diff --git a/ud/interfaces.go b/ud/interfaces.go
new file mode 100644
index 0000000000000000000000000000000000000000..02d01f2159580699273b2e1e2d608700e2391ad5
--- /dev/null
+++ b/ud/interfaces.go
@@ -0,0 +1,26 @@
+package ud
+
+import (
+	"gitlab.com/elixxir/client/cmix"
+	"gitlab.com/elixxir/client/cmix/identity/receptionID"
+	"gitlab.com/elixxir/client/interfaces/user"
+	"gitlab.com/elixxir/client/single"
+	"gitlab.com/elixxir/client/stoppable"
+	"gitlab.com/elixxir/crypto/contact"
+	"gitlab.com/elixxir/crypto/cyclic"
+	"gitlab.com/xx_network/crypto/csprng"
+	"gitlab.com/xx_network/primitives/id"
+)
+
+type Userinfo interface {
+	PortableUserInfo() user.Info
+	GetUsername() (string, error)
+	GetReceptionRegistrationValidationSignature() []byte
+}
+
+type SingleInterface interface {
+	TransmitRequest(recipient contact.Contact, tag string, payload []byte,
+		callback single.Response, param single.RequestParams, net cmix.Client, rng csprng.Source,
+		e2eGrp *cyclic.Group) (id.Round, receptionID.EphemeralIdentity, error)
+	StartProcesses() (stoppable.Stoppable, error)
+}
diff --git a/ud/lookup.go b/ud/lookup.go
index 5ff0899f9f77efc19ae3cabe4b510759e452ab4c..5f64ab3de53e4469e78308f155ce208a2aa7c8d6 100644
--- a/ud/lookup.go
+++ b/ud/lookup.go
@@ -104,6 +104,8 @@ func lookup(services cmix.Client, callback single.Response,
 // will be passed into the callback.
 func (m *Manager) lookupResponseProcess(uid *id.ID, cb single.Response,
 	payload []byte, err error) {
+	grp := m.e2e.GetGroup()
+
 	if err != nil {
 		go cb.Callback(contact.Contact{}, errors.WithMessage(err, "Failed to lookup."))
 		return
@@ -124,7 +126,7 @@ func (m *Manager) lookupResponseProcess(uid *id.ID, cb single.Response,
 
 	c := contact.Contact{
 		ID:       uid,
-		DhPubKey: m.grp.NewIntFromBytes(lookupResponse.PubKey),
+		DhPubKey: grp.NewIntFromBytes(lookupResponse.PubKey),
 	}
 
 	if lookupResponse.Username != "" {
diff --git a/ud/manager.go b/ud/manager.go
index 821718a58b2fdd29e3427e7c655f88e8288cf364..b5d8d4d7d5babd0ed183a0a84b8585463698114d 100644
--- a/ud/manager.go
+++ b/ud/manager.go
@@ -6,43 +6,21 @@ import (
 	jww "github.com/spf13/jwalterweatherman"
 	"gitlab.com/elixxir/client/api"
 	"gitlab.com/elixxir/client/cmix"
-	"gitlab.com/elixxir/client/cmix/identity/receptionID"
 	"gitlab.com/elixxir/client/e2e"
 	"gitlab.com/elixxir/client/event"
-	"gitlab.com/elixxir/client/interfaces/user"
-	"gitlab.com/elixxir/client/single"
-	"gitlab.com/elixxir/client/stoppable"
 	"gitlab.com/elixxir/client/storage/versioned"
+	store "gitlab.com/elixxir/client/ud/store/ud"
 	"gitlab.com/elixxir/comms/client"
 	"gitlab.com/elixxir/crypto/contact"
-	"gitlab.com/elixxir/crypto/cyclic"
 	"gitlab.com/elixxir/crypto/fastRNG"
 	"gitlab.com/elixxir/primitives/fact"
 	"gitlab.com/xx_network/comms/connect"
-	"gitlab.com/xx_network/crypto/csprng"
 	"gitlab.com/xx_network/crypto/signature/rsa"
 	"gitlab.com/xx_network/primitives/id"
 	"math"
 	"time"
 )
 
-type SingleInterface interface {
-	TransmitRequest(recipient contact.Contact, tag string, payload []byte,
-		callback single.Response, param single.RequestParams, net cmix.Client, rng csprng.Source,
-		e2eGrp *cyclic.Group) (id.Round, receptionID.EphemeralIdentity, error)
-	StartProcesses() (stoppable.Stoppable, error)
-}
-
-type Userinfo interface {
-	PortableUserInfo() user.Info
-	GetUsername() (string, error)
-	GetReceptionRegistrationValidationSignature() []byte
-}
-
-const (
-// todo: populate with err messages
-)
-
 // todo: newuserDiscRegistratration, loadUserDiscRegistration
 //  neworLoad?
 // fixme: search/lookup off ud object
@@ -68,13 +46,6 @@ type Manager struct {
 
 	kv *versioned.KV
 
-	// Loaded from external access
-	privKey *rsa.PrivateKey
-	grp     *cyclic.Group
-
-	// internal structures
-	myID *id.ID
-
 	// alternate User discovery service to circumvent production
 	alternativeUd *alternateUd
 }
@@ -90,9 +61,9 @@ type alternateUd struct {
 // NewManager builds a new user discovery manager. It requires that an updated
 // NDF is available and will error if one is not.
 // todo: docstring, organize the order of arguments in a meaningful way
-func NewManager(services cmix.Client, e2e e2e.Handler, events event.Manager,
-	comms Comms, userStore Userinfo, rng *fastRNG.StreamGenerator,
-	privKey *rsa.PrivateKey, username string,
+func NewManager(services cmix.Client, e2e e2e.Handler,
+	events event.Manager, comms Comms, userStore Userinfo,
+	rng *fastRNG.StreamGenerator, username string,
 	kv *versioned.KV) (*Manager, error) {
 	jww.INFO.Println("ud.NewManager()")
 
@@ -114,9 +85,6 @@ func NewManager(services cmix.Client, e2e e2e.Handler, events event.Manager,
 		comms:    comms,
 		rng:      rng,
 		store:    udStore,
-		myID:     e2e.GetReceptionID(),
-		grp:      e2e.GetGroup(),
-		privKey:  privKey,
 		user:     userStore,
 		kv:       kv,
 	}
@@ -129,13 +97,6 @@ func NewManager(services cmix.Client, e2e e2e.Handler, events event.Manager,
 			"information, is there network access?: Cert not present.")
 	}
 
-	// Pull user discovery ID from NDF
-	udID, err := id.Unmarshal(def.UDB.ID)
-	if err != nil {
-		return nil, errors.Errorf("failed to unmarshal UD ID "+
-			"from NDF: %+v", err)
-	}
-
 	udHost, err := m.getOrAddUdHost()
 	if err != nil {
 		return nil, errors.WithMessage(err, "User Discovery host object could "+
@@ -162,36 +123,38 @@ func NewManager(services cmix.Client, e2e e2e.Handler, events event.Manager,
 // NewManagerFromBackup builds a new user discover manager from a backup.
 // It will construct a manager that is already registered and restore
 // already registered facts into store.
-func NewManagerFromBackup(client *api.Client, single *single.Manager,
-	email, phone fact.Fact) (*Manager, error) {
+func NewManagerFromBackup(services cmix.Client, e2e e2e.Handler, comms Comms, userStore Userinfo, rng *fastRNG.StreamGenerator, email, phone fact.Fact, kv *versioned.KV) (*Manager, error) {
 	jww.INFO.Println("ud.NewManagerFromBackup()")
 	if client.NetworkFollowerStatus() != api.Running {
 		return nil, errors.New(
-			"cannot start UD Manager when network follower is not running.")
+			"cannot start UD Manager when " +
+				"network follower is not running.")
 	}
 
-	registered := uint32(0)
-
 	m := &Manager{
-		client:     client,
-		comms:      client.GetComms(),
-		rng:        client.GetRng(),
-		sw:         client.GetSwitchboard(),
-		storage:    client.GetStorage(),
-		net:        client.GetNetworkInterface(),
-		single:     single,
-		registered: &registered,
+		services: services,
+		e2e:      e2e,
+		comms:    comms,
+		user:     userStore,
+		rng:      rng,
+		kv:       kv,
 	}
 
-	err := m.client.GetStorage().GetUd().
-		BackUpMissingFacts(email, phone)
+	udStore, err := store.NewOrLoadStore(kv)
+	if err != nil {
+		return nil, err
+	}
+
+	m.store = udStore
+
+	err = m.store.BackUpMissingFacts(email, phone)
 	if err != nil {
 		return nil, errors.WithMessage(err, "Failed to restore UD store "+
 			"from backup")
 	}
 
 	// check that user discovery is available in the NDF
-	def := m.net.GetInstance().GetPartialNdf().Get()
+	def := m.services.GetInstance().GetPartialNdf().Get()
 
 	if def.UDB.Cert == "" {
 		return nil, errors.New("NDF does not have User Discovery information, " +
@@ -206,21 +169,14 @@ func NewManagerFromBackup(client *api.Client, single *single.Manager,
 	hp.SendTimeout = 3 * time.Second
 	hp.AuthEnabled = false
 
-	m.myID = m.storage.User().GetCryptographicIdentity().GetReceptionID()
-
-	// Get the commonly used data from storage
-	m.privKey = m.storage.GetUser().ReceptionRSA
-
 	// Set as registered. Since it's from a backup,
 	// the client is already registered
+	// todo: maybe we don't need this?
 	if err = m.setRegistered(); err != nil {
 		return nil, errors.WithMessage(err, "failed to set client as "+
 			"registered with user discovery.")
 	}
 
-	// Store the pointer to the group locally for easy access
-	m.grp = m.storage.E2e().GetGroup()
-
 	return m, nil
 }
 
@@ -235,7 +191,6 @@ func LoadManager(services cmix.Client, e2e e2e.Handler, events event.Manager,
 		comms:    comms,
 		user:     userStore,
 		rng:      rng,
-		privKey:  privKey,
 		kv:       kv,
 	}
 
@@ -314,10 +269,11 @@ func (m *Manager) GetStringifiedFacts() []string {
 
 // GetContact returns the contact for UD as retrieved from the NDF.
 func (m *Manager) GetContact() (contact.Contact, error) {
+	grp := m.e2e.GetGroup()
 	// Return alternative User discovery contact if set
 	if m.alternativeUd != nil {
 		// Unmarshal UD DH public key
-		alternativeDhPubKey := m.grp.NewInt(1)
+		alternativeDhPubKey := grp.NewInt(1)
 		if err := alternativeDhPubKey.
 			UnmarshalJSON(m.alternativeUd.dhPubKey); err != nil {
 			return contact.Contact{},
@@ -343,7 +299,7 @@ func (m *Manager) GetContact() (contact.Contact, error) {
 	}
 
 	// Unmarshal UD DH public key
-	dhPubKey := m.grp.NewInt(1)
+	dhPubKey := grp.NewInt(1)
 	if err = dhPubKey.UnmarshalJSON(netDef.UDB.DhPubKey); err != nil {
 		return contact.Contact{},
 			errors.WithMessage(err, "Failed to unmarshal UD DH "+
diff --git a/ud/remove.go b/ud/remove.go
index 973a997ad81aa440917cbeb9c94b240b916c82fc..29be3b90e4e8823d1df623fc678f2cc323b0c235 100644
--- a/ud/remove.go
+++ b/ud/remove.go
@@ -42,14 +42,15 @@ func (m *Manager) removeFact(f fact.Fact,
 	fHash := factID.Fingerprint(f)
 
 	// Sign our inFact for putting into the request
-	fSig, err := rsa.Sign(rand.Reader, m.privKey, hash.CMixHash, fHash, nil)
+	privKey := m.user.PortableUserInfo().ReceptionRSA
+	fSig, err := rsa.Sign(rand.Reader, privKey, hash.CMixHash, fHash, nil)
 	if err != nil {
 		return err
 	}
 
 	// Create our Fact Removal Request message data
 	remFactMsg := mixmessages.FactRemovalRequest{
-		UID:         m.myID.Marshal(),
+		UID:         m.e2e.GetReceptionID().Marshal(),
 		RemovalData: &mmFact,
 		FactSig:     fSig,
 	}
@@ -77,8 +78,9 @@ func (m *Manager) RemoveUser(f fact.Fact) error {
 	if err != nil {
 		return err
 	}
+	privKey := m.user.PortableUserInfo().ReceptionRSA
 
-	return removeUser(f, m.myID, m.privKey, m.comms, udHost)
+	return removeUser(f, m.e2e.GetReceptionID(), privKey, m.comms, udHost)
 }
 
 func removeUser(f fact.Fact, myId *id.ID, privateKey *rsa.PrivateKey,
diff --git a/ud/search.go b/ud/search.go
index 7cb6bc6d5209f5eca5fa031f7c532a1b39c6c76d..836278a66f33bde3a81069716c8be721996f770b 100644
--- a/ud/search.go
+++ b/ud/search.go
@@ -139,7 +139,7 @@ func hashFactList(list fact.FactList) ([]*HashFact, map[string]fact.Fact) {
 func (m *Manager) parseContacts(response []*Contact,
 	hashMap map[string]fact.Fact) ([]contact.Contact, error) {
 	contacts := make([]contact.Contact, len(response))
-
+	grp := m.e2e.GetGroup()
 	// Convert each contact message into a new contact.Contact
 	for i, c := range response {
 		// Unmarshal user ID bytes
@@ -154,7 +154,7 @@ func (m *Manager) parseContacts(response []*Contact,
 		// Create new Contact
 		contacts[i] = contact.Contact{
 			ID:       uid,
-			DhPubKey: m.grp.NewIntFromBytes(c.PubKey),
+			DhPubKey: grp.NewIntFromBytes(c.PubKey),
 			Facts:    facts,
 		}