federation

package
v0.0.0-...-4bafeee Latest Latest
Warning

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

Go to latest
Published: Oct 14, 2026 License: AGPL-3.0 Imports: 42 Imported by: 0

Documentation

Overview

Package federation lets independent StrafeChat instances talk to each other.

Model in one paragraph: every instance has a domain and an Ed25519 signing key it publishes at https://<domain>/.well-known/strafe. Users are addressed across instances as `name@domain`, and carry a federated id (FID) `@<id>:<domain>` where <id> is what their *home* instance calls them. A remote user gets a local "shadow" users row so the rest of the codebase keeps working with plain local ids; the FID ↔ shadow mapping lives in users_by_remote. Rooms are mirrored: each participating instance stores its own copy of the room and messages under local ids, tied together by the room's origin (origin_domain, origin_room_id) and per-message origin ids. Every server-to-server request is signed (see signing.go) and verified against the peer's published key.

Index

Constants

View Source
const (
	HeaderInstance  = "X-Strafe-Instance"
	HeaderTimestamp = "X-Strafe-Timestamp"
	HeaderNonce     = "X-Strafe-Nonce"
	HeaderSignature = "X-Strafe-Signature"

	// MaxClockSkewSeconds bounds how old/new a request timestamp may be.
	MaxClockSkewSeconds = 300
	// BasePath is where inbound federation routes are mounted on the API.
	BasePath = "/federation/v1"
)

Request signature scheme ("strafe-fed-v1").

Headers on every server-to-server request:

X-Strafe-Instance:  sender's domain
X-Strafe-Timestamp: unix seconds (receiver rejects if more than MaxClockSkew away)
X-Strafe-Nonce:     random string, unique per request (receiver rejects replays)
X-Strafe-Signature: <key_id>=<base64 ed25519 signature>

The signature covers the method, the federation-relative path (what follows /federation/v1, query string included - so reverse proxies that mount the API under a prefix don't change what's signed), the timestamp, the nonce and the SHA-256 of the body.

View Source
const KeyID = "ed25519:1"

KeyID identifies the signing key in /.well-known/strafe and request signatures. Bump when the format of the signed payload changes.

View Source
const LegacyServer = "strafe.internal"

LegacyServer is the synthetic server name the E2EE engine used before federation existed (@<id>:strafe.internal). Treated as "this instance" everywhere so an instance that never enables federation keeps working with clients that still send it.

View Source
const WellKnownPath = "/.well-known/strafe"

WellKnownPath is where InstanceInfo is served, relative to https://<domain>.

Variables

View Source
var (
	ErrInvalidHandle      = errors.New("invalid handle - expected name@domain")
	ErrInvalidFID         = errors.New("invalid federated id - expected @id:domain")
	ErrPeerNotAllowed     = errors.New("that instance is not allowed by this server's federation policy")
	ErrFederationOff      = errors.New("federation is not enabled on this instance")
	ErrRemoteUserNotFound = errors.New("no such user on that instance")
	ErrRemoteUnavailable  = errors.New("the other instance could not be reached")
)
View Source
var ErrPayloadTooLarge = errors.New("federation payload exceeds limits")

ErrPayloadTooLarge is returned for inbound federation payloads over the caps.

Functions

func FormatFID

func FormatFID(originID int64, domain string) string

FormatFID builds the federated id the E2EE engine and peers use for a user.

func NewInfoProvider

func NewInfoProvider(session gocqlx.Session, domain string) *infoProvider

NewInfoProvider builds the read-only federation view; domain is this instance's own.

func ParseFID

func ParseFID(s string) (originID int64, domain string, err error)

ParseFID splits "@id:domain" into its parts. The domain may be empty for bare ids.

func RequesterDomain

func RequesterDomain(c fiber.Ctx) string

RequesterDomain is the verified peer domain of an inbound federation request.

func RoomFID

func RoomFID(originRoomID int64, originDomain string) string

RoomFID is the global room identity the E2EE engine keys Megolm sessions by: !<origin_room_id>:<origin_domain>. Every participating instance must agree on it.

func ValidPeerDomain

func ValidPeerDomain(cfg config.FederationConfig, domain string) bool

ValidPeerDomain reports whether a domain may be looked up at all. With AllowInsecure off (production) it also refuses IP literals and loopback/link-local names, so a forged X-Strafe-Instance header cannot point discovery at this host's own network (cloud metadata endpoints, internal services).

func VerifySignature

func VerifySignature(pubB64 string, msg []byte, sigB64 string) bool

VerifySignature checks sigB64 over msg with a peer's base64 public key.

func WithExcludedPeer

func WithExcludedPeer(ctx context.Context, domain string) context.Context

WithExcludedPeer marks the instance whose request is being served, so the relays the request causes skip it: it applies the reply itself, and a copy would race it.

func WithRelayCapture

func WithRelayCapture(ctx context.Context, domain string) (context.Context, *relayCapture)

WithRelayCapture is WithExcludedPeer where the relays meant for the excluded instance are collected instead of dropped, to be returned in the reply.

Types

type Client

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

Client makes signed requests to peer instances.

func NewClient

func NewClient(cfg config.FederationConfig, signer *Signer, discovery *Discovery) *Client

func (*Client) Do

func (c *Client) Do(ctx context.Context, domain, method, fedPath string, body any, out any) (int, error)

Do sends a signed JSON request to domain's federation endpoint. fedPath is relative to /federation/v1 (e.g. "/rooms" or "/users/lookup?username=a"). A JSON 2xx body is decoded into out when out is non-nil.

type DirectoryReply

type DirectoryReply struct {
	Spaces []DirectorySpace `json:"spaces"`
}

DirectoryReply: GET /discover - the spaces this instance lists and shares.

type DirectorySource

type DirectorySource interface {
	// SharedListings is what this instance tells a peer it lists.
	SharedListings(ctx context.Context) ([]discover.RemoteListing, error)
	// SharesSpace reports whether a space hosted here is listed and shared, which is what
	// lets a peer's user join it without an invite.
	SharesSpace(ctx context.Context, spaceID int64) (bool, error)
}

DirectorySource is the local Discover module, as this engine needs it. Set from route setup; nil leaves the endpoints answering "nothing listed", which is also what an instance with DISCOVER_FEDERATION off wants.

type DirectorySpace

type DirectorySpace struct {
	SpaceID     string   `json:"space_id"`
	Name        string   `json:"name"`
	NameAcronym string   `json:"name_acronym,omitempty"`
	Icon        string   `json:"icon,omitempty"`
	Banner      string   `json:"banner,omitempty"`
	Description string   `json:"description,omitempty"`
	Tagline     string   `json:"tagline,omitempty"`
	Tags        []string `json:"tags,omitempty"`
	MemberCount int      `json:"member_count"`
	OnlineCount int      `json:"online_count"`
}

DirectorySpace is one card. Ids are the serving instance's own.

type Discovery

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

Discovery resolves a peer domain to its InstanceInfo, with an in-memory cache.

func NewDiscovery

func NewDiscovery(cfg config.FederationConfig) *Discovery

func (*Discovery) Lookup

func (d *Discovery) Lookup(ctx context.Context, domain string, force bool) (*InstanceInfo, error)

Lookup returns the peer's instance info, from cache unless `force` (used after a signature failure, in case the peer rotated its key).

type EmojiRef

type EmojiRef struct {
	ID       string `json:"id"`
	Name     string `json:"name"`
	URL      string `json:"url"`
	Animated bool   `json:"animated,omitempty"`
}

EmojiRef describes a custom emoji an event uses, so an instance that has never seen the emoji's space can still render it (see content.go). The id is the emoji's own.

type Handle

type Handle struct {
	Username string
	Domain   string
}

Handle is a user address as people type it: name@domain (domain optional = local). Usernames are unique per instance, so the name alone identifies someone there.

func ParseHandle

func ParseHandle(s string) (Handle, error)

func (Handle) String

func (h Handle) String() string

type Handler

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

Handler serves the inbound side of /federation/v1 (every route except /instance sits behind Service.RequireInstance).

func NewHandler

func NewHandler(svc *Service) *Handler

func (*Handler) Directory

func (h *Handler) Directory(c fiber.Ctx) error

Directory GET /discover - serves this instance's shared listings to a peer.

func (*Handler) GetUser

func (h *Handler) GetUser(c fiber.Ctx) error

GetUser GET /users/:id - a local user's profile by id.

func (*Handler) Instance

func (h *Handler) Instance(c fiber.Ctx) error

Instance serves the instance document (also mounted at /.well-known/strafe).

func (*Handler) KeysClaim

func (h *Handler) KeysClaim(c fiber.Ctx) error

KeysClaim POST /keys/claim - one-time keys of this instance's users.

func (*Handler) KeysQuery

func (h *Handler) KeysQuery(c fiber.Ctx) error

KeysQuery POST /keys/query - device keys of this instance's users.

func (*Handler) LookupUser

func (h *Handler) LookupUser(c fiber.Ctx) error

