diff --git a/api/mnemonic.go b/api/mnemonic.go
new file mode 100644
index 0000000000000000000000000000000000000000..fd3ce7259be248692591a48bc86f745fd0089e7c
--- /dev/null
+++ b/api/mnemonic.go
@@ -0,0 +1,133 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright © 2020 xx network SEZC                                          //
+//                                                                           //
+// Use of this source code is governed by a license that can be found in the //
+// LICENSE file                                                              //
+///////////////////////////////////////////////////////////////////////////////
+
+package api
+
+import (
+	"github.com/pkg/errors"
+	"gitlab.com/elixxir/crypto/fastRNG"
+	"gitlab.com/xx_network/crypto/csprng"
+	xxMnemonic "gitlab.com/xx_network/crypto/mnemonic"
+	"gitlab.com/xx_network/primitives/utils"
+	"golang.org/x/crypto/chacha20poly1305"
+	"path/filepath"
+	"strings"
+)
+
+const mnemonicFile = ".recovery"
+
+// StoreSecretWithMnemonic creates a mnemonic and uses it to encrypt the secret.
+// This encrypted data saved in storage.
+func StoreSecretWithMnemonic(secret []byte, path string) (string, error) {
+	// Use fastRNG for RNG ops (AES fortuna based RNG using system RNG)
+	rng := fastRNG.NewStreamGenerator(12, 3, csprng.NewSystemRNG).GetStream()
+
+	// Ensure path is appended by filepath separator "/"
+	if !strings.HasSuffix(path, string(filepath.Separator)) {
+		path = path + string(filepath.Separator)
+	}
+
+	// Create a mnemonic
+	mnemonic, err := xxMnemonic.GenerateMnemonic(rng, 32)
+	if err != nil {
+		return "", errors.Errorf("Failed to generate mnemonic: %v", err)
+	}
+
+	// Decode mnemonic
+	decodedMnemonic, err := xxMnemonic.DecodeMnemonic(mnemonic)
+	if err != nil {
+		return "", errors.Errorf("Failed to decode mnemonic: %v", err)
+	}
+
+	// Encrypt secret with mnemonic as key
+	ciphertext, err := encryptWithMnemonic(secret, decodedMnemonic, rng)
+	if err != nil {
+		return "", errors.Errorf("Failed to encrypt secret with mnemonic: %v", err)
+	}
+
+	// Save encrypted secret to file
+	recoveryFile := path + mnemonicFile
+	err = utils.WriteFileDef(recoveryFile, ciphertext)
+	if err != nil {
+		return "", errors.Errorf("Failed to save mnemonic information to file")
+	}
+
+	return mnemonic, nil
+}
+
+// LoadSecretWithMnemonic loads the encrypted secret from storage and decrypts
+// the secret using the given mnemonic.
+func LoadSecretWithMnemonic(mnemonic, path string) (secret []byte, err error) {
+	// Ensure path is appended by filepath separator "/"
+	if !strings.HasSuffix(path, string(filepath.Separator)) {
+		path = path + string(filepath.Separator)
+	}
+
+	// Ensure that the recovery file exists
+	recoveryFile := path + mnemonicFile
+	if !utils.Exists(recoveryFile) {
+		return nil, errors.Errorf("Recovery file does not exist. " +
+			"Did you properly set up recovery or provide an incorrect filepath?")
+	}
+
+	// Read file from storage
+	data, err := utils.ReadFile(recoveryFile)
+	if err != nil {
+		return nil, errors.Errorf("Failed to load mnemonic information: %v", err)
+	}
+
+	// Decode mnemonic
+	decodedMnemonic, err := xxMnemonic.DecodeMnemonic(mnemonic)
+	if err != nil {
+		return nil, errors.Errorf("Failed to decode mnemonic: %v", err)
+	}
+
+	// Decrypt the stored secret
+	secret, err = decryptWithMnemonic(data, decodedMnemonic)
+	if err != nil {
+		return nil, errors.Errorf("Failed to decrypt secret: %v", err)
+	}
+
+	return secret, nil
+}
+
+// encryptWithMnemonic is a helper function which encrypts the given secret
+// using the mnemonic as the key.
+func encryptWithMnemonic(data, decodedMnemonic []byte,
+	rng csprng.Source) (ciphertext []byte, error error) {
+	chaCipher, err := chacha20poly1305.NewX(decodedMnemonic[:])
+	if err != nil {
+		return nil, errors.Errorf("Failed to initalize encryption algorithm: %v", err)
+	}
+
+	// Generate the nonce
+	nonce := make([]byte, chaCipher.NonceSize())
+	nonce, err = csprng.Generate(chaCipher.NonceSize(), rng)
+	if err != nil {
+		return nil, errors.Errorf("Failed to generate nonce: %v", err)
+	}
+
+	ciphertext = chaCipher.Seal(nonce, nonce, data, nil)
+	return ciphertext, nil
+}
+
+// decryptWithMnemonic is a helper function which decrypts the secret
+// from storage, using the mnemonic as the key.
+func decryptWithMnemonic(data, decodedMnemonic []byte) ([]byte, error) {
+	chaCipher, err := chacha20poly1305.NewX(decodedMnemonic[:])
+	if err != nil {
+		return nil, errors.Errorf("Failed to initalize encryption algorithm: %v", err)
+	}
+
+	nonceLen := chaCipher.NonceSize()
+	nonce, ciphertext := data[:nonceLen], data[nonceLen:]
+	plaintext, err := chaCipher.Open(nil, nonce, ciphertext, nil)
+	if err != nil {
+		return nil, errors.Wrap(err, "Cannot decrypt with password!")
+	}
+	return plaintext, nil
+}
diff --git a/api/mnemonic_test.go b/api/mnemonic_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..66b2f13dfc8e3f297be051f1f58206c94d22c399
--- /dev/null
+++ b/api/mnemonic_test.go
@@ -0,0 +1,106 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright © 2020 xx network SEZC                                          //
+//                                                                           //
+// Use of this source code is governed by a license that can be found in the //
+// LICENSE file                                                              //
+///////////////////////////////////////////////////////////////////////////////
+
+package api
+
+import (
+	"bytes"
+	"gitlab.com/xx_network/crypto/csprng"
+	xxMnemonic "gitlab.com/xx_network/crypto/mnemonic"
+	"gitlab.com/xx_network/primitives/utils"
+	"io"
+	"math/rand"
+	"testing"
+)
+
+func TestStoreSecretWithMnemonic(t *testing.T) {
+	secret := []byte("test123")
+	storageDir := "ignore.1/"
+	mnemonic, err := StoreSecretWithMnemonic(secret, storageDir)
+	if err != nil {
+		t.Errorf("StoreSecretWithMnemonic error; %v", err)
+	}
+
+	// Tests the mnemonic returned is valid
+	_, err = xxMnemonic.DecodeMnemonic(mnemonic)
+	if err != nil {
+		t.Errorf("StoreSecretWithMnemonic did not return a decodable mnemonic: %v", err)
+	}
+
+	// Test that the file was written to
+	if !utils.Exists(storageDir + mnemonicFile) {
+		t.Errorf("Mnemonic file does not exist in storage: %v", err)
+	}
+
+}
+
+func TestEncryptDecryptMnemonic(t *testing.T) {
+	prng := NewPrng(32)
+
+	// Generate a test mnemonic
+	testMnemonic, err := xxMnemonic.GenerateMnemonic(prng, 32)
+	if err != nil {
+		t.Fatalf("GenerateMnemonic error: %v", err)
+	}
+
+	decodedMnemonic, err := xxMnemonic.DecodeMnemonic(testMnemonic)
+	if err != nil {
+		t.Fatalf("DecodeMnemonic error: %v", err)
+	}
+
+	secret := []byte("test123")
+
+	// Encrypt the secret
+	ciphertext, err := encryptWithMnemonic(secret, decodedMnemonic, prng)
+	if err != nil {
+		t.Fatalf("encryptWithMnemonic error: %v", err)
+	}
+
+	// Decrypt the secret
+	received, err := decryptWithMnemonic(ciphertext, decodedMnemonic)
+	if err != nil {
+		t.Fatalf("decryptWithMnemonic error: %v", err)
+	}
+
+	// Test if secret matches decrypted data
+	if !bytes.Equal(received, secret) {
+		t.Fatalf("Decrypted data does not match original plaintext."+
+			"\n\tExpected: %v\n\tReceived: %v", secret, received)
+	}
+}
+
+func TestLoadSecretWithMnemonic(t *testing.T) {
+	secret := []byte("test123")
+	storageDir := "ignore.1"
+	mnemonic, err := StoreSecretWithMnemonic(secret, storageDir)
+	if err != nil {
+		t.Errorf("StoreSecretWithMnemonic error; %v", err)
+	}
+
+	received, err := LoadSecretWithMnemonic(mnemonic, storageDir)
+	if err != nil {
+		t.Errorf("LoadSecretWithMnemonic error: %v", err)
+	}
+
+	if !bytes.Equal(received, secret) {
+		t.Fatalf("Loaded secret does not match original data."+
+			"\n\tExpected: %v\n\tReceived: %v", secret, received)
+	}
+
+	_, err = LoadSecretWithMnemonic(mnemonic, "badDirectory")
+	if err == nil {
+		t.Fatalf("LoadSecretWithMnemonic should error when provided a path " +
+			"where a recovery file does not exist.")
+	}
+}
+
+// Prng is a PRNG that satisfies the csprng.Source interface.
+type Prng struct{ prng io.Reader }
+
+func NewPrng(seed int64) csprng.Source     { return &Prng{rand.New(rand.NewSource(seed))} }
+func (s *Prng) Read(b []byte) (int, error) { return s.prng.Read(b) }
+func (s *Prng) SetSeed([]byte) error       { return nil }
diff --git a/bindings/mnemonic.go b/bindings/mnemonic.go
new file mode 100644
index 0000000000000000000000000000000000000000..7062341a1958bb3cd2c0e0e9a960b454f1a143cf
--- /dev/null
+++ b/bindings/mnemonic.go
@@ -0,0 +1,34 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright © 2020 xx network SEZC                                          //
+//                                                                           //
+// Use of this source code is governed by a license that can be found in the //
+// LICENSE file                                                              //
+///////////////////////////////////////////////////////////////////////////////
+
+package bindings
+
+import "gitlab.com/elixxir/client/api"
+
+// StoreSecretWithMnemonic stores the secret tied with the mnemonic to storage.
+// Unlike other storage operations, this does not use EKV, as that is
+// intrinsically tied to client operations, which the user will not have while
+// trying to recover their account. As such, we store the encrypted data
+// directly, with a specified path. Path will be a valid filepath in which the
+// recover file will be stored as ".recovery".
+//
+// As an example, given "home/user/xxmessenger/storagePath",
+// the recovery file will be stored at
+// "home/user/xxmessenger/storagePath/.recovery"
+func StoreSecretWithMnemonic(secret []byte, path string) (string, error) {
+	return api.StoreSecretWithMnemonic(secret, path)
+}
+
+// LoadSecretWithMnemonic loads the secret stored from the call to
+// StoreSecretWithMnemonic. The path given should be the same filepath
+// as the path given in StoreSecretWithMnemonic. There should be a file
+// in this path called ".recovery". This operation is not tied
+// to client operations, as the user will not have a client when trying to
+// recover their account.
+func LoadSecretWithMnemonic(mnemonic, path string) (secret []byte, err error) {
+	return api.LoadSecretWithMnemonic(mnemonic, path)
+}
diff --git a/go.sum b/go.sum
index 91ba50390e2c10e39f32b1e9c8e2a2166c79793a..13e906a9f6aae43758b96700122e89d52a2cdb02 100644
--- a/go.sum
+++ b/go.sum
@@ -239,6 +239,8 @@ github.com/ttacon/builder v0.0.0-20170518171403-c099f663e1c2 h1:5u+EJUQiosu3JFX0
 github.com/ttacon/builder v0.0.0-20170518171403-c099f663e1c2/go.mod h1:4kyMkleCiLkgY6z8gK5BkI01ChBtxR0ro3I1ZDcGM3w=
 github.com/ttacon/libphonenumber v1.2.1 h1:fzOfY5zUADkCkbIafAed11gL1sW+bJ26p6zWLBMElR4=
 github.com/ttacon/libphonenumber v1.2.1/go.mod h1:E0TpmdVMq5dyVlQ7oenAkhsLu86OkUl+yR4OAxyEg/M=
