asyncapi: 3.0.0
info:
  title: Aoba XMPP Messaging API
  version: 1.0.0
  description: |
    XMPP-based real-time messaging API using WebSocket transport (RFC 7395).

    ## Purpose

    This API provides real-time chat functionality including:
    - Direct messaging (1:1 chat)
    - Group chat (Multi-User Chat / MUC)
    - Presence (online/offline/away status)
    - Message delivery receipts (XEP-0184)

    ## Protocol

    Uses XMPP (Extensible Messaging and Presence Protocol) over WebSocket.
    Messages are XML stanzas following XMPP Core (RFC 6120) and relevant XEPs.

    ## Authentication

    1. Client opens WebSocket to `/xmpp`
    2. Server validates JWT from `auth_token` cookie during upgrade
    3. XMPP stream negotiation begins with pre-authenticated session
    4. SASL authentication auto-succeeds (cookie already validated)
    5. Client sends initial presence to indicate availability

    ## XEPs Implemented

    - **XEP-0045**: Multi-User Chat (MUC) for group messaging
    - **XEP-0184**: Message Delivery Receipts
    - **XEP-0199**: XMPP Ping (keepalive)
  license:
    name: MIT
  contact:
    name: Aoba Team

servers:
  development:
    host: localhost:8082
    protocol: ws
    description: Local development server
  staging:
    host: staging.aoba.chat
    protocol: wss
    description: Staging server (TLS required)
  production:
    host: api.aoba.chat
    protocol: wss
    description: Production server (TLS required)

defaultContentType: application/xml

channels:
  xmpp:
    address: /xmpp
    title: XMPP WebSocket Channel
    description: |
      XMPP over WebSocket endpoint following RFC 7395.

      **Authentication**: JWT token must be present in `auth_token` cookie.
      Connection uses XMPP framing (`urn:ietf:params:xml:ns:xmpp-framing`).

      **JID Format**: `{username}@{domain}/{resource}`
      - Example: `alice@localhost/web-abc123`
    messages:
      ChatMessage:
        $ref: '#/components/messages/ChatMessage'
      GroupChatMessage:
        $ref: '#/components/messages/GroupChatMessage'
      PresenceUpdate:
        $ref: '#/components/messages/PresenceUpdate'
      DeliveryReceipt:
        $ref: '#/components/messages/DeliveryReceipt'
      DeliveryReceiptRequest:
        $ref: '#/components/messages/DeliveryReceiptRequest'
    bindings:
      ws:
        method: GET
        headers:
          type: object
          properties:
            Cookie:
              type: string
              description: Must contain auth_token={jwt}
            Sec-WebSocket-Protocol:
              type: string
              const: xmpp
              description: WebSocket subprotocol

operations:
  # Direct Messaging
  sendChatMessage:
    action: send
    channel:
      $ref: '#/channels/xmpp'
    summary: Send direct message to user
    description: |
      Send a 1:1 chat message to another user. The message is routed
      through the XMPP server to the recipient's connected clients.
    messages:
      - $ref: '#/channels/xmpp/messages/ChatMessage'

  receiveChatMessage:
    action: receive
    channel:
      $ref: '#/channels/xmpp'
    summary: Receive direct message from user
    description: |
      Receive a 1:1 chat message from another user.
    messages:
      - $ref: '#/channels/xmpp/messages/ChatMessage'

  # Group Messaging
  sendGroupMessage:
    action: send
    channel:
      $ref: '#/channels/xmpp'
    summary: Send message to group
    description: |
      Send a message to a MUC room. The message is broadcast to all
      room participants by the server.

      **JID Format**: `group-{uuid}@{domain}`
    messages:
      - $ref: '#/channels/xmpp/messages/GroupChatMessage'

  receiveGroupMessage:
    action: receive
    channel:
      $ref: '#/channels/xmpp'
    summary: Receive message from group
    description: |
      Receive a group chat message. The `from` JID includes the
      sender's nickname in the resource part.

      **From Format**: `group-{uuid}@{domain}/{senderUsername}`
    messages:
      - $ref: '#/channels/xmpp/messages/GroupChatMessage'

  # Presence
  sendPresence:
    action: send
    channel:
      $ref: '#/channels/xmpp'
    summary: Send presence update
    description: |
      Broadcast presence status to contacts and joined rooms.
      Initial presence must be sent after authentication to appear online.
    messages:
      - $ref: '#/channels/xmpp/messages/PresenceUpdate'

  receivePresence:
    action: receive
    channel:
      $ref: '#/channels/xmpp'
    summary: Receive presence update
    description: |
      Receive presence updates from contacts and room participants.
    messages:
      - $ref: '#/channels/xmpp/messages/PresenceUpdate'

  # Delivery Receipts (XEP-0184)
  sendMessageWithReceipt:
    action: send
    channel:
      $ref: '#/channels/xmpp'
    summary: Send message with delivery receipt request
    description: |
      Send a message with XEP-0184 receipt request. The recipient
      should respond with a delivery receipt when the message is received.
    messages:
      - $ref: '#/channels/xmpp/messages/DeliveryReceiptRequest'

  receiveDeliveryReceipt:
    action: receive
    channel:
      $ref: '#/channels/xmpp'
    summary: Receive delivery receipt
    description: |
      Receive confirmation that a sent message was delivered.
      The receipt contains the original message ID.
    messages:
      - $ref: '#/channels/xmpp/messages/DeliveryReceipt'

