diff --git a/Makefile b/Makefile
index 37f286586e0ae2aeeb980466bafe7dd933d0c27c..764f7bc7e06b85bacf7dad6ee856915f73ceaa9f 100644
--- a/Makefile
+++ b/Makefile
@@ -24,6 +24,7 @@ update_release:
 	GOFLAGS="" go get -u gitlab.com/elixxir/crypto@release
 	GOFLAGS="" go get -u gitlab.com/xx_network/comms@release
 	GOFLAGS="" go get -u gitlab.com/elixxir/comms@release
+	GOFLAGS="" go get -u gitlab.com/elixxir/client@release
 
 update_master:
 	GOFLAGS="" go get -u gitlab.com/elixxir/primitives@master
@@ -31,6 +32,7 @@ update_master:
 	GOFLAGS="" go get -u gitlab.com/elixxir/crypto@master
 	GOFLAGS="" go get -u gitlab.com/xx_network/comms@master
 	GOFLAGS="" go get -u gitlab.com/elixxir/comms@master
+	GOFLAGS="" go get -u gitlab.com/elixxir/client@master
 
 master: clean update_master build version
 
diff --git a/README.md b/README.md
index ee04257af4df339150df7c489796a959eb81731a..d7ae8dbd695f57dbaf25ebae686c831dbe87cd56 100644
--- a/README.md
+++ b/README.md
@@ -45,9 +45,16 @@ minGatewayVersion: "0.0.0"
 # The minimum version required of servers to connect
 minServerVersion:  "0.0.0"
 
+# The minimum version required of clients to connect
+minClientVersion: "0.0.0"
+
 # Disable pinging of Gateway public IP address.
 disableGatewayPing: false
 
+# Disable pruning of NDF for offline nodes
+# if set to false, network will sleep for five minutes on start
+disableNDFPruning: true
+
 # Database connection information
 dbUsername: "cmix"
 dbPassword: ""