+github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8=
+github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U=
 github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
 github.com/zeebo/assert v0.0.0-20181109011804-10f827ce2ed6/go.mod h1:yssERNPivllc1yU3BvpjYI5BUW+zglcz6QWqeVRL5t0=
 github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY=
@@ -272,6 +274,8 @@ gitlab.com/xx_network/crypto v0.0.3/go.mod h1:DF2HYvvCw9wkBybXcXAgQMzX+MiGbFPjwt
 gitlab.com/xx_network/crypto v0.0.4/go.mod h1:+lcQEy+Th4eswFgQDwT0EXKp4AXrlubxalwQFH5O0Mk=
 gitlab.com/xx_network/crypto v0.0.5-0.20210803231814-b18476a2257c h1:pwP50UFdh68KPcP7VopsH34/d6rIuVDwXymgFXnCOjA=
 gitlab.com/xx_network/crypto v0.0.5-0.20210803231814-b18476a2257c/go.mod h1:e/5MGrKDQbCNJnskBeDscrolr2EK5iIatEgTQnEWmOg=
+gitlab.com/xx_network/crypto v0.0.5-0.20210909170042-07755821e8c5 h1:1K+FuxnlOTHI5H2OmoBokh/K9Pbmel8VBaCdP5RW9FU=
+gitlab.com/xx_network/crypto v0.0.5-0.20210909170042-07755821e8c5/go.mod h1:hLNL8YCSiEMof0wgZHuRmN5C98TYZiLaz/rLqUYTdII=
 gitlab.com/xx_network/primitives v0.0.0-20200803231956-9b192c57ea7c/go.mod h1:wtdCMr7DPePz9qwctNoAUzZtbOSHSedcK++3Df3psjA=
 gitlab.com/xx_network/primitives v0.0.0-20200804183002-f99f7a7284da/go.mod h1:OK9xevzWCaPO7b1wiluVJGk7R5ZsuC7pHY5hteZFQug=
 gitlab.com/xx_network/primitives v0.0.2/go.mod h1:cs0QlFpdMDI6lAo61lDRH2JZz+3aVkHy+QogOB6F/qc=
