diff --git a/indexedDb/init.go b/indexedDb/init.go
index 95ae98a0ae3ce85da85e559dd59a4a8775f1d284..271300b4bafcec519a1b15ea05518fb0cfae5d6a 100644
--- a/indexedDb/init.go
+++ b/indexedDb/init.go
@@ -186,5 +186,12 @@ func v1Upgrade(db *idb.Database) error {
 		return err
 	}
 
+	// Get the database name and save it to storage
+	if databaseName, err := db.Name(); err != nil {
+		return err
+	} else if err = storage.StoreIndexedDb(databaseName); err != nil {
+		return err
+	}
+
 	return nil
 }
diff --git a/main.go b/main.go
index 4a2f6f6617c02a84750ffbb7a4bef5bb035426ac..7c5f570cf47b27cad75d3e65ba1465bd0590667e 100644
--- a/main.go
+++ b/main.go
@@ -44,6 +44,9 @@ func main() {
 		js.FuncOf(storage.ChangeExternalPassword))
 	js.Global().Set("VerifyPassword", js.FuncOf(storage.VerifyPassword))
 
+	// storage/purge.go
+	js.Global().Set("Purge", js.FuncOf(storage.Purge))
+
 	// utils/array.go
 	js.Global().Set("Uint8ArrayToBase64", js.FuncOf(utils.Uint8ArrayToBase64))
 	js.Global().Set("Base64ToUint8Array", js.FuncOf(utils.Base64ToUint8Array))