diff --git a/cmd/bannedNodeTracker_test.go b/cmd/bannedNodeTracker_test.go
index 9ca3e37a2700738d99979bc52b2f6251f05960e1..27c1fca22dd59288c02d316e9630c67a82a27e70 100644
--- a/cmd/bannedNodeTracker_test.go
+++ b/cmd/bannedNodeTracker_test.go
@@ -28,7 +28,7 @@ func TestBannedNodeTracker(t *testing.T) {
 
 	// Build network state
 	privKey, _ := rsa.GenerateKey(rand.Reader, 2048)
-	testState, err := storage.NewState(privKey, 8)
+	testState, err := storage.NewState(privKey, 8, "")
 	impl := &RegistrationImpl{
 		State:   testState,
 		NDFLock: sync.Mutex{},
@@ -47,7 +47,7 @@ func TestBannedNodeTracker(t *testing.T) {
 	// Create an active and banned node
 	bannedNode := createNode(testState, "0", "AAA", 10, node.Banned, t)
 	activeNode := createNode(testState, "1", "BBB", 20, node.Active, t)
-	curDef := testState.GetFullNdf().Get()
+	curDef := testState.GetUnprunedNdf()
 	curDef.Nodes = append(curDef.Nodes, ndf.Node{
 		ID:             bannedNode.Marshal(),
 		Address:        "",
@@ -69,7 +69,7 @@ func TestBannedNodeTracker(t *testing.T) {
 		t.Errorf("Error with node tracker: %v", err)
 	}
 
-	updatedDef := testState.GetFullNdf().Get()
+	updatedDef := testState.GetUnprunedNdf()
 	if len(updatedDef.Nodes) != 1 {
 		t.Error("Banned node tracker did not alter ndf")
 	}
diff --git a/cmd/impl.go b/cmd/impl.go
index 942063ece60a2da42c9ccd88c8bb25961ccce054..96add3bac0ff3bf9fad5f5bbd36a04035d7021de 100644
--- a/cmd/impl.go
+++ b/cmd/impl.go
@@ -76,7 +76,7 @@ func StartRegistration(params Params) (*RegistrationImpl, error) {
 	}
 
 	// Initialize the state tracking object
-	state, err := storage.NewState(pk, params.addressSpace)
+	state, err := storage.NewState(pk, params.addressSpace, params.NdfOutputPath)
 	if err != nil {
 		return nil, err
 	}
@@ -188,7 +188,7 @@ func BannedNodeTracker(impl *RegistrationImpl) error {
 
 	impl.NDFLock.Lock()
 	defer impl.NDFLock.Unlock()
-	def := state.GetFullNdf().Get()
+	def := state.GetUnprunedNdf()
 
 	// Parse through the returned node list
 	for _, n := range bannedNodes {
diff --git a/cmd/params.go b/cmd/params.go
index 0c845bf730e106ae419da9da724162975dbf38c2..3f49b249770bfe7826d9aa1735fc744c70a5803c 100644
--- a/cmd/params.go
+++ b/cmd/params.go
@@ -43,6 +43,8 @@ type Params struct {
 	// User registration can take userRegCapacity registrations in userRegLeakPeriod period of time
 	userRegCapacity   uint32
 	userRegLeakPeriod time.Duration
+
+	disableNDFPruning bool
 }
 
 // toGroup takes a group represented by a map of string to string,
diff --git a/cmd/permissioning.go b/cmd/permissioning.go
index de0b26fb0d8d149f96fc8d6bf233e5a46a1e9297..82992ffa915d01c2ff3a6f5ee9565e52ea79e32e 100644
--- a/cmd/permissioning.go
+++ b/cmd/permissioning.go
@@ -22,7 +22,6 @@ import (
 	"gitlab.com/xx_network/crypto/xx"
 	"gitlab.com/xx_network/primitives/id"
 	"gitlab.com/xx_network/primitives/ndf"
-	"gitlab.com/xx_network/primitives/utils"
 	"sync/atomic"
 )
 
@@ -213,7 +212,7 @@ func (m *RegistrationImpl) completeNodeRegistration(regCode string) error {
 
 	// Add the new node to the topology
 	m.NDFLock.Lock()
-	networkDef := m.State.GetFullNdf().Get()
+	networkDef := m.State.GetUnprunedNdf()
 	gateway, n, regTime, err := assembleNdf(regCode)
 	if err != nil {
 		m.NDFLock.Unlock()
@@ -235,6 +234,12 @@ func (m *RegistrationImpl) completeNodeRegistration(regCode string) error {
 		return errors.WithMessage(err, "Failed to insert nodes in definition")
 	}
 
+	//set the node as pruned if pruning is not disabled to ensure they have
+	//to be online to get scheduled
+	if !m.params.disableNDFPruning {
+		m.State.SetPrunedNode(nodeID)
+	}
+
 	// update the internal state with the newly-updated ndf
 	err = m.State.UpdateNdf(networkDef)
 	m.NDFLock.Unlock()
@@ -242,14 +247,6 @@ func (m *RegistrationImpl) completeNodeRegistration(regCode string) error {
 		return err
 	}
 
-	// Output the current topology to a JSON file
-	err = outputToJSON(networkDef, m.ndfOutputPath)
-	if err != nil {
-		err := errors.Errorf("unable to output NDF JSON file: %+v", err)
-		jww.ERROR.Print(err.Error())
-		return errors.Errorf("Could not complete registration: %+v", err)
-	}
-
 	// Kick off the network if the minimum number of nodes has been met
 	if uint32(m.numRegistered) == m.params.minimumNodes {
 		atomic.CompareAndSwapUint32(m.NdfReady, 0, 1)
@@ -339,16 +336,3 @@ func assembleNdf(code string) (ndf.Gateway, ndf.Node, int64, error) {
 
 	return gateway, n, nodeInfo.DateRegistered.UnixNano(), nil
 }
-
-// outputNodeTopologyToJSON encodes the NodeTopology structure to JSON and
-// outputs it to the specified file path. An error is returned if the JSON
-// marshaling fails or if the JSON file cannot be created.
-func outputToJSON(ndfData *ndf.NetworkDefinition, filePath string) error {
-	// Generate JSON from structure
-	data, err := ndfData.Marshal()
-	if err != nil {
-		return err
-	}
-	// Write JSON to file
-	return utils.WriteFile(filePath, data, utils.FilePerms, utils.DirPerms)
-}
diff --git a/cmd/permissioning_test.go b/cmd/permissioning_test.go
index a118f8b0993ec91d64bddafdce89bc92e87cb8b7..b14e05d45bae68aebe19410b0fd02daa2bad9f76 100644
--- a/cmd/permissioning_test.go
+++ b/cmd/permissioning_test.go
@@ -67,10 +67,11 @@ func TestLoadAllRegisteredNodes(t *testing.T) {
 	//region Test code
 	// Create params for test registration server
 	testParams := Params{
-		CertPath:      testkeys.GetCACertPath(),
-		KeyPath:       testkeys.GetCAKeyPath(),
-		NdfOutputPath: testkeys.GetNDFPath(),
-		udbCertPath:   testkeys.GetUdbCertPath(),
+		CertPath:          testkeys.GetCACertPath(),
+		KeyPath:           testkeys.GetCAKeyPath(),
+		NdfOutputPath:     testkeys.GetNDFPath(),
+		udbCertPath:       testkeys.GetUdbCertPath(),
+		disableNDFPruning: true,
 	}
 
 	// Start registration server
@@ -113,7 +114,7 @@ func TestLoadAllRegisteredNodes(t *testing.T) {
 		t.Errorf("Unexpected number of nodes found in node map:\n\tGot: %d\n"+
 			"\tExpected: %d", len(nodeMapNodes), expected_nodes)
 	}
-	def := impl.State.GetFullNdf().Get()
+	def := impl.State.GetUnprunedNdf()
 	id0, err := id.Unmarshal(def.Nodes[0].ID)
 	if err != nil {
 		t.Error("Failed to unmarshal ID")
diff --git a/cmd/poll.go b/cmd/poll.go
index be65a0c3048a19f2cb8f0866dff8afd8df85992f..0260baade6eb193264be9d29fb519cfed30628e8 100644
--- a/cmd/poll.go
+++ b/cmd/poll.go
@@ -21,6 +21,7 @@ import (
 	"gitlab.com/xx_network/comms/signature"
 	"gitlab.com/xx_network/primitives/id"
 	"gitlab.com/xx_network/primitives/ndf"
+	"math/rand"
 	"sync/atomic"
 )
 
@@ -36,12 +37,6 @@ func (m *RegistrationImpl) Poll(msg *pb.PermissioningPoll, auth *connect.Auth) (
 			"is nil, poll cannot be processed")
 	}
 
-	// Ensure the NDF is ready to be returned
-	regComplete := atomic.LoadUint32(m.NdfReady)
-	if regComplete != 1 {
-		return response, errors.New(ndf.NO_NDF)
-	}
-
 	// Ensure client is properly authenticated
 	if !auth.IsAuthenticated || auth.Sender.IsDynamicHost() {
 		return response, connect.AuthError(auth.Sender.GetId())
@@ -58,18 +53,11 @@ func (m *RegistrationImpl) Poll(msg *pb.PermissioningPoll, auth *connect.Auth) (
 
 	// Check if the node has been deemed out of network
 	if n.IsBanned() {
-		return nil, errors.Errorf("Node %s has been banned from the network", nid)
+		return response, errors.Errorf("Node %s has been banned from the network", nid)
 	}
 
 	activity := current.Activity(msg.Activity)
 
-	// check that the activity is not error and then poll, do not count error
-	// polls so erring nodes are not issues rounds
-	if activity != current.ERROR {
-		// Increment the Node's poll count
-		n.IncrementNumPolls()
-	}
-
 	// update ip addresses if necessary
 	err := checkIPAddresses(m, n, msg, auth.Sender)
 	if err != nil {
@@ -81,7 +69,22 @@ func (m *RegistrationImpl) Poll(msg *pb.PermissioningPoll, auth *connect.Auth) (
 	err = checkVersion(m.params.minGatewayVersion, m.params.minServerVersion,
 		msg)
 	if err != nil {
-		return nil, err
+		return response, err
+	}
+
+	// Increment the Node's poll count
+	n.IncrementNumPolls()
+
+	// Check the node's connectivity
+	continuePoll, err := m.checkConnectivity(n, activity, m.GetDisableGatewayPingFlag())
+	if err != nil || !continuePoll {
+		return response, err
+	}
+
+	// Ensure the NDF is ready to be returned
+	regComplete := atomic.LoadUint32(m.NdfReady)
+	if regComplete != 1 {
+		return response, errors.New(ndf.NO_NDF)
 	}
 
 	// Return updated NDF if provided hash does not match current NDF hash
@@ -99,12 +102,6 @@ func (m *RegistrationImpl) Poll(msg *pb.PermissioningPoll, auth *connect.Auth) (
 		return response, err
 	}
 
-	// Check the node's connectivity
-	continuePoll, err := m.checkConnectivity(n, activity, m.GetDisableGatewayPingFlag())
-	if err != nil || !continuePoll {
-		return response, err
-	}
-
 	// Commit updates reported by the node if node involved in the current round
 	jww.TRACE.Printf("Updating state for node %s: %+v",
 		auth.Sender.GetId(), msg)
@@ -130,6 +127,11 @@ func (m *RegistrationImpl) Poll(msg *pb.PermissioningPoll, auth *connect.Auth) (
 		return response, err
 	}
 
+	//check if the node is pruned if it is, bail
+	if m.State.IsPruned(n.GetID()) {
+		return response, err
+	}
+
 	// when a node poll is received, the nodes polling lock is taken here. If
 	// there is no update, it is released in this endpoint, otherwise it is
 	// released in the scheduling algorithm which blocks all future polls until
@@ -330,7 +332,7 @@ func checkIPAddresses(m *RegistrationImpl, n *node.State,
 		}
 
 		m.NDFLock.Lock()
-		currentNDF := m.State.GetFullNdf().Get()
+		currentNDF := m.State.GetUnprunedNdf()
 
 		if nodeUpdate {
 			nodeHost.UpdateAddress(nodeAddress)
@@ -354,13 +356,6 @@ func checkIPAddresses(m *RegistrationImpl, n *node.State,
 			return err
 		}
 		m.NDFLock.Unlock()
-
-		// Output the current topology to a JSON file
-		err = outputToJSON(currentNDF, m.ndfOutputPath)
-		if err != nil {
-			err := errors.Errorf("unable to output NDF JSON file: %+v", err)
-			jww.ERROR.Print(err.Error())
-		}
 	}
 
 	return nil
@@ -419,34 +414,44 @@ func (m *RegistrationImpl) checkConnectivity(n *node.State,
 
 		// this will approximately force a recheck of the node state every 3~5
 		// minutes
-		if n.GetNumPolls()%211 == 13 {
+		if rand.Uint64()%211==13 {
 			n.SetConnectivity(node.PortUnknown)
 		}
+		nodeAddress := "unknown"
+		if nodeHost, exists := m.Comms.GetHost(n.GetID()); exists{
+			nodeAddress = nodeHost.GetAddress()
+		}
 		// If only the Node port has been marked as failed,
 		// we send an error informing the node of such
-		return false, errors.Errorf("Node %s cannot be contacted "+
-			"by Permissioning, are ports properly forwarded?", n.GetID())
+		return false, errors.Errorf("Node %s at %s cannot be contacted "+
+			"by Permissioning, are ports properly forwarded?", n.GetID(), nodeAddress)
 	case node.GatewayPortFailed:
 		// this will approximately force a recheck of the node state every 3~5
 		// minutes
-		if n.GetNumPolls()%211 == 13 {
+		if rand.Uint64()%211==13 {
 			n.SetConnectivity(node.PortUnknown)
 		}
+		gwID := n.GetID().DeepCopy()
+		gwID.SetType(id.Gateway)
 		// If only the Gateway port has been marked as failed,
 		// we send an error informing the node of such
-		return false, errors.Errorf("Gateway with address %s cannot be contacted "+
-			"by Permissioning, are ports properly forwarded?", n.GetGatewayAddress())
+		return false, errors.Errorf("Gateway %s with address %s cannot be contacted "+
+			"by Permissioning, are ports properly forwarded?", gwID, n.GetGatewayAddress())
 	case node.PortFailed:
 		// this will approximately force a recheck of the node state every 3~5
 		// minutes
-		if n.GetNumPolls()%211 == 13 {
+		if rand.Uint64()%211==13 {
 			n.SetConnectivity(node.PortUnknown)
 		}
+		nodeAddress := "unknown"
+		if nodeHost, exists := m.Comms.GetHost(n.GetID()); exists{
+			nodeAddress = nodeHost.GetAddress()
+		}
 		// If the port has been marked as failed,
 		// we send an error informing the node of such
-		return false, errors.Errorf("Both Node %s and Gateway with address %s "+
+		return false, errors.Errorf("Both Node %s at %s and Gateway with address %s "+
 			"cannot be contacted by Permissioning, are ports properly forwarded?",
-			n.GetID(), n.GetGatewayAddress())
+			n.GetID(), nodeAddress, n.GetGatewayAddress())
 	}
 
 	return false, nil
diff --git a/cmd/poll_test.go b/cmd/poll_test.go
index 9719726d4a982e236a00591ad03db0e40e826a82..9f09f271988252ab0b112aa0e97e6bf3dbdd70b5 100644
--- a/cmd/poll_test.go
+++ b/cmd/poll_test.go
@@ -11,6 +11,7 @@ import (
 	"fmt"
 	pb "gitlab.com/elixxir/comms/mixmessages"
 	"gitlab.com/elixxir/comms/registration"
+	"gitlab.com/elixxir/comms/testutils"
 	"gitlab.com/elixxir/primitives/current"
 	"gitlab.com/elixxir/primitives/states"
 	"gitlab.com/elixxir/primitives/version"
@@ -118,24 +119,17 @@ func TestRegistrationImpl_Poll(t *testing.T) {
 	impl.Comms.Shutdown()
 }
 
-// Error path: Ndf not ready
+/*// Error path: Ndf not ready
 func TestRegistrationImpl_PollNoNdf(t *testing.T) {
 
-	// Read in private key
-	key, err := utils.ReadFile(testkeys.GetCAKeyPath())
-	if err != nil {
-		t.Errorf("failed to read key at %+v: %+v",
-			testkeys.GetCAKeyPath(), err)
-	}
-
-	pk, err := rsa.LoadPrivateKeyFromPem(key)
+	pk, err := testutils.LoadPrivateKeyTesting(t)
 	if err != nil {
 		t.Errorf("Failed to parse permissioning server key: %+v. "+
 			"PermissioningKey is %+v", err, pk)
 	}
 	// Start registration server
 	ndfReady := uint32(0)
-	state, err := storage.NewState(pk, 8)
+	state, err := storage.NewState(pk, 8, "")
 	if err != nil {
 		t.Errorf("Unable to create state: %+v", err)
 	}
@@ -155,7 +149,7 @@ func TestRegistrationImpl_PollNoNdf(t *testing.T) {
 	if err == nil || err.Error() != ndf.NO_NDF {
 		t.Errorf("Unexpected error polling: %+v", err)
 	}
-}
+}*/
 
 // Error path: Failed auth
 func TestRegistrationImpl_PollFailAuth(t *testing.T) {
@@ -163,7 +157,7 @@ func TestRegistrationImpl_PollFailAuth(t *testing.T) {
 
 	// Start registration server
 	ndfReady := uint32(1)
-	state, err := storage.NewState(getTestKey(), 8)
+	state, err := storage.NewState(getTestKey(), 8, "")
 	if err != nil {
 		t.Errorf("Unable to create state: %+v", err)
 	}
@@ -221,6 +215,7 @@ func TestRegistrationImpl_PollNdf(t *testing.T) {
 	udbId := id.NewIdFromUInt(5, id.User, t)
 	RegParams.udbId = udbId.Marshal()
 	RegParams.minimumNodes = 3
+	RegParams.disableNDFPruning = true
 	// Start registration server
 	impl, err := StartRegistration(RegParams)
 	if err != nil {
@@ -280,7 +275,6 @@ func TestRegistrationImpl_PollNdf(t *testing.T) {
 			string(observedNDFBytes))
 	}
 
-	fmt.Printf("\n\n\nndf: %v\n\n\n", observedNDF.Nodes)
 	if bytes.Compare(observedNDF.UDB.ID, udbId.Marshal()) != 0 {
 		t.Errorf("Failed to set udbID. Expected: %v, \nRecieved: %v, \nNdf: %+v",
 			udbId, observedNDF.UDB.ID, observedNDF)
@@ -313,7 +307,7 @@ func TestRegistrationImpl_PollNdf_NoNDF(t *testing.T) {
 	udbId := id.NewIdFromUInt(5, id.User, t)
 	RegParams.udbId = udbId.Marshal()
 	RegParams.minimumNodes = 3
-
+	RegParams.disableNDFPruning = true
 	// Start registration server
 	impl, err := StartRegistration(testParams)
 	if err != nil {
@@ -357,6 +351,7 @@ func TestPoll_BannedNode(t *testing.T) {
 	testString := "test"
 	// Start registration server
 	testParams.KeyPath = testkeys.GetCAKeyPath()
+	testParams.disableNDFPruning = true
 	impl, err := StartRegistration(testParams)
 	if err != nil {
 		t.Errorf("Unable to start registration: %+v", err)
@@ -878,20 +873,14 @@ func TestVerifyError(t *testing.T) {
 	}
 
 	// Read in private key
-	key, err := utils.ReadFile(testkeys.GetCAKeyPath())
-	if err != nil {
-		t.Errorf("failed to read key at %+v: %+v",
-			testkeys.GetCAKeyPath(), err)
-	}
-
-	pk, err := rsa.LoadPrivateKeyFromPem(key)
+	pk, err := testutils.LoadPrivateKeyTesting(t)
 	if err != nil {
 		t.Errorf("Failed to parse permissioning server key: %+v. "+
 			"PermissioningKey is %+v", err, pk)
 	}
 	// Start registration server
 	ndfReady := uint32(0)
-	state, err := storage.NewState(pk, 8)
+	state, err := storage.NewState(pk, 8, "")
 	if err != nil {
 		t.Errorf("Unable to create state: %+v", err)
 	}
@@ -903,6 +892,7 @@ func TestVerifyError(t *testing.T) {
 		params: &Params{
 			minGatewayVersion: testVersion,
 			minServerVersion:  testVersion,
+			disableNDFPruning: true,
 		},
 		Comms: &registration.Comms{
 			ProtoComms: &connect.ProtoComms{
diff --git a/cmd/registration.go b/cmd/registration.go
index 46bb05ee41f70c5f8d59e9043baafe75143f7790..5a52abbf59dc99c459fc318988845188be60d89b 100644
--- a/cmd/registration.go
+++ b/cmd/registration.go
@@ -22,20 +22,19 @@ var rateLimitErr = errors.New("Too many client registrations. Try again later")
 // Handle registration attempt by a Client
 // Returns rsa signature and error
 func (m *RegistrationImpl) RegisterUser(regCode string, pubKey string, receptionKey string) ([]byte, []byte, error) {
-	jww.INFO.Printf("RegisterUser %s", pubKey)
 	// Check for pre-existing registration for this public key first
 	if user, err := storage.PermissioningDb.GetUser(pubKey); err == nil && user != nil {
-		jww.INFO.Printf("Previous registration found for %s", pubKey)
+		jww.WARN.Printf("Previous registration found for %s", pubKey)
 	} else if regCode != "" {
 		// Fail early for non-valid reg codes
 		err = storage.PermissioningDb.UseCode(regCode)
 		if err != nil {
-			jww.INFO.Printf("RegisterUser error: %+v", err)
+			jww.WARN.Printf("RegisterUser error: %+v", err)
 			return nil, nil, err
 		}
 	} else if regCode == "" && !m.registrationLimiting.Add(1) {
 		// Rate limited, fail early
-		jww.INFO.Printf("RegisterUser error: %+v", rateLimitErr)
+		jww.WARN.Printf("RegisterUser error: %+v", rateLimitErr)
 		return nil, nil, rateLimitErr
 	}
 
@@ -45,7 +44,7 @@ func (m *RegistrationImpl) RegisterUser(regCode string, pubKey string, reception
 	h.Write([]byte(pubKey))
 	transmissionSig, err := rsa.Sign(rand.Reader, m.State.GetPrivateKey(), hash.CMixHash, h.Sum(nil), nil)
 	if err != nil {
-		jww.INFO.Printf("RegisterUser error: can't sign pubkey")
+		jww.WARN.Printf("RegisterUser error: can't sign pubkey")
 		return make([]byte, 0), make([]byte, 0), errors.Errorf(
 			"Unable to sign client public key: %+v", err)
 	}
diff --git a/cmd/root.go b/cmd/root.go
index a44619a582bd1e3147316db52a71c9930bc533c5..c76c371aae8d681c072175d23a7d36f5db44ac7b 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -22,6 +22,7 @@ import (
 	"gitlab.com/elixxir/registration/scheduling"
 	"gitlab.com/elixxir/registration/storage"
 	"gitlab.com/elixxir/registration/storage/node"
+	"gitlab.com/xx_network/primitives/id"
 	"gitlab.com/xx_network/primitives/utils"
 	"net"
 	"os"
@@ -223,6 +224,7 @@ var rootCmd = &cobra.Command{
 			userRegLeakPeriod:     userRegLeakPeriod,
 			userRegCapacity:       userRegCapacity,
 			addressSpace:          viper.GetUint32("addressSpace"),
+			disableNDFPruning:     viper.GetBool("disableNDFPruning"),
 		}
 
 		jww.INFO.Println("Starting Permissioning Server...")
@@ -276,7 +278,7 @@ var rootCmd = &cobra.Command{
 				select {
 				// Wait for the ticker to fire
 				case <-nodeTicker.C:
-
+					var toPrune []*id.ID
 					// Iterate over the Node States
 					nodeStates := impl.State.GetNodeMap().GetNodeStates()
 					for _, nodeState := range nodeStates {
@@ -290,6 +292,11 @@ var rootCmd = &cobra.Command{
 							NumPings:  nodeState.GetAndResetNumPolls(),
 						}
 
+						//set the node to prune if it has not contacted
+						if metric.NumPings == 0 {
+							toPrune = append(toPrune, nodeState.GetID())
+						}
+
 						// Store the NodeMetric
 						err := storage.PermissioningDb.InsertNodeMetric(metric)
 						if err != nil {
@@ -297,6 +304,16 @@ var rootCmd = &cobra.Command{
 								"Unable to store node metric: %+v", err)
 						}
 					}
+
+					if !RegParams.disableNDFPruning {
+						jww.DEBUG.Printf("Setting %d pruned nodes", len(toPrune))
+						impl.State.SetPrunedNodes(toPrune)
+						err := impl.State.UpdateNdf(impl.State.GetUnprunedNdf())
+						if err != nil {
+							jww.ERROR.Printf("Failed to regenerate the " +
+								"NDF after changing pruning")
+						}
+					}
 				}
 			}
 		}(metricTrackerQuitChan)
diff --git a/cmd/version.go b/cmd/version.go
index 848188f02b942e09a501c2fadb75fa83e1bb4717..0887c28039c03be0f68aadf2d6d934d663621367 100644
--- a/cmd/version.go
+++ b/cmd/version.go
@@ -16,7 +16,7 @@ import (
 )
 
 // Change this value to set the version for this build
-const currentVersion = "2.0.0"
+const currentVersion = "2.1.0"
 
 func printVersion() {
 	fmt.Printf("xx network Permissioning Server v%s -- %s\n\n",
diff --git a/cmd/version_vars.go b/cmd/version_vars.go
index 7911f58eda25589cbfc023f274a6355615000de2..25f85082e0ebe1e8e76c74c6bea2062308e98a5d 100644
--- a/cmd/version_vars.go
+++ b/cmd/version_vars.go
@@ -1,10 +1,10 @@
 // Code generated by go generate; DO NOT EDIT.
 // This file was generated by robots at
-// 2021-03-10 10:12:36.159679 -0800 PST m=+0.033523506
+// 2021-03-15 14:57:45.9221426 -0700 PDT m=+0.184213301
 package cmd
 
-const GITVERSION = `93d59af update deps`
-const SEMVER = "2.0.0"
+const GITVERSION = `56bc43b Merge branch 'NDFPruning' into 'release'`
+const SEMVER = "2.1.0"
 const DEPENDENCIES = `module gitlab.com/elixxir/registration
 
 go 1.13
@@ -19,18 +19,26 @@ require (
 	github.com/lib/pq v1.5.2 // indirect
 	github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect
 	github.com/mitchellh/go-homedir v1.1.0
+	github.com/mitchellh/mapstructure v1.4.1 // indirect
+	github.com/nyaruka/phonenumbers v1.0.67 // indirect
 	github.com/pkg/errors v0.9.1
 	github.com/smartystreets/assertions v1.1.0 // indirect
-	github.com/spf13/cobra v1.1.1
+	github.com/spf13/cobra v1.1.3
 	github.com/spf13/jwalterweatherman v1.1.0
 	github.com/spf13/viper v1.7.1
-	gitlab.com/elixxir/client v1.2.1-0.20210222224029-4300043d7ce8
-	gitlab.com/elixxir/comms v0.0.4-0.20210309193245-64181ff10b68
+	gitlab.com/elixxir/client v1.5.1-0.20210315164614-e33b49a633c3
+	gitlab.com/elixxir/comms v0.0.4-0.20210311180506-28ae742c5e35
 	gitlab.com/elixxir/crypto v0.0.7-0.20210309193114-8a6225c667e2
 	gitlab.com/elixxir/primitives v0.0.3-0.20210309193003-ef42ebb4800b
 	gitlab.com/xx_network/comms v0.0.4-0.20210309192940-6b7fb39b4d01
 	gitlab.com/xx_network/crypto v0.0.5-0.20210309192854-cf32117afb96
 	gitlab.com/xx_network/primitives v0.0.4-0.20210309173740-eb8cd411334a
+	golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b // indirect
+	golang.org/x/net v0.0.0-20210315170653-34ac3e1c2000 // indirect
+	golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005 // indirect
+	golang.org/x/text v0.3.5 // indirect
+	google.golang.org/genproto v0.0.0-20210315173758-2651cd453018 // indirect
+	google.golang.org/grpc v1.36.0 // indirect
 )
 
 replace google.golang.org/grpc => github.com/grpc/grpc-go v1.27.1
diff --git a/go.mod b/go.mod
index 9d8d546e1db743e56bb18de2835e648a000b6def..790ac23a22abaa5ee769619a12f7c1381f1853a7 100644
--- a/go.mod
+++ b/go.mod
@@ -12,18 +12,26 @@ require (
 	github.com/lib/pq v1.5.2 // indirect
 	github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect
 	github.com/mitchellh/go-homedir v1.1.0
+	github.com/mitchellh/mapstructure v1.4.1 // indirect
+	github.com/nyaruka/phonenumbers v1.0.67 // indirect
 	github.com/pkg/errors v0.9.1
 	github.com/smartystreets/assertions v1.1.0 // indirect
-	github.com/spf13/cobra v1.1.1
+	github.com/spf13/cobra v1.1.3
 	github.com/spf13/jwalterweatherman v1.1.0
 	github.com/spf13/viper v1.7.1
 	gitlab.com/elixxir/client v1.2.1-0.20210222224029-4300043d7ce8
-	gitlab.com/elixxir/comms v0.0.4-0.20210311180506-28ae742c5e35
-	gitlab.com/elixxir/crypto v0.0.7-0.20210309193114-8a6225c667e2
+	gitlab.com/elixxir/comms v0.0.4-0.20210316224000-76529267620c
+	gitlab.com/elixxir/crypto v0.0.7-0.20210316212419-025ba777a80b
 	gitlab.com/elixxir/primitives v0.0.3-0.20210309193003-ef42ebb4800b
-	gitlab.com/xx_network/comms v0.0.4-0.20210309192940-6b7fb39b4d01
-	gitlab.com/xx_network/crypto v0.0.5-0.20210309192854-cf32117afb96
+	gitlab.com/xx_network/comms v0.0.4-0.20210316212314-d9a2a5e589c8
+	gitlab.com/xx_network/crypto v0.0.5-0.20210316212152-504c0a1e0da0
 	gitlab.com/xx_network/primitives v0.0.4-0.20210309173740-eb8cd411334a
+	golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b // indirect
+	golang.org/x/net v0.0.0-20210315170653-34ac3e1c2000 // indirect
+	golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005 // indirect
+	golang.org/x/text v0.3.5 // indirect
+	google.golang.org/genproto v0.0.0-20210315173758-2651cd453018 // indirect
+	google.golang.org/grpc v1.36.0 // indirect
 )
 
 replace google.golang.org/grpc => github.com/grpc/grpc-go v1.27.1
diff --git a/go.sum b/go.sum
index 627e6e5fb860aaaf1d8b8a80fd83b129ed6abb3f..fc0907502a173eba1604b80e120b1e8a93203212 100644
--- a/go.sum
+++ b/go.sum
@@ -193,6 +193,8 @@ github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQz
 github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
 github.com/mitchellh/mapstructure v1.4.0 h1:7ks8ZkOP5/ujthUsT07rNv+nkLXCQWKNHuwzOAesEks=
 github.com/mitchellh/mapstructure v1.4.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
+github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
+github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
 github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
@@ -200,6 +202,8 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWb
 github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
 github.com/nyaruka/phonenumbers v1.0.60 h1:nnAcNwmZflhegiImm6MkvjlRRyoaSw1ox/jGPAewWTg=
 github.com/nyaruka/phonenumbers v1.0.60/go.mod h1:sDaTZ/KPX5f8qyV9qN+hIm+4ZBARJrupC6LuhshJq1U=
+github.com/nyaruka/phonenumbers v1.0.67 h1:FnLv5VdR8NemWsP5fj6OBggw4e7Tb9fQUdd3kGd445s=
+github.com/nyaruka/phonenumbers v1.0.67/go.mod h1:sDaTZ/KPX5f8qyV9qN+hIm+4ZBARJrupC6LuhshJq1U=
 github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
 github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
 github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
@@ -249,6 +253,8 @@ github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng=
 github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
 github.com/spf13/cobra v1.1.1 h1:KfztREH0tPxJJ+geloSLaAkaPkr4ki2Er5quFV1TDo4=
 github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
+github.com/spf13/cobra v1.1.3 h1:xghbfqPkxzxP3C/f3n5DdpAbdKLj4ZE4BWQI362l53M=
+github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo=
 github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
 github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
 github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
@@ -292,14 +298,33 @@ gitlab.com/elixxir/comms v0.0.4-0.20210218234550-f2e03b19bdb2 h1:p5GunVi5sP9atTw
 gitlab.com/elixxir/comms v0.0.4-0.20210218234550-f2e03b19bdb2/go.mod h1:GCbfPWB7VF5ZeDsLBCwfy0JiquG4OK6gsRjaIS66+yg=
 gitlab.com/elixxir/comms v0.0.4-0.20210309193245-64181ff10b68 h1:HR45PZyVl+gvksIKoHPCxFndhOpBT6z3rl7vaa3BaAc=
 gitlab.com/elixxir/comms v0.0.4-0.20210309193245-64181ff10b68/go.mod h1:96cMuVVlarB+I6nuFKdq4zCagQkbhVK/MUzRk3yOymI=
+gitlab.com/elixxir/comms v0.0.4-0.20210309195247-fc17eb8560cf h1:/VDBO0GxcpqGVOFBFeuk/yKxWOKC6j7A2gLnOc1Syuc=
+gitlab.com/elixxir/comms v0.0.4-0.20210309195247-fc17eb8560cf/go.mod h1:DNJES7GZaGZnXsIDDP0nNS3lk21mhN27byQlODA+5fA=
 gitlab.com/elixxir/comms v0.0.4-0.20210311180506-28ae742c5e35 h1:t/ILeoWel5Im+zLQUX2FIroZvrfAkxOaL3DCA8enKcE=
 gitlab.com/elixxir/comms v0.0.4-0.20210311180506-28ae742c5e35/go.mod h1:96cMuVVlarB+I6nuFKdq4zCagQkbhVK/MUzRk3yOymI=
+gitlab.com/elixxir/comms v0.0.4-0.20210311204728-9b628ad57f79 h1:k8FLPJidj+/YEGAzwB/XMYOnGQfnU82q/NtyX2QgivI=
+gitlab.com/elixxir/comms v0.0.4-0.20210311204728-9b628ad57f79/go.mod h1:96cMuVVlarB+I6nuFKdq4zCagQkbhVK/MUzRk3yOymI=
+gitlab.com/elixxir/comms v0.0.4-0.20210311225503-dc1b9386d22b h1:LllyDrF0r8ogLDNVhRkvQRyhDQVwI3qP9izgmbXpKSE=
+gitlab.com/elixxir/comms v0.0.4-0.20210311225503-dc1b9386d22b/go.mod h1:96cMuVVlarB+I6nuFKdq4zCagQkbhVK/MUzRk3yOymI=
+gitlab.com/elixxir/comms v0.0.4-0.20210315172845-e08a127d601c h1:1XOPIQMQi7wRen2ABXrKE/+tCw7kSg45rrSlep9sNks=
+gitlab.com/elixxir/comms v0.0.4-0.20210315172845-e08a127d601c/go.mod h1:96cMuVVlarB+I6nuFKdq4zCagQkbhVK/MUzRk3yOymI=
+gitlab.com/elixxir/comms v0.0.4-0.20210315190821-f82a14c64d8e h1:0aVompsgMwGt22HINf99geqmFgkuHq4I85Yj3qGtZcc=
+gitlab.com/elixxir/comms v0.0.4-0.20210315190821-f82a14c64d8e/go.mod h1:96cMuVVlarB+I6nuFKdq4zCagQkbhVK/MUzRk3yOymI=
+gitlab.com/elixxir/comms v0.0.4-0.20210316192645-36e23d019d12 h1:AgcrSaZ7dDTnpyHTV1rR5pOIIyod4qiC9gR1Goj2uW4=
+gitlab.com/elixxir/comms v0.0.4-0.20210316192645-36e23d019d12/go.mod h1:96cMuVVlarB+I6nuFKdq4zCagQkbhVK/MUzRk3yOymI=
+gitlab.com/elixxir/comms v0.0.4-0.20210316212548-21282c4ffdeb h1:uHSUmO4Z1GKj+khrFu261AjQhTmj0zrPJwi6HPdqF1Y=
+gitlab.com/elixxir/comms v0.0.4-0.20210316212548-21282c4ffdeb/go.mod h1:yMy042ResQpS2cM6mdaoRYhAtQX9Yx5r3H9jS7619AU=
+gitlab.com/elixxir/comms v0.0.4-0.20210316224000-76529267620c h1:0G5UuaWzSersRRq6xcXD0LTP5KY2eKrB1G+TusLxcbk=
+gitlab.com/elixxir/comms v0.0.4-0.20210316224000-76529267620c/go.mod h1:yMy042ResQpS2cM6mdaoRYhAtQX9Yx5r3H9jS7619AU=
 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.20210216174551-f806f79610eb h1:aPcrTC0QdrPqz4NgoAt5sfXt/+EFrNUwIns0s0VCQmg=
 gitlab.com/elixxir/crypto v0.0.7-0.20210216174551-f806f79610eb/go.mod h1:CLP8kULKW9A5oZHQ1zMCx4swMhBw2IMO68z4U/FkvcU=
+gitlab.com/elixxir/crypto v0.0.7-0.20210305221450-c94429c34886/go.mod h1:ugRjIGSOJJvVKGfIDpS/k62yeG4cTIrS/YNLqmpEIDM=
 gitlab.com/elixxir/crypto v0.0.7-0.20210309193114-8a6225c667e2 h1:JMbUxcOjFpdCBUMZS5g8CWfNdPJ6pP8xsAZbnLj66jc=
 gitlab.com/elixxir/crypto v0.0.7-0.20210309193114-8a6225c667e2/go.mod h1:TMZMB24OsjF6y3LCyBMzDucbOx1cGQCCeuKV9lJA/DU=
+gitlab.com/elixxir/crypto v0.0.7-0.20210316212419-025ba777a80b h1:YpXzYPgkV22hVS2q1Ni3wLjFeQZKpY9UjPKoa/IQDlQ=
+gitlab.com/elixxir/crypto v0.0.7-0.20210316212419-025ba777a80b/go.mod h1:3SqRFi6QwGRBuQ4on0gJmPI4jpJdgnPIWPBwK/TnY5g=
 gitlab.com/elixxir/ekv v0.1.4 h1:NLVMwsFEKArWcsDHu2DbXlm9374iSgn7oIA3rVSsvjc=
 gitlab.com/elixxir/ekv v0.1.4/go.mod h1:e6WPUt97taFZe5PFLPb1Dupk7tqmDCTQu1kkstqJvw4=
 gitlab.com/elixxir/primitives v0.0.0-20200731184040-494269b53b4d/go.mod h1:OQgUZq7SjnE0b+8+iIAT2eqQF+2IFHn73tOo+aV11mg=
@@ -308,19 +333,26 @@ gitlab.com/elixxir/primitives v0.0.0-20200804182913-788f47bded40/go.mod h1:tzdFF
 gitlab.com/elixxir/primitives v0.0.1/go.mod h1:kNp47yPqja2lHSiS4DddTvFpB/4D9dB2YKnw5c+LJCE=
 gitlab.com/elixxir/primitives v0.0.3-0.20210216174458-2a23825c1eb1 h1:BfcaQtKgIbafExdHkeKIJ5XEGe9MvUiv+yg9u7jwqhY=
 gitlab.com/elixxir/primitives v0.0.3-0.20210216174458-2a23825c1eb1/go.mod h1:Wpz7WGZ/CpO6oHNmVTgTNBETTRXi40arWjom1uwu/1s=
+gitlab.com/elixxir/primitives v0.0.3-0.20210305221357-0b3f19cc3c8a/go.mod h1:7o0Gcvq6tE2by2hZyd0CDueOLUQGfbcuStnQEtLc56Y=
 gitlab.com/elixxir/primitives v0.0.3-0.20210309193003-ef42ebb4800b h1:TswWfqiZqsdPLeWsfe7VJHMlV01W792kRHGYfYwb2Lk=
 gitlab.com/elixxir/primitives v0.0.3-0.20210309193003-ef42ebb4800b/go.mod h1:/e3a4KPqmA9V22qKSZ9prfYYNzIzvLI8xh7noVV091w=
 gitlab.com/xx_network/comms v0.0.0-20200805174823-841427dd5023/go.mod h1:owEcxTRl7gsoM8c3RQ5KAm5GstxrJp5tn+6JfQ4z5Hw=
 gitlab.com/xx_network/comms v0.0.4-0.20210216174438-0790d1f1f225 h1:ZVxPD76xDLdTSGY2w/aGRMiiry7SauD8sq4c+see6aM=
 gitlab.com/xx_network/comms v0.0.4-0.20210216174438-0790d1f1f225/go.mod h1:e7dy2wznC4U4bG/U3xFGYYsnnd8zHOhoSxzFkGPQYX4=
+gitlab.com/xx_network/comms v0.0.4-0.20210305221336-a7e28febdbf6/go.mod h1:bs4GLnnL1UqInyjT4MbHamgTpiTLNtdC5e61CCnVapo=
 gitlab.com/xx_network/comms v0.0.4-0.20210309192940-6b7fb39b4d01 h1:f93iz7mTHt3r37O97vaQD8otohihLN3OnAEEbDGQdVs=
 gitlab.com/xx_network/comms v0.0.4-0.20210309192940-6b7fb39b4d01/go.mod h1:aNPRHmPssXc1JMJ83DAknT2C2iMgKL1wH3//AqQrhQc=
+gitlab.com/xx_network/comms v0.0.4-0.20210316212314-d9a2a5e589c8 h1:WED5SlL+t6nW/gwFgyJv5vqPb/1pFVd1HRC1wgL+9wY=
+gitlab.com/xx_network/comms v0.0.4-0.20210316212314-d9a2a5e589c8/go.mod h1:+4Qb4c5K0yj9Je0b8fxxw+aKZdM5y3PrpQ+ZXUwdUsI=
 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.20210216174356-e81e1ddf8fb7 h1:vyL+m7D7w+RgMPARzcKCR8UMGC2foqNU6cSb1J6Dkis=
 gitlab.com/xx_network/crypto v0.0.5-0.20210216174356-e81e1ddf8fb7/go.mod h1:8J+/VBcMlBj2sZuSDaVKI/i58awFZ5Zdb4JdEwGVrqo=
+gitlab.com/xx_network/crypto v0.0.5-0.20210305221255-f0ef174f5135/go.mod h1:Kg4EgRbpzHjxavzuKP9E1JEgYBT0ez80rFtB7Bwuw+g=
 gitlab.com/xx_network/crypto v0.0.5-0.20210309192854-cf32117afb96 h1:VZGJNhuU6YunKyK4MbNZf25UxQsmU1bH5SnbK93tI7Q=
 gitlab.com/xx_network/crypto v0.0.5-0.20210309192854-cf32117afb96/go.mod h1:TtaHpuX0lcuTTtcq+pz+lMusjyTgvSohIHFOlVwN1uU=
+gitlab.com/xx_network/crypto v0.0.5-0.20210316212152-504c0a1e0da0 h1:3ZuCXkP5sH23xUFdNhatIN2YX+EOl8G4jrewKQHsNnk=
+gitlab.com/xx_network/crypto v0.0.5-0.20210316212152-504c0a1e0da0/go.mod h1:rSBB1QLVgUQoaUvWXQTDCGNV9fGjgJahvted+7DEqSw=
 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=
@@ -328,6 +360,8 @@ gitlab.com/xx_network/primitives v0.0.4-0.20210215192713-e32335847d4f h1:0wFEYIH
 gitlab.com/xx_network/primitives v0.0.4-0.20210215192713-e32335847d4f/go.mod h1:9imZHvYwNFobxueSvVtHneZLk9wTK7HQTzxPm+zhFhE=
 gitlab.com/xx_network/primitives v0.0.4-0.20210219231511-983054dbee36 h1:41qeW7XB9Rllsi6fe37+eaQCLjTGchpvcJqwEvZxeXE=
 gitlab.com/xx_network/primitives v0.0.4-0.20210219231511-983054dbee36/go.mod h1:9imZHvYwNFobxueSvVtHneZLk9wTK7HQTzxPm+zhFhE=
+gitlab.com/xx_network/primitives v0.0.4-0.20210303180604-1ee442e6463f/go.mod h1:9imZHvYwNFobxueSvVtHneZLk9wTK7HQTzxPm+zhFhE=
+gitlab.com/xx_network/primitives v0.0.4-0.20210308175329-436b0c8753ea/go.mod h1:9imZHvYwNFobxueSvVtHneZLk9wTK7HQTzxPm+zhFhE=
 gitlab.com/xx_network/primitives v0.0.4-0.20210309173740-eb8cd411334a h1:Ume9QbJ4GoJh7v5yg/YVDjowJHx/VFeOC/A4PJZUm9g=
 gitlab.com/xx_network/primitives v0.0.4-0.20210309173740-eb8cd411334a/go.mod h1:9imZHvYwNFobxueSvVtHneZLk9wTK7HQTzxPm+zhFhE=
 gitlab.com/xx_network/ring v0.0.2 h1:TlPjlbFdhtJrwvRgIg4ScdngMTaynx/ByHBRZiXCoL0=
@@ -355,6 +389,8 @@ golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPh
 golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY=
 golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
+golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b h1:wSOdpTq0/eI46Ez/LkDwIsAKA71YP2SRKBODiRWM0as=
+golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -391,6 +427,9 @@ golang.org/x/net v0.0.0-20201029221708-28c70e62bb1d h1:dOiJ2n2cMwGLce/74I/QHMbnp
 golang.org/x/net v0.0.0-20201029221708-28c70e62bb1d/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 golang.org/x/net v0.0.0-20201224014010-6772e930b67b h1:iFwSg7t5GZmB/Q5TjiEAsdoLDrdJRC1RiF2WhuV29Qw=
 golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20210315170653-34ac3e1c2000 h1:6mqyFav9MzRNys8OnKlbKYSJxsoVvhb773Si3bu5fYE=
+golang.org/x/net v0.0.0-20210315170653-34ac3e1c2000/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -423,6 +462,8 @@ golang.org/x/sys v0.0.0-20201029080932-201ba4db2418/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210105210732-16f7687f5001 h1:/dSxr6gT0FNI1MO5WLJo8mTmItROeOKTkDn+7OwWBos=
 golang.org/x/sys v0.0.0-20210105210732-16f7687f5001/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005 h1:pDMpM2zh2MT0kHy037cKlSby2nEhD50SYqwQk76Nm40=
+golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
@@ -434,6 +475,8 @@ golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc=
 golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ=
+golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -481,6 +524,8 @@ google.golang.org/genproto v0.0.0-20201030142918-24207fddd1c3 h1:sg8vLDNIxFPHTch
 google.golang.org/genproto v0.0.0-20201030142918-24207fddd1c3/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 google.golang.org/genproto v0.0.0-20210105202744-fe13368bc0e1 h1:Zk6zlGXdtYdcY5TL+VrbTfmifvk3VvsXopCpszsHPBA=
 google.golang.org/genproto v0.0.0-20210105202744-fe13368bc0e1/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20210315173758-2651cd453018 h1:ZH67MZWSz0sEGHWoYAO1ysjL0yWEXTQ7gOxytol4KAk=
+google.golang.org/genproto v0.0.0-20210315173758-2651cd453018/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 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=
diff --git a/scheduling/nodeStateChange_test.go b/scheduling/nodeStateChange_test.go
index 4148c58a2102add5f20a638b15d4287d6636a2bb..8094d0c0d6c6c2ab86d0b56096fce1492e859f9c 100644
--- a/scheduling/nodeStateChange_test.go
+++ b/scheduling/nodeStateChange_test.go
@@ -35,7 +35,7 @@ func TestHandleNodeStateChance_Waiting(t *testing.T) {
 
 	privKey, _ := rsa.GenerateKey(rand.Reader, 2048)
 
-	testState, err := storage.NewState(privKey, 8)
+	testState, err := storage.NewState(privKey, 8, "")
 	if err != nil {
 		t.Errorf("Failed to create test state: %v", err)
 		t.FailNow()
@@ -89,7 +89,7 @@ func TestHandleNodeStateChance_Waiting_SetNodeToOnline(t *testing.T) {
 
 	privKey, _ := rsa.GenerateKey(rand.Reader, 2048)
 
-	testState, err := storage.NewState(privKey, 8)
+	testState, err := storage.NewState(privKey, 8, "")
 	if err != nil {
 		t.Errorf("Failed to create test state: %v", err)
 		t.FailNow()
@@ -149,7 +149,7 @@ func TestHandleNodeStateChance_Standby(t *testing.T) {
 	}
 
 	privKey, _ := rsa.GenerateKey(rand.Reader, 2048)
-	testState, err := storage.NewState(privKey, 8)
+	testState, err := storage.NewState(privKey, 8, "")
 	if err != nil {
 		t.Errorf("Failed to create test state: %v", err)
 		t.FailNow()
@@ -225,7 +225,7 @@ func TestHandleNodeStateChance_Standby_NoRound(t *testing.T) {
 
 	privKey, _ := rsa.GenerateKey(rand.Reader, 2048)
 
-	testState, err := storage.NewState(privKey, 8)
+	testState, err := storage.NewState(privKey, 8, "")
 	if err != nil {
 		t.Errorf("Failed to create test state: %v", err)
 		t.FailNow()
@@ -284,7 +284,7 @@ func TestHandleNodeUpdates_Completed(t *testing.T) {
 
 	privKey, _ := rsa.GenerateKey(rand.Reader, 2048)
 
-	testState, err := storage.NewState(privKey, 8)
+	testState, err := storage.NewState(privKey, 8, "")
 	if err != nil {
 		t.Errorf("Failed to create test state: %v", err)
 		t.FailNow()
@@ -358,7 +358,7 @@ func TestHandleNodeUpdates_Completed_NoRound(t *testing.T) {
 
 	privKey, _ := rsa.GenerateKey(rand.Reader, 2048)
 
-	testState, err := storage.NewState(privKey, 8)
+	testState, err := storage.NewState(privKey, 8, "")
 	if err != nil {
 		t.Errorf("Failed to create test state: %v", err)
 		t.FailNow()
@@ -411,7 +411,7 @@ func TestHandleNodeUpdates_Error(t *testing.T) {
 
 	privKey, _ := rsa.GenerateKey(rand.Reader, 2048)
 
-	testState, err := storage.NewState(privKey, 8)
+	testState, err := storage.NewState(privKey, 8, "")
 	if err != nil {
 		t.Errorf("Failed to create test state: %v", err)
 		t.FailNow()
@@ -471,7 +471,7 @@ func TestHandleNodeUpdates_BannedNode(t *testing.T) {
 
 	privKey, _ := rsa.GenerateKey(rand.Reader, 2048)
 
-	testState, err := storage.NewState(privKey, 8)
+	testState, err := storage.NewState(privKey, 8, "")
 	if err != nil {
 		t.Errorf("Failed to create test state: %v", err)
 		t.FailNow()
@@ -560,7 +560,7 @@ func TestKillRound(t *testing.T) {
 
 	privKey, _ := rsa.GenerateKey(rand.Reader, 2048)
 
-	testState, err := storage.NewState(privKey, 8)
+	testState, err := storage.NewState(privKey, 8, "")
 	if err != nil {
 		t.Errorf("Failed to create test state: %v", err)
 		t.FailNow()
@@ -614,7 +614,7 @@ func TestHandleNodeUpdates_Precomputing_RoundError(t *testing.T) {
 
 	privKey, _ := rsa.GenerateKey(rand.Reader, 2048)
 
-	testState, err := storage.NewState(privKey, 8)
+	testState, err := storage.NewState(privKey, 8, "")
 	if err != nil {
 		t.Errorf("Failed to create test state: %v", err)
 		t.FailNow()
@@ -662,7 +662,7 @@ func TestHandleNodeUpdates_Realtime(t *testing.T) {
 
 	privKey, _ := rsa.GenerateKey(rand.Reader, 2048)
 
-	testState, err := storage.NewState(privKey, 8)
+	testState, err := storage.NewState(privKey, 8, "")
 	if err != nil {
 		t.Errorf("Failed to create test state: %v", err)
 		t.FailNow()
@@ -715,7 +715,7 @@ func TestHandleNodeUpdates_Realtime_RoundError(t *testing.T) {
 
 	privKey, _ := rsa.GenerateKey(rand.Reader, 2048)
 
-	testState, err := storage.NewState(privKey, 8)
+	testState, err := storage.NewState(privKey, 8, "")
 	if err != nil {
 		t.Errorf("Failed to create test state: %v", err)
 		t.FailNow()
@@ -764,7 +764,7 @@ func TestHandleNodeUpdates_Realtime_UpdateError(t *testing.T) {
 
 	privKey, _ := rsa.GenerateKey(rand.Reader, 2048)
 
-	testState, err := storage.NewState(privKey, 8)
+	testState, err := storage.NewState(privKey, 8, "")
 	if err != nil {
 		t.Errorf("Failed to create test state: %v", err)
 		t.FailNow()
@@ -821,7 +821,7 @@ func TestHandleNodeUpdates_RoundErrored(t *testing.T) {
 
 	privKey, _ := rsa.GenerateKey(rand.Reader, 2048)
 
-	testState, err := storage.NewState(privKey, 8)
+	testState, err := storage.NewState(privKey, 8, "")
 	if err != nil {
 		t.Errorf("Failed to create test state: %v", err)
 		t.FailNow()
@@ -875,7 +875,7 @@ func TestHandleNodeUpdates_NOT_STARTED(t *testing.T) {
 
 	privKey, _ := rsa.GenerateKey(rand.Reader, 2048)
 
-	testState, err := storage.NewState(privKey, 8)
+	testState, err := storage.NewState(privKey, 8, "")
 	if err != nil {
 		t.Errorf("Failed to create test state: %v", err)
 		t.FailNow()
diff --git a/scheduling/pool_test.go b/scheduling/pool_test.go
index f2c59d12919080074dbe29ad0a36e1a5145f662a..9a445298b46729226d305d8756e4e5dc001b030d 100644
--- a/scheduling/pool_test.go
+++ b/scheduling/pool_test.go
@@ -338,7 +338,7 @@ func setupNode(t *testing.T, testState *storage.NetworkState, newId uint64) *nod
 func setupNodeMap(t *testing.T) *storage.NetworkState {
 	// Build network state
 	privKey, _ := rsa.GenerateKey(rand.Reader, 2048)
-	testState, err := storage.NewState(privKey, 8)
+	testState, err := storage.NewState(privKey, 8, "")
 	if err != nil {
 		t.Errorf("Failed to create test state: %v", err)
 		t.FailNow()
diff --git a/scheduling/schedule_test.go b/scheduling/schedule_test.go
index 35e5d3036d573056d428f1fc757d6dfcb807ac8b..6246ee0387fc7d176ab9425a0a468f137b36afaf 100644
--- a/scheduling/schedule_test.go
+++ b/scheduling/schedule_test.go
@@ -2,11 +2,11 @@ package scheduling
 
 import (
 	"encoding/json"
+	"gitlab.com/elixxir/comms/testutils"
 	"gitlab.com/elixxir/primitives/current"
 	"gitlab.com/elixxir/registration/storage"
 	"gitlab.com/elixxir/registration/storage/node"
 	"gitlab.com/elixxir/registration/testkeys"
-	"gitlab.com/xx_network/crypto/signature/rsa"
 	"gitlab.com/xx_network/primitives/id"
 	"gitlab.com/xx_network/primitives/utils"
 	"reflect"
@@ -29,19 +29,13 @@ func TestScheduler_NonRandom(t *testing.T) {
 	}
 
 	// Read in private key
-	key, err := utils.ReadFile(testkeys.GetCAKeyPath())
-	if err != nil {
-		t.Errorf("failed to read key at %+v: %+v",
-			testkeys.GetCAKeyPath(), err)
-	}
-
-	pk, err := rsa.LoadPrivateKeyFromPem(key)
+	pk, err := testutils.LoadPrivateKeyTesting(t)
 	if err != nil {
 		t.Errorf("Failed to parse permissioning server key: %+v. "+
 			"PermissioningKey is %+v", err, pk)
 	}
 	// Start registration server
-	state, err := storage.NewState(pk, 8)
+	state, err := storage.NewState(pk, 8, "")
 	if err != nil {
 		t.Errorf("Unable to create state: %+v", err)
 	}
diff --git a/scheduling/secureCreateRound_test.go b/scheduling/secureCreateRound_test.go
index 49bdcf4c4da129997152a57ae7ddab59eff9f221..40b19fd0a05daf29dd61c0450fea5d4d72c3bf60 100644
--- a/scheduling/secureCreateRound_test.go
+++ b/scheduling/secureCreateRound_test.go
@@ -28,7 +28,7 @@ func TestCreateRound(t *testing.T) {
 
 	// Build network state
 	privKey, _ := rsa.GenerateKey(rand.Reader, 2048)
-	testState, err := storage.NewState(privKey, 8)
+	testState, err := storage.NewState(privKey, 8, "")
 	if err != nil {
 		t.Errorf("Failed to create test state: %v", err)
 		t.FailNow()
@@ -76,7 +76,7 @@ func TestCreateRound_Error_NotEnoughForTeam(t *testing.T) {
 
 	// Build network state
 	privKey, _ := rsa.GenerateKey(rand.Reader, 2048)
-	testState, err := storage.NewState(privKey, 8)
+	testState, err := storage.NewState(privKey, 8, "")
 	if err != nil {
 		t.Errorf("Failed to create test state: %v", err)
 		t.FailNow()
@@ -129,7 +129,7 @@ func TestCreateRound_Error_NotEnoughForThreshold(t *testing.T) {
 
 	// Build network state
 	privKey, _ := rsa.GenerateKey(rand.Reader, 2048)
-	testState, err := storage.NewState(privKey, 8)
+	testState, err := storage.NewState(privKey, 8, "")
 	if err != nil {
 		t.Errorf("Failed to create test state: %v", err)
 		t.FailNow()
@@ -184,7 +184,7 @@ func TestCreateRound_EfficientTeam_AllRegions(t *testing.T) {
 
 	// Build network state
 	privKey, _ := rsa.GenerateKey(rand.Reader, 2048)
-	testState, err := storage.NewState(privKey, 8)
+	testState, err := storage.NewState(privKey, 8, "")
 	if err != nil {
 		t.Errorf("Failed to create test state: %v", err)
 		t.FailNow()
@@ -283,7 +283,7 @@ func TestCreateRound_EfficientTeam_RandomRegions(t *testing.T) {
 
 	// Build network state
 	privKey, _ := rsa.GenerateKey(rand.Reader, 2048)
-	testState, err := storage.NewState(privKey, 8)
+	testState, err := storage.NewState(privKey, 8, "")
 	if err != nil {
 		t.Errorf("Failed to create test state: %v", err)
 		t.FailNow()
diff --git a/scheduling/simpleCreateRound_test.go b/scheduling/simpleCreateRound_test.go
index d33801ede33c5833a3014c1a1e484c154bd6ca40..1192cde2ba60664dd4c426a61c6ccd31d7586e5a 100644
--- a/scheduling/simpleCreateRound_test.go
+++ b/scheduling/simpleCreateRound_test.go
@@ -32,7 +32,7 @@ func TestCreateRound_NonRandom(t *testing.T) {
 
 	// Build network state
 	privKey, _ := rsa.GenerateKey(rand.Reader, 2048)
-	testState, err := storage.NewState(privKey, 8)
+	testState, err := storage.NewState(privKey, 8, "")
 	if err != nil {
 		t.Errorf("Failed to create test state: %v", err)
 		t.FailNow()
@@ -105,7 +105,7 @@ func TestCreateRound_Random(t *testing.T) {
 
 	// Build network state
 	privKey, _ := rsa.GenerateKey(rand.Reader, 2048)
-	testState, err := storage.NewState(privKey, 8)
+	testState, err := storage.NewState(privKey, 8, "")
 	if err != nil {
 		t.Errorf("Failed to create test state: %v", err)
 		t.FailNow()
@@ -167,7 +167,7 @@ func TestCreateRound_BadOrdering(t *testing.T) {
 
 	// Build network state
 	privKey, _ := rsa.GenerateKey(rand.Reader, 2048)
-	testState, err := storage.NewState(privKey, 8)
+	testState, err := storage.NewState(privKey, 8, "")
 	if err != nil {
 		t.Errorf("Failed to create test state: %v", err)
 		t.FailNow()
@@ -218,7 +218,7 @@ func TestCreateRound_RandomOrdering(t *testing.T) {
 
 	// Build network state
 	privKey, _ := rsa.GenerateKey(rand.Reader, 2048)
-	testState, err := storage.NewState(privKey, 8)
+	testState, err := storage.NewState(privKey, 8, "")
 	if err != nil {
 		t.Errorf("Failed to create test state: %v", err)
 		t.FailNow()
@@ -292,7 +292,7 @@ func TestCreateSimpleRound_SemiOptimal(t *testing.T) {
 
 	// Build network state
 	privKey, _ := rsa.GenerateKey(rand.Reader, 2048)
-	testState, err := storage.NewState(privKey, 8)
+	testState, err := storage.NewState(privKey, 8, "")
 	if err != nil {
 		t.Errorf("Failed to create test state: %v", err)
 		t.FailNow()
@@ -401,7 +401,7 @@ func TestCreateSimpleRound_SemiOptimal_BadRegion(t *testing.T) {
 
 	// Build network state
 	privKey, _ := rsa.GenerateKey(rand.Reader, 2048)
-	testState, err := storage.NewState(privKey, 8)
+	testState, err := storage.NewState(privKey, 8, "")
 	if err != nil {
 		t.Errorf("Failed to create test state: %v", err)
 		t.FailNow()
diff --git a/scheduling/startRound_test.go b/scheduling/startRound_test.go
index cf61f93fae96435cde0f9e10eb4de013c7f1cb86..7c6f1d3dc3bed4daa8a30bb9210c85efaafaa923 100644
--- a/scheduling/startRound_test.go
+++ b/scheduling/startRound_test.go
@@ -37,7 +37,7 @@ func TestStartRound(t *testing.T) {
 
 	// Build network state
 	privKey, _ := rsa.GenerateKey(rand.Reader, 2048)
-	testState, err := storage.NewState(privKey, 8)
+	testState, err := storage.NewState(privKey, 8, "")
 	if err != nil {
 		t.Errorf("Failed to create test state: %v", err)
 		t.FailNow()
@@ -105,7 +105,7 @@ func TestStartRound_BadState(t *testing.T) {
 
 	// Build network state
 	privKey, _ := rsa.GenerateKey(rand.Reader, 2048)
-	testState, err := storage.NewState(privKey, 8)
+	testState, err := storage.NewState(privKey, 8, "")
 	if err != nil {
 		t.Errorf("Failed to create test state: %v", err)
 		t.FailNow()
@@ -172,7 +172,7 @@ func TestStartRound_BadNode(t *testing.T) {
 
 	// Build network state
 	privKey, _ := rsa.GenerateKey(rand.Reader, 2048)
-	testState, err := storage.NewState(privKey, 8)
+	testState, err := storage.NewState(privKey, 8, "")
 	if err != nil {
 		t.Errorf("Failed to create test state: %v", err)
 		t.FailNow()
diff --git a/scheduling/trackRounds.go b/scheduling/trackRounds.go
index dcfbb4337b5b59fcd6787223383fe5bc719dadcb..889a7a2252f29eaa1020d80b23cfda7db95d85a6 100644
--- a/scheduling/trackRounds.go
+++ b/scheduling/trackRounds.go
@@ -45,7 +45,7 @@ func trackRounds(params Params, state *storage.NetworkState, pool *waitingPool,
 
 		for _, nodeState := range nodeStates {
 
-			if nodeState.IsBanned(){
+			if nodeState.IsBanned() {
 				bStr := fmt.Sprintf("\tNode %s (AppID: %v) is banned ", nodeState.GetID(), nodeState.GetAppID())
 				banned = append(banned, bStr)
 				continue
@@ -178,9 +178,9 @@ func trackRounds(params Params, state *storage.NetworkState, pool *waitingPool,
 			jww.INFO.Printf("")
 		}
 
-		if len(banned)>0{
+		if len(banned) > 0 {
 			jww.INFO.Printf("Banned nodes:")
-			for _, s := range banned{
+			for _, s := range banned {
 				jww.INFO.Print(s)
 			}
 			jww.INFO.Printf("")
diff --git a/storage/state.go b/storage/state.go
index 7e4102eb6fe59ba1c0cecdb391ca7a9de74188e2..b8e0ea92695897ca0f7cbc52eb76230d0c64c11e 100644
--- a/storage/state.go
+++ b/storage/state.go
@@ -22,6 +22,7 @@ import (
 	"gitlab.com/xx_network/crypto/signature/rsa"
 	"gitlab.com/xx_network/primitives/id"
 	"gitlab.com/xx_network/primitives/ndf"
+	"gitlab.com/xx_network/primitives/utils"
 	"strconv"
 	"strings"
 	"sync"
@@ -49,15 +50,22 @@ type NetworkState struct {
 	disabledNodesStates *disabledNodes
 
 	// NDF state
-	partialNdf *dataStructures.Ndf
-	fullNdf    *dataStructures.Ndf
+
+	unprunedNdf *ndf.NetworkDefinition
+
+	pruneListMux sync.RWMutex
+	pruneList    map[id.ID]interface{}
+	partialNdf   *dataStructures.Ndf
+	fullNdf      *dataStructures.Ndf
 
 	// Address space size
 	addressSpaceSize uint32
+
+	ndfOutputPath string
 }
 
 // NewState returns a new NetworkState object.
-func NewState(pk *rsa.PrivateKey, addressSpaceSize uint32) (*NetworkState, error) {
+func NewState(pk *rsa.PrivateKey, addressSpaceSize uint32, ndfOutputPath string) (*NetworkState, error) {
 	fullNdf, err := dataStructures.NewNdf(&ndf.NetworkDefinition{})
 	if err != nil {
 		return nil, err
@@ -72,10 +80,13 @@ func NewState(pk *rsa.PrivateKey, addressSpaceSize uint32) (*NetworkState, error
 		roundUpdates:     dataStructures.NewUpdates(),
 		update:           make(chan node.UpdateNotification, updateBufferLength),
 		nodes:            node.NewStateMap(),
+		unprunedNdf:      &ndf.NetworkDefinition{},
 		fullNdf:          fullNdf,
 		partialNdf:       partialNdf,
 		privateKey:       pk,
 		addressSpaceSize: addressSpaceSize,
+		pruneList:        make(map[id.ID]interface{}),
+		ndfOutputPath:    ndfOutputPath,
 	}
 
 	// Obtain round & update Id from Storage
@@ -119,6 +130,36 @@ func NewState(pk *rsa.PrivateKey, addressSpaceSize uint32) (*NetworkState, error
 	return state, nil
 }
 
+func (s *NetworkState) SetPrunedNodes(ids []*id.ID) {
+	s.pruneListMux.Lock()
+	defer s.pruneListMux.Unlock()
+
+	s.pruneList = make(map[id.ID]interface{})
+
+	for _, i := range ids {
+		s.pruneList[*i] = nil
+	}
+}
+
+func (s *NetworkState) SetPrunedNode(id *id.ID) {
+	s.pruneListMux.Lock()
+	defer s.pruneListMux.Unlock()
+
+	s.pruneList[*id] = nil
+}
+
+func (s *NetworkState) IsPruned(node *id.ID) bool {
+	s.pruneListMux.RLock()
+	defer s.pruneListMux.RUnlock()
+
+	_, exists := s.pruneList[*node]
+	return exists
+}
+
+func (s *NetworkState) GetUnprunedNdf() *ndf.NetworkDefinition {
+	return s.unprunedNdf
+}
+
 // GetFullNdf returns the full NDF.
 func (s *NetworkState) GetFullNdf() *dataStructures.Ndf {
 	return s.fullNdf
@@ -159,11 +200,30 @@ func (s *NetworkState) AddRoundUpdate(r *pb.RoundInfo) error {
 
 	jww.TRACE.Printf("Round Info: %+v", roundCopy)
 
-	return s.roundUpdates.AddRound(roundCopy)
+	rnd := dataStructures.NewVerifiedRound(roundCopy, s.privateKey.GetPublic())
+	return s.roundUpdates.AddRound(rnd)
 }
 
 // UpdateNdf updates internal NDF structures with the specified new NDF.
 func (s *NetworkState) UpdateNdf(newNdf *ndf.NetworkDefinition) (err error) {
+
+	ndfMarshabled, _ := newNdf.Marshal()
+	s.unprunedNdf, _ = ndf.Unmarshal(ndfMarshabled)
+
+	s.pruneListMux.RLock()
+
+	//prune the NDF
+	for i := 0; i < len(newNdf.Nodes); i++ {
+		nid, _ := id.Unmarshal(newNdf.Nodes[i].ID)
+		if _, exists := s.pruneList[*nid]; exists {
+			newNdf.Nodes = append(newNdf.Nodes[:i], newNdf.Nodes[i+1:]...)
+			newNdf.Gateways = append(newNdf.Gateways[:i], newNdf.Gateways[i+1:]...)
+			i--
+		}
+	}
+
+	s.pruneListMux.RUnlock()
+
 	// Build NDF comms messages
 	fullNdfMsg := &pb.NDF{}
 	fullNdfMsg.Ndf, err = newNdf.Marshal()
@@ -191,7 +251,18 @@ func (s *NetworkState) UpdateNdf(newNdf *ndf.NetworkDefinition) (err error) {
 	if err != nil {
 		return err
 	}
-	return s.partialNdf.Update(partialNdfMsg)
+
+	err = s.partialNdf.Update(partialNdfMsg)
+	if err != nil {
+		return err
+	}
+
+	err = outputToJSON(newNdf, s.ndfOutputPath)
+	if err != nil {
+		jww.ERROR.Printf("unable to output NDF JSON file: %+v", err)
+	}
+
+	return nil
 }
 
 // GetPrivateKey returns the server's private key.
@@ -315,3 +386,16 @@ func (s *NetworkState) GetDisabledNodesSet() *set.Set {
 
 	return nil
 }
+
+// outputNodeTopologyToJSON encodes the NodeTopology structure to JSON and
+// outputs it to the specified file path. An error is returned if the JSON
+// marshaling fails or if the JSON file cannot be created.
+func outputToJSON(ndfData *ndf.NetworkDefinition, filePath string) error {
+	// Generate JSON from structure
+	data, err := ndfData.Marshal()
+	if err != nil {
+		return err
+	}
+	// Write JSON to file
+	return utils.WriteFile(filePath, data, utils.FilePerms, utils.DirPerms)
+}
diff --git a/storage/state_test.go b/storage/state_test.go
index 9eb840f617f8cdb1065a8b914b338c1c012ac272..e6f2683f554b9d9b857b6e62ed4f26de5ede9099 100644
--- a/storage/state_test.go
+++ b/storage/state_test.go
@@ -12,6 +12,8 @@ import (
 	"github.com/pkg/errors"
 	pb "gitlab.com/elixxir/comms/mixmessages"
 	"gitlab.com/elixxir/comms/network/dataStructures"
+	"gitlab.com/elixxir/comms/testkeys"
+	"gitlab.com/elixxir/comms/testutils"
 	"gitlab.com/elixxir/primitives/current"
 	"gitlab.com/elixxir/registration/storage/node"
 	"gitlab.com/elixxir/registration/storage/round"
@@ -56,7 +58,7 @@ func TestNewState(t *testing.T) {
 	}
 
 	// Generate new NetworkState
-	state, err := NewState(privateKey, 8)
+	state, err := NewState(privateKey, 8, "")
 	if err != nil {
 		t.Errorf("NewState() produced an unexpected error:\n%v", err)
 	}
@@ -122,7 +124,7 @@ func TestNewState_PrivateKeyError(t *testing.T) {
 	}
 
 	// Generate new NetworkState
-	state, err := NewState(privateKey, 8)
+	state, err := NewState(privateKey, 8, "")
 
 	// Test NewState() output
 	if err == nil || err.Error() != expectedErr {
@@ -185,7 +187,7 @@ func TestNetworkState_GetPartialNdf(t *testing.T) {
 // Smoke test of GetUpdates() by adding rounds and then calling GetUpdates().
 func TestNetworkState_GetUpdates(t *testing.T) {
 	// Generate new NetworkState
-	state, _, err := generateTestNetworkState()
+	state, privKey, err := generateTestNetworkState()
 	if err != nil {
 		t.Fatalf("%+v", err)
 	}
@@ -197,10 +199,16 @@ func TestNetworkState_GetUpdates(t *testing.T) {
 			ID:       0,
 			UpdateID: uint64(3 + i),
 		}
-
-		err = state.roundUpdates.AddRound(roundInfo)
+		err = testutils.SignRoundInfo(roundInfo, t)
+		if err != nil {
+			t.Errorf("Failed to sign round info: %v", err)
+			t.FailNow()
+		}
+		rnd := dataStructures.NewRound(roundInfo, privKey.GetPublic())
+		err = state.roundUpdates.AddRound(rnd)
 		if err != nil {
 			t.Errorf("AddRound() produced an unexpected error:\n%+v", err)
+			t.FailNow()
 		}
 
 		expectedRoundInfo = append(expectedRoundInfo, roundInfo)
@@ -499,18 +507,21 @@ func TestNetworkState_NodeUpdateNotification_Error(t *testing.T) {
 // key. Errors created by generating the key or NetworkState are returned.
 func generateTestNetworkState() (*NetworkState, *rsa.PrivateKey, error) {
 	// Generate new private RSA key
-	privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
+	keyPath := testkeys.GetNodeKeyPath()
+	keyData := testkeys.LoadFromPath(keyPath)
+
+	privKey, err := rsa.LoadPrivateKeyFromPem(keyData)
 	if err != nil {
-		return nil, privateKey, fmt.Errorf("Failed to generate private key:\n+%v", err)
+		return nil, privKey, errors.Errorf("Could not load public key: %v", err)
 	}
 
 	// Generate new NetworkState using the private key
-	state, err := NewState(privateKey, 8)
+	state, err := NewState(privKey, 8, "")
 	if err != nil {
-		return state, privateKey, fmt.Errorf("NewState() produced an unexpected error:\n+%v", err)
+		return state, privKey, fmt.Errorf("NewState() produced an unexpected error:\n+%v", err)
 	}
 
-	return state, privateKey, nil
+	return state, privKey, nil
 }
 
 // Tests that IncrementRoundID() increments the ID correctly.