diff --git a/network/ephemeral/tracker.go b/network/ephemeral/tracker.go
index 82b25ec0ad6fd15b5d406773933a1cd4c3e20e0e..b3265afe1a14738d9abd1247dd7932024a0debc3 100644
--- a/network/ephemeral/tracker.go
+++ b/network/ephemeral/tracker.go
@@ -141,15 +141,13 @@ func generateIdentities(protoIds []ephemeral.ProtoIdentity, ourId *id.ID,
 	// Add identities for every ephemeral ID
 	for i, eid := range protoIds {
 		// Expand the grace period for both start and end
-		eid.End.Add(validityGracePeriod)
-		eid.Start.Add(-validityGracePeriod)
 		identities[i] = reception.Identity{
 			EphId:       eid.Id,
 			Source:      ourId,
 			AddressSize: addressSize,
 			End:         eid.End,
-			StartValid:  eid.Start,
-			EndValid:    eid.End,
+			StartValid:  eid.Start.Add(-validityGracePeriod),
+			EndValid:    eid.End.Add(validityGracePeriod),
 			Ephemeral:   false,
 		}
 
@@ -211,6 +209,6 @@ func calculateTickerTime(baseIDs []ephemeral.ProtoIdentity, now time.Time) time.
 
 	// Factor out the grace period previously expanded upon
 	// Calculate and return that duration
-	gracePeriod := lastIdentity.End.Add(-2 * validityGracePeriod)
+	gracePeriod := lastIdentity.End.Add(-1 * validityGracePeriod)
 	return gracePeriod.Sub(now)
 }
diff --git a/network/ephemeral/tracker_test.go b/network/ephemeral/tracker_test.go
index 3307446bc17f25cbf8d8bc119e01a2889b1c2ae2..47b8f034766c45c003f9f4997a599cf1ad22cfbd 100644
--- a/network/ephemeral/tracker_test.go
+++ b/network/ephemeral/tracker_test.go
@@ -17,6 +17,7 @@ import (
 	"gitlab.com/xx_network/comms/signature"
 	"gitlab.com/xx_network/crypto/signature/rsa"
 	"gitlab.com/xx_network/primitives/id"
+	"gitlab.com/xx_network/primitives/id/ephemeral"
 	"gitlab.com/xx_network/primitives/netTime"
 	"gitlab.com/xx_network/primitives/utils"
 	"testing"
@@ -122,3 +123,18 @@ func setupInstance(instance interfaces.NetworkManager) error {
 
 	return nil
 }
+
+func TestGenerateIdentities(t *testing.T) {
+	eid, s, e, err := ephemeral.GetId(id.NewIdFromString("zezima", id.Node, t), 16, time.Now().UnixNano())
+	if err != nil {
+		t.Errorf("Failed to get eid: %+v", err)
+	}
+	protoIds := []ephemeral.ProtoIdentity{{eid, s, e}}
+	generated := generateIdentities(protoIds, id.NewIdFromString("escaline", id.Node, t), 16)
+	if generated[0].EndValid != protoIds[0].End.Add(5*time.Minute) {
+		t.Errorf("End was not modified.  Orig %+v, Generated %+v", protoIds[0].End, generated[0].End)
+	}
+	if generated[0].StartValid != protoIds[0].Start.Add(-5*time.Minute) {
+		t.Errorf("End was not modified.  Orig %+v, Generated %+v", protoIds[0].End, generated[0].End)
+	}
+}
diff --git a/network/gateway/sender.go b/network/gateway/sender.go
index d689a91dac3794f8c49e4e7039a0533d2b1b2ed7..ddd41478cc7845347bd5adde640794766cf9cc8f 100644
--- a/network/gateway/sender.go
+++ b/network/gateway/sender.go
@@ -102,8 +102,8 @@ func (s *Sender) SendToPreferred(targets []*id.ID,
 		for targetIdx := range proxies {
 			target := targets[targetIdx]
 			targetProxies := proxies[targetIdx]
-			if !(int(proxyIdx)<len(targetProxies)){
-				jww.WARN.Printf("Failed to send to proxy %d on target %d (%s) " +
+			if !(int(proxyIdx) < len(targetProxies)) {
+				jww.WARN.Printf("Failed to send to proxy %d on target %d (%s) "+
 					"due to not enough proxies (only %d), skipping attempt", proxyIdx,
 					targetIdx, target, len(targetProxies))
 				continue
diff --git a/network/manager.go b/network/manager.go
index 3ebc1d1d9cd84007e01bce843cb9afd11d09e24b..f15199a419335f5ec6a02e5b2ca9299bfd3f83ec 100644
--- a/network/manager.go
+++ b/network/manager.go
@@ -86,6 +86,7 @@ func NewManager(session *storage.Session, switchboard *switchboard.Switchboard,
 		param:     params,
 		tracker:   &tracker,
 		addrSpace: ephemeral.NewAddressSpace(),
+		events:    events,
 	}
 
 	m.Internal = internal.Internal{
diff --git a/storage/session.go b/storage/session.go
index e42cad445594e30d7b85eafa688d90b80d4d62ed..fd98a85c22e1d1c3aea073ad3d1d9d6a709c6e1c 100644
--- a/storage/session.go
+++ b/storage/session.go
@@ -46,7 +46,8 @@ const currentSessionVersion = 0
 
 // Session object, backed by encrypted filestore
 type Session struct {
-	kv  *versioned.KV
+	kv *versioned.KV
+
 	mux sync.RWMutex
 
 	//memoized data