LookupUser GET /users/lookup?username= - a local user's profile.

func (*Handler) MessageCreate

func (h *Handler) MessageCreate(c fiber.Ctx) error

MessageCreate POST /rooms/messages.

func (*Handler) MessageDelete

func (h *Handler) MessageDelete(c fiber.Ctx) error

MessageDelete POST /rooms/messages/delete.

func (*Handler) MessageEdit

func (h *Handler) MessageEdit(c fiber.Ctx) error

MessageEdit PATCH /rooms/messages.

func (*Handler) Peers

func (h *Handler) Peers(c fiber.Ctx) error

Peers GET /federation/peers (client-authenticated, not S2S): instances we've talked to.

func (*Handler) PinUpdate

func (h *Handler) PinUpdate(c fiber.Ctx) error

PinUpdate serves POST /rooms/pins: one of the requesting instance's users pinned or unpinned a message in a shared room (a PM, or a channel whose origin is the requester).

func (*Handler) Presence

func (h *Handler) Presence(c fiber.Ctx) error

Presence POST /users/presence - how a user on the requesting instance now appears to others. Applied to their shadow row (so lists and READY carry it) and fanned out to their local friends like a local presence change.

func (*Handler) ReactionAdd

func (h *Handler) ReactionAdd(c fiber.Ctx) error

func (*Handler) ReactionRemove

func (h *Handler) ReactionRemove(c fiber.Ctx) error

func (*Handler) Relationship

func (h *Handler) Relationship(c fiber.Ctx) error

Relationship POST /relationships - a friend request, acceptance or teardown from a user on the requesting instance towards one of ours.

func (*Handler) RoomCreate

func (h *Handler) RoomCreate(c fiber.Ctx) error

RoomCreate POST /rooms - mirror a room the requesting instance created.

func (*Handler) RoomParticipants

func (h *Handler) RoomParticipants(c fiber.Ctx) error

RoomParticipants PUT /rooms/participants - reconcile the member set.

func (*Handler) RoomPatch

func (h *Handler) RoomPatch(c fiber.Ctx) error

RoomPatch PATCH /rooms - name / E2EE changes.

func (*Handler) RoomTyping

func (h *Handler) RoomTyping(c fiber.Ctx) error

RoomTyping POST /rooms/typing.

func (*Handler) SpaceDelete

func (h *Handler) SpaceDelete(c fiber.Ctx) error

SpaceDelete POST /spaces/delete - the origin deleted the space.

func (*Handler) SpaceEmoji

func (h *Handler) SpaceEmoji(c fiber.Ctx) error

SpaceEmoji POST /spaces/emoji.

func (*Handler) SpaceInviteCreate

func (h *Handler) SpaceInviteCreate(c fiber.Ctx) error

SpaceInviteCreate POST /spaces/invites.

func (*Handler) SpaceInvitePreview

func (h *Handler) SpaceInvitePreview(c fiber.Ctx) error

SpaceInvitePreview GET /spaces/invites/:code - what a local invite code leads to.

func (*Handler) SpaceJoin

func (h *Handler) SpaceJoin(c fiber.Ctx) error

SpaceJoin POST /spaces/join - a user on the requesting instance redeems an invite. The answer is the whole space, so the requester can build its mirror in one step.

func (*Handler) SpaceJoinListed

func (h *Handler) SpaceJoinListed(c fiber.Ctx) error

SpaceJoinListed POST /spaces/join_listed - add a peer's user to a space listed here. The listing is checked again on this side: a peer holding a stale card, or inventing one, cannot use it to get into a space that is not publicly listed.

func (*Handler) SpaceLeave

func (h *Handler) SpaceLeave(c fiber.Ctx) error

SpaceLeave POST /spaces/leave.

func (*Handler) SpaceManage

func (h *Handler) SpaceManage(c fiber.Ctx) error

SpaceManage POST /spaces/manage - run a management operation as a member elsewhere.

func (*Handler) SpaceMembers

func (h *Handler) SpaceMembers(c fiber.Ctx) error

SpaceMembers POST /spaces/members.

func (*Handler) SpaceMembersList

func (h *Handler) SpaceMembersList(c fiber.Ctx) error

SpaceMembersList POST /spaces/members/list - a mirror fetches the next page of members.

func (*Handler) SpaceMessageCreate

func (h *Handler) SpaceMessageCreate(c fiber.Ctx) error

SpaceMessageCreate POST /spaces/messages.

func (*Handler) SpaceMessageDelete

func (h *Handler) SpaceMessageDelete(c fiber.Ctx) error

SpaceMessageDelete POST /spaces/messages/delete.

func (*Handler) SpaceMessageEdit

func (h *Handler) SpaceMessageEdit(c fiber.Ctx) error

SpaceMessageEdit PATCH /spaces/messages.

func (*Handler) SpaceMessageGet

func (h *Handler) SpaceMessageGet(c fiber.Ctx) error

SpaceMessageGet POST /spaces/messages/get.

func (*Handler) SpaceMessagesList

func (h *Handler) SpaceMessagesList(c fiber.Ctx) error

SpaceMessagesList POST /spaces/messages/list - a page of history as the member sees it.

func (*Handler) SpaceMessagesSearch

func (h *Handler) SpaceMessagesSearch(c fiber.Ctx) error

SpaceMessagesSearch POST /spaces/messages/search - the origin's side: search a hosted space (or one channel of it) as the asking member.

func (*Handler) SpacePeers

func (h *Handler) SpacePeers(c fiber.Ctx) error

SpacePeers POST /spaces/peers - the origin says an instance joined or left the space.

func (*Handler) SpacePin

func (h *Handler) SpacePin(c fiber.Ctx) error

SpacePin POST /spaces/pins: a mirror's member pins or unpins in a channel hosted here. The origin checks Manage Messages for them, posts the notice and relays to every other mirror; the reply is the event as applied, so the asking mirror records the same pinned_at.

func (*Handler) SpacePinsList

func (h *Handler) SpacePinsList(c fiber.Ctx) error

SpacePinsList POST /spaces/pins/list - a hosted channel's pin list as the member sees it.

func (*Handler) SpaceReactionAdd

func (h *Handler) SpaceReactionAdd(c fiber.Ctx) error

func (*Handler) SpaceReactionRemove

func (h *Handler) SpaceReactionRemove(c fiber.Ctx) error

func (*Handler) SpaceRoles

func (h *Handler) SpaceRoles(c fiber.Ctx) error

SpaceRoles POST /spaces/roles.

func (*Handler) SpaceRooms

func (h *Handler) SpaceRooms(c fiber.Ctx) error

SpaceRooms POST /spaces/rooms.

func (*Handler) SpaceSync

func (h *Handler) SpaceSync(c fiber.Ctx) error

SpaceSync POST /spaces/sync.

func (*Handler) SpaceUpdate

func (h *Handler) SpaceUpdate(c fiber.Ctx) error

SpaceUpdate POST /spaces/update.

func (*Handler) ToDevice

func (h *Handler) ToDevice(c fiber.Ctx) error

ToDevice POST /to_device - Olm/Megolm to-device traffic for this instance's users.

func (*Handler) UserUpdated

func (h *Handler) UserUpdated(c fiber.Ctx) error

UserUpdated POST /users/update - refresh a shadow and tell local rooms.

func (*Handler) VoiceCall

func (h *Handler) VoiceCall(c fiber.Ctx) error

VoiceCall POST /rooms/voice/call - mirror a call event the origin reported.

func (*Handler) VoiceDecline

func (h *Handler) VoiceDecline(c fiber.Ctx) error

VoiceDecline POST /rooms/voice/decline.

func (*Handler) VoiceJoin

func (h *Handler) VoiceJoin(c fiber.Ctx) error

VoiceJoin POST /rooms/voice/join - mint a token for one of the requesting instance's users in a call this instance hosts.

func (*Handler) VoiceLeave

func (h *Handler) VoiceLeave(c fiber.Ctx) error

VoiceLeave POST /rooms/voice/leave.

func (*Handler) VoiceRing

func (h *Handler) VoiceRing(c fiber.Ctx) error

VoiceRing POST /rooms/voice/ring.

func (*Handler) VoiceSelf

func (h *Handler) VoiceSelf(c fiber.Ctx) error

VoiceSelf POST /rooms/voice/self.

func (*Handler) VoiceState

func (h *Handler) VoiceState(c fiber.Ctx) error

VoiceState POST /rooms/voice/state - mirror a state the origin reported.

type InstanceInfo

type InstanceInfo struct {
	Domain        string       `json:"domain"`
	Version       int          `json:"version"`
	APIURL        string       `json:"api_url"`
	GatewayURL    string       `json:"gateway_url,omitempty"`
	FederationURL string       `json:"federation_url"`
	KeyID         string       `json:"key_id"`
	PublicKey     string       `json:"public_key"`
	Software      SoftwareInfo `json:"software"`
}

