diff --git a/README.md b/README.md
index a5041e876b23c787616d2f260830f2aca19de0d3..70f68111713b9158077701f20fcccd73423a4fd9 100644
--- a/README.md
+++ b/README.md
@@ -145,59 +145,48 @@ Usage:
   client [command]
 
 Available Commands:
-  generate    Generates version and dependency information for the
-              Elixxir binary
+  generate    Generates version and dependency information for the Elixxir binary
+  getndf      Download the network definition file from the network and print it.
+  group       Group commands for cMix client
   help        Help about any command
-  version     Print the version and dependency information for the
-              Elixxir binary
+  init        Initialize a user ID but do not connect to the network
+  single      Send and respond to single-use messages.
+  ud          Register for and search users using the xx network user discovery service.
+  version     Print the version and dependency information for the Elixxir binary
 
 Flags:
-      --accept-channel            Accept the channel request for the
-                                  corresponding recipient ID
-      --delete-channel            Delete the channel information for the
-                                  corresponding recipient ID                            
+      --accept-channel            Accept the channel request for the corresponding recipient ID
+      --auth-timeout uint         The number of seconds to wait for an authentication channelto confirm (default 120)
+      --delete-channel            Delete the channel information for the corresponding recipient ID
       --destfile string           Read this contact file for the destination id
-  -d, --destid string             ID to send message to (if below 40, will be
-                                  precanned. Use '0x' or 'b64:' for hex and
-                                  base64 representations) (default "0")
-      --forceHistoricalRounds     Force all rounds to be sent to historical
-                                  round retrieval
-      --forceMessagePickupRetry   Enable a mechanism which forces a 50% chance 
-                                  of no message pickup, instead triggering the 
-                                  message pickup retry mechanism
+  -d, --destid string             ID to send message to (if below 40, will be precanned. Use '0x' or 'b64:' for hex and base64 representations) (default "0")
+      --e2eMaxKeys uint           Max keys used before blocking until a rekey completes (default 800)
+      --e2eMinKeys uint           Minimum number of keys used before requesting rekey (default 500)
+      --e2eNumReKeys uint         Number of rekeys reserved for rekey operations (default 16)
+      --forceHistoricalRounds     Force all rounds to be sent to historical round retrieval
+      --forceMessagePickupRetry   Enable a mechanism which forces a 50% chance of no message pickup, instead triggering the message pickup retry mechanism
   -h, --help                      help for client
-  -l, --log string                Path to the log output path (- is stdout)
-                                  (default "-")
+  -l, --log string                Path to the log output path (- is stdout) (default "-")
+  -v, --logLevel uint             Verbose mode for debugging
   -m, --message string            Message to send
-  -n, --ndf string                Path to the network definition JSON file
-                                  (default "ndf.json")
+  -n, --ndf string                Path to the network definition JSON file (default "ndf.json")
   -p, --password string           Password to the session file
