channels

package
v4.6.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 20, 2023 License: BSD-2-Clause, BSD-2-Clause Imports: 45 Imported by: 3

README

Channels provide a channels implementation on top of broadcast which is capable of handing the user facing features of channels, including replies, reactions, and eventually admin commands.

on sending, data propagates as follows:

Send function (Example: SendMessage) - > SendGeneric ->
Broadcast.BroadcastWithAssembler -> cmix.SendWithAssembler

on receiving messages propagate as follows:

cmix message pickup (by service)- > broadcast.Processor ->
userListener ->  events.triggerEvent ->
messageTypeHandler (example: Text) ->
eventModel (example: ReceiveMessage)

on sendingAdmin, data propagates as follows:

Send function - > SendAdminGeneric ->
Broadcast.BroadcastAsymmetricWithAssembler -> cmix.SendWithAssembler

on receiving admin messages propagate as follows:

cmix message pickup (by service)- > broadcast.Processor -> adminListener ->
events.triggerAdminEvent -> messageTypeHandler (example: Text) ->
eventModel (example: ReceiveMessage)

Documentation

Overview

Package channels provides a channels implementation on top of broadcast which is capable of handing the user facing features of channels, including replies, reactions, and eventually admin commands.

Index

Constants

View Source
const (

	// SendMessageTag is the base tag used when generating a debug tag for
	// sending a message.
	SendMessageTag = "ChMessage"

	// SendReplyTag is the base tag used when generating a debug tag for
	// sending a reply.
	SendReplyTag = "ChReply"

	// SendReactionTag is the base tag used when generating a debug tag for
	// sending a reaction.
	SendReactionTag = "ChReaction"

	// SendDeleteTag is the base tag used when generating a debug tag for a
	// delete message.
	SendDeleteTag = "ChDelete"

	// SendPinnedTag is the base tag used when generating a debug tag for a
	// pinned message.
	SendPinnedTag = "ChPinned"

	// SendMuteTag is the base tag used when generating a debug tag for a mute
	// message.
	SendMuteTag = "ChMute"

	// SendAdminReplayTag is the base tag used when generating a debug tag for an
	// admin replay message.
	SendAdminReplayTag = "ChAdminReplay"
)
View Source
const AdminUsername = "Admin"

AdminUsername defines the displayed username of admin messages, which are unique users for every channel defined by the channel's private key.

View Source
const (

	// MessageLife is how long a message is available from the network before it
	// expires from the network and is irretrievable from the gateways.
	MessageLife = 500 * time.Hour
)

Variables

View Source
var (
	// ChannelAlreadyExistsErr is returned when attempting to join a channel
	// that the user is already in.
	ChannelAlreadyExistsErr = errors.New(
		"the channel cannot be added because it already exists")

	// ChannelDoesNotExistsErr is returned when a channel does not exist.
	ChannelDoesNotExistsErr = errors.New("the channel cannot be found")

	// MessageTooLongErr is returned when attempting to send a message that is
	// too large.
	MessageTooLongErr = errors.New("the passed message is too long")

	// WrongPrivateKeyErr is returned when the private key does not match the
	// channel's public key.
	WrongPrivateKeyErr = errors.New(
		"the passed private key does not match the channel")

	// WrongPasswordErr is returned when the encrypted packet could not be
	// decrypted using the supplied password.
	WrongPasswordErr = errors.New(
		"incorrect password")

	// MessageTypeAlreadyRegistered is returned if a handler has already been
	// registered with the supplied message type. Only one handler can be
	// registered per type.
	MessageTypeAlreadyRegistered = errors.New(
		"the given message type has already been registered")
)
View Source
var AdminFakePubKey = ed25519.PublicKey{}

AdminFakePubKey is the placeholder for the Ed25519 public key used when the admin trigger calls a message handler.

View Source
var File_channelMessages_proto protoreflect.FileDescriptor
View Source
var NotAnAdminErr = errors.New("user not a member of the channel")

NotAnAdminErr is returned if the user is attempting to do an admin command while not being an admin.

View Source
var ValidForever = time.Duration(math.MaxInt64)

ValidForever is used as a validUntil lease when sending to denote the message or operation never expires.

Note: A message relay must be present to enforce this otherwise things expire after 3 weeks due to network retention.

Functions

func IsNicknameValid

func IsNicknameValid(nick string) error

IsNicknameValid checks if a nickname is valid.

Rules:

  • A nickname must not be longer than 24 characters.
  • A nickname must not be shorter than 1 character.

TODO: Add character filtering.

func LoadOrNewNicknameManager added in v4.4.4

func LoadOrNewNicknameManager(kv *versioned.KV) *nicknameManager

LoadOrNewNicknameManager returns the stored nickname manager if there is one or returns a new one.

Types

type ActionLeaseList added in v4.4.4

type ActionLeaseList struct {
	// contains filtered or unexported fields
}

ActionLeaseList keeps a list of messages and actions and undoes each action when its lease is up.

func NewActionLeaseList added in v4.4.4

func NewActionLeaseList(triggerFn triggerActionEventFunc, store *CommandStore,
	kv *versioned.KV, rng *fastRNG.StreamGenerator) *ActionLeaseList

NewActionLeaseList initialises a new empty ActionLeaseList.

func NewOrLoadActionLeaseList added in v4.4.4

func NewOrLoadActionLeaseList(triggerFn triggerActionEventFunc,
	store *CommandStore, kv *versioned.KV, rng *fastRNG.StreamGenerator) (
	*ActionLeaseList, error)

NewOrLoadActionLeaseList loads an existing ActionLeaseList from storage, if it exists. Otherwise, it initialises a new empty ActionLeaseList.

func (*ActionLeaseList) AddMessage added in v4.4.4

func (all *ActionLeaseList) AddMessage(channelID *id.ID, messageID message.ID,
	action MessageType, unsanitizedPayload, sanitizedPayload,
	encryptedPayload []byte, timestamp, originatingTimestamp time.Time,
	lease time.Duration, originatingRound id.Round, round rounds.Round,
	fromAdmin bool) error

