proto

package
v0.0.0-...-978c921 Latest Latest
Warning

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

Go to latest
Published: Dec 19, 2019 License: MIT, CC-BY-4.0, MIT, + 1 more Imports: 33 Imported by: 0

Documentation

Index

Constants

View Source
const (
	MinPasswordLength            = 6
	ClientKeyType                = security.AES128
	PasswordResetRequestLifetime = time.Hour
)
View Source
const (
	AgentIDSize  = 8
	AgentKeyType = security.AES128
)
View Source
const (
	PasswordChangedEmail       = "password-changed"
	PasswordResetEmail         = "password-reset"
	RoomInvitationEmail        = "room-invitation"
	RoomInvitationWelcomeEmail = "room-invitation-welcome"
	VerificationEmail          = "verification"
	WelcomeEmail               = "welcome"
)
View Source
const (
	MaxMessageLength             = 1 << 20
	MaxMessageTransmissionLength = 4096
)
View Source
const (
	RoomManagerKeyType = security.AES128
	RoomMessageKeyType = security.AES128
)
View Source
const (
	AuthPasscode = AuthOption("passcode")
)
View Source
const MaxNickLength = 36

Variables

View Source
var (
	DefaultCommonEmailParams = CommonEmailParams{
		CommonData: emails.CommonData{
			LocalDomain: "heim.invalid",
		},
		SenderAddress: "noreply@heim.invalid",
		HelpAddress:   "help@heim.invalid",
		SiteName:      "heim",
		SiteURL:       "https://heim.invalid",
	}

	EmailScenarios = map[string]map[string]templates.TemplateTest{
		WelcomeEmail: map[string]templates.TemplateTest{
			"default": templates.TemplateTest{
				Data: &WelcomeEmailParams{
					CommonEmailParams: DefaultCommonEmailParams,
					VerificationToken: "token",
				},
			},
		},

		PasswordChangedEmail: map[string]templates.TemplateTest{
			"default": templates.TemplateTest{
				Data: &PasswordChangedEmailParams{
					CommonEmailParams: DefaultCommonEmailParams,
					AccountName:       "yourname",
				},
			},
		},

		PasswordResetEmail: map[string]templates.TemplateTest{
			"default": templates.TemplateTest{
				Data: &PasswordResetEmailParams{
					CommonEmailParams: DefaultCommonEmailParams,
					AccountName:       "yourname",
				},
			},
		},

		RoomInvitationEmail: map[string]templates.TemplateTest{
			"default": templates.TemplateTest{
				Data: &RoomInvitationEmailParams{
					CommonEmailParams: DefaultCommonEmailParams,
					SenderName:        "(‿|‿)",
					RoomName:          "butts",
					SenderMessage:     "hey, i heard you like butts",
				},
			},
		},

		RoomInvitationWelcomeEmail: map[string]templates.TemplateTest{
			"default": templates.TemplateTest{
				Data: &RoomInvitationWelcomeEmailParams{
					CommonEmailParams: DefaultCommonEmailParams,
					SenderName:        "thatguy",
					RoomName:          "cabal",
					RoomPrivacy:       "private",
					SenderMessage:     "let's move our machinations here",
				},
			},
		},
	}
)
View Source
var (
	ErrAccessDenied                    = fmt.Errorf("access denied")
	ErrAccountIdentityInUse            = fmt.Errorf("account identity already in use")
	ErrAccountNotFound                 = fmt.Errorf("account not found")
	ErrAgentAlreadyExists              = fmt.Errorf("agent already exists")
	ErrAgentNotFound                   = fmt.Errorf("agent not found")
	ErrCapabilityNotFound              = fmt.Errorf("capability not found")
	ErrClientKeyNotFound               = fmt.Errorf("client key not found")
	ErrEditInconsistent                = fmt.Errorf("edit inconsistent")
	ErrEmailNotFound                   = fmt.Errorf("email not found")
	ErrEmailAlreadyDelivered           = fmt.Errorf("email already delivered")
	ErrInvalidConfirmationCode         = fmt.Errorf("invalid confirmation code")
	ErrInvalidNick                     = fmt.Errorf("invalid nick")
	ErrInvalidParent                   = fmt.Errorf("invalid parent ID")
	ErrInvalidUserID                   = fmt.Errorf("invalid user ID")
	ErrInvalidVerificationToken        = fmt.Errorf("invalid verification token")
	ErrLoggedIn                        = fmt.Errorf("logged in")
	ErrOTPAlreadyEnrolled              = fmt.Errorf("otp already enrolled")
	ErrOTPNotEnrolled                  = fmt.Errorf("otp not enrolled")
	ErrManagerNotFound                 = fmt.Errorf("manager not found")
	ErrMessageNotFound                 = fmt.Errorf("message not found")
	ErrMessageTooLong                  = fmt.Errorf("message too long")
	ErrNotLoggedIn                     = fmt.Errorf("not logged in")
	ErrPMNotFound                      = fmt.Errorf("pm not found")
	ErrPersonalIdentityAlreadyVerified = fmt.Errorf("personal identity already verified")
	ErrPersonalIdentityInUse           = fmt.Errorf("personal identity already in use")
	ErrRoomNotFound                    = fmt.Errorf("room not found")
)
View Source
var (
	AuthType      = PacketType("auth")
	AuthReplyType = AuthType.Reply()

	BanType        = PacketType("ban")
	BanReplyType   = BanType.Reply()
	UnbanType      = PacketType("unban")
	UnbanReplyType = UnbanType.Reply()

	SendType      = PacketType("send")
	SendEventType = SendType.Event()
	SendReplyType = SendType.Reply()

	ChangeEmailType      = PacketType("change-email")
	ChangeEmailReplyType = ChangeEmailType.Reply()

	ChangeNameType      = PacketType("change-name")
	ChangeNameReplyType = ChangeNameType.Reply()

	ChangePasswordType      = PacketType("change-password")
	ChangePasswordReplyType = ChangePasswordType.Reply()

	EditMessageType      = PacketType("edit-message")
	EditMessageEventType = EditMessageType.Event()
	EditMessageReplyType = EditMessageType.Reply()

	GetMessageType      = PacketType("get-message")
	GetMessageReplyType = GetMessageType.Reply()

	GrantAccessType      = PacketType("grant-access")
	GrantAccessReplyType = GrantAccessType.Reply()

	GrantManagerType      = PacketType("grant-manager")
	GrantManagerReplyType = GrantManagerType.Reply()

	JoinType      = PacketType("join")
	JoinEventType = JoinType.Event()
	PartType      = PacketType("part")
	PartEventType = PartType.Event()

	LogType      = PacketType("log")
	LogReplyType = LogType.Reply()

	LoginType      = PacketType("login")
	LoginEventType = LoginType.Event()
	LoginReplyType = LoginType.Reply()

	LogoutType      = PacketType("logout")
	LogoutEventType = LogoutType.Event()
	LogoutReplyType = LogoutType.Reply()

	NickType      = PacketType("nick")
	NickEventType = NickType.Event()
	NickReplyType = NickType.Reply()

	PingType      = PacketType("ping")
	PingEventType = PingType.Event()
	PingReplyType = PingType.Reply()

	PMInitiateType      = PacketType("pm-initiate")
	PMInitiateEventType = PacketType("pm-initiate-event")
	PMInitiateReplyType = PacketType("pm-initiate-reply")

	RegisterAccountType      = PacketType("register-account")
	RegisterAccountReplyType = RegisterAccountType.Reply()

	ResendVerificationEmailType      = PacketType("resend-verification-email")
	ResendVerificationEmailReplyType = ResendVerificationEmailType.Reply()

	ResetPasswordType      = PacketType("reset-password")
	ResetPasswordReplyType = ResetPasswordType.Reply()

	RevokeAccessType      = PacketType("revoke-access")
	RevokeAccessReplyType = RevokeAccessType.Reply()

	RevokeManagerType      = PacketType("revoke-manager")
	RevokeManagerReplyType = RevokeManagerType.Reply()

	StaffCreateRoomType      = PacketType("staff-create-room")
	StaffCreateRoomReplyType = StaffCreateRoomType.Reply()

	StaffEnrollOTPType      = PacketType("staff-enroll-otp")
	StaffEnrollOTPReplyType = StaffEnrollOTPType.Reply()

	StaffValidateOTPType      = PacketType("staff-validate-otp")
	StaffValidateOTPReplyType = StaffValidateOTPType.Reply()

	StaffGrantManagerType      = PacketType("staff-grant-manager")
	StaffGrantManagerReplyType = StaffGrantManagerType.Reply()

	StaffInspectIPType      = PacketType("staff-inspect-ip")
	StaffInspectIPReplyType = StaffInspectIPType.Reply()

	StaffInvadeType      = PacketType("staff-invade")
	StaffInvadeReplyType = StaffInvadeType.Reply()

	StaffLockRoomType      = PacketType("staff-lock-room")
	StaffLockRoomReplyType = StaffLockRoomType.Reply()

	StaffRevokeAccessType      = PacketType("staff-revoke-access")
	StaffRevokeAccessReplyType = StaffRevokeAccessType.Reply()

	StaffRevokeManagerType      = PacketType("staff-revoke-manager")
	StaffRevokeManagerReplyType = StaffRevokeManagerType.Reply()

	UnlockStaffCapabilityType      = PacketType("unlock-staff-capability")
	UnlockStaffCapabilityReplyType = UnlockStaffCapabilityType.Reply()

	WhoType      = PacketType("who")
	WhoReplyType = WhoType.Reply()

	BounceEventType     = PacketType("bounce").Event()
	DisconnectEventType = PacketType("disconnect").Event()
	HelloEventType      = PacketType("hello").Event()
	NetworkEventType    = PacketType("network").Event()
	SnapshotEventType   = PacketType("snapshot").Event()

	ErrorReplyType = PacketType("error").Reply()
)