InstanceInfo is what an instance publishes at /.well-known/strafe (and mirrors at /federation/v1/instance): enough for a peer to find its federation endpoint and verify its signatures, and for a client to discover where to connect.

type KeysClaim

type KeysClaim struct {
	OneTimeKeys map[string]map[string]string `json:"one_time_keys"`
}

type KeysQuery

type KeysQuery struct {
	DeviceKeys map[string][]string `json:"device_keys"`
}

KeysQuery / KeysClaim mirror the Matrix-shaped device endpoints, scoped to the receiver's own users.

type MessageDelete

type MessageDelete struct {
	Room    RoomRef    `json:"room"`
	Message MessageRef `json:"message"`
}

MessageDelete: POST /rooms/messages/delete.

type MessageEdit

type MessageEdit struct {
	Room       RoomRef    `json:"room"`
	Message    MessageRef `json:"message"`
	Ciphertext string     `json:"ciphertext,omitempty"`
	Plaintext  string     `json:"plaintext,omitempty"`
	Emojis     []EmojiRef `json:"emojis,omitempty"`
}

MessageEdit: PATCH /rooms/messages.

type MessageEvent

type MessageEvent struct {
	Room            RoomRef               `json:"room"`
	Message         MessageRef            `json:"message"`
	Sender          *Profile              `json:"sender,omitempty"`
	SenderDeviceID  string                `json:"sender_device_id,omitempty"`
	Ciphertext      string                `json:"ciphertext,omitempty"`
	Plaintext       string                `json:"plaintext,omitempty"`
	ReplyTo         *MessageRef           `json:"reply_to,omitempty"`
	Mentions        []string              `json:"mentions,omitempty"`
	MentionRoles    []string              `json:"mention_roles,omitempty"` // space channels: the origin's role ids
	MentionEveryone bool                  `json:"mention_everyone,omitempty"`
	Attachments     []messages.Attachment `json:"attachments,omitempty"`
	// Emojis describes the custom emoji the text uses; mentions in the text are <@FID>.
	Emojis        []EmojiRef `json:"emojis,omitempty"`
	SystemType    string     `json:"system_type,omitempty"`
	SystemPayload string     `json:"system_payload,omitempty"`
	CreatedAt     time.Time  `json:"created_at"`
	UpdatedAt     time.Time  `json:"updated_at,omitempty"`
	// Reactions is set on history read from a space's origin (as the asking user sees
	// them); never on relays.
	Reactions []messages.ReactionSummary `json:"reactions,omitempty"`
	// Pinned state, carried on history and pin lists read from a space's origin.
	PinnedAt *time.Time `json:"pinned_at,omitempty"`
	PinnedBy string     `json:"pinned_by,omitempty"` // FID
}

MessageEvent: POST /rooms/messages - a new message (including system messages, which have SystemType set and no sender).

type MessageMapping

type MessageMapping struct {
	RoomID          int64  `db:"room_id"`
	MessageID       int64  `db:"message_id"`
	OriginDomain    string `db:"origin_domain"`
	OriginMessageID int64  `db:"origin_message_id"`
}

MessageMapping ties a local message to the id its origin instance uses.

type MessageRef

type MessageRef struct {
	OriginDomain    string `json:"origin_domain"`
	OriginMessageID string `json:"origin_message_id"`
}

type OutboxEntry

type OutboxEntry struct {
	Domain    string    `db:"domain"`
	Seq       int64     `db:"seq"`
	Method    string    `db:"method"`
	Path      string    `db:"path"`
	Body      string    `db:"body"`
	CreatedAt time.Time `db:"created_at"`
}

OutboxEntry is one relay waiting to be delivered to a peer (see outbox.go).

type ParticipantsUpdate

type ParticipantsUpdate struct {
	Room         RoomRef   `json:"room"`
	Participants []Profile `json:"participants"`
	Removed      []string  `json:"removed,omitempty"`
}

ParticipantsUpdate: PUT /rooms/participants - the full current member set plus who left.

type Peer

type Peer struct {
	Domain        string    `db:"domain"`
	KeyID         string    `db:"key_id"`
	PublicKey     string    `db:"public_key"`
	FederationURL string    `db:"federation_url"`
	APIURL        string    `db:"api_url"`
	FirstSeen     time.Time `db:"first_seen"`
	LastSeen      time.Time `db:"last_seen"`
	Blocked       bool      `db:"blocked"`
	// LastError/LastFailure: set while the outbox cannot deliver to the peer, cleared by
	// the next relay it takes.
	LastError   string     `db:"last_error"`
	LastFailure *time.Time `db:"last_failure"`
}

Peer is a remote instance we've talked to.

type PinEvent

type PinEvent struct {
	Room     RoomRef    `json:"room"`
	Message  MessageRef `json:"message"`
	User     string     `json:"user"` // FID, must belong to the requesting instance
	PinnedAt time.Time  `json:"pinned_at"`
	Remove   bool       `json:"remove,omitempty"`
}

