diff --git a/README.md b/README.md
index 7759d94a2da36e8554586f60d8ec1b5a9407306c..8749ea91c78eebd45698d47a7d3a1a38dec11e51 100644
--- a/README.md
+++ b/README.md
@@ -41,27 +41,36 @@ Optional args:
 
 |Long flag|Short flag|Effect|Example|
 |---|---|---|---|
-|--gwaddresses|-g|Addresses:port of the gateways to connect to, separated by commas (Overrides config file)|-g localhost:8443,localhost:8444|
-|--destid|-d|ID of the user to send messages to|-d 6|
-|--message|-m|Text message to send|-m "let's both have a good day"|
-|--verbose|-v|Prints more logging messages for debugging|-v|
-|--version|-V|Show the generated version information. Run `$ go generate cmd/version.go` if the information is out of date.|--version|
-|--sessionfile|-f|File path for storing the session. If not specified, the session will be stored in RAM and won't persist.|-f mySuperCoolSessionFile|
-|--noBlockingTransmission| |Disables transmission rate limiting (useful for dummy client)|--noBlockingTransmission|
-|--help|-h|Prints a help message with all of these flags|-h|
-|--gwcertpath|-c|Enables TLS by passing in path to the gateway certificate file|-c "~/Documents/gateway.cert"|
-|--registrationcertpath|-r|Enables TLS by passing in path to the registration server certificate file|-r "~/Documents/registration.cert"|
-|--registrationaddr|-a|Address:Port for connecting to the registration server|-a "localhost:11420"|
-|--dummyfrequency| |How often dummy messages should be sent per second. This flag is likely to be replaced when we implement better dummy message sending.|--dummyfrequency 0.5|
-|--end2end| |Send messages with E2E encryption to destination user|--end2end|
-|--keyParams| |Set E2E key generation parameters. Pass values in comma separated list, with the following order: MinKeys,MaxKeys,NumRekeys,TTLScalar,MinNumKeys|--keyParams 100,200,32,1.2,50|
-|--email|-E|Email to register for User Discovery (default "default@default.com")||
-|--nick| |Nickname to register for User Discovery (default "Default")||
+|--message|-ms|Message to send|-m "top of the morning"|
+|--messageTimeout|-t|The number of seconds to wait for 'waitForMessages' messages to arrive (default 45)|-t 42|
+|--ndf|-n|Path to the network definition JSON file (default "ndf.json")| -n "ndf.json"|
+|--SearchForUser| |Sets the email to search for to find a user with user discovery| -s "david@chaum.com|
+|--dest64| |Sets the destination user id encoded in base 64| --dest64 "yCvV6AsEK3l+45Gn4awBJ4lpb+hT2sO6yzxjeraRor0="|
+|--destid|-d|ID to send message to| -d 69|
+|--email|-E|Email to register for User Discovery| -e "david@chaum.com"|
+|--end2end| |Send messages with E2E encryption to destination user. Must have found each other via UDB first| -end2end|
+|--help| |help for client| --help|
+|--keyParams| |Define key generation parameters. Pass values in comma separated list in the following order: MinKeys,MaxKeys,NumRekeys,TTLScalar,MinNumKeys| |
 |--ndfPubKey|-p|Path to the public key for the network definition JSON file|
