diff --git a/Makefile b/Makefile
index cd183023d09da89d457b2f7a4ec08d33ab75db15..693d4c626a8ceecb85204819f36ccca46e203d3c 100644
--- a/Makefile
+++ b/Makefile
@@ -11,20 +11,20 @@ build:
 	GOOS=js GOARCH=wasm go build ./...
 
 update_release:
-	GOFLAGS="" go get gitlab.com/elixxir/wasm-utils@release
 	GOFLAGS="" go get gitlab.com/xx_network/primitives@release
 	GOFLAGS="" go get gitlab.com/elixxir/primitives@release
 	GOFLAGS="" go get gitlab.com/xx_network/crypto@release
 	GOFLAGS="" go get gitlab.com/elixxir/crypto@release
-	GOFLAGS="" go get -d gitlab.com/elixxir/client/v4@project/HavenBeta
+	GOFLAGS="" go get -d gitlab.com/elixxir/client/v4@release
+	GOFLAGS="" go get gitlab.com/elixxir/wasm-utils@release
 
 update_master:
-	GOFLAGS="" go get gitlab.com/elixxir/wasm-utils@master
 	GOFLAGS="" go get gitlab.com/xx_network/primitives@master
 	GOFLAGS="" go get gitlab.com/elixxir/primitives@master
 	GOFLAGS="" go get gitlab.com/xx_network/crypto@master
 	GOFLAGS="" go get gitlab.com/elixxir/crypto@master
 	GOFLAGS="" go get -d gitlab.com/elixxir/client/v4@master
+	GOFLAGS="" go get gitlab.com/elixxir/wasm-utils@master
 
 binary:
 	GOOS=js GOARCH=wasm go build -ldflags '-w -s' -trimpath -o xxdk.wasm main.go