Functions

func CheckEmailVerificationToken

func CheckEmailVerificationToken(kms security.KMS, account Account, email string, token []byte) error

func DecryptPayload

func DecryptPayload(payload interface{}, auth *Authorization, level PrivilegeLevel) (interface{}, error)

func EncryptMessage

func EncryptMessage(msg *Message, keyID string, key *security.ManagedKey) error

func LoadEmoji

func LoadEmoji(path string) error

LoadEmoji takes a json key-value object stored in the file at path and unmarshals it into the global validEmoji map[string]string.

func NormalizeNick

func NormalizeNick(name string) (string, error)

NormalizeNick validates and normalizes a proposed name from a user. If the proposed name is not valid, returns an error. Otherwise, returns the normalized form of the name. Normalization for a nick consists of:

1. Remove leading and trailing whitespace 2. Collapse all internal whitespace to single spaces 3. Replace all

func PacketsByType

func PacketsByType() map[PacketType]string

func ParsePasswordResetConfirmation

func ParsePasswordResetConfirmation(confirmation string) (snowflake.Snowflake, []byte, error)

func ValidateAccountPassword

func ValidateAccountPassword(password string) (bool, string)

func ValidateEmailTemplates

func ValidateEmailTemplates(templater *templates.Templater) []error

func ValidatePersonalIdentity

func ValidatePersonalIdentity(namespace, id string) (bool, string)

Types

type Account

type Account interface {
	ID() snowflake.Snowflake
	Name() string
	Email() (string, bool)
	KeyFromPassword(password string) *security.ManagedKey
	KeyPair() security.ManagedKeyPair
	Unlock(clientKey *security.ManagedKey) (*security.ManagedKeyPair, error)
	IsStaff() bool
	UnlockStaffKMS(clientKey *security.ManagedKey) (security.KMS, error)
	PersonalIdentities() []PersonalIdentity
	UserKey() security.ManagedKey
	SystemKey() security.ManagedKey
	View(roomName string) *AccountView
}

type AccountGrantable

type AccountGrantable interface {
	GrantToAccount(
		ctx scope.Context, kms security.KMS, manager Account, managerClientKey *security.ManagedKey,
		target Account) error

	StaffGrantToAccount(ctx scope.Context, kms security.KMS, target Account) error

	RevokeFromAccount(ctx scope.Context, account Account) error

	AccountCapability(ctx scope.Context, account Account) (*security.PublicKeyCapability, error)
}

type AccountManager

type AccountManager interface {
	// GetAccount returns the account with the given ID.
	Get(ctx scope.Context, id snowflake.Snowflake) (Account, error)

	// RegisterAccount creates and returns a new, unverified account, along with
	// its (unencrypted) client key.
	Register(
		ctx scope.Context, kms security.KMS, namespace, id, password string,
		agentID string, agentKey *security.ManagedKey) (
		Account, *security.ManagedKey, error)

	// ResolveAccount returns any account registered under the given account identity.
	Resolve(ctx scope.Context, namespace, id string) (Account, error)

	// GrantStaff adds a StaffKMS capability to the identified account.
	GrantStaff(ctx scope.Context, accountID snowflake.Snowflake, kmsCred security.KMSCredential) error

	// RevokeStaff removes a StaffKMS capability from the identified account.
	RevokeStaff(ctx scope.Context, accountID snowflake.Snowflake) error

	// VerifyPersonalIdentity marks a personal identity as verified.
	VerifyPersonalIdentity(ctx scope.Context, namespace, id string) error

	// ChangeClientKey re-encrypts account keys with a new client key.
	// The correct former client key must also be given.
	ChangeClientKey(
		ctx scope.Context, accountID snowflake.Snowflake,
		oldClientKey, newClientKey *security.ManagedKey) error

	// RequestPasswordReset generates a temporary password-reset record.
	RequestPasswordReset(
		ctx scope.Context, kms security.KMS, namespace, id string) (Account, *PasswordResetRequest, error)

	// CheckPasswordResetRequest returns the account associated with
	// a password reset request, or an error if invalid or expired.
	GetPasswordResetAccount(ctx scope.Context, confirmation string) (Account, error)

	// ConfirmPasswordReset verifies a password reset confirmation code,
	// and applies the new password to the account referred to by the
	// confirmation code.
	ConfirmPasswordReset(ctx scope.Context, kms security.KMS, confirmation, password string) error

	// ChangeEmail changes an account's primary email address. It returns true if the email
	// address is verified for this account. If false is returned, then a verification
	// email will be sent out.
	ChangeEmail(ctx scope.Context, accountID snowflake.Snowflake, email string) (bool, error)

	// ChangeName changes an account's name.
	ChangeName(ctx scope.Context, accountID snowflake.Snowflake, name string) error

	// OTP unlocks and returns the user's enrolled OTP, or nil if one has never
	// been generated.
	OTP(ctx scope.Context, kms security.KMS, accountID snowflake.Snowflake) (*OTP, error)

	// GenerateOTP generates a new OTP secret for the user. If one has been generated
	// before, then it is replaced if it was never validated, or an error is returned.
	GenerateOTP(ctx scope.Context, heim *Heim, kms security.KMS, account Account) (*OTP, error)

	// ValidateOTP validates a one-time passcode according to the user's enrolled OTP.
	ValidateOTP(ctx scope.Context, kms security.KMS, accountID snowflake.Snowflake, passcode string) error
}

type AccountSecurity

type AccountSecurity struct {
	Nonce     []byte
	MAC       []byte
	SystemKey security.ManagedKey
	UserKey   security.ManagedKey
	KeyPair   security.ManagedKeyPair
}

func NewAccountSecurity

