This document describes the complete client-server interaction flow in the Ethora chat system, from user authentication to message handling.
- User provides credentials (email/password) or uses configured authentication method
- Client initializes XMPP connection:
client = xmpp.client({ service: wss://xmpp.ethoradev.com:5443/ws, username: walletToUsername(username), password: password })
- Connection states:
- 'connecting': Initial connection attempt
- 'online': Successfully connected
- 'error': Connection failed
- 'offline': Disconnected
- After successful connection:
<!-- Client sends initial presence stanza --> <presence/> <!-- Server acknowledges with session establishment --> <iq type='result'> <session xmlns='urn:ietf:params:xml:ns:xmpp-session'/> </iq>
- Client requests available rooms:
<iq type='get' id='getUserRooms'> <query xmlns='http://jabber.org/protocol/disco#items'/> </iq>
- Server responds with room list:
<iq type='result' id='getUserRooms'> <query xmlns='http://jabber.org/protocol/disco#items'> <item jid='[email protected]' name='Room Name' users_cnt='5' room_background='url' room_thumbnail='url'/> </query> </iq>
- For each room:
<!-- Client sends presence to join --> <presence from='[email protected]' to='roomJID/username' id='presenceInRoom'> <x xmlns='http://jabber.org/protocol/muc'/> </presence> <!-- Server confirms membership --> <presence from='roomJID/username' to='[email protected]'> <x xmlns='http://jabber.org/protocol/muc#user'> <item affiliation='member' role='participant'/> </x> </presence>
Server responds with:
<!-- Room roster (member list) -->
<iq type='result' id='roomMemberInfo'>
<query xmlns='ns:room:last'>
<activity name='User Name'
role='owner'
ban_status='clear'
last_active='1731677817'
jid='[email protected]'/>
</query>
</iq>
<!-- Room configuration -->
<iq type='result' id='roomInfo'>
<query xmlns='http://jabber.org/protocol/muc#owner'>
<x xmlns='jabber:x:data' type='form'>
<field var='muc#roomconfig_roomname'/>
<field var='muc#roomconfig_roomdesc'/>
</x>
</query>
</iq>
-
Regular text message:
<message type='groupchat' to='roomJID' id='unique-id'> <body>message text</body> <store xmlns='urn:xmpp:hints'/> </message>
-
Media message:
// 1. Client uploads media to storage const mediaData = new FormData(); mediaData.append('files', file); const response = await uploadFile(mediaData); // 2. Sends message with media URL and metadata const mediaMessage = xml( 'message', { id: id, type: 'groupchat', from: client.jid.toString(), to: roomJID, }, xml('body', {}, 'media'), xml('store', { xmlns: 'urn:xmpp:hints' }), xml('data', { location: response.location, mimetype: response.mimetype, size: response.size, fileName: response.filename, // ... other metadata }) );
-
Server broadcasts to room members:
<message from='roomJID/sender' to='roomJID/recipient' type='groupchat'> <body>message text</body> <!-- Additional metadata --> </message>
-
Client processes incoming stanzas:
// Stanza handler for different types switch (stanza.name) { case 'message': onRealtimeMessage(stanza); onMessageHistory(stanza); handleComposing(stanza); break; case 'presence': onPresenceInRoom(stanza); break; case 'iq': onGetChatRooms(stanza); onGetMembers(stanza); break; }
- User starts typing:
<message to='roomJID'> <composing xmlns='http://jabber.org/protocol/chatstates'/> </message>
- User stops typing:
<message to='roomJID'> <paused xmlns='http://jabber.org/protocol/chatstates'/> </message>
- Sent (client to server)
- Delivered (server to recipients)
- Read (recipient viewed)
- Client attempts automatic reconnection (max 3 attempts)
- On reconnect:
- Rejoin rooms
- Request missed messages
- Restore presence state
- Failed delivery notifications
- Rate limiting responses
- Permission errors
- Client requests history:
<iq type='get' id='history'> <query xmlns='urn:xmpp:mam:2'/> </iq>
- Server returns paginated message history
- Create rooms
- Modify room settings
- Invite users
- Manage member roles
-
Create rooms:
// Create new room async function createRoom(title: string, description: string) { const roomId = `${sha256(title + Date.now())}@conference.domain`; // 1. Create room with presence await createRoomPresence(roomId); // 2. Set creator as owner await setMeAsOwner(roomId); // 3. Configure room await roomConfig(roomId, title, description); }
-
Modify room settings:
<iq to='roomJID' type='set'> <query xmlns='http://jabber.org/protocol/muc#owner'> <x xmlns='jabber:x:data' type='submit'> <field var='muc#roomconfig_roomname'> <value>New Room Name</value> </field> <field var='muc#roomconfig_roomdesc'> <value>New Description</value> </field> </x> </query> </iq>
-
Invite users:
<message to='roomJID'> <x xmlns='http://jabber.org/protocol/muc#user'> <invite to='user@domain'> <reason>Please join our room</reason> </invite> </x> </message>
-
Member roles:
- Owner: Full control over room
- Admin: Can moderate users and messages
- Member: Regular participant
- None: No special privileges
- Banned: Blocked from room
Role changes are managed through IQ stanzas:
<iq to='roomJID' type='set'> <query xmlns='http://jabber.org/protocol/muc#admin'> <item affiliation='admin' jid='user@domain'/> </query> </iq>