-|--ndf|-n|Path to the network definition JSON file|
-|--skipNDFVerification| |Specifies if the NDF should be loaded without the signature (default false)|
-|--ndfRegistration| |Overwrite the Registration values for the NDF|
-|--ndfUDB| |Overwrite the UDB values for the NDF|
+|--nick| |Nickname to register for User Discovery (default "Default")| --nick "zezima"|
+|--noBlockingTransmission| |Sets if transmitting messages blocks or not.  Defaults to true if unset.|--noBlockingTransmission|
+|--noTLS| |Set to ignore TLS. Connections will fail if the network requires TLS. For debugging|--noTLS|
+|--privateKey| |The path for a PEM encoded private key which will be used to create the user|--privateKey "key.pem"|
+|--rateLimiting| |Sets the amount of time, in ms, that the client waits between sending messages.  set to zero to disable.  Automatically disabled if 'blockingTransmission' is false (default 1000)| --rateLimiting 100|
+|--regcode string|-r|Registration Code with the registration server |--regcode "AAAA"|
+|--sessionfile|-f|Passes a file path for loading a session.  If the file doesnt exist the code will register the user and store it there.  If not passed the session will be stored to ram and lost when the cli finishes| -s "user.session"|
+|--skipNDFVerification| |Specifies if the NDF should be loaded without the signature|--skipNDFVerification|
+|--userid|-i|ID to sign in as. Does not register, must be an available precanned user |-i 32|
+|--verbose|-v|Verbose mode for debugging|-v|
+|--version|-V|Show the client version information|-V|
+|--waitForMessages|-w|Denotes the number of messages the client should receive before closing (default 1)|-w 7|
+|--dummyfrequency| |Frequency of dummy messages in Hz.  If no message is passed, will transmit a random message.  Dummies are only sent if this flag is passed| --dummyfrequency 30.5|
+
+Runs a client for cMix anonymous communication platform
+
+Use "client [command] --help" for more information about a command.
+
+
 
 ##Project Structure
 