PinEvent: POST /rooms/pins (to a PM's other instances and a channel's mirrors) and POST /spaces/pins (to a channel's origin, by a mirror): User pinned - or, with Remove, unpinned - Message in Room at PinnedAt. The origin answers /spaces/pins with the event as applied (its PinnedAt).

type PinItem

type PinItem struct {
	PinnedAt time.Time    `json:"pinned_at"`
	PinnedBy string       `json:"pinned_by,omitempty"` // FID
	Message  MessageEvent `json:"message"`
}

type PinsQuery

type PinsQuery struct {
	Room RoomRef `json:"room"`
	User string  `json:"user"` // FID of the member asking
}

PinsQuery / PinsReply: POST /spaces/pins/list - a hosted channel's pin list from its origin, as the asking member sees it, newest pin first.

type PinsReply

type PinsReply struct {
	Items []PinItem `json:"items"`
}

type PresenceEvent

type PresenceEvent struct {
	User     string              `json:"user"` // FID, must belong to the requesting instance
	Presence auth.PublicPresence `json:"presence"`
}

PresenceEvent: POST /users/presence - how a user on the sending instance now appears to others (invisible already reads as offline), sent to the home instances of their remote friends.

type Profile

type Profile struct {
	FID         string `json:"fid"`
	Username    string `json:"username"`
	DisplayName string `json:"display_name"`
	Avatar      string `json:"avatar,omitempty"`
	Banner      string `json:"banner,omitempty"`
	Bio         string `json:"bio,omitempty"`
	AboutMe     string `json:"about_me,omitempty"`
	Pronouns    string `json:"pronouns,omitempty"`
	Bot         bool   `json:"bot,omitempty"`
}

Profile is a user as one instance describes them to another. Carrying the profile with every reference means a receiver can create the shadow row without a round trip.

type ProfileUpdate

type ProfileUpdate struct {
	User Profile `json:"user"`
}

ProfileUpdate: POST /users/update - a user on the sending instance changed their profile.

type ReactionEvent

type ReactionEvent struct {
	Room     RoomRef    `json:"room"`
	Message  MessageRef `json:"message"`
	User     string     `json:"user"` // FID, must belong to the requesting instance
	Emoji    string     `json:"emoji"`
	EmojiRef *EmojiRef  `json:"emoji_ref,omitempty"`
}

ReactionEvent: POST /rooms/reactions and POST /rooms/reactions/delete - a user on the sending instance reacted to (or withdrew a reaction from) a message in a shared room. A "custom:<id>" emoji is a custom emoji id; EmojiRef describes it so the receiving instance can render it even when it has never seen the emoji's space (content.go).

type RelationshipEvent

type RelationshipEvent struct {
	Action string  `json:"action"` // request | accept | remove
	Actor  Profile `json:"actor"`
	Target string  `json:"target"` // FID of a user on the receiving instance
}

RelationshipEvent: POST /relationships - a user on the sending instance sent a friend request to, accepted one from, or dropped one of the receiver's users. "remove" covers every teardown (withdrawn, declined, unfriended, blocked): the receiver's own state says which it was, and a block itself is never announced.

type RelayEvent

type RelayEvent struct {
	Method string          `json:"method"`
	Path   string          `json:"path"`
	Body   json.RawMessage `json:"body"`
}

RelayEvent is one relay as it would have gone out: the request a peer would have received. The origin hands these back in the reply to a request that caused them, so the asking instance applies exactly what every other mirror gets.

type Repository

type Repository interface {
	GetRoomMapping(ctx context.Context, roomID int64) (*RoomMapping, error)
	// GetRoomMappings is GetRoomMapping for many rooms in one query (missing ids absent).
	GetRoomMappings(ctx context.Context, roomIDs []int64) (map[int64]*RoomMapping, error)
	GetRoomByOrigin(ctx context.Context, originDomain string, originRoomID int64) (*RoomMapping, error)
	PutRoomMapping(ctx context.Context, m *RoomMapping) error
	DeleteRoomMapping(ctx context.Context, m *RoomMapping) error

	GetSpaceMapping(ctx context.Context, spaceID int64) (*SpaceMapping, error)
	GetSpaceByOrigin(ctx context.Context, originDomain string, originSpaceID int64) (*SpaceMapping, error)
	PutSpaceMapping(ctx context.Context, m *SpaceMapping) error
	DeleteSpaceMapping(ctx context.Context, m *SpaceMapping) error
	// ListSpaceMappings scans every mapping (mirrors and hosted spaces alike).
	ListSpaceMappings(ctx context.Context) ([]SpaceMapping, error)

	// Space peers: the instances mirroring a space this instance hosts.
	ListSpacePeers(ctx context.Context, spaceID int64) ([]string, error)
	IsSpacePeer(ctx context.Context, spaceID int64, domain string) (bool, error)
	AddSpacePeer(ctx context.Context, spaceID int64, domain string) error
	RemoveSpacePeer(ctx context.Context, spaceID int64, domain string) error
	DeleteSpacePeers(ctx context.Context, spaceID int64) error

	GetMessageMapping(ctx context.Context, roomID, messageID int64) (*MessageMapping, error)
	GetMessageByOrigin(ctx context.Context, originDomain string, originMessageID int64) (*MessageMapping, error)
	PutMessageMapping(ctx context.Context, m *MessageMapping) error

	GetPeer(ctx context.Context, domain string) (*Peer, error)
	UpsertPeer(ctx context.Context, p *Peer) error
	ListPeers(ctx context.Context) ([]Peer, error)
	MarkPeerFailure(ctx context.Context, domain, message string, at time.Time) error
	ClearPeerFailure(ctx context.Context, domain string) error

	// Outbox: relays waiting for delivery, per peer, oldest first.
	PutOutbox(ctx context.Context, e *OutboxEntry, ttl time.Duration) error
	ListOutbox(ctx context.Context, domain string, limit int) ([]OutboxEntry, error)
	DeleteOutbox(ctx context.Context, domain string, seq int64) error
	ListOutboxDomains(ctx context.Context) ([]string, error)
	CountOutbox(ctx context.Context, domain string) (int, error)
}

func NewRepository

func NewRepository(session gocqlx.Session) Repository

type RoomAnnounce

type RoomAnnounce struct {
	Room         RoomRef   `json:"room"`
	Type         int       `json:"type"`
	Name         string    `json:"name,omitempty"`
	E2EEEnabled  bool      `json:"e2ee_enabled"`
	Creator      string    `json:"creator,omitempty"`
	Participants []Profile `json:"participants"`
}

RoomAnnounce: POST /rooms - the origin instance tells a peer about a new room some of its users are in. Idempotent on (origin_domain, origin_room_id).

type RoomAnnounceReply

type RoomAnnounceReply struct {
	RoomID string `json:"room_id"`
}

RoomAnnounceReply returns the receiver's local room id (for diagnostics only).

type RoomMapping

type RoomMapping struct {
	RoomID       int64     `db:"room_id"`
	OriginDomain string    `db:"origin_domain"`
	OriginRoomID int64     `db:"origin_room_id"`
	CreatedAt    time.Time `db:"created_at"`
}

RoomMapping ties a local room to its global identity.

type RoomPatch

type RoomPatch struct {
	Room        RoomRef `json:"room"`
	Name        *string `json:"name,omitempty"`
	E2EEEnabled *bool   `json:"e2ee_enabled,omitempty"`
}

RoomPatch: PATCH /rooms - name / E2EE setting changes.

type RoomRef

type RoomRef struct {
	OriginDomain string `json:"origin_domain"`
	OriginRoomID string `json:"origin_room_id"`
}

type Service

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

Service is the federation engine: outbound relays (implements rooms.Federator, messages.Federator, spaces.Federator, devices.KeyRouter/FIDResolver) plus the state inbound handlers use.

func New

func New(cfg *config.Config, session gocqlx.Session, rdb *redis.Client) (*Service, error)

New builds the engine with its own repository/service instances (stateless wrappers over the same Scylla session the API uses).

func NewWithExistingKey

func NewWithExistingKey(cfg *config.Config, session gocqlx.Session, rdb *redis.Client) (*Service, error)

NewWithExistingKey is New for a process that shares the API's signing key but must never create it (the gateway): until the API has written the key this fails instead of minting a second identity that no peer would accept.

func (*Service) AfterCallChanged

func (s *Service) AfterCallChanged(ctx context.Context, room *rooms.RoomWithParticipants, event string, call *voice.Call, starter *auth.User, rerung bool)

func (*Service) AfterMessageCreated

func (s *Service) AfterMessageCreated(ctx context.Context, roomID int64, participants []int64, m *messages.Message)

func (*Service) AfterMessageDeleted

func (s *Service) AfterMessageDeleted(ctx context.Context, roomID int64, participants []int64, msgID int64)

func (*Service) AfterMessageEdited

func (s *Service) AfterMessageEdited(ctx context.Context, roomID int64, participants []int64, m *messages.Message)

func (*Service) AfterMessagePinned

func (s *Service) AfterMessagePinned(ctx context.Context, roomID int64, participants []int64, msgID, userID int64, at time.Time, remove bool)

AfterMessagePinned tells the room's other instances about a pin or unpin made here: a PM's other participants' instances, or every mirror of a channel hosted here.

func (*Service) AfterParticipantsChanged

func (s *Service) AfterParticipantsChanged(ctx context.Context, rwp *rooms.RoomWithParticipants, removed []int64)

func (*Service) AfterPresenceChanged

func (s *Service) AfterPresenceChanged(ctx context.Context, u *auth.User)

AfterPresenceChanged tells the home instance of each remote friend, and every instance sharing a space with the user, how the user now appears to others - the same audience the local gateway tells.

func (*Service) AfterProfileUpdated

func (s *Service) AfterProfileUpdated(ctx context.Context, u *auth.User)

AfterProfileUpdated tells every instance that holds a shadow of this user (any peer sharing a room or a space with them, or the home of a remote friend) about the new profile.

func (*Service) AfterReactionAdded

func (s *Service) AfterReactionAdded(ctx context.Context, roomID int64, participants []int64, msgID, userID int64, emoji string)

func (*Service) AfterReactionRemoved

func (s *Service) AfterReactionRemoved(ctx context.Context, roomID int64, participants []int64, msgID, userID int64, emoji string)

func (*Service) AfterRelationshipAccepted

func (s *Service) AfterRelationshipAccepted(_ context.Context, actor, target *auth.User)

func (*Service) AfterRelationshipRemoved

func (s *Service) AfterRelationshipRemoved(_ context.Context, actor, target *auth.User)

func (*Service) AfterRelationshipRequested

func (s *Service) AfterRelationshipRequested(_ context.Context, actor, target *auth.User)

func (*Service) AfterRoomCreated

func (s *Service) AfterRoomCreated(ctx context.Context, rwp *rooms.RoomWithParticipants)

func (*Service) AfterRoomUpdated

func (s *Service) AfterRoomUpdated(ctx context.Context, rwp *rooms.RoomWithParticipants)

func (*Service) AfterSpaceDeleted

func (s *Service) AfterSpaceDeleted(ctx context.Context, sp *spaces.Space)

func (*Service) AfterSpaceEmojiChanged

func (s *Service) AfterSpaceEmojiChanged(ctx context.Context, spaceID int64, e *spaces.SpaceEmoji, deleted bool)

func (*Service) AfterSpaceMemberAdded

func (s *Service) AfterSpaceMemberAdded(ctx context.Context, spaceID int64, m *spaces.SpaceMember)

func (*Service) AfterSpaceMemberRemoved

func (s *Service) AfterSpaceMemberRemoved(ctx context.Context, spaceID, userID int64, how string)

func (*Service) AfterSpaceMemberUpdated

func (s *Service) AfterSpaceMemberUpdated(ctx context.Context, spaceID int64, m *spaces.SpaceMember)

func (*Service) AfterSpaceRoleChanged

func (s *Service) AfterSpaceRoleChanged(ctx context.Context, spaceID int64, role *spaces.SpaceRole, deleted bool)

func (*Service) AfterSpaceRoomChanged

func (s *Service) AfterSpaceRoomChanged(ctx context.Context, spaceID int64, room *rooms.Room, overrides spaces.RoomOverrides, deleted bool)

func (*Service) AfterSpaceRoomsReordered

func (s *Service) AfterSpaceRoomsReordered(ctx context.Context, spaceID int64, list []*rooms.Room)

func (*Service) AfterSpaceUpdated

func (s *Service) AfterSpaceUpdated(ctx context.Context, sp *spaces.Space)

func (*Service) AfterTyping

func (s *Service) AfterTyping(ctx context.Context, roomID, userID int64)

func (*Service) AfterVoiceStateChanged

func (s *Service) AfterVoiceStateChanged(ctx context.Context, room *rooms.RoomWithParticipants, st *voice.State, left bool)

func (*Service) ClaimRemote

func (s *Service) ClaimRemote(ctx context.Context, domain string, req map[string]map[string]string) (*devices.ClaimKeysOutput, error)

func (*Service) CreateRemote

func (s *Service) CreateRemote(ctx context.Context, origin string, room *rooms.Room, userID int64, in *messages.CreateMessageInput, attachments []messages.Attachment) (*messages.Message, error)

func (*Service) CreateRemoteInvite

func (s *Service) CreateRemoteInvite(ctx context.Context, origin string, spaceID int64, user *auth.User, in *spaces.CreateInviteInput) (*spaces.SpaceInvite, error)

func (*Service) DeclineRemoteCall

func (s *Service) DeclineRemoteCall(ctx context.Context, origin string, room *rooms.RoomWithParticipants, user *auth.User)

func (*Service) DeleteRemote

func (s *Service) DeleteRemote(ctx context.Context, origin string, room *rooms.Room, userID, msgID int64) error

func (*Service) Domain

func (s *Service) Domain() string

func (*Service) EditRemote

func (s *Service) EditRemote(ctx context.Context, origin string, room *rooms.Room, userID, msgID int64, in *messages.EditMessageInput) (*messages.Message, error)

func (*Service) Enabled

func (s *Service) Enabled() bool

func (*Service) EnsureShadow

func (s *Service) EnsureShadow(ctx context.Context, domain string, p Profile) (*auth.User, error)

EnsureShadow creates or refreshes the local shadow row for a remote user. The profile's FID must be at `domain` - a peer can only vouch for its own users.

func (*Service) FIDOf

func (s *Service) FIDOf(u *auth.User) string

FIDOf is a user's federated id: their home instance's id for them at their home domain.

func (*Service) FetchDirectory

func (s *Service) FetchDirectory(ctx context.Context, domain string) ([]discover.RemoteListing, error)

FetchDirectory asks one peer what it lists.

func (*Service) GetRemote

func (s *Service) GetRemote(ctx context.Context, origin string, room *rooms.Room, userID, msgID int64) (*messages.Message, []messages.ReactionSummary, error)

func (*Service) Info

func (s *Service) Info() *InstanceInfo

Info is this instance's /.well-known/strafe document.

func (*Service) IsLocalServer

func (s *Service) IsLocalServer(server string) bool

IsLocalServer: the server part of @id:server names this instance (or the pre-federation synthetic name, or nothing at all).

func (*Service) JoinListedSpace

func (s *Service) JoinListedSpace(ctx context.Context, domain string, originSpaceID, userID int64) (*spaces.Space, error)

JoinListedSpace joins a space `domain` lists publicly, on behalf of a local user, and builds this instance's mirror of it from the answer - the same path an invite join takes.

func (*Service) JoinRemoteSpace

func (s *Service) JoinRemoteSpace(ctx context.Context, domain, code string, user *auth.User) (*spaces.Space, error)

func (*Service) JoinRemoteVoice

func (s *Service) JoinRemoteVoice(ctx context.Context, origin string, room *rooms.RoomWithParticipants, user *auth.User, in voice.JoinInput) (*voice.RemoteJoin, error)

func (*Service) LeaveRemoteSpace

func (s *Service) LeaveRemoteSpace(ctx context.Context, origin string, spaceID int64, user *auth.User) error

func (*Service) LeaveRemoteVoice

func (s *Service) LeaveRemoteVoice(ctx context.Context, origin string, room *rooms.RoomWithParticipants, user *auth.User)

func (*Service) ListAfterRemote

func (s *Service) ListAfterRemote(ctx context.Context, origin string, room *rooms.Room, userID, afterID int64, limit int) ([]messages.Message, map[int64][]messages.ReactionSummary, error)

ListAfterRemote / ListAroundRemote: the other two history cursors (jump-to-message and reading downward out of a jumped-to window), served by the origin like ListRemote.

func (*Service) ListAroundRemote

func (s *Service) ListAroundRemote(ctx context.Context, origin string, room *rooms.Room, userID, aroundID int64, limit int) ([]messages.Message, map[int64][]messages.ReactionSummary, error)

func (*Service) ListPinsRemote

func (s *Service) ListPinsRemote(ctx context.Context, origin string, room *rooms.Room, userID int64) ([]messages.PinnedMessage, error)

ListPinsRemote reads a hosted channel's pin list from its origin as the local member, and keeps a copy of the messages like history does.

func (*Service) ListRemote

func (s *Service) ListRemote(ctx context.Context, origin string, room *rooms.Room, userID int64, beforeID *int64, limit int) ([]messages.Message, map[int64][]messages.ReactionSummary, error)

func (*Service) LocalFID

func (s *Service) LocalFID(userID int64) string

func (*Service) PeerDomains

func (s *Service) PeerDomains(ctx context.Context) []string

PeerDomains is every instance this one may talk to: the ones it has met and the ones configured statically, minus anything the allow/block policy refuses.

func (*Service) PinRemote

func (s *Service) PinRemote(ctx context.Context, origin string, room *rooms.Room, userID, msgID int64, remove bool) error

PinRemote asks a channel's origin to pin or unpin as the local member: the origin checks the permission, posts the notice and tells every other mirror; this instance applies the origin's answer to its own copy.

func (*Service) PreviewRemoteInvite

func (s *Service) PreviewRemoteInvite(ctx context.Context, domain, code string) (*spaces.InvitePreview, error)

func (*Service) ProfileOf

func (s *Service) ProfileOf(u *auth.User) Profile

func (*Service) QueryRemote

func (s *Service) QueryRemote(ctx context.Context, domain string, req map[string][]string) (*devices.QueryKeysOutput, error)

func (*Service) ReactRemote

func (s *Service) ReactRemote(ctx context.Context, origin string, room *rooms.Room, userID, msgID int64, emoji string, remove bool) ([]messages.ReactionSummary, error)

func (*Service) RemoteAddMember

func (s *Service) RemoteAddMember(ctx context.Context, origin string, spaceID int64, actor, user *auth.User) (bool, error)

func (*Service) RemoteCreateEmoji

func (s *Service) RemoteCreateEmoji(ctx context.Context, origin string, spaceID int64, user *auth.User, emojiID int64, name, url string, animated bool) (*spaces.SpaceEmoji, error)

func (*Service) RemoteCreateRole

func (s *Service) RemoteCreateRole(ctx context.Context, origin string, spaceID int64, user *auth.User, in *spaces.CreateSpaceRoleInput) (*spaces.SpaceRole, error)

func (*Service) RemoteCreateRoom

func (s *Service) RemoteCreateRoom(ctx context.Context, origin string, spaceID int64, user *auth.User, in *spaces.CreateRoomInput) (*rooms.Room, error)

func (*Service) RemoteDeleteEmoji

func (s *Service) RemoteDeleteEmoji(ctx context.Context, origin string, spaceID int64, user *auth.User, emojiID int64) (*spaces.SpaceEmoji, error)

func (*Service) RemoteDeleteInvite

func (s *Service) RemoteDeleteInvite(ctx context.Context, origin string, spaceID int64, user *auth.User, code string) error

func (*Service) RemoteDeleteRole

func (s *Service) RemoteDeleteRole(ctx context.Context, origin string, spaceID int64, user *auth.User, roleID int64) error

func (*Service) RemoteDeleteRoom

func (s *Service) RemoteDeleteRoom(ctx context.Context, origin string, spaceID int64, user *auth.User, roomID int64) error

func (*Service) RemoteDeleteSpace

func (s *Service) RemoteDeleteSpace(ctx context.Context, origin string, spaceID int64, user *auth.User, confirmName string) error

func (*Service) RemoteInstallBot

func (s *Service) RemoteInstallBot(ctx context.Context, origin string, spaceID int64, user, bot *auth.User, permissions int64) (int64, error)

func (*Service) RemoteListAuditLog

func (s *Service) RemoteListAuditLog(ctx context.Context, origin string, spaceID int64, user *auth.User, beforeID int64, limit int, action string) ([]spaces.SpaceAuditEntry, map[int64]*auth.User, error)

func (*Service) RemoteListBans

func (s *Service) RemoteListBans(ctx context.Context, origin string, spaceID int64, user *auth.User) ([]spaces.SpaceBan, map[int64]*auth.User, error)

func (*Service) RemoteListInvites

func (s *Service) RemoteListInvites(ctx context.Context, origin string, spaceID int64, user *auth.User) ([]spaces.SpaceInvite, map[int64]*auth.User, error)

func (*Service) RemoteModerate

func (s *Service) RemoteModerate(ctx context.Context, origin string, spaceID int64, user *auth.User, action string, targetID int64, reason string) error

func (*Service) RemoteMoveChannel

func (s *Service) RemoteMoveChannel(ctx context.Context, origin string, spaceID int64, user *auth.User, channelID int64, parent, before *int64) error

func (*Service) RemoteOrigin

func (s *Service) RemoteOrigin(ctx context.Context, room *rooms.Room) string

func (*Service) RemoteOverride

func (s *Service) RemoteOverride(ctx context.Context, origin string, spaceID int64, user *auth.User, roomID, roleID, targetUserID int64, allow, deny int64, remove bool) error

func (*Service) RemotePatchSpace

func (s *Service) RemotePatchSpace(ctx context.Context, origin string, spaceID int64, user *auth.User, in *spaces.PatchSpaceInput) (*spaces.Space, error)

func (*Service) RemoteRenameEmoji

func (s *Service) RemoteRenameEmoji(ctx context.Context, origin string, spaceID int64, user *auth.User, emojiID int64, name string) (*spaces.SpaceEmoji, error)

func (*Service) RemoteReorderRooms

func (s *Service) RemoteReorderRooms(ctx context.Context, origin string, spaceID int64, user *auth.User, in *spaces.ReorderRoomsInput) error

func (*Service) RemoteSetMemberRoles

func (s *Service) RemoteSetMemberRoles(ctx context.Context, origin string, spaceID int64, user *auth.User, targetID int64, roleIDs []int64) error

func (*Service) RemoteSetSpaceImage

func (s *Service) RemoteSetSpaceImage(ctx context.Context, origin string, spaceID int64, user *auth.User, kind, url string) (*spaces.Space, error)

func (*Service) RemoteSpaceOrigin

func (s *Service) RemoteSpaceOrigin(ctx context.Context, spaceID int64) string

RemoteSpaceOrigin names the instance hosting a space when it is not this one.

func (*Service) RemoteTransferOwnership

func (s *Service) RemoteTransferOwnership(ctx context.Context, origin string, spaceID int64, user *auth.User, newOwnerID int64) (*spaces.Space, error)

func (*Service) RemoteUpdateRole

func (s *Service) RemoteUpdateRole(ctx context.Context, origin string, spaceID int64, user *auth.User, roleID int64, in *spaces.UpdateSpaceRoleInput) (*spaces.SpaceRole, error)

func (*Service) RemoteUpdateRoom

func (s *Service) RemoteUpdateRoom(ctx context.Context, origin string, spaceID int64, user *auth.User, roomID int64, in *spaces.UpdateRoomInput) error

func (*Service) RequireInstance

func (s *Service) RequireInstance() fiber.Handler

RequireInstance verifies the request signature (see signing.go), the peer policy, the timestamp window and nonce uniqueness, then records the peer domain in Locals.

func (*Service) ResolveHandle

func (s *Service) ResolveHandle(ctx context.Context, raw string) (*auth.User, error)

ResolveHandle turns "name@domain" into a local user row: the local user for this instance's own domain (or no domain), or an up-to-date shadow row fetched from the remote instance.

func (*Service) ResolveLocalID

func (s *Service) ResolveLocalID(ctx context.Context, fid string) (int64, error)

ResolveLocalID maps a federated id to the local row that represents it.

func (*Service) ResyncMirror

func (s *Service) ResyncMirror(ctx context.Context, m *SpaceMapping) error

ResyncMirror reconciles one mirror against its origin's current snapshot. A space the origin no longer has is dropped here too.

func (*Service) ResyncRemote

func (s *Service) ResyncRemote(ctx context.Context, origin string, spaceID int64) error

ResyncRemote is spaces.Federator's resync of one mirror.

func (*Service) RingRemoteCall

func (s *Service) RingRemoteCall(ctx context.Context, origin string, room *rooms.RoomWithParticipants, user *auth.User, targets []int64)

func (*Service) RoomFederation

func (s *Service) RoomFederation(ctx context.Context, roomID int64) *rooms.Federation

func (*Service) RoomFederations

func (s *Service) RoomFederations(ctx context.Context, roomIDs []int64) map[int64]*rooms.Federation

func (*Service) SearchRemote

func (s *Service) SearchRemote(ctx context.Context, origin string, room *rooms.Room, userID int64, opts *messages.SearchOptions) (*messages.SearchResult, error)

func (*Service) SearchSpaceRemote

func (s *Service) SearchSpaceRemote(ctx context.Context, origin string, spaceID, userID int64, opts *messages.SpaceSearchOptions) (*messages.SpaceSearchResult, error)

func (*Service) SendToDeviceRemote

func (s *Service) SendToDeviceRemote(ctx context.Context, domain string, senderUserID, senderDeviceID int64, eventType string, msgs map[string]map[string]json.RawMessage) error

func (*Service) SetDirectory

func (s *Service) SetDirectory(d DirectorySource)

func (*Service) SetSpaces

func (s *Service) SetSpaces(svc *spaces.Service)

SetSpaces hands in the API's spaces service (the one with the system-room messenger), so a remote member's join posts the same notice a local join does.

func (*Service) SetVoice

func (s *Service) SetVoice(v *voice.Service)

SetVoice wires the voice service federation serves remote users through.

func (*Service) SpaceFederation

func (s *Service) SpaceFederation(ctx context.Context, spaceID int64) *spaces.SpaceFederation

func (*Service) StartMaintenance

func (s *Service) StartMaintenance(ctx context.Context)

StartMaintenance makes this process the one that delivers queued relays (outbox.go) and runs ResyncMirror for every mirrored space on a timer until ctx ends.

func (*Service) UpdateRemoteVoiceSelf

func (s *Service) UpdateRemoteVoiceSelf(ctx context.Context, origin string, room *rooms.RoomWithParticipants, user *auth.User, in voice.SelfInput)

type Signer

type Signer struct {
	KeyID  string
	Public ed25519.PublicKey
	// contains filtered or unexported fields
}

Signer holds this instance's Ed25519 identity.

func LoadExistingSigner

func LoadExistingSigner(cfg config.FederationConfig) (*Signer, error)

LoadExistingSigner is LoadSigner for a process that shares the API's key but must never mint its own (the gateway): a missing key file is an error, not a new identity.

func LoadSigner

func LoadSigner(cfg config.FederationConfig) (*Signer, error)

LoadSigner builds the instance key from FEDERATION_SIGNING_KEY (base64 32-byte seed) or, when unset, from KeyFile - generating and writing a new seed there on first start so the identity is stable across restarts (mount that path on a volume in Docker).

func (*Signer) PublicKeyBase64

func (s *Signer) PublicKeyBase64() string

func (*Signer) Sign

func (s *Signer) Sign(msg []byte) string

Sign returns the base64 Ed25519 signature over msg.

type SoftwareInfo

type SoftwareInfo struct {
	Name    string `json:"name"`
	Version string `json:"version"`
}

type SpaceDeleteEvent

type SpaceDeleteEvent struct {
	Space SpaceRef `json:"space"`
}

type SpaceEmojiEvent

type SpaceEmojiEvent struct {
	Space  SpaceRef       `json:"space"`
	Action string         `json:"action"` // upsert | delete
	Emoji  SpaceEmojiWire `json:"emoji"`
}

SpaceEmojiEvent: POST /spaces/emoji - the origin reports a custom emoji added, renamed or removed. Emoji keep the origin's ids (messages reference them by id).

type SpaceEmojiWire

type SpaceEmojiWire struct {
	ID        string    `json:"id"`
	Name      string    `json:"name"`
	URL       string    `json:"url"`
	Animated  bool      `json:"animated"`
	Creator   string    `json:"creator,omitempty"`
	CreatedAt time.Time `json:"created_at"`
	UpdatedAt time.Time `json:"updated_at"`
}

type SpaceInfo

type SpaceInfo struct {
	Name                  string    `json:"name"`
	NameAcronym           string    `json:"name_acronym"`
	Description           string    `json:"description,omitempty"`
	Icon                  string    `json:"icon,omitempty"`
	Banner                string    `json:"banner,omitempty"`
	Owner                 string    `json:"owner"`
	VerificationLevel     int       `json:"verification_level"`
	DefaultMessageNotif   int       `json:"default_message_notifications"`
	ExplicitContentFilter int       `json:"explicit_content_filter"`
	Features              []string  `json:"features,omitempty"`
	AFKRoom               string    `json:"afk_room,omitempty"`
	AFKTimeout            int       `json:"afk_timeout"`
	SystemRoom            string    `json:"system_room,omitempty"`
	SystemRoomFlags       int       `json:"system_room_flags"`
	RulesRoom             string    `json:"rules_room,omitempty"`
	PublicUpdatesRoom     string    `json:"public_updates_room,omitempty"`
	WidgetRoom            string    `json:"widget_room,omitempty"`
	WidgetEnabled         bool      `json:"widget_enabled"`
	MaxPresences          int       `json:"max_presences"`
	MaxMembers            int       `json:"max_members"`
	MaxVideoRoomUsers     int       `json:"max_video_room_users"`
	VanityURLCode         string    `json:"vanity_url_code,omitempty"`
	PreferredLocale       string    `json:"preferred_locale,omitempty"`
	EveryoneRole          string    `json:"everyone_role"`
	CreatedAt             time.Time `json:"created_at"`
	UpdatedAt             time.Time `json:"updated_at"`
}

SpaceInfo is a space's settings as the origin describes them (room ids are its own).

type SpaceInvitePreviewReply

type SpaceInvitePreviewReply struct {
	Space       SpaceRef `json:"space"`
	Name        string   `json:"name"`
	NameAcronym string   `json:"name_acronym"`
	Description string   `json:"description,omitempty"`
	Icon        string   `json:"icon,omitempty"`
	Banner      string   `json:"banner,omitempty"`
	MemberCount int      `json:"member_count"`
	Inviter     string   `json:"inviter,omitempty"`
}

SpaceInvitePreviewReply: GET /spaces/invites/:code.

type SpaceInviteReply

type SpaceInviteReply struct {
	Code        string     `json:"code"`
	MaxUses     int        `json:"max_uses"`
	CurrentUses int        `json:"current_uses"`
	ExpiresAt   *time.Time `json:"expires_at,omitempty"`
	CreatedAt   time.Time  `json:"created_at"`
}

type SpaceInviteRequest

type SpaceInviteRequest struct {
	Space         SpaceRef `json:"space"`
	User          string   `json:"user"`
	MaxAgeSeconds int      `json:"max_age_seconds,omitempty"`
	MaxUses       int      `json:"max_uses,omitempty"`
}

SpaceInviteRequest: POST /spaces/invites - a member on the requesting instance wants an invite for a space hosted here.

type SpaceJoinListedRequest

type SpaceJoinListedRequest struct {
	Space    SpaceRef             `json:"space"`
	User     Profile              `json:"user"`
	Presence *auth.PublicPresence `json:"presence,omitempty"`
}

SpaceJoinListedRequest: POST /spaces/join_listed - a peer's user joining a space this instance lists publicly. No invite: the listing is the permission.

type SpaceJoinRequest

type SpaceJoinRequest struct {
	Code     string               `json:"code"`
	User     Profile              `json:"user"`
	Presence *auth.PublicPresence `json:"presence,omitempty"`
}

SpaceJoinRequest: POST /spaces/join - a user on the requesting instance redeems an invite for a space hosted here. Presence is how they appear right now, so the member every mirror is told about starts with their real status.

type SpaceManageReply

type SpaceManageReply struct {
	Result json.RawMessage `json:"result,omitempty"`
	Events []RelayEvent    `json:"events"`
}

SpaceManageReply: the op's result (shape per op) and the relays captured for the asking instance.

type SpaceManageRequest

type SpaceManageRequest struct {
	Space  SpaceRef        `json:"space"`
	User   string          `json:"user"` // FID, must belong to the requesting instance
	Op     string          `json:"op"`
	Params json.RawMessage `json:"params,omitempty"`
}

SpaceManageRequest: POST /spaces/manage.

type SpaceMapping

type SpaceMapping struct {
	SpaceID       int64     `db:"space_id"`
	OriginDomain  string    `db:"origin_domain"`
	OriginSpaceID int64     `db:"origin_space_id"`
	CreatedAt     time.Time `db:"created_at"`
}

SpaceMapping ties a local space (a mirror, or a hosted space that has started federating) to its global identity.

type SpaceMemberEvent

type SpaceMemberEvent struct {
	Space  SpaceRef        `json:"space"`
	Action string          `json:"action"` // add | update | remove
	Member SpaceMemberWire `json:"member"`
	How    string          `json:"how,omitempty"` // remove: kicked | banned | left
}

type SpaceMemberWire

type SpaceMemberWire struct {
	User     Profile   `json:"user"`
	Roles    []string  `json:"roles"`
	Nickname string    `json:"nickname,omitempty"`
	JoinedAt time.Time `json:"joined_at"`
	// Presence as others see it, so a mirror's member list starts with real statuses
	// instead of "offline until their next change".
	Presence auth.PublicPresence `json:"presence"`
}

type SpaceMembersQuery

type SpaceMembersQuery struct {
	Space SpaceRef `json:"space"`
	After string   `json:"after,omitempty"`
}

SpaceMembersQuery: POST /spaces/members/list - the page of a hosted space's members after a cursor (a snapshot's members_next, or an earlier page's next).

type SpaceMembersReply

type SpaceMembersReply struct {
	Members []SpaceMemberWire `json:"members"`
	Next    string            `json:"next,omitempty"`
}

type SpaceMessageEditRequest

type SpaceMessageEditRequest struct {
	Room       RoomRef    `json:"room"`
	Message    MessageRef `json:"message"`
	User       string     `json:"user"`
	Ciphertext string     `json:"ciphertext,omitempty"`
	Plaintext  string     `json:"plaintext,omitempty"`
	Emojis     []EmojiRef `json:"emojis,omitempty"`
}

SpaceMessageEditRequest: PATCH /spaces/messages.

type SpaceMessageRequest

type SpaceMessageRequest struct {
	Room            RoomRef               `json:"room"`
	User            string                `json:"user"`
	SenderDeviceID  string                `json:"sender_device_id,omitempty"`
	Ciphertext      string                `json:"ciphertext,omitempty"`
	Plaintext       string                `json:"plaintext,omitempty"`
	ReplyTo         *MessageRef           `json:"reply_to,omitempty"`
	Mentions        []string              `json:"mentions,omitempty"`
	MentionRoles    []string              `json:"mention_roles,omitempty"`
	MentionEveryone bool                  `json:"mention_everyone,omitempty"`
	Attachments     []messages.Attachment `json:"attachments,omitempty"`
	// Emojis describes the custom emoji the text uses; mentions in it are <@FID> (content.go).
	Emojis []EmojiRef `json:"emojis,omitempty"`
}

SpaceMessageRequest: POST /spaces/messages - a member on the requesting instance sends into a channel hosted here. Attachments were uploaded to their own instance.

type SpaceMessageRequestRef

type SpaceMessageRequestRef struct {
	Room    RoomRef    `json:"room"`
	Message MessageRef `json:"message"`
	User    string     `json:"user"`
}

SpaceMessageRequestRef: POST /spaces/messages/delete and /spaces/messages/get.

type SpaceMessagesQuery

type SpaceMessagesQuery struct {
	Room RoomRef `json:"room"`
	User string  `json:"user"`
	// At most one of Before / After / Around (origin message ids): the page below a
	// message, the page above one (oldest-first), or the window around one - the same
	// three cursors GET /rooms/:id/messages takes.
	Before string `json:"before,omitempty"`
	After  string `json:"after,omitempty"`
	Around string `json:"around,omitempty"`
	Limit  int    `json:"limit,omitempty"`
}

SpaceMessagesQuery: POST /spaces/messages/list - a page of a channel's history as the asking member may see it.

type SpaceMessagesReply

type SpaceMessagesReply struct {
	Messages []MessageEvent `json:"messages"`
}

type SpaceOverrideWire

type SpaceOverrideWire struct {
	Role      string    `json:"role,omitempty"`
	User      string    `json:"user,omitempty"`
	Allow     int64     `json:"allow"`
	Deny      int64     `json:"deny"`
	CreatedAt time.Time `json:"created_at"`
	UpdatedAt time.Time `json:"updated_at"`
}

SpaceOverrideWire is a room override for a role (Role set) or a user (User set).

type SpacePeer

type SpacePeer struct {
	SpaceID   int64     `db:"space_id"`
	Domain    string    `db:"domain"`
	CreatedAt time.Time `db:"created_at"`
}

SpacePeer is an instance that mirrors a space this instance hosts.

type SpacePeerEvent

type SpacePeerEvent struct {
	Space  SpaceRef `json:"space"`
	Action string   `json:"action"` // add | remove
	Domain string   `json:"domain"`
}

SpacePeerEvent: POST /spaces/peers - an instance started or stopped mirroring the space (origin → mirrors).

type SpaceReactionReply

type SpaceReactionReply struct {
	Reactions []messages.ReactionSummary `json:"reactions"`
}

type SpaceRef

type SpaceRef struct {
	OriginDomain  string `json:"origin_domain"`
	OriginSpaceID string `json:"origin_space_id"`
}

type SpaceRoleEvent

type SpaceRoleEvent struct {
	Space  SpaceRef      `json:"space"`
	Action string        `json:"action"` // upsert | delete
	Role   SpaceRoleWire `json:"role"`
}

type SpaceRoleWire

type SpaceRoleWire struct {
	ID          string    `json:"id"`
	Name        string    `json:"name"`
	Permissions int64     `json:"permissions"`
	Position    int       `json:"position"`
	Color       int       `json:"color"`
	Hoist       bool      `json:"hoist"`
	Mentionable bool      `json:"mentionable"`
	Bot         string    `json:"bot,omitempty"` // FID of the bot a managed role belongs to
	CreatedAt   time.Time `json:"created_at"`
	UpdatedAt   time.Time `json:"updated_at"`
}

type SpaceRoomEvent

type SpaceRoomEvent struct {
	Space  SpaceRef         `json:"space"`
	Action string           `json:"action"` // upsert | delete | reorder
	Room   *SpaceRoomWire   `json:"room,omitempty"`
	Order  []SpaceRoomOrder `json:"order,omitempty"`
}

type SpaceRoomOrder

type SpaceRoomOrder struct {
	ID       string `json:"id"`
	Parent   string `json:"parent,omitempty"`
	Position int    `json:"position"`
}

type SpaceRoomWire

type SpaceRoomWire struct {
	ID              string              `json:"id"`
	Type            int                 `json:"type"`
	Parent          string              `json:"parent,omitempty"`
	Name            string              `json:"name"`
	Topic           string              `json:"topic,omitempty"`
	SlowmodeSeconds int                 `json:"slowmode_seconds,omitempty"`
	Position        int                 `json:"position"`
	E2EEEnabled     bool                `json:"e2ee_enabled"`
	UserLimit       int                 `json:"user_limit,omitempty"`
	Bitrate         int                 `json:"bitrate,omitempty"`
	Overrides       []SpaceOverrideWire `json:"overrides,omitempty"`
	CreatedAt       time.Time           `json:"created_at"`
	UpdatedAt       time.Time           `json:"updated_at"`
	// Thread is set for a thread room (type 6): its state as the origin holds it. Thread
	// membership is not mirrored, so a private thread is not visible on a mirror.
	Thread *ThreadWire `json:"thread,omitempty"`
}

type SpaceSearchQuery

type SpaceSearchQuery struct {
	Space    SpaceRef `json:"space"`
	Room     *RoomRef `json:"room,omitempty"`
	User     string   `json:"user"`
	Query    string   `json:"query,omitempty"`
	From     string   `json:"from,omitempty"`
	Mentions string   `json:"mentions,omitempty"`
	Has      string   `json:"has,omitempty"`
	Before   string   `json:"before,omitempty"`
	Limit    int      `json:"limit,omitempty"`
}

SpaceSearchQuery: POST /spaces/messages/search - search a hosted space's text channels (or one of them, Room) as the asking member may read them. From and Mentions are FIDs.

type SpaceSearchReply

type SpaceSearchReply struct {
	Messages       []MessageEvent `json:"messages"`
	NextBefore     string         `json:"next_before,omitempty"`
	RoomsSearched  int            `json:"rooms_searched"`
	EncryptedRooms int            `json:"encrypted_rooms"`
}

type SpaceSnapshot

type SpaceSnapshot struct {
	Space       SpaceRef          `json:"space"`
	Info        SpaceInfo         `json:"info"`
	Roles       []SpaceRoleWire   `json:"roles"`
	Rooms       []SpaceRoomWire   `json:"rooms"`
	Members     []SpaceMemberWire `json:"members"`
	MembersNext string            `json:"members_next,omitempty"`
	Emoji       []SpaceEmojiWire  `json:"emoji,omitempty"`
	MemberCount int               `json:"member_count"`
	Peers       []string          `json:"peers,omitempty"`
}

SpaceSnapshot is everything a mirror is built from: the join reply (and a resync). Members is the first page; MembersNext, when set, is the cursor to fetch the rest with /spaces/members/list. Peers are the other instances in the space (the origin itself excluded), so a mirror's users reach them directly with presence and profile changes.

type SpaceSyncRequest

type SpaceSyncRequest struct {
	Space SpaceRef `json:"space"`
}

SpaceSyncRequest: POST /spaces/sync - a mirror asks for a fresh snapshot.

type SpaceUpdateEvent

type SpaceUpdateEvent struct {
	Space SpaceRef  `json:"space"`
	Info  SpaceInfo `json:"info"`
}

Origin → mirrors.

type SpaceUserRequest

type SpaceUserRequest struct {
	Space SpaceRef `json:"space"`
	User  string   `json:"user"` // FID, must belong to the requesting instance
}

SpaceUserRequest: POST /spaces/leave.

type StatusError

type StatusError struct {
	Domain string
	Status int
	Body   string
}

StatusError is a non-2xx reply from a peer.

func (*StatusError) Error

func (e *StatusError) Error() string

type ThreadWire

type ThreadWire struct {
	Archived           bool       `json:"archived"`
	ArchivedAt         *time.Time `json:"archived_at,omitempty"`
	AutoArchiveMinutes int        `json:"auto_archive_minutes,omitempty"`
	Locked             bool       `json:"locked"`
	Private            bool       `json:"private"`
	Invitable          bool       `json:"invitable"`
	LastActiveAt       *time.Time `json:"last_active_at,omitempty"`
	Owner              string     `json:"owner,omitempty"` // FID
	StarterMessage     string     `json:"starter_message,omitempty"`
}

ThreadWire is a thread's state on the wire (see rooms.Room's Thread* fields).

