diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index ace4829d5840be78e594ad6aaf9f42ec74174d2f..e34283de662aeddaa855cfaf713d3d938ed17670 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -6,12 +6,6 @@ cache:
   paths:
     - vendor/
 
-variables:
-  REPO_DIR: gitlab.com/elixxir
-  REPO_NAME: registration
-  DOCKER_IMAGE: elixxirlabs/cuda-go:go1.16-cuda11.1
-  MIN_CODE_COVERAGE: "0.0"
-
 before_script:
   - go version || echo "Go executable not found."
   - echo $CI_BUILD_REF
diff --git a/README.md b/README.md
index 3e8b9d1c0bf7664cfbaa897c11adc4eb06bc5b5a..abbbf72c068d05c587473907f891b5e09e4d8fe3 100644
--- a/README.md
+++ b/README.md
@@ -16,8 +16,12 @@ logLevel: 1
 # Path to log file
 logPath: "registration.log"
 
-# Path to the node topology permissioning info
-ndfOutputPath: "ndf.json"
+# Path to the node topology permissioning info. Contains the full NDF.
+fullNdfOutputPath: "ndf.json"
+
+# Path to the signed partial ndf uploaded to the Internet for clients
+# to pull from.
+signedPartialNDFOutputPath: "signedPartial.txt"
 
 # Path to JSON containing list of IDs exempt from rate limiting
 whitelistedIdsPath: "whitelistedIds.json"
@@ -171,9 +175,8 @@ Note: All times in MS
   "BatchSize": 64,
   "MinimumDelay": 60,
   "RealtimeDelay": 3000,
-  "Threshold": 10,
+  "Threshold": 0.3,
   "NodeCleanUpInterval": 180000,  
-  "Secure": true,
   "PrecomputationTimeout": 30000,
   "RealtimeTimeout": 15000,
   "ResourceQueueTimeout": 180000,
diff --git a/cmd/addressSpaceUpdate_test.go b/cmd/addressSpaceUpdate_test.go
index 2eb8c6ca248d5d51dec0052972137349d9816109..5274eb2a094e25415f9565306f7a2353f18e845c 100644
--- a/cmd/addressSpaceUpdate_test.go
+++ b/cmd/addressSpaceUpdate_test.go
@@ -32,7 +32,7 @@ func TestRegistrationImpl_updateAddressSpace(t *testing.T) {
 	}
 
 	// Create a new state
-	state, err := storage.NewState(getTestKey(), 8, "", region.GetCountryBins(), nil, nil)
+	state, err := storage.NewState(getTestKey(), 8, "", "", region.GetCountryBins())
 	if err != nil {
 		t.Errorf("Unable to create state: %+v", err)
 	}
@@ -94,7 +94,7 @@ func TestRegistrationImpl_updateAddressSpace_NoUpdates(t *testing.T) {
 	}
 
 	// Create a new state
-	state, err := storage.NewState(getTestKey(), 8, "", region.GetCountryBins(), nil, nil)
+	state, err := storage.NewState(getTestKey(), 8, "", "", region.GetCountryBins())
 	if err != nil {
 		t.Errorf("Unable to create state: %+v", err)
 	}