diff --git a/go.mod b/go.mod
index 9afb4df05b449b791d4a5df76681991a1b32002f..72228bffc17b7aadb4927722e1fdce07594bb1c3 100644
--- a/go.mod
+++ b/go.mod
@@ -9,8 +9,8 @@ require (
 	github.com/spf13/cobra v1.7.0
 	github.com/spf13/jwalterweatherman v1.1.0
 	github.com/stretchr/testify v1.8.2
-	gitlab.com/elixxir/client/v4 v4.6.4-0.20230601191407-1b0289b33708
-	gitlab.com/elixxir/crypto v0.0.7-0.20230522162218-45433d877235
+	gitlab.com/elixxir/client/v4 v4.6.4-0.20230607172502-b56996e764f5
+	gitlab.com/elixxir/crypto v0.0.7-0.20230607170539-92d9508c78f9
 	gitlab.com/elixxir/primitives v0.0.3-0.20230214180039-9a25e2d3969c
 	gitlab.com/elixxir/wasm-utils v0.0.0-20230522231408-a43b2c1481b2
 	gitlab.com/xx_network/crypto v0.0.5-0.20230214003943-8a09396e95dd
@@ -21,6 +21,7 @@ require (
 require (
 	filippo.io/edwards25519 v1.0.0 // indirect
 	git.xx.network/elixxir/grpc-web-go-client v0.0.0-20230214175953-5b5a8c33d28a // indirect
+	github.com/Max-Sum/base32768 v0.0.0-20230304063302-18e6ce5945fd // indirect
 	github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 // indirect
 	github.com/badoux/checkmail v1.2.1 // indirect
 	github.com/cenkalti/backoff/v4 v4.1.3 // indirect
diff --git a/go.sum b/go.sum
index 73f80ff896fd23f961e61e7c74b4aed600bf767f..7a76f7af79184ab39ab0dac3c8edf792824442ec 100644
--- a/go.sum
+++ b/go.sum
@@ -43,6 +43,8 @@ git.xx.network/elixxir/grpc-web-go-client v0.0.0-20230214175953-5b5a8c33d28a/go.
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
 github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
+github.com/Max-Sum/base32768 v0.0.0-20230304063302-18e6ce5945fd h1:nzE1YQBdx1bq9IlZinHa+HVffy+NmVRoKr+wHN8fpLE=
+github.com/Max-Sum/base32768 v0.0.0-20230304063302-18e6ce5945fd/go.mod h1:C8yoIfvESpM3GD07OCHU7fqI7lhwyZ2Td1rbNbTAhnc=
 github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
 github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
 github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
@@ -545,12 +547,14 @@ gitlab.com/elixxir/client/v4 v4.6.4-0.20230531224642-b24cd3f5e4a4 h1:YacWU7IJUfi
 gitlab.com/elixxir/client/v4 v4.6.4-0.20230531224642-b24cd3f5e4a4/go.mod h1:fegbuF1/6a+H3QgsoMG8teLnyuKtDxkELMw8pn5WlZ8=
 gitlab.com/elixxir/client/v4 v4.6.4-0.20230601191407-1b0289b33708 h1:wNIKci4XuDfzAJM8+4awP5CgY0jlOEaVbHgp+BkEtz4=
 gitlab.com/elixxir/client/v4 v4.6.4-0.20230601191407-1b0289b33708/go.mod h1:fegbuF1/6a+H3QgsoMG8teLnyuKtDxkELMw8pn5WlZ8=
+gitlab.com/elixxir/client/v4 v4.6.4-0.20230607172502-b56996e764f5 h1:TbuQ5a0HJifvNaUey1AnqpX8v/OPdb2GvNmHPy2hm9g=
+gitlab.com/elixxir/client/v4 v4.6.4-0.20230607172502-b56996e764f5/go.mod h1:LmbxL4WIT2qiDN7wzpcJG+U13jn3pSqUHtT1+uxN4VI=
 gitlab.com/elixxir/comms v0.0.4-0.20230519211512-4a998f4b0938 h1:f27+QUFiGWrprKm+fstOg3ABkYLpWcZi3+8Lf5eDnqY=
 gitlab.com/elixxir/comms v0.0.4-0.20230519211512-4a998f4b0938/go.mod h1:z+qW0D9VpY5QKTd7wRlb5SK4kBNqLYsa4DXBcUXue9Q=
-gitlab.com/elixxir/crypto v0.0.7-0.20230522162218-45433d877235 h1:0BySdXTzRWxzH8k5RiNNMmmn2lpuQWLVcDDA/7ehyqc=
-gitlab.com/elixxir/crypto v0.0.7-0.20230522162218-45433d877235/go.mod h1:IYInxKr5Q7EH3oNhg1QX1/sTTRNi7L0JkcyfdRegoio=
-gitlab.com/elixxir/ekv v0.3.1-0.20230525165450-f444c687504b h1:hf28yepO93tCacx1bUAh8vVFkBUEuBaJhOjifBxEQK4=
-gitlab.com/elixxir/ekv v0.3.1-0.20230525165450-f444c687504b/go.mod h1:EMaUQrsOxvEPQ0/8V/PSkGqFmEC2axBG/uqY0oW2uJM=
+gitlab.com/elixxir/crypto v0.0.7-0.20230531164732-6951a4e33a55 h1:J0h3APo7OcIWPnmtIJCyeMOkE9wT6vI8Bw+oZq53S8E=
+gitlab.com/elixxir/crypto v0.0.7-0.20230531164732-6951a4e33a55/go.mod h1:lAib0KO9TeTLWbwgFk2uszRxPkHeu843xqnYdkzdEB0=
+gitlab.com/elixxir/crypto v0.0.7-0.20230607170539-92d9508c78f9 h1:+RZcoU7NOPXCenkKM/ImYyLcNDdG4vsWrJyKqoAAv8k=
+gitlab.com/elixxir/crypto v0.0.7-0.20230607170539-92d9508c78f9/go.mod h1:lAib0KO9TeTLWbwgFk2uszRxPkHeu843xqnYdkzdEB0=
 gitlab.com/elixxir/ekv v0.3.1-0.20230525213559-f9da13f4fce1 h1:8XBo6QQBXXGCTrgXHFuqPL21mROLKLAoO3X9xR5TwA0=
 gitlab.com/elixxir/ekv v0.3.1-0.20230525213559-f9da13f4fce1/go.mod h1:UStTZ9d1UVn9Ahyb49lrbPKyr/Wb8xFWqMXbDgIqQhE=
 gitlab.com/elixxir/primitives v0.0.3-0.20230214180039-9a25e2d3969c h1:muG8ff95woeVVwQoJHCEclxBFB22lc7EixPylEkYDRU=
diff --git a/indexedDb/impl/channels/callbacks.go b/indexedDb/impl/channels/callbacks.go
index 2a901597117fac367e360ee2f80a2567fcab036f..7fe0a8a13385c5984ccc005853231634a9d4a8d6 100644
--- a/indexedDb/impl/channels/callbacks.go
+++ b/indexedDb/impl/channels/callbacks.go
@@ -18,8 +18,8 @@ import (
 	"gitlab.com/elixxir/client/v4/channels"
 	"gitlab.com/elixxir/client/v4/cmix/rounds"
 	cryptoBroadcast "gitlab.com/elixxir/crypto/broadcast"
-	cryptoChannel "gitlab.com/elixxir/crypto/channel"
 	"gitlab.com/elixxir/crypto/fastRNG"
+	idbCrypto "gitlab.com/elixxir/crypto/indexedDb"
 	"gitlab.com/elixxir/crypto/message"
 	wChannels "gitlab.com/elixxir/xxdk-wasm/indexedDb/worker/channels"
 	"gitlab.com/elixxir/xxdk-wasm/worker"
@@ -64,7 +64,7 @@ func (m *manager) newWASMEventModelCB(data []byte) ([]byte, error) {
 
 	// Create new encryption cipher
 	rng := fastRNG.NewStreamGenerator(12, 1024, csprng.NewSystemRNG)
-	encryption, err := cryptoChannel.NewCipherFromJSON(
+	encryption, err := idbCrypto.NewCipherFromJSON(
 		[]byte(msg.EncryptionJSON), rng.GetStream())
 	if err != nil {
 		return []byte{}, errors.Errorf(
diff --git a/indexedDb/impl/channels/implementation.go b/indexedDb/impl/channels/implementation.go
index e755acbafb3f750e577305fb9e241c537f30febe..dfc000eba4476870a38b3e6dbd7c8181f55fb5f3 100644
--- a/indexedDb/impl/channels/implementation.go
+++ b/indexedDb/impl/channels/implementation.go
@@ -25,7 +25,7 @@ import (
 	"gitlab.com/elixxir/client/v4/channels"
 	"gitlab.com/elixxir/client/v4/cmix/rounds"
 	cryptoBroadcast "gitlab.com/elixxir/crypto/broadcast"
-	cryptoChannel "gitlab.com/elixxir/crypto/channel"
+	idbCrypto "gitlab.com/elixxir/crypto/indexedDb"
 	"gitlab.com/elixxir/crypto/message"
 	"gitlab.com/elixxir/wasm-utils/utils"
 	"gitlab.com/elixxir/xxdk-wasm/indexedDb/impl"
@@ -37,7 +37,7 @@ import (
 // caller to ensure that its methods are called sequentially.
 type wasmModel struct {
 	db          *idb.Database
-	cipher      cryptoChannel.Cipher
+	cipher      idbCrypto.Cipher
 	eventUpdate func(eventType int64, jsonMarshallable any)
 }
 
@@ -145,12 +145,11 @@ func (w *wasmModel) ReceiveMessage(channelID *id.ID, messageID message.ID,
 	nickname, text string, pubKey ed25519.PublicKey, dmToken uint32,
 	codeset uint8, timestamp time.Time, lease time.Duration, round rounds.Round,
 	mType channels.MessageType, status channels.SentStatus, hidden bool) uint64 {
-	textBytes := []byte(text)
 	var err error
 
 	// Handle encryption, if it is present
 	if w.cipher != nil {
-		textBytes, err = w.cipher.Encrypt([]byte(text))
+		text, err = w.cipher.Encrypt([]byte(text))
 		if err != nil {
 			jww.ERROR.Printf("Failed to encrypt Message: %+v", err)
 			return 0
@@ -161,7 +160,7 @@ func (w *wasmModel) ReceiveMessage(channelID *id.ID, messageID message.ID,
 
 	msgToInsert := buildMessage(
 		channelIDBytes, messageID.Bytes(), nil, nickname,
-		textBytes, pubKey, dmToken, codeset, timestamp, lease, round.ID, mType,
+		text, pubKey, dmToken, codeset, timestamp, lease, round.ID, mType,
 		false, hidden, status)
 
 	uuid, err := w.upsertMessage(msgToInsert)
@@ -189,12 +188,11 @@ func (w *wasmModel) ReceiveReply(channelID *id.ID, messageID,
 	dmToken uint32, codeset uint8, timestamp time.Time, lease time.Duration,
 	round rounds.Round, mType channels.MessageType, status channels.SentStatus,
 	hidden bool) uint64 {
-	textBytes := []byte(text)
 	var err error
 
 	// Handle encryption, if it is present
 	if w.cipher != nil {
-		textBytes, err = w.cipher.Encrypt([]byte(text))
+		text, err = w.cipher.Encrypt([]byte(text))
 		if err != nil {
 			jww.ERROR.Printf("Failed to encrypt Message: %+v", err)
 			return 0
@@ -204,7 +202,7 @@ func (w *wasmModel) ReceiveReply(channelID *id.ID, messageID,
 	channelIDBytes := channelID.Marshal()
 
 	msgToInsert := buildMessage(channelIDBytes, messageID.Bytes(),
-		replyTo.Bytes(), nickname, textBytes, pubKey, dmToken, codeset,
+		replyTo.Bytes(), nickname, text, pubKey, dmToken, codeset,
 		timestamp, lease, round.ID, mType, hidden, false, status)
 
 	uuid, err := w.upsertMessage(msgToInsert)
@@ -232,12 +230,11 @@ func (w *wasmModel) ReceiveReaction(channelID *id.ID, messageID,
 	dmToken uint32, codeset uint8, timestamp time.Time, lease time.Duration,
 	round rounds.Round, mType channels.MessageType, status channels.SentStatus,
 	hidden bool) uint64 {
-	textBytes := []byte(reaction)
 	var err error
 
 	// Handle encryption, if it is present
 	if w.cipher != nil {
-		textBytes, err = w.cipher.Encrypt([]byte(reaction))
+		reaction, err = w.cipher.Encrypt([]byte(reaction))
 		if err != nil {
 			jww.ERROR.Printf("Failed to encrypt Message: %+v", err)
 			return 0
@@ -247,7 +244,7 @@ func (w *wasmModel) ReceiveReaction(channelID *id.ID, messageID,
 	channelIDBytes := channelID.Marshal()
 	msgToInsert := buildMessage(
 		channelIDBytes, messageID.Bytes(), reactionTo.Bytes(), nickname,
-		textBytes, pubKey, dmToken, codeset, timestamp, lease, round.ID, mType,
+		reaction, pubKey, dmToken, codeset, timestamp, lease, round.ID, mType,
 		false, hidden, status)
 
 	uuid, err := w.upsertMessage(msgToInsert)
@@ -349,8 +346,8 @@ func (w *wasmModel) UpdateFromMessageID(messageID message.ID,
 // NOTE: ID is not set inside this function because we want to use the
 // autoincrement key by default. If you are trying to overwrite an existing
 // message, then you need to set it manually yourself.
-func buildMessage(channelID, messageID, parentID []byte, nickname string,
-	text []byte, pubKey ed25519.PublicKey, dmToken uint32, codeset uint8,
+func buildMessage(channelID, messageID, parentID []byte, nickname,
+	text string, pubKey ed25519.PublicKey, dmToken uint32, codeset uint8,
 	timestamp time.Time, lease time.Duration, round id.Round,
 	mType channels.MessageType, pinned, hidden bool,
 	status channels.SentStatus) *Message {
@@ -499,7 +496,7 @@ func (w *wasmModel) GetMessage(
 		Status:          channels.SentStatus(lookupResult.Status),
 		Hidden:          lookupResult.Hidden,
 		Pinned:          lookupResult.Pinned,
-		Content:         lookupResult.Text,
+		Content:         []byte(lookupResult.Text),
 		Type:            channels.MessageType(lookupResult.Type),
 		Round:           id.Round(lookupResult.Round),
 		PubKey:          lookupResult.Pubkey,
diff --git a/indexedDb/impl/channels/implementation_test.go b/indexedDb/impl/channels/implementation_test.go
index f596065dcc47aa39e3b5aeda7b97a1f389071977..58cd04a1c6d346a02d0073ef33e9e5e292c8607e 100644
--- a/indexedDb/impl/channels/implementation_test.go
+++ b/indexedDb/impl/channels/implementation_test.go
@@ -27,8 +27,8 @@ import (
 	cft "gitlab.com/elixxir/client/v4/channelsFileTransfer"
 	"gitlab.com/elixxir/client/v4/cmix/rounds"
 	cryptoBroadcast "gitlab.com/elixxir/crypto/broadcast"
-	cryptoChannel "gitlab.com/elixxir/crypto/channel"
 	"gitlab.com/elixxir/crypto/fileTransfer"
+	idbCrypto "gitlab.com/elixxir/crypto/indexedDb"
 	"gitlab.com/elixxir/crypto/message"
 	"gitlab.com/elixxir/wasm-utils/storage"
 	"gitlab.com/elixxir/xxdk-wasm/indexedDb/impl"
@@ -120,12 +120,12 @@ func TestWasmModel_ReceiveFile(t *testing.T) {
 
 // Happy path, insert message and look it up
 func TestWasmModel_GetMessage(t *testing.T) {
-	cipher, err := cryptoChannel.NewCipher(
+	cipher, err := idbCrypto.NewCipher(
 		[]byte("testPass"), []byte("testSalt"), 128, csprng.NewSystemRNG())
 	if err != nil {
 		t.Fatalf("Failed to create cipher")
 	}
-	for _, c := range []cryptoChannel.Cipher{nil, cipher} {
+	for _, c := range []idbCrypto.Cipher{nil, cipher} {
 		cs := ""
 		if c != nil {
 			cs = "_withCipher"
@@ -142,7 +142,7 @@ func TestWasmModel_GetMessage(t *testing.T) {
 			}
 
 			testMsg := buildMessage(id.NewIdFromBytes([]byte(testString), t).Marshal(),
-				testMsgId.Bytes(), nil, testString, []byte(testString),
+				testMsgId.Bytes(), nil, testString, testString,
 				[]byte{8, 6, 7, 5}, 0, 0, netTime.Now(),
 				time.Second, 0, 0, false, false, channels.Sent)
 			_, err = eventModel.upsertMessage(testMsg)
@@ -173,7 +173,7 @@ func TestWasmModel_DeleteMessage(t *testing.T) {
 
 	// Insert a message
 	testMsg := buildMessage([]byte(testString), testMsgId.Bytes(), nil,
-		testString, []byte(testString), []byte{8, 6, 7, 5}, 0, 0, netTime.Now(),
+		testString, testString, []byte{8, 6, 7, 5}, 0, 0, netTime.Now(),
 		time.Second, 0, 0, false, false, channels.Sent)
 	_, err = eventModel.upsertMessage(testMsg)
 	if err != nil {
@@ -207,12 +207,12 @@ func TestWasmModel_DeleteMessage(t *testing.T) {
 
 // Test wasmModel.UpdateSentStatus happy path and ensure fields don't change.
 func Test_wasmModel_UpdateSentStatus(t *testing.T) {
-	cipher, err := cryptoChannel.NewCipher(
+	cipher, err := idbCrypto.NewCipher(
 		[]byte("testPass"), []byte("testSalt"), 128, csprng.NewSystemRNG())
 	if err != nil {
 		t.Fatalf("Failed to create cipher")
 	}
-	for _, c := range []cryptoChannel.Cipher{nil, cipher} {
+	for _, c := range []idbCrypto.Cipher{nil, cipher} {
 		cs := ""
 		if c != nil {
 			cs = "_withCipher"
@@ -234,7 +234,7 @@ func Test_wasmModel_UpdateSentStatus(t *testing.T) {
 
 			// Store a test message
 			testMsg := buildMessage(cid.Bytes(), testMsgId.Bytes(), nil,
-				testString, []byte(testString), []byte{8, 6, 7, 5}, 0, 0,
+				testString, testString, []byte{8, 6, 7, 5}, 0, 0,
 				netTime.Now(), time.Second, 0, 0, false, false, channels.Sent)
 			uuid, err2 := eventModel.upsertMessage(testMsg)
 			if err2 != nil {
@@ -282,12 +282,12 @@ func Test_wasmModel_UpdateSentStatus(t *testing.T) {
 
 // Smoke test wasmModel.JoinChannel/wasmModel.LeaveChannel happy paths.
 func Test_wasmModel_JoinChannel_LeaveChannel(t *testing.T) {
-	cipher, err := cryptoChannel.NewCipher(
+	cipher, err := idbCrypto.NewCipher(
 		[]byte("testPass"), []byte("testSalt"), 128, csprng.NewSystemRNG())
 	if err != nil {
 		t.Fatalf("Failed to create cipher")
 	}
-	for _, c := range []cryptoChannel.Cipher{nil, cipher} {
+	for _, c := range []idbCrypto.Cipher{nil, cipher} {
 		cs := ""
 		if c != nil {
 			cs = "_withCipher"
@@ -334,12 +334,12 @@ func Test_wasmModel_JoinChannel_LeaveChannel(t *testing.T) {
 
 // Test UUID gets returned when different messages are added.
 func Test_wasmModel_UUIDTest(t *testing.T) {
-	cipher, err := cryptoChannel.NewCipher(
+	cipher, err := idbCrypto.NewCipher(
 		[]byte("testPass"), []byte("testSalt"), 128, csprng.NewSystemRNG())
 	if err != nil {
 		t.Fatalf("Failed to create cipher")
 	}
-	for _, c := range []cryptoChannel.Cipher{nil, cipher} {
+	for _, c := range []idbCrypto.Cipher{nil, cipher} {
 		cs := ""
 		if c != nil {
 			cs = "_withCipher"
@@ -381,12 +381,12 @@ func Test_wasmModel_UUIDTest(t *testing.T) {
 
 // Tests if the same message ID being sent always returns the same UUID.
 func Test_wasmModel_DuplicateReceives(t *testing.T) {
-	cipher, err := cryptoChannel.NewCipher(
+	cipher, err := idbCrypto.NewCipher(
 		[]byte("testPass"), []byte("testSalt"), 128, csprng.NewSystemRNG())
 	if err != nil {
 		t.Fatalf("Failed to create cipher")
 	}
-	for _, c := range []cryptoChannel.Cipher{nil, cipher} {
+	for _, c := range []idbCrypto.Cipher{nil, cipher} {
 		cs := ""
 		if c != nil {
 			cs = "_withCipher"
@@ -428,12 +428,12 @@ func Test_wasmModel_DuplicateReceives(t *testing.T) {
 // Happy path: Inserts many messages, deletes some, and checks that the final
 // result is as expected.
 func Test_wasmModel_deleteMsgByChannel(t *testing.T) {
-	cipher, err := cryptoChannel.NewCipher(
+	cipher, err := idbCrypto.NewCipher(
 		[]byte("testPass"), []byte("testSalt"), 128, csprng.NewSystemRNG())
 	if err != nil {
 		t.Fatalf("Failed to create cipher")
 	}
-	for _, c := range []cryptoChannel.Cipher{nil, cipher} {
+	for _, c := range []idbCrypto.Cipher{nil, cipher} {
 		cs := ""
 		if c != nil {
 			cs = "_withCipher"
@@ -501,12 +501,12 @@ func Test_wasmModel_deleteMsgByChannel(t *testing.T) {
 // This test is designed to prove the behavior of unique indexes.
 // Inserts will not fail, they simply will not happen.
 func TestWasmModel_receiveHelper_UniqueIndex(t *testing.T) {
-	cipher, err := cryptoChannel.NewCipher(
+	cipher, err := idbCrypto.NewCipher(
 		[]byte("testPass"), []byte("testSalt"), 128, csprng.NewSystemRNG())
 	if err != nil {
 		t.Fatalf("Failed to create cipher")
 	}
-	for i, c := range []cryptoChannel.Cipher{nil, cipher} {
+	for i, c := range []idbCrypto.Cipher{nil, cipher} {
 		cs := ""
 		if c != nil {
 			cs = "_withCipher"
@@ -542,12 +542,12 @@ func TestWasmModel_receiveHelper_UniqueIndex(t *testing.T) {
 
 			testMsgId := message.DeriveChannelMessageID(&id.ID{1}, 0, []byte(testString))
 			testMsg := buildMessage([]byte(testString), testMsgId.Bytes(), nil,
-				testString, []byte(testString), []byte{8, 6, 7, 5}, 0, 0,
+				testString, testString, []byte{8, 6, 7, 5}, 0, 0,
 				netTime.Now(), time.Second, 0, 0, false, false, channels.Sent)
 
 			testMsgId2 := message.DeriveChannelMessageID(&id.ID{2}, 0, []byte(testString))
 			testMsg2 := buildMessage([]byte(testString), testMsgId2.Bytes(), nil,
-				testString, []byte(testString), []byte{8, 6, 7, 5}, 0, 0,
+				testString, testString, []byte{8, 6, 7, 5}, 0, 0,
 				netTime.Now(), time.Second, 0, 0, false, false, channels.Sent)
 
 			// First message insert should succeed
diff --git a/indexedDb/impl/channels/init.go b/indexedDb/impl/channels/init.go
index 48f5359f9b61c67b42533a42fa37cec4c3c917e7..e604b666636e0953a0a9e19947c53550972d1e76 100644
--- a/indexedDb/impl/channels/init.go
+++ b/indexedDb/impl/channels/init.go
@@ -18,7 +18,7 @@ import (
 
 	"gitlab.com/elixxir/client/v4/bindings"
 	"gitlab.com/elixxir/client/v4/channels"
-	cryptoChannel "gitlab.com/elixxir/crypto/channel"
+	idbCrypto "gitlab.com/elixxir/crypto/indexedDb"
 	"gitlab.com/elixxir/xxdk-wasm/indexedDb/impl"
 )
 
@@ -29,13 +29,13 @@ const currentVersion uint = 1
 // NewWASMEventModel returns a [channels.EventModel] backed by a wasmModel.
 // The name should be a base64 encoding of the users public key. Returns the
 // EventModel based on IndexedDb and the database name as reported by IndexedDb.
-func NewWASMEventModel(databaseName string, encryption cryptoChannel.Cipher,
+func NewWASMEventModel(databaseName string, encryption idbCrypto.Cipher,
 	uiCallbacks bindings.ChannelUICallbacks) (channels.EventModel, error) {
 	return newWASMModel(databaseName, encryption, uiCallbacks)
 }
 
 // newWASMModel creates the given [idb.Database] and returns a wasmModel.
-func newWASMModel(databaseName string, encryption cryptoChannel.Cipher,
+func newWASMModel(databaseName string, encryption idbCrypto.Cipher,
 	uiCallbacks bindings.ChannelUICallbacks) (*wasmModel, error) {
 	// Attempt to open database object
 	ctx, cancel := impl.NewContext()
diff --git a/indexedDb/impl/channels/model.go b/indexedDb/impl/channels/model.go
index e5d3e00aa5209985de60a77f5330a53208b1af88..702d489b04d4eb6bfac510a5b9e77f753f411cee 100644
--- a/indexedDb/impl/channels/model.go
+++ b/indexedDb/impl/channels/model.go
@@ -56,7 +56,7 @@ type Message struct {
 	Status          uint8     `json:"status"`
 	Hidden          bool      `json:"hidden"`
 	Pinned          bool      `json:"pinned"` // Index
-	Text            []byte    `json:"text"`
+	Text            string    `json:"text"`
 	Type            uint16    `json:"type"`
 	Round           uint64    `json:"round"`
 
diff --git a/indexedDb/impl/dm/callbacks.go b/indexedDb/impl/dm/callbacks.go
index 380f04d9953fdda65c4b0d80a66e7b40a7edc2b1..061c46a31b5667dd6c04303def1b3b1bab57a66a 100644
--- a/indexedDb/impl/dm/callbacks.go
+++ b/indexedDb/impl/dm/callbacks.go
@@ -17,8 +17,8 @@ import (
 	jww "github.com/spf13/jwalterweatherman"
 
 	"gitlab.com/elixxir/client/v4/dm"
-	cryptoChannel "gitlab.com/elixxir/crypto/channel"
 	"gitlab.com/elixxir/crypto/fastRNG"
+	idbCrypto "gitlab.com/elixxir/crypto/indexedDb"
 	wDm "gitlab.com/elixxir/xxdk-wasm/indexedDb/worker/dm"
 	"gitlab.com/elixxir/xxdk-wasm/worker"
 	"gitlab.com/xx_network/crypto/csprng"
@@ -61,7 +61,7 @@ func (m *manager) newWASMEventModelCB(data []byte) ([]byte, error) {
 
 	// Create new encryption cipher
 	rng := fastRNG.NewStreamGenerator(12, 1024, csprng.NewSystemRNG)
-	encryption, err := cryptoChannel.NewCipherFromJSON(
+	encryption, err := idbCrypto.NewCipherFromJSON(
 		[]byte(msg.EncryptionJSON), rng.GetStream())
 	if err != nil {
 		return []byte{}, errors.Errorf("failed to JSON unmarshal channel "+
diff --git a/indexedDb/impl/dm/implementation.go b/indexedDb/impl/dm/implementation.go
index a92691878ebc4b6c21cd1d38297aca23e4109920..f40791ece46d05b397dadd6766ec455045168452 100644
--- a/indexedDb/impl/dm/implementation.go
+++ b/indexedDb/impl/dm/implementation.go
@@ -24,7 +24,7 @@ import (
 
 	"gitlab.com/elixxir/client/v4/cmix/rounds"
 	"gitlab.com/elixxir/client/v4/dm"
-	cryptoChannel "gitlab.com/elixxir/crypto/channel"
+	idbCrypto "gitlab.com/elixxir/crypto/indexedDb"
 	"gitlab.com/elixxir/crypto/message"
 	"gitlab.com/elixxir/wasm-utils/utils"
 	"gitlab.com/elixxir/xxdk-wasm/indexedDb/impl"
@@ -36,7 +36,7 @@ import (
 // caller to ensure that its methods are called sequentially.
 type wasmModel struct {
 	db                *idb.Database
-	cipher            cryptoChannel.Cipher
+	cipher            idbCrypto.Cipher
 	receivedMessageCB MessageReceivedCallback
 }
 
@@ -81,7 +81,7 @@ func (w *wasmModel) upsertConversation(nickname string,
 // NOTE: ID is not set inside this function because we want to use the
 // autoincrement key by default. If you are trying to overwrite an existing
 // message, then you need to set it manually yourself.
-func buildMessage(messageID, parentID, text []byte, partnerKey,
+func buildMessage(messageID, parentID []byte, text string, partnerKey []byte,
 	senderKey ed25519.PublicKey, timestamp time.Time, round id.Round,
 	mType dm.MessageType, codeset uint8, status dm.Status) *Message {
 	return &Message{
@@ -276,9 +276,8 @@ func (w *wasmModel) receiveWrapper(messageID message.ID, parentID *message.ID, n
 	}
 
 	// Handle encryption, if it is present
-	textBytes := []byte(data)
 	if w.cipher != nil {
-		textBytes, err = w.cipher.Encrypt(textBytes)
+		data, err = w.cipher.Encrypt([]byte(data))
 		if err != nil {
 			return 0, err
 		}
@@ -289,7 +288,7 @@ func (w *wasmModel) receiveWrapper(messageID message.ID, parentID *message.ID, n
 		parentIdBytes = parentID.Marshal()
 	}
 
-	msgToInsert := buildMessage(messageID.Bytes(), parentIdBytes, textBytes,
+	msgToInsert := buildMessage(messageID.Bytes(), parentIdBytes, data,
 		partnerKey, senderKey, timestamp, round.ID, mType, codeset, status)
 
 	uuid, err := w.upsertMessage(msgToInsert)
diff --git a/indexedDb/impl/dm/init.go b/indexedDb/impl/dm/init.go
index 8332866b95055e7d322308cdf8669d1be78a95ac..aabad5c593eaca751ada0e364893aa6382135f35 100644
--- a/indexedDb/impl/dm/init.go
+++ b/indexedDb/impl/dm/init.go
@@ -17,7 +17,7 @@ import (
 	jww "github.com/spf13/jwalterweatherman"
 
 	"gitlab.com/elixxir/client/v4/dm"
-	cryptoChannel "gitlab.com/elixxir/crypto/channel"
+	idbCrypto "gitlab.com/elixxir/crypto/indexedDb"
 	"gitlab.com/elixxir/xxdk-wasm/indexedDb/impl"
 )
 
@@ -35,13 +35,13 @@ type MessageReceivedCallback func(
 // NewWASMEventModel returns a [channels.EventModel] backed by a wasmModel.
 // The name should be a base64 encoding of the users public key. Returns the
 // EventModel based on IndexedDb and the database name as reported by IndexedDb.
-func NewWASMEventModel(databaseName string, encryption cryptoChannel.Cipher,
+func NewWASMEventModel(databaseName string, encryption idbCrypto.Cipher,
 	cb MessageReceivedCallback) (dm.EventModel, error) {
 	return newWASMModel(databaseName, encryption, cb)
 }
 
 // newWASMModel creates the given [idb.Database] and returns a wasmModel.
-func newWASMModel(databaseName string, encryption cryptoChannel.Cipher,
+func newWASMModel(databaseName string, encryption idbCrypto.Cipher,
 	cb MessageReceivedCallback) (*wasmModel, error) {
 	// Attempt to open database object
 	ctx, cancel := impl.NewContext()
diff --git a/indexedDb/impl/dm/model.go b/indexedDb/impl/dm/model.go
index 774d011fe4987078808febd74a1657839e06dc8a..6893fc3a6a7fffd95eb31e389e8352040e797b9f 100644
--- a/indexedDb/impl/dm/model.go
+++ b/indexedDb/impl/dm/model.go
@@ -46,7 +46,7 @@ type Message struct {
 	SenderPubKey       []byte    `json:"sender_pub_key"` // Index
 	CodesetVersion     uint8     `json:"codeset_version"`
 	Status             uint8     `json:"status"`
-	Text               []byte    `json:"text"`
+	Text               string    `json:"text"`
 	Type               uint16    `json:"type"`
 	Round              uint64    `json:"round"`
 }
diff --git a/indexedDb/worker/channels/init.go b/indexedDb/worker/channels/init.go
index d24120aa445d6c161bf148a242439f4a6bf05eb1..d14a80ebfc5d91dd3e100e58d7ec2cb45f7d281e 100644
--- a/indexedDb/worker/channels/init.go
+++ b/indexedDb/worker/channels/init.go
@@ -18,8 +18,7 @@ import (
 	jww "github.com/spf13/jwalterweatherman"
 	"gitlab.com/elixxir/client/v4/bindings"
 	"gitlab.com/elixxir/client/v4/channels"
-
-	cryptoChannel "gitlab.com/elixxir/crypto/channel"
+	idbCrypto "gitlab.com/elixxir/crypto/indexedDb"
 	"gitlab.com/elixxir/xxdk-wasm/storage"
 	"gitlab.com/elixxir/xxdk-wasm/worker"
 )
@@ -36,7 +35,7 @@ type eventUpdateCallback func(eventType int64, jsonData []byte)
 // the channel manager to define the path but the callback is the same
 // across the board.
 func NewWASMEventModelBuilder(wasmJsPath string,
-	encryption cryptoChannel.Cipher,
+	encryption idbCrypto.Cipher,
 	channelCbs bindings.ChannelUICallbacks) channels.EventModelBuilder {
 	fn := func(path string) (channels.EventModel, error) {
 		return NewWASMEventModel(path, wasmJsPath, encryption,
@@ -54,7 +53,7 @@ type NewWASMEventModelMessage struct {
 
 // NewWASMEventModel returns a [channels.EventModel] backed by a wasmModel.
 // The name should be a base64 encoding of the users public key.
-func NewWASMEventModel(path, wasmJsPath string, encryption cryptoChannel.Cipher,
+func NewWASMEventModel(path, wasmJsPath string, encryption idbCrypto.Cipher,
 	channelCbs bindings.ChannelUICallbacks) (
 	channels.EventModel, error) {
 	databaseName := path + databaseSuffix
diff --git a/indexedDb/worker/dm/init.go b/indexedDb/worker/dm/init.go
index 3fd1cd13897bdc8684e2efc13750e1fed20ff000..b7afc2b2237c2258feb91084e644402e775c45f1 100644
--- a/indexedDb/worker/dm/init.go
+++ b/indexedDb/worker/dm/init.go
@@ -18,7 +18,7 @@ import (
 	jww "github.com/spf13/jwalterweatherman"
 
 	"gitlab.com/elixxir/client/v4/dm"
-	cryptoChannel "gitlab.com/elixxir/crypto/channel"
+	idbCrypto "gitlab.com/elixxir/crypto/indexedDb"
 	"gitlab.com/elixxir/xxdk-wasm/storage"
 	"gitlab.com/elixxir/xxdk-wasm/worker"
 )
@@ -42,7 +42,7 @@ type NewWASMEventModelMessage struct {
 
 // NewWASMEventModel returns a [channels.EventModel] backed by a wasmModel.
 // The name should be a base64 encoding of the users public key.
-func NewWASMEventModel(path, wasmJsPath string, encryption cryptoChannel.Cipher,
+func NewWASMEventModel(path, wasmJsPath string, encryption idbCrypto.Cipher,
 	cb MessageReceivedCallback) (dm.EventModel, error) {
 	databaseName := path + databaseSuffix
 
diff --git a/main.go b/main.go
index 5315e3738a2c8825791ac725026aa5c828a5ea44..b2a964719718eb79e9a006ec3e8b225c0e075b17 100644
--- a/main.go
+++ b/main.go
@@ -136,8 +136,12 @@ func setGlobals() {
 		js.FuncOf(wasm.GetChannelNotificationReportsForMe))
 	js.Global().Set("GetNoMessageErr", js.FuncOf(wasm.GetNoMessageErr))
 	js.Global().Set("CheckNoMessageErr", js.FuncOf(wasm.CheckNoMessageErr))
-	js.Global().Set("NewChannelsDatabaseCipher",
-		js.FuncOf(wasm.NewChannelsDatabaseCipher))
+	js.Global().Set("GetNotificationReportsForMe",
+		js.FuncOf(wasm.GetChannelNotificationReportsForMe))
+
+	// wasm/cipher.go
+	js.Global().Set("NewDatabaseCipher",
+		js.FuncOf(wasm.NewDatabaseCipher))
 
 	// wasm/dm.go
 	js.Global().Set("InitChannelsFileTransfer",
@@ -150,7 +154,7 @@ func setGlobals() {
 	js.Global().Set("NewDMClientWithIndexedDbUnsafe",
 		js.FuncOf(wasm.NewDMClientWithIndexedDbUnsafe))
 	js.Global().Set("NewDMsDatabaseCipher",
-		js.FuncOf(wasm.NewDMsDatabaseCipher))
+		js.FuncOf(wasm.NewDatabaseCipher))
 
 	// wasm/cmix.go
 	js.Global().Set("NewCmix", js.FuncOf(wasm.NewCmix))
diff --git a/storage/password.go b/storage/password.go
index 7be49b310b54b7aa2764678f35d0b91550d2c491..5dec9055907b819c3290be05cd07454ddd312036 100644
--- a/storage/password.go
+++ b/storage/password.go
@@ -23,6 +23,7 @@ import (
 	"github.com/pkg/errors"
 	jww "github.com/spf13/jwalterweatherman"
 
+	"gitlab.com/elixxir/crypto/hash"
 	"gitlab.com/elixxir/wasm-utils/exception"
 	"gitlab.com/elixxir/wasm-utils/storage"
 	"gitlab.com/elixxir/wasm-utils/utils"
@@ -40,6 +41,8 @@ const (
 	// saltLen is the length of the salt. Recommended to be 16 bytes here:
 	// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-argon2-04#section-3.1
 	saltLen = 16
+
+	internalPasswordConstant = "XXInternalPassword"
 )
 
 // Storage keys.
@@ -58,7 +61,7 @@ const (
 // Error messages.
 const (
 	// initInternalPassword
-	readInternalPasswordErr     = "could not generate internal password: %+v"
+	readInternalPasswordErr     = "could not generate"
 	internalPasswordNumBytesErr = "expected %d bytes for internal password, found %d bytes"
 
 	// getInternalPassword
@@ -153,6 +156,9 @@ func getOrInit(externalPassword string) ([]byte, error) {
 // changeExternalPassword is the private function for ChangeExternalPassword
 // that is used for testing.
 func changeExternalPassword(oldExternalPassword, newExternalPassword string) error {
+	// NOTE: the following no longer works in synchronized environments, so
+	// disabled in produciton.
+	jww.FATAL.Panicf("cannot change password, unimplemented")
 	localStorage := storage.GetLocalStorage()
 	internalPassword, err := getInternalPassword(
 		oldExternalPassword, localStorage)
@@ -193,14 +199,22 @@ func initInternalPassword(externalPassword string,
 	params argonParams) ([]byte, error) {
 	internalPassword := make([]byte, internalPasswordLen)
 
+	// FIXME: The internal password is now just an expansion of
+	// the users password text. We couldn't preserve the following
+	// when doing cross-device sync.
+	h := hash.CMixHash.New()
+	h.Write([]byte(externalPassword))
+	h.Write(internalPassword)
+	copy(internalPassword, h.Sum(nil)[:internalPasswordLen])
+
 	// Generate internal password
-	n, err := csprng.Read(internalPassword)
-	if err != nil {
-		return nil, errors.Errorf(readInternalPasswordErr, err)
-	} else if n != internalPasswordLen {
-		return nil, errors.Errorf(
-			internalPasswordNumBytesErr, internalPasswordLen, n)
-	}
+	// n, err := csprng.Read(internalPassword)
+	// if err != nil {
+	// 	return nil, errors.Errorf(readInternalPasswordErr, err)
+	// } else if n != internalPasswordLen {
+	// 	return nil, errors.Errorf(
+	// 		internalPasswordNumBytesErr, internalPasswordLen, n)
+	// }
 
 	// Generate and store salt
 	salt, err := makeSalt(csprng)
diff --git a/storage/password_test.go b/storage/password_test.go
index 0e4c4c64105e93247cf24d1665360fdbd834a687..fecd785c51c7e919ab511e2d8d5821dd6f4b37f5 100644
--- a/storage/password_test.go
+++ b/storage/password_test.go
@@ -44,37 +44,37 @@ func Test_getOrInit(t *testing.T) {
 
 // Tests that changeExternalPassword correctly changes the password and updates
 // the encryption.
-func Test_changeExternalPassword(t *testing.T) {
-	oldExternalPassword := "myPassword"
-	newExternalPassword := "hunter2"
-	oldInternalPassword, err := getOrInit(oldExternalPassword)
-	if err != nil {
-		t.Errorf("%+v", err)
-	}
-
-	err = changeExternalPassword(oldExternalPassword, newExternalPassword)
-	if err != nil {
-		t.Errorf("%+v", err)
-	}
-
-	newInternalPassword, err := getOrInit(newExternalPassword)
-	if err != nil {
-		t.Errorf("%+v", err)
-	}
-
-	if !bytes.Equal(oldInternalPassword, newInternalPassword) {
-		t.Errorf("Internal password was not changed in storage. Old and new "+
-			"should be different.\nold: %+v\nnew: %+v",
-			oldInternalPassword, newInternalPassword)
-	}
-
-	_, err = getOrInit(oldExternalPassword)
-	expectedErr := strings.Split(decryptWithPasswordErr, "%")[0]
-	if err == nil || !strings.Contains(err.Error(), expectedErr) {
-		t.Errorf("Unexpected error when trying to get internal password with "+
-			"old external password.\nexpected: %s\nreceived: %+v", expectedErr, err)
-	}
-}
+// func Test_changeExternalPassword(t *testing.T) {
+// 	oldExternalPassword := "myPassword"
+// 	newExternalPassword := "hunter2"
+// 	oldInternalPassword, err := getOrInit(oldExternalPassword)
+// 	if err != nil {
+// 		t.Errorf("%+v", err)
+// 	}
+
+// 	err = changeExternalPassword(oldExternalPassword, newExternalPassword)
+// 	if err != nil {
+// 		t.Errorf("%+v", err)
+// 	}
+
+// 	newInternalPassword, err := getOrInit(newExternalPassword)
+// 	if err != nil {
+// 		t.Errorf("%+v", err)
+// 	}
+
+// 	if !bytes.Equal(oldInternalPassword, newInternalPassword) {
+// 		t.Errorf("Internal password was not changed in storage. Old and new "+
+// 			"should be different.\nold: %+v\nnew: %+v",
+// 			oldInternalPassword, newInternalPassword)
+// 	}
+
+// 	_, err = getOrInit(oldExternalPassword)
+// 	expectedErr := strings.Split(decryptWithPasswordErr, "%")[0]
+// 	if err == nil || !strings.Contains(err.Error(), expectedErr) {
+// 		t.Errorf("Unexpected error when trying to get internal password with "+
+// 			"old external password.\nexpected: %s\nreceived: %+v", expectedErr, err)
+// 	}
+// }
 
 // Tests that verifyPassword returns true for a valid password and false for an
 // invalid password
@@ -154,20 +154,20 @@ func Test_initInternalPassword_CsprngReadError(t *testing.T) {
 
 // Tests that initInternalPassword returns  an error when the RNG does not
 // return enough bytes.
-func Test_initInternalPassword_CsprngReadNumBytesError(t *testing.T) {
-	externalPassword := "myPassword"
-	ls := storage.GetLocalStorage()
-	b := bytes.NewBuffer(make([]byte, internalPasswordLen/2))
-
-	expectedErr := fmt.Sprintf(
-		internalPasswordNumBytesErr, internalPasswordLen, internalPasswordLen/2)
-
-	_, err := initInternalPassword(externalPassword, ls, b, defaultParams())
-	if err == nil || !strings.Contains(err.Error(), expectedErr) {
-		t.Errorf("Unexpected error when RNG does not return enough bytes."+
-			"\nexpected: %s\nreceived: %+v", expectedErr, err)
-	}
-}
+// func Test_initInternalPassword_CsprngReadNumBytesError(t *testing.T) {
+// 	externalPassword := "myPassword"
+// 	ls := storage.GetLocalStorage()
+// 	b := bytes.NewBuffer(make([]byte, internalPasswordLen/2))
+
+// 	expectedErr := fmt.Sprintf(
+// 		internalPasswordNumBytesErr, internalPasswordLen, internalPasswordLen/2)
+
+// 	_, err := initInternalPassword(externalPassword, ls, b, defaultParams())
+// 	if err == nil || !strings.Contains(err.Error(), expectedErr) {
+// 		t.Errorf("Unexpected error when RNG does not return enough bytes."+
+// 			"\nexpected: %s\nreceived: %+v", expectedErr, err)
+// 	}
+// }
 
 // Tests that getInternalPassword returns the internal password that is saved
 // to local storage by initInternalPassword.
diff --git a/wasm/channels.go b/wasm/channels.go
index 13562a200cef571cad1f3d881ee172e6c5f6d399..b3535c9c8be609ab9cf992c8de440797d3ee4723 100644
--- a/wasm/channels.go
+++ b/wasm/channels.go
@@ -369,9 +369,9 @@ func LoadChannelsManager(_ js.Value, args []js.Value) any {
 //     [bindings.ChannelUICallbacks]. It is a callback that informs the UI about
 //     various events. The entire interface can be nil, but if defined, each
 //     method must be implemented.
-//   - args[6] - ID of [ChannelDbCipher] object in tracker (int). Create this
-//     object with [NewChannelsDatabaseCipher] and get its id with
-//     [ChannelDbCipher.GetID].
+//   - args[6] - ID of [DbCipher] object in tracker (int). Create this
+//     object with [NewDatabaseCipher] and get its id with
+//     [DbCipher.GetID].
 //
 // Returns a promise:
 //   - Resolves to a Javascript representation of the [ChannelsManager] object.
@@ -386,7 +386,7 @@ func NewChannelsManagerWithIndexedDb(_ js.Value, args []js.Value) any {
 	cUI := newChannelUI(args[5])
 	cipherID := args[6].Int()
 
-	cipher, err := bindings.GetChannelDbCipherTrackerFromID(cipherID)
+	cipher, err := dbCipherTrackerSingleton.get(cipherID)
 	if err != nil {
 		exception.ThrowTrace(err)
 	}
@@ -444,10 +444,10 @@ func NewChannelsManagerWithIndexedDbUnsafe(_ js.Value, args []js.Value) any {
 func newChannelsManagerWithIndexedDb(cmixID int, wasmJsPath string,
 	privateIdentity, extensionBuilderIDsJSON []byte, notificationsID int,
 	channelsCbs bindings.ChannelUICallbacks,
-	cipher *bindings.ChannelDbCipher) any {
+	cipher *DbCipher) any {
 
 	model := channelsDb.NewWASMEventModelBuilder(
-		wasmJsPath, cipher, channelsCbs)
+		wasmJsPath, cipher.api, channelsCbs)
 
 	promiseFn := func(resolve, reject func(args ...any) js.Value) {
 		cm, err := bindings.NewChannelsManagerGoEventModel(cmixID,
@@ -487,9 +487,9 @@ func newChannelsManagerWithIndexedDb(cmixID int, wasmJsPath string,
 //     [bindings.ChannelUICallbacks]. It is a callback that informs the UI about
 //     various events. The entire interface can be nil, but if defined, each
 //     method must be implemented.
-//   - args[6] - ID of [ChannelDbCipher] object in tracker (int). Create this
-//     object with [NewChannelsDatabaseCipher] and get its id with
-//     [ChannelDbCipher.GetID].
+//   - args[6] - ID of [DbCipher] object in tracker (int). Create this
+//     object with [NewDatabaseCipher] and get its id with
+//     [DbCipher.GetID].
 //
 // Returns a promise:
 //   - Resolves to a Javascript representation of the [ChannelsManager] object.
@@ -504,7 +504,7 @@ func LoadChannelsManagerWithIndexedDb(_ js.Value, args []js.Value) any {
 	channelsCbs := newChannelUI(args[5])
 	cipherID := args[6].Int()
 
-	cipher, err := bindings.GetChannelDbCipherTrackerFromID(cipherID)
+	cipher, err := dbCipherTrackerSingleton.get(cipherID)
 	if err != nil {
 		exception.ThrowTrace(err)
 	}
@@ -557,10 +557,10 @@ func LoadChannelsManagerWithIndexedDbUnsafe(_ js.Value, args []js.Value) any {
 
 func loadChannelsManagerWithIndexedDb(cmixID int, wasmJsPath, storageTag string,
 	extensionBuilderIDsJSON []byte, notificationsID int, channelsCbs bindings.ChannelUICallbacks,
-	cipher *bindings.ChannelDbCipher) any {
+	cipher *DbCipher) any {
 
 	model := channelsDb.NewWASMEventModelBuilder(
-		wasmJsPath, cipher, channelsCbs)
+		wasmJsPath, cipher.api, channelsCbs)
 
 	promiseFn := func(resolve, reject func(args ...any) js.Value) {
 		cm, err := bindings.LoadChannelsManagerGoEventModel(
@@ -2315,145 +2315,6 @@ type MessageAndError struct {
 	Error string
 }
 
-////////////////////////////////////////////////////////////////////////////////
-// Channel Cipher                                                             //
-////////////////////////////////////////////////////////////////////////////////
-
-// ChannelDbCipher wraps the [bindings.ChannelDbCipher] object so its methods
-// can be wrapped to be Javascript compatible.
-type ChannelDbCipher struct {
-	api *bindings.ChannelDbCipher
-}
-
-// newChannelDbCipherJS creates a new Javascript compatible object
-// (map[string]any) that matches the [ChannelDbCipher] structure.
-func newChannelDbCipherJS(api *bindings.ChannelDbCipher) map[string]any {
-	c := ChannelDbCipher{api}
-	channelDbCipherMap := map[string]any{
-		"GetID":         js.FuncOf(c.GetID),
-		"Encrypt":       js.FuncOf(c.Encrypt),
-		"Decrypt":       js.FuncOf(c.Decrypt),
-		"MarshalJSON":   js.FuncOf(c.MarshalJSON),
-		"UnmarshalJSON": js.FuncOf(c.UnmarshalJSON),
-	}
-
-	return channelDbCipherMap
-}
-
-// NewChannelsDatabaseCipher constructs a [ChannelDbCipher] object.
-//
-// Parameters:
-//   - args[0] - The tracked [Cmix] object ID (int).
-//   - args[1] - The password for storage. This should be the same password
-//     passed into [NewCmix] (Uint8Array).
-//   - args[2] - The maximum size of a payload to be encrypted. A payload passed
-//     into [ChannelDbCipher.Encrypt] that is larger than this value will result
-//     in an error (int).
-//
-// Returns:
-//   - JavaScript representation of the [ChannelDbCipher] object.
-//   - Throws an error if creating the cipher fails.
-func NewChannelsDatabaseCipher(_ js.Value, args []js.Value) any {
-	cmixId := args[0].Int()
-	password := utils.CopyBytesToGo(args[1])
-	plaintTextBlockSize := args[2].Int()
-
-	cipher, err := bindings.NewChannelsDatabaseCipher(
-		cmixId, password, plaintTextBlockSize)
-	if err != nil {
-		exception.ThrowTrace(err)
-		return nil
-	}
-
-	return newChannelDbCipherJS(cipher)
-}
-
-// GetID returns the ID for this [bindings.ChannelDbCipher] in the
-// channelDbCipherTracker.
-//
-// Returns:
-//   - Tracker ID (int).
-func (c *ChannelDbCipher) GetID(js.Value, []js.Value) any {
-	return c.api.GetID()
-}
-
-// Encrypt will encrypt the raw data. It will return a ciphertext. Padding is
-// done on the plaintext so all encrypted data looks uniform at rest.
-//
-// Parameters:
-//   - args[0] - The data to be encrypted (Uint8Array). This must be smaller
-//     than the block size passed into [NewChannelsDatabaseCipher]. If it is
-//     larger, this will return an error.
-//
-// Returns:
-//   - The ciphertext of the plaintext passed in (Uint8Array).
-//   - Throws an error if it fails to encrypt the plaintext.
-func (c *ChannelDbCipher) Encrypt(_ js.Value, args []js.Value) any {
-	ciphertext, err := c.api.Encrypt(utils.CopyBytesToGo(args[0]))
-	if err != nil {
-		exception.ThrowTrace(err)
-		return nil
-	}
-
-	return utils.CopyBytesToJS(ciphertext)
-}
-
-// Decrypt will decrypt the passed in encrypted value. The plaintext will be
-// returned by this function. Any padding will be discarded within this
-// function.
-//
-// Parameters:
-//   - args[0] - the encrypted data returned by [ChannelDbCipher.Encrypt]
-//     (Uint8Array).
-//
-// Returns:
-//   - The plaintext of the ciphertext passed in (Uint8Array).
-//   - Throws an error if it fails to encrypt the plaintext.
-func (c *ChannelDbCipher) Decrypt(_ js.Value, args []js.Value) any {
-	plaintext, err := c.api.Decrypt(utils.CopyBytesToGo(args[0]))
-	if err != nil {
-		exception.ThrowTrace(err)
-		return nil
-	}
-
-	return utils.CopyBytesToJS(plaintext)
-}
-
-// MarshalJSON marshals the cipher into valid JSON.
-//
-// Returns:
-//   - JSON of the cipher (Uint8Array).
-//   - Throws an error if marshalling fails.
-func (c *ChannelDbCipher) MarshalJSON(js.Value, []js.Value) any {
-	data, err := c.api.MarshalJSON()
-	if err != nil {
-		exception.ThrowTrace(err)
-		return nil
-	}
-
-	return utils.CopyBytesToJS(data)
-}
-
-// UnmarshalJSON unmarshalls JSON into the cipher.
-//
-// Note that this function does not transfer the internal RNG. Use
-// [channel.NewCipherFromJSON] to properly reconstruct a cipher from JSON.
-//
-// Parameters:
-//   - args[0] - JSON data to unmarshal (Uint8Array).
-//
-// Returns:
-//   - JSON of the cipher (Uint8Array).
-//   - Throws an error if marshalling fails.
-func (c *ChannelDbCipher) UnmarshalJSON(_ js.Value, args []js.Value) any {
-	err := c.api.UnmarshalJSON(utils.CopyBytesToGo(args[0]))
-	if err != nil {
-		exception.ThrowTrace(err)
-		return nil
-	}
-	return nil
-}
-
 // newChannelUI maps the methods on the Javascript object to the
 // channelUI callbacks implementation struct.
 func newChannelUI(cbImpl js.Value) *channelUI {
diff --git a/wasm/channels_test.go b/wasm/channels_test.go
index 1b4d9eb555177951ea66e9fb81ed7420418f2b22..9ebbc691debd6d330a9cadc3a4aa4c9c62d342ad 100644
--- a/wasm/channels_test.go
+++ b/wasm/channels_test.go
@@ -60,47 +60,6 @@ func Test_ChannelsManagerMethods(t *testing.T) {
 	}
 }
 
-// Tests that the map representing ChannelDbCipher returned by
-// newChannelDbCipherJS contains all of the methods on ChannelDbCipher.
-func Test_newChannelDbCipherJS(t *testing.T) {
-	cipherType := reflect.TypeOf(&ChannelDbCipher{})
-
-	cipher := newChannelDbCipherJS(&bindings.ChannelDbCipher{})
-	if len(cipher) != cipherType.NumMethod() {
-		t.Errorf("ChannelDbCipher JS object does not have all methods."+
-			"\nexpected: %d\nreceived: %d", cipherType.NumMethod(), len(cipher))
-	}
-
-	for i := 0; i < cipherType.NumMethod(); i++ {
-		method := cipherType.Method(i)
-
-		if _, exists := cipher[method.Name]; !exists {
-			t.Errorf("Method %s does not exist.", method.Name)
-		}
-	}
-}
-
-// Tests that ChannelDbCipher has all the methods that
-// [bindings.ChannelDbCipher] has.
-func Test_ChannelDbCipherMethods(t *testing.T) {
-	cipherType := reflect.TypeOf(&ChannelDbCipher{})
-	binCipherType := reflect.TypeOf(&bindings.ChannelDbCipher{})
-
-	if binCipherType.NumMethod() != cipherType.NumMethod() {
-		t.Errorf("WASM ChannelDbCipher object does not have all methods from "+
-			"bindings.\nexpected: %d\nreceived: %d",
-			binCipherType.NumMethod(), cipherType.NumMethod())
-	}
-
-	for i := 0; i < binCipherType.NumMethod(); i++ {
-		method := binCipherType.Method(i)
-
-		if _, exists := cipherType.MethodByName(method.Name); !exists {
-			t.Errorf("Method %s does not exist.", method.Name)
-		}
-	}
-}
-
 type jsIdentity struct {
 	pubKey  js.Value
 	codeset js.Value
diff --git a/wasm/cipher.go b/wasm/cipher.go
new file mode 100644
index 0000000000000000000000000000000000000000..5504d2f3a426c78640ff1611e025103e91fb876e
--- /dev/null
+++ b/wasm/cipher.go
@@ -0,0 +1,232 @@
+////////////////////////////////////////////////////////////////////////////////
+// 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 wasm
+
+import (
+	"github.com/pkg/errors"
+	"gitlab.com/elixxir/client/v4/bindings"
+	"gitlab.com/elixxir/client/v4/storage/utility"
+	"gitlab.com/elixxir/crypto/indexedDb"
+	"gitlab.com/elixxir/wasm-utils/exception"
+	"gitlab.com/elixxir/wasm-utils/utils"
+	"sync"
+	"syscall/js"
+)
+
+// dbCipherTrackerSingleton is used to track DbCipher objects
+// so that they can be referenced by ID back over the bindings.
+var dbCipherTrackerSingleton = &DbCipherTracker{
+	tracked: make(map[int]*DbCipher),
+	count:   0,
+}
+
+// DbCipherTracker is a singleton used to keep track of extant
+// DbCipher objects, preventing race conditions created by passing it
+// over the bindings.
+type DbCipherTracker struct {
+	tracked map[int]*DbCipher
+	count   int
+	mux     sync.RWMutex
+}
+
+// create creates a DbCipher from a [indexedDb.Cipher], assigns it a unique
+// ID, and adds it to the DbCipherTracker.
+func (ct *DbCipherTracker) create(c indexedDb.Cipher) *DbCipher {
+	ct.mux.Lock()
+	defer ct.mux.Unlock()
+
+	chID := ct.count
+	ct.count++
+
+	ct.tracked[chID] = &DbCipher{
+		api: c,
+		id:  chID,
+	}
+
+	return ct.tracked[chID]
+}
+
+// get an DbCipher from the DbCipherTracker given its ID.
+func (ct *DbCipherTracker) get(id int) (*DbCipher, error) {
+	ct.mux.RLock()
+	defer ct.mux.RUnlock()
+
+	c, exist := ct.tracked[id]
+	if !exist {
+		return nil, errors.Errorf(
+			"Cannot get DbCipher for ID %d, does not exist", id)
+	}
+
+	return c, nil
+}
+
+// delete removes a DbCipherTracker from the DbCipherTracker.
+func (ct *DbCipherTracker) delete(id int) {
+	ct.mux.Lock()
+	defer ct.mux.Unlock()
+
+	delete(ct.tracked, id)
+}
+
+// DbCipher wraps the [indexedDb.Cipher] object so its methods
+// can be wrapped to be Javascript compatible.
+type DbCipher struct {
+	api  indexedDb.Cipher
+	salt []byte
+	id   int
+}
+
+// newDbCipherJS creates a new Javascript compatible object
+// (map[string]any) that matches the [DbCipher] structure.
+func newDbCipherJS(c *DbCipher) map[string]any {
+	DbCipherMap := map[string]any{
+		"GetID":         js.FuncOf(c.GetID),
+		"Encrypt":       js.FuncOf(c.Encrypt),
+		"Decrypt":       js.FuncOf(c.Decrypt),
+		"MarshalJSON":   js.FuncOf(c.MarshalJSON),
+		"UnmarshalJSON": js.FuncOf(c.UnmarshalJSON),
+	}
+
+	return DbCipherMap
+}
+
+// NewDatabaseCipher constructs a [DbCipher] object.
+//
+// Parameters:
+//   - args[0] - The tracked [Cmix] object ID (int).
+//   - args[1] - The password for storage. This should be the same password
+//     passed into [NewCmix] (Uint8Array).
+//   - args[2] - The maximum size of a payload to be encrypted. A payload passed
+//     into [DbCipher.Encrypt] that is larger than this value will result
+//     in an error (int).
+//
+// Returns:
+//   - JavaScript representation of the [DbCipher] object.
+//   - Throws an error if creating the cipher fails.
+func NewDatabaseCipher(_ js.Value, args []js.Value) any {
+	cmixId := args[0].Int()
+	password := utils.CopyBytesToGo(args[1])
+	plaintTextBlockSize := args[2].Int()
+
+	// Get user from singleton
+	user, err := bindings.GetCMixInstance(cmixId)
+	if err != nil {
+		exception.ThrowTrace(err)
+		return nil
+	}
+
+	// Generate RNG
+	stream := user.GetRng().GetStream()
+
+	// Load or generate a salt
+	salt, err := utility.NewOrLoadSalt(
+		user.GetStorage().GetKV(), stream)
+	if err != nil {
+		exception.ThrowTrace(err)
+		return nil
+	}
+
+	// Construct a cipher
+	c, err := indexedDb.NewCipher(
+		password, salt, plaintTextBlockSize, stream)
+	if err != nil {
+		exception.ThrowTrace(err)
+		return nil
+	}
+
+	// Add to singleton and return
+	return newDbCipherJS(dbCipherTrackerSingleton.create(c))
+}
+
+// GetID returns the ID for this [DbCipher] in the
+// DbCipherTracker.
+//
+// Returns:
+//   - Tracker ID (int).
+func (c *DbCipher) GetID(js.Value, []js.Value) any {
+	return c.id
+}
+
+// Encrypt will encrypt the raw data. It will return a ciphertext. Padding is
+// done on the plaintext so all encrypted data looks uniform at rest.
+//
+// Parameters:
+//   - args[0] - The data to be encrypted (Uint8Array). This must be smaller
+//     than the block size passed into [NewDatabaseCipher]. If it is
+//     larger, this will return an error.
+//
+// Returns:
+//   - The ciphertext of the plaintext passed in (String).
+//   - Throws an error if it fails to encrypt the plaintext.
+func (c *DbCipher) Encrypt(_ js.Value, args []js.Value) any {
+	ciphertext, err := c.api.Encrypt(utils.CopyBytesToGo(args[0]))
+	if err != nil {
+		exception.ThrowTrace(err)
+		return nil
+	}
+
+	return ciphertext
+}
+
+// Decrypt will decrypt the passed in encrypted value. The plaintext will be
+// returned by this function. Any padding will be discarded within this
+// function.
+//
+// Parameters:
+//   - args[0] - the encrypted data returned by [DbCipher.Encrypt]
+//     (String).
+//
+// Returns:
+//   - The plaintext of the ciphertext passed in (Uint8Array).
+//   - Throws an error if it fails to encrypt the plaintext.
+func (c *DbCipher) Decrypt(_ js.Value, args []js.Value) any {
+	plaintext, err := c.api.Decrypt(args[0].String())
+	if err != nil {
+		exception.ThrowTrace(err)
+		return nil
+	}
+
+	return utils.CopyBytesToJS(plaintext)
+}
+
+// MarshalJSON marshals the cipher into valid JSON.
+//
+// Returns:
+//   - JSON of the cipher (Uint8Array).
+//   - Throws an error if marshalling fails.
+func (c *DbCipher) MarshalJSON(js.Value, []js.Value) any {
+	data, err := c.api.MarshalJSON()
+	if err != nil {
+		exception.ThrowTrace(err)
+		return nil
+	}
+
+	return utils.CopyBytesToJS(data)
+}
+
+// UnmarshalJSON unmarshalls JSON into the cipher.
+//
+// Note that this function does not transfer the internal RNG. Use
+// [indexedDb.NewCipherFromJSON] to properly reconstruct a cipher from JSON.
+//
+// Parameters:
+//   - args[0] - JSON data to unmarshal (Uint8Array).
+//
+// Returns:
+//   - JSON of the cipher (Uint8Array).
+//   - Throws an error if marshalling fails.
+func (c *DbCipher) UnmarshalJSON(_ js.Value, args []js.Value) any {
+	err := c.api.UnmarshalJSON(utils.CopyBytesToGo(args[0]))
+	if err != nil {
+		exception.ThrowTrace(err)
+		return nil
+	}
+	return nil
+}
diff --git a/wasm/cipher_test.go b/wasm/cipher_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..0f83461509bc0f70ff5afd7104de9ab5fb3e6e11
--- /dev/null
+++ b/wasm/cipher_test.go
@@ -0,0 +1,35 @@
+////////////////////////////////////////////////////////////////////////////////
+// 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 wasm
+
+import (
+	"reflect"
+	"testing"
+)
+
+// Tests that the map representing DbCipher returned by
+// newDbCipherJS contains all the methods on DbCipher.
+func Test_newChannelDbCipherJS(t *testing.T) {
+	cipherType := reflect.TypeOf(&DbCipher{})
+
+	cipher := newDbCipherJS(&DbCipher{})
+	if len(cipher) != cipherType.NumMethod() {
+		t.Errorf("DbCipher JS object does not have all methods."+
+			"\nexpected: %d\nreceived: %d", cipherType.NumMethod(), len(cipher))
+	}
+
+	for i := 0; i < cipherType.NumMethod(); i++ {
+		method := cipherType.Method(i)
+
+		if _, exists := cipher[method.Name]; !exists {
+			t.Errorf("Method %s does not exist.", method.Name)
+		}
+	}
+}
diff --git a/wasm/collective.go b/wasm/collective.go
index 5bd0fa145fd214752475bc1eaa9601dc881297e5..5c2eb771502e3206210cfc0dca78b3743f7b3fc0 100644
--- a/wasm/collective.go
+++ b/wasm/collective.go
@@ -516,13 +516,12 @@ func (rsCB *RemoteStore) Write(path string, data []byte) error {
 // Returns:
 //   - JSON of [bindings.RemoteStoreReport] (Uint8Array).
 //   - Catches any thrown errors (of type Error) and returns it as an error.
-func (rsCB *RemoteStore) GetLastModified(path string) ([]byte, error) {
-	fn := func() js.Value { return rsCB.getLastModified(path) }
-	v, err := exception.RunAndCatch(fn)
+func (rsCB *RemoteStore) GetLastModified(path string) (string, error) {
+	v, err := utils.Await(rsCB.getLastModified(path))
 	if err != nil {
-		return nil, err
+		return "", js.Error{Value: err[0]}
 	}
-	return utils.CopyBytesToGo(v), err
+	return v[0].String(), nil
 }
 
 // GetLastWrite implements [bindings.RemoteStore.GetLastWrite()
@@ -530,13 +529,12 @@ func (rsCB *RemoteStore) GetLastModified(path string) ([]byte, error) {
 // Returns:
 //   - JSON of [bindings.RemoteStoreReport] (Uint8Array).
 //   - Catches any thrown errors (of type Error) and returns it as an error.
-func (rsCB *RemoteStore) GetLastWrite() ([]byte, error) {
-	fn := func() js.Value { return rsCB.getLastWrite() }
-	v, err := exception.RunAndCatch(fn)
+func (rsCB *RemoteStore) GetLastWrite() (string, error) {
+	v, err := utils.Await(rsCB.getLastWrite())
 	if err != nil {
-		return nil, err
+		return "", js.Error{Value: err[0]}
 	}
-	return utils.CopyBytesToGo(v), err
+	return v[0].String(), nil
 }
 
 // ReadDir implements [bindings.RemoteStore.ReadDir]
diff --git a/wasm/dm.go b/wasm/dm.go
index 53d123a26c3f92743f9868e03017e7c1858ca9bb..3786b0df32a68b9b0b70c09a9d5664263df3792f 100644
--- a/wasm/dm.go
+++ b/wasm/dm.go
@@ -128,8 +128,8 @@ func NewDMClient(_ js.Value, args []js.Value) any {
 //     The row in the database that was updated can be found using the UUID.
 //     messageUpdate is true if the message already exists and was edited.
 //     conversationUpdate is true if the Conversation was created or modified.
-//   - args[4] - ID of [DMDbCipher] object in tracker (int). Create this object
-//     with [NewDMsDatabaseCipher] and get its id with [DMDbCipher.GetID].
+//   - args[4] - ID of [DbCipher] object in tracker (int). Create this object
+//     with [NewDatabaseCipher] and get its id with [DbCipher.GetID].
 //
 // Returns:
 //   - Resolves to a Javascript representation of the [DMClient] object.
@@ -142,7 +142,7 @@ func NewDMClientWithIndexedDb(_ js.Value, args []js.Value) any {
 	messageReceivedCB := args[3]
 	cipherID := args[4].Int()
 
-	cipher, err := bindings.GetDMDbCipherTrackerFromID(cipherID)
+	cipher, err := dbCipherTrackerSingleton.get(cipherID)
 	if err != nil {
 		exception.ThrowTrace(err)
 	}
@@ -190,7 +190,7 @@ func NewDMClientWithIndexedDbUnsafe(_ js.Value, args []js.Value) any {
 }
 
 func newDMClientWithIndexedDb(cmixID int, wasmJsPath string,
-	privateIdentity []byte, cb js.Value, cipher *bindings.DMDbCipher) any {
+	privateIdentity []byte, cb js.Value, cipher *DbCipher) any {
 
 	messageReceivedCB := func(uuid uint64, pubKey ed25519.PublicKey,
 		messageUpdate, conversationUpdate bool) {
@@ -206,7 +206,7 @@ func newDMClientWithIndexedDb(cmixID int, wasmJsPath string,
 		}
 		dmPath := base64.RawStdEncoding.EncodeToString(pi.PubKey[:])
 		model, err := indexDB.NewWASMEventModel(
-			dmPath, wasmJsPath, cipher, messageReceivedCB)
+			dmPath, wasmJsPath, cipher.api, messageReceivedCB)
 		if err != nil {
 			reject(exception.NewTrace(err))
 		}
@@ -671,35 +671,6 @@ func DecodeDMShareURL(_ js.Value, args []js.Value) any {
 	return utils.CopyBytesToJS(report)
 }
 
-////////////////////////////////////////////////////////////////////////////////
-// Channel Receiving Logic and Callback Registration                          //
-////////////////////////////////////////////////////////////////////////////////
-
-// channelMessageReceptionCallback wraps Javascript callbacks to adhere to the
-// [bindings.ChannelMessageReceptionCallback] interface.
-type dmReceptionCallback struct {
-	callback func(args ...any) js.Value
-}
-
-// Callback returns the context for a channel message.
-//
-// Parameters:
-//   - receivedChannelMessageReport - Returns the JSON of
-//     [bindings.ReceivedChannelMessageReport] (Uint8Array).
-//   - err - Returns an error on failure (Error).
-//
-// Returns:
-//   - It must return a unique UUID for the message that it can be referenced by
-//     later (int).
-func (cmrCB *dmReceptionCallback) Callback(
-	receivedChannelMessageReport []byte, err error) int {
-	uuid := cmrCB.callback(
-		utils.CopyBytesToJS(receivedChannelMessageReport),
-		exception.NewTrace(err))
-
-	return uuid.Int()
-}
-
 ////////////////////////////////////////////////////////////////////////////////
 // Event Model Logic                                                          //
 ////////////////////////////////////////////////////////////////////////////////
@@ -990,146 +961,6 @@ func (em *dmReceiver) GetConversations() []byte {
 	return conversationsBytes
 }
 
-////////////////////////////////////////////////////////////////////////////////
-// DM DB Cipher                                                               //
-////////////////////////////////////////////////////////////////////////////////
-
-// DMDbCipher wraps the [bindings.DMDbCipher] object so its methods
-// can be wrapped to be Javascript compatible.
-type DMDbCipher struct {
-	api *bindings.DMDbCipher
-}
-
-// newDMDbCipherJS creates a new Javascript compatible object
-// (map[string]any) that matches the [DMDbCipher] structure.
-func newDMDbCipherJS(api *bindings.DMDbCipher) map[string]any {
-	c := DMDbCipher{api}
-	channelDbCipherMap := map[string]any{
-		"GetID":         js.FuncOf(c.GetID),
-		"Encrypt":       js.FuncOf(c.Encrypt),
-		"Decrypt":       js.FuncOf(c.Decrypt),
-		"MarshalJSON":   js.FuncOf(c.MarshalJSON),
-		"UnmarshalJSON": js.FuncOf(c.UnmarshalJSON),
-	}
-
-	return channelDbCipherMap
-}
-
-// NewDMsDatabaseCipher constructs a [DMDbCipher] object.
-//
-// Parameters:
-//   - args[0] - The tracked [Cmix] object ID (int).
-//   - args[1] - The password for storage. This should be the same password
-//     passed into [NewCmix] (Uint8Array).
-//   - args[2] - The maximum size of a payload to be encrypted. A payload passed
-//     into [DMDbCipher.Encrypt] that is larger than this value will result
-//     in an error (int).
-//
-// Returns:
-//   - JavaScript representation of the [DMDbCipher] object.
-//   - Throws an error if creating the cipher fails.
-func NewDMsDatabaseCipher(_ js.Value, args []js.Value) any {
-	cmixId := args[0].Int()
-	password := utils.CopyBytesToGo(args[1])
-	plaintTextBlockSize := args[2].Int()
-
-	cipher, err := bindings.NewDMsDatabaseCipher(
-		cmixId, password, plaintTextBlockSize)
-	if err != nil {
-		exception.ThrowTrace(err)
-		return nil
-	}
-
-	return newDMDbCipherJS(cipher)
-}
-
-// GetID returns the ID for this [bindings.DMDbCipher] in the
-// channelDbCipherTracker.
-//
-// Returns:
-//   - Tracker ID (int).
-func (c *DMDbCipher) GetID(js.Value, []js.Value) any {
-	return c.api.GetID()
-}
-
-// Encrypt will encrypt the raw data. It will return a ciphertext. Padding is
-// done on the plaintext so all encrypted data looks uniform at rest.
-//
-// Parameters:
-//   - args[0] - The data to be encrypted (Uint8Array). This must be smaller
-//     than the block size passed into [NewDMsDatabaseCipher]. If it is
-//     larger, this will return an error.
-//
-// Returns:
-//   - The ciphertext of the plaintext passed in (Uint8Array).
-//   - Throws an error if it fails to encrypt the plaintext.
-func (c *DMDbCipher) Encrypt(_ js.Value, args []js.Value) any {
-	ciphertext, err := c.api.Encrypt(utils.CopyBytesToGo(args[0]))
-	if err != nil {
-		exception.ThrowTrace(err)
-		return nil
-	}
-
-	return utils.CopyBytesToJS(ciphertext)
-}
-
-// Decrypt will decrypt the passed in encrypted value. The plaintext will be
-// returned by this function. Any padding will be discarded within this
-// function.
-//
-// Parameters:
-//   - args[0] - the encrypted data returned by [DMDbCipher.Encrypt]
-//     (Uint8Array).
-//
-// Returns:
-//   - The plaintext of the ciphertext passed in (Uint8Array).
-//   - Throws an error if it fails to encrypt the plaintext.
-func (c *DMDbCipher) Decrypt(_ js.Value, args []js.Value) any {
-	plaintext, err := c.api.Decrypt(utils.CopyBytesToGo(args[0]))
-	if err != nil {
-		exception.ThrowTrace(err)
-		return nil
-	}
-
-	return utils.CopyBytesToJS(plaintext)
-}
-
-// MarshalJSON marshals the cipher into valid JSON.
-//
-// Returns:
-//   - JSON of the cipher (Uint8Array).
-//   - Throws an error if marshalling fails.
-func (c *DMDbCipher) MarshalJSON(js.Value, []js.Value) any {
-	data, err := c.api.MarshalJSON()
-	if err != nil {
-		exception.ThrowTrace(err)
-		return nil
-	}
-
-	return utils.CopyBytesToJS(data)
-}
-
-// UnmarshalJSON unmarshalls JSON into the cipher. This function adheres to the
-// json.Unmarshaler interface.
-//
-// Note that this function does not transfer the internal RNG. Use
-// [channel.NewCipherFromJSON] to properly reconstruct a cipher from JSON.
-//
-// Parameters:
-//   - args[0] - JSON data to unmarshal (Uint8Array).
-//
-// Returns:
-//   - JSON of the cipher (Uint8Array).
-//   - Throws an error if marshalling fails.
-func (c *DMDbCipher) UnmarshalJSON(_ js.Value, args []js.Value) any {
-	err := c.api.UnmarshalJSON(utils.CopyBytesToGo(args[0]))
-	if err != nil {
-		exception.ThrowTrace(err)
-		return nil
-	}
-	return nil
-}
-
 // truncate truncates the string to length n. If the string is trimmed, then
 // ellipses (...) are appended.
 func truncate(s string, n int) string {
diff --git a/wasm/dm_test.go b/wasm/dm_test.go
index 58f837ea0bb857deaede29fd9f5c37f4cd936e34..f959c9463fe793d7cac78927dcdc8eba5980d5f7 100644
--- a/wasm/dm_test.go
+++ b/wasm/dm_test.go
@@ -62,43 +62,3 @@ func Test_DMClientMethods(t *testing.T) {
 		}
 	}
 }
-
-// Tests that the map representing DMDbCipher returned by newDMDbCipherJS
-// contains all of the methods on DMDbCipher.
-func Test_newDMDbCipherJS(t *testing.T) {
-	cipherType := reflect.TypeOf(&DMDbCipher{})
-
-	cipher := newDMDbCipherJS(&bindings.DMDbCipher{})
-	if len(cipher) != cipherType.NumMethod() {
-		t.Errorf("DMDbCipher JS object does not have all methods."+
-			"\nexpected: %d\nreceived: %d", cipherType.NumMethod(), len(cipher))
-	}
-
-	for i := 0; i < cipherType.NumMethod(); i++ {
-		method := cipherType.Method(i)
-
-		if _, exists := cipher[method.Name]; !exists {
-			t.Errorf("Method %s does not exist.", method.Name)
-		}
-	}
-}
-
-// Tests that DMDbCipher has all the methods that [bindings.DMDbCipher] has.
-func Test_DMDbCipherMethods(t *testing.T) {
-	cipherType := reflect.TypeOf(&DMDbCipher{})
-	binCipherType := reflect.TypeOf(&bindings.DMDbCipher{})
-
-	if binCipherType.NumMethod() != cipherType.NumMethod() {
-		t.Errorf("WASM DMDbCipher object does not have all methods from "+
-			"bindings.\nexpected: %d\nreceived: %d",
-			binCipherType.NumMethod(), cipherType.NumMethod())
-	}
-
-	for i := 0; i < binCipherType.NumMethod(); i++ {
-		method := binCipherType.Method(i)
-
-		if _, exists := cipherType.MethodByName(method.Name); !exists {
-			t.Errorf("Method %s does not exist.", method.Name)
-		}
-	}
-}
diff --git a/wasm_test.go b/wasm_test.go
index b415ece5623aa4e235511fb1f9cb24058e02e384..1e0494dbe4f6e08ec9276aa3b984b5c245cfa7e8 100644
--- a/wasm_test.go
+++ b/wasm_test.go
@@ -42,7 +42,7 @@ func TestPublicFunctions(t *testing.T) {
 		"NewEventModel":                   {},
 		"NewChannelsManagerGoEventModel":  {},
 		"LoadChannelsManagerGoEventModel": {},
-		"GetChannelDbCipherTrackerFromID": {},
+		"GetDbCipherTrackerFromID":        {},
 
 		// Version functions were renamed to differentiate between WASM and
 		// client versions