AddMessage triggers the lease message for insertion. An error is returned if the message should be dropped. A message is dropped if its lease has expired already or if it is older than an already stored replay for the command.

func (*ActionLeaseList) AddOrOverwrite added in v4.4.4

func (all *ActionLeaseList) AddOrOverwrite(channelID *id.ID, action MessageType,
	payload []byte) error

AddOrOverwrite adds a new lease or overwrites an existing lease to trigger a replay soon (between 3 and 10 minutes).

func (*ActionLeaseList) RegisterReplayFn added in v4.4.4

func (all *ActionLeaseList) RegisterReplayFn(replayFn ReplayActionFunc)

RegisterReplayFn registers the function that is called to replay an action.

func (*ActionLeaseList) RemoveChannel added in v4.4.4

func (all *ActionLeaseList) RemoveChannel(channelID *id.ID)

RemoveChannel triggers all leases for the channel for removal.

func (*ActionLeaseList) RemoveMessage added in v4.4.4

func (all *ActionLeaseList) RemoveMessage(channelID *id.ID,
	messageID message.ID, action MessageType, unsanitizedPayload,
	sanitizedPayload, encryptedPayload []byte, timestamp,
	originatingTimestamp time.Time, lease time.Duration,
	originatingRound id.Round, round rounds.Round, fromAdmin bool) error

RemoveMessage triggers the lease message for removal. An error is returned if the message should be dropped. A message is dropped if its lease has expired already or if it is older than an already stored replay for the command.

func (*ActionLeaseList) StartProcesses added in v4.4.4

func (all *ActionLeaseList) StartProcesses() (stoppable.Stoppable, error)

StartProcesses starts the thread that checks for expired action leases and undoes the action. This function adheres to the xxdk.Service type.

This function always returns a nil error.

type AddServiceFn added in v4.4.4

type AddServiceFn func(sp xxdk.Service) error

AddServiceFn adds a service to be controlled by the client thread control. These will be started and stopped with the network follower.

This type must match [Cmix.AddService].

type CMIXChannelDelete added in v4.4.4

type CMIXChannelDelete struct {
	Version   uint32 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"`
	MessageID []byte `protobuf:"bytes,2,opt,name=messageID,proto3" json:"messageID,omitempty"` // The [channel.MessageID] of the message to delete
	// contains filtered or unexported fields
}

CMIXChannelDelete is the payload for a Delete MessageType. It deletes the message with the messageID from storage.

func (*CMIXChannelDelete) Descriptor deprecated added in v4.4.4

func (*CMIXChannelDelete) Descriptor() ([]byte, []int)

Deprecated: Use CMIXChannelDelete.ProtoReflect.Descriptor instead.

func (*CMIXChannelDelete) GetMessageID added in v4.4.4

func (x *CMIXChannelDelete) GetMessageID() []byte

func (*CMIXChannelDelete) GetVersion added in v4.4.4

func (x *CMIXChannelDelete) GetVersion() uint32

func (*CMIXChannelDelete) ProtoMessage added in v4.4.4

func (*CMIXChannelDelete) ProtoMessage()

func (*CMIXChannelDelete) ProtoReflect added in v4.4.4

func (x *CMIXChannelDelete) ProtoReflect() protoreflect.Message

func (*CMIXChannelDelete) Reset added in v4.4.4

func (x *CMIXChannelDelete) Reset()

func (*CMIXChannelDelete) String added in v4.4.4

func (x *CMIXChannelDelete) String() string

type CMIXChannelMute added in v4.4.4

type CMIXChannelMute struct {
	Version    uint32 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"`
	PubKey     []byte `protobuf:"bytes,2,opt,name=pubKey,proto3" json:"pubKey,omitempty"`          // The [ed25519.PublicKey] of the user to mute
	UndoAction bool   `protobuf:"varint,3,opt,name=undoAction,proto3" json:"undoAction,omitempty"` // If true, the user is un-muted
	// contains filtered or unexported fields
}

CMIXChannelMute is the payload for a Mute MessageType. It mutes a specific user so all future messages from them will be dropped when received. It also prevents the user from sending messages.

func (*CMIXChannelMute) Descriptor deprecated added in v4.4.4

func (*CMIXChannelMute) Descriptor() ([]byte, []int)

Deprecated: Use CMIXChannelMute.ProtoReflect.Descriptor instead.

func (*CMIXChannelMute) GetPubKey added in v4.4.4

func (x *CMIXChannelMute) GetPubKey() []byte

func (*CMIXChannelMute) GetUndoAction added in v4.4.4

func (x *CMIXChannelMute) GetUndoAction() bool

func (*CMIXChannelMute) GetVersion added in v4.4.4

func (x *CMIXChannelMute) GetVersion() uint32

func (*CMIXChannelMute) ProtoMessage added in v4.4.4

func (*CMIXChannelMute) ProtoMessage()

func (*CMIXChannelMute) ProtoReflect added in v4.4.4

func (x *CMIXChannelMute) ProtoReflect() protoreflect.Message

func (*CMIXChannelMute) Reset added in v4.4.4

func (x *CMIXChannelMute) Reset()

func (*CMIXChannelMute) String added in v4.4.4

func (x *CMIXChannelMute) String() string

type CMIXChannelPinned added in v4.4.4

type CMIXChannelPinned struct {
	Version    uint32 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"`
	MessageID  []byte `protobuf:"bytes,2,opt,name=messageID,proto3" json:"messageID,omitempty"`    // The [channel.MessageID] of the message to pin
	UndoAction bool   `protobuf:"varint,3,opt,name=undoAction,proto3" json:"undoAction,omitempty"` // If true, the message is unpinned
	// contains filtered or unexported fields
}

CMIXChannelPinned is the payload for a Pinned MessageType. It pins a specific message to a channel.

func (*CMIXChannelPinned) Descriptor deprecated added in v4.4.4

func (*CMIXChannelPinned) Descriptor() ([]byte, []int)

Deprecated: Use CMIXChannelPinned.ProtoReflect.Descriptor instead.

func (*CMIXChannelPinned) GetMessageID added in v4.4.4

func (x *CMIXChannelPinned) GetMessageID() []byte

