diff --git a/Makefile b/Makefile
index 117f48a3a54be1a5a4ed6dd976d193e03adfbcdc..ef9bc2a690b2397ea8342e79aa5d5217fff7d4c7 100644
--- a/Makefile
+++ b/Makefile
@@ -14,6 +14,7 @@ build:
 update_release:
 	GOFLAGS="" go get -d gitlab.com/elixxir/client@release
 	GOFLAGS="" go get gitlab.com/elixxir/crypto@release
+	GOFLAGS="" go get gitlab.com/elixxir/primitives@release
 	GOFLAGS="" go get gitlab.com/xx_network/primitives@release
 
 update_master:
diff --git a/go.mod b/go.mod
index c7017f57c4c9508a34a02966a60e9fb2da5df669..7ad5bd5a28679e5085b85792726e4bc68b0ce78c 100644
--- a/go.mod
+++ b/go.mod
@@ -9,6 +9,7 @@ require (
 	github.com/spf13/jwalterweatherman v1.1.0
 	gitlab.com/elixxir/client v1.5.1-0.20220924203712-7d4b1dce86b8
 	gitlab.com/elixxir/crypto v0.0.7-0.20220923233816-0364f1b203c6
+	gitlab.com/elixxir/primitives v0.0.3-0.20220901220638-1acc75fabdc6
 	gitlab.com/xx_network/primitives v0.0.4-0.20220809193445-9fc0a5209548
 )
 