-      --receiveCount uint         How many messages we should wait for before
-                                  quitting (default 1)
-      --regcode string            Registration code (optional)
-      --sendCount uint            The number of times to send the message
-                                  (default 1)
-      --sendDelay uint            The delay between sending the messages in ms
-                                  (default 500)
-      --sendid uint               Use precanned user id (must be between 1 and
-                                  40, inclusive)
-      --slowPolling bool          Enables polling for all network updates and RSA signed rounds.
-                                  Defaults to true (filtered updates with ECC signed rounds) if not set
-
-  -s, --session string            Sets the initial directory for client storage
-      --unsafe                    Send raw, unsafe messages without e2e
-                                  encryption.
-      --unsafe-channel-creation   Turns off the user identity authenticated
-                                  channel check, automatically approving
-                                  authenticated channels
-      --verboseRoundTracking      Verbose round tracking, keeps track and prints 
-                                  all rounds the client was aware of while running. 
-                                  Defaults to false if not set.
-  -v, --logLevel uint             Level of debugging to print (0 = info, 
-                                  1 = debug, >1 = trace). (Default info)
-      --waitTimeout uint          The number of seconds to wait for messages to
-                                  arrive (default 15)
-  -w, --writeContact string       Write the contact file for this user to this
+      --profile-cpu string        Enable cpu profiling to this file
+      --protoUserOut string       Path to which a normally constructed client will write proto user JSON file (default "protoUser.json")
+      --protoUserPath string      Path to proto user JSON file containing cryptographic primitives the client will load (default "protoUser.json")
+      --receiveCount uint         How many messages we should wait for before quitting (default 1)
+      --regcode string            Identity code (optional)
+      --send-auth-request         Send an auth request to the specified destination and waitfor confirmation
+      --sendCount uint            The number of times to send the message (default 1)
+      --sendDelay uint            The delay between sending the messages in ms (default 500)
+      --sendid uint               Use precanned user id (must be between 1 and 40, inclusive)
+  -s, --session string            Sets the initial storage directory for client session data
+      --slowPolling               Enables polling for unfiltered network updates with RSA signatures
+      --unsafe                    Send raw, unsafe messages without e2e encryption.
+      --unsafe-channel-creation   Turns off the user identity authenticated channel check, automatically approving authenticated channels
+      --verboseRoundTracking      Verbose round tracking, keeps track and prints all rounds the client was aware of while running. Defaults to false if not set.
+      --waitTimeout uint          The number of seconds to wait for messages to arrive (default 15)
+  -w, --writeContact string       Write contact information, if any, to this file,  defaults to stdout (default "-")
                                   file
 
 Use "client [command] --help" for more information about a command.