func (*CMIXChannelPinned) GetUndoAction added in v4.4.4

func (x *CMIXChannelPinned) GetUndoAction() bool

func (*CMIXChannelPinned) GetVersion added in v4.4.4

func (x *CMIXChannelPinned) GetVersion() uint32

func (*CMIXChannelPinned) ProtoMessage added in v4.4.4

func (*CMIXChannelPinned) ProtoMessage()

func (*CMIXChannelPinned) ProtoReflect added in v4.4.4

func (x *CMIXChannelPinned) ProtoReflect() protoreflect.Message

func (*CMIXChannelPinned) Reset added in v4.4.4

func (x *CMIXChannelPinned) Reset()

func (*CMIXChannelPinned) String added in v4.4.4

func (x *CMIXChannelPinned) String() string

type CMIXChannelReaction

type CMIXChannelReaction struct {
	Version           uint32 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"`
	Reaction          string `protobuf:"bytes,2,opt,name=reaction,proto3" json:"reaction,omitempty"`
	ReactionMessageID []byte `protobuf:"bytes,3,opt,name=reactionMessageID,proto3" json:"reactionMessageID,omitempty"`
	// contains filtered or unexported fields
}

CMIXChannelReaction is the payload for reactions. The reaction must be a single emoji and the reactionMessageID must be non nil and a real message in the channel.

func (*CMIXChannelReaction) Descriptor deprecated

func (*CMIXChannelReaction) Descriptor() ([]byte, []int)

Deprecated: Use CMIXChannelReaction.ProtoReflect.Descriptor instead.

func (*CMIXChannelReaction) GetReaction

func (x *CMIXChannelReaction) GetReaction() string

func (*CMIXChannelReaction) GetReactionMessageID

func (x *CMIXChannelReaction) GetReactionMessageID() []byte

func (*CMIXChannelReaction) GetVersion

func (x *CMIXChannelReaction) GetVersion() uint32

func (*CMIXChannelReaction) ProtoMessage

func (*CMIXChannelReaction) ProtoMessage()

func (*CMIXChannelReaction) ProtoReflect

func (x *CMIXChannelReaction) ProtoReflect() protoreflect.Message

func (*CMIXChannelReaction) Reset

func (x *CMIXChannelReaction) Reset()

func (*CMIXChannelReaction) String

func (x *CMIXChannelReaction) String() string

type CMIXChannelText

type CMIXChannelText struct {
	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"`
	// contains filtered or unexported fields
}

CMIXChannelText is the payload for sending normal text messages to channels the replyMessageID is nil when it is not a reply.

func (*CMIXChannelText) Descriptor deprecated

func (*CMIXChannelText) Descriptor() ([]byte, []int)

Deprecated: Use CMIXChannelText.ProtoReflect.Descriptor instead.

func (*CMIXChannelText) GetReplyMessageID

func (x *CMIXChannelText) GetReplyMessageID() []byte

func (*CMIXChannelText) GetText

func (x *CMIXChannelText) GetText() string

func (*CMIXChannelText) GetVersion

func (x *CMIXChannelText) GetVersion() uint32

func (*CMIXChannelText) ProtoMessage

func (*CMIXChannelText) ProtoMessage()

func (*CMIXChannelText) ProtoReflect

func (x *CMIXChannelText) ProtoReflect() protoreflect.Message

func (*CMIXChannelText) Reset

func (x *CMIXChannelText) Reset()

func (*CMIXChannelText) String

func (x *CMIXChannelText) String() string

type ChannelMessage

type ChannelMessage struct {

	// Lease is the length that this channel message will take effect.
	Lease int64 `protobuf:"varint,1,opt,name=Lease,proto3" json:"Lease,omitempty"`
	// The round this message was sent on.
	RoundID uint64 `protobuf:"varint,2,opt,name=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"`
	// 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"`
	// nickname is the name which the user is using for this message it will not
	// be longer than 24 characters.
	Nickname string `protobuf:"bytes,5,opt,name=Nickname,proto3" json:"Nickname,omitempty"`
	// Nonce is 32 bits of randomness to ensure that two messages in the same
	// round with that have the same nickname, payload, and lease will not have
	// the same message ID.
	Nonce []byte `protobuf:"bytes,6,opt,name=Nonce,proto3" json:"Nonce,omitempty"`
	// LocalTimestamp is the timestamp when the "send call" is made based upon
	// the local clock. If this differs by more than 5 seconds +/- from when the
	// round it sent on is queued, then a random mutation on the queued time
	// (+/- 200ms) will be used by local clients instead.
	LocalTimestamp int64  `protobuf:"varint,7,opt,name=LocalTimestamp,proto3" json:"LocalTimestamp,omitempty"`
	DMToken        uint32 `protobuf:"varint,8,opt,name=DMToken,proto3" json:"DMToken,omitempty"` // hash of private key
	// contains filtered or unexported fields
}

ChannelMessage is transmitted by the channel. Effectively it is a command for the channel sent by a user with admin access of the channel.

func (*ChannelMessage) Descriptor deprecated

func (*ChannelMessage) Descriptor() ([]byte, []int)

Deprecated: Use ChannelMessage.ProtoReflect.Descriptor instead.

func (*ChannelMessage) GetDMToken added in v4.4.4

func (x *ChannelMessage) GetDMToken() uint32

func (*ChannelMessage) GetLease

func (x *ChannelMessage) GetLease() int64

func (*ChannelMessage) GetLocalTimestamp

func (x *ChannelMessage) GetLocalTimestamp() int64

func (*ChannelMessage) GetNickname

func (x *ChannelMessage) GetNickname() string

func (*ChannelMessage) GetNonce

func (x *ChannelMessage) GetNonce() []byte

func (*ChannelMessage) GetPayload

func (x *ChannelMessage) GetPayload() []byte

func (*ChannelMessage) GetPayloadType

func (x *ChannelMessage) GetPayloadType() uint32

func (*ChannelMessage) GetRoundID

func (x *ChannelMessage) GetRoundID() uint64

func (*ChannelMessage) ProtoMessage

func (*ChannelMessage) ProtoMessage()

func (*ChannelMessage) ProtoReflect