diff --git a/cmd/root.go b/cmd/root.go
index f827de7f7b1457fe2598b660a029c729135233a8..f89d808e56e367c6ea270bc7f58f5fb0f0f865e1 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -27,7 +27,6 @@ import (
 	"gitlab.com/elixxir/primitives/switchboard"
 	"io/ioutil"
 	"log"
-	"math/big"
 	"os"
 	"strconv"
 	"sync/atomic"
@@ -45,7 +44,6 @@ var dummyFrequency float64
 var noBlockingTransmission bool
 var rateLimiting uint32
 var showVer bool
-var registrationCertPath string
 var registrationCode string
 var userEmail string
 var userNick string
@@ -55,13 +53,16 @@ var ndfPath string
 var skipNDFVerification bool
 var ndfPubKey string
 var noTLS bool
+var searchForUser string
+var waitForMessages uint
+var messageTimeout uint
 
 // Execute adds all child commands to the root command and sets flags
 // appropriately.  This is called by main.main(). It only needs to
 // happen once to the rootCmd.
 func Execute() {
 	if err := rootCmd.Execute(); err != nil {
-		jww.ERROR.Println(err)
+		fmt.Println(err)
 		os.Exit(1)
 	}
 }
@@ -173,11 +174,16 @@ func sessionInitialization() (*id.User, string, *api.Client) {
 
 		if sourcePublicKeyPath != "" {
 			pubKeyBytes, err := ioutil.ReadFile(utils.GetFullPath(sourcePublicKeyPath))
-			jww.FATAL.Panicf("Could not load user public key PEM from "+
-				"path %s: %+v", sourcePublicKeyPath, err)
+			if err != nil {
+				globals.Log.FATAL.Panicf("Could not load user public key PEM from "+
+					"path %s: %+v", sourcePublicKeyPath, err)
+			}
+
 			privKey, err = rsa.LoadPrivateKeyFromPem(pubKeyBytes)
-			jww.FATAL.Panicf("Could not public key from "+
-				"PEM: %+v", err)
+			if err != nil {
+				globals.Log.FATAL.Panicf("Could not public key from "+
+					"PEM: %+v", err)
+			}
 		}
 
 		uid, err = client.Register(userId != 0, regCode, userNick, userEmail, privKey)
@@ -297,42 +303,22 @@ func (l *TextListener) Hear(item switchboard.Item, isHeardElsewhere bool) {
 	atomic.AddInt64(&l.MessagesReceived, 1)
 }
 
-type ChannelListener struct {
-	MessagesReceived int64
+type userSearcher struct {
+	foundUserChan chan []byte
 }
 
-//used to get the client object into hear
-var globalClient *api.Client
-
-func (l *ChannelListener) Hear(item switchboard.Item, isHeardElsewhere bool) {
-	message := item.(*parse.Message)
-	globals.Log.INFO.Println("Hearing a channel message")
-	result := cmixproto.ChannelMessage{}
-	err := proto.Unmarshal(message.Body, &result)
+func newUserSearcher() api.SearchCallback {
+	us := userSearcher{}
+	us.foundUserChan = make(chan []byte)
+	return &us
+}
 
+func (us *userSearcher) Callback(userID, pubKey []byte, err error) {
 	if err != nil {
-		globals.Log.ERROR.Printf("Could not unmarhsal message, message "+
-			"not processed: %+v", err)
-	}
-
-	sender, ok := user.Users.GetUser(message.Sender)
-	var senderNick string
-	if !ok {
-		globals.Log.ERROR.Printf("Couldn't get sender %v", message.Sender)
+		globals.Log.ERROR.Printf("Could not find searched user: %+v", err)
 	} else {
-		senderNick = sender.Nick
+		us.foundUserChan <- userID
 	}
-
-	fmt.Printf("Message from channel %v, %v: ",
-		new(big.Int).SetBytes(message.Sender[:]).Text(10), senderNick)
-	typedBody, _ := parse.Parse(result.Message)
-	speakerId := id.NewUserFromBytes(result.SpeakerID)
-	globalClient.GetSwitchboard().Speak(&parse.Message{
-		TypedBody: *typedBody,
-		Sender:    speakerId,
-		Receiver:  id.ZeroID,
-	})
-	atomic.AddInt64(&l.MessagesReceived, 1)
 }
 
 // rootCmd represents the base command when called without any subcommands
@@ -352,7 +338,6 @@ var rootCmd = &cobra.Command{
 		var timer *time.Timer
 
 		userID, _, client := sessionInitialization()
-		globalClient = client
 		// Set Key parameters if defined
 		if len(keyParams) == 5 {
 			setKeyParams(client)
@@ -364,10 +349,6 @@ var rootCmd = &cobra.Command{
 		text := TextListener{}
 		client.Listen(id.ZeroID, int32(cmixproto.Type_TEXT_MESSAGE),
 			&text)
-		// Channel messages
-		channel := ChannelListener{}
-		client.Listen(id.ZeroID,
-			int32(cmixproto.Type_CHANNEL_MESSAGE), &channel)
 		// All other messages
 		fallback := FallbackListener{}
 		client.Listen(id.ZeroID, int32(cmixproto.Type_NO_TYPE),
@@ -396,7 +377,7 @@ var rootCmd = &cobra.Command{
 		var recipientId *id.User
 
 		if destinationUserId != 0 && destinationUserIDBase64 != "" {
-			jww.FATAL.Panicf("Two destiantions set for the message, can only have one")
+			globals.Log.FATAL.Panicf("Two destiantions set for the message, can only have one")
 		}
 
 		if destinationUserId == 0 && destinationUserIDBase64 == "" {
@@ -404,7 +385,7 @@ var rootCmd = &cobra.Command{
 		} else if destinationUserIDBase64 != "" {
 			recipientIdBytes, err := base64.StdEncoding.DecodeString(destinationUserIDBase64)
 			if err != nil {
-				jww.FATAL.Panic("Could not decode the destination user ID")
+				globals.Log.FATAL.Panic("Could not decode the destination user ID")
 			}
 			recipientId = id.NewUserFromBytes(recipientIdBytes)
 
@@ -446,6 +427,13 @@ var rootCmd = &cobra.Command{
 			}
 		}
 
+		var udbLister api.SearchCallback
+
+		if searchForUser != "" {
+			udbLister = newUserSearcher()
+			client.SearchForUser(searchForUser, udbLister)
+		}
+
 		if dummyFrequency != 0 {
 			timer = time.NewTimer(dummyPeriod)
 		}
@@ -481,21 +469,33 @@ var rootCmd = &cobra.Command{
 		} else {
 			// Wait up to 45s to receive a message
 			for end, timeout := false, time.After(45*time.Second); !end; {
-				if text.MessagesReceived > 0 {
+				numMsgRecieved := atomic.LoadInt64(&text.MessagesReceived)
+				if numMsgRecieved == int64(waitForMessages) {
 					end = true
 				}
 
 				select {
 				case <-timeout:
-					fmt.Println("Timing out client " +
-						"as no messages have" +
-						" been received")
+					fmt.Println("Timing out client, "+
+						"%v/%v message(s) been received", numMsgRecieved,
+						waitForMessages)
 					end = true
 				default:
 				}
 			}
 		}
 
+		if searchForUser != "" {
+			foundUser := <-udbLister.(*userSearcher).foundUserChan
+			if isValidUser(foundUser) {
+				userIDBase64 := base64.StdEncoding.EncodeToString(foundUser)
+				globals.Log.INFO.Printf("Found User %s at ID: %s",
+					userEmail, userIDBase64)
+			} else {
+				globals.Log.INFO.Printf("Found User %s is invalid")
+			}
+		}
+
 		//Logout
 		err = client.Logout()
 
@@ -507,6 +507,18 @@ var rootCmd = &cobra.Command{
 	},
 }
 
+func isValidUser(usr []byte) bool {
+	if len(usr) != id.UserLen {
+		return false
+	}
+	for _, b := range usr {
+		if b != 0 {
+			return true
+		}
+	}
+	return false
+}
+
 // init is the initialization function for Cobra which defines commands
 // and flags.
 func init() {
@@ -532,16 +544,12 @@ func init() {
 			"Automatically disabled if 'blockingTransmission' is false")
 
 	rootCmd.PersistentFlags().Uint64VarP(&userId, "userid", "i", 0,
-		"ID to sign in as")
-	rootCmd.PersistentFlags().StringVarP(&registrationCertPath, "registrationcertpath", "r",
-		"",
-		"Path to the certificate file for connecting to registration server"+
-			" using TLS")
+		"ID to sign in as. Does not register, must be an available precanned user")
 
 	rootCmd.PersistentFlags().StringVarP(&registrationCode,
-		"regcode", "e",
+		"regcode", "r",
 		"",
-		"Registration Code")
+		"Registration Code with the registration server")
 
 	rootCmd.PersistentFlags().StringVarP(&userEmail,
 		"email", "E",
@@ -589,21 +597,32 @@ func init() {
 			"will transmit a random message.  Dummies are only sent if this flag is passed")
 
 	rootCmd.PersistentFlags().BoolVarP(&end2end, "end2end", "", false,
-		"Send messages with E2E encryption to destination user")
+		"Send messages with E2E encryption to destination user. Must have found each other via UDB first")
 
 	rootCmd.PersistentFlags().StringSliceVarP(&keyParams, "keyParams", "",
 		make([]string, 0), "Define key generation parameters. Pass values in comma separated list"+
 			" in the following order: MinKeys,MaxKeys,NumRekeys,TTLScalar,MinNumKeys")
 
 	rootCmd.Flags().BoolVarP(&noTLS, "noTLS", "", false,
-		"Set to ignore TLS")
+		"Set to ignore TLS. Connections will fail if the network requires TLS. For debugging")
 
-	rootCmd.Flags().StringVar(&sourcePublicKeyPath, "pubKey", "",
-		"The path for a PEM encoded public key which will be used "+
+	rootCmd.Flags().StringVar(&sourcePublicKeyPath, "privateKey", "",
+		"The path for a PEM encoded private key which will be used "+
 			"to create the user")
 
 	rootCmd.Flags().StringVar(&destinationUserIDBase64, "dest64", "",
 		"Sets the destination user id encoded in base 64")
+
+	rootCmd.Flags().UintVarP(&waitForMessages, "waitForMessages",
+		"w", 1, "Denotes the number of messages the "+
+			"client should receive before closing")
+
+	rootCmd.Flags().StringVar(&searchForUser, "SearchForUser", "",
+		"Sets the email to search for to find a user with user discovery")
+
+	rootCmd.Flags().UintVarP(&messageTimeout, "messageTimeout",
+		"t", 45, "The number of seconds to wait for "+
+			"'waitForMessages' messages to arrive")
 }
 
 // initConfig reads in config file and ENV variables if set.