diff options
| author | Owen Jacobson <owen@grimoire.ca> | 2024-09-18 22:49:38 -0400 |
|---|---|---|
| committer | Owen Jacobson <owen@grimoire.ca> | 2024-09-20 16:42:44 -0400 |
| commit | 22348bfa35f009e62abe2f30863e0434079a1fe2 (patch) | |
| tree | c5b5b5e660a1ee2a05785f4669102c1023b6e7b0 /docs/api.md | |
| parent | aafdeb9ffaf9a993ca4462b3422667e04469b2e3 (diff) | |
Remove the HTML client, and expose a JSON API.
This API structure fell out of a conversation with Kit. Described loosely:
kit: ok
kit: Here's what I'm picturing in a client
kit: list channels, make-new-channel, zero to one active channels, post-to-active.
kit: login/sign-up, logout
owen: you will likely also want "am I logged in" here
kit: sure, whoami
Diffstat (limited to 'docs/api.md')
| -rw-r--r-- | docs/api.md | 181 |
1 files changed, 181 insertions, 0 deletions
diff --git a/docs/api.md b/docs/api.md new file mode 100644 index 0000000..91e4148 --- /dev/null +++ b/docs/api.md @@ -0,0 +1,181 @@ +# the hi api + +## The basics + +The `hi` API is exposed as HTTP endpoints that accept JSON and return JSON on success, with a few exceptions noted below. + +On errors, the response body is freeform text and is meant to be shown to the user, logged, or otherwise handled. Programmatic action should rely on the status code, as documented. + +Requests that require a JSON body must include a `content-type: application/json` header. For requests that take a JSON body, if the body does not match the required schema, the endpoint will return a 422 Unprocessable Entity response, instead of the responses documented for that endpoint. + +## Authentication + +Other than where noted below, all endpoints require authentication. + +To authenticate a request, send a `cookie: identity=YOUR TOKEN HERE` header in the request. Tokens can be obtained via the `/api/auth/login` endpoint. This authentication protocol is intended to integrate with browsers and browser-like environments, where the browser handles cookie headers automatically. + +If the token is not valid or has expired, then `hi` will send back a 401 Unauthorized response, instead of the responses documented for the endpoint the request was intended for. + +## Endpoints + +### `GET /api/boot` + +Returns information needed to boot the client. Also the recommended way to check whether the current `identity` cookie is valid, and what login it authenticates. + +#### On success + +```json +{ + "login": { + "name": "example username", + "id": "L1234abcd", + } +} +``` + +### `POST /api/auth/login` + +Authenticates the user by login name and password, creating a login if none exists. **This endpoint does not require an `identity` cookie.** + +#### Request + +```json +{ + "name": "example username", + "password": "the plaintext password", +} +``` + +#### On success + +This endpoint returns a 204 No Content response on success, with a `Set-Cookie` header setting the `identity` cookie to a newly created token for this login. This cookie must be presented in future requests, and will authenticate the associated login. + +The cookie will expire if it is not used regularly. (As of this writing, identity cookies expire seven days after their last use, but this time period may change.) + +#### Authentication failures + +If the login already exists, and the provided password is different from the one used to create the login, then this will return a 401 Unauthorized response. + +### `POST /api/auth/logout` + +Invalidates the identity token, logging the user out. + +#### Request + +```json +{} +``` + +#### On success + +This endpoint returns a 204 No Content response on success, with a `Set-Cookie` header that clears the `identity` cookie. Even if this header is not processed, the cookie provided in the request is invalidated and will not authenticate future requests. + +### `GET /api/channels` + +Lists channels. + +#### On success + +Responds with a list of channel objects, one per channel: + +```json +[ + { + "name": "nonsense and such", + "id": "C1234abcd", + } +] +``` + +### `POST /api/channels` + +Creates a channel. + +#### Request + +```json +{ + "name": "a unique channel name" +} +``` + +#### On succeess + +```json +{ + "name": "a unique channel name", + "id": "C9876cyyz" +} +``` + +#### On duplicate channel name + +Channel names must be unique. If a channel with the same name already exists, this will return a 400 Bad Request error. + +### `POST /api/channels/:channel` + +Sends a chat message to a channel. It will be relayed to clients subscribed to the channel's events, and recorded for replay. + +The `:channel` placeholder must be a channel ID, as returned by `GET /api/channels` or `POST /api/channels`. + +#### Request + +```json +{ + "message": "my amazing thoughts, by bob" +} +``` + +#### On success + +Once the message is accepted, this will return a 202 Accepted response. The message will be delivered to subscribers asynchronously, as soon as is feasible. + +#### Invalid channel ID + +If the channel ID is not valid, this will return a 404 Not Found response. + +### `GET /api/events` + +Subscribes to events. This endpoint returns an `application/event-stream` response, and is intended for use with the `EventSource` browser API. Events will be delivered on this stream as they occur, and the request will remain open to deliver events. + +The returned stream may terminate, to limit the number of outstanding messages held by the server. Clients can and should repeat the request, using the `Last-Event-Id` header to resume from where they left off. Events will be replayed from that point, and the stream will resume. + +#### Query parameters + +This endpoint accepts the following query parameters: + +* `channel`: a channel ID. Events for this channel will be included in the response. This parameter may be provided multiple times. + +Browsers generally limit the number of open connections, often to embarrassingly low limits. Clients should subscribe to multiple streams in a single request, and should not subscribe to each stream individually. + +Requests without parameters will be successful, but will return an empty stream. + +(If you're wondering: it has to be query parameters or something equivalent to it, since `EventSource` can only issue `GET` requests.) + +#### Request headers + +This endpoint accepts an optional `Last-Event-Id` header for resuming an interrupted stream. If this header is provided, it must be set to the `id` of the last event processed by the client. The new stream will resume immediately after that event. If this header is omitted, then the stream will start from the beginning. + +If you're using a browser's `EventSource` API, this is handled for you automatically. + +#### On success + +The returned event stream is a sequence of events: + +```json +id: 1234 +data: { +data: "channel": "C9876cyyz", +data: "id": "Mabcd1234", +data: "sender": { +data: "id": "L1234abcd", +data: "name": "example username" +data: }, +data: "body": "my amazing thoughts, by bob", +data: "sent_at": "2024-09-19T02:30:50.915462Z" +data: } +``` + +The event `id` (`1234`, in the example above) is used to support resuming the stream after an interruption. See the "Request headers" section, above, for details. + +The `"id"` field uniquely identifies the message in related API requests, but is not used to resume the stream. |