diff --git a/storage/indexedDbEncryptionTrack_test.go b/storage/indexedDbEncryptionTrack_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..ec754f87a940e14f6802e347f86f44fa437f4ff9
--- /dev/null
+++ b/storage/indexedDbEncryptionTrack_test.go
@@ -0,0 +1,40 @@
+////////////////////////////////////////////////////////////////////////////////
+// Copyright © 2022 xx foundation                                             //
+//                                                                            //
+// Use of this source code is governed by a license that can be found in the  //
+// LICENSE file.                                                              //
+////////////////////////////////////////////////////////////////////////////////
+
+//go:build js && wasm
+
+package storage
+
+import (
+	"testing"
+)
+
+// Tests that StoreIndexedDbEncryptionStatus stores the initial encryption value
+// and return that value on subsequent checks.
+func TestStoreIndexedDbEncryptionStatus(t *testing.T) {
+	databaseName := "databaseA"
+
+	encrypted, err := StoreIndexedDbEncryptionStatus(databaseName, true)
+	if err != nil {
+		t.Errorf("Failed to store/get encryption status: %+v", err)
+	}
+
+	if encrypted != true {
+		t.Errorf("Incorrect encryption values.\nexpected: %t\nreceived: %t",
+			true, encrypted)
+	}
+
+	encrypted, err = StoreIndexedDbEncryptionStatus(databaseName, false)
+	if err != nil {
+		t.Errorf("Failed to store/get encryption status: %+v", err)
+	}
+
+	if encrypted != true {
+		t.Errorf("Incorrect encryption values.\nexpected: %t\nreceived: %t",
+			true, encrypted)
+	}
+}
diff --git a/storage/indexedDbList.go b/storage/indexedDbList.go
new file mode 100644
index 0000000000000000000000000000000000000000..a736984f0ef742d96e7a63238ea6ebfbdc25d9f9
--- /dev/null
+++ b/storage/indexedDbList.go
@@ -0,0 +1,53 @@
+////////////////////////////////////////////////////////////////////////////////
+// Copyright © 2022 xx foundation                                             //
+//                                                                            //
+// Use of this source code is governed by a license that can be found in the  //
+// LICENSE file.                                                              //
+////////////////////////////////////////////////////////////////////////////////
+
+//go:build js && wasm
+
+package storage
+
+import (
+	"encoding/json"
+	"github.com/pkg/errors"
+	"os"
+)
+
+const indexedDbListKey = "xxDkWasmIndexedDbList"
+
+// GetIndexedDbList returns the list of stored indexedDb databases.
+func GetIndexedDbList() (map[string]struct{}, error) {
+	list := make(map[string]struct{})
+	listBytes, err := GetLocalStorage().GetItem(indexedDbListKey)
+	if err != nil && !errors.Is(err, os.ErrNotExist) {
+		return nil, err
+	} else if err == nil {
+		err = json.Unmarshal(listBytes, &list)
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	return list, nil
+}
+
+// StoreIndexedDb saved the indexedDb database name to storage.
+func StoreIndexedDb(databaseName string) error {
+	list, err := GetIndexedDbList()
+	if err != nil {
+		return err
+	}
+
+	list[databaseName] = struct{}{}
+
+	listBytes, err := json.Marshal(list)
+	if err != nil {
+		return err
+	}
+
+	GetLocalStorage().SetItem(indexedDbListKey, listBytes)
+
+	return nil
+}
diff --git a/storage/indexedDbList_test.go b/storage/indexedDbList_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..76270fce099c03b1774c49ce17736b8baeefea31
--- /dev/null
+++ b/storage/indexedDbList_test.go
@@ -0,0 +1,38 @@
+////////////////////////////////////////////////////////////////////////////////
+// Copyright © 2022 xx foundation                                             //
+//                                                                            //
+// Use of this source code is governed by a license that can be found in the  //
+// LICENSE file.                                                              //
+////////////////////////////////////////////////////////////////////////////////
+
+//go:build js && wasm
+
+package storage
+
+import (
+	"reflect"
+	"testing"
+)
+
+// Tests that three indexedDb database names stored with StoreIndexedDb are
+// retrieved with GetIndexedDbList.
+func TestStoreIndexedDb_GetIndexedDbList(t *testing.T) {
+	expected := map[string]struct{}{"db1": {}, "db2": {}, "db3": {}}
+
+	for name := range expected {
+		err := StoreIndexedDb(name)
+		if err != nil {
+			t.Errorf("Failed to store database name %q: %+v", name, err)
+		}
+	}
+
+	list, err := GetIndexedDbList()
+	if err != nil {
+		t.Errorf("Failed to get database list: %+v", err)
+	}
+
+	if !reflect.DeepEqual(expected, list) {
+		t.Errorf("Did not get expected list.\nexpected: %s\nreceived: %s",
+			expected, list)
+	}
+}
diff --git a/storage/localStorage.go b/storage/localStorage.go
index 2f2329209ca951c5f1dcc4e063dfde9ba9edc7f4..b0fcf33e96d095832bfafec2ec92ae7d4b63a64f 100644
--- a/storage/localStorage.go
+++ b/storage/localStorage.go
@@ -12,9 +12,16 @@ package storage
 import (
 	"encoding/base64"
 	"os"
+	"strings"
 	"syscall/js"
 )
 
+// localStorageWasmPrefix is prefixed to every keyName saved to local storage by
+// LocalStorage. It allows the identifications and deletion of keys only created
+// by this WASM binary while ignoring keys made by other scripts on the same
+// page.
+const localStorageWasmPrefix = "xxdkWasmStorage/"
+
 // LocalStorage contains the js.Value representation of localStorage.
 type LocalStorage struct {
 	// The Javascript value containing the localStorage object
@@ -43,7 +50,7 @@ func GetLocalStorage() *LocalStorage {
 //  - Documentation:
 //    https://developer.mozilla.org/en-US/docs/Web/API/Storage/getItem
 func (ls *LocalStorage) GetItem(keyName string) ([]byte, error) {
-	keyValue := ls.getItem(keyName)
+	keyValue := ls.getItem(localStorageWasmPrefix + keyName)
 	if keyValue.IsNull() {
 		return nil, os.ErrNotExist
 	}
@@ -65,7 +72,7 @@ func (ls *LocalStorage) GetItem(keyName string) ([]byte, error) {
 //    https://developer.mozilla.org/en-US/docs/Web/API/Storage/setItem
 func (ls *LocalStorage) SetItem(keyName string, keyValue []byte) {
 	encodedKeyValue := base64.StdEncoding.EncodeToString(keyValue)
-	ls.setItem(keyName, encodedKeyValue)
+	ls.setItem(localStorageWasmPrefix+keyName, encodedKeyValue)
 }
 
 // RemoveItem removes a key's value from local storage given its name. If there
@@ -77,7 +84,7 @@ func (ls *LocalStorage) SetItem(keyName string, keyValue []byte) {
 //  - Documentation:
 //    https://developer.mozilla.org/en-US/docs/Web/API/Storage/removeItem
 func (ls *LocalStorage) RemoveItem(keyName string) {
-	ls.removeItem(keyName)
+	ls.removeItem(localStorageWasmPrefix + keyName)
 }
 
 // Clear clears all the keys in storage. Underneath, it calls
@@ -91,6 +98,38 @@ func (ls *LocalStorage) Clear() {
 	ls.clear()
 }
 
+// ClearPrefix clears all keys with the given prefix.
+func (ls *LocalStorage) ClearPrefix(prefix string) {
+	// Get a copy of all key names at once
+	keys := js.Global().Get("Object").Call("keys", ls.v)
+
+	// Loop through each key
+	for i := 0; i < keys.Length(); i++ {
+		if v := keys.Index(i); !v.IsNull() {
+			keyName := strings.TrimPrefix(v.String(), localStorageWasmPrefix)
+			if strings.HasPrefix(keyName, prefix) {
+				ls.RemoveItem(keyName)
+			}
+		}
+	}
+}
+
+// ClearWASM clears all the keys in storage created by WASM.
+func (ls *LocalStorage) ClearWASM() {
+	// Get a copy of all key names at once
+	keys := js.Global().Get("Object").Call("keys", ls.v)
+
+	// Loop through each key
+	for i := 0; i < keys.Length(); i++ {
+		if v := keys.Index(i); !v.IsNull() {
+			keyName := v.String()
+			if strings.HasPrefix(keyName, localStorageWasmPrefix) {
+				ls.RemoveItem(strings.TrimPrefix(keyName, localStorageWasmPrefix))
+			}
+		}
+	}
+}
+
 // Key returns the name of the nth key in localStorage. Return os.ErrNotExist if
 // the key does not exist. The order of keys is not defined. If there is no item
 // with the given key, this function does nothing. Underneath, it calls
@@ -106,7 +145,7 @@ func (ls *LocalStorage) Key(n int) (string, error) {
 		return "", os.ErrNotExist
 	}
 
-	return keyName.String(), nil
+	return strings.TrimPrefix(keyName.String(), localStorageWasmPrefix), nil
 }
 
 // Length returns the number of keys in localStorage. Underneath, it accesses
diff --git a/storage/localStorage_test.go b/storage/localStorage_test.go
index 155bd7c4c32a558f05ea14989f18c1cb83a38051..6e96814e7594a8cb5a25b536fdfdb61692ec5a2e 100644
--- a/storage/localStorage_test.go
+++ b/storage/localStorage_test.go
@@ -12,6 +12,7 @@ package storage
 import (
 	"bytes"
 	"github.com/pkg/errors"
+	"math/rand"
 	"os"
 	"strconv"
 	"testing"
@@ -84,6 +85,73 @@ func TestLocalStorage_Clear(t *testing.T) {
 	}
 }
 
+// Tests that LocalStorage.ClearPrefix deletes only the keys with the given
+// prefix.
+func TestLocalStorage_ClearPrefix(t *testing.T) {
+	jsStorage.clear()
+	prng := rand.New(rand.NewSource(11))
+	var yesPrefix, noPrefix []string
+	prefix := "keyNamePrefix/"
+
+	for i := 0; i < 10; i++ {
+		keyName := "keyNum" + strconv.Itoa(i)
+		if prng.Intn(2) == 0 {
+			keyName = prefix + keyName
+			yesPrefix = append(yesPrefix, keyName)
+		} else {
+			noPrefix = append(noPrefix, keyName)
+		}
+
+		jsStorage.SetItem(keyName, []byte(strconv.Itoa(i)))
+	}
+
+	jsStorage.ClearPrefix(prefix)
+
+	for _, keyName := range noPrefix {
+		if _, err := jsStorage.GetItem(keyName); err != nil {
+			t.Errorf("Could not get keyName %q: %+v", keyName, err)
+		}
+	}
+	for _, keyName := range yesPrefix {
+		keyValue, err := jsStorage.GetItem(keyName)
+		if err == nil || !errors.Is(err, os.ErrNotExist) {
+			t.Errorf("Found keyName %q: %q", keyName, keyValue)
+		}
+	}
+}
+
+// Tests that LocalStorage.ClearWASM deletes all the WASM keys from storage and
+// does not remove any others
+func TestLocalStorage_ClearWASM(t *testing.T) {
+	jsStorage.clear()
+	prng := rand.New(rand.NewSource(11))
+	var yesPrefix, noPrefix []string
+	for i := 0; i < 10; i++ {
+		keyName := "keyNum" + strconv.Itoa(i)
+		if prng.Intn(2) == 0 {
+			yesPrefix = append(yesPrefix, keyName)
+			jsStorage.SetItem(keyName, []byte(strconv.Itoa(i)))
+		} else {
+			noPrefix = append(noPrefix, keyName)
+			jsStorage.setItem(keyName, strconv.Itoa(i))
+		}
+	}
+
+	jsStorage.ClearWASM()
+
+	for _, keyName := range noPrefix {
+		if v := jsStorage.getItem(keyName); v.IsNull() {
+			t.Errorf("Could not get keyName %q.", keyName)
+		}
+	}
+	for _, keyName := range yesPrefix {
+		keyValue, err := jsStorage.GetItem(keyName)
+		if err == nil || !errors.Is(err, os.ErrNotExist) {
+			t.Errorf("Found keyName %q: %q", keyName, keyValue)
+		}
+	}
+}
+
 // Tests that LocalStorage.Key return all added keys when looping through all
 // indexes.
 func TestLocalStorage_Key(t *testing.T) {
diff --git a/storage/purge.go b/storage/purge.go
new file mode 100644
index 0000000000000000000000000000000000000000..ed0b12e40ab0ff4884da8dff8dacf9e5a59279dd
--- /dev/null
+++ b/storage/purge.go
@@ -0,0 +1,81 @@
+////////////////////////////////////////////////////////////////////////////////
+// Copyright © 2022 xx foundation                                             //
+//                                                                            //
+// Use of this source code is governed by a license that can be found in the  //
+// LICENSE file.                                                              //
+////////////////////////////////////////////////////////////////////////////////
+
+//go:build js && wasm
+
+package storage
+
+import (
+	"github.com/hack-pad/go-indexeddb/idb"
+	"github.com/pkg/errors"
+	"gitlab.com/elixxir/xxdk-wasm/utils"
+	"sync/atomic"
+	"syscall/js"
+)
+
+// NumClientsRunning is an atomic that tracks the current number of Cmix
+// followers that have been started. Every time one is started, this counter
+// must be incremented and every time one is stopped, it must be decremented.
+//
+// This variable is an atomic. Only access it with atomic functions
+var NumClientsRunning uint64
+
+// Purge clears all local storage and indexedDb databases saved by this WASM
+// binary. All Cmix followers must be closed and the user's password is
+// required.
+//
+// Warning: This deletes all storage local to the webpage running this WASM.
+// Only use if you want to destroy everything.
+//
+// Parameters:
+//  - args[0] - Storage directory path (string).
+//  - args[1] - Password used for storage (Uint8Array).
+//
+// Returns:
+//  - Throws a TypeError if the password is incorrect or if not all Cmix
+//    followers have been stopped.
+func Purge(_ js.Value, args []js.Value) interface{} {
+	// Check the password
+	if !verifyPassword(args[1].String()) {
+		utils.Throw(utils.TypeError, errors.New("invalid password"))
+		return nil
+	}
+
+	// Verify all Cmix followers are stopped
+	if n := atomic.LoadUint64(&NumClientsRunning); n != 0 {
+		utils.Throw(
+			utils.TypeError, errors.Errorf("%d Cmix followers running", n))
+		return nil
+	}
+
+	// Get all indexedDb database names
+	databaseList, err := GetIndexedDbList()
+	if err != nil {
+		utils.Throw(
+			utils.TypeError, errors.Errorf(
+				"failed to get list of indexedDb database names: %+v", err))
+		return nil
+	}
+
+	// Delete each database
+	for dbName := range databaseList {
+		_, err = idb.Global().DeleteDatabase(dbName)
+		if err != nil {
+			utils.Throw(
+				utils.TypeError, errors.Errorf(
+					"failed to delete indexedDb database %q: %+v", dbName, err))
+			return nil
+		}
+	}
+
+	// Clear WASM local storage and EKV
+	ls := GetLocalStorage()
+	ls.ClearWASM()
+	ls.ClearPrefix(args[0].String())
+
+	return nil
+}
diff --git a/wasm/follow.go b/wasm/follow.go
index 4b343314206a28f5139b9d464fd4e965c11ea8a0..ed98bfbf4e54b92105f92cde0618e31cb674be77 100644
--- a/wasm/follow.go
+++ b/wasm/follow.go
@@ -10,7 +10,9 @@
 package wasm
 
 import (
+	"gitlab.com/elixxir/xxdk-wasm/storage"
 	"gitlab.com/elixxir/xxdk-wasm/utils"
+	"sync/atomic"
 	"syscall/js"
 )
 
@@ -60,6 +62,8 @@ func (c *Cmix) StartNetworkFollower(_ js.Value, args []js.Value) interface{} {
 		return nil
 	}
 
+	atomic.AddUint64(&storage.NumClientsRunning, 1)
+
 	return nil
 }
 
@@ -77,6 +81,7 @@ func (c *Cmix) StopNetworkFollower(js.Value, []js.Value) interface{} {
 		utils.Throw(utils.TypeError, err)
 		return nil
 	}
+	atomic.AddUint64(&storage.NumClientsRunning, ^uint64(0))
 
 	return nil
 }