diff --git a/cmixproto/types.pb.go b/cmixproto/types.pb.go
index c0b870e2ae83e59fc0b2cffb7db46d80afedec20..d1cb16dcb305fc09a49da1002e2d909f7d40eff6 100644
--- a/cmixproto/types.pb.go
+++ b/cmixproto/types.pb.go
@@ -1,24 +1,37 @@
+////////////////////////////////////////////////////////////////////////////////
+// Copyright © 2018 Privategrity Corporation                                   /
+//                                                                             /
+// All rights reserved.                                                        /
+////////////////////////////////////////////////////////////////////////////////
+
+// Call ./generate.sh to generate the protocol buffer code
+
 // Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.25.0
+// 	protoc        (unknown)
 // source: types.proto
 
 package cmixproto
 
 import (
-	fmt "fmt"
 	proto "github.com/golang/protobuf/proto"
-	math "math"
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	reflect "reflect"
+	sync "sync"
 )
 
-// Reference imports to suppress errors if they are not otherwise used.
-var _ = proto.Marshal
-var _ = fmt.Errorf
-var _ = math.Inf
+const (
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
 
-// This is a compile-time assertion to ensure that this generated file
-// is compatible with the proto package it is being compiled against.
-// A compilation error at this line likely means your copy of the
-// proto package needs to be updated.
-const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
+// This is a compile-time assertion that a sufficiently up-to-date version
+// of the legacy proto package is being used.
+const _ = proto.ProtoPackageIsVersion4
 
 type Type int32
 
@@ -28,11 +41,6 @@ const (
 	Type_NO_TYPE Type = 0
 	// See proto buf documentation below
 	Type_TEXT_MESSAGE Type = 1
-	// See proto buf
-	Type_CHANNEL_MESSAGE Type = 2
-	// Nickname request and response messages
-	Type_NICKNAME_REQUEST  Type = 6
-	Type_NICKNAME_RESPONSE Type = 7
 	// Second field is the key data itself. This should be 2048 bits long
 	// (according to the message length that our prime allows) and is
 	// base64-encoded.
@@ -77,478 +85,141 @@ const (
 	// which you can use with GET_KEY to get the keys you need to talk with
 	// that user. Otherwise, this fourth field won't exist.
 	Type_UDB_SEARCH_RESPONSE Type = 17
-	// To get a message of this type, call the methods in the wallet.
-	// TODO expose these methods over the API
-	Type_PAYMENT_TRANSACTION Type = 20
-	// See proto buf
-	Type_PAYMENT_RESPONSE Type = 21
-	// See proto buf
-	Type_PAYMENT_INVOICE Type = 22
-	// This message type includes only the message hash of the original
-	// invoice message. Since there are no fields to delimit, it's not a
-	// proto buffer. When the payee gets a receipt back, they know that the
-	// other person probably paid them, and to check the next published
-	// blockchain for the images of their coins to make sure.
-	// The wallet sends this message after receiving a PAYMENT_RESPONSE
-	// indicating success.
-	Type_PAYMENT_RECEIPT Type = 23
 	// End to End Rekey message types
 	// Trigger a rekey, this message is used locally in client only
 	Type_REKEY_TRIGGER Type = 30
 	// Rekey confirmation message. Sent by partner to confirm completion of a rekey
 	Type_REKEY_CONFIRM Type = 31
-	// This message type is a single fixed-length hash of the invoice
-	// that the client just received. The client can look up this hash in the
-	// inbound transaction list to display the most recently received invoice
-	// in the UI.
-	Type_PAYMENT_INVOICE_UI Type = 9000
-	// This message type is a single fixed-length hash of the original invoice
-	// that this client sent to the paying client. The UI can look up the
-	// corresponding transaction in the list of completed transactions and
-	// display payment success on the UI. The wallet sends this message
-	// locally after receiving a PAYMENT_RECEIPT message.
-	Type_PAYMENT_RECEIPT_UI Type = 9001
-)
-
-var Type_name = map[int32]string{
-	0:    "NO_TYPE",
-	1:    "TEXT_MESSAGE",
-	2:    "CHANNEL_MESSAGE",
-	6:    "NICKNAME_REQUEST",
-	7:    "NICKNAME_RESPONSE",
-	10:   "UDB_PUSH_KEY",
-	11:   "UDB_PUSH_KEY_RESPONSE",
-	12:   "UDB_GET_KEY",
-	13:   "UDB_GET_KEY_RESPONSE",
-	14:   "UDB_REGISTER",
-	15:   "UDB_REGISTER_RESPONSE",
-	16:   "UDB_SEARCH",
-	17:   "UDB_SEARCH_RESPONSE",
-	20:   "PAYMENT_TRANSACTION",
-	21:   "PAYMENT_RESPONSE",
-	22:   "PAYMENT_INVOICE",
-	23:   "PAYMENT_RECEIPT",
-	30:   "REKEY_TRIGGER",
-	31:   "REKEY_CONFIRM",
-	9000: "PAYMENT_INVOICE_UI",
-	9001: "PAYMENT_RECEIPT_UI",
-}
-
-var Type_value = map[string]int32{
-	"NO_TYPE":               0,
-	"TEXT_MESSAGE":          1,
-	"CHANNEL_MESSAGE":       2,
-	"NICKNAME_REQUEST":      6,
-	"NICKNAME_RESPONSE":     7,
-	"UDB_PUSH_KEY":          10,
-	"UDB_PUSH_KEY_RESPONSE": 11,
-	"UDB_GET_KEY":           12,
-	"UDB_GET_KEY_RESPONSE":  13,
-	"UDB_REGISTER":          14,
-	"UDB_REGISTER_RESPONSE": 15,
-	"UDB_SEARCH":            16,
-	"UDB_SEARCH_RESPONSE":   17,
-	"PAYMENT_TRANSACTION":   20,
-	"PAYMENT_RESPONSE":      21,
-	"PAYMENT_INVOICE":       22,
-	"PAYMENT_RECEIPT":       23,
-	"REKEY_TRIGGER":         30,
-	"REKEY_CONFIRM":         31,
-	"PAYMENT_INVOICE_UI":    9000,
-	"PAYMENT_RECEIPT_UI":    9001,
-}
-
-func (x Type) String() string {
-	return proto.EnumName(Type_name, int32(x))
-}
-
-func (Type) EnumDescriptor() ([]byte, []int) {
-	return fileDescriptor_d938547f84707355, []int{0}
-}
-
-// Use this enumeration to get specific transactions from specific transaction
-// lists from the wallet
-type TransactionListTag int32
-
-const (
-	// Transactions in this list are invoices this user made to another user
-	// Most importantly, they include the preimage that this user wants fulfilled,
-	// but the image of this preimage is what the client will send in the invoice.
-	// Transactions enter this list when this user invoices another user.
-	TransactionListTag_OUTBOUND_REQUESTS TransactionListTag = 0
-	// Transactions in this list are invoices that this user received from
-	// other users. Most importantly, this includes the image that this user
-	// will fund.
-	TransactionListTag_INBOUND_REQUESTS TransactionListTag = 1
-	// Transactions in this list are currently processing on a payment bot.
-	// Transactions move from INBOUND_REQUESTS to PENDING_TRANSACTIONS after
-	// a Pay() call.
-	TransactionListTag_PENDING_TRANSACTIONS TransactionListTag = 2
-	// Transactions in this list are completed transactions that increased
-	// the value of this user's wallet. NOTE: They correspond to transactions
-	// originally in the OUTBOUND_REQUESTS list that went through.
-	TransactionListTag_COMPLETED_INBOUND_PAYMENTS TransactionListTag = 3
-	// Transactions in this list are completed transactions that decreased
-	// the value of this user's wallet. NOTE: They correspond to transactions
-	// originally in the INBOUND_REQUESTS list that went through.
-	TransactionListTag_COMPLETED_OUTBOUND_PAYMENTS TransactionListTag = 4
 )
 
-var TransactionListTag_name = map[int32]string{
-	0: "OUTBOUND_REQUESTS",
-	1: "INBOUND_REQUESTS",
-	2: "PENDING_TRANSACTIONS",
-	3: "COMPLETED_INBOUND_PAYMENTS",
-	4: "COMPLETED_OUTBOUND_PAYMENTS",
-}
-
-var TransactionListTag_value = map[string]int32{
-	"OUTBOUND_REQUESTS":           0,
-	"INBOUND_REQUESTS":            1,
-	"PENDING_TRANSACTIONS":        2,
-	"COMPLETED_INBOUND_PAYMENTS":  3,
-	"COMPLETED_OUTBOUND_PAYMENTS": 4,
-}
-
-func (x TransactionListTag) String() string {
-	return proto.EnumName(TransactionListTag_name, int32(x))
-}
-
-func (TransactionListTag) EnumDescriptor() ([]byte, []int) {
-	return fileDescriptor_d938547f84707355, []int{1}
-}
-
-// Use this enumeration to request different sort orders of transaction lists
-// when getting all IDs in a transaction list
-type TransactionListOrder int32
-
-const (
-	// Most recently initiated transaction first
-	TransactionListOrder_TIMESTAMP_DESCENDING TransactionListOrder = 0
-	// Stalest transaction first
-	TransactionListOrder_TIMESTAMP_ASCENDING TransactionListOrder = 1
-	// Biggest transaction first
-	TransactionListOrder_VALUE_DESCENDING TransactionListOrder = 2
-	// Smallest transaction first
-	TransactionListOrder_VALUE_ASCENDING TransactionListOrder = 3
-)
-
-var TransactionListOrder_name = map[int32]string{
-	0: "TIMESTAMP_DESCENDING",
-	1: "TIMESTAMP_ASCENDING",
-	2: "VALUE_DESCENDING",
-	3: "VALUE_ASCENDING",
-}
-
-var TransactionListOrder_value = map[string]int32{
-	"TIMESTAMP_DESCENDING": 0,
-	"TIMESTAMP_ASCENDING":  1,
-	"VALUE_DESCENDING":     2,
-	"VALUE_ASCENDING":      3,
-}
-
-func (x TransactionListOrder) String() string {
-	return proto.EnumName(TransactionListOrder_name, int32(x))
-}
-
-func (TransactionListOrder) EnumDescriptor() ([]byte, []int) {
-	return fileDescriptor_d938547f84707355, []int{2}
-}
-
-// Is Type.TEXT_MESSAGE
-// Used for conveying simple text messages between users
-type TextMessage struct {
-	// Terminal text foreground color. Used by the console UI
-	Color int32 `protobuf:"zigzag32,2,opt,name=color,proto3" json:"color,omitempty"`
-	// The message text itself. Varies in length
-	Message string `protobuf:"bytes,3,opt,name=message,proto3" json:"message,omitempty"`
-	// Timestamp (Unix time in seconds)
-	// You can use this to display the time when the other user sent the message
-	// TODO Remove this when all messages have timestamps
-	Time                 int64    `protobuf:"varint,4,opt,name=time,proto3" json:"time,omitempty"`
-	XXX_NoUnkeyedLiteral struct{} `json:"-"`
-	XXX_unrecognized     []byte   `json:"-"`
-	XXX_sizecache        int32    `json:"-"`
-}
-
-func (m *TextMessage) Reset()         { *m = TextMessage{} }
-func (m *TextMessage) String() string { return proto.CompactTextString(m) }
-func (*TextMessage) ProtoMessage()    {}
-func (*TextMessage) Descriptor() ([]byte, []int) {
-	return fileDescriptor_d938547f84707355, []int{0}
-}
-
-func (m *TextMessage) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_TextMessage.Unmarshal(m, b)
-}
-func (m *TextMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_TextMessage.Marshal(b, m, deterministic)
-}
-func (m *TextMessage) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_TextMessage.Merge(m, src)
-}
-func (m *TextMessage) XXX_Size() int {
-	return xxx_messageInfo_TextMessage.Size(m)
-}
-func (m *TextMessage) XXX_DiscardUnknown() {
-	xxx_messageInfo_TextMessage.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_TextMessage proto.InternalMessageInfo
-
-func (m *TextMessage) GetColor() int32 {
-	if m != nil {
-		return m.Color
-	}
-	return 0
-}
-
-func (m *TextMessage) GetMessage() string {
-	if m != nil {
-		return m.Message
-	}
-	return ""
-}
-
-func (m *TextMessage) GetTime() int64 {
-	if m != nil {
-		return m.Time
-	}
-	return 0
-}
-
-// Is Type.CHANNEL_MESSAGE
-// This is the type of all messages that come from the channelbot.
-type ChannelMessage struct {
-	// This is the original speaker of the channel message, who sent the
-	// message to the channel bot.
-	SpeakerID []byte `protobuf:"bytes,3,opt,name=speakerID,proto3" json:"speakerID,omitempty"`
-	// This is a serialized parse message under the hood. When writing a
-	// listener for a channel message on a client, you need to unpack the
-	// serialized parse message and rebroadcast it through the listeners.
-	Message              []byte   `protobuf:"bytes,4,opt,name=message,proto3" json:"message,omitempty"`
-	XXX_NoUnkeyedLiteral struct{} `json:"-"`
-	XXX_unrecognized     []byte   `json:"-"`
-	XXX_sizecache        int32    `json:"-"`
-}
-
-func (m *ChannelMessage) Reset()         { *m = ChannelMessage{} }
-func (m *ChannelMessage) String() string { return proto.CompactTextString(m) }
-func (*ChannelMessage) ProtoMessage()    {}
-func (*ChannelMessage) Descriptor() ([]byte, []int) {
-	return fileDescriptor_d938547f84707355, []int{1}
-}
-
-func (m *ChannelMessage) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_ChannelMessage.Unmarshal(m, b)
-}
-func (m *ChannelMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_ChannelMessage.Marshal(b, m, deterministic)
-}
-func (m *ChannelMessage) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_ChannelMessage.Merge(m, src)
-}
-func (m *ChannelMessage) XXX_Size() int {
-	return xxx_messageInfo_ChannelMessage.Size(m)
-}
-func (m *ChannelMessage) XXX_DiscardUnknown() {
-	xxx_messageInfo_ChannelMessage.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_ChannelMessage proto.InternalMessageInfo
-
-func (m *ChannelMessage) GetSpeakerID() []byte {
-	if m != nil {
-		return m.SpeakerID
+// Enum value maps for Type.
+var (
+	Type_name = map[int32]string{
+		0:  "NO_TYPE",
+		1:  "TEXT_MESSAGE",
+		10: "UDB_PUSH_KEY",
+		11: "UDB_PUSH_KEY_RESPONSE",
+		12: "UDB_GET_KEY",
+		13: "UDB_GET_KEY_RESPONSE",
+		14: "UDB_REGISTER",
+		15: "UDB_REGISTER_RESPONSE",
+		16: "UDB_SEARCH",
+		17: "UDB_SEARCH_RESPONSE",
+		30: "REKEY_TRIGGER",
+		31: "REKEY_CONFIRM",
 	}
-	return nil
-}
-
-func (m *ChannelMessage) GetMessage() []byte {
-	if m != nil {
-		return m.Message
+	Type_value = map[string]int32{
+		"NO_TYPE":               0,
+		"TEXT_MESSAGE":          1,
+		"UDB_PUSH_KEY":          10,
+		"UDB_PUSH_KEY_RESPONSE": 11,
+		"UDB_GET_KEY":           12,
+		"UDB_GET_KEY_RESPONSE":  13,
+		"UDB_REGISTER":          14,
+		"UDB_REGISTER_RESPONSE": 15,
+		"UDB_SEARCH":            16,
+		"UDB_SEARCH_RESPONSE":   17,
+		"REKEY_TRIGGER":         30,
+		"REKEY_CONFIRM":         31,
 	}
-	return nil
-}
-
-// Is Type.PAYMENT_RESPONSE
-type PaymentResponse struct {
-	// Indicates whether the payment succeeded or failed
-	Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"`
-	// Response message from the payment bot. You should display this to the
-	// user.
-	Response string `protobuf:"bytes,2,opt,name=response,proto3" json:"response,omitempty"`
-	// TODO Is it correct to use the whole hash?
-	// This is the hash of the payment message that the payment bot received.
-	// The client uses it to remove the correct pending transaction from the
-	// list of pending transactions.
-	ID                   []byte   `protobuf:"bytes,3,opt,name=ID,proto3" json:"ID,omitempty"`
-	XXX_NoUnkeyedLiteral struct{} `json:"-"`
-	XXX_unrecognized     []byte   `json:"-"`
-	XXX_sizecache        int32    `json:"-"`
-}
+)
 
-func (m *PaymentResponse) Reset()         { *m = PaymentResponse{} }
-func (m *PaymentResponse) String() string { return proto.CompactTextString(m) }
-func (*PaymentResponse) ProtoMessage()    {}
-func (*PaymentResponse) Descriptor() ([]byte, []int) {
-	return fileDescriptor_d938547f84707355, []int{2}
+func (x Type) Enum() *Type {
+	p := new(Type)
+	*p = x
+	return p
 }
 
-func (m *PaymentResponse) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_PaymentResponse.Unmarshal(m, b)
-}
-func (m *PaymentResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_PaymentResponse.Marshal(b, m, deterministic)
-}
-func (m *PaymentResponse) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_PaymentResponse.Merge(m, src)
-}
-func (m *PaymentResponse) XXX_Size() int {
-	return xxx_messageInfo_PaymentResponse.Size(m)
-}
-func (m *PaymentResponse) XXX_DiscardUnknown() {
-	xxx_messageInfo_PaymentResponse.DiscardUnknown(m)
+func (x Type) String() string {
+	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
 }
 
-var xxx_messageInfo_PaymentResponse proto.InternalMessageInfo
-
-func (m *PaymentResponse) GetSuccess() bool {
-	if m != nil {
-		return m.Success
-	}
-	return false
+func (Type) Descriptor() protoreflect.EnumDescriptor {
+	return file_types_proto_enumTypes[0].Descriptor()
 }
 
-func (m *PaymentResponse) GetResponse() string {
-	if m != nil {
-		return m.Response
-	}
-	return ""
+func (Type) Type() protoreflect.EnumType {
+	return &file_types_proto_enumTypes[0]
 }
 
-func (m *PaymentResponse) GetID() []byte {
-	if m != nil {
-		return m.ID
-	}
-	return nil
+func (x Type) Number() protoreflect.EnumNumber {
+	return protoreflect.EnumNumber(x)
 }
 
-// Is Type.PAYMENT_INVOICE
-type PaymentInvoice struct {
-	// Timestamp (Unix time in seconds)
-	// Not currently used but could be useful for the user to verify the
-	// correctness of an invoice.
-	Time int64 `protobuf:"varint,1,opt,name=time,proto3" json:"time,omitempty"`
-	// This is a single compound coin that the invoicer wants to be funded. The
-	// payer should send a message to the payment bot to fund this compound, if
-	// they wish to pay the payee.
-	CreatedCoin []byte `protobuf:"bytes,2,opt,name=createdCoin,proto3" json:"createdCoin,omitempty"`
-	// The payee should fill this out to describe what the payment is for or
-	// about.
-	Memo                 string   `protobuf:"bytes,3,opt,name=memo,proto3" json:"memo,omitempty"`
-	XXX_NoUnkeyedLiteral struct{} `json:"-"`
-	XXX_unrecognized     []byte   `json:"-"`
-	XXX_sizecache        int32    `json:"-"`
-}
+// Deprecated: Use Type.Descriptor instead.
+func (Type) EnumDescriptor() ([]byte, []int) {
+	return file_types_proto_rawDescGZIP(), []int{0}
+}
+
+var File_types_proto protoreflect.FileDescriptor
+
+var file_types_proto_rawDesc = []byte{
+	0x0a, 0x0b, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x05, 0x70,
+	0x61, 0x72, 0x73, 0x65, 0x2a, 0xf9, 0x01, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a,
+	0x07, 0x4e, 0x4f, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x10, 0x00, 0x12, 0x10, 0x0a, 0x0c, 0x54, 0x45,
+	0x58, 0x54, 0x5f, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c,
+	0x55, 0x44, 0x42, 0x5f, 0x50, 0x55, 0x53, 0x48, 0x5f, 0x4b, 0x45, 0x59, 0x10, 0x0a, 0x12, 0x19,
+	0x0a, 0x15, 0x55, 0x44, 0x42, 0x5f, 0x50, 0x55, 0x53, 0x48, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x52,
+	0x45, 0x53, 0x50, 0x4f, 0x4e, 0x53, 0x45, 0x10, 0x0b, 0x12, 0x0f, 0x0a, 0x0b, 0x55, 0x44, 0x42,
+	0x5f, 0x47, 0x45, 0x54, 0x5f, 0x4b, 0x45, 0x59, 0x10, 0x0c, 0x12, 0x18, 0x0a, 0x14, 0x55, 0x44,
+	0x42, 0x5f, 0x47, 0x45, 0x54, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x52, 0x45, 0x53, 0x50, 0x4f, 0x4e,
+	0x53, 0x45, 0x10, 0x0d, 0x12, 0x10, 0x0a, 0x0c, 0x55, 0x44, 0x42, 0x5f, 0x52, 0x45, 0x47, 0x49,
+	0x53, 0x54, 0x45, 0x52, 0x10, 0x0e, 0x12, 0x19, 0x0a, 0x15, 0x55, 0x44, 0x42, 0x5f, 0x52, 0x45,
+	0x47, 0x49, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x50, 0x4f, 0x4e, 0x53, 0x45, 0x10,
+	0x0f, 0x12, 0x0e, 0x0a, 0x0a, 0x55, 0x44, 0x42, 0x5f, 0x53, 0x45, 0x41, 0x52, 0x43, 0x48, 0x10,
+	0x10, 0x12, 0x17, 0x0a, 0x13, 0x55, 0x44, 0x42, 0x5f, 0x53, 0x45, 0x41, 0x52, 0x43, 0x48, 0x5f,
+	0x52, 0x45, 0x53, 0x50, 0x4f, 0x4e, 0x53, 0x45, 0x10, 0x11, 0x12, 0x11, 0x0a, 0x0d, 0x52, 0x45,
+	0x4b, 0x45, 0x59, 0x5f, 0x54, 0x52, 0x49, 0x47, 0x47, 0x45, 0x52, 0x10, 0x1e, 0x12, 0x11, 0x0a,
+	0x0d, 0x52, 0x45, 0x4b, 0x45, 0x59, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x52, 0x4d, 0x10, 0x1f,
+	0x42, 0x0b, 0x5a, 0x09, 0x63, 0x6d, 0x69, 0x78, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70,
+	0x72, 0x6f, 0x74, 0x6f, 0x33,
+}
+
+var (
+	file_types_proto_rawDescOnce sync.Once
+	file_types_proto_rawDescData = file_types_proto_rawDesc
+)
 
-func (m *PaymentInvoice) Reset()         { *m = PaymentInvoice{} }
-func (m *PaymentInvoice) String() string { return proto.CompactTextString(m) }
-func (*PaymentInvoice) ProtoMessage()    {}
-func (*PaymentInvoice) Descriptor() ([]byte, []int) {
-	return fileDescriptor_d938547f84707355, []int{3}
+func file_types_proto_rawDescGZIP() []byte {
+	file_types_proto_rawDescOnce.Do(func() {
+		file_types_proto_rawDescData = protoimpl.X.CompressGZIP(file_types_proto_rawDescData)
+	})
+	return file_types_proto_rawDescData
 }
 
-func (m *PaymentInvoice) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_PaymentInvoice.Unmarshal(m, b)
-}
-func (m *PaymentInvoice) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_PaymentInvoice.Marshal(b, m, deterministic)
-}
-func (m *PaymentInvoice) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_PaymentInvoice.Merge(m, src)
-}
-func (m *PaymentInvoice) XXX_Size() int {
-	return xxx_messageInfo_PaymentInvoice.Size(m)
+var file_types_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
+var file_types_proto_goTypes = []interface{}{
+	(Type)(0), // 0: parse.Type
 }
-func (m *PaymentInvoice) XXX_DiscardUnknown() {
-	xxx_messageInfo_PaymentInvoice.DiscardUnknown(m)
+var file_types_proto_depIdxs = []int32{
+	0, // [0:0] is the sub-list for method output_type
+	0, // [0:0] is the sub-list for method input_type
+	0, // [0:0] is the sub-list for extension type_name
+	0, // [0:0] is the sub-list for extension extendee
+	0, // [0:0] is the sub-list for field type_name
 }
 
-var xxx_messageInfo_PaymentInvoice proto.InternalMessageInfo
-
-func (m *PaymentInvoice) GetTime() int64 {
-	if m != nil {
-		return m.Time
-	}
-	return 0
-}
-
-func (m *PaymentInvoice) GetCreatedCoin() []byte {
-	if m != nil {
-		return m.CreatedCoin
-	}
-	return nil
-}
-
-func (m *PaymentInvoice) GetMemo() string {
-	if m != nil {
-		return m.Memo
+func init() { file_types_proto_init() }
+func file_types_proto_init() {
+	if File_types_proto != nil {
+		return
 	}
-	return ""
-}
-
-func init() {
-	proto.RegisterEnum("parse.Type", Type_name, Type_value)
-	proto.RegisterEnum("parse.TransactionListTag", TransactionListTag_name, TransactionListTag_value)
-	proto.RegisterEnum("parse.TransactionListOrder", TransactionListOrder_name, TransactionListOrder_value)
-	proto.RegisterType((*TextMessage)(nil), "parse.TextMessage")
-	proto.RegisterType((*ChannelMessage)(nil), "parse.ChannelMessage")
-	proto.RegisterType((*PaymentResponse)(nil), "parse.PaymentResponse")
-	proto.RegisterType((*PaymentInvoice)(nil), "parse.PaymentInvoice")
-}
-
-func init() { proto.RegisterFile("types.proto", fileDescriptor_d938547f84707355) }
-
-var fileDescriptor_d938547f84707355 = []byte{
-	// 622 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x5c, 0x54, 0xdb, 0x72, 0xda, 0x3a,
-	0x14, 0x8d, 0x81, 0x5c, 0xd8, 0x10, 0x10, 0x0a, 0x39, 0xe1, 0xe4, 0x9c, 0x49, 0x18, 0x9e, 0x98,
-	0x3c, 0xf4, 0xa5, 0x5f, 0xe0, 0x18, 0x05, 0x34, 0xc1, 0xb2, 0x23, 0xc9, 0x69, 0x93, 0x17, 0x8f,
-	0xeb, 0x68, 0x52, 0xa6, 0xc1, 0x66, 0x6c, 0xb7, 0x93, 0xfc, 0x4b, 0x3f, 0xa0, 0xfd, 0x91, 0x7e,
-	0x57, 0x47, 0x06, 0x5f, 0x26, 0x4f, 0x68, 0xaf, 0xb5, 0xb4, 0xb4, 0xd7, 0xde, 0x83, 0xa1, 0x93,
-	0xbd, 0x6d, 0x54, 0xfa, 0x61, 0x93, 0xc4, 0x59, 0x8c, 0xf7, 0x37, 0x41, 0x92, 0xaa, 0xc9, 0x1d,
-	0x74, 0xa4, 0x7a, 0xcd, 0x6c, 0x95, 0xa6, 0xc1, 0xb3, 0xc2, 0x43, 0xd8, 0x0f, 0xe3, 0x97, 0x38,
-	0x19, 0x35, 0xc6, 0xc6, 0x74, 0xc0, 0xb7, 0x05, 0x1e, 0xc1, 0xe1, 0x7a, 0x2b, 0x18, 0x35, 0xc7,
-	0xc6, 0xb4, 0xcd, 0x8b, 0x12, 0x63, 0x68, 0x65, 0xab, 0xb5, 0x1a, 0xb5, 0xc6, 0xc6, 0xb4, 0xc9,
-	0xf3, 0xf3, 0x64, 0x01, 0x3d, 0xeb, 0x6b, 0x10, 0x45, 0xea, 0xa5, 0x70, 0xfd, 0x1f, 0xda, 0xe9,
-	0x46, 0x05, 0xdf, 0x54, 0x42, 0x67, 0xb9, 0x43, 0x97, 0x57, 0x40, 0xdd, 0xbd, 0x95, 0x73, 0x45,
-	0x39, 0xf9, 0x04, 0x7d, 0x37, 0x78, 0x5b, 0xab, 0x28, 0xe3, 0x2a, 0xdd, 0xc4, 0x51, 0xaa, 0xb4,
-	0x38, 0xfd, 0x1e, 0x86, 0x2a, 0x4d, 0x47, 0xc6, 0xd8, 0x98, 0x1e, 0xf1, 0xa2, 0xc4, 0xe7, 0x70,
-	0x94, 0xec, 0x54, 0x79, 0xf7, 0x6d, 0x5e, 0xd6, 0xb8, 0x07, 0x8d, 0xf2, 0xe5, 0x06, 0x9d, 0x4d,
-	0x1e, 0xa1, 0xb7, 0x33, 0xa6, 0xd1, 0x8f, 0x78, 0x15, 0x56, 0x41, 0x8c, 0x2a, 0x08, 0x1e, 0x43,
-	0x27, 0x4c, 0x54, 0x90, 0xa9, 0x27, 0x2b, 0x5e, 0x45, 0xb9, 0x69, 0x97, 0xd7, 0x21, 0x7d, 0x6b,
-	0xad, 0xd6, 0xf1, 0x6e, 0x2a, 0xf9, 0xf9, 0xea, 0x4f, 0x13, 0x5a, 0xf2, 0x6d, 0xa3, 0x70, 0x07,
-	0x0e, 0x99, 0xe3, 0xcb, 0x07, 0x97, 0xa0, 0x3d, 0x8c, 0xa0, 0x2b, 0xc9, 0x67, 0xe9, 0xdb, 0x44,
-	0x08, 0x73, 0x4e, 0x90, 0x81, 0x4f, 0xa0, 0x6f, 0x2d, 0x4c, 0xc6, 0xc8, 0xb2, 0x04, 0x1b, 0x78,
-	0x08, 0x88, 0x51, 0xeb, 0x96, 0x99, 0x36, 0xf1, 0x39, 0xb9, 0xf3, 0x88, 0x90, 0xe8, 0x00, 0x9f,
-	0xc2, 0xa0, 0x86, 0x0a, 0xd7, 0x61, 0x82, 0xa0, 0x43, 0xed, 0xe9, 0xcd, 0xae, 0x7d, 0xd7, 0x13,
-	0x0b, 0xff, 0x96, 0x3c, 0x20, 0xc0, 0xff, 0xc2, 0x69, 0x1d, 0xa9, 0xc4, 0x1d, 0xdc, 0x87, 0x8e,
-	0xa6, 0xe6, 0x44, 0xe6, 0xda, 0x2e, 0x1e, 0xc1, 0xb0, 0x06, 0x54, 0xd2, 0xe3, 0xc2, 0x97, 0x93,
-	0x39, 0x15, 0x92, 0x70, 0xd4, 0x2b, 0x7c, 0x0b, 0xa4, 0x12, 0xf7, 0x71, 0x0f, 0x40, 0x53, 0x82,
-	0x98, 0xdc, 0x5a, 0x20, 0x84, 0xcf, 0xe0, 0xa4, 0xaa, 0x2b, 0xe1, 0x40, 0x13, 0xae, 0xf9, 0x60,
-	0x13, 0x26, 0x7d, 0xc9, 0x4d, 0x26, 0x4c, 0x4b, 0x52, 0x87, 0xa1, 0xa1, 0xce, 0x5c, 0x10, 0xa5,
-	0xfc, 0x54, 0x8f, 0xa7, 0x40, 0x29, 0xbb, 0x77, 0xa8, 0x45, 0xd0, 0x3f, 0x75, 0x90, 0x13, 0x8b,
-	0x50, 0x57, 0xa2, 0x33, 0x3c, 0x80, 0x63, 0x4e, 0x74, 0x04, 0xc9, 0xe9, 0x7c, 0x4e, 0x38, 0xba,
-	0xa8, 0x20, 0xcb, 0x61, 0x37, 0x94, 0xdb, 0xe8, 0x12, 0x9f, 0x01, 0x7e, 0xe7, 0xe7, 0x7b, 0x14,
-	0xfd, 0xba, 0xa9, 0x13, 0x3b, 0x4f, 0x4d, 0xfc, 0xbe, 0xb9, 0xfa, 0x69, 0x00, 0x96, 0x49, 0x10,
-	0xa5, 0x41, 0x98, 0xad, 0xe2, 0x68, 0xb9, 0x4a, 0x33, 0x19, 0x3c, 0xeb, 0x65, 0x38, 0x9e, 0xbc,
-	0x76, 0x3c, 0x36, 0x2b, 0x56, 0x24, 0xd0, 0x9e, 0x4e, 0x41, 0xd9, 0x3b, 0xd4, 0xd0, 0x43, 0x76,
-	0x09, 0x9b, 0x51, 0x36, 0xaf, 0x87, 0x16, 0xa8, 0x81, 0x2f, 0xe0, 0xdc, 0x72, 0x6c, 0x77, 0x49,
-	0x24, 0x99, 0xf9, 0xc5, 0xcd, 0x5d, 0x23, 0x02, 0x35, 0xf1, 0x25, 0xfc, 0x57, 0xf1, 0xe5, 0x83,
-	0xa5, 0xa0, 0x75, 0x95, 0xc1, 0xf0, 0x5d, 0x77, 0x4e, 0xf2, 0xa4, 0xf4, 0x9f, 0x75, 0x28, 0xa9,
-	0x4d, 0x84, 0x34, 0x6d, 0xd7, 0x9f, 0x11, 0x61, 0x6d, 0xdf, 0x47, 0x7b, 0x7a, 0x03, 0x15, 0x63,
-	0x96, 0x84, 0xa1, 0x7b, 0xbf, 0x37, 0x97, 0x1e, 0xa9, 0xcb, 0x1b, 0x7a, 0xd8, 0x5b, 0xb4, 0x92,
-	0x36, 0xaf, 0x3b, 0x8f, 0xed, 0x70, 0xbd, 0x7a, 0xcd, 0xbf, 0x21, 0x5f, 0x0e, 0xf2, 0x9f, 0x8f,
-	0x7f, 0x03, 0x00, 0x00, 0xff, 0xff, 0x3f, 0xff, 0xd7, 0x16, 0x59, 0x04, 0x00, 0x00,
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_types_proto_rawDesc,
+			NumEnums:      1,
+			NumMessages:   0,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_types_proto_goTypes,
+		DependencyIndexes: file_types_proto_depIdxs,
+		EnumInfos:         file_types_proto_enumTypes,
+	}.Build()
+	File_types_proto = out.File
+	file_types_proto_rawDesc = nil
+	file_types_proto_goTypes = nil
+	file_types_proto_depIdxs = nil
 }
diff --git a/cmixproto/types.proto b/cmixproto/types.proto
index d64600bbe301fad6852519326b6d2648d9ab4372..141ad7b831546b07ecc4e89bc4be802c00755df0 100644
--- a/cmixproto/types.proto
+++ b/cmixproto/types.proto
@@ -19,13 +19,6 @@ enum Type {
 
     // See proto buf documentation below
     TEXT_MESSAGE = 1;
-    // See proto buf
-    CHANNEL_MESSAGE = 2;
-
-
-    // Nickname request and response messages
-    NICKNAME_REQUEST = 6;
-    NICKNAME_RESPONSE = 7;
 
     // None of the UDB message types are proto bufs because I haven't had time
     // to migrate UDB fully to the new systems yet.
@@ -106,143 +99,17 @@ enum Type {
     // All of the seeds and compounds are in an ordered list, and they get
     // categorized and processed on the payment bot.
 
-    // To get a message of this type, call the methods in the wallet.
-    // TODO expose these methods over the API
-    PAYMENT_TRANSACTION = 20;
-    // See proto buf
-    PAYMENT_RESPONSE = 21;
-    // See proto buf
-    PAYMENT_INVOICE = 22;
-    // This message type includes only the message hash of the original
-    // invoice message. Since there are no fields to delimit, it's not a
-    // proto buffer. When the payee gets a receipt back, they know that the
-    // other person probably paid them, and to check the next published
-    // blockchain for the images of their coins to make sure.
-    // The wallet sends this message after receiving a PAYMENT_RESPONSE
-    // indicating success.
-    PAYMENT_RECEIPT = 23;
-
     // End to End Rekey message types
     // Trigger a rekey, this message is used locally in client only
     REKEY_TRIGGER = 30;
     // Rekey confirmation message. Sent by partner to confirm completion of a rekey
     REKEY_CONFIRM = 31;
-
-    // These are specialized messages that convey the information that
-    // the UI needs to know once the wallet's finished updating. They shouldn't
-    // go over the network. Types 9000-9999 are reserved for messages like this.
-
-    // This message type is a single fixed-length hash of the invoice
-    // that the client just received. The client can look up this hash in the
-    // inbound transaction list to display the most recently received invoice
-    // in the UI.
-    PAYMENT_INVOICE_UI = 9000;
-    // This message type is a single fixed-length hash of the original invoice
-    // that this client sent to the paying client. The UI can look up the
-    // corresponding transaction in the list of completed transactions and
-    // display payment success on the UI. The wallet sends this message
-    // locally after receiving a PAYMENT_RECEIPT message.
-    PAYMENT_RECEIPT_UI = 9001;
-}
-
-// Use this enumeration to get specific transactions from specific transaction
-// lists from the wallet
-enum TransactionListTag {
-
-    // Transactions in this list are invoices this user made to another user
-    // Most importantly, they include the preimage that this user wants fulfilled,
-    // but the image of this preimage is what the client will send in the invoice.
-    // Transactions enter this list when this user invoices another user.
-    OUTBOUND_REQUESTS = 0;
-
-    // Transactions in this list are invoices that this user received from
-    // other users. Most importantly, this includes the image that this user
-    // will fund.
-    INBOUND_REQUESTS = 1;
-
-    // Transactions in this list are currently processing on a payment bot.
-    // Transactions move from INBOUND_REQUESTS to PENDING_TRANSACTIONS after
-    // a Pay() call.
-    PENDING_TRANSACTIONS = 2;
-
-    // Transactions in this list are completed transactions that increased
-    // the value of this user's wallet. NOTE: They correspond to transactions
-    // originally in the OUTBOUND_REQUESTS list that went through.
-    COMPLETED_INBOUND_PAYMENTS = 3;
-
-    // Transactions in this list are completed transactions that decreased
-    // the value of this user's wallet. NOTE: They correspond to transactions
-    // originally in the INBOUND_REQUESTS list that went through.
-    COMPLETED_OUTBOUND_PAYMENTS = 4;
 }
 
-// Use this enumeration to request different sort orders of transaction lists
-// when getting all IDs in a transaction list
-enum TransactionListOrder {
-    // Most recently initiated transaction first
-    TIMESTAMP_DESCENDING = 0;
-    // Stalest transaction first
-    TIMESTAMP_ASCENDING = 1;
-    // Biggest transaction first
-    VALUE_DESCENDING = 2;
-    // Smallest transaction first
-    VALUE_ASCENDING = 3;
-}
-
-// Text message types
-
-// Is Type.TEXT_MESSAGE
-// Used for conveying simple text messages between users
-message TextMessage {
-    // Terminal text foreground color. Used by the console UI
-    sint32 color = 2;
-    // The message text itself. Varies in length
-    string message = 3;
-    // Timestamp (Unix time in seconds)
-    // You can use this to display the time when the other user sent the message
-    // TODO Remove this when all messages have timestamps
-    int64 time = 4;
-}
+message RekeyTrigger {
+    // PublicKey used in the registration
+    bytes publicKey = 1;
+    bytes SessionID = 2;
 
-// Is Type.CHANNEL_MESSAGE
-// This is the type of all messages that come from the channelbot.
-message ChannelMessage {
-    // This is the original speaker of the channel message, who sent the
-    // message to the channel bot.
-    bytes speakerID = 3;
-    // This is a serialized parse message under the hood. When writing a
-    // listener for a channel message on a client, you need to unpack the
-    // serialized parse message and rebroadcast it through the listeners.
-    bytes message = 4;
-}
-
-// Payment message types
-
-// Is Type.PAYMENT_RESPONSE
-message PaymentResponse {
-    // Indicates whether the payment succeeded or failed
-    bool success = 1;
-    // Response message from the payment bot. You should display this to the
-    // user.
-    string response = 2;
-    // TODO Is it correct to use the whole hash?
-    // This is the hash of the payment message that the payment bot received.
-    // The client uses it to remove the correct pending transaction from the
-    // list of pending transactions.
-    bytes ID = 3;
-}
 
-// Is Type.PAYMENT_INVOICE
-message PaymentInvoice {
-    // Timestamp (Unix time in seconds)
-    // Not currently used but could be useful for the user to verify the
-    // correctness of an invoice.
-    int64 time = 1;
-    // This is a single compound coin that the invoicer wants to be funded. The
-    // payer should send a message to the payment bot to fund this compound, if
-    // they wish to pay the payee.
-    bytes createdCoin = 2;
-    // The payee should fill this out to describe what the payment is for or
-    // about.
-    string memo = 3;
 }
diff --git a/context/networkManager.go b/context/networkManager.go
index 910cd11241f90c934eea954ad72f583dd23d9ad1..595abefb364f0aa6f631600f41b721ec8b5da69d 100644
--- a/context/networkManager.go
+++ b/context/networkManager.go
@@ -1,17 +1,17 @@
 package context
 
 import (
+	"gitlab.com/elixxir/client/context/params"
+	"gitlab.com/elixxir/client/context/stoppable"
 	"gitlab.com/elixxir/comms/network"
 	"gitlab.com/elixxir/primitives/format"
 	"gitlab.com/xx_network/primitives/id"
 )
 
 type NetworkManager interface {
-	SendE2E(m Message) ([]id.Round, error)
+	SendE2E(m Message, e2eP params.E2E, cmixP params.CMIX) ([]id.Round, error)
 	SendUnsafe(m Message) ([]id.Round, error)
 	SendCMIX(message format.Message) (id.Round, error)
-	GetRekeyChan() chan id.ID
 	GetInstance() *network.Instance
-	//placeholder to stop active threads
-	Kill() bool
+	Stoppable() stoppable.Stoppable
 }
diff --git a/context/params/CMIX.go b/context/params/CMIX.go
new file mode 100644
index 0000000000000000000000000000000000000000..cefc970ce4dc1f1756805fd9d22b0a37775ec934
--- /dev/null
+++ b/context/params/CMIX.go
@@ -0,0 +1,9 @@
+package params
+
+type CMIX struct {
+	Retries uint
+}
+
+func GetDefaultCMIX() CMIX {
+	return CMIX{Retries: 3}
+}
diff --git a/context/params/E2E.go b/context/params/E2E.go
new file mode 100644
index 0000000000000000000000000000000000000000..2d0d0ee9cd3b9f24aaa4f3c499ae9fdfb77ec279
--- /dev/null
+++ b/context/params/E2E.go
@@ -0,0 +1,29 @@
+package params
+
+import "fmt"
+
+type E2E struct {
+	Type SendType
+}
+
+func GetDefaultE2E() E2E {
+	return E2E{Type: Standard}
+}
+
+type SendType uint8
+
+const (
+	Standard    SendType = 0
+	KeyExchange SendType = 1
+)
+
+func (st SendType) String() string {
+	switch st {
+	case Standard:
+		return "Standard"
+	case KeyExchange:
+		return "KeyExchange"
+	default:
+		return fmt.Sprintf("Unknown SendType %v", uint8(st))
+	}
+}
diff --git a/context/stoppable/multi.go b/context/stoppable/multi.go
index 986787d5055ebc24115f7468ea554877fa0c391e..f30c484758a5689e6fd0388b6b996f6bf764a9e0 100644
--- a/context/stoppable/multi.go
+++ b/context/stoppable/multi.go
@@ -1,7 +1,9 @@
 package stoppable
 
 import (
+	"fmt"
 	"github.com/pkg/errors"
+	jww "github.com/spf13/jwalterweatherman"
 	"sync"
 	"sync/atomic"
 	"time"
@@ -65,10 +67,10 @@ func (m *Multi) Close(timeout time.Duration) error {
 
 	wg := &sync.WaitGroup{}
 
-	for _, stopable := range m.stoppables {
+	for _, stoppable := range m.stoppables {
 		wg.Add(1)
 		go func() {
-			if stopable.Close(timeout) != nil {
+			if stoppable.Close(timeout) != nil {
 				atomic.AddUint32(&numErrors, 1)
 			}
 			wg.Done()
@@ -80,8 +82,10 @@ func (m *Multi) Close(timeout time.Duration) error {
 	atomic.StoreUint32(&m.running, 0)
 
 	if numErrors > 0 {
-		return errors.Errorf("MultiStopper %s failed to close "+
+		errStr := fmt.Sprintf("MultiStopper %s failed to close "+
 			"%v/%v stoppers", m.name, numErrors, len(m.stoppables))
+		jww.ERROR.Println(errStr)
+		return errors.New(errStr)
 	}
 
 	return nil
diff --git a/context/utility/trackResults.go b/context/utility/trackResults.go
new file mode 100644
index 0000000000000000000000000000000000000000..200615a6eb15db6704bba6b4bf5bfe05ee3661da
--- /dev/null
+++ b/context/utility/trackResults.go
@@ -0,0 +1,23 @@
+package utility
+
+import (
+	ds "gitlab.com/elixxir/comms/network/dataStructures"
+	"gitlab.com/elixxir/primitives/states"
+)
+
+// Function to track the results of events. It returns true if the collection of
+// events resolved well, and then a count of how many rounds failed and how
+// many roundEvents timed out.
+func TrackResults(resultsCh chan ds.EventReturn, numResults int) (bool, int, int) {
+	numTimeOut, numRoundFail := 0, 0
+	for numResponses := 0; numResponses < numResults; numResponses++ {
+		er := <-resultsCh
+		if er.TimedOut {
+			numTimeOut++
+		} else if states.Round(er.RoundInfo.State) == states.FAILED {
+			numRoundFail++
+		}
+	}
+
+	return (numTimeOut + numRoundFail) > 0, numRoundFail, numTimeOut
+}
diff --git a/go.mod b/go.mod
index a7b3c09e61d611f872e96573ca65bc965c18159f..6d29483ab5a4dd8fdc78fbb6dbf97243d0562731 100644
--- a/go.mod
+++ b/go.mod
@@ -15,7 +15,7 @@ require (
 	github.com/spf13/jwalterweatherman v1.1.0
 	github.com/spf13/pflag v1.0.5 // indirect
 	github.com/spf13/viper v1.6.2
-	gitlab.com/elixxir/comms v0.0.0-20200827193018-c0911a1a1ec0
+	gitlab.com/elixxir/comms v0.0.0-20200828220824-6e9c8abca9bc
 	gitlab.com/elixxir/crypto v0.0.0-20200827170914-14227f20900c
 	gitlab.com/elixxir/ekv v0.1.1
 	gitlab.com/elixxir/primitives v0.0.0-20200827170420-5d50351f99b4
diff --git a/go.sum b/go.sum
index 456e23218b6fda4856698cd2ea23165f3f313b63..bd7749dbf83ec7bfa18f59a8a169948b67762d0f 100644
--- a/go.sum
+++ b/go.sum
@@ -171,6 +171,8 @@ gitlab.com/elixxir/comms v0.0.0-20200827170208-d1f872422b7e h1:BEBxLOW6yMdT53rBv
 gitlab.com/elixxir/comms v0.0.0-20200827170208-d1f872422b7e/go.mod h1:HW3Ige10aeJeyb2fcQ/YOBPiyzY/4jHau1Cj6/1WBHc=
 gitlab.com/elixxir/comms v0.0.0-20200827193018-c0911a1a1ec0 h1:C0P5VqrgGcTuV9kCicxakHLSUFN3qNk55O/nrBmY0fk=
 gitlab.com/elixxir/comms v0.0.0-20200827193018-c0911a1a1ec0/go.mod h1:HW3Ige10aeJeyb2fcQ/YOBPiyzY/4jHau1Cj6/1WBHc=
+gitlab.com/elixxir/comms v0.0.0-20200828220824-6e9c8abca9bc h1:+Plp0oXKNTvHUZkoYusFyLELlyXgAKn3O5I3389xv+A=
+gitlab.com/elixxir/comms v0.0.0-20200828220824-6e9c8abca9bc/go.mod h1:HW3Ige10aeJeyb2fcQ/YOBPiyzY/4jHau1Cj6/1WBHc=
 gitlab.com/elixxir/crypto v0.0.0-20200804182833-984246dea2c4 h1:28ftZDeYEko7xptCZzeFWS1Iam95dj46TWFVVlKmw6A=
 gitlab.com/elixxir/crypto v0.0.0-20200804182833-984246dea2c4/go.mod h1:ucm9SFKJo+K0N2GwRRpaNr+tKXMIOVWzmyUD0SbOu2c=
 gitlab.com/elixxir/crypto v0.0.0-20200805174804-bdf909f2a16d/go.mod h1:cu6uNoANVLV0J6HyTL6KqVtVyh9SHU1RjJhytYlsbVQ=
diff --git a/io/keyExchange/init.go b/io/keyExchange/init.go
new file mode 100644
index 0000000000000000000000000000000000000000..7159d57803c07649e28612d4bf6b6dc9372593ae
--- /dev/null
+++ b/io/keyExchange/init.go
@@ -0,0 +1,15 @@
+package keyExchange
+
+import (
+	"gitlab.com/elixxir/client/context"
+	"gitlab.com/elixxir/client/context/stoppable"
+	"gitlab.com/elixxir/primitives/switchboard"
+)
+
+func Init(ctx *context.Context) stoppable.Stoppable {
+
+	//register the rekey request thread
+	rekeyRequestCh := make(chan switchboard.Item, 10)
+	ctx.Switchboard.RegisterChannel()
+
+}
diff --git a/io/keyExchange/rekey.go b/io/keyExchange/rekey.go
index 9e1334bfcea646ecd8c0e20455097f8cc20b0cad..45092934626f433f1b76ea395062cd90d25d028c 100644
--- a/io/keyExchange/rekey.go
+++ b/io/keyExchange/rekey.go
@@ -1,33 +1,60 @@
 package keyExchange
 
 import (
+	"github.com/pkg/errors"
 	"gitlab.com/elixxir/client/context"
+	"gitlab.com/elixxir/client/context/params"
+	"gitlab.com/elixxir/client/context/utility"
 	"gitlab.com/elixxir/client/storage/e2e"
+	ds "gitlab.com/elixxir/comms/network/dataStructures"
 	"gitlab.com/elixxir/crypto/diffieHellman"
-	"gitlab.com/xx_network/primitives/id"
+	"gitlab.com/elixxir/primitives/states"
 	jww "github.com/spf13/jwalterweatherman"
+	"time"
 )
 
-type KeyExchangeSend interface {
-	SendRekey(m context.Message) ([]id.Round, error)
+func CheckKeyExchanges(ctx *context.Context, manager *e2e.Manager) {
+	sessions := manager.TriggerNegotiations()
+	for _, ses := range sessions {
+		locakSes := ses
+		go trigger(ctx, manager, locakSes)
+	}
 }
 
-func Rekey(ctx *context.Context, partner *id.ID, kx KeyExchangeSend) {
-	e2eStore := ctx.Session.E2e()
-
-	//get the key manager
-	manager, err := e2eStore.GetPartner(partner)
-	if err != nil {
-		jww.ERROR.Printf("Failed to Rekey with %s: Failed to retrieve "+
-			"key manager: %s", partner, err)
+// There are two types of key negotiations that can be triggered, creating a new
+// session and negotiation, or resenting a negotiation for an already created
+// session. They run the same negotiation, the former does it on a newly created
+// session while the latter on an extand
+func trigger(ctx *context.Context, manager *e2e.Manager, session *e2e.Session) {
+	var negotiatingSession *e2e.Session
+	switch session.ConfirmationStatus() {
+	// If the passed session is triggering a negotiation on a new session to
+	// replace itself, then create the session
+	case e2e.NewSessionTriggered:
+		//create the session, pass a nil private key to generate a new one
+		negotiatingSession = manager.NewSendSession(nil, e2e.GetDefaultSessionParams())
+		//move the state of the triggering session forward
+		session.SetNegotiationStatus(e2e.NewSessionCreated)
+	// If the session has not successfully negotiated, redo its negotiation
+	case e2e.Unconfirmed:
+		negotiatingSession = session
+	default:
+		jww.FATAL.Panicf("Session %s provided invalid e2e "+
+			"negotiating status: %s", session, session.ConfirmationStatus())
 	}
 
-	//create the session
-	session, err := manager.NewSendSession(nil, e2e.GetDefaultSessionParams())
+	// send the rekey notification to the partner
+	err := negotiate(ctx, negotiatingSession)
+	// if sending the negotiation fails, revert the state of the session to
+	// unconfirmed so it will be triggered in the future
 	if err != nil {
-		jww.ERROR.Printf("Failed to Rekey with %s: Failed to make new "+
-			"session: %s", partner, err)
+		jww.ERROR.Printf("Failed to do Key Negotiation: %s", err)
+		negotiatingSession.SetNegotiationStatus(e2e.Unconfirmed)
 	}
+}
+
+func negotiate(ctx *context.Context, session *e2e.Session) error {
+	e2eStore := ctx.Session.E2e()
 
 	//generate public key
 	pubKey := diffieHellman.GeneratePublicKey(session.GetMyPrivKey(),
@@ -35,14 +62,59 @@ func Rekey(ctx *context.Context, partner *id.ID, kx KeyExchangeSend) {
 
 	//send session
 	m := context.Message{
-		Recipient:   partner,
+		Recipient:   session.GetPartner(),
 		Payload:     pubKey.Bytes(),
 		MessageType: 42,
 	}
 
-	rounds, err := kx.SendRekey(m)
+	//send the message under the key exchange
+	e2eParams := params.GetDefaultE2E()
+	e2eParams.Type = params.KeyExchange
+	cmixParams := params.GetDefaultCMIX()
+	cmixParams.Retries = 20
+
+	rounds, err := ctx.Manager.SendE2E(m, e2eParams, cmixParams)
+	// If the send fails, returns the error so it can be handled. The caller
+	// should ensure the calling session is in a state where the Rekey will
+	// be triggered next time a key is used
 	if err != nil {
+		return errors.Errorf("Failed to send the key negotation message "+
+			"for %s: %s", session, err)
+	}
+
+	//create the runner which will handle the result of sending the messages
+	sendResults := make(chan ds.EventReturn, len(rounds))
+
+	//Register the event for all rounds
+	roundEvents := ctx.Manager.GetInstance().GetRoundEvents()
+	for _, r := range rounds {
+		roundEvents.AddRoundEventChan(r, sendResults, 1*time.Minute,
+			states.COMPLETED, states.FAILED)
+	}
 
+	//Start the thread which will handle the outcome of the send
+	go trackNegotiationResult(sendResults, len(rounds), session)
+}
+
+func trackNegotiationResult(resultsCh chan ds.EventReturn, numResults int, session *e2e.Session) {
+	success, numTimeOut, numRoundFail := utility.TrackResults(resultsCh, numResults)
+
+	// If a single partition of the Key Negotiation request does not
+	// transmit, the partner cannot read the result. Log the error and set
+	// the session as unconfirmed so it will re-trigger the negotiation
+	if !success {
+		jww.ERROR.Printf("Key Negotiation for %s failed to "+
+			"transmit %v/%v paritions: %v round failures, %v timeouts",
+			session, numRoundFail+numTimeOut, numResults, numRoundFail,
+			numTimeOut)
+		session.SetNegotiationStatus(e2e.Unconfirmed)
+		return
 	}
 
+	// otherwise, the transmission is a success and this should be denoted
+	// in the session and the log
+	jww.INFO.Printf("Key Negotiation transmission for %s sucesfull",
+		session)
+	session.SetNegotiationStatus(e2e.Sent)
 }
+
diff --git a/storage/cmix/key.go b/storage/cmix/key.go
index f2078b678e1d78902337663ce4cede06b5fddeff..2042bc56975504f3d9bfdda2a5ba4738a656b2bd 100644
--- a/storage/cmix/key.go
+++ b/storage/cmix/key.go
@@ -1,6 +1,7 @@
 package cmix
 
 import (
+	jww "github.com/spf13/jwalterweatherman"
 	"gitlab.com/elixxir/client/storage/versioned"
 	"gitlab.com/elixxir/crypto/cyclic"
 	"gitlab.com/xx_network/primitives/id"
@@ -70,9 +71,11 @@ func (k *key) save() error {
 }
 
 // deletes the key from the versioned keystore
-func (k *key) delete(kv *versioned.KV, id *id.ID) error {
+func (k *key) delete(kv *versioned.KV, id *id.ID) {
 	key := keyKey(id)
-	return kv.Delete(key)
+	if err := kv.Delete(key); err != nil {
+		jww.FATAL.Panicf("Failed to delete key %s: %s", k, err)
+	}
 }
 
 // makes a binary representation of the given key in the keystore
@@ -86,6 +89,12 @@ func (k *key) unmarshal(b []byte) error {
 	return k.k.GobDecode(b)
 }
 
+// Adheres to the stringer interface
+func (k *key) String() string {
+	return k.storeKey
+
+}
+
 // generates the key used in the keystore for the given key
 func keyKey(id *id.ID) string {
 	return "nodeKey:" + id.String()
diff --git a/storage/cmix/store.go b/storage/cmix/store.go
index 9ca51397c7cd572a343484f217e0bd43e6158c0d..561968779f2a2b4701f001867e656fac70c6c1e0 100644
--- a/storage/cmix/store.go
+++ b/storage/cmix/store.go
@@ -17,6 +17,7 @@ import (
 	"gitlab.com/xx_network/primitives/id"
 	"sync"
 	"time"
+	jww "github.com/spf13/jwalterweatherman"
 )
 
 const currentStoreVersion = 0
@@ -95,37 +96,38 @@ func LoadStore(kv *versioned.KV) (*Store, error) {
 
 // adds the key for a round to the cmix storage object. Saves the updated list
 // of nodes and the key to disk
-func (s *Store) Add(nid *id.ID, k *cyclic.Int) error {
+func (s *Store) Add(nid *id.ID, k *cyclic.Int) {
 	s.mux.Lock()
 	defer s.mux.Unlock()
 
 	nodekey, err := NewKey(s.kv, k, nid)
 	if err != nil {
-		return err
+		jww.FATAL.Panicf("Failed to make nodeKey for %s: %s", nid, err)
 	}
 
 	s.nodes[*nid] = nodekey
-	return s.save()
+	if err = s.save(); err != nil {
+		jww.FATAL.Panicf("Failed to save nodeKey list for %s: %s", nid, err)
+	}
 }
 
 // Remove a Node key from the nodes map and save
-func (s *Store) Remove(nid *id.ID) error {
+func (s *Store) Remove(nid *id.ID) {
 	s.mux.Lock()
 	defer s.mux.Unlock()
 
 	nodekey, ok := s.nodes[*nid]
 	if !ok {
-		return errors.New("Cannot remove, no key with given ID found")
+		return
 	}
 
-	err := nodekey.delete(s.kv, nid)
-	if err != nil {
-		return err
-	}
+	nodekey.delete(s.kv, nid)
 
 	delete(s.nodes, *nid)
 
-	return s.save()
+	if err := s.save(); err != nil {
+		jww.FATAL.Panicf("Failed    make nodeKey for %s: %s", nid, err)
+	}
 }
 
 //Returns a RoundKeys for the topology and a list of nodes it did not have a key for
diff --git a/storage/e2e/confirmation.go b/storage/e2e/confirmation.go
deleted file mode 100644
index 72ba4060e2d03f49a54608f7d898a8eecbce7ac7..0000000000000000000000000000000000000000
--- a/storage/e2e/confirmation.go
+++ /dev/null
@@ -1,9 +0,0 @@
-package e2e
-
-type Confirmation uint8
-
-const (
-	Unconfimed Confirmation = 0
-	InProgress              = 1
-	Confirmed               = 2
-)
diff --git a/storage/e2e/key.go b/storage/e2e/key.go
index 4dfa8396553735ae77bec55075d83d40a77b66f5..539a681fb476ab0d1b9494840483b239c5b25994 100644
--- a/storage/e2e/key.go
+++ b/storage/e2e/key.go
@@ -95,8 +95,8 @@ func (k *Key) Decrypt(msg format.Message) (format.Message, error) {
 }
 
 // Sets the key as used
-func (k *Key) denoteUse() error {
-	return k.session.useKey(k.keyNum)
+func (k *Key) denoteUse() {
+	k.session.useKey(k.keyNum)
 }
 
 // Generates the key and returns it
diff --git a/storage/e2e/manager.go b/storage/e2e/manager.go
index 1606ef8ca5b8f7c53c1e4001284555c44f4cf37e..26a437d9a1fc98ff2f907555d97feb3fe2042175 100644
--- a/storage/e2e/manager.go
+++ b/storage/e2e/manager.go
@@ -2,8 +2,10 @@ package e2e
 
 import (
 	"github.com/pkg/errors"
+	"gitlab.com/elixxir/client/context/params"
 	"gitlab.com/elixxir/crypto/cyclic"
 	"gitlab.com/xx_network/primitives/id"
+	jww "github.com/spf13/jwalterweatherman"
 )
 
 type Manager struct {
@@ -17,7 +19,7 @@ type Manager struct {
 
 // create the manager and its first send and receive sessions
 func newManager(ctx *context, partnerID *id.ID, myPrivKey *cyclic.Int,
-	partnerPubKey *cyclic.Int, sendParams, receiveParams SessionParams) (*Manager, error) {
+	partnerPubKey *cyclic.Int, sendParams, receiveParams SessionParams) *Manager {
 	m := &Manager{
 		ctx:     ctx,
 		partner: partnerID,
@@ -26,31 +28,15 @@ func newManager(ctx *context, partnerID *id.ID, myPrivKey *cyclic.Int,
 	m.send = NewSessionBuff(m, "send")
 	m.receive = NewSessionBuff(m, "receive")
 
-	sendSession, err := newSession(m, myPrivKey, partnerPubKey, sendParams, Send)
-	if err != nil {
-		return nil, errors.WithMessage(err,
-			"Failed to create the send session")
-	}
+	sendSession := newSession(m, myPrivKey, partnerPubKey, sendParams, Send)
 
-	err = m.send.AddSession(sendSession)
-	if err != nil {
-		return nil, errors.WithMessage(err,
-			"Failed to add the send session to buffer")
-	}
+	m.send.AddSession(sendSession)
 
-	receiveSession, err := newSession(m, myPrivKey, partnerPubKey, receiveParams, Receive)
-	if err != nil {
-		return nil, errors.WithMessage(err,
-			"Failed to create the receive session")
-	}
+	receiveSession := newSession(m, myPrivKey, partnerPubKey, receiveParams, Receive)
 
-	err = m.receive.AddSession(receiveSession)
-	if err != nil {
-		return nil, errors.WithMessage(err,
-			"Failed to add the receive session to buffer")
-	}
+	m.receive.AddSession(receiveSession)
 
-	return m, nil
+	return m
 }
 
 //loads a manager and all buffers and sessions from disk
@@ -87,57 +73,57 @@ func (m *Manager) GetPartnerID() *id.ID {
 
 // creates a new receive session using the latest private key this user has sent
 // and the new public key received from the partner.
-func (m *Manager) NewReceiveSession(partnerPubKey *cyclic.Int, params SessionParams) error {
+func (m *Manager) NewReceiveSession(partnerPubKey *cyclic.Int, params SessionParams) *Session {
 	//find your last confirmed private key
-	myPrivKey := m.send.GetNewestConfirmed().GetMyPrivKey()
+	myPrivKey := m.send.GetNewestRekeyableSession().GetMyPrivKey()
 
 	//create the session
-	session, err := newSession(m, myPrivKey, partnerPubKey, params, Receive)
-
-	if err != nil {
-		return err
-	}
+	session := newSession(m, myPrivKey, partnerPubKey, params, Receive)
 
 	//add the session to the buffer
-	err = m.receive.AddSession(session)
-	if err != nil {
-		//delete the session if it failed to add to the buffer
-		session.Delete()
-	}
+	m.receive.AddSession(session)
 
-	return err
+	return session
 }
 
 // creates a new receive session using the latest public key received from the
 // partner and a mew private key for the user
 // passing in a private key is optional. a private key will be generated if
 // none is passed
-func (m *Manager) NewSendSession(myPrivKey *cyclic.Int, params SessionParams) (*Session, error) {
+func (m *Manager) NewSendSession(myPrivKey *cyclic.Int, params SessionParams) *Session {
 	//find the latest public key from the other party
-	partnerPubKey := m.receive.GetNewestConfirmed().partnerPubKey
+	partnerPubKey := m.receive.GetNewestRekeyableSession().partnerPubKey
 
-	session, err := newSession(m, myPrivKey, partnerPubKey, params, Send)
-	if err != nil {
-		return nil, err
-	}
+	//create the session
+	session := newSession(m, myPrivKey, partnerPubKey, params, Send)
 
-	//add the session to the buffer
-	err = m.send.AddSession(session)
-	if err != nil {
-		//delete the session if it failed to add to the buffer
-		session.Delete()
-		return nil, err
-	}
+	//add the session to the send session buffer and return
+	m.send.AddSession(session)
 
-	return session, nil
+	return session
 }
 
-// gets the session buffer for message reception
-func (m *Manager) GetSendingSession() *Session {
-	return m.send.GetSessionForSending()
+// gets the correct session to send with depending on the type of send
+func (m *Manager) GetSendingSession(st params.SendType) *Session {
+	switch st {
+	case params.Standard:
+		return m.send.GetSessionForSending()
+	case params.KeyExchange:
+		return m.send.GetNewestRekeyableSession()
+	default:
+		jww.ERROR.Printf("Cannot get session for invalid Send Type: %s",
+			st)
+	}
+
+	return nil
 }
 
 // Confirms a send session is known about by the partner
 func (m *Manager) Confirm(sid SessionID) error {
 	return m.send.Confirm(sid)
 }
+
+// returns a list of key exchange operations if any are necessary
+func (m *Manager) TriggerNegotiations() []*Session {
+	return m.send.TriggerNegotiation()
+}
diff --git a/storage/e2e/negotiation.go b/storage/e2e/negotiation.go
new file mode 100644
index 0000000000000000000000000000000000000000..ad6431c3cbce34f816b661118f3782d8fdcab331
--- /dev/null
+++ b/storage/e2e/negotiation.go
@@ -0,0 +1,37 @@
+package e2e
+
+import "fmt"
+
+// Fix-me: this solution is incompatible with offline sending, when that is
+// added, a session which has not been confirmed will never trigger the
+// creation of new session, the Unconfirmed->Confirmed and
+// Confirmed->NewSessionCreated most likely need to be two separate enums
+// tracked separately
+type Negotiation uint8
+
+const (
+	Unconfirmed         Negotiation = 0
+	Sending                         = 1
+	Sent                            = 2
+	Confirmed                       = 3
+	NewSessionTriggered             = 4
+	NewSessionCreated               = 5
+)
+
+//Adherence to stringer interface
+func (c Negotiation) String() string {
+	switch c {
+	case Unconfirmed:
+		return "Unconfirmed"
+	case Sending:
+		return "Sending"
+	case Sent:
+		return "Sent"
+	case Confirmed:
+		return "Confirmed"
+	default:
+		return fmt.Sprintf("Unknown Negotiation %v", uint8(c))
+	}
+}
+
+type exchangeType uint8
diff --git a/storage/e2e/session.go b/storage/e2e/session.go
index 876d384dccdc2d738289d395a047f5fea05efe11..1129eb5921d1c1ef8a1bb378ce1977eb1daa5bc7 100644
--- a/storage/e2e/session.go
+++ b/storage/e2e/session.go
@@ -2,6 +2,7 @@ package e2e
 
 import (
 	"encoding/json"
+	"fmt"
 	"github.com/pkg/errors"
 	jww "github.com/spf13/jwalterweatherman"
 	"gitlab.com/elixxir/client/storage/versioned"
@@ -10,6 +11,7 @@ import (
 	dh "gitlab.com/elixxir/crypto/diffieHellman"
 	"gitlab.com/elixxir/crypto/e2e"
 	"gitlab.com/elixxir/crypto/hash"
+	"gitlab.com/xx_network/primitives/id"
 	"sync"
 	"time"
 )
@@ -34,7 +36,7 @@ type Session struct {
 	partnerPubKey *cyclic.Int
 
 	//denotes if the other party has confirmed this key
-	confirmStatus Confirmation
+	negotiationStatus Negotiation
 
 	// Value of the counter at which a rekey is triggered
 	ttl uint32
@@ -72,27 +74,32 @@ type SessionDisk struct {
 
 /*CONSTRUCTORS*/
 //Generator which creates all keys and structures
-func newSession(manager *Manager, myPrivKey *cyclic.Int, partnerPubKey *cyclic.Int, params SessionParams, t SessionType) (*Session, error) {
-	session := &Session{
-		params:        params,
-		manager:       manager,
-		t:             t,
-		myPrivKey:     myPrivKey,
-		partnerPubKey: partnerPubKey,
-		confirmed:     t == Receive,
+func newSession(manager *Manager, myPrivKey *cyclic.Int, partnerPubKey *cyclic.Int, params SessionParams, t SessionType) *Session {
+
+	confirmation := Unconfirmed
+	if t == Receive {
+		confirmation = Confirmed
 	}
 
-	err := session.generate()
-	if err != nil {
-		return nil, err
+
+	session := &Session{
+		params:            params,
+		manager:           manager,
+		t:                 t,
+		myPrivKey:         myPrivKey,
+		partnerPubKey:     partnerPubKey,
+		negotiationStatus: confirmation,
 	}
 
-	err = session.save()
+	session.generate()
+
+	err := session.save()
 	if err != nil {
-		return nil, err
+		jww.FATAL.Printf("Failed to make new session for Partner %s: %s",
+			manager.partner, err)
 	}
 
-	return session, nil
+	return session
 }
 
 // Load session and state vector from kv and populate runtime fields
@@ -189,6 +196,11 @@ func (s *Session) GetID() SessionID {
 	return sid
 }
 
+// returns the ID of the partner for this session
+func (s *Session) GetPartner() *id.ID {
+	return s.manager.partner
+}
+
 //ekv functions
 func (s *Session) marshal() ([]byte, error) {
 	sd := SessionDisk{}
@@ -198,7 +210,17 @@ func (s *Session) marshal() ([]byte, error) {
 	sd.BaseKey = s.baseKey.Bytes()
 	sd.MyPrivKey = s.myPrivKey.Bytes()
 	sd.PartnerPubKey = s.partnerPubKey.Bytes()
-	sd.Confirmed = s.confirmed
+
+	// assume in progress confirmations and session creations have failed on
+	// reset, therefore do not store their pending progress
+	if s.negotiationStatus == Sending {
+		sd.Confirmation = uint8(Unconfirmed)
+	} else if s.negotiationStatus == NewSessionTriggered {
+		sd.Confirmation = uint8(Confirmed)
+	} else {
+		sd.Confirmation = uint8(s.negotiationStatus)
+	}
+
 	sd.TTL = s.ttl
 
 	return json.Marshal(&sd)
@@ -221,7 +243,7 @@ func (s *Session) unmarshal(b []byte) error {
 	s.baseKey = grp.NewIntFromBytes(sd.BaseKey)
 	s.myPrivKey = grp.NewIntFromBytes(sd.MyPrivKey)
 	s.partnerPubKey = grp.NewIntFromBytes(sd.PartnerPubKey)
-	s.confirmed = sd.Confirmed
+	s.negotiationStatus = Negotiation(sd.Confirmation)
 	s.ttl = sd.TTL
 
 	statesKey := makeStateVectorKey(keyEKVPrefix, s.GetID())
@@ -262,50 +284,160 @@ func (s *Session) PopReKey() (*Key, error) {
 // returns the state of the session, which denotes if the Session is active,
 // functional but in need of a rekey, empty of send key, or empty of rekeys
 func (s *Session) Status() Status {
-	if s.keyState.GetNumAvailable() == 0 {
+	// copy the num available so it stays consistent as this function does its
+	// checks
+	numAvailable := s.keyState.GetNumAvailable()
+
+	if numAvailable == 0 {
 		return RekeyEmpty
-	} else if s.keyState.GetNumAvailable() <= uint32(s.params.NumRekeys) {
+	} else if numAvailable <= uint32(s.params.NumRekeys) {
 		return Empty
-	} else if s.keyState.GetNumAvailable() <= s.keyState.GetNumKeys()-s.ttl {
+		// do not need to make a copy of getNumKeys becasue it is static and
+		// only used once
+	} else if numAvailable <= s.keyState.GetNumKeys()-s.ttl {
 		return RekeyNeeded
 	} else {
 		return Active
 	}
 }
 
-// returns the state of the session, which denotes if the Session is active,
-// functional but in need of a rekey, empty of send key, or empty of rekeys
-func (s *Session) IsReKeyNeeded() bool {
-	return s.keyState.GetNumAvailable() == s.ttl
+// Sets the negotiation status, this tracks the state of the key negotiation,
+// only certain movements are allowed
+//   Unconfirmed <--> Sending --> Sent --> Confirmed <--> NewSessionTriggered --> NewSessionCreated
+//
+// Saves the session unless the status is sending so that on reload the rekey
+// will be redone if it was in the process of sending
+
+// Moving from Unconfirmed to Sending and from Confirmed to NewSessionTriggered
+// is handled by  Session.triggerNegotiation() which is called by the
+// Manager as part of Manager.TriggerNegotiations() and will be rejected
+// from this function
+
+var legalStateChanges = [][]bool{
+	{false, false, false, false, false, false},
+	{true, false, true, true, false, false},
+	{false, false, false, true, false, false},
+	{false, false, false, false, false, false},
+	{false, false, false, true, false, true},
+	{false, false, false, false, false, false},
+}
+
+func (s *Session) SetNegotiationStatus(status Negotiation) {
+	s.mux.Lock()
+	defer s.mux.Unlock()
+	//only allow the correct state changes to propagate
+	if !legalStateChanges[s.negotiationStatus][status] {
+		jww.FATAL.Panicf("Negotiation status change from %s to %s "+
+			"is not valid", s.negotiationStatus, status)
+	}
+
+	// the states of Sending and NewSessionTriggered are not saved to disk when
+	// moved from Unconfirmed or Confirmed respectively so the actions are
+	// re-triggered if there is a crash and reload. As a result, a save when
+	// reverting states is unnecessary
+	save := !((s.negotiationStatus == Sending && status == Unconfirmed) ||
+		(s.negotiationStatus == NewSessionTriggered && status == Confirmed))
+
+	//change the state
+	s.negotiationStatus = status
+
+	//save the status if appropriate
+	if save {
+		if err := s.save(); err != nil {
+			jww.FATAL.Printf("Failed to save Session %s when moving from %s to %s")
+		}
+	}
+}
+
+// This function, in a mostly thread safe manner, checks if the session needs a
+// negotiation, returns if it does while updating the session to denote the
+// negotiation was triggered
+// WARNING: This function relies on proper action by the caller for data safety.
+// When triggering the creation of a new session (the first case) it does not
+// store to disk the fact that it has triggered the session. This is because
+// every session should only trigger one other session and in the event that
+// session trigger does not resolve before a crash, by not storing it the
+// trigger will automatically happen again when reloading after the crash.
+// In order to ensure the session creation is not triggered again after the
+// reload, it is the responsibility of the caller to call
+// Session.SetConfirmationStatus(NewSessionCreated) .
+func (s *Session) triggerNegotiation() bool {
+	// Due to the fact that a read lock cannot be transitioned to a
+	// write lock, the state checks need to happen a second time because it
+	// is possible for another thread to take the read lock and update the
+	// state between this thread releasing it and regaining it again. In this
+	// case, such double locking is preferable because the majority of the time,
+	// the checked cases will turn out to be false.
+	s.mux.RLock()
+	//trigger a rekey to create a new session
+	if s.keyState.GetNumAvailable() >= s.ttl && s.negotiationStatus == Confirmed {
+		s.mux.RUnlock()
+		s.mux.Lock()
+		if s.keyState.GetNumAvailable() >= s.ttl && s.negotiationStatus == Confirmed {
+			s.negotiationStatus = NewSessionTriggered
+			// no save is make after the update because we do not want this state
+			// saved to disk. The caller will shortly execute the operation,
+			// and then move to the next state. If a crash occurs before, by not
+			// storing this state this operation will be repeated after reload
+			// The save function has been modified so if another call causes a
+			// save, "NewSessionTriggerd" will be overwritten with "Confirmed"
+			// in the saved data.
+			s.mux.Unlock()
+			return true
+		} else {
+			s.mux.Unlock()
+			return false
+		}
+		// retrigger this sessions negotiation
+	} else if s.negotiationStatus == Unconfirmed {
+		s.mux.RUnlock()
+		s.mux.Lock()
+		if s.negotiationStatus == Unconfirmed {
+			s.negotiationStatus = Sending
+			// no save is make after the update because we do not want this state
+			// saved to disk. The caller will shortly execute the operation,
+			// and then move to the next state. If a crash occurs before, by not
+			// storing this state this operation will be repeated after reload
+			// The save function has been modified so if another call causes a
+			// save, "Sending" will be overwritten with "Unconfirmed"
+			// in the saved data.
+			s.mux.Unlock()
+			return true
+		} else {
+			s.mux.Unlock()
+			return false
+		}
+	}
+	s.mux.RUnlock()
+	return true
 }
 
 // checks if the session has been confirmed
-func (s *Session) IsConfirmed() bool {
+func (s *Session) ConfirmationStatus() Negotiation {
 	s.mux.RLock()
 	defer s.mux.RUnlock()
-	return s.confirmed
+	return s.negotiationStatus
 }
 
-/*PRIVATE*/
+// checks if the session has been confirmed
+func (s *Session) IsConfirmed() bool {
+	c := s.ConfirmationStatus()
+	return c >= Confirmed
+}
 
-// Sets the confirm bool. this is set when the partner is certain to share the
-// session. It should be called immediately for receive keys and only on rekey
-// confirmation for send keys. Confirmation can only be made by the sessionBuffer
-// because it is used to keep track of active sessions for rekey as well
-func (s *Session) confirm() error {
-	s.mux.Lock()
-	defer s.mux.Unlock()
-	s.confirmed = true
-	return s.save()
+func (s *Session) String() string {
+	return fmt.Sprintf("{Partner: %s, ID: %s}",
+		s.manager.partner, s.GetID())
 }
 
-func (s *Session) useKey(keynum uint32) error {
-	return s.keyState.Use(keynum)
+/*PRIVATE*/
+func (s *Session) useKey(keynum uint32) {
+	s.keyState.Use(keynum)
 }
 
 // generates keys from the base data stored in the session object.
 // myPrivKey will be generated if not present
-func (s *Session) generate() error {
+func (s *Session) generate() {
 	grp := s.manager.ctx.grp
 
 	//generate private key if it is not present
@@ -335,7 +467,7 @@ func (s *Session) generate() error {
 	var err error
 	s.keyState, err = newStateVector(s.manager.ctx, makeStateVectorKey(keyEKVPrefix, s.GetID()), numKeys)
 	if err != nil {
-		return errors.WithMessage(err, "Failed key generation")
+		jww.FATAL.Printf("Failed key generation: %s", err)
 	}
 
 	//register keys for reception if this is a reception session
@@ -343,8 +475,6 @@ func (s *Session) generate() error {
 		//register keys
 		s.manager.ctx.fa.add(s.getUnusedKeys())
 	}
-
-	return nil
 }
 
 //returns key objects for all unused keys
diff --git a/storage/e2e/sessionBuff.go b/storage/e2e/sessionBuff.go
index 547027133789575b5706d1c8913c3f018b6af456..26616158703396d12836a2f07a75eda0fdb458ee 100644
--- a/storage/e2e/sessionBuff.go
+++ b/storage/e2e/sessionBuff.go
@@ -121,16 +121,21 @@ func (sb *sessionBuff) unmarshal(b []byte) error {
 	return nil
 }
 
-func (sb *sessionBuff) AddSession(s *Session) error {
+func (sb *sessionBuff) AddSession(s *Session) {
 	sb.mux.Lock()
 	defer sb.mux.Unlock()
 
 	sb.addSession(s)
-	return sb.save()
+	if err := sb.save(); err != nil {
+		key := makeSessionBuffKey(sb.keyPrefix, sb.manager.partner)
+		jww.FATAL.Printf("Failed to save Session Buffer %s after "+
+			"adding session %s: %s", key, s, err)
+	}
+
+	return
 }
 
 func (sb *sessionBuff) addSession(s *Session) {
-
 	sb.sessions = append([]*Session{s}, sb.sessions...)
 	sb.sessionByID[s.GetID()] = s
 	return
@@ -147,9 +152,9 @@ func (sb *sessionBuff) GetNewest() *Session {
 
 // returns the session which is most likely to be successful for sending
 func (sb *sessionBuff) GetSessionForSending() *Session {
-	sb.mux.RLock()
-	defer sb.mux.RUnlock()
-	if len(sb.sessions) == 0 {
+	//dont need to take the lock due to the use of a copy of the buffer
+	sessions := sb.getInternalBufferShallowCopy()
+	if len(sessions) == 0 {
 		return nil
 	}
 
@@ -157,9 +162,9 @@ func (sb *sessionBuff) GetSessionForSending() *Session {
 	var unconfirmedActive []*Session
 	var unconfirmedRekey []*Session
 
-	for _, s := range sb.sessions {
+	for _, s := range sessions {
 		status := s.Status()
-		confirmed := s.confirmed
+		confirmed := s.IsConfirmed()
 		if status == Active && confirmed {
 			//always return the first confirmed active, happy path
 			return s
@@ -184,21 +189,38 @@ func (sb *sessionBuff) GetSessionForSending() *Session {
 	return nil
 }
 
-func (sb *sessionBuff) GetNewestConfirmed() *Session {
-	sb.mux.RLock()
-	defer sb.mux.RUnlock()
-	if len(sb.sessions) == 0 {
+// returns a list of session that need rekeys. Nil instances mean a new rekey
+// from scratch
+func (sb *sessionBuff) TriggerNegotiation() []*Session {
+	//dont need to take the lock due to the use of a copy of the buffer
+	sessions := sb.getInternalBufferShallowCopy()
+	var instructions []*Session
+	for _, ses := range sessions {
+		if ses.triggerNegotiation() {
+			instructions = append(instructions, ses)
+		}
+	}
+	return instructions
+}
+
+// returns the newest session which can be used to start a key negotiation
+func (sb *sessionBuff) GetNewestRekeyableSession() *Session {
+	//dont need to take the lock due to the use of a copy of the buffer
+	sessions := sb.getInternalBufferShallowCopy()
+
+	if len(sessions) == 0 {
 		return nil
 	}
 
 	for _, s := range sb.sessions {
-		status := s.Status()
-		confirmed := s.confirmed
-		if status != RekeyEmpty && confirmed {
+		// This looks like it might not be thread safe, I think it is because
+		// the failure mode is it skips to a lower key to rekey with, which is
+		// always valid. It isn't clear it can fail though because we are
+		// accessing the data in the same order it would be written (i think)
+		if s.Status() != RekeyEmpty && s.IsConfirmed() {
 			return s
 		}
 	}
-
 	return nil
 }
 
@@ -219,20 +241,28 @@ func (sb *sessionBuff) Confirm(id SessionID) error {
 		return errors.Errorf("Could not confirm session %s, does not exist", s.GetID())
 	}
 
-	err := s.confirm()
-	if err != nil {
-		jww.FATAL.Panicf("Failed to confirm session "+
-			"%s for %s: %s", s.GetID(), sb.manager.partner, err.Error())
-	}
+	s.SetNegotiationStatus(Confirmed)
+
+	sb.clean()
+
+	return nil
+}
 
-	return sb.clean()
+// adding or removing a session is always done via replacing the entire
+// slice, this allow us to copy the slice under the read lock and do the
+// rest of the work while not taking the lock
+func (sb *sessionBuff) getInternalBufferShallowCopy() []*Session {
+	sb.mux.RLock()
+	defer sb.mux.RUnlock()
+	return sb.sessions
 }
 
-func (sb *sessionBuff) clean() error {
+func (sb *sessionBuff) clean() {
 
 	numConfirmed := uint(0)
 
 	var newSessions []*Session
+	editsMade := false
 
 	for _, s := range sb.sessions {
 		if s.IsConfirmed() {
@@ -241,16 +271,23 @@ func (sb *sessionBuff) clean() error {
 			if numConfirmed > maxUnconfirmed {
 				delete(sb.sessionByID, s.GetID())
 				s.Delete()
-
-				break
+				editsMade = true
+				continue
 			}
 		}
 		newSessions = append(newSessions, s)
 	}
 
-	sb.sessions = newSessions
+	//only do the update and save if changes occured
+	if editsMade {
+		sb.sessions = newSessions
 
-	return sb.save()
+		if err := sb.save(); err != nil {
+			key := makeSessionBuffKey(sb.keyPrefix, sb.manager.partner)
+			jww.FATAL.Printf("Failed to save Session Buffer %s after "+
+				"clean: %s", key, err)
+		}
+	}
 }
 
 func makeSessionBuffKey(keyPrefix string, partnerID *id.ID) string {
diff --git a/storage/e2e/session_test.go b/storage/e2e/session_test.go
index 46de8d439ba15dc915a6fa3fc9db1d9ccbeeda2b..9c7f69a76e4de260ce7ebd70594465cdc8641fd8 100644
--- a/storage/e2e/session_test.go
+++ b/storage/e2e/session_test.go
@@ -35,10 +35,7 @@ func TestSession_generate_noPrivateKeyReceive(t *testing.T) {
 	}
 
 	//run the generate command
-	err := s.generate()
-	if err != nil {
-		t.Fatal(err)
-	}
+	s.generate()
 
 	//check that it generated a private key
 	if s.myPrivKey == nil {
@@ -100,10 +97,7 @@ func TestSession_generate_PrivateKeySend(t *testing.T) {
 	}
 
 	//run the generate command
-	err := s.generate()
-	if err != nil {
-		t.Fatal(err)
-	}
+	s.generate()
 
 	//check that it generated a private key
 	if s.myPrivKey.Cmp(myPrivKey) != 0 {
@@ -141,11 +135,9 @@ func TestNewSession(t *testing.T) {
 	// Make a test session to easily populate all the fields
 	sessionA, _ := makeTestSession(t)
 	// Make a new session with the variables we got from makeTestSession
-	sessionB, err := newSession(sessionA.manager, sessionA.myPrivKey, sessionA.partnerPubKey, sessionA.params, sessionA.t)
-	if err != nil {
-		t.Fatal(err)
-	}
-	err = cmpSerializedFields(sessionA, sessionB)
+	sessionB := newSession(sessionA.manager, sessionA.myPrivKey, sessionA.partnerPubKey, sessionA.params, sessionA.t)
+
+	err := cmpSerializedFields(sessionA, sessionB)
 	if err != nil {
 		t.Error(err)
 	}
@@ -239,7 +231,7 @@ func TestSession_Serialization(t *testing.T) {
 // compare fields also represented in SessionDisk
 // fields not represented in SessionDisk shouldn't be expected to be populated by Unmarshal
 func cmpSerializedFields(a *Session, b *Session) error {
-	if a.confirmed != b.confirmed {
+	if a.negotiationStatus != b.negotiationStatus {
 		return errors.New("confirmed differed")
 	}
 	if a.t != b.t {
@@ -406,11 +398,11 @@ func TestSession_GetMyPrivKey(t *testing.T) {
 // Shows that IsConfirmed returns whether the session is confirmed
 func TestSession_IsConfirmed(t *testing.T) {
 	s, _ := makeTestSession(t)
-	s.confirmed = false
+	s.negotiationStatus = Unconfirmed
 	if s.IsConfirmed() {
 		t.Error("s was confirmed when it shouldn't have been")
 	}
-	s.confirmed = true
+	s.negotiationStatus = Confirmed
 	if !s.IsConfirmed() {
 		t.Error("s wasn't confirmed when it should have been")
 	}
@@ -500,9 +492,9 @@ func makeTestSession(t *testing.T) (*Session, *context) {
 		manager: &Manager{
 			ctx: ctx,
 		},
-		t:         Receive,
-		confirmed: true,
-		ttl:       5,
+		t:                 Receive,
+		negotiationStatus: Confirmed,
+		ttl:               5,
 	}
 	var err error
 	s.keyState, err = newStateVector(ctx, makeStateVectorKey(keyEKVPrefix, s.GetID()), 1024)
diff --git a/storage/e2e/stateVector.go b/storage/e2e/stateVector.go
index f93619700e94ee0b37c1c0d1837a86ddbf531835..3844705e7f1cc95e77a4175a8d339be98f314d55 100644
--- a/storage/e2e/stateVector.go
+++ b/storage/e2e/stateVector.go
@@ -2,10 +2,12 @@ package e2e
 
 import (
 	"encoding/json"
+	"fmt"
 	"github.com/pkg/errors"
 	"gitlab.com/elixxir/client/storage/versioned"
 	"sync"
 	"time"
+	jww "github.com/spf13/jwalterweatherman"
 )
 
 const currentStateVectorVersion = 0
@@ -85,7 +87,7 @@ func (sv *stateVector) save() error {
 	return sv.ctx.kv.Set(sv.key, &obj)
 }
 
-func (sv *stateVector) Use(keynum uint32) error {
+func (sv *stateVector) Use(keynum uint32) {
 	sv.mux.Lock()
 	defer sv.mux.Unlock()
 
@@ -100,7 +102,9 @@ func (sv *stateVector) Use(keynum uint32) error {
 
 	sv.numAvailable--
 
-	return sv.save()
+	if err := sv.save(); err != nil {
+		jww.FATAL.Printf("Failed to save %s on Use(): %s", sv, err)
+	}
 }
 
 func (sv *stateVector) GetNumAvailable() uint32 {
@@ -136,7 +140,11 @@ func (sv *stateVector) Next() (uint32, error) {
 	sv.nextAvailable()
 	sv.numAvailable--
 
-	return next, sv.save()
+	if err := sv.save(); err != nil {
+		jww.FATAL.Printf("Failed to save %s on Next(): %s", sv, err)
+	}
+
+	return next, nil
 
 }
 
@@ -176,6 +184,12 @@ func (sv *stateVector) GetUsedKeyNums() []uint32 {
 	return keyNums
 }
 
+//Adheres to the stringer interface
+func (sv *stateVector) String() string {
+	return fmt.Sprintf("stateVector: %s", sv.key)
+}
+
+
 // finds the next used state and sets that as firstAvailable. This does not
 // execute a store and a store must be executed after.
 func (sv *stateVector) nextAvailable() {
diff --git a/storage/e2e/stateVector_test.go b/storage/e2e/stateVector_test.go
index a447786cdb5f50a5feecc0a71e49a011f66d2369..b6cff62f08e4c178e3c94f480b98f4072d6745a2 100644
--- a/storage/e2e/stateVector_test.go
+++ b/storage/e2e/stateVector_test.go
@@ -124,10 +124,7 @@ func TestStateVector_Use(t *testing.T) {
 
 	for numCalls := range keyNums {
 		// These calls to Use won't set nextAvailable, because the first keyNum set
-		err := sv.Use(keyNums[numCalls])
-		if err != nil {
-			t.Fatal(err)
-		}
+		sv.Use(keyNums[numCalls])
 		if !reflect.DeepEqual(expectedVect[numCalls], sv.vect) {
 			t.Errorf("sv.vect differed from expected at index %v", numCalls)
 			fmt.Println(sv.vect)
diff --git a/storage/e2e/store.go b/storage/e2e/store.go
index dac6ec27090b039b06c2e754fd341c7efb1c5c9d..cef07d447abbda92a2aca1a90a3699e39610d840 100644
--- a/storage/e2e/store.go
+++ b/storage/e2e/store.go
@@ -127,19 +127,20 @@ func (s *Store) save() error {
 }
 
 func (s *Store) AddPartner(partnerID *id.ID, myPrivKey *cyclic.Int,
-	partnerPubKey *cyclic.Int, sendParams, receiveParams SessionParams) error {
+	partnerPubKey *cyclic.Int, sendParams, receiveParams SessionParams) {
 	s.mux.Lock()
 	defer s.mux.Unlock()
 
-	m, err := newManager(&s.context, partnerID, myPrivKey, partnerPubKey, sendParams, receiveParams)
-
-	if err != nil {
-		return err
-	}
+	m := newManager(&s.context, partnerID, myPrivKey, partnerPubKey, sendParams, receiveParams)
 
 	s.managers[*partnerID] = m
 
-	return s.save()
+	if err := s.save(); err != nil {
+		jww.FATAL.Printf("Failed to add Parter %s: Save of store "+
+			"failed: %s", partnerID, err)
+	}
+
+	return
 }
 
 func (s *Store) GetPartner(partnerID *id.ID) (*Manager, error) {
@@ -272,11 +273,7 @@ func (f *fingerprints) Pop(fingerprint format.Fingerprint) (*Key, error) {
 
 	delete(f.toKey, fingerprint)
 
-	err := key.denoteUse()
-
-	if err != nil {
-		return nil, err
-	}
+	key.denoteUse()
 
 	key.fp = &fingerprint