func (x *ChannelMessage) ProtoReflect() protoreflect.Message

func (*ChannelMessage) Reset

func (x *ChannelMessage) Reset()

func (*ChannelMessage) String

func (x *ChannelMessage) String() string

type Client

type Client interface {
	GetMaxMessageLength() int
	SendWithAssembler(recipient *id.ID, assembler cmix.MessageAssembler,
		cmixParams cmix.CMIXParams) (rounds.Round, ephemeral.Id, error)
	IsHealthy() bool
	AddIdentity(id *id.ID, validUntil time.Time, persistent bool,
		fallthroughProcessor message.Processor)
	AddIdentityWithHistory(
		id *id.ID, validUntil, beginning time.Time,
		persistent bool, fallthroughProcessor message.Processor)
	AddService(clientID *id.ID, newService message.Service,
		response message.Processor)
	DeleteClientService(clientID *id.ID)
	RemoveIdentity(id *id.ID)
	GetRoundResults(timeout time.Duration, roundCallback cmix.RoundEventCallback,
		roundList ...id.Round)
	AddHealthCallback(f func(bool)) uint64
	RemoveHealthCallback(uint64)
}

Client contains the methods from cmix.Client that are required by the Manager.

type CommandMessage added in v4.4.4

type CommandMessage struct {
	// ChannelID is the ID of the channel.
	ChannelID *id.ID `json:"channelID"`

	// MessageID is the ID of the message.
	MessageID message.ID `json:"messageID"`

	// MessageType is the Type of channel message.
	MessageType MessageType `json:"messageType"`

	// Nickname is the nickname of the sender.
	Nickname string `json:"nickname"`

	// Content is the message contents. In most cases, this is the various
	// marshalled proto messages (e.g., channels.CMIXChannelText and
	// channels.CMIXChannelDelete).
	Content []byte `json:"content"`

	// EncryptedPayload is the encrypted contents of the received format.Message
	// (with its outer layer of encryption removed). This is the encrypted
	// channels.ChannelMessage.
	EncryptedPayload []byte `json:"encryptedPayload"`

	// PubKey is the Ed25519 public key of the sender.
	PubKey ed25519.PublicKey `json:"pubKey"`

	// Codeset is the codeset version.
	Codeset uint8 `json:"codeset"`

	// Timestamp is the time that the round was queued. It is set by the
	// listener to be either ChannelMessage.LocalTimestamp or the timestamp for
	// states.QUEUED of the round it was sent on, if that is significantly later
	// than OriginatingTimestamp. If the message is a replay, then Timestamp
	// will
	// always be the queued time of the round.
	Timestamp time.Time `json:"timestamp"`

	// OriginatingTimestamp is the time the sender queued the message for
	// sending on their client.
	OriginatingTimestamp time.Time `json:"originatingTimestamp"`

	// Lease is how long the message should persist.
	Lease time.Duration `json:"lease"`

	// OriginatingRound is the ID of the round the message was originally sent
	// on.
	OriginatingRound id.Round `json:"originatingRound"`

	// Round is the information about the round the message was sent on. For
	// replay messages, this is the round of the most recent replay, not the
	// round of the original message.
	Round rounds.Round `json:"round"`

	// Status is the current status of the message. It is set to Delivered by
	// the listener.
	Status SentStatus `json:"status"`

	// FromAdmin indicates if the message came from the channel admin.
	FromAdmin bool `json:"fromAdmin"`

	// UserMuted indicates if the sender of the message is muted.
	UserMuted bool `json:"userMuted"`
}

CommandMessage contains all the information about a command channel message that will be saved to storage

type CommandStore added in v4.4.4

type CommandStore struct {
	// contains filtered or unexported fields
}

CommandStore stores message information about channel commands in storage. Each message

func NewCommandStore added in v4.4.4

func NewCommandStore(kv *versioned.KV) *CommandStore

NewCommandStore initialises a new message CommandStore object with a prefixed KV.

func (*CommandStore) DeleteCommand added in v4.4.4

func (cs *CommandStore) DeleteCommand(
	channelID *id.ID, messageType MessageType, content []byte) error

DeleteCommand deletes the command message from storage.

func (*CommandStore) LoadCommand added in v4.4.4

func (cs *CommandStore) LoadCommand(channelID *id.ID,
	messageType MessageType, content []byte) (*CommandMessage, error)

LoadCommand loads the command message from storage.

func (*CommandStore) SaveCommand added in v4.4.4

func (cs *CommandStore) SaveCommand(channelID *id.ID, messageID message.ID,
	messageType MessageType, nickname string, content, encryptedPayload []byte,
	pubKey ed25519.PublicKey, codeset uint8, timestamp,
	originatingTimestamp time.Time, lease time.Duration,
	originatingRound id.Round, round rounds.Round, status SentStatus, fromAdmin,
	userMuted bool) error

SaveCommand stores the command message and its data to storage.

type EventModel

