diff options
| author | ojacobson <ojacobson@noreply.codeberg.org> | 2025-07-04 05:00:21 +0200 |
|---|---|---|
| committer | ojacobson <ojacobson@noreply.codeberg.org> | 2025-07-04 05:00:21 +0200 |
| commit | c35be3ae29e77983f013c01260dda20208175f2b (patch) | |
| tree | abf0b9d993ef03a53903aae03f375b78473952da /docs/api/conversations-messages.md | |
| parent | 981cd3c0f4cf912c1d91ee5d9c39f5c1aa7afecf (diff) | |
| parent | 9b38cb1a62ede4900fde4ba47a7b065db329e994 (diff) | |
Rename "channels" to "conversations."
The term "channel" for a conversational container has a long and storied history, but is mostly evocative of IRC and of other, ah, "nerd-centric" services. It does show up in more widespread contexts: Discord and Slack both refer to their primary conversational containers as "channels," for example. However, I think it's unnecessary jargon, and I'd like to do away with it.
To that end, this change pervasively changes one term to the other wherever it appears, with the following exceptions:
* A `channel` concept (unrelated to conversations) is also provided by an external library; we can't and shouldn't try to rename that.
* The code to deal with the `pilcrow:channelData` and `pilcrow:lastActiveChannel` local storage properties is still present, to migrate existing data to new keys. It will be removed in a later change.
This is a **breaking API change**. As we are not yet managing any API compatibility promises, this is formally not an issue, but it is something to be aware of practically. The major API changes are:
* Paths beginning with `/api/channels` are now under `/api/conversations`, without other modifications.
* Fields labelled with `channel…` terms are now labelled with `conversation…` terms. For example, a `message` `sent` event is now sent to a `conversation`, not a `channel`.
This is also a **breaking UI change**. Specifically, any saved paths for `/ch/CHANNELID` will now lead to a 404. The corresponding paths are `/c/CONVERSATIONID`. While I've made an effort to migrate the location of stored data, I have not tried to provide adapters to fix this specific issue, because the disruption is short-lived and very easily addressed by opening a channel in the client UI.
This change is obnoxiously large and difficult to review, for which I apologize. If this shows up in `git annotate`, please forgive me. These kinds of renamings are hard to carry out without a major disruption, especially when the concept ("channel" in this case) is used so pervasively throughout the system.
I think it's worth making this change that pervasively so that we don't have an indefinitely-long tail of "well, it's a conversation in the docs, but the table is called `channel` for historical reasons" type issues.
Merges conversations-not-channels into main.
Diffstat (limited to 'docs/api/conversations-messages.md')
| -rw-r--r-- | docs/api/conversations-messages.md | 231 |
1 files changed, 231 insertions, 0 deletions
diff --git a/docs/api/conversations-messages.md b/docs/api/conversations-messages.md new file mode 100644 index 0000000..c7995f7 --- /dev/null +++ b/docs/api/conversations-messages.md @@ -0,0 +1,231 @@ +# Conversations and messages + +```mermaid +--- +Conversation lifecycle +--- +stateDiagram-v2 + [*] --> Active : POST /api/conversations + Active --> Deleted : DELETE /api/conversations/C1234 + Active --> Deleted : Expiry + Deleted --> [*] : Purge +``` + +```mermaid +--- +Message lifecycle +--- +stateDiagram-v2 + [*] --> Sent : POST /api/conversations/C1234 + Sent --> Deleted : DELETE /api/messages/Mabcd + Sent --> Deleted : Expiry + Deleted --> [*] : Purge +``` + +Messages allow logins to communicate with one another. Conversations are the containers to which those messages are sent. + +Every conversation has a unique name, chosen when the conversation is created. + +## Names + +<!-- This prose is duplicated in authentication.md. If you change it here, consider changing it there, too. --> + +The service handles conversation names using two separate forms. + +The first form is as given in the request used to create the conversation. This form of the conversation name is used throughout the API, and the service will preserve the name as entered (other than applying normalization), so that users' preferences around capitalization and accent marks are preserved. + +The second form is a "canonical" form, used internally by the service to control uniqueness and match names to conversations. The canonical form is both case-folded and normalized. + +The canonical form is not available to API clients, but its use has practical consequences. Names that differ only by case or only by code point sequence are treated as the same name. If the name is in use, changing the capitalization or changing the sequence of combining marks will not allow the creation of a second "identical" conversation. + +## Expiry and purging + +Both conversations and messages expire after a time. Messages sent to a conversation will keep the conversation from expiring until the messages also expire. + +Deleted conversations and messages, including those that have expired, are temporarily retained by the service, to allow clients that are not connected to receive the corresponding deletion [events](./events.md). To limit storage growth, deleted conversations and messages are purged from the service seven days after they were deleted. + +## `POST /api/conversations` + +Creates a conversation. + +### Request + +```json +{ + "name": "a unique conversation name" +} +``` + +The request must have the following fields: + +| Field | Type | Description | +| :----- | :----- | :----------------------- | +| `name` | string | The conversation's name. | + +The proposed `name` must be valid. The precise definition of valid is still up in the air, but, at minimum: + +- It must be non-empty. +- It must not be "too long." (Currently, 64 characters is too long.) +- It must begin with a printing character. +- It must end with a printing character. +- It must not contain runs of multiple whitespace characters. + +### Success + +This endpoint will respond with a status of `202 Accepted` when successful. The body of the response will be a JSON object describing the new conversation: + +```json +{ + "id": "C9876cyyz", + "name": "a unique conversation name" +} +``` + +The response will have the following fields: + +| Field | Type | Description | +| :----- | :----- | :----------------------------------------------------------------------------------------------------------------------------------------------------- | +| `id` | string | A unique identifier for the conversation. This can be used to associate the conversation with events, or to make API calls targeting the conversation. | +| `name` | string | The conversation's normalized name. | + +The returned name may not be identical to the name requested, as the name will be converted to [normalization form C](http://www.unicode.org/reports/tr15/) automatically. The returned name will include this normalization; the service will use the normalized name elsewhere, and does not store the originally requested name. + +When completed, the service will emit a [conversation created](events.md#conversation-created) event with the conversation's ID. + +### Name not valid + +This endpoint will respond with a status of `400 Bad Request` if the proposed `name` is not valid. + +### Conversation name in use + +This endpoint will respond with a status of `409 Conflict` if a conversation with the requested name already exists. + +## `POST /api/conversations/:id` + +Sends a message to a conversation. + +This endpoint requires the following path parameter: + +| Parameter | Type | Description | +| :-------- | :----- | :----------------- | +| `id` | string | A conversation ID. | + +### Request + +```json +{ + "body": "my amazing thoughts, by bob" +} +``` + +The request must have the following fields: + +| Field | Type | Description | +| :----- | :----- | :------------------------------------------ | +| `body` | string | The message to deliver to the conversation. | + +### Success + +This endpoint will respond with a status of `202 Accepted` when successful. The body of the response will be a JSON object describing the newly-sent message: + +```json +{ + "at": "2024-10-19T04:37:09.467325Z", + "conversation": "Cfqdn1234", + "sender": "Uabcd1234", + "id": "Mgh98yp75", + "body": "my amazing thoughts, by bob" +} +``` + +The response will have the following fields: + +| Field | Type | Description | +| :------------- | :-------- | :-------------------------------------------------------------------------------------------------------------------------------------- | +| `at` | timestamp | The moment the message was sent. | +| `conversation` | string | The ID of the conversation the message was sent to. | +| `sender` | string | The ID of the user that sent the message. | +| `id` | string | A unique identifier for the message. This can be used to associate the message with events, or to make API calls targeting the message. | +| `body` | string | The message's normalized body. | + +The returned message body may not be identical to the body as sent, as the body will be converted to [normalization form C](http://www.unicode.org/reports/tr15/) automatically. The returned body will include this normalization; the service will use the normalized body elsewhere, and does not store the originally submitted body. + +When completed, the service will emit a [message sent](events.md#message-sent) event with the message's ID. + +### Invalid conversation ID + +This endpoint will respond with a status of `404 Not Found` if the conversation ID is not valid. + +## `DELETE /api/conversations/:id` + +Deletes a conversation. + +Deleting a conversation prevents it from receiving any further messages. The conversation must be empty; to delete a conversation with messages in it, delete the messages first (or wait for them to expire). + +This endpoint requires the following path parameter: + +| Parameter | Type | Description | +| :-------- | :----- | :----------------- | +| `id` | string | A conversation ID. | + +### Success + +This endpoint will respond with a status of `202 Accepted` when successful. The body of the response will be a JSON object describing the deleted conversation: + +```json +{ + "id": "Cfqdn1234" +} +``` + +The response will have the following fields: + +| Field | Type | Description | +| :---- | :----- | :--------------------- | +| `id` | string | The conversation's ID. | + +When completed, the service will emit a [message deleted](events.md#message-deleted) event for each message in the conversation, followed by a [conversation deleted](events.md#conversation-deleted) event with the conversation's ID. + +### Conversation not empty + +This endpoint will respond with a status of `409 Conflict` if the conversation contains messages. + +### Invalid conversation ID + +This endpoint will respond with a status of `404 Not Found` if the conversation ID is not valid. + +## `DELETE /api/messages/:id` + +Deletes a message. + +This endpoint requires the following path parameter: + +| Parameter | Type | Description | +| :-------- | :----- | :------------ | +| `id` | string | A message ID. | + +### Success + +This endpoint will respond with a status of `202 Accepted` when successful. The body of the response will be a JSON object describing the deleted message: + +```json +{ + "id": "Mgh98yp75" +} +``` + +The response will have the following fields: + +| Field | Type | Description | +| :---- | :----- | :---------------- | +| `id` | string | The message's ID. | + +When completed, the service will emit a [message deleted](events.md#message-deleted) event with the message's ID. + +### Invalid message ID + +This endpoint will respond with a status of `404 Not Found` if the message ID is not valid. + +### Not the sender + +This endpoint will respond with a status of `403 Forbidden` if the message was sent by a different login. |
