diff --git a/broadcast/asymmetric.go b/broadcast/asymmetric.go
index 2b263cad705cadcfd0f9fb5a819a1c019cb8bb4b..3eab48f0de4060db3a29801d5cb3b84e50253133 100644
--- a/broadcast/asymmetric.go
+++ b/broadcast/asymmetric.go
@@ -12,6 +12,7 @@ import (
 	"github.com/pkg/errors"
 	"gitlab.com/elixxir/client/cmix"
 	"gitlab.com/elixxir/client/cmix/message"
+	"gitlab.com/elixxir/primitives/format"
 	"gitlab.com/xx_network/crypto/multicastRSA"
 	"gitlab.com/xx_network/primitives/id"
 	"gitlab.com/xx_network/primitives/id/ephemeral"
@@ -23,52 +24,86 @@ const (
 	internalPayloadSizeLength     = 2
 )
 
-// BroadcastAsymmetric broadcasts the payload to the channel. Requires a healthy network state to send
-// Payload must be equal to bc.MaxAsymmetricPayloadSize, and the channel PrivateKey must be passed in
-func (bc *broadcastClient) BroadcastAsymmetric(pk multicastRSA.PrivateKey, payload []byte, cMixParams cmix.CMIXParams) (
-	id.Round, ephemeral.Id, error) {
+// BroadcastAsymmetric broadcasts the payload to the channel. Requires a
+// healthy network state to send Payload length must be equal to
+// bc.MaxAsymmetricPayloadSize, and the channel PrivateKey must be passed in
+func (bc *broadcastClient) BroadcastAsymmetric(pk multicastRSA.PrivateKey,
+	payload []byte, cMixParams cmix.CMIXParams) (id.Round, ephemeral.Id, error) {
+	// Confirm network health
+
+	assemble := func(rid id.Round) (payload []byte, err error) {
+		return payload, nil
+	}
+	return bc.BroadcastAsymmetricWithAssembler(pk, assemble, cMixParams)
+}
+
+// BroadcastAsymmetricWithAssembler broadcasts the payload to the channel with
+// a function which builds the payload based upon the ID of the selected round.
+// Requires a healthy network state to send Payload must be equal to
+// bc.MaxAsymmetricPayloadSize when returned, and the channel PrivateKey
+// must be passed in
+func (bc *broadcastClient) BroadcastAsymmetricWithAssembler(
+	pk multicastRSA.PrivateKey, assembler Assembler,
+	cMixParams cmix.CMIXParams) (id.Round, ephemeral.Id, error) {
 	// Confirm network health
 	if !bc.net.IsHealthy() {
 		return 0, ephemeral.Id{}, errors.New(errNetworkHealth)
 	}
 
-	// Check payload size
-	if len(payload) > bc.MaxAsymmetricPayloadSize() {
-		return 0, ephemeral.Id{},
-			errors.Errorf(errPayloadSize, len(payload), bc.MaxAsymmetricPayloadSize())
-	}
-	payloadLength := uint16(len(payload))
+	assemble := func(rid id.Round) (fp format.Fingerprint,
+		service message.Service, encryptedPayload, mac []byte, err error) {
+		payload, err := assembler(rid)
+		if err != nil {
+			return format.Fingerprint{}, message.Service{}, nil,
+				nil, err
+		}
+		// Check payload size
+		if len(payload) > bc.MaxAsymmetricPayloadSize() {
+			return format.Fingerprint{}, message.Service{}, nil,
+				nil, errors.Errorf(errPayloadSize, len(payload),
+					bc.MaxAsymmetricPayloadSize())
+		}
+		payloadLength := uint16(len(payload))
 
-	finalPayload := make([]byte, bc.maxAsymmetricPayloadSizeRaw())
-	binary.BigEndian.PutUint16(finalPayload[:internalPayloadSizeLength], payloadLength)
-	copy(finalPayload[internalPayloadSizeLength:], payload)
+		finalPayload := make([]byte, bc.maxAsymmetricPayloadSizeRaw())
+		binary.BigEndian.PutUint16(finalPayload[:internalPayloadSizeLength],
+			payloadLength)
+		copy(finalPayload[internalPayloadSizeLength:], payload)
 
-	// Encrypt payload
-	encryptedPayload, mac, fp, err := bc.channel.EncryptAsymmetric(finalPayload, pk, bc.rng.GetStream())
-	if err != nil {
-		return 0, ephemeral.Id{}, errors.WithMessage(err, "Failed to encrypt asymmetric broadcast message")
-	}
+		// Encrypt payload
+		encryptedPayload, mac, fp, err =
+			bc.channel.EncryptAsymmetric(finalPayload, pk, bc.rng.GetStream())
+		if err != nil {
+			return format.Fingerprint{}, message.Service{}, nil,
+				nil, errors.WithMessage(err, "Failed to encrypt "+
+					"asymmetric broadcast message")
+		}
 
-	// Create service using asymmetric broadcast service tag & channel reception ID
-	// Allows anybody with this info to listen for messages on this channel
-	service := message.Service{
-		Identifier: bc.channel.ReceptionID.Bytes(),
-		Tag:        asymmetricBroadcastServiceTag,
-	}
+		// Create service using asymmetric broadcast service tag & channel
+		// reception ID allows anybody with this info to listen for messages on
+		// this channel
+		service = message.Service{
+			Identifier: bc.channel.ReceptionID.Bytes(),
+			Tag:        asymmetricBroadcastServiceTag,
+		}
 
-	if cMixParams.DebugTag == cmix.DefaultDebugTag {
-		cMixParams.DebugTag = asymmCMixSendTag
-	}
+		if cMixParams.DebugTag == cmix.DefaultDebugTag {
+			cMixParams.DebugTag = asymmCMixSendTag
+		}
+
+		// Create payload sized for sending over cmix
+		sizedPayload := make([]byte, bc.net.GetMaxMessageLength())
+		// Read random data into sized payload
+		_, err = bc.rng.GetStream().Read(sizedPayload)
+		if err != nil {
+			return format.Fingerprint{}, message.Service{}, nil,
+				nil, errors.WithMessage(err, "Failed to add "+
+					"random data to sized broadcast")
+		}
+		copy(sizedPayload[:len(encryptedPayload)], encryptedPayload)
 
-	// Create payload sized for sending over cmix
-	sizedPayload := make([]byte, bc.net.GetMaxMessageLength())
-	// Read random data into sized payload
-	_, err = bc.rng.GetStream().Read(sizedPayload)
-	if err != nil {
-		return 0, ephemeral.Id{}, errors.WithMessage(err, "Failed to add random data to sized broadcast")
+		return
 	}
-	copy(sizedPayload[:len(encryptedPayload)], encryptedPayload)
 
-	return bc.net.Send(
-		bc.channel.ReceptionID, fp, service, sizedPayload, mac, cMixParams)
+	return bc.net.SendWithAssembler(bc.channel.ReceptionID, assemble, cMixParams)
 }
diff --git a/broadcast/broadcastClient.go b/broadcast/client.go
similarity index 100%
rename from broadcast/broadcastClient.go
rename to broadcast/client.go
diff --git a/broadcast/interface.go b/broadcast/interface.go
index 2a3aa4be7f41bbfb2d311e94b590a66ef479a68b..7eef2d0d74e4b0826b2437bb5df9b87b64ebb514 100644
--- a/broadcast/interface.go
+++ b/broadcast/interface.go
@@ -29,7 +29,8 @@ type Channel interface {
 	// MaxPayloadSize returns the maximum size for a symmetric broadcast payload
 	MaxPayloadSize() int
 
-	// MaxAsymmetricPayloadSize returns the maximum size for an asymmetric broadcast payload
+	// MaxAsymmetricPayloadSize returns the maximum size for an asymmetric
+	//broadcast payload
 	MaxAsymmetricPayloadSize() int
 
 	// Get returns the underlying crypto.Channel
@@ -40,18 +41,27 @@ type Channel interface {
 	Broadcast(payload []byte, cMixParams cmix.CMIXParams) (
 		id.Round, ephemeral.Id, error)
 
-	// BroadcastWithAssembler broadcasts a payload over a symmetric channel. With
-	// a payload assembled after the round is selected, allowing the round
-	// info to be included in the payload.
-	// Network must be healthy to send
+	// BroadcastWithAssembler broadcasts a payload over a symmetric channel.
+	// With a payload assembled after the round is selected, allowing the round
+	// info to be included in the payload. Network must be healthy to send.
 	// Requires a payload of size bc.MaxSymmetricPayloadSize()
 	BroadcastWithAssembler(assembler Assembler, cMixParams cmix.CMIXParams) (
 		id.Round, ephemeral.Id, error)
 
-	// BroadcastAsymmetric broadcasts an asymmetric payload to the channel. The payload size must be
-	// equal to MaxPayloadSize & private key for channel must be passed in
-	BroadcastAsymmetric(pk multicastRSA.PrivateKey, payload []byte, cMixParams cmix.CMIXParams) (
-		id.Round, ephemeral.Id, error)
+	// BroadcastAsymmetric broadcasts the payload to the channel. Requires a
+	// healthy network state to send. Payload length must be equal to
+	// bc.MaxAsymmetricPayloadSize and the channel PrivateKey must be passed in
+	BroadcastAsymmetric(pk multicastRSA.PrivateKey, payload []byte,
+		cMixParams cmix.CMIXParams) (id.Round, ephemeral.Id, error)
+
+	// BroadcastAsymmetricWithAssembler broadcasts the payload to the channel.
+	// Requires a healthy network state to send. Payload length must be equal to
+	// bc.MaxAsymmetricPayloadSize and the channel PrivateKey must be passed in.
+	// The assembler will run once a round is selected and will receive the
+	// round ID
+	BroadcastAsymmetricWithAssembler(
+		pk multicastRSA.PrivateKey, assembler Assembler,
+		cMixParams cmix.CMIXParams) (id.Round, ephemeral.Id, error)
 
 	// RegisterListener registers a listener for broadcast messages
 	RegisterListener(listenerCb ListenerFunc, method Method) error
diff --git a/channels/adminListener.go b/channels/adminListener.go
new file mode 100644
index 0000000000000000000000000000000000000000..c19e8fadcd4e7886f8053257b3d168acf776c932
--- /dev/null
+++ b/channels/adminListener.go
@@ -0,0 +1,55 @@
+package channels
+
+import (
+	"github.com/golang/protobuf/proto"
+	jww "github.com/spf13/jwalterweatherman"
+	"gitlab.com/elixxir/client/broadcast"
+	"gitlab.com/elixxir/client/cmix/identity/receptionID"
+	"gitlab.com/elixxir/client/cmix/rounds"
+	"gitlab.com/elixxir/crypto/channel"
+	"gitlab.com/xx_network/primitives/id"
+)
+
+type adminListener struct {
+	name   NameService
+	events *events
+	chID   *id.ID
+}
+
+func (al *adminListener) Listen(payload []byte,
+	receptionID receptionID.EphemeralIdentity, round rounds.Round) {
+
+	//Remove the padding
+	payloadUnpadded, err := broadcast.DecodeSizedBroadcast(payload)
+	if err != nil {
+		jww.WARN.Printf("Failed to strip the padding on User Message "+
+			"on channel %s", al.chID)
+		return
+	}
+
+	//get the message ID
+	msgID := channel.MakeMessageID(payloadUnpadded)
+
+	//Decode the message as a channel message
+	var cm *ChannelMessage
+	if err = proto.Unmarshal(payloadUnpadded, cm); err != nil {
+		jww.WARN.Printf("Failed to unmarshal Channel Message from Admin"+
+			" on channel %s", al.chID)
+		return
+	}
+
+	/*CRYPTOGRAPHICALLY RELEVANT CHECKS*/
+
+	// check the round to ensure the message is not a replay
+	if id.Round(cm.RoundID) != round.ID {
+		jww.WARN.Printf("The round message %s send on %d referenced "+
+			"(%d) was not the same as the round the message was found on (%d)",
+			msgID, al.chID, cm.RoundID, round.ID, al.chID)
+		return
+	}
+
+	//Submit the message to the event model for listening
+	al.events.triggerAdminEvent(al.chID, cm, msgID, receptionID, round)
+
+	return
+}
diff --git a/channels/errors.go b/channels/errors.go
new file mode 100644
index 0000000000000000000000000000000000000000..c9b25870881501b61f3e7598d01271007ff07e5c
--- /dev/null
+++ b/channels/errors.go
@@ -0,0 +1,10 @@
+package channels
+
+import "github.com/pkg/errors"
+
+var ChannelAlreadyExistsErr = errors.New("the channel cannot be added " +
+	"becasue it already exists")
+
+var ChannelDoesNotExistsErr = errors.New("the channel cannot be found")
+
+var MessageTooLongErr = errors.New("the passed message is too long")
diff --git a/channels/eventModel.go b/channels/eventModel.go
index 2712071e0b96072f58cfba1b72e3443e8a9b6839..f86ec23e14ff8ca96574be95ddb65d3a5f03bc4c 100644
--- a/channels/eventModel.go
+++ b/channels/eventModel.go
@@ -14,6 +14,8 @@ import (
 	"gitlab.com/xx_network/primitives/id"
 )
 
+const AdminUsername = "Admin"
+
 var (
 	MessageTypeAlreadyRegistered = errors.New("the given message type has " +
 		"already been registered")
@@ -23,12 +25,23 @@ type EventModel interface {
 	JoinChannel(channel cryptoBroadcast.Channel)
 	LeaveChannel(ChannelID *id.ID)
 
-	ReceiveTextMessage(ChannelID *id.ID, MessageID cryptoChannel.MessageID,
-		messageType MessageType, SenderUsername string, Content []byte,
-		timestamp time.Time, lease time.Duration, round rounds.Round)
-	ReceiveAdminTextMessage(ChannelID *id.ID, MessageID cryptoChannel.MessageID,
-		messageType MessageType, SenderUsername string, Content []byte,
+	ReceiveMessage(ChannelID *id.ID, MessageID cryptoChannel.MessageID,
+		SenderUsername string, Content []byte,
 		timestamp time.Time, lease time.Duration, round rounds.Round)
+
+	ReceiveReply(ChannelID *id.ID, MessageID cryptoChannel.MessageID,
+		ReplyTo cryptoChannel.MessageID, SenderUsername string,
+		Content []byte, timestamp time.Time, lease time.Duration,
+		round rounds.Round)
+	ReceiveReaction(ChannelID *id.ID, MessageID cryptoChannel.MessageID,
+		ReactionTo cryptoChannel.MessageID, SenderUsername string,
+		Reaction []byte, timestamp time.Time, lease time.Duration,
+		round rounds.Round)
+
+	IgnoreMessage(ChannelID *id.ID, MessageID cryptoChannel.MessageID)
+	UnIgnoreMessage(ChannelID *id.ID, MessageID cryptoChannel.MessageID)
+	PinMessage(ChannelID *id.ID, MessageID cryptoChannel.MessageID, end time.Time)
+	UnPinMessage(ChannelID *id.ID, MessageID cryptoChannel.MessageID)
 }
 
 type MessageTypeReceiveMessage func(ChannelID *id.ID,
@@ -71,7 +84,7 @@ func (e *events) RegisterReceiveHandler(messageType MessageType,
 	return nil
 }
 
-func (e *events) hear(chID *id.ID, umi *UserMessageInternal,
+func (e *events) triggerEvent(chID *id.ID, umi *UserMessageInternal,
 	receptionID receptionID.EphemeralIdentity, round rounds.Round) {
 	um := umi.GetUserMessage()
 	cm := umi.GetChannelMessage()
@@ -93,3 +106,24 @@ func (e *events) hear(chID *id.ID, umi *UserMessageInternal,
 		cm.Payload, round.Timestamps[states.QUEUED], time.Duration(cm.Lease), round)
 	return
 }
+
+func (e *events) triggerAdminEvent(chID *id.ID, cm *ChannelMessage,
+	messageID cryptoChannel.MessageID, receptionID receptionID.EphemeralIdentity, round rounds.Round) {
+	messageType := MessageType(cm.PayloadType)
+
+	//check if the type is already registered
+	e.mux.RLock()
+	listener, exists := e.registered[messageType]
+	e.mux.RUnlock()
+	if !exists {
+		jww.WARN.Printf("Received Admin message from %s on channel %s in "+
+			"round %d which could not be handled due to unregistered message "+
+			"type %s; Contents: %v", AdminUsername, chID, round.ID, messageType,
+			cm.Payload)
+	}
+
+	//Call the listener. This is already in an instanced event, no new thread needed.
+	listener(chID, messageID, messageType, AdminUsername,
+		cm.Payload, round.Timestamps[states.QUEUED], time.Duration(cm.Lease), round)
+	return
+}
diff --git a/channels/generateProto.sh b/channels/generateProto.sh
index 43c17e7e0587fe877ef77e52452f20d646a29670..13678dee233c7fd43c3f7977c7008d98096ccfed 100755
--- a/channels/generateProto.sh
+++ b/channels/generateProto.sh
@@ -7,4 +7,5 @@
 #/ LICENSE file                                                               //
 #///////////////////////////////////////////////////////////////////////////////
 
-protoc --go_out=paths=source_relative:. channels/messages.proto
+protoc --go_out=paths=source_relative:. messages.proto
+protoc --go_out=paths=source_relative:. text.proto
diff --git a/channels/interface.go b/channels/interface.go
index 66ad46c23b07e947b598c0158ea804dacb026b78..8ecc115e96c73d7e8087e692a677fcc9fdcfd56d 100644
--- a/channels/interface.go
+++ b/channels/interface.go
@@ -1,4 +1,33 @@
 package channels
 
+import (
+	"gitlab.com/elixxir/client/cmix"
+	cryptoChannel "gitlab.com/elixxir/crypto/channel"
+	"gitlab.com/xx_network/crypto/signature/rsa"
+	"gitlab.com/xx_network/primitives/id"
+	"gitlab.com/xx_network/primitives/id/ephemeral"
+	"math"
+	"time"
+)
+
+var ValidForever = time.Duration(math.MaxInt64)
+
 type Manager interface {
+	SendGeneric(channelID *id.ID, messageType MessageType, msg []byte,
+		validUntil time.Duration, params cmix.CMIXParams) (
+		cryptoChannel.MessageID, id.Round, ephemeral.Id, error)
+	SendAdminGeneric(privKey *rsa.PrivateKey, channelID *id.ID,
+		msg []byte, validUntil time.Duration, messageType MessageType,
+		params cmix.CMIXParams) (cryptoChannel.MessageID, id.Round, ephemeral.Id,
+		error)
+
+	SendMessage(channelID *id.ID, msg string,
+		validUntil time.Duration, params cmix.CMIXParams) (
+		cryptoChannel.MessageID, id.Round, ephemeral.Id, error)
+	SendReply(channelID *id.ID, msg string, replyTo cryptoChannel.MessageID,
+		validUntil time.Duration, params cmix.CMIXParams) (
+		cryptoChannel.MessageID, id.Round, ephemeral.Id, error)
+	SendReaction(channelID *id.ID, msg []byte,
+		validUntil time.Duration, params cmix.CMIXParams) (
+		cryptoChannel.MessageID, id.Round, ephemeral.Id, error)
 }
diff --git a/channels/joinedChannel.go b/channels/joinedChannel.go
index 9a6fd4dad0d9c77e8ec2724e6c79af84a5fcd93f..0936b40eac894e6806d87ddc0faa77c230eafc42 100644
--- a/channels/joinedChannel.go
+++ b/channels/joinedChannel.go
@@ -2,7 +2,6 @@ package channels
 
 import (
 	"encoding/json"
-	"errors"
 	jww "github.com/spf13/jwalterweatherman"
 	"gitlab.com/elixxir/client/broadcast"
 	"gitlab.com/elixxir/client/storage/versioned"
@@ -19,11 +18,6 @@ const (
 	joinedChannelKey      = "JoinedChannelKey-"
 )
 
-var ChannelAlreadyExistsErr = errors.New("the channel cannot be added " +
-	"becasue it already exists")
-
-var ChannelDoesNotExistsErr = errors.New("the channel cannot be found")
-
 // store Stores the list of joined channels to disk while taking the read lock
 func (m *manager) store() error {
 	m.mux.RLock()
@@ -95,7 +89,7 @@ func (m *manager) addChannel(channel cryptoBroadcast.Channel) error {
 	}
 
 	//Connect to listeners
-	err = b.RegisterListener((&genericUserListener{
+	err = b.RegisterListener((&userListener{
 		name:   m.name,
 		events: &m.events,
 		chID:   channel.ReceptionID,
@@ -104,15 +98,26 @@ func (m *manager) addChannel(channel cryptoBroadcast.Channel) error {
 		return err
 	}
 
+	err = b.RegisterListener((&adminListener{
+		name:   m.name,
+		events: &m.events,
+		chID:   channel.ReceptionID,
+	}).Listen, broadcast.Asymmetric)
+	if err != nil {
+		return err
+	}
+
 	jc := &joinedChannel{
 		broadcast: b,
 	}
 
 	if err = jc.Store(m.kv); err != nil {
+		go b.Stop()
 		return err
 	}
 
 	if err = m.storeUnsafe(); err != nil {
+		go b.Stop()
 		return err
 	}
 	return nil
@@ -197,7 +202,7 @@ func loadJoinedChannel(chId *id.ID, kv *versioned.KV, net broadcast.Client,
 		return nil, err
 	}
 
-	err = b.RegisterListener((&genericUserListener{
+	err = b.RegisterListener((&userListener{
 		name:   name,
 		events: e,
 		chID:   jcd.broadcast.ReceptionID,
@@ -206,6 +211,15 @@ func loadJoinedChannel(chId *id.ID, kv *versioned.KV, net broadcast.Client,
 		return nil, err
 	}
 
+	err = b.RegisterListener((&adminListener{
+		name:   name,
+		events: e,
+		chID:   jcd.broadcast.ReceptionID,
+	}).Listen, broadcast.Asymmetric)
+	if err != nil {
+		return nil, err
+	}
+
 	jc := &joinedChannel{broadcast: b}
 	return jc, nil
 }
diff --git a/channels/manager.go b/channels/manager.go
index 2e928c90a59ef234347fd902e2f11265cdfecc0a..43ffa87b6de5ca4d574edac9064b906fce828bc2 100644
--- a/channels/manager.go
+++ b/channels/manager.go
@@ -5,14 +5,18 @@ import (
 	"gitlab.com/elixxir/client/broadcast"
 	"gitlab.com/elixxir/client/cmix"
 	"gitlab.com/elixxir/client/storage/versioned"
+	cryptoBroadcast "gitlab.com/elixxir/crypto/broadcast"
 	cryptoChannel "gitlab.com/elixxir/crypto/channel"
 	"gitlab.com/elixxir/crypto/fastRNG"
+	"gitlab.com/xx_network/crypto/signature/rsa"
 	"gitlab.com/xx_network/primitives/id"
 	"gitlab.com/xx_network/primitives/id/ephemeral"
 	"sync"
 	"time"
 )
 
+const cmixChannelTextVerion = 0
+
 type manager struct {
 	//List of all channels
 	channels map[*id.ID]*joinedChannel
@@ -29,9 +33,17 @@ type manager struct {
 	broadcastMaker broadcast.NewBroadcastChannelFunc
 }
 
-func (m *manager) Send(channelID *id.ID, msg []byte, validUntil time.Duration,
-	messageType MessageType, params cmix.CMIXParams) (cryptoChannel.MessageID, id.Round, ephemeral.Id,
-	error) {
+func NewManager() {
+
+}
+
+func (m *manager) JoinChannel(channel cryptoBroadcast.Channel) error {
+	return m.addChannel(channel)
+}
+
+func (m *manager) SendGeneric(channelID *id.ID, msg []byte, validUntil time.Duration,
+	messageType MessageType, params cmix.CMIXParams) (cryptoChannel.MessageID,
+	id.Round, ephemeral.Id, error) {
 
 	//find the channel
 	ch, err := m.getChannel(channelID)
@@ -101,3 +113,112 @@ func (m *manager) Send(channelID *id.ID, msg []byte, validUntil time.Duration,
 	rid, ephid, err := ch.broadcast.BroadcastWithAssembler(assemble, params)
 	return msgId, rid, ephid, err
 }
+
+func (m *manager) SendAdminGeneric(privKey *rsa.PrivateKey, channelID *id.ID,
+	msg []byte, validUntil time.Duration, messageType MessageType,
+	params cmix.CMIXParams) (cryptoChannel.MessageID, id.Round, ephemeral.Id,
+	error) {
+
+	//find the channel
+	ch, err := m.getChannel(channelID)
+	if err != nil {
+		return cryptoChannel.MessageID{}, 0, ephemeral.Id{}, err
+	}
+
+	var msgId cryptoChannel.MessageID
+	//Note: we are not checking check if message is too long before trying to
+	//find a round
+
+	//Build the function pointer that will build the message
+	assemble := func(rid id.Round) ([]byte, error) {
+
+		//Build the message
+		chMsg := &ChannelMessage{
+			Lease:       validUntil.Nanoseconds(),
+			RoundID:     uint64(rid),
+			PayloadType: uint32(messageType),
+			Payload:     msg,
+		}
+
+		//Serialize the message
+		chMsgSerial, err := proto.Marshal(chMsg)
+		if err != nil {
+			return nil, err
+		}
+
+		//check if the message is too long
+		if len(chMsgSerial) > broadcast.MaxSizedBroadcastPayloadSize(privKey.Size()) {
+			return nil, MessageTooLongErr
+		}
+
+		//Fill in any extra bits in the payload to ensure it is the right size
+		chMsgSerialSized, err := broadcast.NewSizedBroadcast(
+			ch.broadcast.MaxAsymmetricPayloadSize(), chMsgSerial)
+		if err != nil {
+			return nil, err
+		}
+
+		msgId = cryptoChannel.MakeMessageID(chMsgSerialSized)
+
+		return chMsgSerialSized, nil
+	}
+
+	//TODO: send the send message over to reception manually so it is added to
+	//the database early
+	rid, ephid, err := ch.broadcast.BroadcastAsymmetricWithAssembler(privKey,
+		assemble, params)
+	return msgId, rid, ephid, err
+}
+
+func (m *manager) SendMessage(channelID *id.ID, msg string,
+	validUntil time.Duration, params cmix.CMIXParams) (
+	cryptoChannel.MessageID, id.Round, ephemeral.Id, error) {
+	txt := &CMIXChannelText{
+		Version:        cmixChannelTextVerion,
+		Text:           msg,
+		ReplyMessageID: nil,
+	}
+
+	txtMarshaled, err := proto.Marshal(txt)
+	if err != nil {
+		return cryptoChannel.MessageID{}, 0, ephemeral.Id{}, err
+	}
+
+	return m.SendGeneric(channelID, txtMarshaled, validUntil, Text, params)
+}
+
+func (m *manager) SendReply(channelID *id.ID, msg string,
+	replyTo cryptoChannel.MessageID, validUntil time.Duration,
+	params cmix.CMIXParams) (cryptoChannel.MessageID, id.Round, ephemeral.Id,
+	error) {
+	txt := &CMIXChannelText{
+		Version:        cmixChannelTextVerion,
+		Text:           msg,
+		ReplyMessageID: replyTo[:],
+	}
+
+	txtMarshaled, err := proto.Marshal(txt)
+	if err != nil {
+		return cryptoChannel.MessageID{}, 0, ephemeral.Id{}, err
+	}
+
+	return m.SendGeneric(channelID, txtMarshaled, validUntil, Text, params)
+}
+
+func (m *manager) SendReaction(channelID *id.ID, msg string,
+	replyTo cryptoChannel.MessageID, validUntil time.Duration,
+	params cmix.CMIXParams) (cryptoChannel.MessageID, id.Round, ephemeral.Id,
+	error) {
+	txt := &CMIXChannelText{
+		Version:        cmixChannelTextVerion,
+		Text:           msg,
+		ReplyMessageID: replyTo[:],
+	}
+
+	txtMarshaled, err := proto.Marshal(txt)
+	if err != nil {
+		return cryptoChannel.MessageID{}, 0, ephemeral.Id{}, err
+	}
+
+	return m.SendGeneric(channelID, txtMarshaled, validUntil, Text, params)
+}
diff --git a/channels/messageTypes.go b/channels/messageTypes.go
index aad369c270a342381ff7dc0f6473e7ea58e1952b..0130995ff8594c6d6d945a634779f637704380ed 100644
--- a/channels/messageTypes.go
+++ b/channels/messageTypes.go
@@ -7,6 +7,7 @@ type MessageType uint32
 const (
 	Text      = MessageType(1)
 	AdminText = MessageType(2)
+	Reaction  = MessageType(3)
 )
 
 func (mt MessageType) String() string {
diff --git a/channels/messages.pb.go b/channels/messages.pb.go
index 9626b2630372d47d4659dcf602961e586f8394cc..4f180c6029e6cdcea4376684797f19bf7913cf01 100644
--- a/channels/messages.pb.go
+++ b/channels/messages.pb.go
@@ -1,217 +1,317 @@
+////////////////////////////////////////////////////////////////////////////////
+// Copyright © 2020 xx network SEZC                                           //
+//                                                                            //
+// Use of this source code is governed by a license that can be found in the  //
+// LICENSE file                                                               //
+////////////////////////////////////////////////////////////////////////////////
+
 // Code generated by protoc-gen-go. DO NOT EDIT.
-// source: channels/messages.proto
+// versions:
+// 	protoc-gen-go v1.26.0
+// 	protoc        (unknown)
+// source: messages.proto
 
 package channels
 
 import (
-	fmt "fmt"
-	proto "github.com/golang/protobuf/proto"
-	math "math"
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	reflect "reflect"
+	sync "sync"
 )
 
-// Reference imports to suppress errors if they are not otherwise used.
-var _ = proto.Marshal
-var _ = fmt.Errorf
-var _ = math.Inf
-
-// This is a compile-time assertion to ensure that this generated file
-// is compatible with the proto package it is being compiled against.
-// A compilation error at this line likely means your copy of the
-// proto package needs to be updated.
-const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
+const (
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
 
 // ChannelMessage is transmitted by the channel. Effectively it is
 // a command for the channel sent by a user with admin access of the channel.
 type ChannelMessage struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
 	// Lease is the length that this channel message will take effect.
-	Lease int64 `protobuf:"varint,1,opt,name=Lease,proto3" json:"Lease,omitempty"`
+	Lease int64 `protobuf:"varint,1,opt,name=Lease,json=lease,proto3" json:"Lease,omitempty"`
 	// The round this message was sent on
-	RoundID uint64 `protobuf:"varint,2,opt,name=RoundID,proto3" json:"RoundID,omitempty"`
+	RoundID uint64 `protobuf:"varint,2,opt,name=RoundID,json=roundID,proto3" json:"RoundID,omitempty"`
 	// The type the below payload is. This may be some form of channel command,
 	// such as BAN<username1>.
-	PayloadType uint32 `protobuf:"varint,3,opt,name=PayloadType,proto3" json:"PayloadType,omitempty"`
+	PayloadType uint32 `protobuf:"varint,3,opt,name=PayloadType,json=payloadType,proto3" json:"PayloadType,omitempty"`
 	// Payload is the actual message payload. It will be processed differently based
 	// on the PayloadType
-	Payload              []byte   `protobuf:"bytes,4,opt,name=Payload,proto3" json:"Payload,omitempty"`
-	XXX_NoUnkeyedLiteral struct{} `json:"-"`
-	XXX_unrecognized     []byte   `json:"-"`
-	XXX_sizecache        int32    `json:"-"`
+	Payload []byte `protobuf:"bytes,4,opt,name=Payload,json=payload,proto3" json:"Payload,omitempty"`
 }
 
-func (m *ChannelMessage) Reset()         { *m = ChannelMessage{} }
-func (m *ChannelMessage) String() string { return proto.CompactTextString(m) }
-func (*ChannelMessage) ProtoMessage()    {}
-func (*ChannelMessage) Descriptor() ([]byte, []int) {
-	return fileDescriptor_91ac4de6d1711204, []int{0}
+func (x *ChannelMessage) Reset() {
+	*x = ChannelMessage{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_messages_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
 }
 
-func (m *ChannelMessage) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_ChannelMessage.Unmarshal(m, b)
-}
-func (m *ChannelMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_ChannelMessage.Marshal(b, m, deterministic)
-}
-func (m *ChannelMessage) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_ChannelMessage.Merge(m, src)
+func (x *ChannelMessage) String() string {
+	return protoimpl.X.MessageStringOf(x)
 }
-func (m *ChannelMessage) XXX_Size() int {
-	return xxx_messageInfo_ChannelMessage.Size(m)
-}
-func (m *ChannelMessage) XXX_DiscardUnknown() {
-	xxx_messageInfo_ChannelMessage.DiscardUnknown(m)
+
+func (*ChannelMessage) ProtoMessage() {}
+
+func (x *ChannelMessage) ProtoReflect() protoreflect.Message {
+	mi := &file_messages_proto_msgTypes[0]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
 }
 
-var xxx_messageInfo_ChannelMessage proto.InternalMessageInfo
+// Deprecated: Use ChannelMessage.ProtoReflect.Descriptor instead.
+func (*ChannelMessage) Descriptor() ([]byte, []int) {
+	return file_messages_proto_rawDescGZIP(), []int{0}
+}
 
-func (m *ChannelMessage) GetLease() int64 {
-	if m != nil {
-		return m.Lease
+func (x *ChannelMessage) GetLease() int64 {
+	if x != nil {
+		return x.Lease
 	}
 	return 0
 }
 
-func (m *ChannelMessage) GetRoundID() uint64 {
-	if m != nil {
-		return m.RoundID
+func (x *ChannelMessage) GetRoundID() uint64 {
+	if x != nil {
+		return x.RoundID
 	}
 	return 0
 }
 
-func (m *ChannelMessage) GetPayloadType() uint32 {
-	if m != nil {
-		return m.PayloadType
+func (x *ChannelMessage) GetPayloadType() uint32 {
+	if x != nil {
+		return x.PayloadType
 	}
 	return 0
 }
 
-func (m *ChannelMessage) GetPayload() []byte {
-	if m != nil {
-		return m.Payload
+func (x *ChannelMessage) GetPayload() []byte {
+	if x != nil {
+		return x.Payload
 	}
 	return nil
 }
 
 // UserMessage is a message sent by a user who is a member within the channel.
 type UserMessage struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
 	// Message contains the contents of the message. This is typically what
-	// the end-user has submitted to the channel.
-	Message []byte `protobuf:"bytes,1,opt,name=Message,proto3" json:"Message,omitempty"`
+	// the end-user has submitted to the channel. This is a serialization of the
+	// ChannelMessage.
+	Message []byte `protobuf:"bytes,1,opt,name=Message,json=message,proto3" json:"Message,omitempty"`
 	// ValidationSignature is the signature validating this user owns
 	// their username and may send messages to the channel under this username.
 	// This signature is provided by UD and may be validated by all members of
 	// the channel.
 	// ValidationSignature = Sig(UD_ECCPrivKey,Username|ECCPublicKey|UsernameLease)
-	ValidationSignature []byte `protobuf:"bytes,2,opt,name=ValidationSignature,proto3" json:"ValidationSignature,omitempty"`
+	ValidationSignature []byte `protobuf:"bytes,2,opt,name=ValidationSignature,json=validationSignature,proto3" json:"ValidationSignature,omitempty"`
 	// Signature is the signature proving this message has been
 	// sent by the owner of this user's public key.
 	// Signature = Sig(User_ECCPublicKey,Message)
-	Signature []byte `protobuf:"bytes,3,opt,name=Signature,proto3" json:"Signature,omitempty"`
+	Signature []byte `protobuf:"bytes,3,opt,name=Signature,json=signature,proto3" json:"Signature,omitempty"`
 	// Username is the username the user has registered with the channel and
 	// with UD.
-	Username string `protobuf:"bytes,4,opt,name=Username,proto3" json:"Username,omitempty"`
+	Username string `protobuf:"bytes,4,opt,name=Username,json=username,proto3" json:"Username,omitempty"`
 	// ECCPublicKey is the user's EC Public key. This is provided by the network.
-	ECCPublicKey []byte `protobuf:"bytes,5,opt,name=ECCPublicKey,proto3" json:"ECCPublicKey,omitempty"`
+	ECCPublicKey []byte `protobuf:"bytes,5,opt,name=ECCPublicKey,json=eCCPublicKey,proto3" json:"ECCPublicKey,omitempty"`
 	// UsernameLease is the lease that has been provided to the username.
 	// This value is provide by UD.
-	UsernameLease        int64    `protobuf:"varint,6,opt,name=UsernameLease,proto3" json:"UsernameLease,omitempty"`
-	XXX_NoUnkeyedLiteral struct{} `json:"-"`
-	XXX_unrecognized     []byte   `json:"-"`
-	XXX_sizecache        int32    `json:"-"`
+	UsernameLease int64 `protobuf:"varint,6,opt,name=UsernameLease,json=usernameLease,proto3" json:"UsernameLease,omitempty"`
 }
 
-func (m *UserMessage) Reset()         { *m = UserMessage{} }
-func (m *UserMessage) String() string { return proto.CompactTextString(m) }
-func (*UserMessage) ProtoMessage()    {}
-func (*UserMessage) Descriptor() ([]byte, []int) {
-	return fileDescriptor_91ac4de6d1711204, []int{1}
+func (x *UserMessage) Reset() {
+	*x = UserMessage{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_messages_proto_msgTypes[1]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
 }
 
-func (m *UserMessage) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_UserMessage.Unmarshal(m, b)
-}
-func (m *UserMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_UserMessage.Marshal(b, m, deterministic)
-}
-func (m *UserMessage) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_UserMessage.Merge(m, src)
+func (x *UserMessage) String() string {
+	return protoimpl.X.MessageStringOf(x)
 }
-func (m *UserMessage) XXX_Size() int {
-	return xxx_messageInfo_UserMessage.Size(m)
-}
-func (m *UserMessage) XXX_DiscardUnknown() {
-	xxx_messageInfo_UserMessage.DiscardUnknown(m)
+
+func (*UserMessage) ProtoMessage() {}
+
+func (x *UserMessage) ProtoReflect() protoreflect.Message {
+	mi := &file_messages_proto_msgTypes[1]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
 }
 
-var xxx_messageInfo_UserMessage proto.InternalMessageInfo
+// Deprecated: Use UserMessage.ProtoReflect.Descriptor instead.
+func (*UserMessage) Descriptor() ([]byte, []int) {
+	return file_messages_proto_rawDescGZIP(), []int{1}
+}
 
-func (m *UserMessage) GetMessage() []byte {
-	if m != nil {
-		return m.Message
+func (x *UserMessage) GetMessage() []byte {
+	if x != nil {
+		return x.Message
 	}
 	return nil
 }
 
-func (m *UserMessage) GetValidationSignature() []byte {
-	if m != nil {
-		return m.ValidationSignature
+func (x *UserMessage) GetValidationSignature() []byte {
+	if x != nil {
+		return x.ValidationSignature
 	}
 	return nil
 }
 
-func (m *UserMessage) GetSignature() []byte {
-	if m != nil {
-		return m.Signature
+func (x *UserMessage) GetSignature() []byte {
+	if x != nil {
+		return x.Signature
 	}
 	return nil
 }
 
-func (m *UserMessage) GetUsername() string {
-	if m != nil {
-		return m.Username
+func (x *UserMessage) GetUsername() string {
+	if x != nil {
+		return x.Username
 	}
 	return ""
 }
 
-func (m *UserMessage) GetECCPublicKey() []byte {
-	if m != nil {
-		return m.ECCPublicKey
+func (x *UserMessage) GetECCPublicKey() []byte {
+	if x != nil {
+		return x.ECCPublicKey
 	}
 	return nil
 }
 
-func (m *UserMessage) GetUsernameLease() int64 {
-	if m != nil {
-		return m.UsernameLease
+func (x *UserMessage) GetUsernameLease() int64 {
+	if x != nil {
+		return x.UsernameLease
 	}
 	return 0
 }
 
-func init() {
-	proto.RegisterType((*ChannelMessage)(nil), "parse.ChannelMessage")
-	proto.RegisterType((*UserMessage)(nil), "parse.UserMessage")
-}
-
-func init() {
-	proto.RegisterFile("channels/messages.proto", fileDescriptor_91ac4de6d1711204)
-}
-
-var fileDescriptor_91ac4de6d1711204 = []byte{
-	// 261 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x90, 0x4f, 0x4b, 0xc3, 0x40,
-	0x10, 0xc5, 0x59, 0xd3, 0xf4, 0xcf, 0x34, 0xf5, 0x30, 0x0a, 0x2e, 0xe2, 0x21, 0x04, 0x0f, 0x39,
-	0xa9, 0xe0, 0x37, 0x30, 0x7a, 0x10, 0x15, 0xca, 0xfa, 0xe7, 0xe0, 0x6d, 0xda, 0x0c, 0x35, 0x90,
-	0xee, 0x86, 0x6c, 0x72, 0x08, 0xf8, 0x55, 0xfd, 0x2e, 0xd2, 0x4d, 0xd7, 0x5a, 0xe8, 0x6d, 0x7e,
-	0x6f, 0xde, 0xf0, 0x86, 0x07, 0x67, 0xcb, 0x2f, 0xd2, 0x9a, 0x4b, 0x7b, 0xbd, 0x66, 0x6b, 0x69,
-	0xc5, 0xf6, 0xaa, 0xaa, 0x4d, 0x63, 0x30, 0xac, 0xa8, 0xb6, 0x9c, 0x7c, 0xc3, 0x71, 0xd6, 0x3b,
-	0x5e, 0xfa, 0x3d, 0x9e, 0x42, 0xf8, 0xcc, 0x64, 0x59, 0x8a, 0x58, 0xa4, 0x81, 0xea, 0x01, 0x25,
-	0x8c, 0x94, 0x69, 0x75, 0xfe, 0x78, 0x2f, 0x8f, 0x62, 0x91, 0x0e, 0x94, 0x47, 0x8c, 0x61, 0x3a,
-	0xa7, 0xae, 0x34, 0x94, 0xbf, 0x75, 0x15, 0xcb, 0x20, 0x16, 0xe9, 0x4c, 0xfd, 0x97, 0x36, 0xb7,
-	0x5b, 0x94, 0x83, 0x58, 0xa4, 0x91, 0xf2, 0x98, 0xfc, 0x08, 0x98, 0xbe, 0x5b, 0xae, 0x7d, 0xb6,
-	0x84, 0xd1, 0x76, 0x74, 0xe9, 0x91, 0xf2, 0x88, 0x37, 0x70, 0xf2, 0x41, 0x65, 0x91, 0x53, 0x53,
-	0x18, 0xfd, 0x5a, 0xac, 0x34, 0x35, 0x6d, 0xcd, 0xee, 0x97, 0x48, 0x1d, 0x5a, 0xe1, 0x05, 0x4c,
-	0x76, 0xbe, 0xc0, 0xf9, 0x76, 0x02, 0x9e, 0xc3, 0x78, 0x13, 0xac, 0x69, 0xcd, 0xee, 0xa9, 0x89,
-	0xfa, 0x63, 0x4c, 0x20, 0x7a, 0xc8, 0xb2, 0x79, 0xbb, 0x28, 0x8b, 0xe5, 0x13, 0x77, 0x32, 0x74,
-	0xc7, 0x7b, 0x1a, 0x5e, 0xc2, 0xcc, 0xfb, 0xfb, 0xb6, 0x86, 0xae, 0xad, 0x7d, 0xf1, 0x0e, 0x3e,
-	0xc7, 0xbe, 0xff, 0xc5, 0xd0, 0xf5, 0x7e, 0xfb, 0x1b, 0x00, 0x00, 0xff, 0xff, 0xcb, 0xb2, 0xc9,
-	0xf7, 0x92, 0x01, 0x00, 0x00,
+var File_messages_proto protoreflect.FileDescriptor
+
+var file_messages_proto_rawDesc = []byte{
+	0x0a, 0x0e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+	0x12, 0x05, 0x70, 0x61, 0x72, 0x73, 0x65, 0x22, 0x7c, 0x0a, 0x0e, 0x43, 0x68, 0x61, 0x6e, 0x6e,
+	0x65, 0x6c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x4c, 0x65, 0x61,
+	0x73, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x12,
+	0x18, 0x0a, 0x07, 0x52, 0x6f, 0x75, 0x6e, 0x64, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04,
+	0x52, 0x07, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x50, 0x61, 0x79,
+	0x6c, 0x6f, 0x61, 0x64, 0x54, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b,
+	0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x54, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x50,
+	0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x61,
+	0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0xdd, 0x01, 0x0a, 0x0b, 0x55, 0x73, 0x65, 0x72, 0x4d, 0x65,
+	0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
+	0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12,
+	0x30, 0x0a, 0x13, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x69, 0x67,
+	0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x13, 0x76, 0x61,
+	0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72,
+	0x65, 0x12, 0x1c, 0x0a, 0x09, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x03,
+	0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12,
+	0x1a, 0x0a, 0x08, 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28,
+	0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x45,
+	0x43, 0x43, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28,
+	0x0c, 0x52, 0x0c, 0x65, 0x43, 0x43, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12,
+	0x24, 0x0a, 0x0d, 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x4c, 0x65, 0x61, 0x73, 0x65,
+	0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65,
+	0x4c, 0x65, 0x61, 0x73, 0x65, 0x42, 0x0b, 0x5a, 0x09, 0x2f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65,
+	0x6c, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+}
+
+var (
+	file_messages_proto_rawDescOnce sync.Once
+	file_messages_proto_rawDescData = file_messages_proto_rawDesc
+)
+
+func file_messages_proto_rawDescGZIP() []byte {
+	file_messages_proto_rawDescOnce.Do(func() {
+		file_messages_proto_rawDescData = protoimpl.X.CompressGZIP(file_messages_proto_rawDescData)
+	})
+	return file_messages_proto_rawDescData
+}
+
+var file_messages_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
+var file_messages_proto_goTypes = []interface{}{
+	(*ChannelMessage)(nil), // 0: parse.ChannelMessage
+	(*UserMessage)(nil),    // 1: parse.UserMessage
+}
+var file_messages_proto_depIdxs = []int32{
+	0, // [0:0] is the sub-list for method output_type
+	0, // [0:0] is the sub-list for method input_type
+	0, // [0:0] is the sub-list for extension type_name
+	0, // [0:0] is the sub-list for extension extendee
+	0, // [0:0] is the sub-list for field type_name
+}
+
+func init() { file_messages_proto_init() }
+func file_messages_proto_init() {
+	if File_messages_proto != nil {
+		return
+	}
+	if !protoimpl.UnsafeEnabled {
+		file_messages_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*ChannelMessage); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_messages_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*UserMessage); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_messages_proto_rawDesc,
+			NumEnums:      0,
+			NumMessages:   2,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_messages_proto_goTypes,
+		DependencyIndexes: file_messages_proto_depIdxs,
+		MessageInfos:      file_messages_proto_msgTypes,
+	}.Build()
+	File_messages_proto = out.File
+	file_messages_proto_rawDesc = nil
+	file_messages_proto_goTypes = nil
+	file_messages_proto_depIdxs = nil
 }
diff --git a/channels/messages.proto b/channels/messages.proto
index d1f4d41626d19b04170d53d26faee0a988f1c737..171f359e971094e8ba63cc1685fa184c4b2498ea 100644
--- a/channels/messages.proto
+++ b/channels/messages.proto
@@ -8,7 +8,7 @@
 syntax = "proto3";
 
 package parse;
-option go_package = "channels";
+option go_package = "/channels";
 
 // ChannelMessage is transmitted by the channel. Effectively it is
 // a command for the channel sent by a user with admin access of the channel.
diff --git a/channels/send.go b/channels/send.go
new file mode 100644
index 0000000000000000000000000000000000000000..d72c017e84aa8688cf59f7a04353b0f96da67b06
--- /dev/null
+++ b/channels/send.go
@@ -0,0 +1 @@
+package channels
diff --git a/channels/text.pb.go b/channels/text.pb.go
new file mode 100644
index 0000000000000000000000000000000000000000..ebada41edb208d4e5534f55a96831dedd407bd06
--- /dev/null
+++ b/channels/text.pb.go
@@ -0,0 +1,252 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright © 2020 xx network SEZC                                           //
+//                                                                            //
+// Use of this source code is governed by a license that can be found in the  //
+// LICENSE file                                                               //
+////////////////////////////////////////////////////////////////////////////////
+
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.26.0
+// 	protoc        (unknown)
+// source: text.proto
+
+package channels
+
+import (
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	reflect "reflect"
+	sync "sync"
+)
+
+const (
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+type CMIXChannelText struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Version        uint32 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"`
+	Text           string `protobuf:"bytes,2,opt,name=text,proto3" json:"text,omitempty"`
+	ReplyMessageID []byte `protobuf:"bytes,3,opt,name=replyMessageID,proto3" json:"replyMessageID,omitempty"`
+}
+
+func (x *CMIXChannelText) Reset() {
+	*x = CMIXChannelText{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_text_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *CMIXChannelText) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*CMIXChannelText) ProtoMessage() {}
+
+func (x *CMIXChannelText) ProtoReflect() protoreflect.Message {
+	mi := &file_text_proto_msgTypes[0]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use CMIXChannelText.ProtoReflect.Descriptor instead.
+func (*CMIXChannelText) Descriptor() ([]byte, []int) {
+	return file_text_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *CMIXChannelText) GetVersion() uint32 {
+	if x != nil {
+		return x.Version
+	}
+	return 0
+}
+
+func (x *CMIXChannelText) GetText() string {
+	if x != nil {
+		return x.Text
+	}
+	return ""
+}
+
+func (x *CMIXChannelText) GetReplyMessageID() []byte {
+	if x != nil {
+		return x.ReplyMessageID
+	}
+	return nil
+}
+
+type CMIXChannelReaction struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Version           uint32 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"`
+	Reaction          uint32 `protobuf:"varint,2,opt,name=reaction,proto3" json:"reaction,omitempty"`
+	ReactionMessageID []byte `protobuf:"bytes,3,opt,name=reactionMessageID,proto3" json:"reactionMessageID,omitempty"`
+}
+
+func (x *CMIXChannelReaction) Reset() {
+	*x = CMIXChannelReaction{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_text_proto_msgTypes[1]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *CMIXChannelReaction) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*CMIXChannelReaction) ProtoMessage() {}
+
+func (x *CMIXChannelReaction) ProtoReflect() protoreflect.Message {
+	mi := &file_text_proto_msgTypes[1]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use CMIXChannelReaction.ProtoReflect.Descriptor instead.
+func (*CMIXChannelReaction) Descriptor() ([]byte, []int) {
+	return file_text_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *CMIXChannelReaction) GetVersion() uint32 {
+	if x != nil {
+		return x.Version
+	}
+	return 0
+}
+
+func (x *CMIXChannelReaction) GetReaction() uint32 {
+	if x != nil {
+		return x.Reaction
+	}
+	return 0
+}
+
+func (x *CMIXChannelReaction) GetReactionMessageID() []byte {
+	if x != nil {
+		return x.ReactionMessageID
+	}
+	return nil
+}
+
+var File_text_proto protoreflect.FileDescriptor
+
+var file_text_proto_rawDesc = []byte{
+	0x0a, 0x0a, 0x74, 0x65, 0x78, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x05, 0x70, 0x61,
+	0x72, 0x73, 0x65, 0x22, 0x67, 0x0a, 0x0f, 0x43, 0x4d, 0x49, 0x58, 0x43, 0x68, 0x61, 0x6e, 0x6e,
+	0x65, 0x6c, 0x54, 0x65, 0x78, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f,
+	0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,
+	0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x78, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
+	0x74, 0x65, 0x78, 0x74, 0x12, 0x26, 0x0a, 0x0e, 0x72, 0x65, 0x70, 0x6c, 0x79, 0x4d, 0x65, 0x73,
+	0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x72, 0x65,
+	0x70, 0x6c, 0x79, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x22, 0x79, 0x0a, 0x13,
+	0x43, 0x4d, 0x49, 0x58, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x61, 0x63, 0x74,
+	0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01,
+	0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a,
+	0x08, 0x72, 0x65, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52,
+	0x08, 0x72, 0x65, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2c, 0x0a, 0x11, 0x72, 0x65, 0x61,
+	0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x18, 0x03,
+	0x20, 0x01, 0x28, 0x0c, 0x52, 0x11, 0x72, 0x65, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65,
+	0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x44, 0x42, 0x0b, 0x5a, 0x09, 0x2f, 0x63, 0x68, 0x61, 0x6e,
+	0x6e, 0x65, 0x6c, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+}
+
+var (
+	file_text_proto_rawDescOnce sync.Once
+	file_text_proto_rawDescData = file_text_proto_rawDesc
+)
+
+func file_text_proto_rawDescGZIP() []byte {
+	file_text_proto_rawDescOnce.Do(func() {
+		file_text_proto_rawDescData = protoimpl.X.CompressGZIP(file_text_proto_rawDescData)
+	})
+	return file_text_proto_rawDescData
+}
+
+var file_text_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
+var file_text_proto_goTypes = []interface{}{
+	(*CMIXChannelText)(nil),     // 0: parse.CMIXChannelText
+	(*CMIXChannelReaction)(nil), // 1: parse.CMIXChannelReaction
+}
+var file_text_proto_depIdxs = []int32{
+	0, // [0:0] is the sub-list for method output_type
+	0, // [0:0] is the sub-list for method input_type
+	0, // [0:0] is the sub-list for extension type_name
+	0, // [0:0] is the sub-list for extension extendee
+	0, // [0:0] is the sub-list for field type_name
+}
+
+func init() { file_text_proto_init() }
+func file_text_proto_init() {
+	if File_text_proto != nil {
+		return
+	}
+	if !protoimpl.UnsafeEnabled {
+		file_text_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*CMIXChannelText); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_text_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*CMIXChannelReaction); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_text_proto_rawDesc,
+			NumEnums:      0,
+			NumMessages:   2,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_text_proto_goTypes,
+		DependencyIndexes: file_text_proto_depIdxs,
+		MessageInfos:      file_text_proto_msgTypes,
+	}.Build()
+	File_text_proto = out.File
+	file_text_proto_rawDesc = nil
+	file_text_proto_goTypes = nil
+	file_text_proto_depIdxs = nil
+}
diff --git a/channels/text.proto b/channels/text.proto
new file mode 100644
index 0000000000000000000000000000000000000000..4e1ad86791428f0d64e92b5bbb1d35d7b02567c4
--- /dev/null
+++ b/channels/text.proto
@@ -0,0 +1,23 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright © 2020 xx network SEZC                                           //
+//                                                                            //
+// Use of this source code is governed by a license that can be found in the  //
+// LICENSE file                                                               //
+////////////////////////////////////////////////////////////////////////////////
+
+syntax = "proto3";
+
+package parse;
+option go_package = "/channels";
+
+message CMIXChannelText {
+  uint32 version = 1;
+  string text = 2;
+  bytes replyMessageID = 3;
+}
+
+message CMIXChannelReaction {
+  uint32 version = 1;
+  uint32 reaction = 2;
+  bytes reactionMessageID = 3;
+}
\ No newline at end of file
diff --git a/channels/genericUserListener.go b/channels/userListener.go
similarity index 94%
rename from channels/genericUserListener.go
rename to channels/userListener.go
index d6fa707ffcd7d2f67c3af28206fc70a9a3b2b3cb..20a3eb1499904dba9fddf310ef4033240c3d73e8 100644
--- a/channels/genericUserListener.go
+++ b/channels/userListener.go
@@ -11,13 +11,13 @@ import (
 	"time"
 )
 
-type genericUserListener struct {
+type userListener struct {
 	name   NameService
 	events *events
 	chID   *id.ID
 }
 
-func (gul *genericUserListener) Listen(payload []byte,
+func (gul *userListener) Listen(payload []byte,
 	receptionID receptionID.EphemeralIdentity, round rounds.Round) {
 
 	//Remove the padding
@@ -80,7 +80,7 @@ func (gul *genericUserListener) Listen(payload []byte,
 	//TODO: Processing of the message relative to admin commands will be here
 
 	//Submit the message to the event model for listening
-	gul.events.hear(gul.chID, umi, receptionID, round)
+	gul.events.triggerEvent(gul.chID, umi, receptionID, round)
 
 	return
 }