type EventModel interface {
	// JoinChannel is called whenever a channel is joined locally.
	JoinChannel(channel *cryptoBroadcast.Channel)

	// LeaveChannel is called whenever a channel is left locally.
	LeaveChannel(channelID *id.ID)

	// ReceiveMessage is called whenever a message is received on a given
	// channel. It may be called multiple times on the same message. It is
	// incumbent on the user of the API to filter such called by message ID.
	//
	// The API needs to return a UUID of the message that can be referenced at a
	// later time.
	//
	// messageID, timestamp, and round are all nillable and may be updated based
	// upon the UUID at a later date. A time of time.Time{} will be passed for a
	// nilled timestamp.
	//
	// nickname may be empty, in which case the UI is expected to display the
	// codename.
	//
	// messageType type is included in the call; it will always be Text (1) for
	// this call, but it may be required in downstream databases.
	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,
		messageType MessageType, status SentStatus, hidden bool) uint64

	// ReceiveReply is called whenever a message is received that is a reply on
	// a given channel. It may be called multiple times on the same message. It
	// is incumbent on the user of the API to filter such called by message ID.
	//
	// Messages may arrive our of order, so a reply, in theory, can arrive
	// before the initial message. As a result, it may be important to buffer
	// replies.
	//
	// The API needs to return a UUID of the message that can be referenced at a
	// later time.
	//
	// messageID, timestamp, and round are all nillable and may be updated based
	// upon the UUID at a later date. A time of time.Time{} will be passed for a
	// nilled timestamp.
	//
	// nickname may be empty, in which case the UI is expected to display the
	// codename.
	//
	// messageType type is included in the call; it will always be Text (1) for
	// this call, but it may be required in downstream databases.
	ReceiveReply(channelID *id.ID, messageID, reactionTo message.ID, nickname,
		text string, pubKey ed25519.PublicKey, dmToken uint32, codeset uint8,
		timestamp time.Time, lease time.Duration, round rounds.Round,
		messageType MessageType, status SentStatus, hidden bool) uint64

	// ReceiveReaction is called whenever a reaction to a message is received on
	// a given channel. It may be called multiple times on the same reaction. It
	// is incumbent on the user of the API to filter such called by message ID.
	//
	// Messages may arrive our of order, so a reply, in theory, can arrive
	// before the initial message. As a result, it may be important to buffer
	// replies.
	//
	// The API needs to return a UUID of the message that can be referenced at a
	// later time.
	//
	// messageID, timestamp, and round are all nillable and may be updated based
	// upon the UUID at a later date. A time of time.Time{} will be passed for a
	// nilled timestamp.
	//
	// nickname may be empty, in which case the UI is expected to display the
	// codename.
	//
	// messageType type is included in the call; it will always be Text (1) for
	// this call, but it may be required in downstream databases.
	ReceiveReaction(channelID *id.ID, messageID, reactionTo message.ID,
		nickname, reaction string, pubKey ed25519.PublicKey, dmToken uint32,
		codeset uint8, timestamp time.Time, lease time.Duration,
		round rounds.Round, messageType MessageType, status SentStatus,
		hidden bool) uint64

	// UpdateFromUUID is called whenever a message at the UUID is modified.
	//
	// messageID, timestamp, round, pinned, hidden, and status are all nillable
	// and may be updated based upon the UUID at a later date. If a nil value is
	// passed, then make no update.
	UpdateFromUUID(uuid uint64, messageID *message.ID, timestamp *time.Time,
		round *rounds.Round, pinned, hidden *bool, status *SentStatus)

	// UpdateFromMessageID is called whenever a message with the message ID is
	// modified.
	//
	// The API needs to return the UUID of the modified message that can be
	// referenced at a later time.
	//
	// timestamp, round, pinned, hidden, and status are all nillable and may be
	// updated based upon the UUID at a later date. If a nil value is passed,
	// then make no update.
	UpdateFromMessageID(messageID message.ID, timestamp *time.Time,
		round *rounds.Round, pinned, hidden *bool, status *SentStatus) uint64

	// GetMessage returns the message with the given channel.MessageID.
	GetMessage(messageID message.ID) (ModelMessage, error)

	// DeleteMessage deletes the message with the given [channel.MessageID] from
	// the database.
	DeleteMessage(messageID message.ID) error

	// MuteUser is called whenever a user is muted or unmuted.
	MuteUser(channelID *id.ID, pubKey ed25519.PublicKey, unmute bool)
}

EventModel is an interface which an external party which uses the channels system passed an object which adheres to in order to get events on the channel.

type EventModelBuilder

type EventModelBuilder func(path string) (EventModel, error)

EventModelBuilder initialises the event model using the given path.

type Manager

