diff --git a/api/client.go b/api/client.go
index f1a9e030fbcebaa229dd2e33ba246f491e3141c4..ac47598a9869e0be6a74f0937979de4cd909c362 100644
--- a/api/client.go
+++ b/api/client.go
@@ -30,6 +30,7 @@ import (
 	"gitlab.com/xx_network/crypto/signature/rsa"
 	"gitlab.com/xx_network/primitives/id"
 	"gitlab.com/xx_network/primitives/ndf"
+	"sync"
 	"time"
 )
 
@@ -65,6 +66,10 @@ type Client struct {
 	services *serviceProcessiesList
 
 	clientErrorChannel chan interfaces.ClientError
+
+	//lock to ensure only once instance of stop/start network follower is
+	//going at a time
+	followerLock sync.Mutex
 }
 
 // NewClient creates client storage, generates keys, connects, and registers
@@ -214,7 +219,7 @@ func Login(storageDir string, password []byte, parameters params.Network) (*Clie
 	}
 
 	//get the NDF to pass into permissioning and the network manager
-	def := c.storage.GetBaseNDF()
+	def := c.storage.GetNDF()
 
 	//initialize permissioning
 	if def.Registration.Address != "" {
@@ -281,7 +286,7 @@ func LoginWithNewBaseNDF_UNSAFE(storageDir string, password []byte,
 	}
 
 	//store the updated base NDF
-	c.storage.SetBaseNDF(def)
+	c.storage.SetNDF(def)
 
 	//initialize permissioning
 	if def.Registration.Address != "" {
@@ -381,10 +386,17 @@ func (c *Client) initPermissioning(def *ndf.NetworkDefinition) error {
 //   - Auth Callback (/auth/callback.go)
 //      Handles both auth confirm and requests
 func (c *Client) StartNetworkFollower(timeout time.Duration) (<-chan interfaces.ClientError, error) {
+	c.followerLock.Lock()
+	defer c.followerLock.Unlock()
 	u := c.GetUser()
 	jww.INFO.Printf("StartNetworkFollower() \n\tTransmisstionID: %s "+
 		"\n\tReceptionID: %s", u.TransmissionID, u.ReceptionID)
 
+	if status := c.status.get(); status != Stopped{
+		return nil, errors.Errorf("Cannot Stop the Network Follower when it is not running, status: %s", status)
+	}
+
+
 	c.clientErrorChannel = make(chan interfaces.ClientError, 1000)
 
 	cer := func(source, message, trace string) {
@@ -441,6 +453,13 @@ func (c *Client) StartNetworkFollower(timeout time.Duration) (<-chan interfaces.
 // if the network follower is running and this fails, the client object will
 // most likely be in an unrecoverable state and need to be trashed.
 func (c *Client) StopNetworkFollower() error {
+	c.followerLock.Lock()
+	defer c.followerLock.Unlock()
+
+	if status := c.status.get(); status != Running{
+		return errors.Errorf("Cannot Stop the Network Follower when it is not running, status: %s", status)
+	}
+
 	err := c.status.toStopping()
 	if err != nil {
 		return errors.WithMessage(err, "Failed to Stop the Network Follower")
@@ -616,7 +635,7 @@ func checkVersionAndSetupStorage(def *ndf.NetworkDefinition, storageDir string,
 	}
 
 	// Save NDF to be used in the future
-	storageSess.SetBaseNDF(def)
+	storageSess.SetNDF(def)
 
 	if !isPrecanned {
 		//store the registration code for later use
diff --git a/bindings/params.go b/bindings/params.go
index 35afbb8901cfd5e8309f8be7a30a14c907496c23..d4896a2e9e89c49c177689c85ff0a2cf410f9277 100644
--- a/bindings/params.go
+++ b/bindings/params.go
@@ -13,22 +13,22 @@ import (
 	"gitlab.com/elixxir/client/interfaces/params"
 )
 
-func (c *Client) GetCMIXParams() (string, error) {
+func GetCMIXParams() (string, error) {
 	p, err := params.GetDefaultCMIX().Marshal()
 	return string(p), err
 }
 
-func (c *Client) GetE2EParams() (string, error) {
+func GetE2EParams() (string, error) {
 	p, err := params.GetDefaultE2E().Marshal()
 	return string(p), err
 }
 
-func (c *Client) GetNetworkParams() (string, error) {
+func GetNetworkParams() (string, error) {
 	p, err := params.GetDefaultNetwork().Marshal()
 	return string(p), err
 }
 
-func (c *Client) GetUnsafeParams() (string, error) {
+func GetUnsafeParams() (string, error) {
 	p, err := params.GetDefaultUnsafe().Marshal()
 	return string(p), err
 }
diff --git a/go.mod b/go.mod
index a534cac91c9efd0f75f3fcfd2d9c5f40587c06d3..751d38d99f09935e88760efb7eb2ba47be7d2755 100644
--- a/go.mod
+++ b/go.mod
@@ -17,13 +17,13 @@ require (
 	github.com/spf13/jwalterweatherman v1.1.0
 	github.com/spf13/viper v1.7.1
 	gitlab.com/elixxir/bloomfilter v0.0.0-20200930191214-10e9ac31b228
-	gitlab.com/elixxir/comms v0.0.4-0.20210607222512-0f2e89b475b4
-	gitlab.com/elixxir/crypto v0.0.7-0.20210607221512-0a9bff216f7c
+	gitlab.com/elixxir/comms v0.0.4-0.20210614160131-de90e88a68cc
+	gitlab.com/elixxir/crypto v0.0.7-0.20210614155844-c1e9c23a6ba7
 	gitlab.com/elixxir/ekv v0.1.5
-	gitlab.com/elixxir/primitives v0.0.3-0.20210607210820-afd1b028b558
-	gitlab.com/xx_network/comms v0.0.4-0.20210603164237-d0c36076d7f0
-	gitlab.com/xx_network/crypto v0.0.5-0.20210603164136-743cb9b0a967
-	gitlab.com/xx_network/primitives v0.0.4-0.20210607221158-361a2cbc5529
+	gitlab.com/elixxir/primitives v0.0.3-0.20210614155726-ebcf2d47a527
+	gitlab.com/xx_network/comms v0.0.4-0.20210614155654-191473de2702
+	gitlab.com/xx_network/crypto v0.0.5-0.20210614155554-8c333814205b
+	gitlab.com/xx_network/primitives v0.0.4-0.20210608160426-670aab2d82cf
 	golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2
 	golang.org/x/net v0.0.0-20210525063256-abc453219eb5
 	google.golang.org/genproto v0.0.0-20210105202744-fe13368bc0e1 // indirect
diff --git a/go.sum b/go.sum
index 66a72049798930218404a8ba6a848ee93c29d687..7f1f0b4b1edfbdfcf92ef60042ae87ba814e74ce 100644
--- a/go.sum
+++ b/go.sum
@@ -247,33 +247,32 @@ github.com/zeebo/pcg v1.0.0 h1:dt+dx+HvX8g7Un32rY9XWoYnd0NmKmrIzpHF7qiTDj0=
 github.com/zeebo/pcg v1.0.0/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
 gitlab.com/elixxir/bloomfilter v0.0.0-20200930191214-10e9ac31b228 h1:Gi6rj4mAlK0BJIk1HIzBVMjWNjIUfstrsXC2VqLYPcA=
 gitlab.com/elixxir/bloomfilter v0.0.0-20200930191214-10e9ac31b228/go.mod h1:H6jztdm0k+wEV2QGK/KYA+MY9nj9Zzatux/qIvDDv3k=
-gitlab.com/elixxir/comms v0.0.4-0.20210607222512-0f2e89b475b4 h1:9h/Dkvu8t4/Q2LY81qoxjbO4GJtVanjWV3B9UMuZgDA=
-gitlab.com/elixxir/comms v0.0.4-0.20210607222512-0f2e89b475b4/go.mod h1:Ox1NgdvFRy4/AfWAIrKZR9W6O+PAYeNOZviGbhqH+eo=
+gitlab.com/elixxir/comms v0.0.4-0.20210614160131-de90e88a68cc h1:MB+Ixmz/0eWt2akBYajjH2h53GF0NjWn4Oy4Q7x4QVg=
+gitlab.com/elixxir/comms v0.0.4-0.20210614160131-de90e88a68cc/go.mod h1:JeCKUXRS9xP3YYGPl4+OMFdvtt7ySJIxEsL9AzgeCu0=
 gitlab.com/elixxir/crypto v0.0.0-20200804182833-984246dea2c4/go.mod h1:ucm9SFKJo+K0N2GwRRpaNr+tKXMIOVWzmyUD0SbOu2c=
 gitlab.com/elixxir/crypto v0.0.3/go.mod h1:ZNgBOblhYToR4m8tj4cMvJ9UsJAUKq+p0gCp07WQmhA=
-gitlab.com/elixxir/crypto v0.0.7-0.20210607221512-0a9bff216f7c h1:G4IE4xEnoapQoZsMK1XRAB5QdOr2z+IyxZ6JOIXXuQ8=
-gitlab.com/elixxir/crypto v0.0.7-0.20210607221512-0a9bff216f7c/go.mod h1:HAW5sLSUHKgylen7C4YoVNr0kp5g21/eKJw6sCuBRFs=
+gitlab.com/elixxir/crypto v0.0.7-0.20210614155844-c1e9c23a6ba7 h1:UBq4/xMUWkYmEzUN2F7nLw5qQeiNKoLaoX3vZ/flz1c=
+gitlab.com/elixxir/crypto v0.0.7-0.20210614155844-c1e9c23a6ba7/go.mod h1:FP848WCzyf81/Csz1lJpi3NXgIdpzJ4hoJam53xwCuo=
 gitlab.com/elixxir/ekv v0.1.5 h1:R8M1PA5zRU1HVnTyrtwybdABh7gUJSCvt1JZwUSeTzk=
 gitlab.com/elixxir/ekv v0.1.5/go.mod h1:e6WPUt97taFZe5PFLPb1Dupk7tqmDCTQu1kkstqJvw4=
 gitlab.com/elixxir/primitives v0.0.0-20200731184040-494269b53b4d/go.mod h1:OQgUZq7SjnE0b+8+iIAT2eqQF+2IFHn73tOo+aV11mg=
 gitlab.com/elixxir/primitives v0.0.0-20200804170709-a1896d262cd9/go.mod h1:p0VelQda72OzoUckr1O+vPW0AiFe0nyKQ6gYcmFSuF8=
 gitlab.com/elixxir/primitives v0.0.0-20200804182913-788f47bded40/go.mod h1:tzdFFvb1ESmuTCOl1z6+yf6oAICDxH2NPUemVgoNLxc=
 gitlab.com/elixxir/primitives v0.0.1/go.mod h1:kNp47yPqja2lHSiS4DddTvFpB/4D9dB2YKnw5c+LJCE=
-gitlab.com/elixxir/primitives v0.0.3-0.20210607210820-afd1b028b558 h1:J8FllvIDv5RkON+Bg61NxNr78cYLOLifRrg/ugm5mW8=
-gitlab.com/elixxir/primitives v0.0.3-0.20210607210820-afd1b028b558/go.mod h1:1EFCSsERjE5RagCjys/70sqkAZC5PimEkWHcljh4bwQ=
+gitlab.com/elixxir/primitives v0.0.3-0.20210614155726-ebcf2d47a527 h1:kBNAGFy5Ylz7F0K3DmyzuHLf1npBg7a3t4qKvfqPL3Y=
+gitlab.com/elixxir/primitives v0.0.3-0.20210614155726-ebcf2d47a527/go.mod h1:nSmBXcw4hkBLFdhu+araAPvf9szCDQF1fpRZ9/BgBec=
 gitlab.com/xx_network/comms v0.0.0-20200805174823-841427dd5023/go.mod h1:owEcxTRl7gsoM8c3RQ5KAm5GstxrJp5tn+6JfQ4z5Hw=
-gitlab.com/xx_network/comms v0.0.4-0.20210603164237-d0c36076d7f0 h1:+/U+6Ra5pqDhIHCqMniESovsakooCFTv/omlpfvffU8=
-gitlab.com/xx_network/comms v0.0.4-0.20210603164237-d0c36076d7f0/go.mod h1:cpogFfWweZFzldGnRmgI9ilW2IFyeINDXpa1sAP950U=
+gitlab.com/xx_network/comms v0.0.4-0.20210614155654-191473de2702 h1:ydi8FaAjFGfxMcvmIGlvnng491K2uEl3ymALC2Hh8Vw=
+gitlab.com/xx_network/comms v0.0.4-0.20210614155654-191473de2702/go.mod h1:ehwxZxcAQHkJjP5BNkwPNK8/o6avUn0j0iDDiu+nMFc=
 gitlab.com/xx_network/crypto v0.0.3/go.mod h1:DF2HYvvCw9wkBybXcXAgQMzX+MiGbFPjwt3t17VRqRE=
 gitlab.com/xx_network/crypto v0.0.4/go.mod h1:+lcQEy+Th4eswFgQDwT0EXKp4AXrlubxalwQFH5O0Mk=
-gitlab.com/xx_network/crypto v0.0.5-0.20210603164136-743cb9b0a967 h1:gGDytDkXdwBDV92Xk8bcfHIzyoUfzIdFRNZnmTtx+Bc=
-gitlab.com/xx_network/crypto v0.0.5-0.20210603164136-743cb9b0a967/go.mod h1:qOOPEZXbsgM9n1wh9UDd9zd2IzuklQE+b7H2CMXaHx0=
+gitlab.com/xx_network/crypto v0.0.5-0.20210614155554-8c333814205b h1:X2Hhg9/IYowxMdI6TTnWj6WW3pnO2vMB/7f4mnu6Muw=
+gitlab.com/xx_network/crypto v0.0.5-0.20210614155554-8c333814205b/go.mod h1:wiaQXyI9C9UGxxgLd+2lDmKyovO+PjFxaesCBgG0YDA=
 gitlab.com/xx_network/primitives v0.0.0-20200803231956-9b192c57ea7c/go.mod h1:wtdCMr7DPePz9qwctNoAUzZtbOSHSedcK++3Df3psjA=
 gitlab.com/xx_network/primitives v0.0.0-20200804183002-f99f7a7284da/go.mod h1:OK9xevzWCaPO7b1wiluVJGk7R5ZsuC7pHY5hteZFQug=
 gitlab.com/xx_network/primitives v0.0.2/go.mod h1:cs0QlFpdMDI6lAo61lDRH2JZz+3aVkHy+QogOB6F/qc=
-gitlab.com/xx_network/primitives v0.0.4-0.20210603164056-0abf3f914f25/go.mod h1:9imZHvYwNFobxueSvVtHneZLk9wTK7HQTzxPm+zhFhE=
-gitlab.com/xx_network/primitives v0.0.4-0.20210607221158-361a2cbc5529 h1:zC1z2Pcxy+fQc3ZzRxz2lOorj1LqBms3xohIVmThPb0=
-gitlab.com/xx_network/primitives v0.0.4-0.20210607221158-361a2cbc5529/go.mod h1:9imZHvYwNFobxueSvVtHneZLk9wTK7HQTzxPm+zhFhE=
+gitlab.com/xx_network/primitives v0.0.4-0.20210608160426-670aab2d82cf h1:4snRmLHx/mgbdMtuXT2ITxyCElgvlOd7bJA1QSx0aI4=
+gitlab.com/xx_network/primitives v0.0.4-0.20210608160426-670aab2d82cf/go.mod h1:9imZHvYwNFobxueSvVtHneZLk9wTK7HQTzxPm+zhFhE=
 gitlab.com/xx_network/ring v0.0.3-0.20210527191221-ce3f170aabd5 h1:FY+4Rh1Q2rgLyv10aKJjhWApuKRCR/054XhreudfAvw=
 gitlab.com/xx_network/ring v0.0.3-0.20210527191221-ce3f170aabd5/go.mod h1:aLzpP2TiZTQut/PVHR40EJAomzugDdHXetbieRClXIM=
 go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
diff --git a/network/ephemeral/tracker.go b/network/ephemeral/tracker.go
index 83f855375c23ad6fa0d95d175802de5d12f5659c..5480125492017b62b6eaf8e14f7bbd6ab384f1f7 100644
--- a/network/ephemeral/tracker.go
+++ b/network/ephemeral/tracker.go
@@ -122,6 +122,7 @@ func track(session *storage.Session, addrSpace *AddressSpace, ourId *id.ID, stop
 		case addressSize = <-addressSizeUpdate:
 			receptionStore.SetToExpire(addressSize)
 		case <-stop.Quit():
+			addrSpace.UnregisterNotification(addressSpaceSizeChanTag)
 			stop.ToStopped()
 			return
 		}
diff --git a/network/follow.go b/network/follow.go
index aa505deecbb5cda93f1ebed0124ef070cf234705..3d5d8734b83c5892aae387e55e9e4ab6dba23e38 100644
--- a/network/follow.go
+++ b/network/follow.go
@@ -159,6 +159,7 @@ func (m *manager) follow(report interfaces.ClientErrorReport, rng csprng.Source,
 
 		// update gateway connections
 		m.GetSender().UpdateNdf(m.GetInstance().GetPartialNdf().Get())
+		m.Session.SetNDF(m.GetInstance().GetPartialNdf().Get())
 	}
 
 	// Update the address space size
diff --git a/network/gateway/hostPool.go b/network/gateway/hostPool.go
index ba8198da839c4e9d2693d866be165f6afaf08d47..1447608bfcc825f5e1269b2941d617718461662d 100644
--- a/network/gateway/hostPool.go
+++ b/network/gateway/hostPool.go
@@ -13,7 +13,6 @@ package gateway
 
 import (
 	"encoding/binary"
-	"fmt"
 	"github.com/pkg/errors"
 	jww "github.com/spf13/jwalterweatherman"
 	"gitlab.com/elixxir/client/storage"
@@ -81,7 +80,7 @@ func DefaultPoolParams() PoolParams {
 	p.HostParams.EnableCoolOff = true
 	p.HostParams.NumSendsBeforeCoolOff = 1
 	p.HostParams.CoolOffTimeout = 5 * time.Minute
-	p.HostParams.SendTimeout = 3500 * time.Millisecond
+	p.HostParams.SendTimeout = 2000 * time.Millisecond
 	return p
 }
 
@@ -115,8 +114,24 @@ func newHostPool(poolParams PoolParams, rng *fastRNG.StreamGenerator, ndf *ndf.N
 		return nil, err
 	}
 
+	// Get the last used list of hosts and use it to seed the host pool list
+	hostList, err := storage.HostList().Get()
+	numHostsAdded := 0
+	if err == nil {
+		for _, hid := range hostList {
+			err := result.replaceHostNoStore(hid, uint32(numHostsAdded))
+			if err != nil {
+				jww.WARN.Printf("Unable to add stored host %s: %s", hid, err.Error())
+			} else {
+				numHostsAdded++
+			}
+		}
+	} else {
+		jww.WARN.Printf("Building new HostPool because no HostList stored: %+v", err)
+	}
+
 	// Build the initial HostPool and return
-	for i := 0; i < len(result.hostList); i++ {
+	for i := numHostsAdded; i < len(result.hostList); i++ {
 		err := result.forceReplace(uint32(i))
 		if err != nil {
 			return nil, err
@@ -279,8 +294,29 @@ func (h *HostPool) forceReplace(oldPoolIndex uint32) error {
 	}
 }
 
-// Replace the given slot in the HostPool with a new Gateway with the specified ID
+// replaceHost replaces the given slot in the HostPool with a new Gateway with
+// the specified ID. The resulting host list is saved to storage.
 func (h *HostPool) replaceHost(newId *id.ID, oldPoolIndex uint32) error {
+	err := h.replaceHostNoStore(newId, oldPoolIndex)
+	if err != nil {
+		return err
+	}
+
+	// Convert list of of non-nil and non-zero hosts to ID list
+	idList := make([]*id.ID, 0, len(h.hostList))
+	for _, host := range h.hostList {
+		if host.GetId() != nil && !host.GetId().Cmp(&id.ID{}) {
+			idList = append(idList, host.GetId())
+		}
+	}
+
+	// Save the list to storage
+	return h.storage.HostList().Store(idList)
+}
+
+// replaceHostNoStore replaces the given slot in the HostPool with a new Gateway
+// with the specified ID.
+func (h *HostPool) replaceHostNoStore(newId *id.ID, oldPoolIndex uint32) error {
 	// Obtain that GwId's Host object
 	newHost, ok := h.manager.GetHost(newId)
 	if !ok {
@@ -291,7 +327,8 @@ func (h *HostPool) replaceHost(newId *id.ID, oldPoolIndex uint32) error {
 	// Keep track of oldHost for cleanup
 	oldHost := h.hostList[oldPoolIndex]
 
-	// Use the poolIdx to overwrite the random Host in the corresponding index in the hostList
+	// Use the poolIdx to overwrite the random Host in the corresponding index
+	// in the hostList
 	h.hostList[oldPoolIndex] = newHost
 	// Use the GwId to keep track of the new random Host's index in the hostList
 	h.hostMap[*newId] = oldPoolIndex
@@ -301,7 +338,9 @@ func (h *HostPool) replaceHost(newId *id.ID, oldPoolIndex uint32) error {
 		delete(h.hostMap, *oldHost.GetId())
 		go oldHost.Disconnect()
 	}
-	jww.DEBUG.Printf("Replaced Host at %d with new Host %s", oldPoolIndex, newId.String())
+	jww.DEBUG.Printf("Replaced Host at %d with new Host %s", oldPoolIndex,
+		newId.String())
+
 	return nil
 }
 
@@ -388,7 +427,7 @@ func (h *HostPool) removeGateway(gwId *id.ID) {
 func (h *HostPool) addGateway(gwId *id.ID, ndfIndex int) {
 	gw := h.ndf.Gateways[ndfIndex]
 
-	//check if the host exists
+	// Check if the host exists
 	host, ok := h.manager.GetHost(gwId)
 	if !ok {
 
@@ -443,7 +482,7 @@ func readUint32(rng io.Reader) uint32 {
 	var rndBytes [4]byte
 	i, err := rng.Read(rndBytes[:])
 	if i != 4 || err != nil {
-		panic(fmt.Sprintf("cannot read from rng: %+v", err))
+		jww.FATAL.Panicf("cannot read from rng: %+v", err)
 	}
 	return binary.BigEndian.Uint32(rndBytes[:])
 }
diff --git a/network/gateway/hostpool_test.go b/network/gateway/hostpool_test.go
index 49dcacf6b8231647b8c0cbc0ab6e6f84b0c7d126..f88bdf49a5c02e757781f3c452b9eef2759862bb 100644
--- a/network/gateway/hostpool_test.go
+++ b/network/gateway/hostpool_test.go
@@ -54,6 +54,49 @@ func TestNewHostPool(t *testing.T) {
 	}
 }
 
+// Tests that the hosts are loaded from storage, if they exist.
+func TestNewHostPool_HostListStore(t *testing.T) {
+	manager := newMockManager()
+	rng := fastRNG.NewStreamGenerator(1, 1, csprng.NewSystemRNG)
+	testNdf := getTestNdf(t)
+	testStorage := storage.InitTestingSession(t)
+	addGwChan := make(chan network.NodeGateway)
+	params := DefaultPoolParams()
+	params.MaxPoolSize = uint32(len(testNdf.Gateways))
+
+	addedIDs := []*id.ID{
+		id.NewIdFromString("testID0", id.Gateway, t),
+		id.NewIdFromString("testID1", id.Gateway, t),
+		id.NewIdFromString("testID2", id.Gateway, t),
+		id.NewIdFromString("testID3", id.Gateway, t),
+	}
+	err := testStorage.HostList().Store(addedIDs)
+	if err != nil {
+		t.Fatalf("Failed to store host list: %+v", err)
+	}
+
+	for i, hid := range addedIDs {
+		testNdf.Gateways[i].ID = hid.Marshal()
+	}
+
+	// Call the constructor
+	hp, err := newHostPool(params, rng, testNdf, manager, testStorage, addGwChan)
+	if err != nil {
+		t.Fatalf("Failed to create mock host pool: %v", err)
+	}
+
+	// Check that the host list was saved to storage
+	hostList, err := hp.storage.HostList().Get()
+	if err != nil {
+		t.Errorf("Failed to get host list: %+v", err)
+	}
+
+	if !reflect.DeepEqual(addedIDs, hostList) {
+		t.Errorf("Failed to save expected host list to storage."+
+			"\nexpected: %+v\nreceived: %+v", addedIDs, hostList)
+	}
+}
+
 // Unit test
 func TestHostPool_ManageHostPool(t *testing.T) {
 	manager := newMockManager()
@@ -115,7 +158,7 @@ func TestHostPool_ManageHostPool(t *testing.T) {
 	for _, ndfGw := range testNdf.Gateways {
 		gwId, err := id.Unmarshal(ndfGw.ID)
 		if err != nil {
-			t.Errorf("Failed to marshal gateway id for %v", ndfGw)
+			t.Fatalf("Failed to marshal gateway id for %v", ndfGw)
 		}
 		if _, ok := testPool.hostMap[*gwId]; ok {
 			t.Errorf("Expected gateway %v to be removed from pool", gwId)
@@ -135,6 +178,7 @@ func TestHostPool_ReplaceHost(t *testing.T) {
 		hostList: make([]*connect.Host, newIndex+1),
 		hostMap:  make(map[id.ID]uint32),
 		ndf:      testNdf,
+		storage:  storage.InitTestingSession(t),
 	}
 
 	/* "Replace" a host with no entry */
@@ -228,6 +272,18 @@ func TestHostPool_ReplaceHost(t *testing.T) {
 			"\n\tReceived: %d", newIndex, retrievedIndex)
 	}
 
+	// Check that the host list was saved to storage
+	hostList, err := hostPool.storage.HostList().Get()
+	if err != nil {
+		t.Errorf("Failed to get host list: %+v", err)
+	}
+
+	expectedList := []*id.ID{gwIdTwo}
+
+	if !reflect.DeepEqual(expectedList, hostList) {
+		t.Errorf("Failed to save expected host list to storage."+
+			"\nexpected: %+v\nreceived: %+v", expectedList, hostList)
+	}
 }
 
 // Error path, could not get host
@@ -754,7 +810,7 @@ func TestHostPool_UpdateConns_RemoveGateways(t *testing.T) {
 	for _, ndfGw := range testNdf.Gateways {
 		gwId, err := id.Unmarshal(ndfGw.ID)
 		if err != nil {
-			t.Errorf("Failed to marshal gateway id for %v", ndfGw)
+			t.Fatalf("Failed to marshal gateway id for %v", ndfGw)
 		}
 		if _, ok := testPool.hostMap[*gwId]; ok {
 			t.Errorf("Expected gateway %v to be removed from pool", gwId)
diff --git a/storage/hostList/hostList.go b/storage/hostList/hostList.go
new file mode 100644
index 0000000000000000000000000000000000000000..5242e7b8483ace3820111aa6c22455c7ba5a0257
--- /dev/null
+++ b/storage/hostList/hostList.go
@@ -0,0 +1,116 @@
+////////////////////////////////////////////////////////////////////////////////
+// Copyright © 2020 xx network SEZC                                           //
+//                                                                            //
+// Use of this source code is governed by a license that can be found in the  //
+// LICENSE file                                                               //
+////////////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////////////
+// Copyright © 2020 xx network SEZC                                           //
+//                                                                            //
+// Use of this source code is governed by a license that can be found in the  //
+// LICENSE file                                                               //
+////////////////////////////////////////////////////////////////////////////////
+
+package hostList
+
+import (
+	"bytes"
+	"github.com/pkg/errors"
+	"gitlab.com/elixxir/client/storage/versioned"
+	"gitlab.com/xx_network/primitives/id"
+	"gitlab.com/xx_network/primitives/netTime"
+)
+
+// Storage values.
+const (
+	hostListPrefix  = "hostLists"
+	hostListKey     = "hostListIDs"
+	hostListVersion = 0
+)
+
+// Error messages.
+const (
+	getStorageErr    = "failed to get host list from storage: %+v"
+	unmarshallIdErr  = "unmarshal host list error: %+v"
+	unmarshallLenErr = "malformed data: length of data %d incorrect"
+)
+
+type Store struct {
+	kv *versioned.KV
+}
+
+// NewStore creates a new Store with a prefixed KV.
+func NewStore(kv *versioned.KV) *Store {
+	return &Store{
+		kv: kv.Prefix(hostListPrefix),
+	}
+}
+
+// Store saves the list of host IDs to storage.
+func (s *Store) Store(list []*id.ID) error {
+	obj := &versioned.Object{
+		Version:   hostListVersion,
+		Data:      marshalHostList(list),
+		Timestamp: netTime.Now(),
+	}
+
+	return s.kv.Set(hostListKey, hostListVersion, obj)
+}
+
+// Get returns the host list from storage.
+func (s *Store) Get() ([]*id.ID, error) {
+	obj, err := s.kv.Get(hostListKey, hostListVersion)
+	if err != nil {
+		return nil, errors.Errorf(getStorageErr, err)
+	}
+
+	return unmarshalHostList(obj.Data)
+}
+
+// marshalHostList marshals the list of IDs into a byte slice.
+func marshalHostList(list []*id.ID) []byte {
+	buff := bytes.NewBuffer(nil)
+	buff.Grow(len(list) * id.ArrIDLen)
+
+	for _, hid := range list {
+		if hid != nil {
+			buff.Write(hid.Marshal())
+		} else {
+			buff.Write((&id.ID{}).Marshal())
+		}
+	}
+
+	return buff.Bytes()
+}
+
+// unmarshalHostList unmarshal the host list data into an ID list. An error is
+// returned if an ID cannot be unmarshalled or if the data is not of the correct
+// length.
+func unmarshalHostList(data []byte) ([]*id.ID, error) {
+	// Return an error if the data is not of the required length
+	if len(data)%id.ArrIDLen != 0 {
+		return nil, errors.Errorf(unmarshallLenErr, len(data))
+	}
+
+	buff := bytes.NewBuffer(data)
+	list := make([]*id.ID, 0, len(data)/id.ArrIDLen)
+
+	// Read each ID from data, unmarshal, and add to list
+	length := id.ArrIDLen
+	for n := buff.Next(length); len(n) == length; n = buff.Next(length) {
+		hid, err := id.Unmarshal(n)
+		if err != nil {
+			return nil, errors.Errorf(unmarshallIdErr, err)
+		}
+
+		// If the ID is all zeroes, then treat it as a nil ID.
+		if *hid == (id.ID{}) {
+			hid = nil
+		}
+
+		list = append(list, hid)
+	}
+
+	return list, nil
+}
diff --git a/storage/hostList/hostList_test.go b/storage/hostList/hostList_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..32780fae0a09db487520596e3748611a1bc5636c
--- /dev/null
+++ b/storage/hostList/hostList_test.go
@@ -0,0 +1,114 @@
+////////////////////////////////////////////////////////////////////////////////
+// Copyright © 2020 xx network SEZC                                           //
+//                                                                            //
+// Use of this source code is governed by a license that can be found in the  //
+// LICENSE file                                                               //
+////////////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////////////
+// Copyright © 2020 xx network SEZC                                           //
+//                                                                            //
+// Use of this source code is governed by a license that can be found in the  //
+// LICENSE file                                                               //
+////////////////////////////////////////////////////////////////////////////////
+
+package hostList
+
+import (
+	"fmt"
+	"gitlab.com/elixxir/client/storage/versioned"
+	"gitlab.com/elixxir/ekv"
+	"gitlab.com/xx_network/primitives/id"
+	"reflect"
+	"strings"
+	"testing"
+)
+
+// Unit test of NewStore.
+func TestNewStore(t *testing.T) {
+	kv := versioned.NewKV(make(ekv.Memstore))
+	expected := &Store{kv: kv.Prefix(hostListPrefix)}
+
+	s := NewStore(kv)
+
+	if !reflect.DeepEqual(expected, s) {
+		t.Errorf("NewStore did not return the expected object."+
+			"\nexpected: %+v\nreceived: %+v", expected, s)
+	}
+}
+
+// Tests that a host list saved by Store.Store matches the host list returned
+// by Store.Get.
+func TestStore_Store_Get(t *testing.T) {
+	s := NewStore(versioned.NewKV(make(ekv.Memstore)))
+	list := []*id.ID{
+		id.NewIdFromString("histID_1", id.Node, t),
+		nil,
+		id.NewIdFromString("histID_2", id.Node, t),
+		id.NewIdFromString("histID_3", id.Node, t),
+	}
+
+	err := s.Store(list)
+	if err != nil {
+		t.Errorf("Store returned an error: %+v", err)
+	}
+
+	newList, err := s.Get()
+	if err != nil {
+		t.Errorf("Get returned an error: %+v", err)
+	}
+
+	if !reflect.DeepEqual(list, newList) {
+		t.Errorf("Failed to save and load host list."+
+			"\nexpected: %+v\nreceived: %+v", list, newList)
+	}
+}
+
+// Error path: tests that Store.Get returns an error if not host list is
+// saved in storage.
+func TestStore_Get_StorageError(t *testing.T) {
+	s := NewStore(versioned.NewKV(make(ekv.Memstore)))
+	expectedErr := strings.SplitN(getStorageErr, "%", 2)[0]
+
+	_, err := s.Get()
+	if err == nil || !strings.Contains(err.Error(), expectedErr) {
+		t.Errorf("Get failed to return the expected error."+
+			"\nexpected: %s\nreceived: %+v", expectedErr, err)
+	}
+}
+
+// Tests that a list of IDs that is marshalled using marshalHostList and
+// unmarshalled using unmarshalHostList matches the original.
+func Test_marshalHostList_unmarshalHostList(t *testing.T) {
+	list := []*id.ID{
+		id.NewIdFromString("histID_1", id.Node, t),
+		nil,
+		id.NewIdFromString("histID_2", id.Node, t),
+		id.NewIdFromString("histID_3", id.Node, t),
+	}
+
+	data := marshalHostList(list)
+
+	newList, err := unmarshalHostList(data)
+	if err != nil {
+		t.Errorf("unmarshalHostList produced an error: %+v", err)
+	}
+
+	if !reflect.DeepEqual(list, newList) {
+		t.Errorf("Failed to marshal and unmarshal ID list."+
+			"\nexpected: %+v\nreceived: %+v", list, newList)
+	}
+}
+
+// Error path: tests that unmarshalHostList returns an error if the data is not
+// of the correct length.
+func Test_unmarshalHostList_InvalidDataErr(t *testing.T) {
+	data := []byte("Invalid Data")
+	expectedErr := fmt.Sprintf(unmarshallLenErr, len(data))
+
+	_, err := unmarshalHostList(data)
+	if err == nil || err.Error() != expectedErr {
+		t.Errorf("unmarshalHostList failed to return the expected error."+
+			"\nexpected: %s\nreceived: %+v", expectedErr, err)
+	}
+}
diff --git a/storage/ndf.go b/storage/ndf.go
index 14cb4147b89a4fa53de52e505627c12dbd3abc15..1b081fd0f68c32b140492cc5a61b4f8052eb2134 100644
--- a/storage/ndf.go
+++ b/storage/ndf.go
@@ -13,25 +13,25 @@ import (
 	"gitlab.com/xx_network/primitives/ndf"
 )
 
-const baseNdfKey = "baseNdf"
+const ndfKey = "ndf"
 
-func (s *Session) SetBaseNDF(def *ndf.NetworkDefinition) {
-	err := utility.SaveNDF(s.kv, baseNdfKey, def)
+func (s *Session) SetNDF(def *ndf.NetworkDefinition) {
+	err := utility.SaveNDF(s.kv, ndfKey, def)
 	if err != nil {
-		jww.FATAL.Printf("Failed to dave the base NDF: %s", err)
+		jww.FATAL.Printf("Failed to dave the NDF: %+v", err)
 	}
-	s.baseNdf = def
+	s.ndf = def
 }
 
-func (s *Session) GetBaseNDF() *ndf.NetworkDefinition {
-	if s.baseNdf != nil {
-		return s.baseNdf
+func (s *Session) GetNDF() *ndf.NetworkDefinition {
+	if s.ndf != nil {
+		return s.ndf
 	}
-	def, err := utility.LoadNDF(s.kv, baseNdfKey)
+	def, err := utility.LoadNDF(s.kv, ndfKey)
 	if err != nil {
-		jww.FATAL.Printf("Could not load the base NDF: %s", err)
+		jww.FATAL.Printf("Could not load the NDF: %+v", err)
 	}
 
-	s.baseNdf = def
+	s.ndf = def
 	return def
 }
diff --git a/storage/session.go b/storage/session.go
index 019e187d3176b9c4ea1b24f26eaf31697e13e458..e42cad445594e30d7b85eafa688d90b80d4d62ed 100644
--- a/storage/session.go
+++ b/storage/session.go
@@ -10,6 +10,7 @@
 package storage
 
 import (
+	"gitlab.com/elixxir/client/storage/hostList"
 	"gitlab.com/elixxir/client/storage/rounds"
 	"sync"
 	"testing"
@@ -50,7 +51,7 @@ type Session struct {
 
 	//memoized data
 	regStatus RegistrationStatus
-	baseNdf   *ndf.NetworkDefinition
+	ndf       *ndf.NetworkDefinition
 
 	//sub-stores
 	e2e                 *e2e.Store
@@ -65,6 +66,7 @@ type Session struct {
 	reception           *reception.Store
 	clientVersion       *clientVersion.Store
 	uncheckedRounds     *rounds.UncheckedRoundStore
+	hostList            *hostList.Store
 }
 
 // Initialize a new Session object
@@ -148,6 +150,9 @@ func New(baseDir, password string, u userInterface.User, currentVersion version.
 	if err != nil {
 		return nil, errors.WithMessage(err, "Failed to create unchecked round store")
 	}
+
+	s.hostList = hostList.NewStore(s.kv)
+
 	return s, nil
 }
 
@@ -224,6 +229,8 @@ func Load(baseDir, password string, currentVersion version.Version,
 		return nil, errors.WithMessage(err, "Failed to load unchecked round store")
 	}
 
+	s.hostList = hostList.NewStore(s.kv)
+
 	return s, nil
 }
 
@@ -300,6 +307,12 @@ func (s *Session) UncheckedRounds() *rounds.UncheckedRoundStore {
 	return s.uncheckedRounds
 }
 
+func (s *Session) HostList() *hostList.Store {
+	s.mux.RLock()
+	defer s.mux.RUnlock()
+	return s.hostList
+}
+
 // Get an object from the session
 func (s *Session) Get(key string) (*versioned.Object, error) {
 	return s.kv.Get(key, currentSessionVersion)
@@ -401,5 +414,7 @@ func InitTestingSession(i interface{}) *Session {
 		jww.FATAL.Panicf("Failed to create uncheckRound store: %v", err)
 	}
 
+	s.hostList = hostList.NewStore(s.kv)
+
 	return s
 }
diff --git a/ud/manager.go b/ud/manager.go
index 842d8d353a6d05ab523db2f887c2036d169f976c..f3b7afc643c27af41d2d1591de2ff89755c6104d 100644
--- a/ud/manager.go
+++ b/ud/manager.go
@@ -89,6 +89,8 @@ func NewManager(client *api.Client, single *single.Manager) (*Manager, error) {
 
 	// Create the user discovery host object
 	hp := connect.GetDefaultHostParams()
+	hp.MaxRetries = 3
+	hp.SendTimeout = 3 * time.Second
 	m.host, err = m.comms.AddHost(&id.UDB, def.UDB.Address, []byte(def.UDB.Cert), hp)
 	if err != nil {
 		return nil, errors.WithMessage(err, "User Discovery host object could "+