@@ -54,7 +55,6 @@ require (
 	gitlab.com/elixxir/bloomfilter v0.0.0-20211222005329-7d931ceead6f // indirect
 	gitlab.com/elixxir/comms v0.0.4-0.20220916185715-f1e9a5eda939 // indirect
 	gitlab.com/elixxir/ekv v0.2.1 // indirect
-	gitlab.com/elixxir/primitives v0.0.3-0.20220901220638-1acc75fabdc6 // indirect
 	gitlab.com/xx_network/comms v0.0.4-0.20220916185248-8a984b8594de // indirect
 	gitlab.com/xx_network/crypto v0.0.5-0.20220913213008-98764f5b3287 // indirect
 	gitlab.com/xx_network/ring v0.0.3-0.20220222211904-da613960ad93 // indirect
diff --git a/go.sum b/go.sum
index 05a97d5a1b6cb0dc391c88db0a90a8b37814f441..a6b844fe56cad8ffc710ab66934718a32b34ef1e 100644
--- a/go.sum
+++ b/go.sum
@@ -628,19 +628,13 @@ github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo=
 github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
 gitlab.com/elixxir/bloomfilter v0.0.0-20211222005329-7d931ceead6f h1:yXGvNBqzZwAhDYlSnxPRbgor6JWoOt1Z7s3z1O9JR40=
 gitlab.com/elixxir/bloomfilter v0.0.0-20211222005329-7d931ceead6f/go.mod h1:H6jztdm0k+wEV2QGK/KYA+MY9nj9Zzatux/qIvDDv3k=
-gitlab.com/elixxir/client v1.5.1-0.20220920212200-25ceacdcbd31 h1:+Di3jF1Vdmr+gQGYwJOSKqbpRylTzLVP81fzkdkpegw=
-gitlab.com/elixxir/client v1.5.1-0.20220920212200-25ceacdcbd31/go.mod h1:pX1uLFS8v6pNVzJEcfbMUrYPTWLPl8p71ghqW2Xm0Ns=
 gitlab.com/elixxir/client v1.5.1-0.20220924203712-7d4b1dce86b8 h1:A1x9EJOWm6YiaAJG5SPHXiwsDvik2XGSjeSylJbDBBk=
 gitlab.com/elixxir/client v1.5.1-0.20220924203712-7d4b1dce86b8/go.mod h1:z1Bdlja75CF3UrzifMC0LQwjlEdOcJCfXEX5k9AKQTQ=
-gitlab.com/elixxir/comms v0.0.4-0.20220913220502-eed192f654bd h1:2nHE7EoptSTBFjCxMeAveKT6urbguCwgg8Jx7XYEVe4=
-gitlab.com/elixxir/comms v0.0.4-0.20220913220502-eed192f654bd/go.mod h1:AO6XkMhaHJW8eXlgL5m3UUcJqsSP8F5Wm1GX+wyq/rw=
 gitlab.com/elixxir/comms v0.0.4-0.20220916185715-f1e9a5eda939 h1:+VRx2ULHKs040bBhDAOKNCZnbcXxUk3jD9JoKQzQpLk=
 gitlab.com/elixxir/comms v0.0.4-0.20220916185715-f1e9a5eda939/go.mod h1:AO6XkMhaHJW8eXlgL5m3UUcJqsSP8F5Wm1GX+wyq/rw=
 gitlab.com/elixxir/crypto v0.0.0-20200804182833-984246dea2c4/go.mod h1:ucm9SFKJo+K0N2GwRRpaNr+tKXMIOVWzmyUD0SbOu2c=
 gitlab.com/elixxir/crypto v0.0.3/go.mod h1:ZNgBOblhYToR4m8tj4cMvJ9UsJAUKq+p0gCp07WQmhA=
 gitlab.com/elixxir/crypto v0.0.7-0.20220913220142-ab0771bad0af/go.mod h1:QF8SzsrYh9Elip9EUYUDAhPjqO9DGrrrQxYHvn+VXok=
-gitlab.com/elixxir/crypto v0.0.7-0.20220920002307-5541473e9aa7 h1:9IsBtL8zcUG86XcfNUVIKcnlL5tyKlyQt1cJ5nogr1U=
-gitlab.com/elixxir/crypto v0.0.7-0.20220920002307-5541473e9aa7/go.mod h1:QF8SzsrYh9Elip9EUYUDAhPjqO9DGrrrQxYHvn+VXok=
 gitlab.com/elixxir/crypto v0.0.7-0.20220923233816-0364f1b203c6 h1:ZCMqzKB86nrs9ldIoF2ZHvcExrkXIIIeH2RlNVwZx2A=
 gitlab.com/elixxir/crypto v0.0.7-0.20220923233816-0364f1b203c6/go.mod h1:QF8SzsrYh9Elip9EUYUDAhPjqO9DGrrrQxYHvn+VXok=
 gitlab.com/elixxir/ekv v0.2.1 h1:dtwbt6KmAXG2Tik5d60iDz2fLhoFBgWwST03p7T+9Is=
@@ -654,7 +648,6 @@ gitlab.com/elixxir/primitives v0.0.3-0.20220810173935-592f34a88326/go.mod h1:9Bb
 gitlab.com/elixxir/primitives v0.0.3-0.20220901220638-1acc75fabdc6 h1:/cxxZBP5jTPDpC3zgOx9vV1ojmJyG8pYtkl3IbcewNQ=
 gitlab.com/elixxir/primitives v0.0.3-0.20220901220638-1acc75fabdc6/go.mod h1:9Bb2+u+CDSwsEU5Droo6saDAXuBDvLRjexpBhPAYxhA=
 gitlab.com/xx_network/comms v0.0.0-20200805174823-841427dd5023/go.mod h1:owEcxTRl7gsoM8c3RQ5KAm5GstxrJp5tn+6JfQ4z5Hw=
-gitlab.com/xx_network/comms v0.0.4-0.20220913215811-c4bf83b27de3 h1:7mReTvEUVoI5Qpltcmbodc/j6rdPPHDIvenY4ZmWP7o=
 gitlab.com/xx_network/comms v0.0.4-0.20220913215811-c4bf83b27de3/go.mod h1:E2QKOKyPKLRjLUwMxgZpTKueEsHDEqshfqOHJ54ttxU=
 gitlab.com/xx_network/comms v0.0.4-0.20220916185248-8a984b8594de h1:44VKuVgT6X1l+MX8/oNmYORA+pa4nkOWV8hYxi4SCzc=
 gitlab.com/xx_network/comms v0.0.4-0.20220916185248-8a984b8594de/go.mod h1:E2QKOKyPKLRjLUwMxgZpTKueEsHDEqshfqOHJ54ttxU=
diff --git a/main.go b/main.go
index 53347e1fcec7d7e8552e7c824472ee50176d391d..072ea2537c4f595b85dd1d923187239026735f9e 100644
--- a/main.go
+++ b/main.go
@@ -42,16 +42,19 @@ func main() {
 	js.Global().Set("ResumeBackup", js.FuncOf(wasm.ResumeBackup))
 
 	// wasm/channels.go
+	js.Global().Set("GenerateChannelIdentity",
+		js.FuncOf(wasm.GenerateChannelIdentity))
+	js.Global().Set("GetPublicChannelIdentity",
+		js.FuncOf(wasm.GetPublicChannelIdentity))
+	js.Global().Set("GetPublicChannelIdentityFromPrivate",
+		js.FuncOf(wasm.GetPublicChannelIdentityFromPrivate))
 	js.Global().Set("NewChannelsManager", js.FuncOf(wasm.NewChannelsManager))
 	js.Global().Set("NewChannelsManagerWithIndexedDb",
 		js.FuncOf(wasm.NewChannelsManagerWithIndexedDb))
-	js.Global().Set("NewChannelsManagerWithIndexedDbDummyNameService",
-		js.FuncOf(wasm.NewChannelsManagerWithIndexedDbDummyNameService))
-	js.Global().Set("NewChannelsManagerDummyNameService",
-		js.FuncOf(wasm.NewChannelsManagerDummyNameService))
-	js.Global().Set("GenerateChannel",
-		js.FuncOf(wasm.GenerateChannel))
+	js.Global().Set("LoadChannelsManager", js.FuncOf(wasm.LoadChannelsManager))
+	js.Global().Set("GenerateChannel", js.FuncOf(wasm.GenerateChannel))
 	js.Global().Set("GetChannelInfo", js.FuncOf(wasm.GetChannelInfo))
+	js.Global().Set("IsNicknameValid", js.FuncOf(wasm.IsNicknameValid))
 
 	// wasm/cmix.go
 	js.Global().Set("NewCmix", js.FuncOf(wasm.NewCmix))
diff --git a/wasm/channels.go b/wasm/channels.go
index a50c91cdd7a0e9015b9052d7711dcc76de77a520..e72e300f9a53c4cf27de2a6022a907598c3acbef 100644
--- a/wasm/channels.go
+++ b/wasm/channels.go
@@ -45,6 +45,11 @@ func newChannelsManagerJS(api *bindings.ChannelsManager) map[string]interface{}
 		"SendMessage":      js.FuncOf(cm.SendMessage),
 		"SendReply":        js.FuncOf(cm.SendReply),
 		"SendReaction":     js.FuncOf(cm.SendReaction),
+		"GetIdentity":      js.FuncOf(cm.GetIdentity),
+		"GetStorageTag":    js.FuncOf(cm.GetStorageTag),
+		"SetNickname":      js.FuncOf(cm.SetNickname),
+		"DeleteNickname":   js.FuncOf(cm.DeleteNickname),
+		"GetNickname":      js.FuncOf(cm.GetNickname),
 
 		// Channel Receiving Logic and Callback Registration
 		"RegisterReceiveHandler": js.FuncOf(cm.RegisterReceiveHandler),
@@ -62,50 +67,99 @@ func (ch *ChannelsManager) GetID(js.Value, []js.Value) interface{} {
 	return ch.api.GetID()
 }
 
-// NewChannelsManager constructs a [ChannelsManager].
+// GenerateChannelIdentity creates a new private channel identity
+// ([channel.PrivateIdentity]). The public component can be retrieved as JSON
+// via [GetPublicChannelIdentityFromPrivate].
 //
 // Parameters:
-//  - args[0] - ID of [E2e] object in tracker (int). This can be retrieved using
-//    [E2e.GetID].
-//  - args[1] - ID of [UserDiscovery] object in tracker (int). This can be
-//    retrieved using [UserDiscovery.GetID].
+//  - args[0] - ID of [Cmix] object in tracker (int).
 //
 // Returns:
-//  - Javascript representation of the [bindings.ChannelsManager] object.
-//  - Throws a TypeError if logging in fails.
-func NewChannelsManager(_ js.Value, args []js.Value) interface{} {
-	cm, err := bindings.NewChannelsManager(args[0].Int(), args[1].Int())
+//  - JSON of [channel.PrivateIdentity] (Uint8Array).
+//  - Throws a TypeError if generating the identity fails.
+func GenerateChannelIdentity(_ js.Value, args []js.Value) interface{} {
+	pi, err := bindings.GenerateChannelIdentity(args[0].Int())
 	if err != nil {
 		utils.Throw(utils.TypeError, err)
-		return nil
 	}
 
-	return newChannelsManagerJS(cm)
+	return utils.CopyBytesToJS(pi)
 }
 
-// NewChannelsManagerWithIndexedDb constructs a [ChannelsManager] using an
-// indexedDb backend.
+// GetPublicChannelIdentity constructs a public identity ([channel.Identity])
+// from a bytes version and returns it JSON marshaled.
 //
 // Parameters:
-//  - args[0] - ID of [E2e] object in tracker (int). This can be retrieved using
-//    [E2e.GetID].
-//  - args[1] - ID of [UserDiscovery] object in tracker (int). This can be
-//    retrieved using [UserDiscovery.GetID].
-//  - args[2] - Codename (string).
+//  - args[0] - Bytes of the public identity ([channel.Identity]) (Uint8Array).
 //
 // Returns:
-//  - Javascript representation of the [bindings.ChannelsManager] object.
-//  - Throws a TypeError if initialising indexedDb or created the new channel
-//    manager fails.
-func NewChannelsManagerWithIndexedDb(_ js.Value, args []js.Value) interface{} {
-	em, err := indexedDb.NewWasmEventModel(args[2].String())
+//  - JSON of the constructed [channel.Identity] (Uint8Array).
+//  - Throws a TypeError if unmarshalling the bytes or marshalling the identity
+//    fails.
+func GetPublicChannelIdentity(_ js.Value, args []js.Value) interface{} {
+	marshaledPublic := utils.CopyBytesToGo(args[0])
+	pi, err := bindings.GetPublicChannelIdentity(marshaledPublic)
 	if err != nil {
 		utils.Throw(utils.TypeError, err)
-		return nil
 	}
 
-	cm, err := bindings.NewChannelsManagerGoEventModel(
-		args[0].Int(), args[1].Int(), em)
+	return utils.CopyBytesToJS(pi)
+}
+
+// GetPublicChannelIdentityFromPrivate returns the public identity
+// ([channel.Identity]) contained in the given private identity
+// ([channel.PrivateIdentity]).
+//
+// Parameters:
+//  - args[0] - Bytes of the private identity
+//    (channel.PrivateIdentity]) (Uint8Array).
+//
+// Returns:
+//  - JSON of the public identity ([channel.Identity]) (Uint8Array).
+//  - Throws a TypeError if unmarshalling the bytes or marshalling the identity
+//    fails.
+func GetPublicChannelIdentityFromPrivate(_ js.Value, args []js.Value) interface{} {
+	marshaledPrivate := utils.CopyBytesToGo(args[0])
+	identity, err := bindings.GetPublicChannelIdentityFromPrivate(
+		marshaledPrivate)
+	if err != nil {
+		utils.Throw(utils.TypeError, err)
+	}
+
+	return utils.CopyBytesToJS(identity)
+}
+
+// NewChannelsManager creates a new [ChannelsManager] from a new private
+// identity ([channel.PrivateIdentity]).
+//
+// This is for creating a manager for an identity for the first time. For
+// generating a new one channel identity, use [GenerateChannelIdentity]. To
+// reload this channel manager, use [LoadChannelsManager], passing in the
+// storage tag retrieved by [ChannelsManager.GetStorageTag].
+//
+// Parameters:
+//  - args[0] - ID of [Cmix] object in tracker (int). This can be retrieved
+//    using [Cmix.GetID].
+//  - args[1] - Bytes of a private identity ([channel.PrivateIdentity]) that is
+//    generated by [GenerateChannelIdentity] (Uint8Array).
+//  - args[2] - Javascript object that matches the [bindings.EventModel]
+//    interface.
+//
+// Returns:
+//  - Javascript representation of the [ChannelsManager] object.
+//  - Throws a TypeError if creating the manager fails.
+func NewChannelsManager(_ js.Value, args []js.Value) interface{} {
+	privateIdentity := utils.CopyBytesToGo(args[1])
+	em := &eventModel{
+		joinChannel:      utils.WrapCB(args[2], "JoinChannel"),
+		leaveChannel:     utils.WrapCB(args[2], "LeaveChannel"),
+		receiveMessage:   utils.WrapCB(args[2], "ReceiveMessage"),
+		receiveReply:     utils.WrapCB(args[2], "ReceiveReply"),
+		receiveReaction:  utils.WrapCB(args[2], "ReceiveReaction"),
+		updateSentStatus: utils.WrapCB(args[2], "UpdateSentStatus"),
+	}
+
+	cm, err := bindings.NewChannelsManager(args[0].Int(), privateIdentity, em)
 	if err != nil {
 		utils.Throw(utils.TypeError, err)
 		return nil
@@ -114,30 +168,38 @@ func NewChannelsManagerWithIndexedDb(_ js.Value, args []js.Value) interface{} {
 	return newChannelsManagerJS(cm)
 }
 
-// NewChannelsManagerWithIndexedDbDummyNameService constructs a
-// [ChannelsManager] using an indexedDb backend and a dummy name server instead
-// of UD.
+// NewChannelsManagerWithIndexedDb creates a new [ChannelsManager] from a new
+// private identity ([channel.PrivateIdentity]) and using indexedDb as a backend
+// to manage the event model.
+//
+// This is for creating a manager for an identity for the first time. For
+// generating a new one channel identity, use [GenerateChannelIdentity]. To
+// reload this channel manager, use [LoadChannelsManagerGoEventModel], passing
+// in the storage tag retrieved by [ChannelsManager.GetStorageTag].
 //
 // This function initialises an indexedDb database.
 //
 // Parameters:
 //  - args[0] - ID of [Cmix] object in tracker (int). This can be retrieved
 //    using [Cmix.GetID].
-//  - args[1] - Username (string).
+//  - args[1] - Bytes of a private identity ([channel.PrivateIdentity]) that is
+//    generated by [GenerateChannelIdentity] (Uint8Array).
+//  - args[2] - Username (string).
 //
 // Returns:
-//  - Javascript representation of the [bindings.ChannelsManager] object.
+//  - Javascript representation of the [ChannelsManager] object.
 //  - Throws a TypeError if initialising indexedDb or created the new channel
 //    manager fails.
-func NewChannelsManagerWithIndexedDbDummyNameService(_ js.Value, args []js.Value) interface{} {
-	em, err := indexedDb.NewWasmEventModel(args[1].String())
+func NewChannelsManagerWithIndexedDb(_ js.Value, args []js.Value) interface{} {
+	privateIdentity := utils.CopyBytesToGo(args[1])
+	em, err := indexedDb.NewWasmEventModel(args[2].String())
 	if err != nil {
 		utils.Throw(utils.TypeError, err)
 		return nil
 	}
 
-	cm, err := bindings.NewChannelsManagerGoEventModelDummyNameService(
-		args[0].Int(), args[1].String(), em)
+	cm, err := bindings.NewChannelsManagerGoEventModel(
+		args[0].Int(), privateIdentity, em)
 	if err != nil {
 		utils.Throw(utils.TypeError, err)
 		return nil
@@ -146,21 +208,25 @@ func NewChannelsManagerWithIndexedDbDummyNameService(_ js.Value, args []js.Value
 	return newChannelsManagerJS(cm)
 }
 
-// NewChannelsManagerDummyNameService constructs a [ChannelsManager]
-// using a Javascript event model backend and a dummy name server instead of UD.
+// LoadChannelsManager loads an existing [ChannelsManager].
+//
+// This is for loading a manager for an identity that has already been created.
+// The channel manager should have previously been created with
+// [NewChannelsManager] and the storage is retrievable with
+// [ChannelsManager.GetStorageTag].
 //
 // Parameters:
-//  - args[0] -  ID of [Cmix] object in tracker (int). This can be retrieved
+//  - args[0] - ID of [Cmix] object in tracker (int). This can be retrieved
 //    using [Cmix.GetID].
-//  - args[1] - Username (string).
+//  - args[1] - The storage tag associated with the previously created channel
+//    manager and retrieved with [ChannelsManager.GetStorageTag] (string).
 //  - args[2] - Javascript object that matches the [bindings.EventModel]
 //    interface.
 //
 // Returns:
-//  - Javascript representation of the [bindings.ChannelsManager] object.
-//  - Throws a TypeError if initialising indexedDb or created the new channel
-//    manager fails.
-func NewChannelsManagerDummyNameService(_ js.Value, args []js.Value) interface{} {
+//  - Javascript representation of the [ChannelsManager] object.
+//  - Throws a TypeError if loading the manager fails.
+func LoadChannelsManager(_ js.Value, args []js.Value) interface{} {
 	em := &eventModel{
 		joinChannel:      utils.WrapCB(args[2], "JoinChannel"),
 		leaveChannel:     utils.WrapCB(args[2], "LeaveChannel"),
@@ -170,8 +236,7 @@ func NewChannelsManagerDummyNameService(_ js.Value, args []js.Value) interface{}
 		updateSentStatus: utils.WrapCB(args[2], "UpdateSentStatus"),
 	}
 
-	cm, err := bindings.NewChannelsManagerGoEventModelDummyNameService(
-		args[0].Int(), args[1].String(), bindings.NewEventModel(em))
+	cm, err := bindings.LoadChannelsManager(args[0].Int(), args[1].String(), em)
 	if err != nil {
 		utils.Throw(utils.TypeError, err)
 		return nil
@@ -180,21 +245,19 @@ func NewChannelsManagerDummyNameService(_ js.Value, args []js.Value) interface{}
 	return newChannelsManagerJS(cm)
 }
 
-// GenerateChannel is used to create a channel. This makes a new channel of
-// which you are the admin. It is only for making new channels, not joining
-// existing ones.
+// GenerateChannel is used to create a channel a new channel of which you are
+// the admin. It is only for making new channels, not joining existing ones.
 //
 // It returns a pretty print of the channel and the private key.
 //
-// The name cannot be longer that ____ characters.
-//
-// The description cannot be longer than ___ and can only use ______ characters.
+// The name cannot be longer that __ characters. The description cannot be
+// longer than __ and can only use ______ characters.
 //
 // Parameters:
 //  - args[0] - ID of [Cmix] object in tracker (int).
 //  - args[1] - The name of the new channel. The name cannot be longer than __
-//    characters and must contain only __ characters. It cannot be changed once
-//    a channel is created (string).
+//    characters and must contain only _____ characters. It cannot be changed
+//    once a channel is created. (string).
 //  - args[2] - The description of a channel. The description cannot be longer
 //    than __ characters and must contain only __ characters. It cannot be
 //    changed once a channel is created (string).
@@ -541,6 +604,106 @@ func (ch *ChannelsManager) SendReaction(_ js.Value, args []js.Value) interface{}
 	return utils.CreatePromise(promiseFn)
 }
 
+// GetIdentity returns the marshaled public identity ([channel.Identity]) that
+// the channel is using.
+//
+// Returns:
+//  - JSON of the [channel.Identity] (Uint8Array).
+//  - Throws TypeError if marshalling the identity fails.
+func (ch *ChannelsManager) GetIdentity(js.Value, []js.Value) interface{} {
+	i, err := ch.api.GetIdentity()
+	if err != nil {
+		utils.Throw(utils.TypeError, err)
+		return nil
+	}
+
+	return utils.CopyBytesToJS(i)
+}
+
+// GetStorageTag returns the storage tag needed to reload the manager.
+//
+// Returns:
+//  - Storage tag (string).
+func (ch *ChannelsManager) GetStorageTag(js.Value, []js.Value) interface{} {
+	return ch.api.GetStorageTag()
+}
+
+// SetNickname sets the nickname for a given channel. The nickname must be valid
+// according to [IsNicknameValid].
+//
+// Parameters:
+//  - args[0] - The nickname to set (string).
+//  - args[1] - Marshalled bytes if the channel's [id.ID] (Uint8Array).
+//
+// Returns:
+//  - Throws TypeError if unmarshalling the ID fails or the nickname is invalid.
+func (ch *ChannelsManager) SetNickname(_ js.Value, args []js.Value) interface{} {
+	err := ch.api.SetNickname(args[0].String(), utils.CopyBytesToGo(args[1]))
+	if err != nil {
+		utils.Throw(utils.TypeError, err)
+		return nil
+	}
+
+	return nil
+}
+
+// DeleteNickname deletes the nickname for a given channel.
+//
+// Parameters:
+//  - args[0] - Marshalled bytes if the channel's [id.ID] (Uint8Array).
+//
+// Returns:
+//  - Throws TypeError if deleting the nickname fails.
+func (ch *ChannelsManager) DeleteNickname(_ js.Value, args []js.Value) interface{} {
+	err := ch.api.DeleteNickname(utils.CopyBytesToGo(args[0]))
+	if err != nil {
+		utils.Throw(utils.TypeError, err)
+		return nil
+	}
+
+	return nil
+}
+
+// GetNickname returns the nickname set for a given channel. Returns an error if
+// there is no nickname set.
+//
+// Parameters:
+//  - args[0] - Marshalled bytes if the channel's [id.ID] (Uint8Array).
+//
+// Returns:
+//  - The nickname (string).
+//  - Throws TypeError if the channel has no nickname set.
+func (ch *ChannelsManager) GetNickname(_ js.Value, args []js.Value) interface{} {
+	nickname, err := ch.api.GetNickname(utils.CopyBytesToGo(args[0]))
+	if err != nil {
+		utils.Throw(utils.TypeError, err)
+		return nil
+	}
+
+	return nickname
+}
+
+// IsNicknameValid checks if a nickname is valid.
+//
+// Rules:
+//  1. A nickname must not be longer than 24 characters.
+//  2. A nickname must not be shorter than 1 character.
+//
+// Parameters:
+//  - args[0] - Nickname to check (string).
+//
+// Returns:
+//  - A Javascript Error object if the nickname is invalid with the reason why.
+//  - Null if the nickname is valid.
+func IsNicknameValid(_ js.Value, args []js.Value) interface{} {
+	err := bindings.IsNicknameValid(args[0].String())
+	if err != nil {
+		return utils.JsError(err)
+	}
+
+	return nil
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // Channel Receiving Logic and Callback Registration                          //
 ////////////////////////////////////////////////////////////////////////////////
@@ -557,10 +720,16 @@ type channelMessageReceptionCallback struct {
 //  - 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 *channelMessageReceptionCallback) Callback(
-	receivedChannelMessageReport []byte, err error) {
-	cmrCB.callback(utils.CopyBytesToJS(receivedChannelMessageReport),
-		utils.JsTrace(err))
+	receivedChannelMessageReport []byte, err error) int {
+	uuid := cmrCB.callback(
+		utils.CopyBytesToJS(receivedChannelMessageReport), utils.JsTrace(err))
+
+	return uuid.Int()
 }
 
 // RegisterReceiveHandler is used to register handlers for non-default message
@@ -632,8 +801,9 @@ func (em *eventModel) LeaveChannel(channelID []byte) {
 //  - channelID - Marshalled bytes of the channel [id.ID] (Uint8Array).
 //  - messageID - The bytes of the [channel.MessageID] of the received message
 //    (Uint8Array).
-//  - senderUsername - The username of the sender of the message (string).
+//  - nickname - The nickname of the sender of the message (string).
 //  - text - The content of the message (string).
+//  - identity - JSON of the sender's public ([channel.Identity]) (Uint8Array).
 //  - timestamp - Time the message was received; represented as nanoseconds
 //    since unix epoch (int).
 //  - lease - The number of nanoseconds that the message is valid for (int).
@@ -644,11 +814,17 @@ func (em *eventModel) LeaveChannel(channelID []byte) {
 //  Sent      =  0
 //  Delivered =  1
 //  Failed    =  2
-func (em *eventModel) ReceiveMessage(channelID, messageID []byte,
-	senderUsername, text string, timestamp, lease, roundId, status int64) {
-	em.receiveMessage(utils.CopyBytesToJS(channelID),
-		utils.CopyBytesToJS(messageID),
-		senderUsername, text, timestamp, lease, roundId, status)
+//
+// Returns:
+//  - A non-negative unique UUID for the message that it can be referenced by
+//    later with [eventModel.UpdateSentStatus].
+func (em *eventModel) ReceiveMessage(channelID, messageID []byte, nickname,
+	text string, identity []byte, timestamp, lease, roundId, status int64) int64 {
+	uuid := em.receiveMessage(utils.CopyBytesToJS(channelID),
+		utils.CopyBytesToJS(messageID), nickname, text,
+		utils.CopyBytesToJS(identity), timestamp, lease, roundId, status)
+
+	return int64(uuid.Int())
 }
 
 // ReceiveReply is called whenever a message is received that is a reply on a
@@ -666,6 +842,7 @@ func (em *eventModel) ReceiveMessage(channelID, messageID []byte,
 //    (Uint8Array).
 //  - senderUsername - The username of the sender of the message (string).
 //  - text - The content of the message (string).
+//  - identity - JSON of the sender's public ([channel.Identity]) (Uint8Array).
 //  - timestamp - Time the message was received; represented as nanoseconds
 //    since unix epoch (int).
 //  - lease - The number of nanoseconds that the message is valid for (int).
@@ -676,11 +853,19 @@ func (em *eventModel) ReceiveMessage(channelID, messageID []byte,
 //  Sent      =  0
 //  Delivered =  1
 //  Failed    =  2
+//
+// Returns:
+//  - A non-negative unique UUID for the message that it can be referenced by
+//    later with [eventModel.UpdateSentStatus].
 func (em *eventModel) ReceiveReply(channelID, messageID, reactionTo []byte,
-	senderUsername, text string, timestamp, lease, roundId, status int64) {
-	em.receiveReply(utils.CopyBytesToJS(channelID),
+	senderUsername, text string, identity []byte, timestamp, lease, roundId,
+	status int64) int64 {
+	uuid := em.receiveReply(utils.CopyBytesToJS(channelID),
 		utils.CopyBytesToJS(messageID), utils.CopyBytesToJS(reactionTo),
-		senderUsername, text, timestamp, lease, roundId, status)
+		senderUsername, text, utils.CopyBytesToJS(identity),
+		timestamp, lease, roundId, status)
+
+	return int64(uuid.Int())
 }
 
 // ReceiveReaction is called whenever a reaction to a message is received on a
@@ -698,6 +883,7 @@ func (em *eventModel) ReceiveReply(channelID, messageID, reactionTo []byte,
 //    (Uint8Array).
 //  - senderUsername - The username of the sender of the message (string).
 //  - reaction - The contents of the reaction message (string).
+//  - identity - JSON of the sender's public ([channel.Identity]) (Uint8Array).
 //  - timestamp - Time the message was received; represented as nanoseconds
 //    since unix epoch (int).
 //  - lease - The number of nanoseconds that the message is valid for (int).
@@ -708,25 +894,39 @@ func (em *eventModel) ReceiveReply(channelID, messageID, reactionTo []byte,
 //  Sent      =  0
 //  Delivered =  1
 //  Failed    =  2
+//
+// Returns:
+//  - A non-negative unique UUID for the message that it can be referenced by
+//    later with [eventModel.UpdateSentStatus].
 func (em *eventModel) ReceiveReaction(channelID, messageID, reactionTo []byte,
-	senderUsername, reaction string, timestamp, lease, roundId, status int64) {
-	em.receiveReaction(utils.CopyBytesToJS(channelID),
+	senderUsername, reaction string, identity []byte, timestamp, lease, roundId,
+	status int64) int64 {
+	uuid := em.receiveReaction(utils.CopyBytesToJS(channelID),
 		utils.CopyBytesToJS(messageID), utils.CopyBytesToJS(reactionTo),
-		senderUsername, reaction, timestamp, lease, roundId, status)
+		senderUsername, reaction, utils.CopyBytesToJS(identity),
+		timestamp, lease, roundId, status)
+
+	return int64(uuid.Int())
 }
 
 // UpdateSentStatus is called whenever the sent status of a message has
 // changed.
 //
 // Parameters:
+//  - uuid - The unique identifier for the message (int).
 //  - messageID - The bytes of the [channel.MessageID] of the received message
 //    (Uint8Array).
+//  - timestamp - Time the message was received; represented as nanoseconds
+//    since unix epoch (int).
+//  - roundId - The ID of the round that the message was received on (int).
 //  - status - The [channels.SentStatus] of the message (int).
 //
 // Statuses will be enumerated as such:
 //  Sent      =  0
 //  Delivered =  1
 //  Failed    =  2
-func (em *eventModel) UpdateSentStatus(messageID []byte, status int64) {
-	em.updateSentStatus(utils.CopyBytesToJS(messageID), status)
+func (em *eventModel) UpdateSentStatus(
+	uuid int64, messageID []byte, timestamp, roundID, status int64) {
+	em.updateSentStatus(
+		uuid, utils.CopyBytesToJS(messageID), timestamp, roundID, status)
 }
diff --git a/wasm_test.go b/wasm_test.go
index d256a93a4d72b0396f9cbda758fa06ab9605dc1e..c17d1df49b02f5ea433b862ddbda430d50314618 100644
--- a/wasm_test.go
+++ b/wasm_test.go
@@ -39,9 +39,9 @@ func TestPublicFunctions(t *testing.T) {
 
 		// These functions are used internally by the WASM bindings but are not
 		// exposed
-		"NewEventModel":                                  {},
-		"NewChannelsManagerGoEventModel":                 {},
-		"NewChannelsManagerGoEventModelDummyNameService": {},
+		"NewEventModel":                   {},
+		"NewChannelsManagerGoEventModel":  {},
+		"LoadChannelsManagerGoEventModel": {},
 	}
 	wasmFuncs := getPublicFunctions("wasm", t)
 	bindingsFuncs := getPublicFunctions(