summaryrefslogtreecommitdiff
path: root/docs/api/boot.md
diff options
context:
space:
mode:
authorojacobson <ojacobson@noreply.codeberg.org>2025-07-01 02:45:39 +0200
committerojacobson <ojacobson@noreply.codeberg.org>2025-07-01 02:45:39 +0200
commitc0c825477e476d6d7331bfc409bceff9c376b484 (patch)
tree2658c58137c64beec670b1a87b46e6ba4d6eb7e7 /docs/api/boot.md
parent022ae4ecf5f05b72280b1d702cb92d6795485639 (diff)
parent7778cdf0c495a04f4f5f3f85b78348c8037a5771 (diff)
Send back the current state as events, not snapshots, during client boot.
There are a couple of contributing reasons for this. * Each client's author - even ourselves - is best positioned to know how best to convert history into state to meet the needs of that specific client. There is (probably) no universal solution. You can already see this with the built-in client, where unread tracking gets stapled onto snapshots locally and maintained as events roll in, and I would expect this to happen more and more regularly over time. If we ever sprout other clients, I'd also expect their local state to be different. The API, on the other hand, must expose a surface that's universal to all clients. For boot, that was a very rote list-of-nouns data model. The other option is to expose a surface specific to one client and make other clients accommodate, which is contrary to the goals of this project. * The need to compute snapshots adds friction when adding or changing the behaviour of the API, even when those changes only tangentially touch `/api/boot`. For example, my work on adding messages to multiple conversations got hung up in trying to figure out how to represent that at boot time, plus how to represent that in the event stream. * The rationale for sending back a computed snapshot of the state was to avoid having the client replay events from the beginning of time, and to limit the amount of data sent back. This didn't pan out - most snapshots in practice consisted of the same data you'd get from the event stream anyways, almost with a 1:1 correspondence (with a `sent` or `created` event standing in for a `messages`, `channels`, or `users` entry). Exceptions - deleted messages and channels - were rare, and are ephemeral. * Generating the snapshots requires loading the entire history into memory anyways. We're not saving any server-side IO by computing snapshots, but we are spending server-side compute time to generate them for clients that are then going to throw them away, as above. This change resolves these tensions by delegating state management _entirely_ to the client, removing the server-side state snapshots. The server communicates in events only. ## Alternatives I joked to @wlonk that the "2.0-bis" version of this change always returns `resume_point` 0 and an empty events list. That would be correct, and compatible with the client logic in this change, and would actually work. In fact, we could get rid of the event part of `/api/boot` entirely, and require clients to consume the event stream from the beginning every time they reconnect. The main reason I _don't_ want to do this has to do with reconnects. Right now - both with snapshots, before this change, and with events, after - the client can cleanly delineate "historical" events (to be applied while the state is not presented to the user) and "current" events (to be presented to the user immediately). The `application/event-stream` protocol has no way to make that distinction out of the box, and while we can hack something in, all the approaches I can think of are nasty. Merges boot-events into main.
Diffstat (limited to 'docs/api/boot.md')
-rw-r--r--docs/api/boot.md82
1 files changed, 43 insertions, 39 deletions
diff --git a/docs/api/boot.md b/docs/api/boot.md
index 7e3dda3..f6e6dc2 100644
--- a/docs/api/boot.md
+++ b/docs/api/boot.md
@@ -21,8 +21,8 @@ sequenceDiagram
Client initialization serves three purposes:
- It confirms that the client's [identity token](./authentication.md) is valid, and tells the client what user that token is associated with.
-- It provides an initial snapshot of the state of the service.
-- It provides a resume point for the [event stream](./events.md), which allows clients to consume events starting from the moment the snapshot was created.
+- It provides an initial event collection.
+- It provides a resume point for the [event stream](./events.md), which allows clients to consume events starting after the initial event collection.
## `GET /api/boot`
@@ -43,26 +43,54 @@ This endpoint will respond with a status of
},
"resume_point": 1312,
"heartbeat": 30,
- "users": [
+ "events": [
{
+ "type": "user",
+ "event": "created",
+ "at": "2025-04-14T23:58:10.421901Z",
"id": "U1234abcd",
"name": "example username"
- }
- ],
- "channels": [
+ },
{
+ "type": "channel",
+ "event": "created",
"at": "2025-04-14T23:58:11.421901Z",
- "name": "nonsense and such",
- "id": "C1234abcd"
- }
- ],
- "messages": [
+ "id": "C1234abcd",
+ "name": "nonsense and such"
+ },
{
+ "type": "message",
+ "event": "sent",
"at": "2024-09-27T23:19:10.208147Z",
"channel": "C1234abcd",
"sender": "U1234abcd",
"id": "M1312acab",
"body": "beep"
+ },
+ {
+ "type": "message",
+ "event": "sent",
+ "at": "2025-06-19T15:14:40.431627Z",
+ "channel": "Ccfdryfdb4krpy77",
+ "sender": "U888j6fyc8ccrnkf",
+ "id": "Mc6jk823wjc82734",
+ "body": "test"
+ },
+ {
+ "type": "channel",
+ "event": "created",
+ "at": "2025-06-19T15:14:44.764263Z",
+ "id": "C2d9y6wckph3n36x",
+ "name": "noob"
+ },
+ {
+ "type": "message",
+ "event": "sent",
+ "at": "2025-06-19T15:29:47.376455Z",
+ "channel": "Ccfdryfdb4krpy77",
+ "sender": "U888j6fyc8ccrnkf",
+ "id": "M3twnj7rfk2ph744",
+ "body": "test"
}
]
}
@@ -75,9 +103,10 @@ The response will include the following fields:
| `user` | object | The details of the caller's identity. |
| `resume_point` | integer | A resume point for [events](./events.md), such that the event stream will begin immediately after the included snapshot. |
| `heartbeat` | integer | The [heartbeat timeout](./events.md#heartbeat-events), in seconds, for events. |
-| `users` | array of object | A snapshot of the users present in the service. |
-| `channels` | array of object | A snapshot of the channels present in the service. |
-| `messages` | array of object | A snapshot of the messages present in the service. |
+| `events` | array of object | The events on the server up to the resume point. |
+
+Each element of the
+`events` object is an event, as described in [Events](./events.md). Events are provided in the same order as they would appear in the event stream response.
The `user` object will include the following fields:
@@ -85,28 +114,3 @@ The `user` object will include the following fields:
| :----- | :----- | :--------------------------------------- |
| `name` | string | The name of the caller's login identity. |
| `id` | string | The ID of the caller's login identity. |
-
-Each element of the `users` array describes a distinct user, and will include the following fields:
-
-| Field | Type | Description |
-| :----- | :----- | :----------------------------------------------------------------------------------------------------------------------------------- |
-| `name` | string | The name for the user. |
-| `id` | string | A unique identifier for the user. This can be used to associate the user with other events, or to make API calls targeting the user. |
-
-Each element of the `channels` array describes a distinct channel, and will include the following fields:
-
-| Field | Type | Description |
-| :----- | :-------- | :-------------------------------------------------------------------------------------------------------------------------------------------- |
-| `at` | timestamp | The moment the channel was created. |
-| `name` | string | The name for the channel. |
-| `id` | string | A unique identifier for the channel. This can be used to associate the channel with other events, or to make API calls targeting the channel. |
-
-Each element of the `messages` array describes a distinct message, and will include the following fields:
-
-| Field | Type | Description |
-| :-------- | :-------- | :-------------------------------------------------------------------------------------------------------------------------------------------- |
-| `at` | timestamp | The moment the message was sent. |
-| `channel` | string | The ID of the channel 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 other events, or to make API calls targeting the message. |
-| `body` | string | The text of the message. |