diff --git a/README.md b/README.md
index a5041e876b23c787616d2f260830f2aca19de0d3..cb6d69e09ad7aab6c7676b2aae8c310539d286b4 100644
--- a/README.md
+++ b/README.md
@@ -145,59 +145,49 @@ 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
+  proto       Load client with a proto client JSON file.
+  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 7919c62b869f1befb9a7567022bdc51475e8b4c2..3cedff02094d341e55735773867ae1f1e04fbbea 100644
--- a/api/client.go
+++ b/api/client.go
@@ -8,6 +8,7 @@
 package api
 
 import (
+	"encoding/json"
 	"github.com/pkg/errors"
 	jww "github.com/spf13/jwalterweatherman"
 	"gitlab.com/elixxir/client/auth"
@@ -74,7 +75,8 @@ type Client struct {
 // with the network. Note that this does not register a username/identity, but
 // merely creates a new cryptographic identity for adding such information
 // at a later date.
-func NewClient(ndfJSON, storageDir string, password []byte, registrationCode string) error {
+func NewClient(ndfJSON, storageDir string, password []byte,
+	registrationCode string) error {
 	jww.INFO.Printf("NewClient(dir: %s)", storageDir)
 	// Use fastRNG for RNG ops (AES fortuna based RNG using system RNG)
 	rngStreamGen := fastRNG.NewStreamGenerator(12, 1024, csprng.NewSystemRNG)
@@ -90,7 +92,7 @@ func NewClient(ndfJSON, storageDir string, password []byte, registrationCode str
 	protoUser := createNewUser(rngStreamGen, cmixGrp, e2eGrp)
 	jww.DEBUG.Printf("User generation took: %s", time.Now().Sub(start))
 
-	err = checkVersionAndSetupStorage(def, storageDir, password, protoUser,
+	_, err = checkVersionAndSetupStorage(def, storageDir, password, protoUser,
 		cmixGrp, e2eGrp, rngStreamGen, false, registrationCode)
 	if err != nil {
 		return err
@@ -105,7 +107,8 @@ func NewClient(ndfJSON, storageDir string, password []byte, registrationCode str
 // with the network. Note that this does not register a username/identity, but
 // merely creates a new cryptographic identity for adding such information
 // at a later date.
-func NewPrecannedClient(precannedID uint, defJSON, storageDir string, password []byte) error {
+func NewPrecannedClient(precannedID uint, defJSON, storageDir string,
+	password []byte) error {
 	jww.INFO.Printf("NewPrecannedClient()")
 	// Use fastRNG for RNG ops (AES fortuna based RNG using system RNG)
 	rngStreamGen := fastRNG.NewStreamGenerator(12, 1024, csprng.NewSystemRNG)
@@ -120,7 +123,7 @@ func NewPrecannedClient(precannedID uint, defJSON, storageDir string, password [
 
 	protoUser := createPrecannedUser(precannedID, rngStream, cmixGrp, e2eGrp)
 
-	err = checkVersionAndSetupStorage(def, storageDir, password, protoUser,
+	_, err = checkVersionAndSetupStorage(def, storageDir, password, protoUser,
 		cmixGrp, e2eGrp, rngStreamGen, true, "")
 	if err != nil {
 		return err
@@ -134,7 +137,8 @@ func NewPrecannedClient(precannedID uint, defJSON, storageDir string, password [
 // with the network. Note that this does not register a username/identity, but
 // merely creates a new cryptographic identity for adding such information
 // at a later date.
-func NewVanityClient(ndfJSON, storageDir string, password []byte, registrationCode string, userIdPrefix string) error {
+func NewVanityClient(ndfJSON, storageDir string, password []byte,
+	registrationCode string, userIdPrefix string) error {
 	jww.INFO.Printf("NewVanityClient()")
 	// Use fastRNG for RNG ops (AES fortuna based RNG using system RNG)
 	rngStreamGen := fastRNG.NewStreamGenerator(12, 1024, csprng.NewSystemRNG)
@@ -149,7 +153,7 @@ func NewVanityClient(ndfJSON, storageDir string, password []byte, registrationCo
 
 	protoUser := createNewVanityUser(rngStream, cmixGrp, e2eGrp, userIdPrefix)
 
-	err = checkVersionAndSetupStorage(def, storageDir, password, protoUser,
+	_, err = checkVersionAndSetupStorage(def, storageDir, password, protoUser,
 		cmixGrp, e2eGrp, rngStreamGen, false, registrationCode)
 	if err != nil {
 		return err
@@ -195,6 +199,55 @@ func OpenClient(storageDir string, password []byte, parameters params.Network) (
 	return c, nil
 }
 
+// NewProtoClient_Unsafe initializes a client object from a JSON containing
+// predefined cryptographic which defines a user. This is designed for some
+// specific deployment procedures and is generally unsafe.
+func NewProtoClient_Unsafe(ndfJSON, storageDir string, password,
+	protoClientJSON []byte) error {
+	jww.INFO.Printf("NewProtoClient_Unsafe")
+
+	// Use fastRNG for RNG ops (AES fortuna based RNG using system RNG)
+	rngStreamGen := fastRNG.NewStreamGenerator(12, 3, csprng.NewSystemRNG)
+
+	// Parse the NDF
+	def, err := parseNDF(ndfJSON)
+	if err != nil {
+		return err
+	}
+
+	cmixGrp, e2eGrp := decodeGroups(def)
+
+	// Pull the proto user from the JSON
+	protoUser := &user.Proto{}
+	err = json.Unmarshal(protoClientJSON, protoUser)
+	if err != nil {
+		return err
+	}
+
+	// Initialize a user object for storage set up
+	usr := user.NewUserFromProto(protoUser)
+
+	// Set up storage
+	storageSess, err := checkVersionAndSetupStorage(def, storageDir, password, usr,
+		cmixGrp, e2eGrp, rngStreamGen, false, protoUser.RegCode)
+	if err != nil {
+		return err
+	}
+
+	// Set registration values in storage
+	storageSess.User().SetReceptionRegistrationValidationSignature(protoUser.ReceptionRegValidationSig)
+	storageSess.User().SetTransmissionRegistrationValidationSignature(protoUser.TransmissionRegValidationSig)
+	storageSess.User().SetRegistrationTimestamp(protoUser.RegistrationTimestamp)
+
+	//move the registration state to indicate registered with registration on proto client
+	err = storageSess.ForwardRegistrationStatus(storage.PermissioningComplete)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
 // Login initializes a client object from existing storage.
 func Login(storageDir string, password []byte, parameters params.Network) (*Client, error) {
 	jww.INFO.Printf("Login()")
@@ -236,7 +289,8 @@ func Login(storageDir string, password []byte, parameters params.Network) (*Clie
 		hp.KaClientOpts.Time = time.Duration(math.MaxInt64)
 		hp.AuthEnabled = false
 		hp.MaxRetries = 5
-		_, err = c.comms.AddHost(&id.NotificationBot, def.Notification.Address, []byte(def.Notification.TlsCertificate), hp)
+		_, err = c.comms.AddHost(&id.NotificationBot, def.Notification.Address,
+			[]byte(def.Notification.TlsCertificate), hp)
 		if err != nil {
 			jww.WARN.Printf("Failed adding host for notifications: %+v", err)
 		}
@@ -382,7 +436,9 @@ func (c *Client) registerFollower() error {
 	}
 
 	//register the core follower service
-	err = c.followerServices.add(func() (stoppable.Stoppable, error) { return c.network.Follow(cer) })
+	err = c.followerServices.add(func() (stoppable.Stoppable, error) {
+		return c.network.Follow(cer)
+	})
 	if err != nil {
 		return errors.WithMessage(err, "Failed to start following "+
 			"the network")
@@ -698,13 +754,15 @@ func decodeGroups(ndf *ndf.NetworkDefinition) (cmixGrp, e2eGrp *cyclic.Group) {
 
 // checkVersionAndSetupStorage is common code shared by NewClient, NewPrecannedClient and NewVanityClient
 // it checks client version and creates a new storage for user data
-func checkVersionAndSetupStorage(def *ndf.NetworkDefinition, storageDir string, password []byte,
-	protoUser user.User, cmixGrp, e2eGrp *cyclic.Group, rngStreamGen *fastRNG.StreamGenerator,
-	isPrecanned bool, registrationCode string) error {
+func checkVersionAndSetupStorage(def *ndf.NetworkDefinition,
+	storageDir string, password []byte,
+	protoUser user.User,
+	cmixGrp, e2eGrp *cyclic.Group, rngStreamGen *fastRNG.StreamGenerator,
+	isPrecanned bool, registrationCode string) (*storage.Session, error) {
 	// Get current client version
 	currentVersion, err := version.ParseVersion(SEMVER)
 	if err != nil {
-		return errors.WithMessage(err, "Could not parse version string.")
+		return nil, errors.WithMessage(err, "Could not parse version string.")
 	}
 
 	// Create Storage
@@ -712,7 +770,7 @@ func checkVersionAndSetupStorage(def *ndf.NetworkDefinition, storageDir string,
 	storageSess, err := storage.New(storageDir, passwordStr, protoUser,
 		currentVersion, cmixGrp, e2eGrp, rngStreamGen)
 	if err != nil {
-		return err
+		return nil, err
 	}
 
 	// Save NDF to be used in the future
@@ -736,9 +794,9 @@ func checkVersionAndSetupStorage(def *ndf.NetworkDefinition, storageDir string,
 	}, protoUser.ReceptionID)
 
 	if err != nil {
-		return errors.WithMessage(err, "Failed to denote state "+
+		return nil, errors.WithMessage(err, "Failed to denote state "+
 			"change in session")
 	}
 
-	return nil
+	return storageSess, nil
 }
diff --git a/api/notifications.go b/api/notifications.go
index 2a7d1b7c107128360f89930c8bf0bcb16dfc54de..58e97190e469a7d21d9175b9b84ddaff89b75e2c 100644
--- a/api/notifications.go
+++ b/api/notifications.go
@@ -43,7 +43,7 @@ func (c *Client) RegisterForNotifications(token string) error {
 			TransmissionSalt:      c.GetUser().TransmissionSalt,
 			TransmissionRsaSig:    c.GetStorage().User().GetTransmissionRegistrationValidationSignature(),
 			IIDTransmissionRsaSig: sig,
-			RegistrationTimestamp: c.GetUser().RegistrationTimestamp.UnixNano(),
+			RegistrationTimestamp: c.GetUser().RegistrationTimestamp,
 		})
 	if err != nil {
 		err := errors.Errorf(
diff --git a/api/permissioning.go b/api/permissioning.go
index ef94e61daf315472ecde87eddc360335c08b445d..85aac34b93f333f4994c49b1dac1ec868965807a 100644
--- a/api/permissioning.go
+++ b/api/permissioning.go
@@ -8,7 +8,9 @@
 package api
 
 import (
+	"encoding/json"
 	"github.com/pkg/errors"
+	"gitlab.com/elixxir/client/interfaces/user"
 	"gitlab.com/elixxir/client/storage"
 )
 
@@ -47,3 +49,41 @@ func (c *Client) registerWithPermissioning() error {
 	}
 	return nil
 }
+
+// ConstructProtoUerFile is a helper function which is used for proto client testing.
+// This is used for development testing.
+func (c *Client) ConstructProtoUerFile() ([]byte, error) {
+
+	//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,
+		ReceptionSalt:                c.GetUser().ReceptionSalt,
+		ReceptionRSA:                 c.GetUser().ReceptionRSA,
+		Precanned:                    c.GetUser().Precanned,
+		RegistrationTimestamp:        c.GetUser().RegistrationTimestamp,
+		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(),
+		E2eDhPublicKey:               c.GetStorage().E2e().GetDHPublicKey(),
+	}
+
+	jsonBytes, err := json.Marshal(Usr)
+	if err != nil {
+		return nil, errors.WithMessage(err, "failed to register with "+
+			"permissioning")
+	}
+
+	return jsonBytes, nil
+}
diff --git a/api/user.go b/api/user.go
index ef8eb1092ee61e8e766c00d2979cb4c20d108d82..1b22a0877235636c55bbd2be922cddfcf4fe2b39 100644
--- a/api/user.go
+++ b/api/user.go
@@ -36,6 +36,62 @@ func createNewUser(rng *fastRNG.StreamGenerator, cmix, e2e *cyclic.Group) user.U
 
 	var cMixKeyBytes, e2eKeyBytes, transmissionSalt, receptionSalt []byte
 
+	cMixKeyBytes, e2eKeyBytes, transmissionSalt, receptionSalt,
+		transmissionRsaKey, receptionRsaKey = createDhKeys(rng, cmix, e2e)
+
+	// Salt, UID, etc gen
+	stream := rng.GetStream()
+	transmissionSalt = make([]byte, SaltSize)
+
+	n, err := stream.Read(transmissionSalt)
+
+	if err != nil {
+		jww.FATAL.Panicf(err.Error())
+	}
+	if n != SaltSize {
+		jww.FATAL.Panicf("transmissionSalt size too small: %d", n)
+	}
+
+	receptionSalt = make([]byte, SaltSize)
+
+	n, err = stream.Read(receptionSalt)
+
+	if err != nil {
+		jww.FATAL.Panicf(err.Error())
+	}
+	if n != SaltSize {
+		jww.FATAL.Panicf("transmissionSalt size too small: %d", n)
+	}
+
+	stream.Close()
+
+	transmissionID, err := xx.NewID(transmissionRsaKey.GetPublic(), transmissionSalt, id.User)
+	if err != nil {
+		jww.FATAL.Panicf(err.Error())
+	}
+
+	receptionID, err := xx.NewID(receptionRsaKey.GetPublic(), receptionSalt, id.User)
+	if err != nil {
+		jww.FATAL.Panicf(err.Error())
+	}
+
+	return user.User{
+		TransmissionID:   transmissionID.DeepCopy(),
+		TransmissionSalt: transmissionSalt,
+		TransmissionRSA:  transmissionRsaKey,
+		ReceptionID:      receptionID.DeepCopy(),
+		ReceptionSalt:    receptionSalt,
+		ReceptionRSA:     receptionRsaKey,
+		Precanned:        false,
+		CmixDhPrivateKey: cmix.NewIntFromBytes(cMixKeyBytes),
+		E2eDhPrivateKey:  e2e.NewIntFromBytes(e2eKeyBytes),
+	}
+}
+
+func createDhKeys(rng *fastRNG.StreamGenerator,
+	cmix, e2e *cyclic.Group) (cMixKeyBytes, e2eKeyBytes,
+	transmissionSalt, receptionSalt []byte,
+	transmissionRsaKey, receptionRsaKey *rsa.PrivateKey) {
 	wg := sync.WaitGroup{}
 
 	wg.Add(4)
@@ -93,53 +149,8 @@ func createNewUser(rng *fastRNG.StreamGenerator, cmix, e2e *cyclic.Group) user.U
 	}()
 	wg.Wait()
 
-	// Salt, UID, etc gen
-	stream := rng.GetStream()
-	transmissionSalt = make([]byte, SaltSize)
-
-	n, err := stream.Read(transmissionSalt)
-
-	if err != nil {
-		jww.FATAL.Panicf(err.Error())
-	}
-	if n != SaltSize {
-		jww.FATAL.Panicf("transmissionSalt size too small: %d", n)
-	}
-
-	receptionSalt = make([]byte, SaltSize)
-
-	n, err = stream.Read(receptionSalt)
-
-	if err != nil {
-		jww.FATAL.Panicf(err.Error())
-	}
-	if n != SaltSize {
-		jww.FATAL.Panicf("transmissionSalt size too small: %d", n)
-	}
-
-	stream.Close()
-
-	transmissionID, err := xx.NewID(transmissionRsaKey.GetPublic(), transmissionSalt, id.User)
-	if err != nil {
-		jww.FATAL.Panicf(err.Error())
-	}
-
-	receptionID, err := xx.NewID(receptionRsaKey.GetPublic(), receptionSalt, id.User)
-	if err != nil {
-		jww.FATAL.Panicf(err.Error())
-	}
+	return
 
-	return user.User{
-		TransmissionID:   transmissionID.DeepCopy(),
-		TransmissionSalt: transmissionSalt,
-		TransmissionRSA:  transmissionRsaKey,
-		ReceptionID:      receptionID.DeepCopy(),
-		ReceptionSalt:    receptionSalt,
-		ReceptionRSA:     receptionRsaKey,
-		Precanned:        false,
-		CmixDhPrivateKey: cmix.NewIntFromBytes(cMixKeyBytes),
-		E2eDhPrivateKey:  e2e.NewIntFromBytes(e2eKeyBytes),
-	}
 }
 
 // TODO: Add precanned user code structures here.
diff --git a/cmd/root.go b/cmd/root.go
index 23416872ee7e210fbe6b886bee12788a17d0e1bc..a2c9db19265e702aec3c5d268185cd2156b3b8dd 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -23,6 +23,7 @@ import (
 	"gitlab.com/elixxir/client/switchboard"
 	"gitlab.com/elixxir/crypto/contact"
 	"gitlab.com/xx_network/primitives/id"
+	"gitlab.com/xx_network/primitives/utils"
 	"io/ioutil"
 	"log"
 	"os"
@@ -382,6 +383,8 @@ func createClient() *api.Client {
 	regCode := viper.GetString("regcode")
 	precannedID := viper.GetUint("sendid")
 	userIDprefix := viper.GetString("userid-prefix")
+	protoUserPath := viper.GetString("protoUserPath")
+
 	//create a new client if none exist
 	if _, err := os.Stat(storeDir); os.IsNotExist(err) {
 		// Load NDF
@@ -394,14 +397,19 @@ func createClient() *api.Client {
 		if precannedID != 0 {
 			err = api.NewPrecannedClient(precannedID,
 				string(ndfJSON), storeDir, []byte(pass))
-		} else {
-			if userIDprefix != "" {
-				err = api.NewVanityClient(string(ndfJSON), storeDir,
-					[]byte(pass), regCode, userIDprefix)
-			} else {
-				err = api.NewClient(string(ndfJSON), storeDir,
-					[]byte(pass), regCode)
+		} else if protoUserPath != "" {
+			protoUserJson, err := utils.ReadFile(protoUserPath)
+			if err != nil {
+				jww.FATAL.Panicf("%v", err)
 			}
+			err = api.NewProtoClient_Unsafe(string(ndfJSON), storeDir,
+				[]byte(pass), protoUserJson)
+		} else if userIDprefix != "" {
+			err = api.NewVanityClient(string(ndfJSON), storeDir,
+				[]byte(pass), regCode, userIDprefix)
+		} else {
+			err = api.NewClient(string(ndfJSON), storeDir,
+				[]byte(pass), regCode)
 		}
 
 		if err != nil {
@@ -431,7 +439,7 @@ func initClient() *api.Client {
 
 	pass := viper.GetString("password")
 	storeDir := viper.GetString("session")
-
+	jww.DEBUG.Printf("sessionDur: %v", storeDir)
 	netParams := params.GetDefaultNetwork()
 	netParams.E2EParams.MinKeys = uint16(viper.GetUint("e2eMinKeys"))
 	netParams.E2EParams.MaxKeys = uint16(viper.GetUint("e2eMaxKeys"))
@@ -453,6 +461,20 @@ func initClient() *api.Client {
 		jww.FATAL.Panicf("%+v", err)
 	}
 
+	if protoUser := viper.GetString("protoUserOut"); protoUser != "" {
+
+		jsonBytes, err := client.ConstructProtoUerFile()
+		if err != nil {
+			jww.FATAL.Panicf("Failed to construct proto user file: %v", err)
+		}
+
+		err = utils.WriteFileDef(protoUser, jsonBytes)
+		if err != nil {
+			jww.FATAL.Panicf("Failed to write proto user to file: %v", err)
+		}
+
+	}
+
 	return client
 }
 
@@ -823,30 +845,30 @@ func init() {
 	rootCmd.Flags().UintP("receiveCount",
 		"", 1, "How many messages we should wait for before quitting")
 	viper.BindPFlag("receiveCount", rootCmd.Flags().Lookup("receiveCount"))
-	rootCmd.Flags().UintP("waitTimeout", "", 15,
+	rootCmd.PersistentFlags().UintP("waitTimeout", "", 15,
 		"The number of seconds to wait for messages to arrive")
 	viper.BindPFlag("waitTimeout",
-		rootCmd.Flags().Lookup("waitTimeout"))
+		rootCmd.PersistentFlags().Lookup("waitTimeout"))
 
 	rootCmd.Flags().BoolP("unsafe", "", false,
 		"Send raw, unsafe messages without e2e encryption.")
 	viper.BindPFlag("unsafe", rootCmd.Flags().Lookup("unsafe"))
 
-	rootCmd.Flags().BoolP("unsafe-channel-creation", "", false,
+	rootCmd.PersistentFlags().BoolP("unsafe-channel-creation", "", false,
 		"Turns off the user identity authenticated channel check, "+
 			"automatically approving authenticated channels")
 	viper.BindPFlag("unsafe-channel-creation",
-		rootCmd.Flags().Lookup("unsafe-channel-creation"))
+		rootCmd.PersistentFlags().Lookup("unsafe-channel-creation"))
 
 	rootCmd.Flags().BoolP("accept-channel", "", false,
 		"Accept the channel request for the corresponding recipient ID")
 	viper.BindPFlag("accept-channel",
 		rootCmd.Flags().Lookup("accept-channel"))
 
-	rootCmd.Flags().Bool("delete-channel", false,
+	rootCmd.PersistentFlags().Bool("delete-channel", false,
 		"Delete the channel information for the corresponding recipient ID")
 	viper.BindPFlag("delete-channel",
-		rootCmd.Flags().Lookup("delete-channel"))
+		rootCmd.PersistentFlags().Lookup("delete-channel"))
 
 	rootCmd.Flags().BoolP("send-auth-request", "", false,
 		"Send an auth request to the specified destination and wait"+
@@ -893,6 +915,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.Flags().String("protoUserPath", "",
+		"Path to proto user JSON file containing cryptographic primitives "+
+			"the client will load")
+	viper.BindPFlag("protoUserPath", rootCmd.Flags().Lookup("protoUserPath"))
+	rootCmd.Flags().String("protoUserOut", "",
+		"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.
diff --git a/interfaces/user/proto.go b/interfaces/user/proto.go
new file mode 100644
index 0000000000000000000000000000000000000000..6b9cf1e3a6b338a08abd386b92772314b5f499e3
--- /dev/null
+++ b/interfaces/user/proto.go
@@ -0,0 +1,33 @@
+package user
+
+import (
+	"gitlab.com/elixxir/crypto/cyclic"
+	"gitlab.com/xx_network/crypto/signature/rsa"
+	"gitlab.com/xx_network/primitives/id"
+)
+
+type Proto struct {
+	//General Identity
+	TransmissionID   *id.ID
+	TransmissionSalt []byte
+	TransmissionRSA  *rsa.PrivateKey
+	ReceptionID      *id.ID
+	ReceptionSalt    []byte
+	ReceptionRSA     *rsa.PrivateKey
+	Precanned        bool
+	// Timestamp in which user has registered with the network
+	RegistrationTimestamp int64
+
+	RegCode string
+
+	TransmissionRegValidationSig []byte
+	ReceptionRegValidationSig    []byte
+
+	//cmix Identity
+	CmixDhPrivateKey *cyclic.Int
+	CmixDhPublicKey  *cyclic.Int
+
+	//e2e Identity
+	E2eDhPrivateKey *cyclic.Int
+	E2eDhPublicKey  *cyclic.Int
+}
diff --git a/interfaces/user/user.go b/interfaces/user/user.go
index 8788856b494cf386dbd903510680935f2b320daa..56f260d03527ce2c605c5fe781f3be3935cc106c 100644
--- a/interfaces/user/user.go
+++ b/interfaces/user/user.go
@@ -13,7 +13,6 @@ import (
 	"gitlab.com/elixxir/primitives/fact"
 	"gitlab.com/xx_network/crypto/signature/rsa"
 	"gitlab.com/xx_network/primitives/id"
-	"time"
 )
 
 type User struct {
@@ -26,7 +25,7 @@ type User struct {
 	ReceptionRSA     *rsa.PrivateKey
 	Precanned        bool
 	// Timestamp in which user has registered with the network
-	RegistrationTimestamp time.Time
+	RegistrationTimestamp int64
 
 	//cmix Identity
 	CmixDhPrivateKey *cyclic.Int
@@ -44,3 +43,20 @@ func (u User) GetContact() contact.Contact {
 		Facts:    make([]fact.Fact, 0),
 	}
 }
+
+func NewUserFromProto(proto *Proto) User {
+	return User{
+		TransmissionID:        proto.TransmissionID,
+		TransmissionSalt:      proto.TransmissionSalt,
+		TransmissionRSA:       proto.TransmissionRSA,
+		ReceptionID:           proto.ReceptionID,
+		ReceptionSalt:         proto.ReceptionSalt,
+		ReceptionRSA:          proto.ReceptionRSA,
+		Precanned:             proto.Precanned,
+		RegistrationTimestamp: proto.RegistrationTimestamp,
+		CmixDhPrivateKey:      proto.CmixDhPrivateKey,
+		CmixDhPublicKey:       proto.CmixDhPublicKey,
+		E2eDhPrivateKey:       proto.E2eDhPrivateKey,
+		E2eDhPublicKey:        proto.E2eDhPublicKey,
+	}
+}
diff --git a/network/message/sendCmixUtils.go b/network/message/sendCmixUtils.go
index 30fb94088c1b339462c87405e7b10e221299f3a2..b889b0ad86a7a09e7df10ab1ea72bd03541a4057 100644
--- a/network/message/sendCmixUtils.go
+++ b/network/message/sendCmixUtils.go
@@ -146,7 +146,7 @@ func buildSlotMessage(msg format.Message, recipient *id.ID, target *id.ID,
 	if param.IdentityPreimage != nil {
 		preimage = param.IdentityPreimage
 		jww.INFO.Printf("Sending to %s with override preimage %v", recipient, preimage)
-	}else{
+	} else {
 		preimage = preimage2.MakeDefault(recipient)
 		jww.INFO.Printf("Sending to %s with default preimage %v", recipient, preimage)
 	}
diff --git a/storage/session.go b/storage/session.go
index 4a380b7911699490b0d6437beaa84c911b3ba080..8ee2b43d3119b222612389685360d53b26bc9abf 100644
--- a/storage/session.go
+++ b/storage/session.go
@@ -94,7 +94,7 @@ func New(baseDir, password string, u userInterface.User, currentVersion version.
 
 	s, err := initStore(baseDir, password)
 	if err != nil {
-		return nil, errors.WithMessage(err, "Failed to create session")
+		return nil, errors.WithMessagef(err, "Failed to create session for %s", baseDir)
 	}
 
 	err = s.newRegStatus()
@@ -103,7 +103,8 @@ func New(baseDir, password string, u userInterface.User, currentVersion version.
 			"Create new session")
 	}
 
-	s.user, err = user.NewUser(s.kv, u.TransmissionID, u.ReceptionID, u.TransmissionSalt, u.ReceptionSalt, u.TransmissionRSA, u.ReceptionRSA, u.Precanned)
+	s.user, err = user.NewUser(s.kv, u.TransmissionID, u.ReceptionID, u.TransmissionSalt,
+		u.ReceptionSalt, u.TransmissionRSA, u.ReceptionRSA, u.Precanned)
 	if err != nil {
 		return nil, errors.WithMessage(err, "Failed to create user")
 	}
diff --git a/storage/user.go b/storage/user.go
index 975b574a2e9678a31eff7f0598b396ab6427a6af..313741471ee5e3fefa12349d2e9b441352b983e1 100644
--- a/storage/user.go
+++ b/storage/user.go
@@ -16,9 +16,9 @@ func (s *Session) GetUser() user.User {
 	return user.User{
 		TransmissionID:        ci.GetTransmissionID().DeepCopy(),
 		TransmissionSalt:      copySlice(ci.GetTransmissionSalt()),
-		TransmissionRSA:       ci.GetReceptionRSA(),
+		TransmissionRSA:       ci.GetTransmissionRSA(),
 		ReceptionID:           ci.GetReceptionID().DeepCopy(),
-		RegistrationTimestamp: s.user.GetRegistrationTimestamp(),
+		RegistrationTimestamp: s.user.GetRegistrationTimestamp().UnixNano(),
 		ReceptionSalt:         copySlice(ci.GetReceptionSalt()),
 		ReceptionRSA:          ci.GetReceptionRSA(),
 		Precanned:             ci.IsPrecanned(),