components:
  messages:
    ChatMessage:
      name: chat
      title: Direct Chat Message
      summary: 1:1 chat message between users
      contentType: application/xml
      payload:
        $ref: '#/components/schemas/MessageStanza'
      examples:
        - name: Simple text message
          payload: |
            <message to="bob@localhost" type="chat" id="msg-123">
              <body>Hello Bob!</body>
            </message>

    GroupChatMessage:
      name: groupchat
      title: Group Chat Message
      summary: Message to a MUC room
      contentType: application/xml
      payload:
        $ref: '#/components/schemas/GroupMessageStanza'
      examples:
        - name: Group message
          payload: |
            <message to="group-abc123@localhost" type="groupchat" id="msg-456">
              <body>Hello everyone!</body>
            </message>
        - name: Received group message (from server)
          payload: |
            <message from="group-abc123@localhost/alice" to="bob@localhost" type="groupchat" id="msg-456">
              <body>Hello everyone!</body>
            </message>

    PresenceUpdate:
      name: presence
      title: Presence Stanza
      summary: User availability status
      contentType: application/xml
      payload:
        $ref: '#/components/schemas/PresenceStanza'
      examples:
        - name: Available (online)
          payload: |
            <presence/>
        - name: Away status
          payload: |
            <presence>
              <show>away</show>
              <status>Be right back</status>
            </presence>
        - name: Unavailable (offline)
          payload: |
            <presence type="unavailable"/>

    DeliveryReceiptRequest:
      name: receipt_request
      title: Message with Receipt Request
      summary: Message requesting delivery confirmation (XEP-0184)
      contentType: application/xml
      payload:
        $ref: '#/components/schemas/MessageWithReceiptRequest'
      examples:
        - name: Message with receipt request
          payload: |
            <message to="bob@localhost" type="chat" id="msg-789">
              <body>Did you get this?</body>
              <request xmlns="urn:xmpp:receipts"/>
            </message>

    DeliveryReceipt:
      name: receipt
      title: Delivery Receipt
      summary: Confirmation of message delivery (XEP-0184)
      contentType: application/xml
      payload:
        $ref: '#/components/schemas/ReceiptStanza'
      examples:
        - name: Receipt acknowledgment
          payload: |
            <message to="alice@localhost" type="chat" id="receipt-001">
              <received xmlns="urn:xmpp:receipts" id="msg-789"/>
            </message>

  schemas:
    MessageStanza:
      type: object
      xml:
        name: message
      description: XMPP message stanza for direct chat
      required:
        - to
        - type
        - id
        - body
      properties:
        to:
          type: string
          description: Recipient JID (bare or full)
          example: bob@localhost
          xml:
            attribute: true
        from:
          type: string
          description: Sender JID (set by server on incoming messages)
          example: alice@localhost/web-123
          xml:
            attribute: true
        type:
          type: string
          enum: [chat]
          description: Message type
          xml:
            attribute: true
        id:
          type: string
          description: Unique message identifier (UUID format)
          example: msg-123
          xml:
            attribute: true
        body:
          type: string
          description: Message text content
          xml:
            wrapped: true

    GroupMessageStanza:
      type: object
      xml:
        name: message
      description: XMPP message stanza for group chat (MUC)
      required:
        - to
        - type
        - id
        - body
      properties:
        to:
          type: string
          description: "Group JID (format: group-{uuid}@domain)"
          example: group-abc123@localhost
          xml:
            attribute: true
        from:
          type: string
          description: "Sender JID. On outgoing: your JID. On incoming: group-{uuid}@domain/{senderUsername}"
          example: group-abc123@localhost/alice
          xml:
            attribute: true
        type:
          type: string
          enum: [groupchat]
          description: Message type (always groupchat for MUC)
          xml:
            attribute: true
        id:
          type: string
          description: Unique message identifier (UUID format)
          example: msg-456
          xml:
            attribute: true
        body:
          type: string
          description: Message text content
          xml:
            wrapped: true

    PresenceStanza:
      type: object
      xml:
        name: presence
      description: XMPP presence stanza
      properties:
        to:
          type: string
          description: Recipient JID (optional, for directed presence)
          xml:
            attribute: true
        from:
          type: string
          description: Sender JID (set by server)
          xml:
            attribute: true
        type:
          type: string
          enum: [unavailable, subscribe, subscribed, unsubscribe, unsubscribed, probe, error]
          description: Presence type (absent for available)
          xml:
            attribute: true
        show:
          type: string
          enum: [away, chat, dnd, xa]
          description: |
            Availability sub-state:
            - away: Temporarily away
            - chat: Actively interested in chatting
            - dnd: Do not disturb
            - xa: Extended away
          xml:
            wrapped: true
        status:
          type: string
          description: Human-readable status message
          example: "In a meeting"
          xml:
            wrapped: true

    MessageWithReceiptRequest:
      type: object
      xml:
        name: message
      description: Message with XEP-0184 receipt request
      allOf:
        - $ref: '#/components/schemas/MessageStanza'
        - type: object
          properties:
            request:
              type: object
              xml:
                namespace: urn:xmpp:receipts
              description: XEP-0184 receipt request element

    ReceiptStanza:
      type: object
      xml:
        name: message
      description: XEP-0184 delivery receipt acknowledgment
      required:
        - to
        - type
        - id
        - received
      properties:
        to:
          type: string
          description: Original sender's JID
          xml:
            attribute: true
        from:
          type: string
          description: Acknowledger's JID
          xml:
            attribute: true
        type:
          type: string
          enum: [chat]
          xml:
            attribute: true
        id:
          type: string
          description: Receipt message ID (not the original message ID)
          xml:
            attribute: true
        received:
          type: object
          xml:
            namespace: urn:xmpp:receipts
          description: Receipt element containing original message ID
          properties:
            id:
              type: string
              description: ID of the message being acknowledged
              xml:
                attribute: true

    JID:
      type: string
      description: |
        Jabber ID (JID) following RFC 7622.

        **Formats**:
        - Bare JID: `localpart@domain` (e.g., `alice@localhost`)
        - Full JID: `localpart@domain/resource` (e.g., `alice@localhost/web-123`)
        - Domain JID: `domain` (e.g., `conference.localhost`)
      pattern: ^([^@/]+@)?[^@/]+(\/[^@/]+)?$
      examples:
        - alice@localhost
        - alice@localhost/web-abc123
        - group-uuid@conference.localhost

    XMPPError:
      type: object
      xml:
        name: error
      description: XMPP error stanza
      properties:
        type:
          type: string
          enum: [auth, cancel, continue, modify, wait]
          description: Error type category
          xml:
            attribute: true
        condition:
          type: string
          description: Error condition (e.g., not-authorized, item-not-found)
        text:
          type: string
          description: Human-readable error description