type ToDevice

type ToDevice struct {
	Sender         Profile                               `json:"sender"`
	SenderDeviceID string                                `json:"sender_device_id"`
	EventType      string                                `json:"event_type"`
	Messages       map[string]map[string]json.RawMessage `json:"messages"`
}

ToDevice: POST /to_device - Olm/Megolm to-device traffic for the receiver's users.

type TypingEvent

type TypingEvent struct {
	Room RoomRef `json:"room"`
	User string  `json:"user"`
}

TypingEvent: POST /rooms/typing.

type VoiceCall

type VoiceCall struct {
	StartedBy string    `json:"started_by"`
	StartedAt time.Time `json:"started_at"`
	Ringing   []string  `json:"ringing"`
}

VoiceCall is a ringing / running call with its users as FIDs.

type VoiceCallEvent

type VoiceCallEvent struct {
	Room    RoomRef    `json:"room"`
	Event   string     `json:"event"` // CALL_CREATE | CALL_UPDATE | CALL_DELETE
	Call    *VoiceCall `json:"call,omitempty"`
	Starter *Profile   `json:"starter,omitempty"`
	Rerung  bool       `json:"rerung,omitempty"`
}

VoiceCallEvent: POST /rooms/voice/call - the origin reports the call starting, changing (ringing set, re-ring) or ending.

