CoMatrix Client Library

CoMatrix RIOT Module - Developer Documentation

CoMatrix provides a high-level interface for Matrix client-server communication (https://matrix.org/docs/spec/client_server/r0.6.1.html) using gcoap CoAP message requests via RIOT’s sock networking API. Only basic functionality is implemented, which enables a IoT device to register, login and logout at a Matrix-Synapse homeserver, to join a specific Matrix room where the user is invited to. As well as sending messages into a specified Matrix room and retrieve the last message of a room.

Requirements

  • Hardware: tested on samr21-xpro board
  • Software: tested on RIOT OS (Release-2021.04,tag 46bc55f514)

CoMatrix Configuration

Application specific settings must be set in an app.config file, consider the app.config.example in the example folders. Required parameters must be set by the CoMatrix init function and stored in the CoMatrix client state.

  • CONFIG_KCONFIG_USEMODULE_COMATRIX - Enable configuration of CoMatrix module
    • Default: y
    • This parameter enables application specific configuration for the external module CoMatrix.
  • CONFIG_COMATRIX_MAX_TOKEN_LENGTH - Length of the buffer for the Matrix-Synapse homeserver access token
    • Default: 300 bytes
    • Due to a change of the length of the Matrix-Synapse access token from around 280 bytes to around 40 bytes in the release Synapse 1.34.0 (2021-05-17) the bandwidth consumption has been reduced. For compability reasons with older versions of the Synapse homeserver we still kept the default value at 300 bytes. Reduce this the CONFIG_COMATRIX_MAX_TOKEN_LENGTH value to 50 bytes in the app.config file if you use the new release.
  • CONFIG_COMATRIX_MAX_ROOMID_LENGTH - Length of the buffer for the Matrix room ID
    • Default: 50 bytes
  • CONFIG_COMATRIX_TXID_LENGTH - Bit-length of the initialization value of the Matrix message ID
    • Default: 0xffff
    • During the initialization of the client state a timestamp is retrieved from the CoMatrix gateway and a part from this timestamp is used as message ID. This value defines the number of the least significant bits which are extracted by a bitwise AND operation.
  • CONFIG_COMATRIX_ENABLE_SHORTURL - Set the proxy url mode to short
  • CONFIG_COMATRIX_ROOMID - Specific Matrix room ID
    • Default: Required (specially for devices without input options)
    • This parameter must be set in the app.config file or the CoMatrix join function needs to be called to set a new Matrix room ID. Use the Element.io Matrix Client to find out the room ID for a specific Room ->Settings->Advanced. The Matrix room ID starts with an ! and has a : in between the host domain. Example Matrix room ID: "!base64string:synapse host"
  • CONFIG_COMATRIX_GATEWAYIP6 - IPv6 address of CoMatrix Gateway
    • Default: fe80::1
    • If you follow the setup steps, the gateway will be configured to fe80::1 to keep the IPv6 link-local address short. Otherwise determine the link-local address of the lowpan0 interface on the gateway with ifconfig.
  • CONFIG_COMATRIX_SYNAPSE - HTTP schema and address or domain and port of the Matrix-Synapse homeserver
    • Default: Required
    • Use this parameter to define the HTTP schema and the Synapse endpoint address or domain and port such as “https://192.168.1.102:8008”. This parameter must be set in the app.config file.
  • CONFIG_COMATRIX_TOKEN - Optional Matrix-Synapse homeserver access token for an user

Further settings:

  • CONFIG_GCOAP_PDU_BUF_SIZE - CoAP packet buffer size
    • Default: 500
    • The length of the UDP packet buffer is changed from the gcoap default value of 128 bytes to 500 bytes. Maybe the default value will change in future (https://github.com/RIOT-OS/RIOT/pull/16377. Change this value according to your application requirements.
  • CONFIG_GCOAP_NON_TIMEOUT - Timeout for confirmable CoAP requests
    • Default: 10000000
    • The timeout value for confirmable CoAP requests has been increased, because we expect that HTTP proxying to the Matrix-Synapse homeserver takes more time than the communication with a local proxy server.

Dependencies

The application Makefile needs to include following modules:

  • gnrc_netdev_default
  • auto_init_gnrc_netif
  • gnrc_ipv6_router_default
  • gcoap
  • comatrix (as an external module)

CoMatrix Client Module Brief Function Definitions

Function definitions Brief description of purpose
CoMatrix Init
int comatrix_init(comatrix_callback_t callback_handler);
- initializes network and CoMatrix settings stored in client state
- sends a CoAP timestamp request to the gateway
- calls the _comatrixlogin_resp_handler response handler, on success a part of the timestamp is stored in client state and used as CoMatrix text message counter (tx_id)
CoMatrix Register
int comatrix_register(char *username, char *password, comatrix_callback_t callback_handler);
- expects a valid Matrix username and password
- sends a CoAP registration request with CBOR encoded payload of username and password to the gateway
- calls the _comatrixlogin_resp_handler response handler which on success sets a Synapse token in CoMatrix client state
CoMatrix Login
int comatrix_login(char *username, char *password, comatrix_callback_t callback_handler);
- expects a valid Matrix username and password
- sends a CoAP login request with CBOR encoded payload of username and password to the gateway
- calls the _comatrixlogin_resp_handler response handler which on success sets a Synapse access token in CoMatrix client state
CoMatrix Logout
int comatrix_logout(comatrix_callback_t callback_handler);
- sends a CoAP logout request to the gateway
- calls the _comatrixlogout_resp_handler on success; the access token is invalidated at the Matrix-Synapse homeserver
CoMatrix Join
int comatrix_join(char * room_id);
- expects a Matrix room id
- sends a CoAP join message request to the gateway
- calls the _comatrixjoin_resp_handler response handler which on success sets the new access token in the CoMatrix client state
CoMatrix Sendmsg
int comatrix_sendmsg(char *msgbuf, size_t msglen);
- expects a message string and sends a CoAP send message request with CBOR encoded msg payload to the gateway, type is non-confirmable and no response handler is called
CoMatrix Recmsg
int comatrix_recmsg(comatrix_callback_t callback_handler);
- expects a pointer to the response message
- sends a CoAP send message request to the gateway
- calls the _comatrixrecmsg_resp_handler which parses the new CoMatrix message and passes a pointer to the message to the application callback function

CoMatrix Client Module Detailed Function Definitions

CoMatrix Init

/**
 *  @brief Initializes client state and sends a COAP GET timestamp request to the Gateway
 *  and calls _comatrixinit_resp_handler response handler
 *
 *  @param[in] callback_handler   pointer to callback function in the application, can be 0
 *
 *  @return 1 if the time COAP GET request is sent, else
 *  @return 0 when link-local address and no valid interface is found
 *  @return -1 on error
 */
int comatrix_init(comatrix_callback_t callback_handler);

Initializes CoMatrix client state. If the optional parameters, a Synapse token (CONFIG_COMATRIX_TOKEN) and a Matrix room ID (CONFIG_COMATRIX_ROOMID), are configured in the application configuration file app.config, these are stored in the CoMatrix client state. The required parameter in the configuration to initialize a remote UDP socket in the client state, is the gateway IPv6 address (CONFIG_COMATRIX_GATEWAYIP6). If this is a link-local address, a valid interface must be found, otherwise the init function will terminate with return 0 and a corresponding DEBUG message. The default CoAP port 5683 can be modified in the app.config by setting the variable CONFIG_GCOAP_PORT.

To send a message into a Matrix room, the CoMatrix client must provide a unique tx_id (for a specific user), therefore the client sends a CoAP request to get a timestamp from the gateway and sets the _comatrixinit_resp_handler. If the message is successfully sent, the function returns 1 otherwise -1.

Known bug/limitation: Unfortunately after a hard reset, the SAMR21-xpro generates the same COAP token for the first CoAP request due to a lack of randomness on initialization.

CoAP Request
CoAP method/connection type GET / CON
Synapse access token -
Comatrix gateway path /time
Synapse Proxy URI -
CBOR encoded payload -
CoAP Response
Response Handler _comatrixinit_resp_handler
Purpose sets message ID in client state
Response payload -
Output Response containing CoAP response code and timestamp is passed to callback handler or debug output

CoMatrix Init Response Handler

The init response handler expects the timestamp value in text format and applies the length as set in the app.config file in CONFIG_COMATRIX_TXID_LENGTH. This value is stored in client state and used as CoMatrix text message counter (tx_id).

If a callback handler is provided by the application, a response containing the CoAP response code and the timestamp is passed to it.

CoMatrix Register

/**
 *  @brief Sends a COAP register POST request to the gateway and
 *  calls the _comatrixlogin_resp_handler response handler which sets a Synapse access token on success
 *
 *  @param[in] username           matrix username
 *  @param[in] password           matrix password
 *  @param[in] callback_handler   pointer to callback function in the application, can be 0
 *
 *  @return length of the payload
 *  @return 0  when the message exceeds the PDU payload buffer size
 *  @return -1 when CoAP login POST request failed
 */
 int comatrix_register(char *username, char *password, comatrix_callback_t callback_handler);

Expects a new Matrix username, password and callback_handler as input. If the input credentials encoded into the CBOR payload exceed the payload buffer length, the function returns 0. Otherwise the arguments are encoded to a CBOR payload (encbor_comatrixlogin), which is sent in a CoAP POST request to the gateway and the login response handler is set.

CoAP Request
CoAP method/connection type POST / CON
Synapse access token -
Comatrix gateway path /register
Synapse Proxy URI long: CONFIG_COMATRIX_SYNAPSE+ '/_matrix/client/r0/register'
Synapse Proxy URI short: CONFIG_COMATRIX_SYNAPSE+'/4'
CBOR encoded payload of payload = '{"username":"' + username + '", "password":"' + password + '", "auth": {"type":"m.login.dummy"}}'
CoAP Response
Response Handler _comatrixlogin_resp_handler
Purpose sets new Synapse access token in CoMatrix client state
Response payload {"access_token":"syt_c2FtcjIxbm9kZQ_WvySpTPnUZLOqVHlVdit_0yhpdY"}
Output CoAP response code passed to callback handler or debug output

CoMatrix Login

/**
 *  @brief Sends a COAP login POST request to the gateway
 *  and calls the _comatrixlogin_resp_handler response handler which sets a Synapse access token on success
 *
 *  @param[in] username           matrix username
 *  @param[in] password           matrix password
 *  @param[in] callback_handler   pointer to callback function in the application, can be 0
 *
 *  @return length of the payload
 *  @return 0  when the message exceeds PDU payload buffer size
 *  @return -1 when CoAP login POST request failed
 */
int comatrix_login(char *username, char *password, comatrix_callback_t callback_handler);

Expects a Matrix username, password and callback_handler as input. If the input credentials encoded into the CBOR payload exceed the payload buffer length, the function returns 0. Otherwise the arguments are encoded to a CBOR payload (encbor_comatrixlogin), which is sent in a CoAP POST request to the gateway and the login response handler is set.

CoAP Request
CoAP method/connection type POST / CON
Synapse access token -
Comatrix gateway path /login
Synapse Proxy URI long: CONFIG_COMATRIX_SYNAPSE+ '/_matrix/client/r0/login'
Synapse Proxy URI short: CONFIG_COMATRIX_SYNAPSE+'/1'
CBOR encoded payload of payload = '{"username":"' + username + '", "password":"' + password + '", "auth": {"type":"m.login.dummy"}}'
CoAP Response
Response Handler _comatrixlogin_resp_handler
Purpose sets new Synapse token in CoMatrix client state
Response payload {"access_token":"syt_c2FtcjIxbm9kZQ_WvySpTPnUZLOqVHlVdit_0yhpdY"}
Output CoAP response code passed to callback handler or debug output

CoMatrix Login Response Handler

The Login Response Handler expects a CBOR encoded response containing the Matrix-Synapse access token. This value is extracted by the decbor_comatrixtoken() function and stored in the CoMatrix client state. If a callback handler is provided by the application, a response containing the CoAP response code is passed to it.

CoMatrix Join

/**
 *  @brief Sends a COAP join POST request to the gateway and calls the _comatrixrecmsg_resp_handler response handler
 *
 *  @param[in] room_id            Matrix room ID of a Matrix-Synapse homeserver
 *  @param[in] callback_handler   Pointer to callback function in the application could be 0
 *
 *  @return length of the payload
 *  @return 0  when the room ID exceeds PDU payload buffer size
 *  @return -1 when sending CoAP login POST request failed
 */
int comatrix_join(char *room_id, comatrix_callback_t callback_handler);

It is required that the application user is already invited to a specific Matrix room. By now it is only possible to join one Matrix room. The function expects a valid Matrix room ID, if the size of the provided room ID exceeds the buffer the function returns 0. Otherwise the Matrix room ID is passed as a URL parameter in a CoAP POST request and the join response handler is set.

CoAP Request
CoAP method/connection type POST / CON
Synapse access token token set
Comatrix gateway path /join
Synapse Proxy URI long: CONFIG_COMATRIX_SYNAPSE+ '/_matrix/client/r0/rooms/' + room_id + '/join'
Synapse Proxy URI short: CONFIG_COMATRIX_SYNAPSE+'/K'+ room_id
CBOR encoded payload of -
CoAP Response
Response Handler call _comatrixjoin_resp_handler
Purpose sets new Matrix room ID in CoMatrix client state
Response Payload {"room_id":"!AHRBprwfaBRKJxOBVW:matrix.localhost"}
Output CoAP response code passed to callback handler or debug output

CoMatrix Join Response Handler

The Join Response Handler expects a CBOR encoded response containing the Matrix room ID. This value is extracted by the decbor_comatrixroomid() function and stored in the CoMatrix client state. If a callback handler is provided by the application, a response containing the CoAP response code is passed to it.

CoMatrix Logout

/**
 *  @brief Sends a COAP logout request to the gateway and calls the _comatrixlogout_resp_handler response handler
 *        on success the access token in the CoMatrix state is invalidated
 *
 *  @param[in] callback_handler   Pointer to callback function in the application, can be 0
 *
 *  @return length of the payload
 *  @return -1 when sending CoAP login POST request failed
 */
int comatrix_logout(comatrix_callback_t callback_handler);

The logout function expects a callback handler as input, which also can be 0. It sends a CoAP logout POST request with a Synapse access token set to the gateway and sets the logout response handler. If the packet is sent, the function returns 1 otherwise -1.

CoAP Request
CoAP method/connection type POST / CON
Synapse access token token set
Comatrix gateway path /logout
Synapse Proxy URI long: CONFIG_COMATRIX_SYNAPSE+ '/_matrix/client/r0/logout'
Synapse Proxy URI short: CONFIG_COMATRIX_SYNAPSE+'/3'
CBOR encoded payload of -
Response
Response Handler call _comatrixlogout_resp_handler
Purpose sets new Matrix room ID in CoMatrix client state
Response Payload -
Output CoAP response code passed to callback handler or debug output

CoMatrix Logout Response Handler

The Logout Response Handler expects a CoAP response. If a callback handler is provided by the application, a response containing the CoAP response code is passed to it. On success the token is invalidated at the Synapse homeserver.

CoMatrix Send Message

/**
 *  @brief Sends a COAP send message PUT request to the gateway, type is non-confirmable and no response handler is called
 *
 *  Send message request is non-confirmable for constrained devices like sensor nodes, when communication errors occur the packet will not be resent
 *
 *  @param[in] msgbuf               message buffer.
 *  @param[in] msglen               length of message buffer.
 *  @param[in] callback_handler     should be 0 - not implemented yet because it is non-confirmable
 *
 *  @return length of the payload
 *  @return 0   when the message exceeds PDU payload buffer size
 *  @return -1  when CoAP send message PUT request failed
 */
int comatrix_sendmsg(char *msgbuf, size_t msglen, comatrix_callback_t callback_handler);

Expects a message, the length of the message buffer and a pointer to a callbackhandler which can be 0. If the CBOR encoded message size exceeds the payload length, the function returns 0. The message is passed to encbor_comatrixmsg() function for CBOR encoding and set as payload in a CoAP PUT request. The proxy URL must contain a valid Matrix room ID (room_id) and a unique message text ID (tx_id). After setting the message text ID the value is incremented and stored in the CoMatrix client state. This request is sent as non-confirmable to reduce the workload for tiny constrained devices like battery powered sensor nodes. When communication errors occur the packet will not be resent.

No response handler is set and on successful sending the packet the function returns 1 otherwise -1.

CoAP Request
CoAP method/connection type PUT / CON
Synapse access token token set
Comatrix gateway path /send
Synapse Proxy URI long: CONFIG_COMATRIX_SYNAPSE+ '_matrix/client/r0/rooms/'+ room_id +'/send/m.room.message/'tx_id
Synapse Proxy URI short: CONFIG_COMATRIX_SYNAPSE+ '/9/'+ room_id +'/m.room.message/'+ tx_id
CBOR encoded payload of '{"msgtype":"m.text","body":"'+ msg +'"}'
CoAP Response
Response Handler -
Purpose
Response Payload -
Output -

CoMatrix Receive Message

/**
 *  @brief Sends a COAP receive message request to the gateway and calls the _comatrixrecmsg_resp_handler response handler
 *
 *  @param[in] callback_handler   Callback when message received, should not be NULL
 *
 *  @return length of the payload if the receive msg COAP GET request is sent
 *  @return -1 sending receive msg COAP GET request failed
 */
int comatrix_recmsg(comatrix_callback_t callback_handler);

Expects a callbackhandler of the application and sends a CoAP GET request to the gateway. A valid Matrix-Synapse access token must be set and the proxy URI must contain a valid Matrix room ID. The request tries to retrieve the last message sent to a Matrix room. The receive message response handler is set and the function returns 1 when the request is successfully sent otherwise -1.

CoAP Request
CoAP method/connection type GET / CON
Synapse access token token set
Comatrix gateway path /getmsg
Synapse Proxy URI long: CONFIG_COMATRIX_SYNAPSE+ '_matrix/client/r0/rooms/'+ room_id +'/messages?dir=b&limit=1
Synapse Proxy URI short: CONFIG_COMATRIX_SYNAPSE+ '/9/'+ room_id
CBOR encoded payload of
CoAP Response
Response Handler _comatrixrecmsg_resp_handler
Purpose decodes payload and passes message to callback handler
Response Payload {'sender':' '@test_account:matrix.localhost', 'body': 'whatever'}
Output Message containing sender, text and CoAP response code is passed to callback handler in the application

CoMatrix Receive Message Response Handler

The Receive Message Response Handler expects a CoAP response packet containing a CBOR encoded payload of the message. The function decbor_comatrixmsg() is used to decode the message and pass it back to a callback handler retrieved from the gcoap memo context. The application must provide a callback handler to get the message. When the last event in a room is an invite or something different than a plain message (i.e. of typ m.room.message), CoMatrix receivemsg gets a 400 Bad request from the gateway.

Known Limitations

  • When the last event in a room is an invite or something different than a plain message (i.e. of typ m.room.message), CoMatrix receivemsg gets a 400 Bad request from the gateway.

Future Work

Sources