func NewAccountSecurity(
	kms security.KMS, password string) (*AccountSecurity, *security.ManagedKey, error)

NewAccountSecurity initializes the nonce and account secrets for a new account with the given password. Returns an encrypted key-encrypting-key, encrypted key-pair, nonce, and error.

func (*AccountSecurity) ChangeClientKey

func (sec *AccountSecurity) ChangeClientKey(oldKey, newKey *security.ManagedKey) error

func (*AccountSecurity) ResetPassword

func (sec *AccountSecurity) ResetPassword(kms security.KMS, password string) (*AccountSecurity, error)

func (*AccountSecurity) Unlock

func (sec *AccountSecurity) Unlock(clientKey *security.ManagedKey) (*security.ManagedKeyPair, error)

type AccountView

type AccountView struct {
	ID   snowflake.Snowflake `json:"id"`   // the id of the account
	Name string              `json:"name"` // the name that the holder of the account goes by
}

AccountView describes an account and its preferred names.

type Agent

type Agent struct {
	ID                 []byte
	IV                 []byte
	MAC                []byte
	EncryptedClientKey *security.ManagedKey
	AccountID          string
	Created            time.Time
	Blessed            bool
	Bot                bool
}

func NewAgent

func NewAgent(agentID []byte, accessKey *security.ManagedKey) (*Agent, error)

func (*Agent) IDString

func (a *Agent) IDString() string

func (*Agent) SetClientKey

func (a *Agent) SetClientKey(accessKey, clientKey *security.ManagedKey) error

func (*Agent) Unlock

func (a *Agent) Unlock(accessKey *security.ManagedKey) (*security.ManagedKey, error)

type AgentTracker

type AgentTracker interface {
	// Register associates the given ID with the given unencrypted key. The key
	// is not stored, but will be required to access the agent.
	Register(ctx scope.Context, agent *Agent) error

	// Get returns details for the agent associated with the given ID.
	Get(ctx scope.Context, agentID string) (*Agent, error)

	// SetClientKey encrypts the given clientKey with accessKey and saves it
	// under the given agentID. Both keys must be unencrypted.
	SetClientKey(
		ctx scope.Context, agentID string, accessKey *security.ManagedKey,
		accountID snowflake.Snowflake, clientKey *security.ManagedKey) error

	// ClearClientKey logs the agent out.
	ClearClientKey(ctx scope.Context, agentID string) error
}

type AuthCommand

type AuthCommand struct {
	Type     AuthOption `json:"type"`               // the method of authentication
	Passcode string     `json:"passcode,omitempty"` // use this field for `passcode` authentication
}

The `auth` command attempts to join a private room. It should be sent in response to a `bounce-event` at the beginning of a session.

type AuthOption

type AuthOption string

type AuthReply

type AuthReply struct {
	Success bool   `json:"success"`          // true if authentication succeeded
	Reason  string `json:"reason,omitempty"` // if `success` was false, the reason for failure
}

The `auth-reply` packet reports whether the `auth` command succeeded.

type Authorization

type Authorization struct {
	ClientKey               *security.ManagedKey
	ManagerKeyEncryptingKey *security.ManagedKey
	ManagerKeyPair          *security.ManagedKeyPair
	MessageKeys             map[string]*security.ManagedKey
	CurrentMessageKeyID     string
}

func (*Authorization) AddMessageKey

func (a *Authorization) AddMessageKey(keyID string, key *security.ManagedKey)

type AuthorizationResult

type AuthorizationResult struct {
	Authorization
	FailureReason string
}

type Backend

type Backend interface {
	AccountManager() AccountManager
	AgentTracker() AgentTracker
	EmailTracker() EmailTracker
	Jobs() jobs.JobService
	PMTracker() PMTracker

	// Ban adds an entry to the global ban list. A zero value for until
	// indicates a permanent ban.
	Ban(ctx scope.Context, ban Ban, until time.Time) error

	// UnbanAgent removes a global ban.
	Unban(ctx scope.Context, ban Ban) error

	Close()

	// Create creates a new room.
	CreateRoom(
		ctx scope.Context, kms security.KMS, private bool, name string, managers ...Account) (ManagedRoom, error)

	// Gets an existing Room by name.
	GetRoom(ctx scope.Context, name string) (ManagedRoom, error)

	// Peers returns a snapshot of known peers in this backend's cluster.
	Peers() []cluster.PeerDesc

	// Version returns the implementation version string.
	Version() string

	// NotifyUser broadcasts a packet to all sessions associated with the given userID
	NotifyUser(ctx scope.Context, userID UserID, packetType PacketType, payload interface{}, excluding ...Session) error
}

A Backend provides Rooms and an implementation version.

type BackendFactory

type BackendFactory func(*Heim) (Backend, error)

type Ban

type Ban struct {
	ID     UserID `json:"id,omitempty"`     // the id of an agent or account
	IP     string `json:"ip,omitempty"`     // an IP address
	Global bool   `json:"global,omitempty"` // if true, the ban applies site-wide and not just to the current room
}

`Ban` describes an entry in a ban list. When incoming sessions match one of these entries, they are rejected.

type BanCommand

type BanCommand struct {
	Ban
	Seconds int `json:"seconds,omitempty"` // the duration of the ban; if not given, the ban is infinite
}

The `ban` command adds an entry to the room's ban list. Any joined sessions that match this entry will be disconnected. New sessions matching the entry will be unable to join the room.

The command is a no-op if an identical entry already exists in the ban list.

type BanReply

type BanReply BanCommand

The `ban-reply` packet indicates that the `ban` command succeeded.

type BounceEvent

type BounceEvent struct {
	Reason      string       `json:"reason,omitempty"`       // the reason why access was denied
	AuthOptions []AuthOption `json:"auth_options,omitempty"` // authentication options that may be used; see [auth](#auth)
	AgentID     UserID       `json:"agent_id,omitempty"`     // internal use only
	IP          string       `json:"ip,omitempty"`           // internal use only
}

A `bounce-event` indicates that access to a room is denied.

type CapabilityTable

type CapabilityTable interface {
	Get(ctx scope.Context, capabilityID string) (security.Capability, error)
	Save(ctx scope.Context, account Account, c security.Capability) error
	Remove(ctx scope.Context, capabilityID string) error
}

type ChangeEmailCommand

type ChangeEmailCommand struct {
	Email    string `json:"email"`    // the new primary email address for the account
	Password string `json:"password"` // the account's password
}

The `change-email` command changes the primary email address associated with the signed in account. The email address may need to be verified before the change is fully applied.

type ChangeEmailReply

type ChangeEmailReply struct {
	Success            bool   `json:"success"`             // true if authentication succeeded and the email was changed
	Reason             string `json:"reason,omitempty"`    // if `success` was false, the reason for failure
	VerificationNeeded bool   `json:"verification_needed"` // if true, a verification email will be sent out, and the user must verify the address before it becomes their primary address
}

The `change-email-reply` packet indicates that the primary email address has been changed.

type ChangeNameCommand

type ChangeNameCommand struct {
	Name string `json:"name"` // the name to associate with the account
}

The `change-name` command changes the name associated with the signed in account.

type ChangeNameReply

type ChangeNameReply struct {
	Name string `json:"name"` // the new name associated with the account
}

The `change-name-reply` packet indicates a successful name change.

type ChangePasswordCommand

type ChangePasswordCommand struct {
	OldPassword string `json:"old_password"` // the current (and soon-to-be former) password
	NewPassword string `json:"new_password"` // the new password
}

The `change-password` command changes the password of the signed in account.

type ChangePasswordReply

type ChangePasswordReply struct{}

The `change-password-reply` packet returns the outcome of changing the password.

type Client

type Client struct {
	IP            string
	UserAgent     string
	Connected     time.Time
	Agent         *Agent
	Account       Account
	Authorization Authorization
}