type VoiceJoinReply

type VoiceJoinReply struct {
	URL      string       `json:"url"`
	Token    string       `json:"token"`
	RoomName string       `json:"room_name"`
	Bitrate  int          `json:"bitrate"`
	State    VoiceState   `json:"state"`
	States   []VoiceState `json:"states"`
	Call     *VoiceCall   `json:"call,omitempty"`
}

VoiceJoinReply is the origin's answer to a join: where to connect, as whom, and who is already there.

type VoiceJoinRequest

type VoiceJoinRequest struct {
	Room     RoomRef `json:"room"`
	User     Profile `json:"user"`
	SelfMute bool    `json:"self_mute"`
	SelfDeaf bool    `json:"self_deaf"`
}

VoiceJoinRequest: POST /rooms/voice/join - a user on the requesting instance wants a token for the call in a room this instance originated.

type VoiceRing

type VoiceRing struct {
	Room    RoomRef  `json:"room"`
	User    string   `json:"user"`
	Targets []string `json:"targets,omitempty"` // FIDs; empty = everyone not yet in
}

VoiceRing: POST /rooms/voice/ring.

type VoiceSelfUpdate

type VoiceSelfUpdate struct {
	Room       RoomRef `json:"room"`
	User       string  `json:"user"`
	SelfMute   *bool   `json:"self_mute,omitempty"`
	SelfDeaf   *bool   `json:"self_deaf,omitempty"`
	SelfVideo  *bool   `json:"self_video,omitempty"`
	SelfStream *bool   `json:"self_stream,omitempty"`
}

