diff --git a/BlogClient.md b/BlogClient.md index a2da210..1090006 100644 --- a/BlogClient.md +++ b/BlogClient.md @@ -1,60 +1,41 @@ -BlogClient is a [BSP client](BSP) that synchronises blog posts among groups of devices. Any subscriber to a blog can invite new subscribers, but only the creator of the blog can post. - -> Implementation note: We propose to use Ed25519 for signatures. +The blog client is a [BSP client](BSP) that synchronises blog posts among groups of devices. Only the creator of a blog can write posts. Posts and comments from other blogs can be reblogged with optional comments. ### Group identifiers -BlogClient uses the following client identifer: -`dafb e56f 0c89 7136 5cea 4bb5 f08e c9a6 1d68 6e05 8b94 3997 b6ff 259b a423 f613` +The client's identifier is `org.briarproject.briar.blog`. -Each blog has its own group. The [group descriptor](BSP#group-identifiers) is a [BDF list](BDF) with two elements: `title` (string) and `public_key` (raw). Posts are signed with the corresponding private key. +Each blog has its own group. The [group descriptor](BSP#group-identifiers) is a [BDF list](BDF) with two elements: `author` (list) and `rss` (boolean). The `author` list has three elements: `formatVersion` (int), `nickname` (string) and `publicKey` (raw). -For exchanging invitations, the client uses a separate group for each pair of contacts. The group descriptor is a BDF list containing the unique IDs of the contacts' identities, sorted in ascending order as byte strings. +`rss` indicates whether the blog contains an imported RSS feed or a user's personal blog. `nickname` is used as the blog's title. Posts are signed with the private key corresponding to `publicKey`. ### Message types -**0: INVITATION** - The content is a BDF list with three elements: `title` (string), `public_key` (raw), and `note` (string or null). The identifier of the blog group to which the invitation refers can be calculated from `title` and `public_key`, as described above. `note` is an optional note from the inviter to the invitee. +**0: POST** - A blog post. The message body is a BDF list with three elements: `messageType` (int), `content` (string), and `signature` (raw). -**1: RESPONSE** - The content is a BDF list with three elements: `invitation_id` (raw), `decision` (boolean), and `note` (string or null). `invitation_id` is the identifier of an invitation created by the opposite peer. `decision` indicates whether the invitee wishes to subscribe. `note` is an optional note from the invitee to the inviter. +The signature covers a BDF list with three elements: `groupId` (unique ID), `timestamp` (int), and `content` (string). The group ID and timestamp are taken from the message header. The public key from the group descriptor is used for verifying the signature. -**2: DEPARTURE** - The content is a BDF list with one element: `invitation_id` (raw), which is the identifier of an invitation created by either peer. +**1: COMMENT** - A pointer to a reblogged post or comment, with an optional comment. The message body is a BDF list with five elements: `messageType` (int), `comment` (string or null), `parentOriginalId` (unique ID), `parentId` (unique ID), and `signature` (raw). -**3: POST** - The content is a BDF list with two elements: `content` (list) and `signature` (raw). +`parentOriginalId` is the ID of a post or comment in this group or another group. `parentId` is the ID of the parent message, which is a post, comment, wrapped post or wrapped comment in this group that had the ID `parentOriginalId` in the group where it was originally posted. The signature covers a BDF list with five elements: `groupId` (unique ID), `timestamp` (int), `comment` (string or null), `parentOriginalId` (unique ID), and `parentId` (unique ID). The group ID and timestamp are taken from the message header. The public key from the group descriptor is used for verifying the signature. -`content` is a list with four elements: `parent_id` (raw or null), `content_type` (string), `body` (raw), and `attachments` (dictionary or null). `parent_id` is the identifier of a post to which this is a follow-up. Each key in `attachments` is the name of an attachment, and the value is a list with two elements: `content_type` (string) and `message_id` (raw). +**2: WRAPPED_POST** - A reblogged post from another blog. The message body is a BDF list with five elements: `messageType` (int), `copiedGroupDescriptor` (list), `copiedTimestamp` (int), `copiedContent` (string), and `copiedSignature` (raw). -`signature` is a signature over a list with four elements: `group_id` (raw), `timestamp` (int), and `content` (list). `group_id` and `timestamp` are taken from the [message header](BSP#message-format). `content` is described above. +`copiedGroupDescriptor` is the descriptor of the blog where the post was originally posted. `copiedTimestamp`, `copiedContent` and `copiedSignature` are copied from the original post. The public key from the original group descriptor is used for verifying the signature. The original group ID must be calculated, as it is covered by the signature. The original message ID must also be calculated, as it is referenced by comments. -**4: ATTACHMENT** - The content is raw data. +**3: WRAPPED_COMMENT** - A reblogged comment from another blog. The message body is a BDF list with eight elements: `messageType` (int), `copiedGroupDescriptor` (list), `copiedTimestamp` (int), `copiedComment` (string or null), `copiedParentOriginalId` (unique ID), `copiedParentId` (unique ID), `copiedSignature` (raw), and `parentId` (unique ID). + +`copiedGroupDescriptor` is the descriptor of the blog where the comment was originally posted. `copiedTimestamp`, `copiedComment`, `copiedParentOriginalId`, `copiedParentId` and `copiedSignature` are copied from the original comment. The public key from the original group descriptor is used for verifying the signature. The original group ID must be calculated, as it is covered by the signature. The original message ID must also be calculated, as it is referenced by comments. `parentId` is the ID of the parent message, which is a post, comment, wrapped post or wrapped comment in this group that had the ID `copiedParentOriginalId` in the group where the parent was originally posted, and the ID `copiedParentId` in the group where the comment was originally posted. ### Validity policy -* An invitation or response must belong to an invitation group. -* A post or attachment must belong to a blog group. -* An invitation is valid if it is well-formed. -* A response is valid if it is well-formed and it references a valid invitation created by the opposite peer. -* A departure is valid if it is well-formed and it references a valid invitation created by either peer. -* A post is valid if it is well-formed, its parent (if any) is a valid post, and it carries a valid signature. -* An attachment is always valid. - -Note that a post can be validated before its attachments have been received, and an attachment can be validated before it has been completely received. +* A post is valid if it is well-formed and has a valid signature. +* A comment is valid if it is well-formed, has a valid signature, and references a valid message of any type in the same group. ### Storage policy -For invitation groups: -* All local messages are stored. -* All remote messages are stored. - -For blog groups: -* All local messages are stored. -* All remote messages are stored. +* All messages are stored. ### Sharing policy -For invitation groups: -* All local messages are shared. -* All remote messages are shared. - -For blog groups: -* All local messages are shared. -* All remote messages are shared. +* All posts and comments are shared. +* Wrapped posts and wrapped comments are not shared unless they are referenced by a post or comment.