type Manager interface {

	// GenerateChannel creates a new channel with the user as the admin and
	// returns the broadcast.Channel object. This function only create a channel
	// and does not join it.
	//
	// The private key is saved to storage and can be accessed with
	// ExportChannelAdminKey.
	//
	// Parameters:
	//   - name - The name of the new channel. The name must be between 3 and 24
	//     characters inclusive. It can only include upper and lowercase Unicode
	//     letters, digits 0 through 9, and underscores (_). It cannot be
	//     changed once a channel is created.
	//   - description - The description of a channel. The description is
	//     optional but cannot be longer than 144 characters and can include all
	//     Unicode characters. It cannot be changed once a channel is created.
	//   - privacyLevel - The broadcast.PrivacyLevel of the channel.
	GenerateChannel(
		name, description string, privacyLevel cryptoBroadcast.PrivacyLevel) (
		*cryptoBroadcast.Channel, error)

	// JoinChannel joins the given channel. It will return the error
	// ChannelAlreadyExistsErr if the channel has already been joined.
	JoinChannel(channel *cryptoBroadcast.Channel) error

	// LeaveChannel leaves the given channel. It will return the error
	// ChannelDoesNotExistsErr if the channel was not previously joined.
	LeaveChannel(channelID *id.ID) error

	// EnableDirectMessages enables the token for direct messaging for this
	// channel.
	EnableDirectMessages(chId *id.ID) error

	// DisableDirectMessages removes the token for direct messaging for a
	// given channel.
	DisableDirectMessages(chId *id.ID) error

	// AreDMsEnabled returns the status of DMs for a given channel;
	// returns true if DMs are enabled.
	AreDMsEnabled(chId *id.ID) bool

	// ReplayChannel replays all messages from the channel within the network's
	// memory (~3 weeks) over the event model. It does this by wiping the
	// underlying state tracking for message pickup for the channel, causing all
	// messages to be re-retrieved from the network.
	//
	// Returns the error ChannelDoesNotExistsErr if the channel was not
	// previously joined.
	ReplayChannel(channelID *id.ID) error

	// GetChannels returns the IDs of all channels that have been joined.
	GetChannels() []*id.ID

	// GetChannel returns the underlying cryptographic structure for a given
	// channel.
	//
	// Returns the error ChannelDoesNotExistsErr if the channel was not
	// previously joined.
	GetChannel(channelID *id.ID) (*cryptoBroadcast.Channel, error)

	// SendGeneric is used to send a raw message over a channel. In general, it
	// should be wrapped in a function that defines the wire protocol.
	//
	// If the final message, before being sent over the wire, is too long, this
	// will return an error. Due to the underlying encoding using compression,
	// it is not possible to define the largest payload that can be sent, but it
	// will always be possible to send a payload of 802 bytes at minimum.
	//
	// The meaning of validUntil depends on the use case.
	//
	// Set tracked to true if the message should be tracked in the sendTracker,
	// which allows messages to be shown locally before they are received on the
	// network. In general, all messages that will be displayed to the user
	// should be tracked while all actions should not be. More technically, any
	// messageType that corresponds to a handler that does not return a unique
	// ID (i.e., always returns 0) cannot be tracked, or it will cause errors.
	SendGeneric(channelID *id.ID, messageType MessageType, msg []byte,
		validUntil time.Duration, tracked bool, params cmix.CMIXParams) (
		message.ID, rounds.Round, ephemeral.Id, error)
	// SendMessage is used to send a formatted message over a channel.
	//
	// Due to the underlying encoding using compression, it is not possible to
	// define the largest payload that can be sent, but it will always be
	// possible to send a payload of 798 bytes at minimum.
	//
	// The message will auto delete validUntil after the round it is sent in,
	// lasting forever if ValidForever is used.
	SendMessage(channelID *id.ID, msg string, validUntil time.Duration,
		params cmix.CMIXParams) (
		message.ID, rounds.Round, ephemeral.Id, error)

	// SendReply is used to send a formatted message over a channel.
	//
	// Due to the underlying encoding using compression, it is not possible to
	// define the largest payload that can be sent, but it will always be
	// possible to send a payload of 766 bytes at minimum.
	//
	// If the message ID that the reply is sent to does not exist, then the
	// other side will post the message as a normal message and not as a reply.
	//
	// The message will auto delete validUntil after the round it is sent in,
	// lasting forever if ValidForever is used.
	SendReply(channelID *id.ID, msg string, replyTo message.ID,
		validUntil time.Duration, params cmix.CMIXParams) (
		message.ID, rounds.Round, ephemeral.Id, error)

	// SendReaction is used to send a reaction to a message over a channel. The
	// reaction must be a single emoji with no other characters, and will be
	// rejected otherwise.
	//
	// Clients will drop the reaction if they do not recognize the reactTo
	// message.
	//
	// The message will auto delete validUntil after the round it is sent in,
	// lasting forever if ValidForever is used.
	SendReaction(channelID *id.ID, reaction string, reactTo message.ID,
		validUntil time.Duration, params cmix.CMIXParams) (
		message.ID, rounds.Round, ephemeral.Id, error)

	// SendAdminGeneric is used to send a raw message over a channel encrypted
	// with admin keys, identifying it as sent by the admin. In general, it
	// should be wrapped in a function that defines the wire protocol.
	//
	// If the final message, before being sent over the wire, is too long, this
	// will return an error. The message must be at most 510 bytes long.
	//
	// If the user is not an admin of the channel (i.e. does not have a private
	// key for the channel saved to storage), then the error NotAnAdminErr is
	// returned.
	//
	// Set tracked to true if the message should be tracked in the sendTracker,
	// which allows messages to be shown locally before they are received on the
	// network. In general, all messages that will be displayed to the user
	// should be tracked while all actions should not be. More technically, any
	// messageType that corresponds to a handler that does not return a unique
	// ID (i.e., always returns 0) cannot be tracked, or it will cause errors.
	SendAdminGeneric(channelID *id.ID, messageType MessageType, msg []byte,
		validUntil time.Duration, tracked bool, params cmix.CMIXParams) (
		message.ID, rounds.Round, ephemeral.Id, error)

	// DeleteMessage deletes the targeted message from storage. Users may delete
	// their own messages but only the channel admin can delete other user's
	// messages. If the user is not an admin of the channel or if they are not
	// the sender of the targetMessage, then the error NotAnAdminErr is
	// returned.
	//
	// Clients will drop the deletion if they do not recognize the target
	// message.
	DeleteMessage(channelID *id.ID, targetMessage message.ID,
		params cmix.CMIXParams) (
		message.ID, rounds.Round, ephemeral.Id, error)

	// PinMessage pins the target message to the top of a channel view for all
	// users in the specified channel. Only the channel admin can pin user
	// messages; if the user is not an admin of the channel, then the error
	// NotAnAdminErr is returned.
	//
	// If undoAction is true, then the targeted message is unpinned. validUntil
	// is the time the message will be pinned for; set this to ValidForever to
	// pin indefinitely. validUntil is ignored if undoAction is true.
	//
	// Clients will drop the pin if they do not recognize the target message.
	PinMessage(channelID *id.ID, targetMessage message.ID,
		undoAction bool, validUntil time.Duration, params cmix.CMIXParams) (
		message.ID, rounds.Round, ephemeral.Id, error)

	// MuteUser is used to mute a user in a channel. Muting a user will cause
	// all future messages from the user being dropped on reception. Muted users
	// are also unable to send messages. Only the channel admin can mute a user;
	// if the user is not an admin of the channel, then the error NotAnAdminErr
	// is returned.
	//
	// If undoAction is true, then the targeted user will be unmuted. validUntil
	// is the time the user will be muted for; set this to ValidForever to mute
	// the user indefinitely. validUntil is ignored if undoAction is true.
	MuteUser(channelID *id.ID, mutedUser ed25519.PublicKey, undoAction bool,
		validUntil time.Duration, params cmix.CMIXParams) (
		message.ID, rounds.Round, ephemeral.Id, error)

	// GetIdentity returns the public identity of the user associated with this
	// channel manager.
	GetIdentity() cryptoChannel.Identity

	// ExportPrivateIdentity encrypts the private identity using the password
	// and exports it to a portable string.
	ExportPrivateIdentity(password string) ([]byte, error)

	// GetStorageTag returns the tag where this manager is stored. To be used
	// when loading the manager. The storage tag is derived from the public key.
	GetStorageTag() string

	// RegisterReceiveHandler registers a listener for non-default message types
	// so that they can be processed by modules. It is important that such
	// modules sync up with the event model implementation.
	//
	// There can only be one handler per message type; the error
	// MessageTypeAlreadyRegistered will be returned on multiple registrations
	// of the same type.
	//
	// To create a ReceiveMessageHandler, use NewReceiveMessageHandler.
	RegisterReceiveHandler(
		messageType MessageType, handler *ReceiveMessageHandler) error

	// SetNickname sets the nickname in a channel after checking that the
	// nickname is valid using [IsNicknameValid].
	SetNickname(nickname string, channelID *id.ID) error

	// DeleteNickname removes the nickname for a given channel. The name will
	// revert back to the codename for this channel instead.
	DeleteNickname(channelID *id.ID) error

	// GetNickname returns the nickname for the given channel, if it exists.
	GetNickname(channelID *id.ID) (nickname string, exists bool)

	// Muted returns true if the user is currently muted in the given channel.
	Muted(channelID *id.ID) bool

	// GetMutedUsers returns the list of the public keys for each muted user in
	// the channel. If there are no muted user or if the channel does not exist,
	// an empty list is returned.
	GetMutedUsers(channelID *id.ID) []ed25519.PublicKey

	// IsChannelAdmin returns true if the user is an admin of the channel.
	IsChannelAdmin(channelID *id.ID) bool

	// ExportChannelAdminKey gets the private key for the given channel ID,
	// encrypts it with the provided encryptionPassword, and exports it into a
	// portable format. Returns an error if the user is not an admin of the
	// channel.
	//
	// This key can be provided to other users in a channel to grant them admin
	// access using ImportChannelAdminKey.
	//
	// The private key is encrypted using a key generated from the password
	// using Argon2. Each call to ExportChannelAdminKey produces a different
	// encrypted packet regardless if the same password is used for the same
	// channel. It cannot be determined which channel the payload is for nor
	// that two payloads are for the same channel.
	//
	// The passwords between each call are not related. They can be the same or
	// different with no adverse impact on the security properties.
	ExportChannelAdminKey(
		channelID *id.ID, encryptionPassword string) ([]byte, error)

	// VerifyChannelAdminKey verifies that the encrypted private key can be
	// decrypted and that it matches the expected channel. Returns false if
	// private key does not belong to the given channel.
	//
	// Returns the error WrongPasswordErr for an invalid password. Returns the
	// error ChannelDoesNotExistsErr if the channel has not already been joined.
	VerifyChannelAdminKey(
		channelID *id.ID, encryptionPassword string, encryptedPrivKey []byte) (
		bool, error)

	// ImportChannelAdminKey decrypts and imports the given encrypted private
	// key and grants the user admin access to the channel the private key
	// belongs to. Returns an error if the private key cannot be decrypted or if
	// the private key is for the wrong channel.
	//
	// Returns the error WrongPasswordErr for an invalid password. Returns the
	// error ChannelDoesNotExistsErr if the channel has not already been joined.
	// Returns the error WrongPrivateKeyErr if the private key does not belong
	// to the channel.
	ImportChannelAdminKey(channelID *id.ID, encryptionPassword string,
		encryptedPrivKey []byte) error

	// DeleteChannelAdminKey deletes the private key for the given channel.
	//
	// CAUTION: This will remove admin access. This cannot be undone. If the
	// private key is deleted, it cannot be recovered and the channel can never
	// have another admin.
	DeleteChannelAdminKey(channelID *id.ID) error
}

