diff --git a/e2e/fpGenerator.go b/e2e/fpGenerator.go
index e9dabae2bdbf1aae89bfd61e13aaa22558695316..2e42f9af79bd186885392d2333f7c69cd461368d 100644
--- a/e2e/fpGenerator.go
+++ b/e2e/fpGenerator.go
@@ -5,6 +5,9 @@ import (
 	"gitlab.com/elixxir/client/e2e/ratchet/partner/session"
 )
 
+// Wrapper which allows the network>manager's fingerprint interface to be
+// passed into ratchet without exposing ratchet to buisness logic
+// adheres to the CypherHandler interface in session
 type fpGenerator struct {
 	*manager
 }
diff --git a/e2e/ratchet/partner/manager.go b/e2e/ratchet/partner/manager.go
index f0a0f918e39f31f328f765b529c6535c59e4bcc5..3ff4befee5f95b491bf07b3aa149308a6fa19cef 100644
--- a/e2e/ratchet/partner/manager.go
+++ b/e2e/ratchet/partner/manager.go
@@ -189,9 +189,8 @@ func (m *Manager) NewReceiveSession(partnerPubKey *cyclic.Int,
 // received from the partner and a new 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,
-	mySIDHPrivKey *sidh.PrivateKey, e2eParams session.Params) *session.Session {
-	// Find the latest public key from the other party
-	sourceSession := m.receive.getNewestRekeyableSession()
+	mySIDHPrivKey *sidh.PrivateKey, e2eParams session.Params,
+	sourceSession *session.Session) *session.Session {
 
 	// Add the session to the Send session buffer and return
 	return m.send.AddSession(myPrivKey, sourceSession.GetPartnerPubKey(), nil,
diff --git a/e2e/ratchet/partner/session/params.go b/e2e/ratchet/partner/session/params.go
index 756bff1fa0a1129fd9c1874e22a9759a0ffbc4bd..033616ac26a8a65dc44435a56c3431dfea51447d 100644
--- a/e2e/ratchet/partner/session/params.go
+++ b/e2e/ratchet/partner/session/params.go
@@ -14,6 +14,10 @@ type Params struct {
 	// many keys are not allowed to be used for sending messages
 	// in order to ensure there are extras for rekeying.
 	NumRekeys uint16
+	// Number from 0 to 1, denotes how often when in the unconfirmed state the
+	// system will automatically resend the rekey request on any message send
+	// from the partner the session is associated with
+	UnconfirmedRetryRatio float64
 }
 
 // DEFAULT KEY GENERATION PARAMETERS
@@ -24,14 +28,16 @@ const (
 	maxKeys       uint16  = 2000
 	rekeyThrshold float64 = 0.05
 	numReKeys     uint16  = 16
+	rekeyRatio    float64 = 1 / 10
 )
 
 func GetDefaultE2ESessionParams() Params {
 	return Params{
-		MinKeys:        minKeys,
-		MaxKeys:        maxKeys,
-		RekeyThreshold: rekeyThrshold,
-		NumRekeys:      numReKeys,
+		MinKeys:               minKeys,
+		MaxKeys:               maxKeys,
+		RekeyThreshold:        rekeyThrshold,
+		NumRekeys:             numReKeys,
+		UnconfirmedRetryRatio: rekeyRatio,
 	}
 }
 
diff --git a/e2e/ratchet/partner/session/session.go b/e2e/ratchet/partner/session/session.go
index 7d1b227cb038651c9560834b59acd285ee137c68..a86a5e0b07e58b66ea5e47b4f399d2b78b5517b7 100644
--- a/e2e/ratchet/partner/session/session.go
+++ b/e2e/ratchet/partner/session/session.go
@@ -309,12 +309,12 @@ func GetSessionIDFromBaseKeyForTesting(baseKey *cyclic.Int, i interface{}) Sessi
 	return GetSessionIDFromBaseKey(baseKey)
 }
 
-//Blake2B hash of base key used for storage
+// GetID Blake2B hash of base key used for storage
 func (s *Session) GetID() SessionID {
 	return s.sID
 }
 
-// returns the ID of the partner for this session
+// GetPartner returns the ID of the partner for this session
 func (s *Session) GetPartner() *id.ID {
 	if s.partner != nil {
 		return s.partner
@@ -323,92 +323,9 @@ func (s *Session) GetPartner() *id.ID {
 	}
 }
 
-//ekv functions
-func (s *Session) marshal() ([]byte, error) {
-	sd := SessionDisk{}
-
-	sd.E2EParams = s.e2eParams
-	sd.Type = uint8(s.t)
-	sd.BaseKey = s.baseKey.Bytes()
-	sd.MyPrivKey = s.myPrivKey.Bytes()
-	sd.PartnerPubKey = s.partnerPubKey.Bytes()
-	sd.MySIDHPrivKey = make([]byte, s.mySIDHPrivKey.Size())
-	sd.PartnerSIDHPubKey = make([]byte, s.partnerSIDHPubKey.Size())
-
-	s.mySIDHPrivKey.Export(sd.MySIDHPrivKey)
-	sd.MySIDHVariant = byte(s.mySIDHPrivKey.Variant())
-
-	s.partnerSIDHPubKey.Export(sd.PartnerSIDHPubKey)
-	sd.PartnerSIDHVariant = byte(s.partnerSIDHPubKey.Variant())
-
-	sd.Trigger = s.partnerSource[:]
-	sd.RelationshipFingerprint = s.relationshipFingerprint
-	sd.Partner = s.partner.Bytes()
-
-	// 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.RekeyThreshold = s.rekeyThreshold
-
-	return json.Marshal(&sd)
-}
-
-func (s *Session) unmarshal(b []byte) error {
-
-	sd := SessionDisk{}
-
-	err := json.Unmarshal(b, &sd)
-
-	if err != nil {
-		return err
-	}
-
-	grp := s.grp
-
-	s.e2eParams = sd.E2EParams
-	s.t = RelationshipType(sd.Type)
-	s.baseKey = grp.NewIntFromBytes(sd.BaseKey)
-	s.myPrivKey = grp.NewIntFromBytes(sd.MyPrivKey)
-	s.partnerPubKey = grp.NewIntFromBytes(sd.PartnerPubKey)
-
-	mySIDHVariant := sidh.KeyVariant(sd.MySIDHVariant)
-	s.mySIDHPrivKey = utility.NewSIDHPrivateKey(mySIDHVariant)
-	err = s.mySIDHPrivKey.Import(sd.MySIDHPrivKey)
-	if err != nil {
-		return err
-	}
-
-	partnerSIDHVariant := sidh.KeyVariant(sd.PartnerSIDHVariant)
-	s.partnerSIDHPubKey = utility.NewSIDHPublicKey(partnerSIDHVariant)
-	err = s.partnerSIDHPubKey.Import(sd.PartnerSIDHPubKey)
-	if err != nil {
-		return err
-	}
-
-	s.negotiationStatus = Negotiation(sd.Confirmation)
-	s.rekeyThreshold = sd.RekeyThreshold
-	s.relationshipFingerprint = sd.RelationshipFingerprint
-	s.partner, _ = id.Unmarshal(sd.Partner)
-	copy(s.partnerSource[:], sd.Trigger)
-
-	s.keyState, err = utility.LoadStateVector(s.kv, "")
-	if err != nil {
-		return err
-	}
-
-	return nil
-}
-
 //key usage
 
-// Popkey Pops the first unused key, skipping any which are denoted as used.
+// PopKey Pops the first unused key, skipping any which are denoted as used.
 // will return if the remaining keys are designated as rekeys
 func (s *Session) PopKey() (*Cypher, error) {
 	if s.keyState.GetNumAvailable() <= uint32(s.e2eParams.NumRekeys) {
@@ -423,6 +340,8 @@ func (s *Session) PopKey() (*Cypher, error) {
 	return newKey(s, keyNum), nil
 }
 
+// PopReKey Pops the first unused key, skipping any which are denoted as used,
+// including keys designated for rekeys
 func (s *Session) PopReKey() (*Cypher, error) {
 	keyNum, err := s.keyState.Next()
 	if err != nil {
@@ -473,12 +392,12 @@ func (s *Session) Status() Status {
 
 var legalStateChanges = [][]bool{
 	// Unconf  Sending  Sent   Confi  NewTrig  NewCreat
-	{false, false, false, false, false, false}, // Unc
-	{true, false, true, true, false, false},    // Sending
-	{false, false, false, true, false, false},  // Sent
-	{false, false, false, false, true, false},  // Confi
-	{false, false, false, true, false, true},   // NewTrig
-	{false, false, true, false, false, false},  // NewCreat
+	{false, true, false, false, false, false}, // Unc
+	{true, false, true, true, false, false},   // Sending
+	{false, false, false, true, false, false}, // Sent
+	{false, false, false, false, true, false}, // Confi
+	{false, false, false, true, false, true},  // NewTrig
+	{false, false, true, false, false, false}, // NewCreat
 }
 
 // todo - doscstring
@@ -561,7 +480,8 @@ func (s *Session) TriggerNegotiation() bool {
 			s.mux.Unlock()
 			return false
 		}
-	} else if s.negotiationStatus == Unconfirmed && decideIfResendRekey(s.rng, 1/10) {
+	} else if s.negotiationStatus == Unconfirmed && decideIfResendRekey(s.rng,
+		s.e2eParams.UnconfirmedRetryRatio) {
 		// retrigger this sessions negotiation
 		s.mux.RUnlock()
 		s.mux.Lock()
@@ -692,7 +612,90 @@ func (s *Session) getUnusedKeys() []*Cypher {
 	return keys
 }
 
-//builds the
+//ekv functions
+func (s *Session) marshal() ([]byte, error) {
+	sd := SessionDisk{}
+
+	sd.E2EParams = s.e2eParams
+	sd.Type = uint8(s.t)
+	sd.BaseKey = s.baseKey.Bytes()
+	sd.MyPrivKey = s.myPrivKey.Bytes()
+	sd.PartnerPubKey = s.partnerPubKey.Bytes()
+	sd.MySIDHPrivKey = make([]byte, s.mySIDHPrivKey.Size())
+	sd.PartnerSIDHPubKey = make([]byte, s.partnerSIDHPubKey.Size())
+
+	s.mySIDHPrivKey.Export(sd.MySIDHPrivKey)
+	sd.MySIDHVariant = byte(s.mySIDHPrivKey.Variant())
+
+	s.partnerSIDHPubKey.Export(sd.PartnerSIDHPubKey)
+	sd.PartnerSIDHVariant = byte(s.partnerSIDHPubKey.Variant())
+
+	sd.Trigger = s.partnerSource[:]
+	sd.RelationshipFingerprint = s.relationshipFingerprint
+	sd.Partner = s.partner.Bytes()
+
+	// 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.RekeyThreshold = s.rekeyThreshold
+
+	return json.Marshal(&sd)
+}
+
+func (s *Session) unmarshal(b []byte) error {
+
+	sd := SessionDisk{}
+
+	err := json.Unmarshal(b, &sd)
+
+	if err != nil {
+		return err
+	}
+
+	grp := s.grp
+
+	s.e2eParams = sd.E2EParams
+	s.t = RelationshipType(sd.Type)
+	s.baseKey = grp.NewIntFromBytes(sd.BaseKey)
+	s.myPrivKey = grp.NewIntFromBytes(sd.MyPrivKey)
+	s.partnerPubKey = grp.NewIntFromBytes(sd.PartnerPubKey)
+
+	mySIDHVariant := sidh.KeyVariant(sd.MySIDHVariant)
+	s.mySIDHPrivKey = utility.NewSIDHPrivateKey(mySIDHVariant)
+	err = s.mySIDHPrivKey.Import(sd.MySIDHPrivKey)
+	if err != nil {
+		return err
+	}
+
+	partnerSIDHVariant := sidh.KeyVariant(sd.PartnerSIDHVariant)
+	s.partnerSIDHPubKey = utility.NewSIDHPublicKey(partnerSIDHVariant)
+	err = s.partnerSIDHPubKey.Import(sd.PartnerSIDHPubKey)
+	if err != nil {
+		return err
+	}
+
+	s.negotiationStatus = Negotiation(sd.Confirmation)
+	s.rekeyThreshold = sd.RekeyThreshold
+	s.relationshipFingerprint = sd.RelationshipFingerprint
+	s.partner, _ = id.Unmarshal(sd.Partner)
+	copy(s.partnerSource[:], sd.Trigger)
+
+	s.keyState, err = utility.LoadStateVector(s.kv, "")
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// MakeSessionPrefix builds the prefix
 func MakeSessionPrefix(sid SessionID) string {
 	return fmt.Sprintf(sessionPrefix, sid)
 }
diff --git a/e2e/rekey/rekey.go b/e2e/rekey/rekey.go
index bdac760b9d61f82fc7d29a6bef0c9f4ef0e3d4d9..9892d0047b126bc908dcfbec91f68031080f9b7d 100644
--- a/e2e/rekey/rekey.go
+++ b/e2e/rekey/rekey.go
@@ -29,7 +29,11 @@ import (
 func CheckKeyExchanges(instance *commsNetwork.Instance, grp *cyclic.Group,
 	sendE2E E2eSender, events event.Manager, manager *partner.Manager,
 	sendTimeout time.Duration) {
+
+	//get all sessions that may need a key exchange
 	sessions := manager.TriggerNegotiations()
+
+	//start an exchange for every session that needs one
 	for _, sess := range sessions {
 		go trigger(instance, grp, sendE2E, events, manager, sess,
 			sendTimeout)
@@ -41,38 +45,50 @@ func CheckKeyExchanges(instance *commsNetwork.Instance, grp *cyclic.Group,
 // session. They run the same negotiation, the former does it on a newly created
 // session while the latter on an extant session
 func trigger(instance *commsNetwork.Instance, grp *cyclic.Group, sendE2E E2eSender,
-	events event.Manager, manager *partner.Manager, sess *session.Session,
+	events event.Manager, manager *partner.Manager, inputSession *session.Session,
 	sendTimeout time.Duration) {
 
 	var negotiatingSession *session.Session
 	jww.INFO.Printf("[REKEY] Negotiation triggered for session %s with "+
-		"status: %s", sess, sess.NegotiationStatus())
-	switch sess.NegotiationStatus() {
+		"status: %s", inputSession, inputSession.NegotiationStatus())
+
+	switch inputSession.NegotiationStatus() {
 	// If the passed session is triggering a negotiation on a new session to
 	// replace itself, then create the session
 	case session.NewSessionTriggered:
+		//todo: check if any sessions have inputSession as a parent. If so,
+		//skip creation, set its status to newSession created, and bail
+		//this state could only happen if a crash occurred in a previous run
+		//between NewSendSession creation and the setting of the negotiation
+		//status on the input session
+
 		//create the session, pass a nil private key to generate a new one
 		negotiatingSession = manager.NewSendSession(nil, nil,
-			session.GetDefaultE2ESessionParams())
+			session.GetDefaultE2ESessionParams(), inputSession)
+
 		//move the state of the triggering session forward
-		sess.SetNegotiationStatus(session.NewSessionCreated)
+		inputSession.SetNegotiationStatus(session.NewSessionCreated)
 
 	// If the session is set to send a negotiation
 	case session.Sending:
-		negotiatingSession = sess
+		negotiatingSession = inputSession
+
+	// should be unreachable, manager.TriggerNegotiations above should limit
+	// states for this switch
 	default:
 		jww.FATAL.Panicf("[REKEY] Session %s provided invalid e2e "+
-			"negotiating status: %s", sess, sess.NegotiationStatus())
+			"negotiating status: %s", inputSession, inputSession.NegotiationStatus())
 	}
 
 	// send the rekey notification to the partner
 	err := negotiate(instance, grp, sendE2E, negotiatingSession,
 		sendTimeout)
+
 	// 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("[REKEY] Failed to do Key Negotiation with "+
-			"session %s: %s", sess, err)
+			"session %s: %s", inputSession, err)
 		events.Report(1, "Rekey", "NegotiationFailed", err.Error())
 	}
 }
@@ -136,7 +152,7 @@ func negotiate(instance *commsNetwork.Instance, grp *cyclic.Group, sendE2E E2eSe
 	// 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 {
-		sess.SetNegotiationStatus(session.Unconfirmed)
+		_ = sess.TrySetNegotiationStatus(session.Unconfirmed)
 		return errors.Errorf("[REKEY] Key Negotiation rekey for %s failed to "+
 			"transmit %v/%v paritions: %v round failures, %v timeouts, msgID: %s",
 			sess, numRoundFail+numTimeOut, len(rounds), numRoundFail,