diff --git a/auth/request.go b/auth/request.go
index 3a80c3c989c4ac5c755d558de46d72c94e60ec80..8fd4aaac2ca098b37654ebcbcb09e2b9435ce04f 100644
--- a/auth/request.go
+++ b/auth/request.go
@@ -31,6 +31,14 @@ import (
 
 const terminator = ";"
 
+const (
+	// ErrChannelExists is a message returned in state.Request when an
+	// authenticated channel exists between the partner and me.
+	// Note that modifications to this should go over usages of this message
+	// in other packages (if applicable).
+	ErrChannelExists = "Authenticated channel already established with partner"
+)
+
 // Request sends a contact request from the user identity in the imported e2e
 // structure to the passed contact, as well as the passed facts (will error if
 // they are too long).
@@ -46,8 +54,7 @@ func (s *state) Request(partner contact.Contact, myfacts fact.FactList) (id.Roun
 	// check that an authenticated channel does not already exist
 	if _, err := s.e2e.GetPartner(partner.ID); err == nil ||
 		!strings.Contains(err.Error(), ratchet.NoPartnerErrorStr) {
-		return 0, errors.Errorf("Authenticated channel already " +
-			"established with partner")
+		return 0, errors.Errorf(ErrChannelExists)
 	}
 
 	return s.request(partner, myfacts, false)
diff --git a/cmd/backup.go b/cmd/backup.go
index c8a5c78608785a10f8b18d2813a5a9a4197c7933..f3c7f308d30b7f3d9f286970a8f72b8c5a9c0c2a 100644
--- a/cmd/backup.go
+++ b/cmd/backup.go
@@ -24,7 +24,7 @@ import (
 // loadOrInitBackup will build a new xxdk.E2e from existing storage
 // or from a new storage that it will create if none already exists
 func loadOrInitBackup(backupPath string, backupPass string, password []byte, storeDir string,
-	cmixParams xxdk.CMIXParams, e2eParams xxdk.E2EParams) *xxdk.E2e {
+	cmixParams xxdk.CMIXParams, e2eParams xxdk.E2EParams, cbs xxdk.AuthCallbacks) *xxdk.E2e {
 	jww.INFO.Printf("Using Backup sender")
 
 	// create a new client if none exist
@@ -92,7 +92,7 @@ func loadOrInitBackup(backupPath string, backupPass string, password []byte, sto
 		}
 	}
 
-	messenger, err := xxdk.Login(net, authCbs, identity, e2eParams)
+	messenger, err := xxdk.Login(net, cbs, identity, e2eParams)
 	if err != nil {
 		jww.FATAL.Panicf("%+v", err)
 	}
diff --git a/cmd/connect.go b/cmd/connect.go
new file mode 100644
index 0000000000000000000000000000000000000000..f4ecd1222be8eb9872c4c01be69145ec2c907e5e
--- /dev/null
+++ b/cmd/connect.go
@@ -0,0 +1,581 @@
+////////////////////////////////////////////////////////////////////////////////
+// Copyright © 2022 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/spf13/cobra"
+	jww "github.com/spf13/jwalterweatherman"
+	"github.com/spf13/viper"
+	"gitlab.com/elixxir/client/catalog"
+	"gitlab.com/elixxir/client/cmix"
+	"gitlab.com/elixxir/client/connect"
+	"gitlab.com/elixxir/client/e2e"
+	"gitlab.com/elixxir/client/e2e/receive"
+	"gitlab.com/elixxir/client/xxdk"
+	"gitlab.com/xx_network/primitives/id"
+	"time"
+)
+
+// connectionCmd handles the operation of connection operations within the CLI.
+var connectionCmd = &cobra.Command{
+	Use:   "connection",
+	Short: "Runs clients and servers in the connections paradigm.",
+	Args:  cobra.NoArgs,
+	Run: func(cmd *cobra.Command, args []string) {
+		logLevel := viper.GetUint("logleval")
+		logPath := viper.GetString("log")
+		initLog(logLevel, logPath)
+		jww.INFO.Printf(Version())
+
+		statePass := parsePassword(viper.GetString("password"))
+		statePath := viper.GetString("session")
+		regCode := viper.GetString("regcode")
+		cmixParams, e2eParams := initParams()
+		forceLegacy := viper.GetBool("force-legacy")
+		if viper.GetBool("startServer") {
+			if viper.GetBool("authenticated") {
+				secureConnServer(forceLegacy, statePass, statePath, regCode,
+					cmixParams, e2eParams)
+			} else {
+				insecureConnServer(forceLegacy, statePass, statePath, regCode,
+					cmixParams, e2eParams)
+			}
+		} else {
+			if viper.GetBool("authenticated") {
+				secureConnClient(forceLegacy, statePass, statePath, regCode,
+					cmixParams, e2eParams)
+			} else {
+				insecureConnClient(forceLegacy, statePass, statePath, regCode,
+					cmixParams, e2eParams)
+			}
+
+		}
+
+	},
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////
+// Connection Server Logic
+////////////////////////////////////////////////////////////////////////////////////////////
+
+// Secure (authenticated) connection server path
+func secureConnServer(forceLegacy bool, statePass []byte, statePath, regCode string,
+	cmixParams xxdk.CMIXParams, e2eParams xxdk.E2EParams) {
+	connChan := make(chan connect.Connection, 1)
+
+	// Load client state and identity------------------------------------------
+	net := loadOrInitNet(statePass, statePath, regCode, cmixParams)
+	identity := loadOrInitReceptionIdentity(forceLegacy, net)
+
+	// Save contact file-------------------------------------------------------
+	writeContact(identity.GetContact())
+
+	// Handle incoming connections---------------------------------------------
+	authCb := connect.AuthenticatedCallback(
+		func(connection connect.AuthenticatedConnection) {
+			partnerId := connection.GetPartner().PartnerId()
+			jww.INFO.Printf("[CONN] Received authenticated connection from %s", partnerId)
+			fmt.Println("Established authenticated connection with client")
+
+			_, err := connection.RegisterListener(catalog.XxMessage, listener{"AuthServer"})
+			if err != nil {
+				jww.FATAL.Panicf("Failed to register listener for client message!")
+			}
+
+			connChan <- connection
+		})
+
+	// Start connection server-------------------------------------------------
+	connectionParam := connect.DefaultConnectionListParams()
+	connectServer, err := connect.StartAuthenticatedServer(identity,
+		authCb, net, e2eParams, connectionParam)
+	if err != nil {
+		jww.FATAL.Panicf("Failed to start authenticated "+
+			"connection server: %v", err)
+	}
+
+	fmt.Println("Established connection server, begin listening...")
+	jww.INFO.Printf("[CONN] Established connection server, begin listening...")
+
+	// Start network threads---------------------------------------------------
+	networkFollowerTimeout := 5 * time.Second
+	err = connectServer.Messenger.StartNetworkFollower(networkFollowerTimeout)
+	if err != nil {
+		jww.FATAL.Panicf("Failed to start network follower: %+v", err)
+	}
+
+	// Set up a wait for the network to be connected
+	waitUntilConnected := func(connected chan bool) {
+		waitTimeout := 30 * time.Second
+		timeoutTimer := time.NewTimer(waitTimeout)
+		isConnected := false
+		// Wait until we connect or panic if we cannot before the timeout
+		for !isConnected {
+			select {
+			case isConnected = <-connected:
+				jww.INFO.Printf("Network Status: %v", isConnected)
+				break
+			case <-timeoutTimer.C:
+				jww.FATAL.Panicf("Timeout on starting network follower")
+			}
+		}
+	}
+
+	// Create a tracker channel to be notified of network changes
+	connected := make(chan bool, 10)
+	// Provide a callback that will be signalled when network health
+	// status changes
+	connectServer.Messenger.GetCmix().AddHealthCallback(
+		func(isConnected bool) {
+			connected <- isConnected
+		})
+	// Wait until connected or crash on timeout
+	waitUntilConnected(connected)
+
+	// Wait for connection establishment----------------------------------------
+
+	// Wait for connection to be established
+	connectionTimeout := time.NewTimer(240 * time.Second)
+	select {
+	case conn := <-connChan:
+		// Perform functionality shared by client & server
+		miscConnectionFunctions(connectServer.Messenger, conn)
+
+	case <-connectionTimeout.C:
+		connectionTimeout.Stop()
+		jww.FATAL.Panicf("[CONN] Failed to establish connection within " +
+			"default time period, closing process")
+	}
+
+	// Keep server running to receive messages------------------------------------
+	serverTimeout := viper.GetDuration("serverTimeout")
+	if serverTimeout != 0 {
+		timer := time.NewTimer(serverTimeout)
+		select {
+		case <-timer.C:
+			fmt.Println("Shutting down connection server")
+			timer.Stop()
+			return
+		}
+	}
+
+	// If timeout is not specified, leave as long-running thread
+	select {}
+
+}
+
+// Insecure (unauthenticated) connection server path
+func insecureConnServer(forceLegacy bool, statePass []byte, statePath, regCode string,
+	cmixParams xxdk.CMIXParams, e2eParams xxdk.E2EParams) {
+
+	connChan := make(chan connect.Connection, 1)
+
+	// Load client state and identity------------------------------------------
+	net := loadOrInitNet(statePass, statePath, regCode, cmixParams)
+	identity := loadOrInitReceptionIdentity(forceLegacy, net)
+
+	// Save contact file-------------------------------------------------------
+	writeContact(identity.GetContact())
+
+	// Handle incoming connections---------------------------------------------
+	cb := connect.Callback(func(connection connect.Connection) {
+		partnerId := connection.GetPartner().PartnerId()
+		jww.INFO.Printf("[CONN] Received connection request from %s", partnerId)
+		fmt.Println("Established connection with client")
+
+		_, err := connection.RegisterListener(catalog.XxMessage, listener{"ConnectionServer"})
+		if err != nil {
+			jww.FATAL.Panicf("Failed to register listener for client message!")
+		}
+
+		connChan <- connection
+	})
+
+	// Start connection server-------------------------------------------------
+	connectionParam := connect.DefaultConnectionListParams()
+	connectServer, err := connect.StartServer(identity,
+		cb, net, e2eParams, connectionParam)
+	if err != nil {
+		jww.FATAL.Panicf("[CONN] Failed to start connection server: %v", err)
+	}
+
+	fmt.Println("Established connection server, begin listening...")
+	jww.INFO.Printf("[CONN] Established connection server, begin listening...")
+
+	// Start network threads---------------------------------------------------
+	networkFollowerTimeout := 5 * time.Second
+	err = connectServer.Messenger.StartNetworkFollower(networkFollowerTimeout)
+	if err != nil {
+		jww.FATAL.Panicf("Failed to start network follower: %+v", err)
+	}
+
+	// Set up a wait for the network to be connected
+	waitUntilConnected := func(connected chan bool) {
+		waitTimeout := 30 * time.Second
+		timeoutTimer := time.NewTimer(waitTimeout)
+		isConnected := false
+		// Wait until we connect or panic if we cannot before the timeout
+		for !isConnected {
+			select {
+			case isConnected = <-connected:
+				jww.INFO.Printf("Network Status: %v", isConnected)
+				break
+			case <-timeoutTimer.C:
+				jww.FATAL.Panicf("Timeout on starting network follower")
+			}
+		}
+	}
+
+	// Create a tracker channel to be notified of network changes
+	connected := make(chan bool, 10)
+	// Provide a callback that will be signalled when network health
+	// status changes
+	connectServer.Messenger.GetCmix().AddHealthCallback(
+		func(isConnected bool) {
+			connected <- isConnected
+		})
+	// Wait until connected or crash on timeout
+	waitUntilConnected(connected)
+
+	// Wait for connection establishment----------------------------------------
+
+	// Wait for connection to be established
+	connectionTimeout := time.NewTimer(240 * time.Second)
+	select {
+	case conn := <-connChan:
+		// Perform functionality shared by client & server
+		miscConnectionFunctions(connectServer.Messenger, conn)
+
+	case <-connectionTimeout.C:
+		connectionTimeout.Stop()
+		jww.FATAL.Panicf("[CONN] Failed to establish connection within " +
+			"default time period, closing process")
+	}
+
+	// Keep server running to receive messages------------------------------------
+	if viper.GetDuration("serverTimeout") != 0 {
+		timer := time.NewTimer(viper.GetDuration("serverTimeout"))
+		select {
+		case <-timer.C:
+			fmt.Println("Shutting down connection server")
+			timer.Stop()
+			return
+		}
+	}
+
+	// If timeout is not specified, leave as long-running thread
+	select {}
+
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////
+// Connection Client Logic
+////////////////////////////////////////////////////////////////////////////////////////////
+
+// Secure (authenticated) connection client path
+func secureConnClient(forceLegacy bool, statePass []byte, statePath, regCode string,
+	cmixParams xxdk.CMIXParams, e2eParams xxdk.E2EParams) {
+	// Load client ------------------------------------------------------------------
+	messenger := loadOrInitMessenger(forceLegacy, statePass, statePath, regCode,
+		cmixParams, e2eParams, xxdk.DefaultAuthCallbacks{})
+
+	// Start network threads---------------------------------------------------------
+
+	// Set networkFollowerTimeout to a value of your choice (seconds)
+	networkFollowerTimeout := 5 * time.Second
+	err := messenger.StartNetworkFollower(networkFollowerTimeout)
+	if err != nil {
+		jww.FATAL.Panicf("Failed to start network follower: %+v", err)
+	}
+
+	// Set up a wait for the network to be connected
+	waitUntilConnected := func(connected chan bool) {
+		waitTimeout := 30 * time.Second
+		timeoutTimer := time.NewTimer(waitTimeout)
+		isConnected := false
+		// Wait until we connect or panic if we cannot before the timeout
+		for !isConnected {
+			select {
+			case isConnected = <-connected:
+				jww.INFO.Printf("Network Status: %v", isConnected)
+				break
+			case <-timeoutTimer.C:
+				jww.FATAL.Panicf("Timeout on starting network follower")
+			}
+		}
+	}
+
+	// Create a tracker channel to be notified of network changes
+	connected := make(chan bool, 10)
+	// Provide a callback that will be signalled when network
+	// health status changes
+	messenger.GetCmix().AddHealthCallback(
+		func(isConnected bool) {
+			connected <- isConnected
+		})
+	// Wait until connected or crash on timeout
+	waitUntilConnected(connected)
+
+	// Connect with the server-------------------------------------------------
+	contactPath := viper.GetString("connect")
+	serverContact := getContactFromFile(contactPath)
+	fmt.Println("Sending connection request")
+
+	// Establish connection with partner
+	conn, err := connect.ConnectWithAuthentication(serverContact, messenger,
+		e2eParams)
+	if err != nil {
+		jww.FATAL.Panicf("[CONN] Failed to build connection with %s: %v",
+			serverContact.ID, err)
+	}
+
+	jww.INFO.Printf("[CONN] Established authenticated connection with %s",
+		conn.GetPartner().PartnerId())
+	fmt.Println("Established authenticated connection with server.")
+
+	miscConnectionFunctions(messenger, conn)
+
+}
+
+// Insecure (unauthenticated) connection client path
+func insecureConnClient(forceLegacy bool, statePass []byte, statePath, regCode string,
+	cmixParams xxdk.CMIXParams, e2eParams xxdk.E2EParams) {
+
+	// Load client ------------------------------------------------------------------
+	messenger := loadOrInitMessenger(forceLegacy, statePass, statePath, regCode,
+		cmixParams, e2eParams, xxdk.DefaultAuthCallbacks{})
+
+	// Start network threads---------------------------------------------------------
+
+	// Set networkFollowerTimeout to a value of your choice (seconds)
+	networkFollowerTimeout := 5 * time.Second
+	err := messenger.StartNetworkFollower(networkFollowerTimeout)
+	if err != nil {
+		jww.FATAL.Panicf("Failed to start network follower: %+v", err)
+	}
+
+	// Set up a wait for the network to be connected
+	waitUntilConnected := func(connected chan bool) {
+		waitTimeout := 30 * time.Second
+		timeoutTimer := time.NewTimer(waitTimeout)
+		isConnected := false
+		// Wait until we connect or panic if we cannot before the timeout
+		for !isConnected {
+			select {
+			case isConnected = <-connected:
+				jww.INFO.Printf("Network Status: %v", isConnected)
+				break
+			case <-timeoutTimer.C:
+				jww.FATAL.Panicf("Timeout on starting network follower")
+			}
+		}
+	}
+
+	// Create a tracker channel to be notified of network changes
+	connected := make(chan bool, 10)
+	// Provide a callback that will be signalled when network
+	// health status changes
+	messenger.GetCmix().AddHealthCallback(
+		func(isConnected bool) {
+			connected <- isConnected
+		})
+	// Wait until connected or crash on timeout
+	waitUntilConnected(connected)
+
+	// Connect with the server-------------------------------------------------
+	contactPath := viper.GetString("connect")
+	serverContact := getContactFromFile(contactPath)
+	fmt.Println("Sending connection request")
+	jww.INFO.Printf("[CONN] Sending connection request to %s",
+		serverContact.ID)
+
+	// Establish connection with partner
+	handler, err := connect.Connect(serverContact, messenger,
+		e2eParams)
+	if err != nil {
+		jww.FATAL.Panicf("[CONN] Failed to build connection with %s: %v",
+			serverContact.ID, err)
+
+	}
+
+	fmt.Println("Established connection with server")
+	jww.INFO.Printf("[CONN] Established connection with %s", handler.GetPartner().PartnerId())
+
+	miscConnectionFunctions(messenger, handler)
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////
+// Misc Logic (shared between client & server)
+////////////////////////////////////////////////////////////////////////////////////////////
+
+// miscConnectionFunctions contains miscellaneous functionality for the subcommand connect.
+// This functionality should be shared between client & server.
+func miscConnectionFunctions(client *xxdk.E2e, conn connect.Connection) {
+	// Send a message to connection partner--------------------------------------------
+	msgBody := viper.GetString("message")
+	paramsE2E := e2e.GetDefaultParams()
+	if msgBody != "" {
+		// Send message
+		jww.INFO.Printf("[CONN] Sending message to %s",
+			conn.GetPartner().PartnerId())
+		payload := []byte(msgBody)
+		for {
+			roundIDs, _, _, err := conn.SendE2E(catalog.XxMessage, payload,
+				paramsE2E)
+			if err != nil {
+				jww.FATAL.Panicf("[CONN] Failed to send E2E message: %v", err)
+			}
+
+			// Verify message sends were successful when verifySendFlag is present
+			if viper.GetBool("verify-sends") {
+				retryChan := make(chan struct{})
+				done := make(chan struct{}, 1)
+
+				// Construct the callback function which
+				// verifies successful message send or retries
+				f := func(allRoundsSucceeded, timedOut bool,
+					rounds map[id.Round]cmix.RoundResult) {
+					printRoundResults(
+						rounds, roundIDs, payload, conn.GetPartner().PartnerId())
+					if !allRoundsSucceeded {
+						retryChan <- struct{}{}
+					} else {
+						done <- struct{}{}
+					}
+				}
+
+				// Monitor rounds for results
+				err := client.GetCmix().GetRoundResults(
+					paramsE2E.CMIXParams.Timeout, f, roundIDs...)
+				if err != nil {
+					jww.DEBUG.Printf("Could not verify messages were sent " +
+						"successfully, resending messages...")
+					continue
+				}
+
+				select {
+				case <-retryChan:
+					// On a retry, go to the top of the loop
+					jww.DEBUG.Printf("Messages were not sent successfully," +
+						" resending messages...")
+					continue
+				case <-done:
+					// Close channels on verification success
+					close(done)
+					close(retryChan)
+					break
+				}
+
+			}
+			jww.INFO.Printf("[CONN] Sent message %q to %s", msgBody,
+				conn.GetPartner().PartnerId())
+			fmt.Printf("Sent message %q to connection partner.\n", msgBody)
+			break
+		}
+	}
+
+	// Disconnect from connection partner--------------------------------------------
+	if viper.GetBool("disconnect") {
+		// Close the connection
+		if err := conn.Close(); err != nil {
+			jww.FATAL.Panicf("Failed to disconnect with %s: %v",
+				conn.GetPartner().PartnerId(), err)
+		}
+		jww.INFO.Printf("[CONN] Disconnected from %s",
+			conn.GetPartner().PartnerId())
+		fmt.Println("Disconnected from partner")
+	}
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Recreated Callback & Listener for connection testing
+///////////////////////////////////////////////////////////////////////////////
+
+//var connAuthCbs *authConnHandler
+
+// listener implements the receive.Listener interface
+type listener struct {
+	name string
+}
+
+// Hear will be called whenever a message matching
+// the RegisterListener call is received
+// User-defined message handling logic goes here
+func (l listener) Hear(item receive.Message) {
+	fmt.Printf("%s heard message \"%s\"\n", l.name, string(item.Payload))
+}
+
+// Name is used for debugging purposes
+func (l listener) Name() string {
+	return l.name
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Command Line Flags                                                         /
+///////////////////////////////////////////////////////////////////////////////
+
+// init initializes commands and flags for Cobra.
+func init() {
+	connectionCmd.Flags().String("connect", "",
+		"This flag is a client side operation. "+
+			"This flag expects a path to a contact file (similar "+
+			"to destfile). It will parse this into an contact object,"+
+			" referred to as a server contact. The client will "+
+			"establish a connection with the server contact. "+
+			"If a connection already exists between "+
+			"the client and the server, this will be used instead of "+
+			"resending a connection request to the server.")
+	err := viper.BindPFlag("connect", connectionCmd.Flags().Lookup("connect"))
+	if err != nil {
+		jww.ERROR.Printf("viper.BindPFlag failed for %q: %+v", "connect", err)
+	}
+
+	connectionCmd.Flags().Bool("startServer", false,
+		"This flag is a server-side operation and takes no arguments. "+
+			"This initiates a connection server. "+
+			"Calling this flag will have this process call "+
+			"connection.StartServer().")
+	err = viper.BindPFlag("startServer", connectionCmd.Flags().Lookup("startServer"))
+	if err != nil {
+		jww.ERROR.Printf("viper.BindPFlag failed for %q: %+v", "startServer", err)
+	}
+
+	connectionCmd.Flags().Duration("serverTimeout", time.Duration(0),
+		"This flag is a connection parameter. "+
+			"This takes as an argument a time.Duration. "+
+			"This duration specifies how long a server will run before "+
+			"closing. Without this flag present, a server will be "+
+			"long-running.")
+	err = viper.BindPFlag("serverTimeout", connectionCmd.Flags().Lookup("serverTimeout"))
+	if err != nil {
+		jww.ERROR.Printf("viper.BindPFlag failed for %q: %+v", "serverTimeout", err)
+	}
+
+	connectionCmd.Flags().Bool("disconnect", false,
+		"This flag is available to both server and client. "+
+			"This uses a contact object from a file specified by --destfile."+
+			"This will close the connection with the given contact "+
+			"if it exists.")
+	err = viper.BindPFlag("disconnect", connectionCmd.Flags().Lookup("disconnect"))
+	if err != nil {
+		jww.ERROR.Printf("viper.BindPFlag failed for %q: %+v", "disconnect", err)
+	}
+
+	connectionCmd.Flags().Bool("authenticated", false,
+		"This flag is available to both server and client. "+
+			"This flag operates as a switch for the authenticated code-path. "+
+			"With this flag present, any additional connection related flags"+
+			" will call the applicable authenticated counterpart")
+	err = viper.BindPFlag("authenticated", connectionCmd.Flags().Lookup("authenticated"))
+	if err != nil {
+		jww.ERROR.Printf("viper.BindPFlag failed for %q: %+v", "authenticated", err)
+	}
+
+	rootCmd.AddCommand(connectionCmd)
+}
diff --git a/cmd/init.go b/cmd/init.go
index 993ee81dba5aa347f37be1159073e9e00ff6e2bb..86356025bda36b5ca713c5a4b9664ba92471a24f 100644
--- a/cmd/init.go
+++ b/cmd/init.go
@@ -74,9 +74,20 @@ func init() {
 // loadOrInitMessenger will build a new xxdk.E2e from existing storage
 // or from a new storage that it will create if none already exists
 func loadOrInitMessenger(forceLegacy bool, password []byte, storeDir, regCode string,
-	cmixParams xxdk.CMIXParams, e2eParams xxdk.E2EParams) *xxdk.E2e {
+	cmixParams xxdk.CMIXParams, e2eParams xxdk.E2EParams, cbs xxdk.AuthCallbacks) *xxdk.E2e {
 	jww.INFO.Printf("Using normal sender")
 
+	net := loadOrInitNet(password, storeDir, regCode, cmixParams)
+	identity := loadOrInitReceptionIdentity(forceLegacy, net)
+	messenger, err := xxdk.Login(net, cbs, identity, e2eParams)
+	if err != nil {
+		jww.FATAL.Panicf("%+v", err)
+	}
+	return messenger
+}
+
+func loadOrInitNet(password []byte, storeDir, regCode string,
+	cmixParams xxdk.CMIXParams) *xxdk.Cmix {
 	// create a new client if none exist
 	if _, err := os.Stat(storeDir); errors.Is(err, fs.ErrNotExist) {
 		// Initialize from scratch
@@ -97,6 +108,10 @@ func loadOrInitMessenger(forceLegacy bool, password []byte, storeDir, regCode st
 		jww.FATAL.Panicf("%+v", err)
 	}
 
+	return net
+}
+
+func loadOrInitReceptionIdentity(forceLegacy bool, net *xxdk.Cmix) xxdk.ReceptionIdentity {
 	// Load or initialize xxdk.ReceptionIdentity storage
 	identity, err := xxdk.LoadReceptionIdentity(identityStorageKey, net)
 	if err != nil {
@@ -116,17 +131,14 @@ func loadOrInitMessenger(forceLegacy bool, password []byte, storeDir, regCode st
 		}
 	}
 
-	messenger, err := xxdk.Login(net, authCbs, identity, e2eParams)
-	if err != nil {
-		jww.FATAL.Panicf("%+v", err)
-	}
-	return messenger
+	return identity
+
 }
 
 // loadOrInitVanity will build a new xxdk.E2e from existing storage
 // or from a new storage that it will create if none already exists
 func loadOrInitVanity(password []byte, storeDir, regCode, userIdPrefix string,
-	cmixParams xxdk.CMIXParams, e2eParams xxdk.E2EParams) *xxdk.E2e {
+	cmixParams xxdk.CMIXParams, e2eParams xxdk.E2EParams, cbs xxdk.AuthCallbacks) *xxdk.E2e {
 	jww.INFO.Printf("Using Vanity sender")
 
 	// create a new client if none exist
@@ -163,7 +175,7 @@ func loadOrInitVanity(password []byte, storeDir, regCode, userIdPrefix string,
 		}
 	}
 
-	messenger, err := xxdk.Login(net, authCbs, identity, e2eParams)
+	messenger, err := xxdk.Login(net, cbs, identity, e2eParams)
 	if err != nil {
 		jww.FATAL.Panicf("%+v", err)
 	}
diff --git a/cmd/precan.go b/cmd/precan.go
index 84076dff5472239cd1310cab37949d16f7d29b93..90e3c31b2352bbac53973faba5a91fdc0a29c024 100644
--- a/cmd/precan.go
+++ b/cmd/precan.go
@@ -24,7 +24,7 @@ import (
 // loadOrInitPrecan will build a new xxdk.E2e from existing storage
 // or from a new storage that it will create if none already exists
 func loadOrInitPrecan(precanId uint, password []byte, storeDir string,
-	cmixParams xxdk.CMIXParams, e2eParams xxdk.E2EParams) *xxdk.E2e {
+	cmixParams xxdk.CMIXParams, e2eParams xxdk.E2EParams, cbs xxdk.AuthCallbacks) *xxdk.E2e {
 	jww.INFO.Printf("Using Precanned sender")
 
 	// create a new client if none exist
@@ -60,7 +60,7 @@ func loadOrInitPrecan(precanId uint, password []byte, storeDir string,
 		}
 	}
 
-	messenger, err := xxdk.Login(net, authCbs, identity, e2eParams)
+	messenger, err := xxdk.Login(net, cbs, identity, e2eParams)
 	if err != nil {
 		jww.FATAL.Panicf("%+v", err)
 	}
diff --git a/cmd/proto.go b/cmd/proto.go
index b8585ca784a1751be5ec1a0be99143d81a2e82ba..9d6cacbe9ce3e6c31a04df410f28d690808dce99 100644
--- a/cmd/proto.go
+++ b/cmd/proto.go
@@ -22,7 +22,7 @@ import (
 // loadOrInitProto will build a new xxdk.E2e from existing storage
 // or from a new storage that it will create if none already exists
 func loadOrInitProto(protoUserPath string, password []byte, storeDir string,
-	cmixParams xxdk.CMIXParams, e2eParams xxdk.E2EParams) *xxdk.E2e {
+	cmixParams xxdk.CMIXParams, e2eParams xxdk.E2EParams, cbs xxdk.AuthCallbacks) *xxdk.E2e {
 	jww.INFO.Printf("Using Proto sender")
 
 	// create a new client if none exist
@@ -70,7 +70,7 @@ func loadOrInitProto(protoUserPath string, password []byte, storeDir string,
 		}
 	}
 
-	messenger, err := xxdk.Login(net, authCbs, identity, e2eParams)
+	messenger, err := xxdk.Login(net, cbs, identity, e2eParams)
 	if err != nil {
 		jww.FATAL.Panicf("%+v", err)
 	}
diff --git a/cmd/root.go b/cmd/root.go
index 4b360c8d188b767768548bf2d2904a46a1f0d5db..ba5e285ac55aed54ce0dc1480dacc807fdb7a7ec 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -595,15 +595,15 @@ func initE2e(cmixParams xxdk.CMIXParams, e2eParams xxdk.E2EParams) *xxdk.E2e {
 	// Initialize the client of the proper type
 	var messenger *xxdk.E2e
 	if precanId != 0 {
-		messenger = loadOrInitPrecan(precanId, storePassword, storeDir, cmixParams, e2eParams)
+		messenger = loadOrInitPrecan(precanId, storePassword, storeDir, cmixParams, e2eParams, authCbs)
 	} else if protoUserPath != "" {
-		messenger = loadOrInitProto(protoUserPath, storePassword, storeDir, cmixParams, e2eParams)
+		messenger = loadOrInitProto(protoUserPath, storePassword, storeDir, cmixParams, e2eParams, authCbs)
 	} else if userIdPrefix != "" {
-		messenger = loadOrInitVanity(storePassword, storeDir, regCode, userIdPrefix, cmixParams, e2eParams)
+		messenger = loadOrInitVanity(storePassword, storeDir, regCode, userIdPrefix, cmixParams, e2eParams, authCbs)
 	} else if backupPath != "" {
-		messenger = loadOrInitBackup(backupPath, backupPass, storePassword, storeDir, cmixParams, e2eParams)
+		messenger = loadOrInitBackup(backupPath, backupPass, storePassword, storeDir, cmixParams, e2eParams, authCbs)
 	} else {
-		messenger = loadOrInitMessenger(forceLegacy, storePassword, storeDir, regCode, cmixParams, e2eParams)
+		messenger = loadOrInitMessenger(forceLegacy, storePassword, storeDir, regCode, cmixParams, e2eParams, authCbs)
 	}
 
 	// Handle protoUser output
diff --git a/connect/authenticated.go b/connect/authenticated.go
index 11f0d2de9d92cb2b42e34dab9618b61d48d77328..7e16fd0f822c7aae8b758c978ac868f23ce8dfd1 100644
--- a/connect/authenticated.go
+++ b/connect/authenticated.go
@@ -71,8 +71,8 @@ func ConnectWithAuthentication(recipient contact.Contact, messenger *xxdk.E2e,
 	if err != nil {
 		return nil, err
 	}
-	return connectWithAuthentication(conn, timeStart, recipient, identity.Salt, privKey,
-		messenger.GetRng(), messenger.GetCmix(), p)
+	return connectWithAuthentication(conn, timeStart, recipient,
+		identity.Salt, privKey, messenger.GetRng(), messenger.GetCmix(), p)
 }
 
 // connectWithAuthentication builds and sends an IdentityAuthentication to
@@ -175,7 +175,7 @@ func connectWithAuthentication(conn Connection, timeStart time.Time,
 // authenticate themselves. An established AuthenticatedConnection will
 // be passed via the callback.
 func StartAuthenticatedServer(identity xxdk.ReceptionIdentity,
-	cb AuthenticatedCallback, net *xxdk.Cmix, p xxdk.E2EParams,
+	authCb AuthenticatedCallback, net *xxdk.Cmix, p xxdk.E2EParams,
 	clParams ConnectionListParams) (
 	*ConnectionServer, error) {
 
@@ -187,7 +187,7 @@ func StartAuthenticatedServer(identity xxdk.ReceptionIdentity,
 		// be passed along via the AuthenticatedCallback
 		_, err := connection.RegisterListener(
 			catalog.ConnectionAuthenticationRequest,
-			buildAuthConfirmationHandler(cb, connection))
+			buildAuthConfirmationHandler(authCb, connection))
 		if err != nil {
 			jww.ERROR.Printf(
 				"Failed to register listener on connection with %s: %+v",
diff --git a/connect/client.go b/connect/client.go
index a916bdca352da2e7aee21d0b2549702acfeebfdf..809c5da3017ba707dd9037ce624ae1045f3f252f 100644
--- a/connect/client.go
+++ b/connect/client.go
@@ -21,21 +21,11 @@ func buildClientAuthRequest(newPartner partner.Manager,
 	rng *fastRNG.StreamGenerator, rsaPrivKey *rsa.PrivateKey,
 	salt []byte) ([]byte, error) {
 
-	// The connection fingerprint (hashed) will be used as a nonce
+	// Create signature
 	connectionFp := newPartner.ConnectionFingerprint().Bytes()
-	opts := rsa.NewDefaultOptions()
-	h := opts.Hash.New()
-	h.Write(connectionFp)
-	nonce := h.Sum(nil)
-
-	// Sign the connection fingerprint
 	stream := rng.GetStream()
 	defer stream.Close()
-	signature, err := rsa.Sign(stream, rsaPrivKey,
-		opts.Hash, nonce, opts)
-	if err != nil {
-		return nil, errors.Errorf("failed to sign nonce: %+v", err)
-	}
+	signature, err := sign(stream, rsaPrivKey, connectionFp)
 
 	// Construct message
 	pemEncodedRsaPubKey := rsa.CreatePublicKeyPem(rsaPrivKey.GetPublic())
@@ -44,11 +34,12 @@ func buildClientAuthRequest(newPartner partner.Manager,
 		RsaPubKey: pemEncodedRsaPubKey,
 		Salt:      salt,
 	}
+
+	// Marshal message
 	payload, err := proto.Marshal(iar)
 	if err != nil {
 		return nil, errors.Errorf("failed to marshal identity request "+
 			"message: %+v", err)
 	}
-
 	return payload, nil
 }
diff --git a/connect/connect.go b/connect/connect.go
index ea755c0b181cbace7c64f2bb8878dcfd612b022e..6e48d0532ed2f3f04c2adb7d0813e29432517614 100644
--- a/connect/connect.go
+++ b/connect/connect.go
@@ -8,6 +8,7 @@ package connect
 
 import (
 	"io"
+	"strings"
 	"sync/atomic"
 	"time"
 
@@ -95,6 +96,16 @@ func Connect(recipient contact.Contact, messenger *xxdk.E2e,
 	// Perform the auth request
 	_, err := messenger.GetAuth().Request(recipient, nil)
 	if err != nil {
+		// Return connection if a partnership already exists
+		if strings.Contains(err.Error(), auth.ErrChannelExists) {
+			newPartner, err := messenger.GetE2E().GetPartner(recipient.ID)
+			if err != nil {
+				return nil, err
+			}
+			conn := BuildConnection(newPartner,
+				messenger.GetE2E(), messenger.GetAuth(), p)
+			return conn, nil
+		}
 		return nil, err
 	}
 
diff --git a/connect/crypto.go b/connect/crypto.go
new file mode 100644
index 0000000000000000000000000000000000000000..391aa1b2778dd936754182f78f57e5fa80854f39
--- /dev/null
+++ b/connect/crypto.go
@@ -0,0 +1,56 @@
+package connect
+
+import (
+	"github.com/pkg/errors"
+	"gitlab.com/xx_network/crypto/signature/rsa"
+	"gitlab.com/xx_network/crypto/xx"
+	"gitlab.com/xx_network/primitives/id"
+	"io"
+)
+
+// Sign creates a signature authenticating an identity for a connection.
+func sign(rng io.Reader, rsaPrivKey *rsa.PrivateKey,
+	connectionFp []byte) ([]byte, error) {
+	// The connection fingerprint (hashed) will be used as a nonce
+	opts := rsa.NewDefaultOptions()
+	h := opts.Hash.New()
+	h.Write(connectionFp)
+	nonce := h.Sum(nil)
+
+	// Sign the connection fingerprint
+	return rsa.Sign(rng, rsaPrivKey,
+		opts.Hash, nonce, opts)
+
+}
+
+// Verify takes a signature for an authentication attempt
+// and verifies the information.
+func verify(partnerId *id.ID, partnerPubKey *rsa.PublicKey,
+	signature, connectionFp, salt []byte) error {
+
+	// Verify the partner's known ID against the information passed
+	// along the wire
+	partnerWireId, err := xx.NewID(partnerPubKey, salt, id.User)
+	if err != nil {
+		return err
+	}
+
+	if !partnerId.Cmp(partnerWireId) {
+		return errors.New("Failed confirm partner's ID over the wire")
+	}
+
+	// Hash the connection fingerprint
+	opts := rsa.NewDefaultOptions()
+	h := opts.Hash.New()
+	h.Write(connectionFp)
+	nonce := h.Sum(nil)
+
+	// Verify the signature
+	err = rsa.Verify(partnerPubKey, opts.Hash, nonce, signature, opts)
+	if err != nil {
+		return err
+	}
+
+	return nil
+
+}
diff --git a/connect/crypto_test.go b/connect/crypto_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..633e47a700e98034df89df6f51192a4c6117a790
--- /dev/null
+++ b/connect/crypto_test.go
@@ -0,0 +1,75 @@
+////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright © 2022 xx network SEZC                                                       //
+//                                                                                        //
+// Use of this source code is governed by a license that can be found in the LICENSE file //
+////////////////////////////////////////////////////////////////////////////////////////////
+
+package connect
+
+import (
+	"bytes"
+	"gitlab.com/xx_network/crypto/signature/rsa"
+	"gitlab.com/xx_network/crypto/xx"
+	"gitlab.com/xx_network/primitives/id"
+	"testing"
+)
+
+var expectedSig = []byte{139, 67, 63, 6, 185, 76, 60, 217, 163, 84, 251, 231,
+	197, 6, 33, 179, 53, 66, 88, 75, 105, 191, 16, 71, 126, 4, 16, 11, 41,
+	237, 34, 245, 242, 97, 44, 58, 154, 120, 58, 235, 240, 140, 223, 80, 232,
+	51, 94, 247, 226, 217, 79, 194, 215, 46, 187, 157, 55, 167, 180, 179, 12,
+	228, 205, 98, 132, 200, 146, 180, 142, 0, 230, 79, 0, 129, 39, 205, 67,
+	79, 252, 62, 187, 125, 130, 232, 125, 41, 99, 63, 106, 79, 234, 131, 109,
+	103, 189, 149, 45, 169, 227, 85, 164, 121, 103, 254, 19, 224, 236, 28, 187,
+	38, 240, 132, 192, 227, 145, 140, 56, 196, 91, 48, 228, 242, 123, 142, 123,
+	221, 159, 160}
+
+type CountingReader struct {
+	count uint8
+}
+
+// Read just counts until 254 then starts over again
+func (c *CountingReader) Read(b []byte) (int, error) {
+	for i := 0; i < len(b); i++ {
+		c.count = (c.count + 1) % 255
+		b[i] = c.count
+	}
+	return len(b), nil
+}
+
+func TestSignVerify_Consistency(t *testing.T) {
+	// use insecure seeded rng to reproduce key
+	notRand := &CountingReader{count: uint8(0)}
+
+	privKey, err := rsa.GenerateKey(notRand, 1024)
+	if err != nil {
+		t.Fatalf("SignVerify error: "+
+			"Could not generate key: %v", err.Error())
+	}
+
+	connFp := []byte("connFp")
+
+	signature, err := sign(notRand, privKey, connFp)
+	if err != nil {
+		t.Logf("Sign error: %v", err)
+	}
+
+	salt := make([]byte, 32)
+	copy(salt, "salt")
+
+	partnerId, err := xx.NewID(privKey.GetPublic(), salt, id.User)
+	if err != nil {
+		t.Fatalf("NewId error: %v", err)
+	}
+
+	err = verify(partnerId, privKey.GetPublic(), signature, connFp, salt)
+	if err != nil {
+		t.Fatalf("Verify error: %v", err)
+	}
+
+	if !bytes.Equal(signature, expectedSig) {
+		t.Errorf("Consistency test failed."+
+			"\nExpected: %v"+
+			"\nReceived: %v", expectedSig, signature)
+	}
+}
diff --git a/connect/server.go b/connect/server.go
index 78e22eed6fedfbe965ac89dcd8cbdf15579d4a26..b9f77b28b05b5f9af692fce2f532808a82b02e99 100644
--- a/connect/server.go
+++ b/connect/server.go
@@ -9,11 +9,9 @@ package connect
 
 import (
 	"github.com/golang/protobuf/proto"
-	"github.com/pkg/errors"
 	jww "github.com/spf13/jwalterweatherman"
 	"gitlab.com/elixxir/client/e2e/receive"
 	"gitlab.com/xx_network/crypto/signature/rsa"
-	"gitlab.com/xx_network/crypto/xx"
 	"gitlab.com/xx_network/primitives/id"
 )
 
@@ -41,7 +39,7 @@ type serverListener struct {
 // buildAuthConfirmationHandler returns a serverListener object.
 // This will handle incoming identity authentication confirmations
 // via the serverListener.Hear method. A successful AuthenticatedConnection
-// will be passed along via the serverListener.connectionCallback
+// will be passed along via the serverListener.connectionCallback.
 func buildAuthConfirmationHandler(cb AuthenticatedCallback,
 	connection Connection) server {
 	return &serverListener{
@@ -62,41 +60,19 @@ func (a serverListener) Hear(item receive.Message) {
 		return
 	}
 
-	// Process the PEM encoded public key to an rsa.PublicKey object
-	partnerPubKey, err := rsa.LoadPublicKeyFromPem(iar.RsaPubKey)
-	if err != nil {
-		a.handleAuthConfirmationErr(err, item.Sender)
-		return
-	}
-
-	// Get the new partner
+	// Get the new partner's connection fingerprint
 	newPartner := a.conn.GetPartner()
+	connectionFp := newPartner.ConnectionFingerprint().Bytes()
 
-	// Verify the partner's known ID against the information passed
-	// along the wire
-	partnerWireId, err := xx.NewID(partnerPubKey, iar.Salt, id.User)
+	// Process the PEM encoded public key to an rsa.PublicKey object
+	partnerPubKey, err := rsa.LoadPublicKeyFromPem(iar.RsaPubKey)
 	if err != nil {
 		a.handleAuthConfirmationErr(err, item.Sender)
-		return
 	}
 
-	if !newPartner.PartnerId().Cmp(partnerWireId) {
-		err = errors.New("Failed confirm partner's ID over the wire")
-		a.handleAuthConfirmationErr(err, item.Sender)
-		return
-	}
-
-	// The connection fingerprint (hashed) will be used as a nonce
-	connectionFp := newPartner.ConnectionFingerprint().Bytes()
-
-	// Hash the connection fingerprint
-	opts := rsa.NewDefaultOptions()
-	h := opts.Hash.New()
-	h.Write(connectionFp)
-	nonce := h.Sum(nil)
-
-	// Verify the signature
-	err = rsa.Verify(partnerPubKey, opts.Hash, nonce, iar.Signature, opts)
+	// Verify the signature within the message
+	err = verify(newPartner.PartnerId(), partnerPubKey,
+		iar.Signature, connectionFp, iar.Salt)
 	if err != nil {
 		a.handleAuthConfirmationErr(err, item.Sender)
 		return