Manager provides an interface to manager channels.

func LoadManager

func LoadManager(storageTag string, kv *versioned.KV, net Client,
	rng *fastRNG.StreamGenerator, model EventModel) (
	Manager, error)

LoadManager restores a channel Manager from disk stored at the given storage tag.

func LoadManagerBuilder added in v4.5.0

func LoadManagerBuilder(storageTag string, kv *versioned.KV, net Client,
	rng *fastRNG.StreamGenerator, modelBuilder EventModelBuilder) (Manager, error)

LoadManagerBuilder restores a channel Manager from disk stored at the given storage tag.

func NewManager

func NewManager(identity cryptoChannel.PrivateIdentity, kv *versioned.KV,
	net Client, rng *fastRNG.StreamGenerator, model EventModel,
	addService AddServiceFn) (Manager, error)

NewManager creates a new channel Manager from a cryptoChannel.PrivateIdentity. It prefixes the KV with a tag derived from the public key that can be retried for reloading using [Manager.GetStorageTag].

func NewManagerBuilder added in v4.5.0

func NewManagerBuilder(identity cryptoChannel.PrivateIdentity, kv *versioned.KV,
	net Client, rng *fastRNG.StreamGenerator, modelBuilder EventModelBuilder,
	addService AddServiceFn) (Manager, error)

NewManagerBuilder creates a new channel Manager using an EventModelBuilder.

type MessageType

type MessageType uint32

MessageType is the type of message being sent to a channel.

const (

	// Text is the default type for a message. It denotes that the message only
	// contains text.
	Text MessageType = 1

	// AdminText denotes that the message only contains text and that it comes
	// from the channel admin.
	AdminText MessageType = 2

	// Reaction denotes that the message is a reaction to another message.
	Reaction MessageType = 3

	// Delete denotes that the message should be deleted. It is removed from the
	// database and deleted from the user's view.
	Delete MessageType = 101

	// Pinned denotes that the message should be pinned to the channel.
	Pinned MessageType = 102

	// Mute denotes that any future messages from the user are hidden. The
	// messages are still received, but they are not visible.
	Mute MessageType = 103

	// AdminReplay denotes that the message contains an admin message.
	AdminReplay MessageType = 104
)

func (MessageType) Bytes added in v4.4.4

func (mt MessageType) Bytes() []byte

Bytes returns the MessageType as a 4-bit byte slice.

func (MessageType) String

func (mt MessageType) String() string

String returns a human-readable version of MessageType, used for debugging and logging. This function adheres to the fmt.Stringer interface.

type MessageTypeReceiveMessage

type MessageTypeReceiveMessage func(channelID *id.ID, messageID message.ID,
	messageType MessageType, nickname string, content, encryptedPayload []byte,
	pubKey ed25519.PublicKey, dmToken uint32, codeset uint8, timestamp,
	originatingTimestamp time.Time, lease time.Duration,
	originatingRound id.Round, round rounds.Round, status SentStatus, fromAdmin,
	hidden bool) uint64

