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.
- Default:
- 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 theapp.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
- Default:
n
- Constrained environments may also have limited bandwith demands, therefore the CoMatrix client library provides the usage of short proxy-urls. The CoMatrix gateway will map to the correct URL targeting the Matrix-Synapse homeserver according to the proposal in (https://github.com/matrix-org/matrix-doc/blob/7d20c1d9c19972fa63d1d9c124c3656928c28c29/proposals/3079-low-bandwidth-csapi.md#appendix-b-coap-path-enums). By now just the short proxy-url mapping is implemented and not the CBOR integer keys.
- Default:
- 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 thelowpan0
interface on the gateway withifconfig
.
- Default:
- 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
- Default: Required (for devices with no input options)
- This token is sent in authenticated CoAP requests as header option with the identifier 256 as proposed in (https://github.com/matrix-org/matrix-doc/blob/7d20c1d9c19972fa63d1d9c124c3656928c28c29/proposals/3079-low-bandwidth-csapi.md#appendix-b-coap-path-enums). For devices without input options (keyboard), the Matrix-Synapse homeserver access token must be set in the application configuration file. Otherwise this user specific access token must be generated and set with the registration or login function, which send an authentication request to the Matrix-Synapse homeserver.
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 Initint 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 Registerint 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 Loginint 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 Logoutint 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 Joinint 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 Sendmsgint 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 Recmsgint 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.confi
g 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
- Implement unit and integration tests
- Implement a callback handler receive function to pass the Matrix message and errors to an application main function when provided by the gateway
- Reduce bandwidth by using CBOR integer keys for the requests and responses as described in (https://github.com/matrix-org/matrix-doc/blob/7d20c1d9c19972fa63d1d9c124c3656928c28c29/proposals/3079-low-bandwidth-csapi.md#appendix-b-coap-path-enums)
- Enable encryption of communication to the CoMatrix gateway via DTLS, OSCORE or OpenThread
Sources
- Compatible Matrix Client-Server API specification: https://matrix.org/docs/spec/client_server/r0.6.1.html
- RIOT-OS on GitHub: https://github.com/RIOT-OS/RIOT
- Documentation of RIOT-OS CoAP libraries:
- TinyCBOR on GitHub: https://github.com/intel/tinycbor