func (*Client) AuthenticateWithAgent

func (c *Client) AuthenticateWithAgent(ctx scope.Context, backend Backend, agent *Agent, agentKey *security.ManagedKey) error

func (*Client) AuthenticateWithPasscode

func (c *Client) AuthenticateWithPasscode(ctx scope.Context, room ManagedRoom, passcode string) (string, error)

func (*Client) FromContext

func (c *Client) FromContext(ctx scope.Context) bool

func (*Client) FromRequest

func (c *Client) FromRequest(ctx scope.Context, r *http.Request)

func (*Client) RoomAuthorize

func (c *Client) RoomAuthorize(ctx scope.Context, room Room) error

func (*Client) UserID

func (c *Client) UserID() UserID

type CommonEmailParams

type CommonEmailParams struct {
	emails.CommonData

	EmailDomain   string        `yaml:"email_domain"`
	SiteName      string        `yaml:"site_name"`
	SiteURL       string        `yaml:"site_url"`
	HelpAddress   template.HTML `yaml:"help_address"`
	SenderAddress template.HTML `yaml:"sender_address"`
}

func (*CommonEmailParams) EmailPreferencesURL

func (p *CommonEmailParams) EmailPreferencesURL() template.HTML

func (*CommonEmailParams) SiteURLShort

func (p *CommonEmailParams) SiteURLShort() template.HTML

type DisconnectEvent

type DisconnectEvent struct {
	Reason string `json:"reason"` // the reason for disconnection
}

A `disconnect-event` indicates that the session is being closed. The client will subsequently be disconnected.

If the disconnect reason is "authentication changed", the client should immediately reconnect.

type EditMessageCommand

type EditMessageCommand struct {
	ID             snowflake.Snowflake `json:"id"`                // the id of the message to edit
	PreviousEditID snowflake.Snowflake `json:"previous_edit_id"`  // the `previous_edit_id` of the message; if this does not match, the edit will fail (basic conflict resolution)
	Parent         snowflake.Snowflake `json:"parent,omitempty"`  // the new parent of the message (*not yet implemented*)
	Content        string              `json:"content,omitempty"` // the new content of the message (*not yet implemented*)
	Delete         bool                `json:"delete"`            // the new deletion status of the message
	Announce       bool                `json:"announce"`          // if true, broadcast an `edit-message-event` to the room
}

The `edit-message` command can be used by active room managers to modify the content or display of a message.