MessageTypeReceiveMessage defines handlers for messages of various message types. Default ones for Text, Reaction, and AdminText.

A unique UUID must be returned by which the message can be referenced later via [EventModel.UpdateFromUUID].

If fromAdmin is true, then the message has been verified to come from the channel admin.

type ModelMessage added in v4.4.4

type ModelMessage struct {
	UUID            uint64            `json:"uuid"`
	Nickname        string            `json:"nickname"`
	MessageID       message.ID        `json:"messageID"`
	ChannelID       *id.ID            `json:"channelID"`
	ParentMessageID message.ID        `json:"parentMessageID"`
	Timestamp       time.Time         `json:"timestamp"`
	Lease           time.Duration     `json:"lease"`
	Status          SentStatus        `json:"status"`
	Hidden          bool              `json:"hidden"`
	Pinned          bool              `json:"pinned"`
	Content         []byte            `json:"content"`
	Type            MessageType       `json:"type"`
	Round           id.Round          `json:"round"`
	PubKey          ed25519.PublicKey `json:"pubKey"`
	CodesetVersion  uint8             `json:"codesetVersion"`
	DmToken         uint32            `json:"dmToken"`
}

ModelMessage contains a message and all of its information.

type NameService

type NameService interface {
	// GetUsername returns the username.
	GetUsername() string

	// GetChannelValidationSignature returns the validation signature and the
	// time it was signed.
	GetChannelValidationSignature() ([]byte, time.Time)

	// GetChannelPubkey returns the user's public key.
	GetChannelPubkey() ed25519.PublicKey

	// SignChannelMessage returns the signature of the given message.
	SignChannelMessage(message []byte) (signature []byte, err error)

	// ValidateChannelMessage validates that a received channel message's
	// username lease is signed by the NameService.
	ValidateChannelMessage(username string, lease time.Time,
		pubKey ed25519.PublicKey, authorIDSignature []byte) bool
}

NameService is an interface which encapsulates the user identity channel tracking service.

NameService is currently unused.

func NewDummyNameService

func NewDummyNameService(username string, rng io.Reader) (NameService, error)

NewDummyNameService returns a dummy object adhering to the name service. This neither produces valid signatures nor validates passed signatures.

THIS IS FOR DEVELOPMENT AND DEBUGGING PURPOSES ONLY.

type ReceiveMessageHandler added in v4.4.4

type ReceiveMessageHandler struct {
	// contains filtered or unexported fields
}

ReceiveMessageHandler contains a message listener MessageTypeReceiveMessage linked to a specific MessageType. It also lists which spaces this handler can receive messages for.

func NewReceiveMessageHandler added in v4.4.4

func NewReceiveMessageHandler(name string, listener MessageTypeReceiveMessage,
	userSpace, adminSpace, mutedSpace bool) *ReceiveMessageHandler

NewReceiveMessageHandler generates a new ReceiveMessageHandler.

Parameters:

  • name - A name describing what type of messages the listener picks up. This is used for debugging and logging.
  • listener - The listener that handles the received message.
  • userSpace - Set to true if this listener can receive messages from normal users.
  • adminSpace - Set to true if this listener can receive messages from admins.
  • mutedSpace - Set to true if this listener can receive messages from muted users.

func (*ReceiveMessageHandler) CheckSpace added in v4.4.4

func (rmh *ReceiveMessageHandler) CheckSpace(user, admin, muted bool) error

CheckSpace checks that ReceiveMessageHandler can receive in the given user spaces. Returns nil if the message matches one or more of the handler's spaces. Returns an error if it does not.

func (*ReceiveMessageHandler) SpaceString added in v4.4.4

func (rmh *ReceiveMessageHandler) SpaceString() string

SpaceString returns a string with the values of each space. This is used for logging and debugging purposes.

type ReplayActionFunc added in v4.4.4

type ReplayActionFunc func(channelID *id.ID, encryptedPayload []byte)

ReplayActionFunc replays the encrypted payload on the channel.

type SentStatus

type SentStatus uint8

SentStatus represents the current status of a channel message.

const (
	// Unsent is the status of a message when it is pending to be sent.
	Unsent SentStatus = iota

	// Sent is the status of a message once the round it is sent on completed.
	Sent

	// Delivered is the status of a message once is has been received.
	Delivered

	// Failed is the status of a message if it failed to send.
	Failed
)

func (SentStatus) String

func (ss SentStatus) String() string

String returns a human-readable version of SentStatus, used for debugging and logging. This function adheres to the fmt.Stringer interface.

type UpdateFromUuidFunc added in v4.4.4

type UpdateFromUuidFunc func(uuid uint64, messageID *message.ID,
	timestamp *time.Time, round *rounds.Round, pinned, hidden *bool,
	status *SentStatus)

UpdateFromUuidFunc is a function type for EventModel.UpdateFromUUID so it can be mocked for testing where used.

type UserMessage

type UserMessage struct {

	// Message contains the contents of the message. This is typically what the
	// end-user has submitted to the channel. This is a serialization of the
	// ChannelMessage.
	Message []byte `protobuf:"bytes,1,opt,name=Message,proto3" json:"Message,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"`
	// 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"`
	// contains filtered or unexported fields
}

UserMessage is a message sent by a user who is a member within the channel.

func (*UserMessage) Descriptor deprecated

func (*UserMessage) Descriptor() ([]byte, []int)

Deprecated: Use UserMessage.ProtoReflect.Descriptor instead.

func (*UserMessage) GetECCPublicKey

func (x *UserMessage) GetECCPublicKey() []byte

func (*UserMessage) GetMessage

func (x *UserMessage) GetMessage() []byte

func (*UserMessage) GetSignature

func (x *UserMessage) GetSignature() []byte

func (*UserMessage) ProtoMessage

func (*UserMessage) ProtoMessage()

func (*UserMessage) ProtoReflect

func (x *UserMessage) ProtoReflect() protoreflect.Message

func (*UserMessage) Reset

func (x *UserMessage) Reset()

func (*UserMessage) String

func (x *UserMessage) String() string

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL