openapi: 3.1.0
info:
  title: Blah Chatserver Proto
  version: 0.0.1

paths:
  /ws:
    get:
      summary: WebSocket endpoint.
      description: |
        Once connection, client must send a JSON text message of type
        `WithSig<AuthPayload>` for authentication.
        If server does not close it immediately, it means success.

        Then server will send JSON text messages on events that user are
        interested in (eg. chat from joined rooms).
        The message has type `Outgoing` in `blahd/src/ws.rs`.

  /room:
    get:
      summary: List rooms on the server
      parameters:
        filter:
          in: query
          required: true
          description: |
            Must be one of following values:
            - "public": list all public rooms on the server.
            - "joined": list rooms the user have joined.
            - "unseen": list rooms the user have joined and have unseen
              messages.
        top:
          in: query
          description:
            The maximum number of items returned in each page. This is only an
            advice and server can clamp it to a smaller value.
        skipToken:
          in: query
          description:
            The page token returned from a previous list response to fetch the
            next page. NB. Other parameters (eg. `joined` and `page_len`)
            should be included (as the same value) for each page fetch.
      headers:
        Authorization:
          description: |
            Proof of membership for private rooms.
            Required if `filter` is other than "public".
          required: false
          schema:
            $ret: WithSig<AuthPayload>
      responses:
        200:
          content:
            application/json:
              $ref: '#/components/schema/RoomList'
        401:
          description: Missing or invalid Authorization header.
          content:
            application/json:
              $ref: '#/components/schemas/ApiError'

  /room/create:
    post:
      summary: Create a new room
      requestBody:
        content:
          application/json:
            schema:
              $ref: WithSig<CreateRoomPayload>
              example:
                sig: 99a77e836538268839ed3419c649eefb043cb51d448f641cc2a1c523811aab4aacd09f92e7c0688ffd659bfc6acb764fea79979a491e132bf6a56dd23adc1d09
                signee:
                  nonce: 670593955
                  payload:
                    typ: create_room
                    attrs: 1 # PUBLIC_READABLE
                    title: 'hello room'
                    members:
                      - user: 83ce46ced47ec0391c64846cbb6c507250ead4985b6a044d68751edc46015dd7
                        permission: -1
                  timestamp: 1724966284
                  user: 83ce46ced47ec0391c64846cbb6c507250ead4985b6a044d68751edc46015dd7
      responses:
        200:
          content:
            application/json:
              type: string
              description: Id of the newly created room (rid).
        403:
          description: The user does not have permission to create room.
          content:
            application/json:
              $ref: '#/components/schemas/ApiError'

  /room/{rid}:
    get:
      summary: Get room metadata
      responses:
        200:
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RoomMetadata'
        404:
          description: |
            Room does not exist or the user does not have permission to get metadata of it.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'


  /room/{rid}/feed.json:
    get:
      summary: JSON feed of room {rid}, which must be public readable
      description: For human and feed reader consumption only.
      responses:
        200:
          text/feed+json:
            scheme:
              $ref: 'https://www.jsonfeed.org/version/1.1/'
        404:
          description: Room does not exist or is private.
          content:
            application/json:
              $ref: '#/components/schemas/ApiError'

  /room/{rid}/item:
    get:
      summary: Get chat history for room {rid}
      description: |
        Return chat items in reversed time order, up to PAGE_LEN items.
        The last (oldest) chat id can be used as query parameter for the next
        GET, to repeatedly fetch full history.
      headers:
        Authorization:
          description: Proof of membership for private rooms.
          required: false
          schema:
            $ret: WithSig<AuthPayload>
      parameters:
        top:
          in: query
          description: |
            The maximum number of items to return. This is an advice and may be
            ignored by server.
        skipToken:
          in: query
          description: |
            Retrieve the next page of items, by providing the last item's `cid`
            from the previous response.
      responses:
        200:
          content:
            application/json:
              x-description: TODO
        404:
          description: |
            Room does not exist or the user does not have permission to read it.
          content:
            application/json:
              $ref: '#/components/schemas/ApiError'

    post:
      summary: Post a chat in room {rid}
      requestBody:
        content:
          application/json:
            schema:
              $ref: WithItemId<WithSig<ChatPayload>>
              example:
                sig: 99a77e836538268839ed3419c649eefb043cb51d448f641cc2a1c523811aab4aacd09f92e7c0688ffd659bfc6acb764fea79979a491e132bf6a56dd23adc1d09
                signee:
                  nonce: 670593955
                  payload:
                    typ: chat
                    room: 7ed9e067-ec37-4054-9fc2-b1bd890929bd
                    rich_text: ["before ",["bold ",{"b":true}],["italic bold ",{"b":true,"i":true}],"end"]
                  timestamp: 1724966284
                  user: 83ce46ced47ec0391c64846cbb6c507250ead4985b6a044d68751edc46015dd7
      responses:
        200:
          content:
            application/json:
              type: string
              description: Created chat id (cid).
        400:
          description: Body is invalid or fails the verification.
          content:
            application/json:
              $ref: '#/components/schemas/ApiError'
        403:
          description: |
            The user does not have permission to post in this room, or the room does not exist.
          content:
            application/json:
              $ref: '#/components/schemas/ApiError'

  /room/{rid}/item/{cid}/seen:
    post:
      summary: Mark item {cid} in room {rid} seen by the current user.
      description:
        Server will enforce that last seen item does not go backward. Marking
        an older item seen or sending the same request multiple times will be a
        no-op.
      headers:
        Authorization:
          description: Proof of membership for private rooms.
          schema:
            $ret: WithSig<AuthPayload>
      responses:
        204:
          description: Operation completed.
        404:
          description: |
            Room does not exist or the user is not in the room.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'

  /room/{rid}/admin:
    post:
      summary: Room management
      requestBody:
        content:
          application/json:
            schema:
              $ref: WithSig<AdminPayload>
              example:
                sig: 99a77e836538268839ed3419c649eefb043cb51d448f641cc2a1c523811aab4aacd09f92e7c0688ffd659bfc6acb764fea79979a491e132bf6a56dd23adc1d09
                signee:
                  nonce: 670593955
                  payload:
                    permission: 1
                    room: 7ed9e067-ec37-4054-9fc2-b1bd890929bd
                    typ: add_member
                    user: 83ce46ced47ec0391c64846cbb6c507250ead4985b6a044d68751edc46015dd7
                  timestamp: 1724966284
                  user: 83ce46ced47ec0391c64846cbb6c507250ead4985b6a044d68751edc46015dd7
      responses:
        204:
          description: Operation completed.
        404:
          description: |
            Room does not exist or the user does not have permission for management.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'

components:
  schemas:
    ApiError:
      type: object
      properties:
        error:
          type: object
          properties:
            code:
              type: string
            message:
              type: string

    RoomList:
      type: object
      required:
        - rooms
      properties:
        rooms:
          type: array
          items:
            $ref: '#/components/schemas/RoomMetadataForList'
        next_token:
          type: string
          description: An opaque token to fetch the next page.

    RoomMetadataForList:
      type: object
      required: ['rid', 'title', 'attrs']
      properties:
        rid:
          type: string
        title:
          type: string
        attrs:
          type: int64
        last_chat:
          $ref: 'WithItemId<WithSig<ChatPayload>>'
        last_seen_cid:
          type: string

    RoomMetadata:
      type: object
      properties:
        rid:
          type: string
        title:
          type: string
        attrs:
          type: int64

    RoomItems:
      type: object
      required:
        - items
      properties:
        items:
          type: array
          items:
            $ref: 'WithItemId<WithSig<ChatPayload>>'
        skip_token:
          type: string