AppView
The Colibri AppView is responsible for aggregating message data from across the network and providing endpoints for interacting with this data. Any requests to the AppView are meant to be proxied via the user’s PDS, to which the client application makes the requests. When requests are forwarded, the following DID URI should be used to indicate the target:
did:web:api.colibri.social#colibri_appviewImplemented Endpoints
Section titled “Implemented Endpoints”The AppView supports a range of XRPC methods that PDSs can call. All com.atproto endpoints adhere to the original specification. There are also certain social.colibri XRPC endpoints which allow for extended functionality. All of these endpoints are available under the /xrpc/ path.
com.atproto.identity.resolveDid
Section titled “com.atproto.identity.resolveDid”Resolves a DID to a DID document. Does not bi-directionally verify the handle.
com.atproto.identity.resolveHandle
Section titled “com.atproto.identity.resolveHandle”Resolves an atproto handle (hostname) to a DID. Does not necessarily bi-directionally verify against the the DID document.
com.atproto.identity.resolveIdentity
Section titled “com.atproto.identity.resolveIdentity”Resolves an atproto handle (hostname) to a DID. Does not necessarily bi-directionally verify against the the DID document.
com.atproto.sync.getRecord
Section titled “com.atproto.sync.getRecord”Get a single cached record from the AppView.
com.atproto.repo.listRecords
Section titled “com.atproto.repo.listRecords”List a range of cached records in a repository, matching a specific collection.
social.colibri.actor.getData
Section titled “social.colibri.actor.getData”Get the status data and bluesky profile for a Colibri user.
social.colibri.actor.setState
Section titled “social.colibri.actor.setState”Sets the online state for a Colibri user.
social.colibri.actor.listCommunities
Section titled “social.colibri.actor.listCommunities”Lists all communities the Colibri user is a part of (and not blocked from).
social.colibri.sync.subscribeEvents
Section titled “social.colibri.sync.subscribeEvents”Opens a connection with a WebSocket stream that transmits relevant events for the user.
Events
Section titled “Events”The event stream can choose to transmit events to clients who have signed up to receive them. All events follow the same JSON data structure: they have a type (which is the same as the heading below), and an optional data field.
Sent when the AppView receives a heartbeat message from the client. This event carries no data.
community_event
Section titled “community_event”Events of this type are sent to clients when a community has been updated or deleted. If the event is set to delete, only the community’s uri needs to be supplied.
{ event: 'upsert' | 'delete'; uri: string; name: string; description: string; picture: blob; categoryOrder: string[];}member_event
Section titled “member_event”Events of this type are sent to clients when a member has joined or left (or been blocked from) a community. The membership property is an AT-URI to the user’s membership declaration for the community.
{ event: 'join' | 'leave'; community: string; membership: string;}category_event
Section titled “category_event”Events of this type are sent to clients when a category has been created, updated, or deleted. When the event is delete, all data except the uri and event can be omitted.
{ event: 'upsert' | 'delete'; uri: string; community: string; name: string; channelOrder: string[];}channel_event
Section titled “channel_event”Events of this type are sent to clients when a channel has been created, updated, or deleted. When the event is delete, all data except the uri and event can be omitted.
{ event: 'upsert' | 'delete' uri: string; community: string; name: string; description: string; type: string;}message_event
Section titled “message_event”Events of this type are sent to clients when a message has been sent, edited, or deleted. When the event is delete, all data except the uri and event can be omitted.
{ event: 'upsert' | 'delete'; uri: string; channel: string; text: string; facets: facet[]; createdAt: string; indexedAt: string; edited: boolean; parent: string; attachments: blob[];}reaction_event
Section titled “reaction_event”Events of this type are sent to clients when a reaction has been added to or removed from a message. When the event is removed, all data except the uri and event can be omitted.
{ event: 'added' | 'removed'; uri: string; emoji: string; target: string;}user_event
Section titled “user_event”Events of this type are sent to clients when a user the client knows about has updated their Bluesky profile or Colibri status.
{ did: string; status?: { emoji?: string; text: string; state: 'online' | 'away' | 'dnd' | 'offline'; }; profile: { displayName?: string; avatar?: blob; banner?: blob; description?: string; handle: string; };}typing_event
Section titled “typing_event”Events of this type are sent to clients when a user is typing in the channel the user is currently viewing. For notes on how to report this to the AppView, see the view message type.
{ event: 'start' | 'stop'; channel: string; did: string;}Messages
Section titled “Messages”The following messages can be sent from the client to the AppView. All messages follow the same JSON data structure: they have a type (which is the same as the heading below), and an optional data field.
heartbeat
Section titled “heartbeat”A generic event that carries no data. It is used to keep the socket connection alive. The AppView will always respond with an ack event.
Clients can send a view message to the AppView to inform it about the channel the user is currently viewing.
{ channel: string;}voice_join
Section titled “voice_join”Clients must send a voice_join message to the AppView when the user joins a voice channel.
{ channel: string; community: string;}voice_leave
Section titled “voice_leave”Clients must send a voice_leave message to the AppView when the user leaves a voice channel.
social.colibri.community.create
Section titled “social.colibri.community.create”Mints a new community DID on the AppView’s own PDS and bootstraps a fully populated community. The AppView authenticates against PDS_LOC using PDS_ADMIN_USER / PDS_ADMIN_PASS (HTTP Basic) so it can create accounts without an invite code, stores the new account’s credentials encrypted in community_credentials, and writes five records to the new repo:
social.colibri.community(pinned atrkey: "self")social.colibri.category— a default"General"categorysocial.colibri.channel— a default"general"text channel inside that categorysocial.colibri.role— an"Owner"role with every permission andprotected: trueso role-management endpoints refuse to delete itsocial.colibri.member—subject= authenticated caller, holding the Owner role
The caller ends up as an owner-equivalent member of the new community.
The AppView identifies communities by DID; handles minted alongside the account are an implementation detail of PDS hosting and are not expected to be used for identity resolution. The AppView fails to start if any of PDS_LOC, PDS_ADMIN_USER, PDS_ADMIN_PASS, APPVIEW_HANDLE_DOMAIN, or CREDENTIAL_ENCRYPTION_KEY are missing.
social.colibri.community.registerCredentials
Section titled “social.colibri.community.registerCredentials”Registers user-supplied credentials (PDS endpoint + identifier + app password) for an existing community DID hosted on a PDS the AppView doesn’t manage. The AppView verifies proof-of-control by performing a com.atproto.server.createSession against the supplied PDS — if the resulting session’s DID matches the claimed community DID, the credentials are accepted, encrypted with AES-256-GCM, and stored. No bootstrap happens; BYO callers are expected to provision the community records themselves.
social.colibri.community.listBlockedUsers
Section titled “social.colibri.community.listBlockedUsers”Lists all blocked users for a community.
social.colibri.community.listCategories
Section titled “social.colibri.community.listCategories”Lists all categories in a community.
social.colibri.community.listChannels
Section titled “social.colibri.community.listChannels”Lists all channels in a community.
social.colibri.community.listRoles
Section titled “social.colibri.community.listRoles”Lists all roles cached for a community.
social.colibri.community.listMembers
Section titled “social.colibri.community.listMembers”Lists all members with their roles, status information and Bluesky profiles.
social.colibri.community.getData
Section titled “social.colibri.community.getData”Returns all cached data for a community in a single response: the community record, its categories, channels, roles, and members. Returns NotFound if no community record is cached for the given URI.
social.colibri.community.blockMessage
Section titled “social.colibri.community.blockMessage”Block a message in a given community.
social.colibri.community.blockUser
Section titled “social.colibri.community.blockUser”Block a user from writing messages in a given community.
social.colibri.community.unblockUser
Section titled “social.colibri.community.unblockUser”Unblock a user from a community, allowing them to write messages there again.
social.colibri.community.createInvitation
Section titled “social.colibri.community.createInvitation”Create an invitation for the specified community.
social.colibri.community.resolveInvitation
Section titled “social.colibri.community.resolveInvitation”Get information about a given invitation code.
social.colibri.community.listInvitations
Section titled “social.colibri.community.listInvitations”List all invitations for the specified community.
social.colibri.community.deleteInvitation
Section titled “social.colibri.community.deleteInvitation”Delete an invitation for the specified community.
social.colibri.channel.listMessages
Section titled “social.colibri.channel.listMessages”Get a paginated message history for the specified channel, newest messages first.
social.colibri.channel.getReadCursor
Section titled “social.colibri.channel.getReadCursor”Get the read cursor for the current user and specified channel.
social.colibri.message.listReactions
Section titled “social.colibri.message.listReactions”Get all reactions for a specified message, grouped by emoji with reactor DIDs.
social.colibri.notification.listNotifications
Section titled “social.colibri.notification.listNotifications”Returns a paginated list of notifications for the authenticated user, newest first. Each notification embeds the underlying message body so clients can render the notification without a follow-up fetch. message is omitted if the underlying message has been removed from the AppView cache.
The kind value is one of:
mention— the authenticated user appears as asocial.colibri.richtext.facet#mentionfeature in the message’s facets.reply— the message’sparentresolves to a message authored by the authenticated user.
Mentions and replies are deduplicated per (recipient, message, kind), so a single message that both mentions and replies to the recipient produces two notification rows.
social.colibri.notification.getUnreadCount
Section titled “social.colibri.notification.getUnreadCount”Returns the count of unseen notifications for the authenticated user.
social.colibri.notification.updateSeen
Section titled “social.colibri.notification.updateSeen”Marks every unseen notification for the authenticated user whose indexedAt is at or before seenAt as seen. If seenAt is omitted, the AppView uses its current clock for both the cutoff and the stamped seenAt — equivalent to “mark everything currently indexed as seen now”.
notification_event
Section titled “notification_event”Notification events are emitted over social.colibri.sync.subscribeEvents immediately after a notification is indexed. The payload mirrors a single entry of listNotifications and always carries the message body — clients should not need to perform a follow-up fetch.
{ id: integer; kind: 'mention' | 'reply'; messageUri: string; authorDid: string; channelRkey: string; indexedAt: string; message: { text: string; facets: facet[]; createdAt: string; parent?: string; attachments: blob[]; edited?: boolean; };}Humming
Section titled “Humming”The Colibri infrastructure is designed to be easy to self-host. In cases where users prefer to host their own infrastructure, under normal circumstances, data that is not saved on the protocol (like online/offline status, writing presence, etc.) is not able to be transmitted to other users who use a different app view.
The Colibri AppView supports a system called Humming to curcumvent this. Other AppView instances that wish to share off-protocol status data with users on other instances can share them via a Hum, a message sent to another AppView instance’s social.colibri.sync.sendHum endpoint, which can then choose to relay it to its clients via its social.colibri.sync.subscribeEvents WebSocket stream. Additionally, AppViews that wish to forward these events to other AppViews can expose a social.colibri.sync.subscribeHums endpoint, to which other AppViews can subscribe.
social.colibri.sync.sendHum
Section titled “social.colibri.sync.sendHum”Inform another AppView of an event that has occured.
social.colibri.sync.subscribeHums
Section titled “social.colibri.sync.subscribeHums”For transmitted event types, please refer to social.colibri.sync.subscribeEvents.