diff --git a/cmd/bannedNodeTracker_test.go b/cmd/bannedNodeTracker_test.go
index 9bbbb5ef1f194768959e0b79b87c0ea453e2b092..507358f7273ac8c5904e19d25a82e66b25b0822c 100644
--- a/cmd/bannedNodeTracker_test.go
+++ b/cmd/bannedNodeTracker_test.go
@@ -29,7 +29,7 @@ func TestBannedNodeTracker(t *testing.T) {
 	// Build network state
 	privKey, _ := rsa.GenerateKey(rand.Reader, 2048)
 
-	testState, err := storage.NewState(privKey, 8, "", region.GetCountryBins(), nil, nil)
+	testState, err := storage.NewState(privKey, 8, "", "", region.GetCountryBins())
 	impl := &RegistrationImpl{
 		State:   testState,
 		NDFLock: sync.Mutex{},
diff --git a/cmd/impl.go b/cmd/impl.go
index f2b52ff48b0ef6b7594ff71ffc13d58ecc9e6536..5086d88386a3b4236f01860d1b0fe4dbab64c092 100644
--- a/cmd/impl.go
+++ b/cmd/impl.go
@@ -34,15 +34,16 @@ import (
 
 // The main registration instance object
 type RegistrationImpl struct {
-	Comms             *registration.Comms
-	params            *Params
-	schedulingParams  *scheduling.SafeParams
-	State             *storage.NetworkState
-	Stopped           *uint32
-	permissioningCert *x509.Certificate
-	ndfOutputPath     string
-	NdfReady          *uint32
-	certFromFile      string
+	Comms                      *registration.Comms
+	params                     *Params
+	schedulingParams           *scheduling.SafeParams
+	State                      *storage.NetworkState
+	Stopped                    *uint32
+	permissioningCert          *x509.Certificate
+	fullNdfOutputPath          string
+	signedPartialNdfOutputPath string
+	NdfReady                   *uint32
+	certFromFile               string
 
 	// registration status trackers
 	numRegistered int
@@ -125,7 +126,7 @@ func StartRegistration(params Params) (*RegistrationImpl, error) {
 	// Build default parameters
 	regImpl := &RegistrationImpl{
 		params:               &params,
-		ndfOutputPath:        params.NdfOutputPath,
+		fullNdfOutputPath:    params.FullNdfOutputPath,
 		NdfReady:             &ndfReady,
 		Stopped:              &roundCreationStopped,
 		numRegistered:        0,
@@ -180,7 +181,6 @@ func StartRegistration(params Params) (*RegistrationImpl, error) {
 
 	whitelistedIpAddresses := make([]string, 0)
 	if regImpl.params.WhitelistedIpAddressPath != "" {
-
 		// Load whitelisted IP addresses file
 		whitelistFile, err := utils.ReadFile(regImpl.params.WhitelistedIpAddressPath)
 		if err != nil {
@@ -191,6 +191,8 @@ func StartRegistration(params Params) (*RegistrationImpl, error) {
 			err = json.Unmarshal(whitelistFile, &whitelistedIpAddresses)
 			if err != nil {
 				jww.WARN.Printf("Could not unmarshal whitelisted IP addresses: %v", err)
+			} else {
+				jww.INFO.Printf("Added whitelisted IPs: %+v", whitelistedIpAddresses)
 			}
 		}
 
@@ -198,7 +200,7 @@ func StartRegistration(params Params) (*RegistrationImpl, error) {
 
 	// Initialize the state tracking object
 	regImpl.State, err = storage.NewState(rsaPrivateKey, uint32(newestAddressSpace.Size),
-		params.NdfOutputPath, geoBins, whitelistedIds, whitelistedIpAddresses)
+		params.FullNdfOutputPath, params.SignedPartialNdfOutputPath, geoBins)
 	if err != nil {
 		return nil, err
 	}
diff --git a/cmd/nodeMetricTracker.go b/cmd/nodeMetricTracker.go
index 79c7d760e2fa69df3711952f82f5d6c1a48c3af8..2a37e2e5a6b65b67ae909250a9e08e1c141dd95e 100644
--- a/cmd/nodeMetricTracker.go
+++ b/cmd/nodeMetricTracker.go
@@ -72,8 +72,9 @@ func TrackNodeMetrics(impl *RegistrationImpl, quitChan chan struct{}, nodeMetric
 				err = json.Unmarshal(whitelistedIpAddressesFile, &whitelistedIpAddresses)
 				if err != nil {
 					jww.ERROR.Printf("Could not unmarshal whitelisted IP addresses: %v", err)
+				} else {
+					jww.INFO.Printf("Added whitelisted IPs: %+v", whitelistedIpAddresses)
 				}
-
 			}
 
 			// Keep track of stale/pruned nodes
diff --git a/cmd/nodeMetricTracker_test.go b/cmd/nodeMetricTracker_test.go
index 22bf2a7f2aebb4da90ec44baa63803d1b765b135..537ed091aa8898e42731ba2079fe1ea88683612a 100644
--- a/cmd/nodeMetricTracker_test.go
+++ b/cmd/nodeMetricTracker_test.go
@@ -36,7 +36,7 @@ func TestTrackNodeMetrics(t *testing.T) {
 	testParams.pruneRetentionLimit = 24 * time.Hour
 	testParams.disableNDFPruning = false
 	// Create a new state
-	state, err := storage.NewState(getTestKey(), 8, "", region.GetCountryBins(), nil, nil)
+	state, err := storage.NewState(getTestKey(), 8, "", "", region.GetCountryBins())
 	if err != nil {
 		t.Errorf("Unable to create state: %+v", err)
 	}
diff --git a/cmd/params.go b/cmd/params.go
index 2b99bb2278b3414b052eef753c141d0c54bf195b..fba9b62ce9f787fa7d9a291332f0b430b0ec857c 100644
--- a/cmd/params.go
+++ b/cmd/params.go
@@ -20,14 +20,15 @@ import (
 
 // Params object for reading in configuration data
 type Params struct {
-	Address                  string
-	CertPath                 string
-	KeyPath                  string
-	NdfOutputPath            string
-	NsCertPath               string
-	NsAddress                string
-	WhitelistedIdsPath       string
-	WhitelistedIpAddressPath string
+	Address                    string
+	CertPath                   string
+	KeyPath                    string
+	FullNdfOutputPath          string
+	SignedPartialNdfOutputPath string
+	NsCertPath                 string
+	NsAddress                  string
+	WhitelistedIdsPath         string
+	WhitelistedIpAddressPath   string
 
 	cmix                  ndf.Group
 	e2e                   ndf.Group
diff --git a/cmd/permissioning_test.go b/cmd/permissioning_test.go
index fa1374f4e4f216249924ca520455b11e49139012..23e4b6a9ff62a45c6559eadede8d175b4eeb9aaa 100644
--- a/cmd/permissioning_test.go
+++ b/cmd/permissioning_test.go
@@ -78,7 +78,7 @@ func TestLoadAllRegisteredNodes(t *testing.T) {
 	testParams := Params{
 		CertPath:           testkeys.GetCACertPath(),
 		KeyPath:            testkeys.GetCAKeyPath(),
-		NdfOutputPath:      testkeys.GetNDFPath(),
+		FullNdfOutputPath:  testkeys.GetNDFPath(),
 		udbCertPath:        testkeys.GetUdbCertPath(),
 		NsCertPath:         testkeys.GetUdbCertPath(),
 		WhitelistedIdsPath: testkeys.GetPreApprovedPath(),
diff --git a/cmd/poll.go b/cmd/poll.go
index deb9fe31e6322541801f9cc8b18a60197d727ffe..4d205f7a5685a50e87840c122b966b77a3d1f5f9 100644
--- a/cmd/poll.go
+++ b/cmd/poll.go
@@ -421,7 +421,8 @@ func (m *RegistrationImpl) checkConnectivity(n *node.State, nodeIpAddr string,
 				gwID.SetType(id.Gateway)
 				params := connect.GetDefaultHostParams()
 				params.AuthEnabled = false
-				gwHost, err := connect.NewHost(gwID, n.GetGatewayAddress(), nil, params)
+				nDb, err := storage.PermissioningDb.GetNodeById(n.GetID())
+				gwHost, err := connect.NewHost(gwID, n.GetGatewayAddress(), []byte(nDb.GatewayCertificate), params)
 
 				//ping the gateway
 				_, isOnline = gwHost.IsOnline()
diff --git a/cmd/poll_test.go b/cmd/poll_test.go
index 52bd1e256f5f27402e7246977acaf545e95b5b76..32d306942db3c1b6e769462978e567442bc51eaf 100644
--- a/cmd/poll_test.go
+++ b/cmd/poll_test.go
@@ -994,7 +994,7 @@ func TestVerifyError(t *testing.T) {
 	// Start registration server
 	ndfReady := uint32(0)
 
-	state, err := storage.NewState(pk, 8, "", region.GetCountryBins(), nil, nil)
+	state, err := storage.NewState(pk, 8, "", "", region.GetCountryBins())
 	if err != nil {
 		t.Errorf("Unable to create state: %+v", err)
 	}
diff --git a/cmd/registration_test.go b/cmd/registration_test.go
index 3fd4c6c3bd3ea4a3a012825c07d854830853dd73..afad839df7cb80de7b93e5bfbe5edb7c80261e16 100644
--- a/cmd/registration_test.go
+++ b/cmd/registration_test.go
@@ -14,6 +14,7 @@ import (
 	"gitlab.com/elixxir/registration/storage"
 	"gitlab.com/elixxir/registration/storage/node"
 	"gitlab.com/elixxir/registration/testkeys"
+	"gitlab.com/xx_network/comms/connect"
 	"gitlab.com/xx_network/primitives/id"
 	"gitlab.com/xx_network/primitives/utils"
 	"os"
@@ -35,6 +36,9 @@ var dblck sync.Mutex
 
 func TestMain(m *testing.M) {
 	jww.SetStdoutThreshold(jww.LevelDebug)
+
+	connect.TestingOnlyDisableTLS = true
+
 	var err error
 	nodeCert, err = utils.ReadFile(testkeys.GetNodeCertPath())
 	if err != nil {
@@ -65,7 +69,7 @@ func TestMain(m *testing.M) {
 		Address:             permAddr,
 		CertPath:            testkeys.GetCACertPath(),
 		KeyPath:             testkeys.GetCAKeyPath(),
-		NdfOutputPath:       testkeys.GetNDFPath(),
+		FullNdfOutputPath:   testkeys.GetNDFPath(),
 		publicAddress:       permAddr,
 		udbCertPath:         testkeys.GetUdbCertPath(),
 		NsCertPath:          testkeys.GetUdbCertPath(),
diff --git a/cmd/root.go b/cmd/root.go
index c57fabe8ca262917987a93d886b9c8965b05ea51..006cf02b248beddd99e1f03551db2cb3ca54de4f 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -86,7 +86,8 @@ var rootCmd = &cobra.Command{
 		keyPath := viper.GetString("keyPath")
 
 		localAddress := fmt.Sprintf("0.0.0.0:%d", viper.GetInt("port"))
-		ndfOutputPath := viper.GetString("ndfOutputPath")
+		fullNdfOutputPath := viper.GetString("fullNdfOutputPath")
+		signedPartialNdfOutputPath := viper.GetString("signedPartialNDFOutputPath")
 		whitelistedIdsPath := viper.GetString("whitelistedIdsPath")
 		whitelistedIpAddressesPath := viper.GetString("whitelistedIpAddressesPath")
 
@@ -207,34 +208,35 @@ var rootCmd = &cobra.Command{
 
 		// Populate params
 		RegParams = Params{
-			Address:                   localAddress,
-			CertPath:                  certPath,
-			KeyPath:                   keyPath,
-			NdfOutputPath:             ndfOutputPath,
-			WhitelistedIdsPath:        whitelistedIdsPath,
-			WhitelistedIpAddressPath:  whitelistedIpAddressesPath,
-			NsCertPath:                nsCertPath,
-			NsAddress:                 nsAddress,
-			cmix:                      *cmix,
-			e2e:                       *e2e,
-			publicAddress:             publicAddress,
-			clientRegistrationAddress: clientRegistration,
-			schedulingKillTimeout:     schedulingKillTimeout,
-			closeTimeout:              closeTimeout,
-			minimumNodes:              viper.GetUint32("minimumNodes"),
-			udbId:                     udbId,
-			udbDhPubKey:               udbDhPubKey,
-			udbCertPath:               udbCertPath,
-			udbAddress:                udbAddress,
-			minGatewayVersion:         minGatewayVersion,
-			minServerVersion:          minServerVersion,
-			minClientVersion:          minClientVersion,
-			addressSpaceSize:          uint8(viper.GetUint("addressSpace")),
-			allowLocalIPs:             viper.GetBool("allowLocalIPs"),
-			disableGeoBinning:         viper.GetBool("disableGeoBinning"),
-			blockchainGeoBinning:      viper.GetBool("blockchainGeoBinning"),
-			onlyScheduleActive:        viper.GetBool("onlyScheduleActive"),
-			enableBlockchain:          viper.GetBool("enableBlockchain"),
+			Address:                    localAddress,
+			CertPath:                   certPath,
+			KeyPath:                    keyPath,
+			FullNdfOutputPath:          fullNdfOutputPath,
+			SignedPartialNdfOutputPath: signedPartialNdfOutputPath,
+			WhitelistedIdsPath:         whitelistedIdsPath,
+			WhitelistedIpAddressPath:   whitelistedIpAddressesPath,
+			NsCertPath:                 nsCertPath,
+			NsAddress:                  nsAddress,
+			cmix:                       *cmix,
+			e2e:                        *e2e,
+			publicAddress:              publicAddress,
+			clientRegistrationAddress:  clientRegistration,
+			schedulingKillTimeout:      schedulingKillTimeout,
+			closeTimeout:               closeTimeout,
+			minimumNodes:               viper.GetUint32("minimumNodes"),
+			udbId:                      udbId,
+			udbDhPubKey:                udbDhPubKey,
+			udbCertPath:                udbCertPath,
+			udbAddress:                 udbAddress,
+			minGatewayVersion:          minGatewayVersion,
+			minServerVersion:           minServerVersion,
+			minClientVersion:           minClientVersion,
+			addressSpaceSize:           uint8(viper.GetUint("addressSpace")),
+			allowLocalIPs:              viper.GetBool("allowLocalIPs"),
+			disableGeoBinning:          viper.GetBool("disableGeoBinning"),
+			blockchainGeoBinning:       viper.GetBool("blockchainGeoBinning"),
+			onlyScheduleActive:         viper.GetBool("onlyScheduleActive"),
+			enableBlockchain:           viper.GetBool("enableBlockchain"),
 
 			disableNDFPruning:     viper.GetBool("disableNDFPruning"),
 			geoIPDBFile:           viper.GetString("geoIPDBFile"),
@@ -407,10 +409,11 @@ var rootCmd = &cobra.Command{
 			}
 			stopOnce.Do(stopRounds)
 			stopForKillOnce.Do(stopForKill)
+			impl.Comms.Shutdown()
 		}
 		ReceiveUSR2Signal(stopEverything)
 
-		// Block forever on Signal Handler for safe program exit
+		// Open Signal Handler for safe program exit
 		stopCh := ReceiveExitSignal()
 
 		// Block forever to prevent the program ending
diff --git a/cmd/version.go b/cmd/version.go
index c0500d744414f273c20da9dec06d8f41dcf6fbbf..790e8d85d00d7a357c5de22917058250daf5f7f0 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 = "3.3.0"
+const currentVersion = "3.4.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 f68424a2fc69b84f713b2ac7df67b9707c15c7ee..70ce6602d6bfe9ac13e37556dbcd9a180f8d52db 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-11-11 13:01:22.955006 -0600 CST m=+0.046409656
+// 2022-01-04 12:42:27.697455 -0600 CST m=+0.023805863
 package cmd
 
-const GITVERSION = `19f2713 Merge branch 'hotfix/CompletedTS' into 'release'`
-const SEMVER = "3.3.0"
+const GITVERSION = `9b51133 Merge branch 'dev' into 'release'`
+const SEMVER = "3.4.0"
 const DEPENDENCIES = `module gitlab.com/elixxir/registration
 
 go 1.13
@@ -26,12 +26,13 @@ require (
 	github.com/spf13/cobra v1.1.3
 	github.com/spf13/jwalterweatherman v1.1.0
 	github.com/spf13/viper v1.7.1
-	gitlab.com/elixxir/comms v0.0.4-0.20211101174956-590ba1b47887
-	gitlab.com/elixxir/crypto v0.0.7-0.20211022013957-3a7899285c4c
-	gitlab.com/elixxir/primitives v0.0.3-0.20211102233208-a716d5c670b6
-	gitlab.com/xx_network/comms v0.0.4-0.20211014163953-e774276b83ae
-	gitlab.com/xx_network/crypto v0.0.5-0.20211014163843-57b345890686
-	gitlab.com/xx_network/primitives v0.0.4-0.20211014163031-53405cf191fb
+	gitlab.com/elixxir/comms v0.0.4-0.20220104174855-044783c5c1e6
+	gitlab.com/elixxir/crypto v0.0.7-0.20220104174238-dbd761b30553
+	gitlab.com/elixxir/primitives v0.0.3-0.20220104173924-275cb9d7834f
+	gitlab.com/xx_network/comms v0.0.4-0.20211227194445-c099754b3cda
+	gitlab.com/xx_network/crypto v0.0.5-0.20211227194420-f311e8920467
+	gitlab.com/xx_network/primitives v0.0.4-0.20211222205802-03e9d7d835b0
 	google.golang.org/genproto v0.0.0-20210315173758-2651cd453018 // indirect
+	google.golang.org/protobuf v1.27.1
 )
 `
diff --git a/go.mod b/go.mod
index 79181a74fa3aae16e62798afdaec6da3d6419b56..ba9645e4cccbaedb49f5ea94342df187269d8041 100644
--- a/go.mod
+++ b/go.mod
@@ -19,11 +19,12 @@ require (
 	github.com/spf13/cobra v1.1.3
 	github.com/spf13/jwalterweatherman v1.1.0
 	github.com/spf13/viper v1.7.1
-	gitlab.com/elixxir/comms v0.0.4-0.20211101174956-590ba1b47887
-	gitlab.com/elixxir/crypto v0.0.7-0.20211022013957-3a7899285c4c
-	gitlab.com/elixxir/primitives v0.0.3-0.20211102233208-a716d5c670b6
-	gitlab.com/xx_network/comms v0.0.4-0.20211014163953-e774276b83ae
-	gitlab.com/xx_network/crypto v0.0.5-0.20211014163843-57b345890686
-	gitlab.com/xx_network/primitives v0.0.4-0.20211014163031-53405cf191fb
+	gitlab.com/elixxir/comms v0.0.4-0.20220104174855-044783c5c1e6
+	gitlab.com/elixxir/crypto v0.0.7-0.20220104174238-dbd761b30553
+	gitlab.com/elixxir/primitives v0.0.3-0.20220104173924-275cb9d7834f
+	gitlab.com/xx_network/comms v0.0.4-0.20211227194445-c099754b3cda
+	gitlab.com/xx_network/crypto v0.0.5-0.20211227194420-f311e8920467
+	gitlab.com/xx_network/primitives v0.0.4-0.20211222205802-03e9d7d835b0
 	google.golang.org/genproto v0.0.0-20210315173758-2651cd453018 // indirect
+	google.golang.org/protobuf v1.27.1
 )
diff --git a/go.sum b/go.sum
index b73c4e9a38ad62a7d455711810c6811f3e8edb28..48aa18e6184f0bf3fe2b8502a08c5a8d67642687 100644
--- a/go.sum
+++ b/go.sum
@@ -269,31 +269,32 @@ github.com/zeebo/blake3 v0.0.4/go.mod h1:YOZo8A49yNqM0X/Y+JmDUZshJWLt1laHsNSn5ny
 github.com/zeebo/blake3 v0.1.1/go.mod h1:G9pM4qQwjRzF1/v7+vabMj/c5mWpGZ2Wzo3Eb4z0pb4=
 github.com/zeebo/pcg v0.0.0-20181207190024-3cdc6b625a05/go.mod h1:Gr+78ptB0MwXxm//LBaEvBiaXY7hXJ6KGe2V32X2F6E=
 github.com/zeebo/pcg v1.0.0/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
-gitlab.com/elixxir/comms v0.0.4-0.20211101174956-590ba1b47887 h1:SOQaoEvc6RqImz86jSjsj7wIW3ZhgxXc38GzvRkKdOw=
-gitlab.com/elixxir/comms v0.0.4-0.20211101174956-590ba1b47887/go.mod h1:rQpTeFVSn08ocbQeEw5AbMhGWXHfXmQ0y1/ZprAIVVU=
+gitlab.com/elixxir/comms v0.0.4-0.20220104174855-044783c5c1e6 h1:MaiS3Mdhjwc3aNKonKJf9FSgnShwXQ6FnAI1QENDi7o=
+gitlab.com/elixxir/comms v0.0.4-0.20220104174855-044783c5c1e6/go.mod h1:r4xZgd+DPDujHTJZ6Y8+JXM2Ae2rZHa6rFggOPxs/Gc=
 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.20211022013957-3a7899285c4c h1:HIr2HBhZqSAKdPRBdEY0/qravISL619O2yuTY/DQTdo=
-gitlab.com/elixxir/crypto v0.0.7-0.20211022013957-3a7899285c4c/go.mod h1:teuTEXyqsqo4N/J1sshcTg9xYOt+wNTurop7pkZOiCg=
+gitlab.com/elixxir/crypto v0.0.7-0.20211230230452-bca020488964/go.mod h1:fexaw14nwGMlT6vL9eIJ1ixgiomyAp88hSHl0Yx0/xU=
+gitlab.com/elixxir/crypto v0.0.7-0.20220104174238-dbd761b30553 h1:BPwepGZspxgiY4QMUwVFMgVIEs8ziuMcT/uXmPQQ9Gs=
+gitlab.com/elixxir/crypto v0.0.7-0.20220104174238-dbd761b30553/go.mod h1:fexaw14nwGMlT6vL9eIJ1ixgiomyAp88hSHl0Yx0/xU=
 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.20211014164029-06022665b576/go.mod h1:zZy8AlOISFm5IG4G4sylypnz7xNBfZ5mpXiibqJT8+8=
-gitlab.com/elixxir/primitives v0.0.3-0.20211102233208-a716d5c670b6 h1:ymWyFBFLcRQiuSId54dq8PVeiV4W7a9737kV46Thjlk=
-gitlab.com/elixxir/primitives v0.0.3-0.20211102233208-a716d5c670b6/go.mod h1:zZy8AlOISFm5IG4G4sylypnz7xNBfZ5mpXiibqJT8+8=
+gitlab.com/elixxir/primitives v0.0.3-0.20211230224340-fc0905d8776e/go.mod h1:zA+1Lp9fGPo6pl1QxtMoNPLeZJ1O5m4kcH7HNxePQnQ=
+gitlab.com/elixxir/primitives v0.0.3-0.20220104173924-275cb9d7834f h1:zg3oYk+a6Wmq9tGRwka3GjJR1XRZIVCsOMpBGxtF2yc=
+gitlab.com/elixxir/primitives v0.0.3-0.20220104173924-275cb9d7834f/go.mod h1:zA+1Lp9fGPo6pl1QxtMoNPLeZJ1O5m4kcH7HNxePQnQ=
 gitlab.com/xx_network/comms v0.0.0-20200805174823-841427dd5023/go.mod h1:owEcxTRl7gsoM8c3RQ5KAm5GstxrJp5tn+6JfQ4z5Hw=
-gitlab.com/xx_network/comms v0.0.4-0.20211014163953-e774276b83ae h1:jmZWmSm8eH40SX5B5uOw2XaYoHYqVn8daTfa6B80AOs=
-gitlab.com/xx_network/comms v0.0.4-0.20211014163953-e774276b83ae/go.mod h1:wR9Vx0KZLrIs0g2Efcp0UwFPStjcDRWkg/DJLVQI2vw=
+gitlab.com/xx_network/comms v0.0.4-0.20211227194445-c099754b3cda h1:oWl8TuAgdx/6J9lwdH8wYGSb4W6D5TG1AZkzbA8EzbE=
+gitlab.com/xx_network/comms v0.0.4-0.20211227194445-c099754b3cda/go.mod h1:5arueRMa2MNa6dALnfJwyZOhqhV53Gqc+tlHRz+Ycjw=
 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.20211014163843-57b345890686 h1:mEjKISxi9LrguYgz6evroFwsfxH78/hYmr32yws+WV0=
-gitlab.com/xx_network/crypto v0.0.5-0.20211014163843-57b345890686/go.mod h1:GeUUB5eMlu7G1u7LXpClfOyUYsSDxAhiZBf+RZeGftc=
+gitlab.com/xx_network/crypto v0.0.5-0.20211227194420-f311e8920467 h1:LkZtWBYrM2e7QRf5aaBAcy7s7CpYGhAqgXRFVCdBRy4=
+gitlab.com/xx_network/crypto v0.0.5-0.20211227194420-f311e8920467/go.mod h1:c+x0w3Xk6QZe5w2Redn5SiaBpqAhgNSfwBr0JGa/yyo=
 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.20211014163031-53405cf191fb h1:0K9dyxFpDYzH9jYLwzg3+bRj9a0uJjwjQkMeIdTxduQ=
-gitlab.com/xx_network/primitives v0.0.4-0.20211014163031-53405cf191fb/go.mod h1:9imZHvYwNFobxueSvVtHneZLk9wTK7HQTzxPm+zhFhE=
+gitlab.com/xx_network/primitives v0.0.4-0.20211222205802-03e9d7d835b0 h1:IHHb59DJEKk02HgfxddqK2ilvkRMAUdPIBFn8rmjjIg=
+gitlab.com/xx_network/primitives v0.0.4-0.20211222205802-03e9d7d835b0/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/scheduling/nodeStateChange_test.go b/scheduling/nodeStateChange_test.go
index 0a29e59b05732ddaf4b76f41a7eac3942e6844d6..bc77f06889af3eaa6da18a017323b56426d4a073 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, "", region.GetCountryBins(), nil, nil)
+	testState, err := storage.NewState(privKey, 8, "", "", region.GetCountryBins())
 	if err != nil {
 		t.Errorf("Failed to create test state: %v", err)
 		t.FailNow()
@@ -91,7 +91,7 @@ func TestHandleNodeStateChance_Waiting_SetNodeToOnline(t *testing.T) {
 
 	privKey, _ := rsa.GenerateKey(rand.Reader, 2048)
 
-	testState, err := storage.NewState(privKey, 8, "", region.GetCountryBins(), nil, nil)
+	testState, err := storage.NewState(privKey, 8, "", "", region.GetCountryBins())
 	if err != nil {
 		t.Errorf("Failed to create test state: %v", err)
 		t.FailNow()
@@ -154,7 +154,7 @@ func TestHandleNodeStateChance_Standby(t *testing.T) {
 
 	privKey, _ := rsa.GenerateKey(rand.Reader, 2048)
 
-	testState, err := storage.NewState(privKey, 8, "", region.GetCountryBins(), nil, nil)
+	testState, err := storage.NewState(privKey, 8, "", "", region.GetCountryBins())
 	if err != nil {
 		t.Errorf("Failed to create test state: %v", err)
 		t.FailNow()
@@ -234,7 +234,7 @@ func TestHandleNodeStateChance_Standby_NoRound(t *testing.T) {
 
 	privKey, _ := rsa.GenerateKey(rand.Reader, 2048)
 
-	testState, err := storage.NewState(privKey, 8, "", region.GetCountryBins(), nil, nil)
+	testState, err := storage.NewState(privKey, 8, "", "", region.GetCountryBins())
 	if err != nil {
 		t.Errorf("Failed to create test state: %v", err)
 		t.FailNow()
@@ -295,7 +295,7 @@ func TestHandleNodeUpdates_Completed(t *testing.T) {
 
 	privKey, _ := rsa.GenerateKey(rand.Reader, 2048)
 
-	testState, err := storage.NewState(privKey, 8, "", region.GetCountryBins(), nil, nil)
+	testState, err := storage.NewState(privKey, 8, "", "", region.GetCountryBins())
 	if err != nil {
 		t.Errorf("Failed to create test state: %v", err)
 		t.FailNow()
@@ -374,7 +374,7 @@ func TestHandleNodeUpdates_Completed_NoRound(t *testing.T) {
 
 	privKey, _ := rsa.GenerateKey(rand.Reader, 2048)
 
-	testState, err := storage.NewState(privKey, 8, "", region.GetCountryBins(), nil, nil)
+	testState, err := storage.NewState(privKey, 8, "", "", region.GetCountryBins())
 	if err != nil {
 		t.Errorf("Failed to create test state: %v", err)
 		t.FailNow()
@@ -429,7 +429,7 @@ func TestHandleNodeUpdates_Error(t *testing.T) {
 
 	privKey, _ := rsa.GenerateKey(rand.Reader, 2048)
 
-	testState, err := storage.NewState(privKey, 8, "", region.GetCountryBins(), nil, nil)
+	testState, err := storage.NewState(privKey, 8, "", "", region.GetCountryBins())
 	if err != nil {
 		t.Errorf("Failed to create test state: %v", err)
 		t.FailNow()
@@ -491,7 +491,7 @@ func TestHandleNodeUpdates_BannedNode(t *testing.T) {
 
 	privKey, _ := rsa.GenerateKey(rand.Reader, 2048)
 
-	testState, err := storage.NewState(privKey, 8, "", region.GetCountryBins(), nil, nil)
+	testState, err := storage.NewState(privKey, 8, "", "", region.GetCountryBins())
 	if err != nil {
 		t.Errorf("Failed to create test state: %v", err)
 		t.FailNow()
@@ -585,7 +585,7 @@ func TestKillRound(t *testing.T) {
 
 	privKey, _ := rsa.GenerateKey(rand.Reader, 2048)
 
-	testState, err := storage.NewState(privKey, 8, "", region.GetCountryBins(), nil, nil)
+	testState, err := storage.NewState(privKey, 8, "", "", region.GetCountryBins())
 	if err != nil {
 		t.Errorf("Failed to create test state: %v", err)
 		t.FailNow()
@@ -638,7 +638,7 @@ func TestHandleNodeUpdates_Precomputing_RoundError(t *testing.T) {
 
 	privKey, _ := rsa.GenerateKey(rand.Reader, 2048)
 
-	testState, err := storage.NewState(privKey, 8, "", region.GetCountryBins(), nil, nil)
+	testState, err := storage.NewState(privKey, 8, "", "", region.GetCountryBins())
 	if err != nil {
 		t.Errorf("Failed to create test state: %v", err)
 		t.FailNow()
@@ -688,7 +688,7 @@ func TestHandleNodeUpdates_Realtime(t *testing.T) {
 
 	privKey, _ := rsa.GenerateKey(rand.Reader, 2048)
 
-	testState, err := storage.NewState(privKey, 8, "", region.GetCountryBins(), nil, nil)
+	testState, err := storage.NewState(privKey, 8, "", "", region.GetCountryBins())
 	if err != nil {
 		t.Errorf("Failed to create test state: %v", err)
 		t.FailNow()
@@ -743,7 +743,7 @@ func TestHandleNodeUpdates_Realtime_RoundError(t *testing.T) {
 
 	privKey, _ := rsa.GenerateKey(rand.Reader, 2048)
 
-	testState, err := storage.NewState(privKey, 8, "", region.GetCountryBins(), nil, nil)
+	testState, err := storage.NewState(privKey, 8, "", "", region.GetCountryBins())
 	if err != nil {
 		t.Errorf("Failed to create test state: %v", err)
 		t.FailNow()
@@ -794,7 +794,7 @@ func TestHandleNodeUpdates_Realtime_UpdateError(t *testing.T) {
 
 	privKey, _ := rsa.GenerateKey(rand.Reader, 2048)
 
-	testState, err := storage.NewState(privKey, 8, "", region.GetCountryBins(), nil, nil)
+	testState, err := storage.NewState(privKey, 8, "", "", region.GetCountryBins())
 	if err != nil {
 		t.Errorf("Failed to create test state: %v", err)
 		t.FailNow()
@@ -853,7 +853,7 @@ func TestHandleNodeUpdates_RoundErrored(t *testing.T) {
 
 	privKey, _ := rsa.GenerateKey(rand.Reader, 2048)
 
-	testState, err := storage.NewState(privKey, 8, "", region.GetCountryBins(), nil, nil)
+	testState, err := storage.NewState(privKey, 8, "", "", region.GetCountryBins())
 	if err != nil {
 		t.Errorf("Failed to create test state: %v", err)
 		t.FailNow()
@@ -907,7 +907,7 @@ func TestHandleNodeUpdates_NOT_STARTED(t *testing.T) {
 
 	privKey, _ := rsa.GenerateKey(rand.Reader, 2048)
 
-	testState, err := storage.NewState(privKey, 8, "", region.GetCountryBins(), nil, nil)
+	testState, err := storage.NewState(privKey, 8, "", "", region.GetCountryBins())
 	if err != nil {
 		t.Errorf("Failed to create test state: %v", err)
 		t.FailNow()
diff --git a/scheduling/params.go b/scheduling/params.go
index 3da502cb833046e62999eb2619025e74feb2ac53..e46712f84c4d8695dacbb0efdc9f1fc691646f34 100644
--- a/scheduling/params.go
+++ b/scheduling/params.go
@@ -34,8 +34,6 @@ func (s *SafeParams) SafeCopy() Params {
 
 // JSONable structure which defines the parameters of the Scheduler
 type Params struct {
-	// selects if the secure or simple node selection algorithm is used
-	Secure bool
 	// number of nodes in a team
 	TeamSize uint32
 	// number of slots in a batch
diff --git a/scheduling/pool_test.go b/scheduling/pool_test.go
index d796fc5231323267e44f2645e7e974c48b3b9340..7e6482e03c5316bd77387eda2d8b8a0c4051c8c6 100644
--- a/scheduling/pool_test.go
+++ b/scheduling/pool_test.go
@@ -200,7 +200,7 @@ func setupNodeMap(t *testing.T) *storage.NetworkState {
 	// Build network state
 	privKey, _ := rsa.GenerateKey(rand.Reader, 2048)
 
-	testState, err := storage.NewState(privKey, 8, "", region.GetCountryBins(), nil, nil)
+	testState, err := storage.NewState(privKey, 8, "", "", region.GetCountryBins())
 	if err != nil {
 		t.Errorf("Failed to create test state: %v", err)
 		t.FailNow()
diff --git a/scheduling/schedule.go b/scheduling/schedule.go
index 2ebc20ec5b1d2c14e8dbd488a3c4c65a35f9e553..5c22b443bbcce2df325dbeae375bda2dcff2c01c 100644
--- a/scheduling/schedule.go
+++ b/scheduling/schedule.go
@@ -37,7 +37,7 @@ const (
 	timeToInactive = 3 * time.Minute
 )
 
-type roundCreator func(params Params, pool *waitingPool, roundID id.Round,
+type roundCreator func(params Params, pool *waitingPool, threshold int, roundID id.Round,
 	state *storage.NetworkState, rng io.Reader) (protoRound, error)
 
 func ParseParams(serialParam []byte) *SafeParams {
@@ -147,14 +147,9 @@ func Scheduler(params *SafeParams, state *storage.NetworkState, killchan chan ch
 	// Select the correct round creator
 	var createRound roundCreator
 
-	// Identify which teaming algorithm we will be using
-	if params.Secure {
-		jww.INFO.Printf("Using Secure Teaming Algorithm")
-		createRound = createSecureRound
-	} else {
-		jww.INFO.Printf("Using Simple Teaming Algorithm")
-		createRound = createSimpleRound
-	}
+	// Set teaming algorithm
+	jww.INFO.Printf("Using Secure Teaming Algorithm")
+	createRound = createSecureRound
 
 	// Channel to communicate that a round has timed out
 	roundTimeoutTracker := make(chan id.Round, 1000)
@@ -246,13 +241,10 @@ func Scheduler(params *SafeParams, state *storage.NetworkState, killchan chan ch
 			numNodesInPool := pool.Len()
 
 			// Create a new round if the pool is full
-			var teamFormationThreshold uint32
-			if paramsCopy.Secure {
-				teamFormationThreshold = uint32(paramsCopy.Threshold * float64(state.CountActiveNodes()))
-			} else {
-				teamFormationThreshold = paramsCopy.TeamSize
-			}
-			if numNodesInPool >= int(teamFormationThreshold) && killed == nil {
+			var teamFormationThreshold int
+			teamSize := int(paramsCopy.TeamSize)
+			teamFormationThreshold = int(paramsCopy.Threshold * float64(state.CountActiveNodes()))
+			if numNodesInPool >= teamFormationThreshold && numNodesInPool >= teamSize && killed == nil {
 
 				// Increment round ID
 				currentID, err := state.IncrementRoundID()
@@ -262,9 +254,8 @@ func Scheduler(params *SafeParams, state *storage.NetworkState, killchan chan ch
 				}
 
 				stream := rng.GetStream()
-				defer stream.Close()
-
-				newRound, err := createRound(paramsCopy, pool, currentID, state, stream)
+				newRound, err := createRound(paramsCopy, pool, teamFormationThreshold, currentID, state, stream)
+				stream.Close()
 				if err != nil {
 					return err
 				}
diff --git a/scheduling/secureCreateRound.go b/scheduling/secureCreateRound.go
index 3d8b784dd370bda0b97dadeed31b8b1fd841e2d2..002cb23f9f327bfeeeb307c653fd1ade5cd7aca4 100644
--- a/scheduling/secureCreateRound.go
+++ b/scheduling/secureCreateRound.go
@@ -29,11 +29,11 @@ import (
 // We shall assume geographical distance causes latency in a naive
 //  manner, as delineated here:
 //  https://docs.google.com/document/d/1oyjIDlqC54u_eoFzQP9SVNU2IqjnQOjpUYd9aqbg5X0/edit#
-func createSecureRound(params Params, pool *waitingPool, roundID id.Round,
+func createSecureRound(params Params, pool *waitingPool, threshold int, roundID id.Round,
 	state *storage.NetworkState, rng io.Reader) (protoRound, error) {
 
 	// Pick nodes from the pool
-	nodes, err := pool.PickNRandAtThreshold(int(params.Threshold), int(params.TeamSize))
+	nodes, err := pool.PickNRandAtThreshold(threshold, int(params.TeamSize))
 	if err != nil {
 		return protoRound{}, errors.Errorf("Failed to pick random node group: %v", err)
 	}
diff --git a/scheduling/secureCreateRound_test.go b/scheduling/secureCreateRound_test.go
index 2db0857aee62f3d3219233bc6bbbc8e0d5665f55..d3a838976bad6a7df369a17ce810bf1b8c984f4f 100644
--- a/scheduling/secureCreateRound_test.go
+++ b/scheduling/secureCreateRound_test.go
@@ -21,14 +21,14 @@ func TestCreateRound(t *testing.T) {
 	testParams := Params{
 		TeamSize:            9,
 		BatchSize:           32,
-		Threshold:           1,
+		Threshold:           0.3,
 		NodeCleanUpInterval: 3,
 	}
 
 	// Build network state
 	privKey, _ := rsa.GenerateKey(rand.Reader, 2048)
 
-	testState, err := storage.NewState(privKey, 8, "", region.GetCountryBins(), nil, nil)
+	testState, err := storage.NewState(privKey, 8, "", "", region.GetCountryBins())
 	if err != nil {
 		t.Errorf("Failed to create test state: %v", err)
 		t.FailNow()
@@ -58,7 +58,7 @@ func TestCreateRound(t *testing.T) {
 
 	prng := mathRand.New(mathRand.NewSource(42))
 
-	_, err = createSecureRound(testParams, testpool, roundID, testState, prng)
+	_, err = createSecureRound(testParams, testpool, int(testParams.Threshold*float64(testParams.TeamSize)), roundID, testState, prng)
 	if err != nil {
 		t.Errorf("Error in happy path: %v", err)
 	}
@@ -78,7 +78,7 @@ func TestCreateRound_Error_NotEnoughForTeam(t *testing.T) {
 	// Build network state
 	privKey, _ := rsa.GenerateKey(rand.Reader, 2048)
 
-	testState, err := storage.NewState(privKey, 8, "", region.GetCountryBins(), nil, nil)
+	testState, err := storage.NewState(privKey, 8, "", "", region.GetCountryBins())
 	if err != nil {
 		t.Errorf("Failed to create test state: %v", err)
 		t.FailNow()
@@ -108,7 +108,7 @@ func TestCreateRound_Error_NotEnoughForTeam(t *testing.T) {
 	}
 	prng := mathRand.New(mathRand.NewSource(42))
 
-	_, err = createSecureRound(testParams, testpool, roundID, testState, prng)
+	_, err = createSecureRound(testParams, testpool, int(testParams.Threshold*float64(testParams.TeamSize)), roundID, testState, prng)
 	if err != nil {
 		return
 	}
@@ -132,7 +132,7 @@ func TestCreateRound_Error_NotEnoughForThreshold(t *testing.T) {
 	// Build network state
 	privKey, _ := rsa.GenerateKey(rand.Reader, 2048)
 
-	testState, err := storage.NewState(privKey, 8, "", region.GetCountryBins(), nil, nil)
+	testState, err := storage.NewState(privKey, 8, "", "", region.GetCountryBins())
 	if err != nil {
 		t.Errorf("Failed to create test state: %v", err)
 		t.FailNow()
@@ -162,7 +162,7 @@ func TestCreateRound_Error_NotEnoughForThreshold(t *testing.T) {
 	}
 	prng := mathRand.New(mathRand.NewSource(42))
 
-	_, err = createSecureRound(testParams, testpool, roundID, testState, prng)
+	_, err = createSecureRound(testParams, testpool, int(testParams.Threshold*float64(testParams.TeamSize)), roundID, testState, prng)
 	if err != nil {
 		return
 	}
diff --git a/scheduling/simpleCreateRound.go b/scheduling/simpleCreateRound.go
deleted file mode 100644
index 93c693ce4dd7c58c4fc5bf1c7e8871a105e6f212..0000000000000000000000000000000000000000
--- a/scheduling/simpleCreateRound.go
+++ /dev/null
@@ -1,67 +0,0 @@
-////////////////////////////////////////////////////////////////////////////////
-// Copyright © 2020 Privategrity Corporation                                   /
-//                                                                             /
-// All rights reserved.                                                        /
-////////////////////////////////////////////////////////////////////////////////
-
-package scheduling
-
-import (
-	"github.com/pkg/errors"
-	"gitlab.com/elixxir/registration/storage"
-	"gitlab.com/elixxir/registration/storage/node"
-	"gitlab.com/xx_network/comms/connect"
-	"gitlab.com/xx_network/primitives/id"
-	"gitlab.com/xx_network/primitives/region"
-	"io"
-	"time"
-)
-
-// createSimpleRound.go contains the logic to construct a team for a round and
-// add that round to the network state
-
-// createSimpleRound.go builds a team for a round out of a pool and round id and places
-// this round into the network state
-func createSimpleRound(params Params, pool *waitingPool, roundID id.Round,
-	state *storage.NetworkState, rng io.Reader) (protoRound, error) {
-
-	nodes, err := pool.PickNRandAtThreshold(int(params.TeamSize), int(params.TeamSize))
-
-	if err != nil {
-		return protoRound{}, errors.Errorf("Failed to pick random node group: %v", err)
-	}
-
-	var newRound protoRound
-
-	//build the topology
-
-	nodeIds := make([]*id.ID, 0, len(nodes))
-	countries := make(map[id.ID]string)
-	for _, n := range nodes {
-		nodeIds = append(nodeIds, n.GetID())
-		countries[*n.GetID()] = n.GetOrdering()
-	}
-
-	// Generate a team based on latency
-	bestOrder, _, err := region.OrderNodeTeam(nodeIds, countries, region.GetCountryBins(),
-		region.CreateSetLatencyTableWeights(region.CreateLinkTable()), rng)
-	if err != nil {
-		return protoRound{}, errors.WithMessage(err,
-			"Failed to generate optimal ordering")
-	}
-
-	// Parse the node list to get the order
-	nodeStateList := make([]*node.State, 0, params.TeamSize)
-	for _, n := range bestOrder {
-		nodeStateList = append(nodeStateList, state.GetNodeMap().GetNode(n))
-	}
-
-	// Construct the proto-round object
-	newRound.Topology = connect.NewCircuit(bestOrder)
-	newRound.ID = roundID
-	newRound.BatchSize = params.BatchSize
-	newRound.NodeStateList = nodeStateList
-	newRound.ResourceQueueTimeout = params.ResourceQueueTimeout * time.Millisecond
-	return newRound, nil
-
-}
diff --git a/scheduling/simpleCreateRound_test.go b/scheduling/simpleCreateRound_test.go
deleted file mode 100644
index fee76f34bb87ea9ea0c3c467c96840db7e1778fb..0000000000000000000000000000000000000000
--- a/scheduling/simpleCreateRound_test.go
+++ /dev/null
@@ -1,270 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////
-// Copyright © 2020 Privategrity Corporation                                   /
-//                                                                             /
-// All rights reserved.                                                        /
-////////////////////////////////////////////////////////////////////////////////
-package scheduling
-
-import (
-	"crypto/rand"
-	"gitlab.com/elixxir/registration/storage"
-	"gitlab.com/elixxir/registration/storage/node"
-	"gitlab.com/xx_network/comms/connect"
-	"gitlab.com/xx_network/crypto/signature/rsa"
-	"gitlab.com/xx_network/primitives/id"
-	"gitlab.com/xx_network/primitives/region"
-	mathRand "math/rand"
-	"reflect"
-	"testing"
-)
-
-// Happy path
-func TestCreateRound_Random(t *testing.T) {
-	// Build params for scheduling
-	testParams := Params{
-		TeamSize:            5,
-		BatchSize:           32,
-		Threshold:           0,
-		NodeCleanUpInterval: 3,
-		Secure:              false,
-	}
-
-	// Build network state
-	privKey, _ := rsa.GenerateKey(rand.Reader, 2048)
-
-	testState, err := storage.NewState(privKey, 8, "", region.GetCountryBins(), nil, nil)
-	if err != nil {
-		t.Errorf("Failed to create test state: %v", err)
-		t.FailNow()
-	}
-
-	// Build node list
-	nodeList := make([]*id.ID, testParams.TeamSize)
-	nodeStateList := make([]*node.State, testParams.TeamSize)
-
-	// Build pool
-	testPool := NewWaitingPool()
-
-	for i := 0; i < int(testParams.TeamSize); i++ {
-		nid := id.NewIdFromUInt(uint64(i), id.Node, t)
-		nodeList[i] = nid
-		err := testState.GetNodeMap().AddNode(nodeList[i], "US", "", "", 0)
-		if err != nil {
-			t.Errorf("Couldn't add node: %v", err)
-			t.FailNow()
-		}
-		nodeState := testState.GetNodeMap().GetNode(nid)
-		nodeStateList[i] = nodeState
-		testPool.Add(nodeState)
-	}
-
-	roundID, err := testState.IncrementRoundID()
-	if err != nil {
-		t.Errorf("IncrementRoundID() failed: %+v", err)
-	}
-	prng := mathRand.New(mathRand.NewSource(42))
-	testProtoRound, err := createSecureRound(testParams, testPool, roundID, testState, prng)
-	if err != nil {
-		t.Errorf("Happy path of createSimpleRound failed: %v", err)
-	}
-
-	if testProtoRound.ID != roundID {
-		t.Errorf("ProtoRound's id returned unexpected value!"+
-			"\n\tExpected: %d"+
-			"\n\tReceived: %d", roundID, testProtoRound.ID)
-	}
-
-	if testParams.BatchSize != testProtoRound.BatchSize {
-		t.Errorf("ProtoRound's batchsize returned unexpected value!"+
-			"\n\tExpected: %v"+
-			"\n\tReceived: %v", testParams.BatchSize, testProtoRound.BatchSize)
-
-	}
-
-}
-
-// Error path: Provide a node ordering that is invalid
-func TestCreateRound_BadOrdering(t *testing.T) {
-	// Build scheduling params
-	testParams := Params{
-		TeamSize:  5,
-		BatchSize: 32,
-	}
-
-	// Build network state
-	privKey, _ := rsa.GenerateKey(rand.Reader, 2048)
-
-	testState, err := storage.NewState(privKey, 8, "", region.GetCountryBins(), nil, nil)
-	if err != nil {
-		t.Errorf("Failed to create test state: %v", err)
-		t.FailNow()
-	}
-
-	// Build a node list that will be invalid
-	nodeList := make([]*id.ID, testParams.TeamSize)
-	for i := uint64(0); i < uint64(len(nodeList)); i++ {
-		nodeList[i] = id.NewIdFromUInt(i, id.Node, t)
-		// Input an invalid ordering to node
-		err := testState.GetNodeMap().AddNode(nodeList[i], "BadNumber", "", "", 0)
-		if err != nil {
-			t.Errorf("Couldn't add node: %v", err)
-			t.FailNow()
-		}
-	}
-
-	// Build pool
-	testPool := NewWaitingPool()
-
-	roundID, err := testState.IncrementRoundID()
-	if err != nil {
-		t.Errorf("IncrementRoundID() failed: %+v", err)
-	}
-
-	// Invalid ordering will cause this to fail
-	prng := mathRand.New(mathRand.NewSource(42))
-	_, err = createSimpleRound(testParams, testPool, roundID, testState, prng)
-	if err != nil {
-		return
-	}
-
-	t.Errorf("Expected error case: passed in an ordering to nodes which were not numbers should result " +
-		"in an error")
-
-}
-
-// Test that the system semi-optimal gets done when both
-// random ordering and semioptimal ordering are set to true
-func TestCreateSimpleRound_SemiOptimal(t *testing.T) {
-	// Build scheduling params
-	testParams := Params{
-		TeamSize:            9,
-		BatchSize:           32,
-		Threshold:           1,
-		Secure:              false,
-		NodeCleanUpInterval: 3,
-	}
-
-	// Build network state
-	privKey, _ := rsa.GenerateKey(rand.Reader, 2048)
-
-	testState, err := storage.NewState(privKey, 8, "", region.GetCountryBins(), nil, nil)
-	if err != nil {
-		t.Errorf("Failed to create test state: %v", err)
-		t.FailNow()
-	}
-
-	// Build the nodes
-	nodeList := make([]*id.ID, testParams.TeamSize)
-	nodeStateList := make([]*node.State, testParams.TeamSize)
-	testPool := NewWaitingPool()
-
-	// Craft regions for nodes
-	regions := []string{"CR", "GB", "SK",
-		"HR", "IQ", "BF", "RU", "CX"}
-
-	for i := uint64(0); i < uint64(len(nodeList)); i++ {
-		// Randomize the regions of the nodes
-		index := mathRand.Intn(8)
-
-		// Generate a test id
-		nid := id.NewIdFromUInt(i, id.Node, t)
-		nodeList[i] = nid
-
-		// Add the node to that node map
-		// Place the node in a random region
-		err := testState.GetNodeMap().AddNode(nodeList[i], regions[index], "", "", 0)
-		if err != nil {
-			t.Errorf("Couldn't add node: %v", err)
-			t.FailNow()
-		}
-
-		// Add the node to the pool
-		nodeState := testState.GetNodeMap().GetNode(nid)
-		nodeStateList[i] = nodeState
-		testPool.Add(nodeState)
-	}
-
-	initialTopology := connect.NewCircuit(nodeList)
-
-	roundID, err := testState.IncrementRoundID()
-	if err != nil {
-		t.Errorf("IncrementRoundID() failed: %+v", err)
-	}
-	prng := mathRand.New(mathRand.NewSource(42))
-
-	testProtoRound, err := createSimpleRound(testParams, testPool, roundID, testState, prng)
-	if err != nil {
-		t.Errorf("Happy path of createSimpleRound failed: %v", err)
-	}
-
-	// Check that shuffling has actually occurred, should not be the initial topology
-	// which is inefficient
-	if reflect.DeepEqual(initialTopology, testProtoRound.Topology) {
-		t.Errorf("Highly unlikely initial topology identical to resulting after shuffling. " +
-			"Possile shuffling is broken")
-	}
-
-}
-
-// Test that the system semi-optimal gets done when both
-// random ordering and semioptimal ordering are set to true
-func TestCreateSimpleRound_SemiOptimal_BadRegion(t *testing.T) {
-	// Build scheduling params
-	testParams := Params{
-		TeamSize:            9,
-		BatchSize:           32,
-		Threshold:           1,
-		Secure:              false,
-		NodeCleanUpInterval: 3,
-	}
-
-	// Build network state
-	privKey, _ := rsa.GenerateKey(rand.Reader, 2048)
-
-	testState, err := storage.NewState(privKey, 8, "", region.GetCountryBins(), nil, nil)
-	if err != nil {
-		t.Errorf("Failed to create test state: %v", err)
-		t.FailNow()
-	}
-
-	// Build the nodes
-	nodeList := make([]*id.ID, testParams.TeamSize)
-	nodeStateList := make([]*node.State, testParams.TeamSize)
-	testPool := NewWaitingPool()
-
-	badRegion := "Mars"
-
-	for i := uint64(0); i < uint64(len(nodeList)); i++ {
-		// Generate a test id
-		nid := id.NewIdFromUInt(i, id.Node, t)
-		nodeList[i] = nid
-
-		// Add the node to that node map
-		// Place the node in a random region
-		err := testState.GetNodeMap().AddNode(nodeList[i], badRegion, "", "", 0)
-		if err != nil {
-			t.Errorf("Couldn't add node: %v", err)
-			t.FailNow()
-		}
-
-		// Add the node to the pool
-		nodeState := testState.GetNodeMap().GetNode(nid)
-		nodeStateList[i] = nodeState
-		testPool.Add(nodeState)
-	}
-
-	// Generate round id
-	roundID, err := testState.IncrementRoundID()
-	if err != nil {
-		t.Errorf("IncrementRoundID() failed: %+v", err)
-	}
-	prng := mathRand.New(mathRand.NewSource(42))
-
-	_, err = createSimpleRound(testParams, testPool, roundID, testState, prng)
-	if err != nil {
-		return
-	}
-
-	t.Errorf("Expected error path: Test should fail when receiving bad region %v!", badRegion)
-
-}
diff --git a/scheduling/startRound_test.go b/scheduling/startRound_test.go
index 1f32f2e2daf7bdc5095940f5390e85fd213317e9..bae07eac2008e8afc0c33766180ccd977926bc73 100644
--- a/scheduling/startRound_test.go
+++ b/scheduling/startRound_test.go
@@ -25,8 +25,7 @@ func TestStartRound(t *testing.T) {
 	testParams := Params{
 		TeamSize:            8,
 		BatchSize:           32,
-		Threshold:           1,
-		Secure:              false,
+		Threshold:           0.3,
 		NodeCleanUpInterval: 3,
 	}
 
@@ -39,7 +38,7 @@ func TestStartRound(t *testing.T) {
 	// Build network state
 	privKey, _ := rsa.GenerateKey(rand.Reader, 2048)
 
-	testState, err := storage.NewState(privKey, 8, "", region.GetCountryBins(), nil, nil)
+	testState, err := storage.NewState(privKey, 8, "", "", region.GetCountryBins())
 	if err != nil {
 		t.Errorf("Failed to create test state: %v", err)
 		t.FailNow()
@@ -70,7 +69,7 @@ func TestStartRound(t *testing.T) {
 	}
 	prng := mathRand.New(mathRand.NewSource(42))
 
-	testProtoRound, err := createSecureRound(testParams, testPool, roundID, testState, prng)
+	testProtoRound, err := createSecureRound(testParams, testPool, int(testParams.Threshold*float64(testParams.TeamSize)), roundID, testState, prng)
 	if err != nil {
 		t.Errorf("Happy path of createSimpleRound failed: %v", err)
 	}
@@ -94,8 +93,7 @@ func TestStartRound_BadState(t *testing.T) {
 	testParams := Params{
 		TeamSize:            8,
 		BatchSize:           32,
-		Threshold:           1,
-		Secure:              false,
+		Threshold:           0.3,
 		NodeCleanUpInterval: 3,
 	}
 
@@ -108,7 +106,7 @@ func TestStartRound_BadState(t *testing.T) {
 	// Build network state
 	privKey, _ := rsa.GenerateKey(rand.Reader, 2048)
 
-	testState, err := storage.NewState(privKey, 8, "", region.GetCountryBins(), nil, nil)
+	testState, err := storage.NewState(privKey, 8, "", "", region.GetCountryBins())
 	if err != nil {
 		t.Errorf("Failed to create test state: %v", err)
 		t.FailNow()
@@ -143,7 +141,7 @@ func TestStartRound_BadState(t *testing.T) {
 	testState.GetRoundMap().AddRound_Testing(badState, t)
 	prng := mathRand.New(mathRand.NewSource(42))
 
-	testProtoRound, err := createSecureRound(testParams, testPool, roundID, testState, prng)
+	testProtoRound, err := createSecureRound(testParams, testPool, int(testParams.Threshold*float64(testParams.TeamSize)), roundID, testState, prng)
 	if err != nil {
 		t.Errorf("Happy path of createSimpleRound failed: %v", err)
 	}
@@ -163,20 +161,18 @@ func TestStartRound_BadState(t *testing.T) {
 
 // Error path
 func TestStartRound_BadNode(t *testing.T) {
-	// Build params for scheduling
 	// Build params for scheduling
 	testParams := Params{
 		TeamSize:            8,
 		BatchSize:           32,
-		Threshold:           1,
-		Secure:              false,
+		Threshold:           0.3,
 		NodeCleanUpInterval: 3,
 	}
 
 	// Build network state
 	privKey, _ := rsa.GenerateKey(rand.Reader, 2048)
 
-	testState, err := storage.NewState(privKey, 8, "", region.GetCountryBins(), nil, nil)
+	testState, err := storage.NewState(privKey, 8, "", "", region.GetCountryBins())
 	if err != nil {
 		t.Errorf("Failed to create test state: %v", err)
 		t.FailNow()
@@ -208,7 +204,7 @@ func TestStartRound_BadNode(t *testing.T) {
 	badState := round.NewState_Testing(roundID, states.COMPLETED, nil, t)
 	prng := mathRand.New(mathRand.NewSource(42))
 
-	testProtoRound, err := createSecureRound(testParams, testPool, roundID, testState, prng)
+	testProtoRound, err := createSecureRound(testParams, testPool, int(testParams.Threshold*float64(testParams.TeamSize)), roundID, testState, prng)
 	if err != nil {
 		t.Errorf("Happy path of createSimpleRound failed: %v", err)
 	}
diff --git a/storage/state.go b/storage/state.go
index 921a24bba3a627f9f734cfb4813bffd4aceed987..fa1acc4188d00d25621aa735bdd0001a64864d4e 100644
--- a/storage/state.go
+++ b/storage/state.go
@@ -26,6 +26,7 @@ import (
 	"gitlab.com/xx_network/primitives/ndf"
 	"gitlab.com/xx_network/primitives/region"
 	"gitlab.com/xx_network/primitives/utils"
+	"google.golang.org/protobuf/proto"
 	"strconv"
 	"strings"
 	"sync"
@@ -69,7 +70,12 @@ type NetworkState struct {
 	// Address space size
 	addressSpaceSize *uint32
 
-	ndfOutputPath string
+	// Output path to the full ndf
+	fullNdfOutputPath string
+
+	// Output path to the signed partial ndf provided to client
+	// by uploading the file
+	signedPartialNdfOutputPath string
 
 	// round adder buffer channel
 	roundUpdatesToAddCh chan *dataStructures.Round
@@ -81,8 +87,8 @@ type NetworkState struct {
 
 // NewState returns a new NetworkState object.
 func NewState(rsaPrivKey *rsa.PrivateKey, addressSpaceSize uint32,
-	ndfOutputPath string, geoBins map[string]region.GeoBin,
-	whitelistedIds []string, whitelistedIpAddresses []string) (*NetworkState, error) {
+	fullNdfOutputPath string, signedPartialNdfOutputPath string,
+	geoBins map[string]region.GeoBin) (*NetworkState, error) {
 
 	fullNdf, err := dataStructures.NewNdf(&ndf.NetworkDefinition{})
 	if err != nil {
@@ -94,19 +100,20 @@ func NewState(rsaPrivKey *rsa.PrivateKey, addressSpaceSize uint32,
 	}
 
 	state := &NetworkState{
-		rounds:              round.NewStateMap(),
-		roundUpdates:        dataStructures.NewUpdates(),
-		update:              make(chan node.UpdateNotification, updateBufferLength),
-		nodes:               node.NewStateMap(),
-		unprunedNdf:         &ndf.NetworkDefinition{},
-		fullNdf:             fullNdf,
-		partialNdf:          partialNdf,
-		rsaPrivateKey:       rsaPrivKey,
-		addressSpaceSize:    &addressSpaceSize,
-		pruneList:           make(map[id.ID]bool),
-		ndfOutputPath:       ndfOutputPath,
-		roundUpdatesToAddCh: make(chan *dataStructures.Round, 500),
-		geoBins:             geoBins,
+		rounds:                     round.NewStateMap(),
+		roundUpdates:               dataStructures.NewUpdates(),
+		update:                     make(chan node.UpdateNotification, updateBufferLength),
+		nodes:                      node.NewStateMap(),
+		unprunedNdf:                &ndf.NetworkDefinition{},
+		fullNdf:                    fullNdf,
+		partialNdf:                 partialNdf,
+		rsaPrivateKey:              rsaPrivKey,
+		addressSpaceSize:           &addressSpaceSize,
+		pruneList:                  make(map[id.ID]bool),
+		fullNdfOutputPath:          fullNdfOutputPath,
+		signedPartialNdfOutputPath: signedPartialNdfOutputPath,
+		roundUpdatesToAddCh:        make(chan *dataStructures.Round, 500),
+		geoBins:                    geoBins,
 	}
 
 	//begin the thread that reads and adds round updates
@@ -187,9 +194,11 @@ func NewState(rsaPrivKey *rsa.PrivateKey, addressSpaceSize uint32,
 }
 
 // CountActiveNodes returns a count of active nodes in the state
-// NOTE: Accounts for pruned, but not stale nodes
 func (s *NetworkState) CountActiveNodes() int {
-	return len(s.GetFullNdf().Get().Nodes)
+	s.pruneListMux.Lock()
+	defer s.pruneListMux.Unlock()
+
+	return len(s.unprunedNdf.Nodes) - len(s.pruneList)
 }
 
 // Adds pruned nodes, used by disabledNodes
@@ -399,9 +408,26 @@ func (s *NetworkState) UpdateNdf(newNdf *ndf.NetworkDefinition) (err error) {
 		return err
 	}
 
-	err = outputToJSON(newNdf, s.ndfOutputPath)
+	// Output full NDF to file
+	err = outputToJSON(newNdf, s.fullNdfOutputPath)
+	if err != nil {
+		jww.ERROR.Printf("unable to output full NDF JSON file: %+v", err)
+	}
+
+	// Marshal signed partial NDF
+	signedPartialNdfMarshal, err := proto.Marshal(s.partialNdf.GetPb())
+	if err != nil {
+		jww.ERROR.Printf("unable to marshal partial ndf")
+	}
+
+	// Base64 encode the signed marshaled NDF
+	signedPartialEncoded := base64.StdEncoding.EncodeToString(signedPartialNdfMarshal)
+
+	// Output signed partial ndf to file
+	err = utils.WriteFile(s.signedPartialNdfOutputPath,
+		[]byte(signedPartialEncoded), utils.FilePerms, utils.DirPerms)
 	if err != nil {
-		jww.ERROR.Printf("unable to output NDF JSON file: %+v", err)
+		jww.ERROR.Printf("unable to output signed partial NDF to file: %+v", err)
 	}
 
 	jww.INFO.Printf("Full NDF updated to: %s", base64.StdEncoding.EncodeToString(s.fullNdf.GetHash()))
diff --git a/storage/state_test.go b/storage/state_test.go
index eb8f0d881dca211d7389016d0b5b85f74e6f3fcb..6db9f61c803325c165b57ccd4017f88dc70f0349 100644
--- a/storage/state_test.go
+++ b/storage/state_test.go
@@ -59,7 +59,7 @@ func TestNewState(t *testing.T) {
 	}
 
 	// Generate new NetworkState
-	state, err := NewState(privateKey, 8, "", region.GetCountryBins(), nil, nil)
+	state, err := NewState(privateKey, 8, "", "", region.GetCountryBins())
 	if err != nil {
 		t.Errorf("NewState() produced an unexpected error:\n%v", err)
 	}
@@ -446,7 +446,7 @@ func generateTestNetworkState() (*NetworkState, *rsa.PrivateKey, error) {
 	}
 
 	// Generate new NetworkState using the private key
-	state, err := NewState(privKey, 8, "", region.GetCountryBins(), nil, nil)
+	state, err := NewState(privKey, 8, "", "", region.GetCountryBins())
 	if err != nil {
 		return state, privKey, fmt.Errorf("NewState() produced an unexpected error:\n+%v", err)
 	}