diff --git a/src/content/docs/components/iota/endpoints.md b/src/content/docs/components/iota/endpoints.md index cedf164..20d7ebb 100644 --- a/src/content/docs/components/iota/endpoints.md +++ b/src/content/docs/components/iota/endpoints.md @@ -2,117 +2,33 @@ title: Endpoints --- -All application messages arrive at the `Iota` through an authenticated MTP -connection. A sealed application operation uses the reserved `Relay` -communication type. Its outer sender is absent, and its outer receiver is -only the next-hop Iota or local client ID. - -The Iota opens and verifies Relay metadata and content. It resolves the -claimed signer ID through trusted user key history; it never treats a public -key carried by the message as authoritative. The `sender_id` fields in the -examples below describe authenticated application data inside the protected -Relay, not an outer MTP sender added by Omikron. - -The Iota ID is used by the MTP routing layer and is not an application signer -identity. Omikron does not rewrite a Relay's sender, emit `MessageOtherIota`, -or inspect the protected operation to choose a route. - -## MTP Relay API - -The Relay API uses the MTP `0.3.0` implementation and protocol version `3.0`. -MTP sends the frame as binary data; the shapes below describe its logical -fields for application integration. - -### `Relay` frame - -```text -CommunicationValue { - id: optional u32, - type: Relay, - sender: absent, - receiver: next-hop u64, - payload: encrypted relay metadata, -} -``` - -The clear `receiver` is the only routing identity. The payload is sealed from -Omikron and Omega. The `id` is an MTP correlation value and is not the -authenticated application message identity. - -After the metadata recipient opens the payload, the authenticated metadata -contains: - -```text -RelayVersion: unsigned integer -MessageId: string -FinalRecipientId: unsigned integer -CreatedAt: Unix epoch milliseconds -Content: encrypted signed application content -Metadata: optional application metadata -``` - -The signed content contains `MessageType` and the application `Content` value. -The content recipient set is separate from the metadata recipient set. Include -the destination Iota in the content recipient set when that Iota must apply -the operation; metadata-only participants forward the sealed content. - -### Relay verification - -The receiving Iota performs these checks before dispatching application data: - -1. The outer receiver is the local Iota ID. -2. The outer sender is absent. -3. The claimed signer ID is read from decrypted metadata but remains untrusted. -4. Trusted signing-key history is resolved from local user state or the - authenticated Omega control plane. -5. The metadata signature and final recipient are verified with those keys. -6. `(signer_id, MessageId)` is inserted into durable replay storage before an - application side effect. -7. The content signature is checked against the same trusted signer keys. - -An application `SenderId` field is checked against the authenticated signer; -it is not a substitute for Relay signature verification. `CreatedAt` can be -used for retention, but it is not part of the replay identity. - -### Relay routing - -The outer receiver changes when the Relay crosses a routing boundary. The -sealed payload and absent outer sender remain unchanged. - -| Stage | Outer receiver | Operation | -| --- | --- | --- | -| Client to origin Iota | Origin Iota ID | Verify the local user's Relay and resolve the final recipient's hosting Iota. | -| Origin Iota to remote Iota | Destination Iota ID | Call `forward_relay_frame()` and send the unchanged sealed Relay through Omikron. | -| Destination Iota to client | Final user ID | Verify and apply content, then forward the original Relay to the local client. | - -The Iota never creates `MessageOtherIota`, reconstructs `SetChatSecret`, or -adds the authenticated signer as an outer MTP sender. Failed delivery stores -the encoded sealed Relay for retry. +All messages to the `Iota` are either from the `Omikron` directly, from a `Client` or another `Iota` trying to message a `User` on the `Iota`. +All messages will be marked with a `sender_id` this will be added on the `Omikron`. +It is important to note that the `Iota ID` is not shared with others as it is what verifies the `Iota` users to Tensamin. +In the response the `sender_id` & `receiver_id` must be swapped as sender. +When a message comes from an iota without a `sender-id` that the iota has access to or is designated for the client (`33333333-3333-3333-3333-333333333333`) the message will not be send. ## Messages ### Messaging Lifecycle -The messaging system persists and verifies each protected operation before it -is delivered or forwarded: +The messaging system follows a strict derivation loop to ensure delivery and correct state synchronization: -1. The client sends a sealed `Relay` containing the application operation to - its associated Iota. -2. The Iota verifies the protected signer, final recipient, message ID, and - application content before applying the operation. -3. When another Iota must receive the operation, the Iota persists any - required delivery state and forwards the same sealed `Relay` through - Omikron. The outer receiver changes only when the next hop changes. -4. The recipient Iota verifies the Relay again, stores the operation, and - sends the resulting application update to its local client. +1. The client sends a `message_send` to its Iota. +2. The Iota sends a response `message_send` as confirmation back to the client. +3. The Iota sends a `message_other_iota` to the chat partner's Iota. + - **If it times out:** + - The initial Iota sends a `message_state` to the client with the state `"sending"`. + - **If successful:** + - The other Iota receives the `message_other_iota` and handles it. + - The other Iota informs its client with a `message_live`. + - **If the client doesn't answer:** + 1. The other Iota will send a `message_state` to the initial Iota with the state `"sent"`. + 2. The initial Iota will store the state and forward the `message_state` to its client. ### Message mutations -`Iota` stores each mutation on the sender and recipient replicas. `Content` -remains encrypted chat content. The sender is the authenticated signer from -the protected Relay and must own the original message for edits and deletion. -The request and update examples in this section show protected application -content, not the outer MTP `CommunicationValue`. +`Iota` stores each mutation on the sender and recipient replicas. `Content` remains encrypted chat content. The sender is taken from the authenticated MTP connection and must own the original message for edits and deletion. #### `MessageEdit` @@ -159,10 +75,6 @@ Changes an existing message. `ChatPartnerId` and `SendTime` identify the message Adds one reaction for the authenticated user. Repeating the same request does not create a duplicate row. -Iota stores at most ten distinct reaction strings per message. A user may use -an existing reaction string after the limit is reached. A new string at the -limit returns `ErrorInvalidData` and produces no live update. - ##### `REQ (C2S):` ```json @@ -230,27 +142,6 @@ Removes the authenticated user's matching reaction. `Accepted` is `true` for an add and `false` for a removal. -#### `MessageGet` - -Fetches one visible message by `SendTime`. Include `ChatPartnerId` when the -request is a reply lookup. The successful response then includes `Offset`, -the message's absolute position in the same descending history order used by -`MessagesGet`. - -```json -{ - "type": "MessageGet", - "data": { - "ChatPartnerId": "", - "SendTime": "" - } -} -``` - -Use the returned `Offset` as the starting position for a page that contains -the target. Requests without `ChatPartnerId` remain accepted for compatibility -but do not include an offset. - #### `MessageDeleteLive` Deletes a message from both replicas. The existing `MessageDeleteLive` MTP type is used for both the authenticated delete request and the recipient update because no separate delete request type exists in the client type map. @@ -294,10 +185,8 @@ Deletes a message from both replicas. The existing `MessageDeleteLive` MTP type 2. The initial Iota will store the state and forward the `message_state` to its client. ### Client adds someone to their contacts - -Resolve a contact name through the control plane before creating the protected -`AddConversation` operation. Omikron does not inspect or mutate this -operation. The JSON below represents the protected application content. +When adding via name, the omikron will intercept and fill the chat_partner_id. +The iota will never read, or handle the chat_partner_name. ##### `REQ:` @@ -308,7 +197,7 @@ operation. The JSON below represents the protected application content. "type": "add_conversation", "id": "", - + "data": { "chat_partner_id": id, "chat_partner_name": "name" @@ -331,33 +220,41 @@ operation. The JSON below represents the protected application content. ### Client messages someone -The following object is the protected application content passed to the MTP -Relay builder. It is not the outer frame and its `SenderId`, when present, is -checked against the authenticated Relay signer. - -##### `CONTENT:` +##### `REQ:` ```json { - "MessageType": "MessageSend", - "Content": { - "ReceiverId": "", - "Content": "", - "Files": [ + "sender_id": "", + "receiver_id": "", + + "type": "message_send", + "id": "", + "data": { + "receiver_id": "", + "content": "", + "files": [ { - "Name": "", - "Id": "", - "Type": "[ image | image_top_right | file ]" + "name": "", + "id": "", + "type": "[ image | image_top_right | file ]" } ] } } ``` -The Relay metadata carries the authenticated `MessageId` and -`FinalRecipientId`. It also carries `CreatedAt` as Unix epoch milliseconds. -The application does not put the signer identity in the outer MTP sender -field. +##### `RES:` + +```json +{ + "sender_id": "", + "receiver_id": "", + + "type": "message_send", + "id": "", + "data": {} +} +``` ### Client loads messages @@ -416,50 +313,49 @@ field. `ClientConnected` and `MessagesGet` include `Edited: true` for messages with at least one edit and a `Reactions` array. Each reaction contains `Reaction` and `SenderId` fields. Clients that do not consume these fields continue to receive the existing message fields. -### Route a message to another Iota +### Send a message to other Iota -The client sends the same `Relay` frame to its origin Iota regardless of the -final recipient's hosting Iota. +##### `REQ:` -```text -Relay { - id: , - sender: absent, - receiver: , - payload: , +```json +{ + "type": "message_send", + "id": "", + "data": { + "receiver_id": "99999999-8888-7777-6666-555555555555", + "content": "Hello, how are you?" + } } ``` -The origin Iota resolves the final recipient through the authenticated control -plane and forwards the original sealed frame with `receiver` set to the -destination Iota. It does not create a second application message. +##### `RES:` -```text -Relay { - id: , - sender: absent, - receiver: , - payload: , +```json +{ + "type": "message", + "id": "", + "receiver": "" } ``` -### Receive a live message from a sealed Relay +### Receive live message (`message_other_iota`) ##### `UPDATE:` -```text -Relay { - id: , - sender: absent, - receiver: , - payload: , +```json +{ + "type": "message_live", + "id": "", + "receiver": "", + + "data": { + "send_time": unixstamp, + "message": "", + "sender_id": "99999999-8888-7777-6666-555555555555" + } } ``` -The destination Iota applies the authenticated operation before forwarding -the frame to the local client. The client receives the application update -through its authenticated MTP connection. - ## Communities ### Client storing a community on their Iota diff --git a/src/content/docs/components/omega/endpoints.md b/src/content/docs/components/omega/endpoints.md index 19a6bf3..b138897 100644 --- a/src/content/docs/components/omega/endpoints.md +++ b/src/content/docs/components/omega/endpoints.md @@ -1,93 +1,73 @@ --- -title: MTP Endpoints -lastUpdated: 2026-08-14 +title: WSS Endpoints --- -`Omega` serves HTTPS API routes and authenticated MTP WebTransport sessions. -`Omikron` connects through the MTP WebTransport endpoint. The Omega service -uses MTP protocol version `0.3.0` and requires transport authentication before -dispatching application frames. +The `Omega` can be reached by HTTPS request from `Client` and `Iota`. +`Omikron` servers establish a Secure Websocket when booting. -This document covers the MTP connection between `Omikron` and `Omega` and the -control messages handled after authentication. HTTP routes are documented in -[`https.md`](./https.md). +This documentation is split into `Omikron` (WSS) and `HTTPS`. -## MTP Omikron Connection +## Omikron -### Authentication +### Identification -The MTP opening exchange negotiates protocol version `3.0` and completes the -configured cryptographic login or registration flow. Omega uses -`ForceAuthentication`, so an unauthenticated WebTransport session does not -reach the application dispatcher. The authenticated MTP client ID identifies -the `Omikron` connection. +Any message to the `Omega` will be ignored until Identification. -The MTP `Identification`, `Register`, challenge, and signed proof frames are -transport messages. They are not application-level JSON endpoints. After the -handshake, Omega validates the authenticated `Omikron` capability description -before accepting control messages or Relay frames. - -### Relay Routing - -`Relay` is the reserved MTP communication type for opaque routing between -`Omikron` instances. The logical shape below describes the MTP frame; MTP sends -the frame as binary data, not as JSON. - -#### `Relay` - -The outer `receiver` is the destination `Iota` next hop. The outer `sender` -field must be absent. The payload contains the sealed Relay metadata and -content and is not opened by Omega. - -##### `REQ (Omikron → Omega):` +##### `REQ:` ```json { - "type": "Relay", - "id": "", - "receiver": "", - "payload": "" + "type": "identification", + "data": { + "omikron": "", + } } ``` -##### `FORWARD:` - -Omega resolves the destination `Iota` through its primary `Omikron` route and -sends the Relay to that one authenticated connection. The outer receiver and -sealed payload remain unchanged. Omega does not decrypt Relay metadata or -content, resolve the original signer, rewrite the sender, or interpret the -inner application type. - -Relay route failures are infrastructure failures. Omega rejects a frame with -an outer sender, a missing destination `Iota`, an unavailable primary route, -an unavailable target connection, or a route that points back to the source -`Omikron`. Omega does not turn these failures into user-authored Relay content. - -### Control Messages - -Control messages use the negotiated MTP type map after the authenticated -connection is established. - -#### `PushNotification` - -Sends a notification to a user. The receiver ID can be specified in the MTP -frame's `receiver` field or in the data payload. - -##### `REQ (Omikron → Omega):` +##### `REQ & RES:` ```json { - "type": "PushNotification", - "receiver_id": "?", - "sender_id": "" + "type": "challenge", + "data": { + "public_key": "", + "challenge": "" + } } ``` - -##### `RES (Omega → Omikron):` +##### `REQ & RES:` ```json { - "type": "PushNotification", - "id": "" + "type": "challenge_response", + "data": { + "challenge": "", + } +} +``` +##### `RES:` + +```json +{ + "type": "identification_response", + "data": { + "accepted": boolean, + } +} +``` + +### Push Notification + +Sends a push notification to a user. The receiver ID can be specified either in the `receiver` field of the message or in the data payload. + +##### `REQ:` + +```json +{ + "type": "push_notification", + "data": { + "receiver_id": "", // Optional, if not in receiver field + "sender_id": "" + } } ``` diff --git a/src/content/docs/components/omega/https.md b/src/content/docs/components/omega/https.md index 3590a22..e3020eb 100644 --- a/src/content/docs/components/omega/https.md +++ b/src/content/docs/components/omega/https.md @@ -1,126 +1,22 @@ --- title: HTTPS Endpoints -lastUpdated: 2026-08-14 --- +#### GET `/api/get/omikron/` +Get an Omikron to connect to. +#### GET `/api/get/omikron/` +Get an Omikron to connect to, when needing a certain omikron. -`Omega` exposes discovery and profile data over HTTPS. All identifiers in the -routes below are positive decimal integers. - -#### GET `/api/get/omikron` - -Returns a randomly selected connected `Omikron`. - -##### `RES:` +#### GET `/api/get/omikron/` +Get an Omikron to connect to, when needing the Omikron a certain User / a certains user's Iota is connected to. +#### GET `/api/get/omikron/` +Get an Omikron to connect to, when needing the Omikron a certain Iota is connected to. ```json { - "status": "success", - "id": "", - "public_key": "", - "ip_address": "", - "port": "" + "type": "omikron", + "data": { + "omikron_id": id, + "domain": "omikron.tensamin.net" + } } -``` - -#### GET `/api/get/omikron/` - -Returns a connected `Omikron` by ID. If `` identifies a connected `Iota` -or a user with an assigned `Iota`, the response identifies that account's -primary connected `Omikron`. - -##### `RES:` - -```json -{ - "status": "success", - "id": "", - "public_key": "", - "ip_address": "", - "port": "" -} -``` - -#### GET `/api/get/connections` - -Returns the live in-memory map of `Omikron` IDs to their connected `Iota` IDs -and hosted user IDs. The map is empty after an Omega restart until connected -`Omikron` instances publish their state. - -##### `RES:` - -```json -{ - "status": "success", - "": { - "": ["", "..."] - } -} -``` - -#### GET `/api/get/iota/` - -Returns the public identity data for an `Iota`. - -##### `RES:` - -```json -{ - "status": "success", - "iota_id": "", - "public_key": "" -} -``` - -#### GET `/api/get/user/` - -Returns a user's profile and assigned `Iota` ID. - -##### `RES:` - -```json -{ - "status": "success", - "username": "", - "public_key": "", - "user_id": "", - "iota_id": "?", - "sub_level": "", - "sub_end": "", - "display": "?", - "status_message": "?", - "about": "?", - "avatar": "?" -} -``` - -#### GET `/api/get/id/` - -Resolves a username to its public profile and identity IDs. The username is -limited to 15 characters after URL decoding. - -##### `RES:` - -```json -{ - "status": "success", - "username": "", - "public_key": "", - "user_id": "", - "iota_id": "?", - "sub_level": "", - "sub_end": "" -} -``` - -#### GET `/api/get/public_key` - -Returns Omega's public MTP key bundle in base64 form. - -##### `RES:` - -```json -{ - "status": "success", - "public_key": "" -} -``` +``` \ No newline at end of file diff --git a/src/content/docs/components/omikron/endpoints.md b/src/content/docs/components/omikron/endpoints.md index 761b5cf..1334526 100644 --- a/src/content/docs/components/omikron/endpoints.md +++ b/src/content/docs/components/omikron/endpoints.md @@ -1,147 +1,252 @@ --- title: Endpoints -lastUpdated: 2026-08-14 --- -Omikron exposes an MTP WebTransport endpoint through its HTTPS listener. It -does not expose the previous JSON-over-WebSocket challenge protocol. Native -and browser clients use the MTP client APIs described in the repository's -`mtp-docs` guides. +To use the `Omikrons` functions a Secure Websocket connection must be established. +Where to connect is provided by the `Omega` Server. +All entries are split into `Iota` & `Client`. +As the `Omikron` connects the `Iota` and `Client` by passing messages from one to the other all Communication from `Iota` to `Client` has to contain a `receiver_id` +The `Omikron` will add a `sender_id` to Messages from `Client` to the `Iota`. -Omikron and its peers use the MTP `0.3.0` implementation and the wire protocol -version `3.0`. The MTP opening exchange negotiates the version and completes -transport authentication before Omikron dispatches application frames. +The `receiver_id` and `sender_id` of the `Omikron` server are `22222222-2222-2222-2222-222222222222`. -## Connection identities +## Messages -The MTP connection description selects the Omikron connection class: +### Message mutation forwarding -| Description | Authentication | Connection | Relay access | -| --- | --- | --- | --- | -| `client` | Registered MTP authentication | User client | Yes | -| `iota` | Registered MTP authentication | Iota node | Yes | -| `anonymous` | Unauthenticated | Temporary anonymous client | No | +`Omikron` forwards `MessageEditLive`, `MessageReactionLive`, and `MessageDeleteLive` by their MTP receiver. The sender Iota persists the authenticated request before forwarding it. The recipient Iota persists the live update before Omikron sends it to the recipient client. -The `client` ID is the authenticated user ID. The `iota` ID is the -authenticated Iota ID. A connection with an unknown description is rejected by -the application dispatcher. MTP challenge and proof frames are transport -messages, not application endpoints. +The request types `MessageEdit`, `MessageReactionAdd`, and `MessageReactionRemove` stay on the sender Iota. `MessageDeleteLive` is both the delete request and the live update; the authenticated connection direction distinguishes the request from a forwarded update. -## MTP frame shape +> See also: `components/iota/endpoints.md` for message mutation payloads. -MTP sends binary `CommunicationValue` frames. The following is a logical shape -for documentation; it is not a JSON wire format. +## Identification -```text -CommunicationValue { - id: optional u32, - type: CommunicationType, - sender: optional u64, - receiver: optional u64, - payload: DataValue, +Any message to the `Omikron` will be ignored until Identification. + +### Iota + +##### `REQ:` + +```json +{ + "type": "identification", + "data": { + "iota_id": "", + } } ``` -`Relay` is the reserved opaque communication type. A Relay has no outer -`sender`, its outer `receiver` is the next-hop ID, and its payload contains -protected relay metadata and content. Omikron reads only the clear routing -fields. It never opens relay metadata or content, resolves the original -signer, or changes the sealed payload. +##### `REQ & RES:` -Tensamin user IDs and Iota IDs currently share the same numeric range. An -Iota-origin Relay whose receiver matches both a local Client identity and a -local Iota identity is rejected as ambiguous; Omikron never chooses a route -from a colliding numeric ID. +```json +{ + "type": "challenge", + "data": { + "public_key": "", + "challenge": "" + } +} +``` +##### `REQ & RES:` -## Relay +```json +{ + "type": "challenge_response", + "data": { + "challenge": "", + } +} +``` +##### `RES:` -### Client to Iota - -A registered Client may send a Relay only to the Iota currently associated -with that Client. - -```text -Relay { - id: request or correlation ID, - sender: absent, - receiver: associated Iota ID, - payload: opaque sealed relay, +```json +{ + "type": "identification_response", + "data": { + "accepted": boolean, + } } ``` -Omikron validates the Relay layout with MTP's `forward_relay_frame` and sends -the unchanged frame to the local Iota connection. A Client cannot address an -arbitrary Iota or Omikron through this path. +--- -### Iota routing +### Client -An Iota may send a Relay to a locally connected Client, another locally -connected Iota, or Omega. Omikron chooses the destination from the clear outer -receiver and the authenticated connection that supplied the frame. +This is a two step process. -| Source connection | Destination | Omikron action | -| --- | --- | --- | -| Client | Its associated Iota | Send to that local Iota | -| Iota | Local Client | Send to matching client sessions | -| Iota | Local Iota | Send to that Iota | -| Iota | Remote Iota | Send to Omega | -| Omega | Local Iota | Send to that Iota | -| Anonymous client | Any destination | Reject | +Get a challenge: -Omega sends a Relay to one destination Omikron. Omikron accepts it only when -the destination Iota is connected locally. It never sends an unavailable -Omega Relay back to Omega or broadcasts it to other Omikrons. +#### `REQ:` -### Validation and errors +```json +{ + "id": "", + "type": "identification", + "data": { + "user_id": "" + } +} +``` -Omikron rejects a Relay when: +#### `RES:` -- the communication type is not the reserved `Relay` type; -- an outer sender is present; -- the next-hop receiver is missing; -- a Client addresses an Iota other than its associated Iota; -- an Iota routes to itself; -- the destination Iota or Client is unavailable; or -- the frame cannot be sent on the selected connection. +```json +{ + "id": "", + "type": "challenge", + "data": { + "challenge": "", + "public_key": "" + } +} +``` -Routing failures are ordinary service error frames sent to the authenticated -source connection. The failure frame is not a Relay and does not claim to be -from the original user. Unknown non-Relay application messages are rejected; -Omikron does not use generic fallthrough forwarding. +Send back the solved challenge: -## Application messages +#### `REQ:` -Direct control messages remain available after MTP authentication. Examples -include `GetUserData`, `GetIotaData`, `ChangeUserData`, `ClientChanged`, and -registration control messages. These messages use the negotiated MTP type map -and may use the existing control-plane sender semantics. +```json +{ + "id": "", + "type": "challenge_response", + "data": { + "challenge": "" + } +} +``` -`MessageGet`, `MessagesGet`, and `GetChatSecret` are authenticated peer-control -messages. A Client sends them to its associated Iota through Omikron. Omikron -adds the authenticated client ID as the outer sender for the Iota request, -waits for the correlated response, and returns that response to the Client. -It does not read or store message contents. +#### `RES:` -Those semantics do not apply to `Relay`. Omikron never adds a user or Iota ID -to a Relay and never uses the outer sender as the original message identity. +```json +{ + "id": "", + "type": "identification_response", + "data": {} +} +``` -Message delivery, edits, reactions, deletion, chat secrets, and other -application operations travel inside the sealed Relay. The recipient Iota -opens and verifies the Relay, checks the original signer against its trusted -public-key history, applies the operation, and persists or forwards the sealed -Relay when required. +## Ping & Pong -## Keepalive +A ping request comes from the Client or Iota and the Omikron returns a pong answer. +The ping request should contain the last Client or Iota ping and the pong answer will contain the last ping of the Omikron.**** -MTP handles protocol Ping and Pong frames through the connection dispatcher. -Application handlers must not treat keepalive frames as Relay traffic. See the -repository's `mtp-docs/CONNECTIONS.md` and `mtp-docs/PROTOCOL-REFERENCE.md` -files. +### Iota -## Related API documentation +#### REQ: -- [Calling](./calling) -- [User management](./user-management) -- `mtp-docs/CONNECTOR.md` -- `mtp-docs/NATIVE-CLIENT.md` -- `mtp-docs/NATIVE-HOST-WEB-SERVER.md` +```json +{ + "id": "", + "type": "ping", + "data": { + "last_ping": long + } +} +``` + +#### RES: + +```json +{ + "id": "", + "type": "pong", + "data": { + "user_ping": { + "": long, + "": long + } + } +} +``` + +### Client + +#### `REQ:` + +```json +{ + "id": "", + "type": "ping", + "data": { + "last_ping": long + } +} +``` + +#### `RES:` + +```json +{ + "id": "", + "type": "pong", + "data": { + "last_ping": long + } +} +``` + +## User Online Status + +### Set Client Status + +The client can change their online status by sending a `client_changed` message. +Supported status values: `user_online`, `user_offline`, `user_dnd`, `user_idle`, `user_wc`, `user_invisible`. + +When `user_invisible` is set, the user remains connected and can send/receive messages, but appears as `user_offline` to all other users. + +#### `REQ` (Client → Omikron): + +```json +{ + "id": "", + "type": "client_changed", + "data": { + "user_state": "" + } +} +``` + +### Online Status Notifications + +When a connected user changes their status, interested clients receive a notification: + +#### `EVENT` (Omikron → Client): + +```json +{ + "type": "client_changed", + "data": { + "user_id": "", + "user_state": "" + } +} +``` + +### Online Status in User Data + +When fetching another user's profile via `get_user_data`, their online status is included in the response. Invisible users are reported as `user_offline`. + +#### `RES` (Omikron → Client, field in `get_user_data`): + +```json +{ + "id": "", + "type": "get_user_data", + "data": { + "user_id": long, + "username": "", + "display": "", + "public_key": "", + "iota_id": long, + "online_status": "", + "omikron_id": long, + "omikron_connections": [long, ...], + "sub_level": long, + "sub_end": long, + "about": "?", + "avatar": "?", + "status": "?" + } +} +```