A message deleted by this command is still stored in the database. Deleted messages may be undeleted by this command. (Messages that have expired from the database due to the room's retention policy are no longer available and cannot be restored by this or any command).

If the `announce` field is set to true, then an edit-message-event will be broadcast to the room.

TODO: support content editing TODO: support reparenting

type EditMessageEvent

type EditMessageEvent struct {
	EditID snowflake.Snowflake `json:"edit_id"` // the id of the edit
	Message
}

An `edit-message-event` indicates that a message in the room has been modified or deleted. If the client offers a user interface and the indicated message is currently displayed, it should update its display accordingly.

The event packet includes a snapshot of the message post-edit.

type EditMessageReply

type EditMessageReply struct {
	EditID snowflake.Snowflake `json:"edit_id"` // the unique id of the edit that was applied
	Message
}

`edit-message-reply` returns the id of a successful edit.

type EmailTracker

type EmailTracker interface {
	Get(ctx scope.Context, accountID snowflake.Snowflake, id string) (*emails.EmailRef, error)
	List(ctx scope.Context, accountID snowflake.Snowflake, n int, before time.Time) ([]*emails.EmailRef, error)
	MarkDelivered(ctx scope.Context, accountID snowflake.Snowflake, id string) error
	Send(
		ctx scope.Context, js jobs.JobService, templater *templates.Templater, deliverer emails.Deliverer,
		account Account, to, templateName string, data interface{}) (*emails.EmailRef, error)
}

type ErrorReply

type ErrorReply struct {
	Error string `json:"error"`
}

type GetMessageCommand

type GetMessageCommand struct {
	ID snowflake.Snowflake `json:"id"` // the id of the message to retrieve
}

The `get-message` command retrieves the full content of a single message in the room.

type GetMessageReply

type GetMessageReply Message

`get-message-reply` returns the message retrieved by `get-message`.

type GrantAccessCommand

type GrantAccessCommand struct {
	AccountID snowflake.Snowflake `json:"account_id,omitempty"` // the id of an account to grant access to
	Passcode  string              `json:"passcode,omitempty"`   // a passcode to grant access to; anyone presenting the same passcode can access the room
}

The `grant-access` command may be used by an active manager in a private room to create a new capability for access. Access may be granted to either a passcode or an account.

If the room is not private, or if the requested access grant already exists, an error will be returned.

type GrantAccessReply

type GrantAccessReply struct{}

`grant-access-reply` confirms that access was granted.

type GrantManager

type GrantManager struct {
	Capabilities     CapabilityTable
	Managers         AccountGrantable
	KeyEncryptingKey *security.ManagedKey
	SubjectKeyPair   *security.ManagedKeyPair
	SubjectNonce     []byte
	PayloadKey       *security.ManagedKey
}

func (*GrantManager) AccountCapability

func (gs *GrantManager) AccountCapability(
	ctx scope.Context, account Account) (*security.PublicKeyCapability, error)

func (*GrantManager) Authority

func (gs *GrantManager) Authority(
	ctx scope.Context, manager Account, managerKey *security.ManagedKey) (
	subjectKeyPair *security.ManagedKeyPair, public, private *json.RawMessage, err error)

func (*GrantManager) GrantToAccount

func (gs *GrantManager) GrantToAccount(
	ctx scope.Context, kms security.KMS, manager Account, managerKey *security.ManagedKey,
	target Account) error

func (*GrantManager) GrantToPasscode

func (gs *GrantManager) GrantToPasscode(
	ctx scope.Context, manager Account, managerKey *security.ManagedKey, passcode string) error

func (*GrantManager) PasscodeCapability

func (gs *GrantManager) PasscodeCapability(
	ctx scope.Context, passcode string) (*security.SharedSecretCapability, error)

func (*GrantManager) RevokeFromAccount

func (gs *GrantManager) RevokeFromAccount(ctx scope.Context, account Account) error

func (*GrantManager) RevokeFromPasscode

func (gs *GrantManager) RevokeFromPasscode(ctx scope.Context, passcode string) error

func (*GrantManager) StaffGrantToAccount

func (gs *GrantManager) StaffGrantToAccount(ctx scope.Context, kms security.KMS, target Account) error

type GrantManagerCommand

type GrantManagerCommand struct {
	AccountID snowflake.Snowflake `json:"account_id"` // the id of an account to grant manager status to
}

The `grant-manager` command may be used by an active room manager to make another account a manager in the same room.

An error is returned if the account can't be found.

type GrantManagerReply

type GrantManagerReply struct{}

`grant-manager-reply` confirms that manager status was granted.

type Heim

type Heim struct {
	Backend    Backend
	Cluster    cluster.Cluster
	PeerDesc   *cluster.PeerDesc
	Context    scope.Context
	KMS        security.KMS
	SiteName   string
	StaticPath string

	EmailDeliverer emails.Deliverer
	EmailTemplater *templates.Templater
	GeoIP          *geoip2.Api
	PageTemplater  *templates.Templater
}

func (*Heim) MockDeliverer

func (heim *Heim) MockDeliverer() emails.MockDeliverer

func (*Heim) NewOTP

func (heim *Heim) NewOTP(account Account) (*OTP, error)

func (*Heim) OnAccountEmailChanged

func (heim *Heim) OnAccountEmailChanged(
	ctx scope.Context, b Backend, account Account, clientKey *security.ManagedKey, email string, verified bool) error

func (*Heim) OnAccountPasswordChanged

func (heim *Heim) OnAccountPasswordChanged(ctx scope.Context, b Backend, account Account) error

func (*Heim) OnAccountPasswordResetRequest

func (heim *Heim) OnAccountPasswordResetRequest(
	ctx scope.Context, b Backend, account Account, req *PasswordResetRequest) error

func (*Heim) OnAccountRegistration

func (heim *Heim) OnAccountRegistration(
	ctx scope.Context, b Backend, account Account, clientKey *security.ManagedKey) error

func (*Heim) SendEmail

func (heim *Heim) SendEmail(
	ctx scope.Context, b Backend, account Account, to, templateName string, data interface{}) (*emails.EmailRef, error)

type HelloEvent

type HelloEvent struct {
	ID                   UserID               `json:"id"`                               // the id of the agent or account logged into this session
	AccountView          *PersonalAccountView `json:"account,omitempty"`                // details about the user's account, if the session is logged in
	SessionView          SessionView          `json:"session"`                          // details about the session
	AccountHasAccess     bool                 `json:"account_has_access,omitempty"`     // if true, then the account has an explicit access grant to the current room
	AccountEmailVerified bool                 `json:"account_email_verified,omitempty"` // whether the account's email address has been verified
	RoomIsPrivate        bool                 `json:"room_is_private"`                  // if true, the session is connected to a private room
	Version              string               `json:"version"`                          // the version of the code being run and served by the server
}

A `hello-event` is sent by the server to the client when a session is started. It includes information about the client's authentication and associated identity.

type Identity

type Identity interface {
	ID() UserID
	Name() string
	ServerID() string
	View() IdentityView
}

An Identity maps to a global persona. It may exist only in the context of a single Room. An Identity may be anonymous.

type IdentityView

type IdentityView struct {
	ID        UserID `json:"id"`         // the id of an agent or account
	Name      string `json:"name"`       // the name-in-use at the time this view was captured
	ServerID  string `json:"server_id"`  // the id of the server that captured this view
	ServerEra string `json:"server_era"` // the era of the server that captured this view
}

type Listing

type Listing []SessionView

A Listing is a sortable list of Identitys present in a Room. TODO: these should be Sessions

func (Listing) Len

func (l Listing) Len() int

func (Listing) Less

func (l Listing) Less(i, j int) bool

func (Listing) Swap

func (l Listing) Swap(i, j int)

type LogCommand

type LogCommand struct {
	N      int                 `json:"n"`                // maximum number of messages to return (up to 1000)
	Before snowflake.Snowflake `json:"before,omitempty"` // return messages prior to this snowflake
}

The `log` command requests messages from the room's message log. This can be used to supplement the log provided by `snapshot-event` (for example, when scrolling back further in history).

type LogReply

type LogReply struct {
	Log    []Message           `json:"log"`              // list of messages returned
	Before snowflake.Snowflake `json:"before,omitempty"` // messages prior to this snowflake were returned
}

The `log-reply` packet returns a list of messages from the room's message log.

type LoginCommand

type LoginCommand struct {
	Namespace string `json:"namespace"` // the namespace of a personal identifier
	ID        string `json:"id"`        // the id of a personal identifier
	Password  string `json:"password"`  // the password for unlocking the account
}

The `login` command attempts to log an anonymous session into an account. It will return an error if the session is already logged in.

If the login succeeds, the client should expect to receive a `disconnect-event` shortly after. The next connection the client makes will be a logged in session.

type LoginEvent

type LoginEvent struct {
	AccountID snowflake.Snowflake `json:"account_id"`
}

The `login-event` packet is sent to all sessions of an agent when that agent is logged in (except for the session that issued the login command).

type LoginReply

type LoginReply struct {
	Success   bool                `json:"success"`              // true if the session is now logged in
	Reason    string              `json:"reason,omitempty"`     // if `success` was false, the reason why
	AccountID snowflake.Snowflake `json:"account_id,omitempty"` // if `success` was true, the id of the account the session logged into.
}

The `login-reply` packet returns whether the session successfully logged into an account.

If this reply returns success, the client should expect to receive a `disconnect-event` shortly after. The next connection the client makes will be a logged in session.

type LogoutCommand

type LogoutCommand struct{}

The `logout` command logs a session out of an account. It will return an error if the session is not logged in.

If the logout is successful, the client should expect to receive a `disconnect-event` shortly after. The next connection the client makes will be a logged out session.

type LogoutEvent

type LogoutEvent struct{}

The `logout-event` packet is sent to all sessions of an agent when that agent is logged out (except for the session that issued the logout command).

type LogoutReply

type LogoutReply struct{}

The `logout-reply` packet confirms a logout.

type ManagedRoom

type ManagedRoom interface {
	Room

	// Ban adds an entry to the room's ban list. A zero value for until
	// indicates a permanent ban.
	Ban(ctx scope.Context, ban Ban, until time.Time) error

	// UnbanAgent removes an agent ban from the room.
	Unban(ctx scope.Context, ban Ban) error

	// GenerateMessageKey generates and stores a new key and nonce
	// for encrypting messages in the room. This invalidates all grants made with
	// the previous key.
	GenerateMessageKey(ctx scope.Context, kms security.KMS) (RoomMessageKey, error)

	// MessageKey returns the room's current message key, or nil if the room is
	// unencrypted.
	MessageKey(ctx scope.Context) (RoomMessageKey, error)

	// ManagerKey returns a handle to the room's manager key.
	ManagerKey(ctx scope.Context) (RoomManagerKey, error)

	// Managers returns the list of accounts managing the room.
	Managers(ctx scope.Context) ([]Account, error)

	// AddManager adds an account as a manager of the room. An unencrypted
	// client key and corresponding account are needed from the user taking
	// this action.
	AddManager(
		ctx scope.Context, kms security.KMS,
		actor Account, actorKey *security.ManagedKey,
		newManager Account) error

	// RemoveManager removes an account as manager. An unencrypted client key
	// and corresponding account are needed from the user taking this action.
	RemoveManager(
		ctx scope.Context, actor Account, actorKey *security.ManagedKey, formerManager Account) error

	// ManagerCapability returns the manager capablity for the given account.
	ManagerCapability(ctx scope.Context, manager Account) (security.Capability, error)

	MinAgentAge() time.Duration
}

type Message

type Message struct {
	ID              snowflake.Snowflake `json:"id"`                          // the id of the message (unique within a room)
	Parent          snowflake.Snowflake `json:"parent,omitempty"`            // the id of the message's parent, or null if top-level
	PreviousEditID  snowflake.Snowflake `json:"previous_edit_id,omitempty"`  // the edit id of the most recent edit of this message, or null if it's never been edited
	UnixTime        Time                `json:"time"`                        // the unix timestamp of when the message was posted
	Sender          SessionView         `json:"sender"`                      // the view of the sender's session
	Content         string              `json:"content"`                     // the content of the message (client-defined)
	EncryptionKeyID string              `json:"encryption_key_id,omitempty"` // the id of the key that encrypts the message in storage
	Edited          Time                `json:"edited,omitempty"`            // the unix timestamp of when the message was last edited
	Deleted         Time                `json:"deleted,omitempty"`           // the unix timestamp of when the message was deleted
	Truncated       bool                `json:"truncated,omitempty"`         // if true, then the full content of this message is not included (see `get-message` to obtain the message with full content)
}

A Message is a node in a Room's Log. It corresponds to a chat message, or a post, or any broadcasted event in a room that should appear in the log.

func DecryptMessage

func DecryptMessage(msg Message, auths map[string]*security.ManagedKey, level PrivilegeLevel) (Message, error)

func (*Message) Encode

func (msg *Message) Encode() ([]byte, error)

type NetworkEvent

type NetworkEvent struct {
	Type      string `json:"type"`       // the type of network event; for now, always `partition`
	ServerID  string `json:"server_id"`  // the id of the affected server
	ServerEra string `json:"server_era"` // the era of the affected server
}

A `network-event` indicates some server-side event that impacts the presence of sessions in a room.

If the network event type is `partition`, then this should be treated as a [part-event](#part-event) for all sessions connected to the same server id/era combo.

type NickCommand

type NickCommand struct {
	Name string `json:"name"` // the requested name (maximum length 36 bytes)
}

The `nick` command sets the name you present to the room. This name applies to all messages sent during this session, until the `nick` command is called again.

type NickEvent

type NickEvent NickReply

`nick-event` announces a nick change by another session in the room.

type NickReply

type NickReply struct {
	SessionID string `json:"session_id"` // the id of the session this name applies to
	ID        UserID `json:"id"`         // the id of the agent or account logged into the session
	From      string `json:"from"`       // the previous name associated with the session
	To        string `json:"to"`         // the name associated with the session henceforth
}

`nick-reply` confirms the `nick` command. It returns the session's former and new names (the server may modify the requested nick).

type OTP

type OTP struct {
	URI       string
	Validated bool
}

func (*OTP) QRImage

func (o *OTP) QRImage(width, height int) (image.Image, error)

func (*OTP) Validate

func (o *OTP) Validate(password string) error

type PM

type PM struct {
	ID                    snowflake.Snowflake
	Initiator             snowflake.Snowflake
	InitiatorNick         string
	Receiver              UserID
	ReceiverNick          string
	ReceiverMAC           []byte
	IV                    []byte
	EncryptedSystemKey    *security.ManagedKey
	EncryptedInitiatorKey *security.ManagedKey
	EncryptedReceiverKey  *security.ManagedKey
}

func InitiatePM

func InitiatePM(
	ctx scope.Context, b Backend, kms security.KMS, client *Client, initiatorNick string, receiver UserID,
	receiverNick string) (*PM, error)

func NewPM

func NewPM(kms security.KMS, client *Client, initiatorNick string, receiver UserID, receiverNick string) (
	*PM, *security.ManagedKey, error)

func (*PM) Access

func (pm *PM) Access(ctx scope.Context, b Backend, kms security.KMS, client *Client) (*security.ManagedKey, bool, string, error)

type PMInitiateCommand

type PMInitiateCommand struct {
	UserID UserID `json:"user_id"` // the id of the user to invite to chat privately
}

The `pm-initiate` command constructs a virtual room for private messaging between the client and the given UserID(#userid).

type PMInitiateEvent

type PMInitiateEvent struct {
	From     UserID              `json:"from"`      // the id of the user inviting the client to chat privately
	FromNick string              `json:"from_nick"` // the nick of the inviting user
	FromRoom string              `json:"from_room"` // the room where the invitation was sent from
	PMID     snowflake.Snowflake `json:"pm_id"`     // the private chat can be accessed at /room/pm:*PMID*
}

The `pm-initiate-event` informs the client that another user wants to chat with them privately.

type PMInitiateReply

type PMInitiateReply struct {
	PMID   snowflake.Snowflake `json:"pm_id"`   // the private chat can be accessed at /room/pm:*PMID*
	ToNick string              `json:"to_nick"` // the nickname of the recipient of the invitation
}

The `pm-initiate-reply` provides the PMID for the requested private messaging room.

type PMTracker

type PMTracker interface {
	Initiate(ctx scope.Context, kms security.KMS, room Room, client *Client, receiver UserID) (snowflake.Snowflake, error)
	Room(ctx scope.Context, kms security.KMS, pmID snowflake.Snowflake, client *Client) (Room, *security.ManagedKey, error)
}

type Packet

type Packet struct {
	ID    string          `json:"id,omitempty"`    // client-generated id for associating replies with commands
	Type  PacketType      `json:"type"`            // the name of the command, reply, or event
	Data  json.RawMessage `json:"data,omitempty"`  // the payload of the command, reply, or event
	Error string          `json:"error,omitempty"` // this field appears in replies if a command fails

	Throttled       bool   `json:"throttled,omitempty"`        // this field appears in replies to warn the client that it may be flooding; the client should slow down its command rate
	ThrottledReason string `json:"throttled_reason,omitempty"` // if throttled is true, this field describes why
}

func MakeEvent

func MakeEvent(payload interface{}) (*Packet, error)

func MakeResponse

func MakeResponse(
	refID string, msgType PacketType, payload interface{}, throttled bool) (*Packet, error)

func ParseRequest

func ParseRequest(data []byte) (*Packet, error)

func (*Packet) Encode

func (cmd *Packet) Encode() ([]byte, error)

func (*Packet) Payload

func (cmd *Packet) Payload() (interface{}, error)

type PacketType

type PacketType string

func (PacketType) Event

func (c PacketType) Event() PacketType

func (PacketType) Reply

func (c PacketType) Reply() PacketType

type PasscodeGrantable

type PasscodeGrantable interface {
	GrantToPasscode(
		ctx scope.Context, manager Account, managerClientKey *security.ManagedKey, passcode string) error

	RevokeFromPasscode(ctx scope.Context, passcode string) error

	PasscodeCapability(ctx scope.Context, passcode string) (*security.SharedSecretCapability, error)
}

type PasswordChangedEmailParams

type PasswordChangedEmailParams struct {
	CommonEmailParams
	AccountName string
}

func (PasswordChangedEmailParams) Subject

type PasswordResetEmailParams

type PasswordResetEmailParams struct {
	CommonEmailParams
	AccountName  string
	Confirmation string
}

func (PasswordResetEmailParams) ResetPasswordURL

func (p PasswordResetEmailParams) ResetPasswordURL() template.HTML

func (PasswordResetEmailParams) Subject

type PasswordResetRequest

type PasswordResetRequest struct {
	ID        snowflake.Snowflake
	AccountID snowflake.Snowflake
	Key       []byte
	Requested time.Time
	Expires   time.Time
}

func GeneratePasswordResetRequest

func GeneratePasswordResetRequest(
	kms security.KMS, accountID snowflake.Snowflake) (*PasswordResetRequest, error)

func (*PasswordResetRequest) MAC

func (req *PasswordResetRequest) MAC() []byte

func (*PasswordResetRequest) String

func (req *PasswordResetRequest) String() string

func (*PasswordResetRequest) VerifyMAC

func (req *PasswordResetRequest) VerifyMAC(mac []byte) bool

type PersonalAccountView

type PersonalAccountView struct {
	AccountView
	Email string `json:"email"` // the account's email address
}

PersonalAccountView describes an account to its owner.

type PersonalIdentity

type PersonalIdentity interface {
	Namespace() string
	ID() string
	Verified() bool
}

type PingCommand

type PingCommand struct {
	UnixTime Time `json:"time"` // an arbitrary value, intended to be a unix timestamp
}

The `ping` command initiates a client-to-server ping. The server will send back a `ping-reply` with the same timestamp as soon as possible.

type PingEvent

type PingEvent struct {
	UnixTime     Time `json:"time"` // a unix timestamp according to the server's clock
	NextUnixTime Time `json:"next"` // the expected time of the next ping-event, according to the server's clock
}

A `ping-event` represents a server-to-client ping. The client should send back a `ping-reply` with the same value for the time field as soon as possible (or risk disconnection).

type PingReply

type PingReply struct {
	UnixTime Time `json:"time,omitempty"` // the timestamp of the ping being replied to
}

`ping-reply` is a response to a `ping` command or `ping-event`.

type Presence

type Presence struct {
	SessionView
	LastInteracted time.Time           `json:"last_interacted"`
	MessageID      snowflake.Snowflake `json:"message_id"`
	Typing         bool                `json:"typing"`
}

type PresenceEvent

type PresenceEvent SessionView

A `presence-event` describes a session joining into or parting from a room.

type PrivilegeLevel

type PrivilegeLevel byte
const (
	General PrivilegeLevel = iota
	Host
	Staff
)

type RegisterAccountCommand

type RegisterAccountCommand LoginCommand

The `register-account` command creates a new account and logs into it. It will return an error if the session is already logged in.

If the account registration succeeds, the client should expect to receive a `disconnect-event` shortly after. The next connection the client makes will be a logged in session using the new account.

type RegisterAccountReply

type RegisterAccountReply LoginReply

The `register-account-reply` packet returns whether the new account was registered.

If this reply returns success, the client should expect to receive a disconnect-event shortly after. The next connection the client makes will be a logged in session, using the newly created account.

type ResendVerificationEmailCommand

type ResendVerificationEmailCommand struct{}

The `resend-verification-email` command forces a new email to be sent for verifying an accounts primary email address. An error will be returned if the account has no unverified email addresses associated with it.

type ResendVerificationEmailReply

type ResendVerificationEmailReply struct{}

The `resend-verification-email-reply` packet indicates that a verification email has been sent.

type ResetPasswordCommand

type ResetPasswordCommand struct {
	Namespace string `json:"namespace"`
	ID        string `json:"id"`
}

The `reset-password` command generates a password reset request. An email will be sent to the owner of the given personal identifier, with instructions and a confirmation code for resetting the password.

type ResetPasswordReply

type ResetPasswordReply struct{}

`reset-password-reply` confirms that the password reset is in progress.

type RevokeAccessCommand

type RevokeAccessCommand struct {
	AccountID snowflake.Snowflake `json:"account_id,omitempty"` // the id of the account to revoke access from
	Passcode  string              `json:"passcode",omitempty`   // the passcode to revoke access from
}

The `revoke-access` command disables an access grant to a private room. The grant may be to an account or to a passcode.

TODO: all live sessions using the revoked grant should be disconnected TODO: support revocation by capability_id, in case a manager doesn't know the passcode

type RevokeAccessReply

type RevokeAccessReply struct{}

`revoke-access-reply` confirms that the access grant was revoked.

type RevokeManagerCommand

type RevokeManagerCommand struct {
	AccountID snowflake.Snowflake `json:"account_id"` // the id of the account to remove as manager
}

The `revoke-manager` command removes an account as manager of the room. This command can be applied to oneself, so be careful not to orphan your room!

type RevokeManagerReply

type RevokeManagerReply struct{}

`revoke-manager-reply` confirms that the manager grant was revoked.

type Room

type Room interface {
	ID() string
	Title() string
	GetMessage(scope.Context, snowflake.Snowflake) (*Message, error)
	Latest(scope.Context, int, snowflake.Snowflake) ([]Message, error)
	Snapshot(ctx scope.Context, session Session, level PrivilegeLevel, numMessages int) (*SnapshotEvent, error)

	// Join inserts a Session into the Room's global presence.
	Join(scope.Context, Session) (virtualClientAddr string, err error)

	// Part removes a Session from the Room's global presence.
	Part(scope.Context, Session) error

	// IsValidParent checks whether the message with the given ID is able to be replied to.
	IsValidParent(id snowflake.Snowflake) (bool, error)

	// Send broadcasts a Message from a Session to the Room.
	Send(scope.Context, Session, Message) (Message, error)

	// Edit modifies or deletes a message.
	EditMessage(scope.Context, Session, EditMessageCommand) (EditMessageReply, error)

	// Listing returns the current global list of connected sessions to this
	// Room.
	Listing(ctx scope.Context, level PrivilegeLevel, exclude ...Session) (Listing, error)

	// RenameUser updates the nickname of a Session in this Room.
	RenameUser(ctx scope.Context, session Session, formerName string) (*NickEvent, error)

	// Version returns the version of the server hosting this Room.
	Version() string

	WaitForPart(sessionID string) error

	MessageKeyID(scope.Context) (string, bool, error)

	ResolveClientAddress(ctx scope.Context, addr string) (net.IP, error)

	ResolveNick(ctx scope.Context, userID UserID) (string, bool, error)
}

A Room is a nexus of communication. Users connect to a Room via Session and interact.

type RoomInvitationEmailParams

type RoomInvitationEmailParams struct {
	CommonEmailParams
	AccountName   string
	RoomName      string
	SenderName    string
	SenderMessage string
}

func (RoomInvitationEmailParams) RoomURL

func (RoomInvitationEmailParams) Subject

type RoomInvitationWelcomeEmailParams

type RoomInvitationWelcomeEmailParams struct {
	CommonEmailParams
	AccountName   string
	RoomName      string
	RoomPrivacy   string
	SenderName    string
	SenderMessage string
}

func (RoomInvitationWelcomeEmailParams) RoomURL

func (RoomInvitationWelcomeEmailParams) Subject

type RoomManagerKey

type RoomManagerKey interface {
	AccountGrantable

	// KeyPair returns the current encrypted ManagedKeyPair for the room.
	KeyPair() security.ManagedKeyPair

	// StaffUnlock uses the KMS to decrypt the room's ManagedKeyPair and returns it.
	StaffUnlock(kms security.KMS) (*security.ManagedKeyPair, error)

	// Unlock decrypts the room's ManagedKeyPair with the given key and returns it.
	Unlock(managerKey *security.ManagedKey) (*security.ManagedKeyPair, error)

	// Nonce returns the current 128-bit nonce for the room.
	Nonce() []byte
}

type RoomMessageKey

type RoomMessageKey interface {
	AccountGrantable
	PasscodeGrantable

	// ID returns a unique identifier for the key.
	KeyID() string

	// Timestamp returns when the key was generated.
	Timestamp() time.Time

	// Nonce returns the current 128-bit nonce for the room.
	Nonce() []byte

	// ManagedKey returns the current encrypted ManagedKey for the room.
	ManagedKey() security.ManagedKey
}

type RoomSecurity

type RoomSecurity struct {
	Nonce            []byte
	MAC              []byte
	KeyEncryptingKey security.ManagedKey
	KeyPair          security.ManagedKeyPair
}

func NewRoomSecurity

func NewRoomSecurity(kms security.KMS, roomName string) (*RoomSecurity, error)

func (*RoomSecurity) Unlock

func (sec *RoomSecurity) Unlock(managerKey *security.ManagedKey) (*security.ManagedKeyPair, error)

type SendCommand

type SendCommand struct {
	Content string              `json:"content"`          // the content of the message (client-defined)
	Parent  snowflake.Snowflake `json:"parent,omitempty"` // the id of the parent message, if any
}

The `send` command sends a message to a room. The session must be successfully joined with the room. This message will be broadcast to all sessions joined with the room.

If the room is private, then the message content will be encrypted before it is stored and broadcast to the rest of the room.

The caller of this command will not receive the corresponding `send-event`, but will receive the same information in the `send-reply`.

type SendEvent

type SendEvent Message

A `send-event` indicates a message received by the room from another session.

type SendReply

type SendReply SendEvent

`send-reply` returns the message that was sent. This includes the message id, which was populated by the server.

type Session

type Session interface {
	// ID returns the globally unique identifier for the Session.
	ID() string

	// ServerID returns the globally unique identifier of the server hosting
	// the Session.
	ServerID() string

	AgentID() string

	// Identity returns the Identity associated with the Session.
	Identity() Identity

	// SetName sets the acting nickname for the Session.
	SetName(name string)

	// Send sends a packet to the Session's client.
	Send(scope.Context, PacketType, interface{}) error

	// Close terminates the Session and disconnects the client.
	Close()

	// CheckAbandoned() issues an immediate ping to the session with a short
	// timeout.
	CheckAbandoned() error

	View(PrivilegeLevel) SessionView
}

A Session is a connection between a client and a Room.

type SessionView

type SessionView struct {
	IdentityView
	SessionID         string `json:"session_id"`                    // id of the session, unique across all sessions globally
	IsStaff           bool   `json:"is_staff,omitempty"`            // if true, this session belongs to a member of staff
	IsManager         bool   `json:"is_manager,omitempty"`          // if true, this session belongs to a manager of the room
	ClientAddress     string `json:"client_address,omitempty"`      // for hosts and staff, the virtual address of the client
	RealClientAddress string `json:"real_client_address,omitempty"` // for staff, the real address of the client
}

SessionView describes a session and its identity.

type SnapshotEvent

type SnapshotEvent struct {
	Identity  UserID    `json:"identity"`       // the id of the agent or account logged into this session
	SessionID string    `json:"session_id"`     // the globally unique id of this session
	Version   string    `json:"version"`        // the server's version identifier
	Listing   Listing   `json:"listing"`        // the list of all other sessions joined to the room (excluding this session)
	Log       []Message `json:"log"`            // the most recent messages posted to the room (currently up to 100)
	Nick      string    `json:"nick,omitempty"` // the acting nick of the session; if omitted, client set nick before speaking

	PMWithNick   string `json:"pm_with_nick,omitempty"`    // if given, this room is for private chat with the given nick
	PMWithUserID UserID `json:"pm_with_user_id,omitempty"` // if given, this room is for private chat with the given user
}

A `snapshot-event` indicates that a session has successfully joined a room. It also offers a snapshot of the room's state and recent history.

type StaffCreateRoomCommand

type StaffCreateRoomCommand struct {
	Name     string                `json:"name"`              // the name of the new rom
	Managers []snowflake.Snowflake `json:"managers"`          // ids of manager accounts for this room (there must be at least one)
	Private  bool                  `json:"private,omitempty"` // if true, create a private room (all managers will be granted access)
}

The `staff-create-room` command creates a new room.

type StaffCreateRoomReply

type StaffCreateRoomReply struct {
	Success       bool   `json:"success"`                  // whether the room was created
	FailureReason string `json:"failure_reason,omitempty"` // if `success` was false, the reason why
}

`staff-create-room-reply` returns the outcome of a room creation request.

type StaffEnrollOTPCommand

type StaffEnrollOTPCommand struct{}

The `staff-enroll-otp` command generates a new OTP key for a staff user. The user must then validate the key by issuing a successful `staff-validate-otp` command. An error will be returned if the user already has a validated OTP key.

type StaffEnrollOTPReply

type StaffEnrollOTPReply struct {
	URI     string `json:"uri"`    // the otpauth URI for the generated key (https://github.com/google/google-authenticator/wiki/Key-Uri-Format)
	QRImage string `json:"qr_uri"` // the data URI for a QR image encoding the otpauth URI
}

`staff-enroll-otp-reply` returns the OTP key in several forms that a user can use to import into their personal authentication app.

type StaffGrantManagerCommand

type StaffGrantManagerCommand GrantManagerCommand

The `staff-grant-manager` command is a version of the [grant-manager](#grant-manager) command that is available to staff. The staff account does not need to be a manager of the room to use this command.

type StaffGrantManagerReply

type StaffGrantManagerReply GrantManagerReply

`staff-grant-manager-reply` confirms that requested manager change was granted.

type StaffInspectIPCommand

type StaffInspectIPCommand struct {
	IP string `json:"ip"` // the IP or virtual client address to inspect
}

The `staff-inspect-ip` command looks up details about a given IP address or virtual client address.

type StaffInspectIPReply

type StaffInspectIPReply struct {
	IP      string          `json:"ip"`      // the IP address resolved from the virtual address
	Details json.RawMessage `json:"details"` // details looked up about the IP address
}

`staff-inspect-ip-reply` returns details about the requested address.

type StaffInvadeCommand

type StaffInvadeCommand struct {
	Password string `json:"password"` // the staff member's current one-time password
}

The `staff-invade` command can be used by staff to acquire temporary host and/or access capabilities in the current room.

type StaffInvadeReply

type StaffInvadeReply struct{}

`staff-invade-reply` indicates that the current session now holds host and access capabilities in the room.

type StaffLockRoomCommand

type StaffLockRoomCommand struct{}

The `staff-lock-room` command makes a room private. If the room is already private, then it generates a new message key (which currently invalidates all access grants).

type StaffLockRoomReply

type StaffLockRoomReply struct{}

`staff-lock-room-reply` confirms that the room has been made newly private.

type StaffRevokeAccessCommand

type StaffRevokeAccessCommand RevokeAccessCommand

The `staff-revoke-access` command is a version of the [revoke-access](#revoke-access) command that is available to staff. The staff account does not need to be a manager of the room to use this command.

type StaffRevokeAccessReply

type StaffRevokeAccessReply RevokeAccessReply

`staff-revoke-access-reply` confirms that requested access capability was revoked.

type StaffRevokeManagerCommand

type StaffRevokeManagerCommand RevokeManagerCommand

The `staff-revoke-manager` command is a version of the [revoke-manager](#revoke-access) command that is available to staff. The staff account does not need to be a manager of the room to use this command.

type StaffRevokeManagerReply

type StaffRevokeManagerReply RevokeManagerReply

`staff-revoke-manager-reply` confirms that requested manager capability was revoked.

type StaffValidateOTPCommand

type StaffValidateOTPCommand struct {
	Password string `json:"password"`
}

The `staff-validate-otp` command validates a one-time password against the latest OTP key generated for the user by the `staff-enroll-otp` command.

type StaffValidateOTPReply

type StaffValidateOTPReply struct{}

`staff-validate-otp-reply` indicates successful authentication with the given one-time password.

type Time

type Time time.Time

func Now

func Now() Time

func (Time) MarshalJSON

func (t Time) MarshalJSON() ([]byte, error)

func (*Time) StdTime

func (t *Time) StdTime() time.Time

func (*Time) UnmarshalJSON

func (t *Time) UnmarshalJSON(data []byte) error

type UnbanCommand

type UnbanCommand struct {
	Ban
}

The `unban` command removes an entry from the room's ban list.

type UnbanReply

type UnbanReply UnbanCommand

The `unban-reply` packet indicates that the `unban` command succeeded.

type UnlockStaffCapabilityCommand

type UnlockStaffCapabilityCommand struct {
	Password string `json:"password"` // the account's password
}

The `unlock-staff-capability` command may be called by a staff account to gain access to staff commands.

type UnlockStaffCapabilityReply

type UnlockStaffCapabilityReply struct {
	Success       bool   `json:"success"`                  // whether staff capability was unlocked
	FailureReason string `json:"failure_reason,omitempty"` // if `success` was false, the reason why
}

`unlock-staff-capability-reply` returns the outcome of unlocking the staff capability.

type UserID

type UserID string

func (UserID) Parse

func (uid UserID) Parse() (kind, id string)

func (UserID) String

func (uid UserID) String() string

type VerificationEmailParams

type VerificationEmailParams struct {
	CommonEmailParams
	VerificationToken string
}

func (VerificationEmailParams) Subject

func (VerificationEmailParams) VerifyEmailURL

func (p VerificationEmailParams) VerifyEmailURL() template.HTML

type WelcomeEmailParams

type WelcomeEmailParams struct {
	CommonEmailParams
	VerificationToken string
}

func (WelcomeEmailParams) Subject

func (p WelcomeEmailParams) Subject() template.HTML

func (*WelcomeEmailParams) VerifyEmailURL

func (p *WelcomeEmailParams) VerifyEmailURL() template.HTML

type WhoCommand

type WhoCommand struct{}

The `who` command requests a list of sessions currently joined in the room.

type WhoReply

type WhoReply struct {
	Listing Listing `json:"listing"` // a list of session views
}

The `who-reply` packet lists the sessions currently joined in the room.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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