VoiceSelfUpdate: POST /rooms/voice/self - the user's own flags changed.

type VoiceState

type VoiceState struct {
	User       Profile   `json:"user"`
	Identity   string    `json:"identity"`
	SessionID  string    `json:"session_id"`
	SelfMute   bool      `json:"self_mute"`
	SelfDeaf   bool      `json:"self_deaf"`
	Mute       bool      `json:"mute"`
	Deaf       bool      `json:"deaf"`
	SelfVideo  bool      `json:"self_video"`
	SelfStream bool      `json:"self_stream"`
	Suppress   bool      `json:"suppress"`
	Priority   bool      `json:"priority_speaker"`
	Connected  bool      `json:"connected"`
	JoinedAt   time.Time `json:"joined_at"`
}

VoiceState is a voice state as instances describe it to each other: the user as a profile (so a receiver can create the shadow and draw the tile) and the LiveKit identity verbatim, since that is what every client matches participants by.

type VoiceStateEvent

type VoiceStateEvent struct {
	Room  RoomRef    `json:"room"`
	State VoiceState `json:"state"`
	Left  bool       `json:"left,omitempty"`
}

VoiceStateEvent: POST /rooms/voice/state - the origin reports one state (changed, or gone when Left).

type VoiceUserRequest

type VoiceUserRequest struct {
	Room RoomRef `json:"room"`
	User string  `json:"user"` // FID, must belong to the requesting instance
}

VoiceUserRequest: POST /rooms/voice/leave and /rooms/voice/decline.

Jump to

Keyboard shortcuts

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