No description
Find a file
2025-10-23 07:15:59 +08:00
README.org Initial commit 2025-10-23 07:15:59 +08:00

Bloom Specification

This is the specification for the Bloom chat protocol version α1.

Preamble

Bloom is a chat protocol built on the following core principles:

  • Simplicity: it must be relatively simple to implement a client or server.
  • Resilience: the Bloom network must be resilient against network instability.
  • Privacy: messages sent via Bloom must be secured against snooping.
  • Comprehensiveness: Bloom must support a comprehensive set of chat features as expected of a modern protocol.
  • Extensibility: Bloom must be easy to extend, and easy to interact with for the developer.

In practice, most of these tenets are yet to be actioned, and Bloom is in a very early stage of development. The following features are planned, and at current state may or may not be implemented (note that many of these features are intended to be opt-in):

  • Reply to message.
  • Edit message.
  • Delete message.
  • Read receipts.
  • Media sharing.
  • Markup text.
  • Custom emojis.
  • Typing indicator.
  • Status indicator.
  • End to end encryption of messages.
  • Self-messaging.
  • Tagging.
  • Pinning.
  • Voice calling.
  • Video Calling.
  • Group chats and Channels.
  • GPS location.
  • Bluetooth messaging.
  • Server multi-networking and duplication.

Overview

Each end-user uses a Client to connect to a Server. Each Client connection is tied to a single User object on the server, which contains metadata about the user (including their login password, phone number, etc). Each user has an associated Inbox stack to which other users can send messages. Messages are sent via Update objects, which are uniquely identified by the a ULID, the origin User, and the destination User.

Updates may be sent directly to the Server, which directs them to the correct Inbox stack or otherwise interprets the request. In order to directly message another user, it is only necessary to know the username of the target, and to send an Update of a support Type to the Server, with the destination of the target user. The Inbox stack for each user is synchronised in real-time between the Server and each Client which is currently logged in as the specified User.

Update Representation

Due to the ease of parser implementation and my undying devotion to the Lisp arts, Update objects in Bloom are represented as simplified s-expressions. The following grammar specification defines all possible Update objects:

  UPDATE ::= '(' TYPE (KEYWORD EXPR)* ')'
  EXPR ::= STRING | LIST | SYMBOL | NUMBER | ULID
  LIST ::= '(' EXPR* ')'
  ULID ::= (ALPHA | NUM)+
  SYMBOL ::= TYPE | KEYWORD
  TYPE ::= IDENT
  KEYWORD ::= ':' IDENT
  IDENT ::= (ALPHA | NUM | SEP)+
  STRING ::= '"' !('"')* '"'
  NUMBER ::= NUM+ ('.' NUM*)? | '.' NUM+

  ALPHA ::= 'a..zA..Z'
  NUM ::= '0..9'
  SEP ::= '-' | '_'

The TYPE of an Update specifies to the server how the Update should be handled. The list of KEYWORDS and EXPRS which follows contains the necessary information for the server to handle the given UPDATE. The list of arguments forms a list of key-value pairs, or an association list in Lisp terminology, and can be mapped to a dictionary in any other language. In each pair of values, for example a keyword ':ping' and an expr '100', the keyword is the key of the value of expr.

Update objects do not require any specific order for the key-value pairs to present in (other than each keyword being followed by it's intended value), and so a Type specification like so:

  (foo :bar STRING :rab NUMBER)

does not require :bar to be present before :rab when the message is sent.

Additional data can also be sent via each Update, which the type does not necessarily require. In the above example, the server can append :metadata to the foo object, and this should not cause any issue for the client or server receiving the Update.

Update Types

The following update types are supported in the core specification.

Response

  (response :action STRING :status NUMBER :message STRING :data LIST)

A reply from the client or server that a requested :action was received and process, resulting in a :status. :message is an empty string unless the :action and :status dictates that additional information should be sent back to the original sender. :data is typically an empty list, however may be a list of further key-value pairs which contain requested information.

Each :status corresponds to a result:

  • 0: the action was successful.
  • 1: the action was unsuccessful due to an error on the recipient (see :message for additional information).
  • 2: the action was unsuccessful as the requester is unauthorized.

Log In

  (log-in :username STRING :password STRING)

A request from the client to the server to log in as the given :username, using the given :password. All update types besides response and log-in will fail with a :status of 2 unless the user logs in to the server first.

Message

  (message :id ULID :origin-user STRING :destination-user STRING :message STRING :timestamp NUMBER)

Sends a :message from the :origin-user to the :destination-user. This message has a unique :id, and :timestamp is the unix timestamp for the original message object from the :origin-user. These updates are pushed by the server to the :destination-users inbox, which is then synchronised by immediately sending the update onwards to any clients connected as the :destination-user.

Backfill

  (backfill :user STRING :since ULID)

Requests a server or client to send all messages in the :users inbox stack, :since the message ULID specified. Requested messages are sent as individual message objects.

Describe User

  (describe-user :user STRING)

Requests the server to provide a list of information detailing a given :user. This information is returned via a response update, where the :data contains at minimum the following information:

  • :username: a string, the username of the requested user.
  • :image: a string, a URL to the profile picture of the requested user. This string may be empty.
  • :phone-number: a string, the phone number of the user. This string may be empty.