diff --git a/api/client.go b/api/client.go
index 8a8fa597878365eb924b84d777b423bb44390a29..d3f6254563940a6b85921df9bb093b3f22b8a834 100644
--- a/api/client.go
+++ b/api/client.go
@@ -366,7 +366,8 @@ func LoginWithNewBaseNDF_UNSAFE(storageDir string, password []byte,
 
 // LoginWithProtoClient creates a client object with a protoclient JSON containing the
 // cryptographic primitives. This is designed for some specific deployment
-//// procedures and is generally unsafe.
+// procedures and is generally unsafe. It is unsafe because all network relationships
+// will no longer be present in the new session file.
 func LoginWithProtoClient(storageDir string, password []byte, protoClientJSON []byte,
 	newBaseNdf string, parameters params.Network) (*Client, error) {
 	jww.INFO.Printf("LoginWithNewBaseNDF_UNSAFE()")
diff --git a/api/permissioning.go b/api/permissioning.go
index 606c436441ba76b80dda1400228903263bc9741c..0f79b94aef4d354648fb78f61c58e7aff2c3d397 100644
--- a/api/permissioning.go
+++ b/api/permissioning.go
@@ -10,7 +10,6 @@ package api
 import (
 	"encoding/json"
 	"github.com/pkg/errors"
-	jww "github.com/spf13/jwalterweatherman"
 	"gitlab.com/elixxir/client/interfaces/user"
 	"gitlab.com/elixxir/client/storage"
 )
@@ -42,13 +41,6 @@ func (c *Client) registerWithPermissioning() error {
 	userData.SetReceptionRegistrationValidationSignature(receptionRegValidationSignature)
 	userData.SetRegistrationTimestamp(registrationTimestamp)
 
-
-
-	err = c.printProtoUser(regCode, transmissionRegValidationSignature, receptionRegValidationSignature)
-	if err != nil {
-		return errors.WithMessage(err, "failed to print proto user")
-	}
-
 	//update the registration state
 	err = c.storage.ForwardRegistrationStatus(storage.PermissioningComplete)
 	if err != nil {
@@ -58,29 +50,35 @@ func (c *Client) registerWithPermissioning() error {
 	return nil
 }
 
-// todo: remove once deploy has been tested
-func (c *Client) printProtoUser(regCode string,
-	transmissionRegValidationSignature, receptionRegValidationSignature []byte) error {
-	// todo: remove this once proto has been generated
+// ConstructProtoUerFile is a helper function which is used for proto client testing.
+// This is used for development testing.
+func (c *Client) ConstructProtoUerFile() ([]byte, error) {
 	username, err := c.GetStorage().User().GetUsername()
 	if err != nil {
-		return errors.WithMessage(err, "failed to register with "+
+		return nil, errors.WithMessage(err, "failed to register with "+
+			"permissioning")
+	}
+
+	//load the registration code
+	regCode, err := c.storage.GetRegCode()
+	if err != nil {
+		return nil, errors.WithMessage(err, "failed to register with "+
 			"permissioning")
 	}
 
 	Usr := user.Proto{
 		TransmissionID:               c.GetUser().TransmissionID,
-		TransmissionSalt:               c.GetUser().TransmissionSalt,
-		TransmissionRSA:                c.GetUser().TransmissionRSA,
-		ReceptionID:                    c.GetUser().ReceptionID,
+		TransmissionSalt:             c.GetUser().TransmissionSalt,
+		TransmissionRSA:              c.GetUser().TransmissionRSA,
+		ReceptionID:                  c.GetUser().ReceptionID,
 		ReceptionSalt:                c.GetUser().ReceptionSalt,
 		ReceptionRSA:                 c.GetUser().ReceptionRSA,
 		Precanned:                    c.GetUser().Precanned,
-		RegistrationTimestamp:       c.GetUser().RegistrationTimestamp,
-		Username:                    username,
-		RegCode:                     regCode,
-		TransmissionRegValidationSig: transmissionRegValidationSignature,
-		ReceptionRegValidationSig:    receptionRegValidationSignature,
+		RegistrationTimestamp:        c.GetUser().RegistrationTimestamp,
+		Username:                     username,
+		RegCode:                      regCode,
+		TransmissionRegValidationSig: c.storage.User().GetTransmissionRegistrationValidationSignature(),
+		ReceptionRegValidationSig:    c.storage.User().GetReceptionRegistrationValidationSignature(),
 		CmixDhPrivateKey:             c.GetStorage().Cmix().GetDHPrivateKey(),
 		CmixDhPublicKey:              c.GetStorage().Cmix().GetDHPublicKey(),
 		E2eDhPrivateKey:              c.GetStorage().E2e().GetDHPrivateKey(),
@@ -89,11 +87,9 @@ func (c *Client) printProtoUser(regCode string,
 
 	jsonBytes, err := json.Marshal(Usr)
 	if err != nil {
-		return errors.WithMessage(err, "failed to register with "+
+		return nil, errors.WithMessage(err, "failed to register with "+
 			"permissioning")
 	}
 
-	jww.INFO.Printf("PROTO USER JSON: \n%s", string(jsonBytes))
-
-	return nil
-}
\ No newline at end of file
+	return jsonBytes, nil
+}
diff --git a/cmd/proto.go b/cmd/proto.go
new file mode 100644
index 0000000000000000000000000000000000000000..f2a43c57392c69757a462935d479e4202cc48ae3
--- /dev/null
+++ b/cmd/proto.go
@@ -0,0 +1,476 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright © 2020 xx network SEZC                                          //
+//                                                                           //
+// Use of this source code is governed by a license that can be found in the //
+// LICENSE file                                                              //
+///////////////////////////////////////////////////////////////////////////////
+
+package cmd
+
+import (
+	"fmt"
+	"github.com/pkg/errors"
+	"github.com/spf13/cobra"
+	jww "github.com/spf13/jwalterweatherman"
+	"github.com/spf13/viper"
+	"gitlab.com/elixxir/client/api"
+	"gitlab.com/elixxir/client/interfaces/message"
+	"gitlab.com/elixxir/client/interfaces/params"
+	"gitlab.com/xx_network/primitives/id"
+	"gitlab.com/xx_network/primitives/utils"
+	"io/ioutil"
+	"sync"
+	"time"
+)
+
+var protoCmd = &cobra.Command{
+	Use:   "proto",
+	Short: "Load client with a proto client JSON file.",
+	Args:  cobra.NoArgs,
+	Run: func(cmd *cobra.Command, args []string) {
+		// If output path is specified, only write to file
+		protoOutputPath := viper.GetString("protoUserOut")
+		if protoOutputPath != "" {
+			client := initClient()
+
+			jsonBytes, err := client.ConstructProtoUerFile()
+			if err != nil {
+				jww.FATAL.Panicf("Failed to construct proto user file: %v", err)
+			}
+
+			err = utils.WriteFileDef(protoOutputPath, jsonBytes)
+			if err != nil {
+				jww.FATAL.Panicf("Failed to write proto user to file: %v", err)
+			}
+			return
+		}
+
+		client := loadProtoClient()
+
+		// Write user to contact file
+		user := client.GetUser()
+		jww.INFO.Printf("User: %s", user.ReceptionID)
+		jww.INFO.Printf("User Transmission: %s", user.TransmissionID)
+		writeContact(user.GetContact())
+
+		// Get Recipient and/or set it to myself
+		isPrecanPartner := false
+		recipientContact := readContact()
+		recipientID := recipientContact.ID
+
+		// Try to get recipientID from destid
+		if recipientID == nil {
+			recipientID, isPrecanPartner = parseRecipient(
+				viper.GetString("destid"))
+		}
+
+		// Set it to myself
+		if recipientID == nil {
+			jww.INFO.Printf("sending message to self")
+			recipientID = user.ReceptionID
+			recipientContact = user.GetContact()
+		}
+
+		confCh, recvCh := initClientCallbacks(client)
+		// The following block is used to check if the request from
+		// a channel authorization is from the recipient we intend in
+		// this run.
+		authConfirmed := false
+		go func() {
+			for {
+				requestor := <-confCh
+				authConfirmed = recipientID.Cmp(requestor)
+			}
+		}()
+
+		err := client.StartNetworkFollower(5 * time.Second)
+		if err != nil {
+			jww.FATAL.Panicf("%+v", err)
+		}
+
+		// Wait until connected or crash on timeout
+		connected := make(chan bool, 10)
+		client.GetHealth().AddChannel(connected)
+		waitUntilConnected(connected)
+
+		// After connection, make sure we have registered with at least
+		// 85% of the nodes
+		numReg := 1
+		total := 100
+		for numReg < (total*3)/4 {
+			time.Sleep(1 * time.Second)
+			numReg, total, err = client.GetNodeRegistrationStatus()
+			if err != nil {
+				jww.FATAL.Panicf("%+v", err)
+			}
+			jww.INFO.Printf("Registering with nodes (%d/%d)...",
+				numReg, total)
+		}
+
+		// Send Messages
+		msgBody := viper.GetString("message")
+
+		time.Sleep(10 * time.Second)
+
+		// Accept auth request for this recipient
+		if viper.GetBool("accept-channel") {
+			acceptChannel(client, recipientID)
+			// Do not wait for channel confirmations if we
+			// accepted one
+			authConfirmed = true
+		}
+
+		if client.HasAuthenticatedChannel(recipientID) {
+			jww.INFO.Printf("Authenticated channel already in "+
+				"place for %s", recipientID)
+			authConfirmed = true
+		}
+
+		// Send unsafe messages or not?
+		unsafe := viper.GetBool("unsafe")
+
+		sendAuthReq := viper.GetBool("send-auth-request")
+		if !unsafe && !authConfirmed && !isPrecanPartner &&
+			sendAuthReq {
+			addAuthenticatedChannel(client, recipientID,
+				recipientContact)
+		} else if !unsafe && !authConfirmed && isPrecanPartner {
+			addPrecanAuthenticatedChannel(client,
+				recipientID, recipientContact)
+			authConfirmed = true
+		}
+
+		if !unsafe && !authConfirmed {
+			jww.INFO.Printf("Waiting for authentication channel"+
+				" confirmation with partner %s", recipientID)
+			scnt := uint(0)
+			waitSecs := viper.GetUint("auth-timeout")
+			for !authConfirmed && scnt < waitSecs {
+				time.Sleep(1 * time.Second)
+				scnt++
+			}
+			if scnt == waitSecs {
+				jww.FATAL.Panicf("Could not confirm "+
+					"authentication channel for %s, "+
+					"waited %d seconds.", recipientID,
+					waitSecs)
+			}
+			jww.INFO.Printf("Authentication channel confirmation"+
+				" took %d seconds", scnt)
+		}
+
+		// Delete this recipient
+		if viper.GetBool("delete-channel") {
+			deleteChannel(client, recipientID)
+		}
+
+		msg := message.Send{
+			Recipient:   recipientID,
+			Payload:     []byte(msgBody),
+			MessageType: message.Text,
+		}
+		paramsE2E := params.GetDefaultE2E()
+		paramsUnsafe := params.GetDefaultUnsafe()
+		wg := &sync.WaitGroup{}
+		sendCnt := int(viper.GetUint("sendCount"))
+		wg.Add(sendCnt)
+		go func() {
+			//sendDelay := time.Duration(viper.GetUint("sendDelay"))
+			for i := 0; i < sendCnt; i++ {
+				go func(i int) {
+					defer wg.Done()
+					fmt.Printf("Sending to %s: %s\n", recipientID, msgBody)
+					var roundIDs []id.Round
+					var roundTimeout time.Duration
+					if unsafe {
+						roundIDs, err = client.SendUnsafe(msg,
+							paramsUnsafe)
+						roundTimeout = paramsUnsafe.Timeout
+					} else {
+						roundIDs, _, _, err = client.SendE2E(msg,
+							paramsE2E)
+						roundTimeout = paramsE2E.Timeout
+					}
+					if err != nil {
+						jww.FATAL.Panicf("%+v", err)
+					}
+
+					// Construct the callback function which prints out the rounds' results
+					f := func(allRoundsSucceeded, timedOut bool,
+						rounds map[id.Round]api.RoundResult) {
+						printRoundResults(allRoundsSucceeded, timedOut, rounds, roundIDs, msg)
+					}
+
+					// Have the client report back the round results
+					err = errors.New("derp")
+					for j := 0; j < 5 && err != nil; j++ {
+						err = client.GetRoundResults(roundIDs, roundTimeout, f)
+					}
+
+					if err != nil {
+						jww.FATAL.Panicf("Message sending for send %d failed: %+v", i, err)
+					}
+				}(i)
+			}
+		}()
+
+		// Wait until message timeout or we receive enough then exit
+		// TODO: Actually check for how many messages we've received
+		expectedCnt := viper.GetUint("receiveCount")
+		receiveCnt := uint(0)
+		waitSecs := viper.GetUint("waitTimeout")
+		waitTimeout := time.Duration(waitSecs) * time.Second
+		done := false
+
+		for !done && expectedCnt != 0 {
+			timeoutTimer := time.NewTimer(waitTimeout)
+			select {
+			case <-timeoutTimer.C:
+				fmt.Println("Timed out!")
+				jww.ERROR.Printf("Timed out on message reception after %s!", waitTimeout)
+				done = true
+				break
+			case m := <-recvCh:
+				fmt.Printf("Message received: %s\n", string(
+					m.Payload))
+				//fmt.Printf("%s", m.Timestamp)
+				receiveCnt++
+				if receiveCnt == expectedCnt {
+					done = true
+					break
+				}
+			}
+		}
+
+		//wait an extra 5 seconds to make sure no messages were missed
+		done = false
+		timer := time.NewTimer(5 * time.Second)
+		for !done {
+			select {
+			case <-timer.C:
+				done = true
+				break
+			case m := <-recvCh:
+				fmt.Printf("Message received: %s\n", string(
+					m.Payload))
+				//fmt.Printf("%s", m.Timestamp)
+				receiveCnt++
+			}
+		}
+
+		jww.INFO.Printf("Received %d/%d Messages!", receiveCnt, expectedCnt)
+		fmt.Printf("Received %d\n", receiveCnt)
+		if roundsNotepad != nil {
+			roundsNotepad.INFO.Printf("\n%s", client.GetNetworkInterface().GetVerboseRounds())
+		}
+		wg.Wait()
+		err = client.StopNetworkFollower()
+		if err != nil {
+			jww.WARN.Printf(
+				"Failed to cleanly close threads: %+v\n",
+				err)
+		}
+
+	},
+}
+
+func loadProtoClient() *api.Client {
+	protoUserPath := viper.GetString("protoUserPath")
+
+	protoUserFile, err := utils.ReadFile(protoUserPath)
+	if err != nil {
+		jww.FATAL.Panicf("Failed to read proto user: %v", err)
+	}
+
+	pass := viper.GetString("password")
+	storeDir := viper.GetString("session")
+
+	netParams := params.GetDefaultNetwork()
+	netParams.E2EParams.MinKeys = uint16(viper.GetUint("e2eMinKeys"))
+	netParams.E2EParams.MaxKeys = uint16(viper.GetUint("e2eMaxKeys"))
+	netParams.E2EParams.NumRekeys = uint16(
+		viper.GetUint("e2eNumReKeys"))
+	netParams.ForceHistoricalRounds = viper.GetBool("forceHistoricalRounds")
+	netParams.FastPolling = viper.GetBool(" slowPolling")
+	netParams.ForceMessagePickupRetry = viper.GetBool("forceMessagePickupRetry")
+	if netParams.ForceMessagePickupRetry {
+		period := 3 * time.Second
+		jww.INFO.Printf("Setting Uncheck Round Period to %v", period)
+		netParams.UncheckRoundPeriod = period
+	}
+	netParams.VerboseRoundTracking = viper.GetBool("verboseRoundTracking")
+
+	// Load NDF
+	ndfPath := viper.GetString("ndf")
+	ndfJSON, err := ioutil.ReadFile(ndfPath)
+	if err != nil {
+		jww.FATAL.Panicf(err.Error())
+	}
+
+	client, err := api.LoginWithProtoClient(storeDir, []byte(pass),
+		protoUserFile, string(ndfJSON), netParams)
+	if err != nil {
+		jww.FATAL.Panicf("Failed to login: %v", err)
+	}
+
+	return client
+}
+
+// init is the initialization function for Cobra which defines commands
+// and flags.
+func init() {
+	// NOTE: The point of init() is to be declarative.
+	// There is one init in each sub command. Do not put variable declarations
+	// here, and ensure all the Flags are of the *P variety, unless there's a
+	// very good reason not to have them as local params to sub command."
+	cobra.OnInitialize(initConfig)
+
+	// Here you will define your flags and configuration settings.
+	// Cobra supports persistent flags, which, if defined here,
+	// will be global for your application.
+
+	// Proto user flags
+	protoCmd.PersistentFlags().String("protoUserPath", "protoUser.json",
+		"Path to proto user JSON file containing cryptographic primitives "+
+			"the client will load")
+	viper.BindPFlag("protoUserPath", protoCmd.Flags().Lookup("protoUserPath"))
+	protoCmd.PersistentFlags().String("protoUserOut", "protoUser.json",
+		"Path to which a normally constructed client "+
+			"will write proto user JSON file")
+	viper.BindPFlag("protoUserOut", protoCmd.Flags().Lookup("protoUserOut"))
+
+	protoCmd.PersistentFlags().UintP("logLevel", "v", 0,
+		"Verbose mode for debugging")
+	viper.BindPFlag("logLevel", protoCmd.PersistentFlags().Lookup("logLevel"))
+
+	protoCmd.PersistentFlags().Bool("verboseRoundTracking", false,
+		"Verbose round tracking, keeps track and prints all rounds the "+
+			"client was aware of while running. Defaults to false if not set.")
+	viper.BindPFlag("verboseRoundTracking", protoCmd.PersistentFlags().Lookup("verboseRoundTracking"))
+
+	protoCmd.PersistentFlags().StringP("session", "s",
+		"", "Sets the initial storage directory for "+
+			"client session data")
+	viper.BindPFlag("session", protoCmd.PersistentFlags().Lookup("session"))
+
+	protoCmd.PersistentFlags().StringP("writeContact", "w",
+		"-", "Write contact information, if any, to this file, "+
+			" defaults to stdout")
+	viper.BindPFlag("writeContact", protoCmd.PersistentFlags().Lookup(
+		"writeContact"))
+
+	protoCmd.PersistentFlags().StringP("password", "p", "",
+		"Password to the session file")
+	viper.BindPFlag("password", protoCmd.PersistentFlags().Lookup(
+		"password"))
+
+	protoCmd.PersistentFlags().StringP("ndf", "n", "ndf.json",
+		"Path to the network definition JSON file")
+	viper.BindPFlag("ndf", protoCmd.PersistentFlags().Lookup("ndf"))
+
+	protoCmd.PersistentFlags().StringP("log", "l", "-",
+		"Path to the log output path (- is stdout)")
+	viper.BindPFlag("log", protoCmd.PersistentFlags().Lookup("log"))
+
+	protoCmd.Flags().StringP("regcode", "", "",
+		"Identity code (optional)")
+	viper.BindPFlag("regcode", protoCmd.Flags().Lookup("regcode"))
+
+	protoCmd.PersistentFlags().StringP("message", "m", "",
+		"Message to send")
+	viper.BindPFlag("message", protoCmd.PersistentFlags().Lookup("message"))
+
+	protoCmd.Flags().UintP("sendid", "", 0,
+		"Use precanned user id (must be between 1 and 40, inclusive)")
+	viper.BindPFlag("sendid", protoCmd.Flags().Lookup("sendid"))
+
+	protoCmd.Flags().StringP("destid", "d", "0",
+		"ID to send message to (if below 40, will be precanned. Use "+
+			"'0x' or 'b64:' for hex and base64 representations)")
+	viper.BindPFlag("destid", protoCmd.Flags().Lookup("destid"))
+
+	protoCmd.Flags().StringP("destfile", "",
+		"", "Read this contact file for the destination id")
+	viper.BindPFlag("destfile", protoCmd.Flags().Lookup("destfile"))
+
+	protoCmd.Flags().UintP("sendCount",
+		"", 1, "The number of times to send the message")
+	viper.BindPFlag("sendCount", protoCmd.Flags().Lookup("sendCount"))
+	protoCmd.Flags().UintP("sendDelay",
+		"", 500, "The delay between sending the messages in ms")
+	viper.BindPFlag("sendDelay", protoCmd.Flags().Lookup("sendDelay"))
+
+	protoCmd.Flags().UintP("receiveCount",
+		"", 1, "How many messages we should wait for before quitting")
+	viper.BindPFlag("receiveCount", protoCmd.Flags().Lookup("receiveCount"))
+	protoCmd.Flags().UintP("waitTimeout", "", 15,
+		"The number of seconds to wait for messages to arrive")
+	viper.BindPFlag("waitTimeout",
+		protoCmd.Flags().Lookup("waitTimeout"))
+
+	protoCmd.Flags().BoolP("unsafe", "", false,
+		"Send raw, unsafe messages without e2e encryption.")
+	viper.BindPFlag("unsafe", protoCmd.Flags().Lookup("unsafe"))
+
+	protoCmd.Flags().BoolP("unsafe-channel-creation", "", false,
+		"Turns off the user identity authenticated channel check, "+
+			"automatically approving authenticated channels")
+	viper.BindPFlag("unsafe-channel-creation",
+		protoCmd.Flags().Lookup("unsafe-channel-creation"))
+
+	protoCmd.Flags().BoolP("accept-channel", "", false,
+		"Accept the channel request for the corresponding recipient ID")
+	viper.BindPFlag("accept-channel",
+		protoCmd.Flags().Lookup("accept-channel"))
+
+	protoCmd.Flags().Bool("delete-channel", false,
+		"Delete the channel information for the corresponding recipient ID")
+	viper.BindPFlag("delete-channel",
+		protoCmd.Flags().Lookup("delete-channel"))
+
+	protoCmd.Flags().BoolP("send-auth-request", "", false,
+		"Send an auth request to the specified destination and wait"+
+			"for confirmation")
+	viper.BindPFlag("send-auth-request",
+		protoCmd.Flags().Lookup("send-auth-request"))
+	protoCmd.Flags().UintP("auth-timeout", "", 120,
+		"The number of seconds to wait for an authentication channel"+
+			"to confirm")
+	viper.BindPFlag("auth-timeout",
+		protoCmd.Flags().Lookup("auth-timeout"))
+
+	protoCmd.Flags().BoolP("forceHistoricalRounds", "", false,
+		"Force all rounds to be sent to historical round retrieval")
+	viper.BindPFlag("forceHistoricalRounds",
+		protoCmd.Flags().Lookup("forceHistoricalRounds"))
+
+	// Network params
+	protoCmd.Flags().BoolP("slowPolling", "", false,
+		"Enables polling for unfiltered network updates with RSA signatures")
+	viper.BindPFlag("slowPolling",
+		protoCmd.Flags().Lookup("slowPolling"))
+	protoCmd.Flags().Bool("forceMessagePickupRetry", false,
+		"Enable a mechanism which forces a 50% chance of no message pickup, "+
+			"instead triggering the message pickup retry mechanism")
+	viper.BindPFlag("forceMessagePickupRetry",
+		protoCmd.Flags().Lookup("forceMessagePickupRetry"))
+
+	// E2E Params
+	defaultE2EParams := params.GetDefaultE2ESessionParams()
+	protoCmd.Flags().UintP("e2eMinKeys",
+		"", uint(defaultE2EParams.MinKeys),
+		"Minimum number of keys used before requesting rekey")
+	viper.BindPFlag("e2eMinKeys", protoCmd.Flags().Lookup("e2eMinKeys"))
+	protoCmd.Flags().UintP("e2eMaxKeys",
+		"", uint(defaultE2EParams.MaxKeys),
+		"Max keys used before blocking until a rekey completes")
+	viper.BindPFlag("e2eMaxKeys", protoCmd.Flags().Lookup("e2eMaxKeys"))
+	protoCmd.Flags().UintP("e2eNumReKeys",
+		"", uint(defaultE2EParams.NumRekeys),
+		"Number of rekeys reserved for rekey operations")
+	viper.BindPFlag("e2eNumReKeys", protoCmd.Flags().Lookup("e2eNumReKeys"))
+
+	protoCmd.Flags().String("profile-cpu", "",
+		"Enable cpu profiling to this file")
+	viper.BindPFlag("profile-cpu", protoCmd.Flags().Lookup("profile-cpu"))
+}
diff --git a/cmd/root.go b/cmd/root.go
index 23416872ee7e210fbe6b886bee12788a17d0e1bc..9cd04f06c83547e04d567d0559be4e5fce403a63 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -893,6 +893,17 @@ func init() {
 	rootCmd.Flags().String("profile-cpu", "",
 		"Enable cpu profiling to this file")
 	viper.BindPFlag("profile-cpu", rootCmd.Flags().Lookup("profile-cpu"))
+
+	// Proto user flags
+	rootCmd.PersistentFlags().String("protoUserPath", "protoUser.json",
+		"Path to proto user JSON file containing cryptographic primitives "+
+			"the client will load")
+	viper.BindPFlag("protoUserPath", rootCmd.Flags().Lookup("protoUserPath"))
+	rootCmd.PersistentFlags().String("protoUserOut", "protoUser.json",
+		"Path to which a normally constructed client "+
+			"will write proto user JSON file")
+	viper.BindPFlag("protoUserOut", rootCmd.Flags().Lookup("protoUserOut"))
+
 }
 
 // initConfig reads in config file and ENV variables if set.