Plugins

The EMQ broker could be extended by plugins. Users could develop plugins to customize authentication, ACL and functions of the broker, or integrate the broker with other systems.

The plugins that EMQ 2.0-rc.2 released:

Plugin Description
emq_dashboard Web Dashboard
emq_retainer Store Retained Messages
emq_modules Extend Modules Plugin
emq_auth_clientid ClientId Auth Plugin
emq_auth_username Username/Password Auth Plugin
emq_auth_ldap LDAP Auth
emq_auth_http HTTP Auth/ACL Plugin
emq_auth_mysql MySQL Auth/ACL Plugin
emq_auth_pgsql PostgreSQL Auth/ACL Plugin
emq_auth_redis Redis Auth/ACL Plugin
emq_auth_mongo MongoDB Auth/ACL Plugin
emq_web_hook Web Hook Plugin
emq_lua_hook Lua Hook Plugin
emq_coap CoAP Protocol Plugin
emq_sn MQTT-SN Protocol Plugin
emq_stomp STOMP Protocol Plugin
emq_sockjs STOMP over SockJS Plugin
emq_recon Recon Plugin
emq_reloader Reloader Plugin
emq_plugin_template Template Plugin

emq_plugin_template - Template Plugin

A plugin is just a normal Erlang application which has its own configuration file: ‘etc/<PluginName>.conf|config’.

emq_plugin_template is a plugin template.

Load, unload Plugin

Use ‘bin/emqttd_ctl plugins’ CLI to load, unload a plugin:

./bin/emqttd_ctl plugins load <PluginName>

./bin/emqttd_ctl plugins unload <PluginName>

./bin/emqttd_ctl plugins list

emq_retainer - Retainer Plugin

Renamed the emq_mod_retainer to emq_retainer project in 2.1-beta release.

Configure Retainer Plugin

etc/plugins/emq_retainer.conf:

## disc: disc_copies, ram: ram_copies
## Notice: retainer's storage_type on each node in a cluster must be the same!
retainer.storage_type = disc

## Max number of retained messages
retainer.max_message_num = 1000000

## Max Payload Size of retained message
retainer.max_payload_size = 64KB

## Expiry interval. Never expired if 0
## h - hour
## m - minute
## s - second
retainer.expiry_interval = 0

emq_auth_clientid - ClientID Auth Plugin

Released in 2.0-rc.2: https://github.com/emqtt/emq_auth_clientid

Configure ClientID Auth Plugin

etc/plugins/emq_auth_clientid.conf:

##auth.client.$N.clientid = clientid
##auth.client.$N.password = passwd

## Examples
##auth.client.1.clientid = id
##auth.client.1.password = passwd
##auth.client.2.clientid = dev:devid
##auth.client.2.password = passwd2
##auth.client.3.clientid = app:appid
##auth.client.3.password = passwd3

Load ClientId Auth Plugin

./bin/emqttd_ctl plugins load emq_auth_clientid

emq_auth_username - Username Auth Plugin

Released in 2.0-rc.2: https://github.com/emqtt/emq_auth_username

Configure Username Auth Plugin

etc/plugins/emq_auth_username.conf:

##auth.user.$N.username = admin
##auth.user.$N.password = public

## Examples:
##auth.user.1.username = admin
##auth.user.1.password = public
##auth.user.2.username = feng@emqtt.io
##auth.user.2.password = public

Add username/password by ./bin/emqttd_ctl users CLI:

$ ./bin/emqttd_ctl users add <Username> <Password>

Load Username Auth Plugin

./bin/emqttd_ctl plugins load emq_auth_username

emq_dashboard - Dashboard Plugin

The Web Dashboard for EMQ broker. The plugin will be loaded automatically when the broker started successfully.

Address https://localhost:18083
Default User admin
Default Password public
_images/dashboard1.png

Configure Dashboard Plugin

etc/plugins/emq_dashboard.conf:

## HTTP Listener
dashboard.listener.http = 18083
dashboard.listener.http.acceptors = 2
dashboard.listener.http.max_clients = 512

## HTTPS Listener
## dashboard.listener.https = 18084
## dashboard.listener.https.acceptors = 2
## dashboard.listener.https.max_clients = 512
## dashboard.listener.https.handshake_timeout = 15s
## dashboard.listener.https.certfile = etc/certs/cert.pem
## dashboard.listener.https.keyfile = etc/certs/key.pem
## dashboard.listener.https.cacertfile = etc/certs/cacert.pem
## dashboard.listener.https.verify = verify_peer
## dashboard.listener.https.fail_if_no_peer_cert = true

emq_auth_ldap: LDAP Auth Plugin

LDAP Auth Plugin: https://github.com/emqtt/emq_auth_ldap

Note

Released in 2.0-beta.1

Configure LDAP Plugin

etc/plugins/emq_auth_ldap.conf:

auth.ldap.servers = 127.0.0.1

auth.ldap.port = 389

auth.ldap.timeout = 30

auth.ldap.user_dn = uid=%u,ou=People,dc=example,dc=com

auth.ldap.ssl = false

Load LDAP Plugin

./bin/emqttd_ctl plugins load emq_auth_ldap

emq_auth_http - HTTP Auth/ACL Plugin

MQTT Authentication/ACL with HTTP API: https://github.com/emqtt/emq_auth_http

Note

Supported in 1.1 release

Configure HTTP Auth/ACL Plugin

etc/plugins/emq_auth_http.conf:

## Variables: %u = username, %c = clientid, %a = ipaddress, %P = password, %t = topic

auth.http.auth_req = https://127.0.0.1:8080/mqtt/auth
auth.http.auth_req.method = post
auth.http.auth_req.params = clientid=%c,username=%u,password=%P

auth.http.super_req = https://127.0.0.1:8080/mqtt/superuser
auth.http.super_req.method = post
auth.http.super_req.params = clientid=%c,username=%u

## 'access' parameter: sub = 1, pub = 2
auth.http.acl_req = https://127.0.0.1:8080/mqtt/acl
auth.http.acl_req.method = get
auth.http.acl_req.params = access=%A,username=%u,clientid=%c,ipaddr=%a,topic=%t

HTTP Auth/ACL API

Return 200 if ok

Return 4xx if unauthorized

Load HTTP Auth/ACL Plugin

./bin/emqttd_ctl plugins load emq_auth_http

emq_auth_mysql - MySQL Auth/ACL Plugin

MQTT Authentication, ACL with MySQL database.

MQTT User Table

CREATE TABLE `mqtt_user` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `username` varchar(100) DEFAULT NULL,
  `password` varchar(100) DEFAULT NULL,
  `salt` varchar(20) DEFAULT NULL,
  `is_superuser` tinyint(1) DEFAULT 0,
  `created` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `mqtt_username` (`username`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

MQTT ACL Table

CREATE TABLE `mqtt_acl` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `allow` int(1) DEFAULT NULL COMMENT '0: deny, 1: allow',
  `ipaddr` varchar(60) DEFAULT NULL COMMENT 'IpAddress',
  `username` varchar(100) DEFAULT NULL COMMENT 'Username',
  `clientid` varchar(100) DEFAULT NULL COMMENT 'ClientId',
  `access` int(2) NOT NULL COMMENT '1: subscribe, 2: publish, 3: pubsub',
  `topic` varchar(100) NOT NULL DEFAULT '' COMMENT 'Topic Filter',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `mqtt_acl` (`id`, `allow`, `ipaddr`, `username`, `clientid`, `access`, `topic`)
VALUES
    (1,1,NULL,'$all',NULL,2,'#'),
    (2,0,NULL,'$all',NULL,1,'$SYS/#'),
    (3,0,NULL,'$all',NULL,1,'eq #'),
    (5,1,'127.0.0.1',NULL,NULL,2,'$SYS/#'),
    (6,1,'127.0.0.1',NULL,NULL,2,'#'),
    (7,1,NULL,'dashboard',NULL,1,'$SYS/#');

Configure MySQL Auth/ACL Plugin

etc/plugins/emq_auth_mysql.conf:

## Mysql Server
auth.mysql.server = 127.0.0.1:3306

## Mysql Pool Size
auth.mysql.pool = 8

## Mysql Username
## auth.mysql.username =

## Mysql Password
## auth.mysql.password =

## Mysql Database
auth.mysql.database = mqtt

## Variables: %u = username, %c = clientid

## Authentication Query: select password only
auth.mysql.auth_query = select password from mqtt_user where username = '%u' limit 1

## Password hash: plain, md5, sha, sha256, pbkdf2
auth.mysql.password_hash = sha256

## %% Superuser Query
auth.mysql.super_query = select is_superuser from mqtt_user where username = '%u' limit 1

## ACL Query Command
auth.mysql.acl_query = select allow, ipaddr, username, clientid, access, topic from mqtt_acl where ipaddr = '%a' or username = '%u' or username = '$all' or clientid = '%c'

Load MySQL Auth/ACL plugin

./bin/emqttd_ctl plugins load emq_auth_mysql

emq_auth_pgsql - PostgreSQL Auth/ACL Plugin

MQTT Authentication/ACL with PostgreSQL Database.

Postgre MQTT User Table

CREATE TABLE mqtt_user (
  id SERIAL primary key,
  is_superuser boolean,
  username character varying(100),
  password character varying(100),
  salt character varying(40)
);

Postgre MQTT ACL Table

CREATE TABLE mqtt_acl (
  id SERIAL primary key,
  allow integer,
  ipaddr character varying(60),
  username character varying(100),
  clientid character varying(100),
  access  integer,
  topic character varying(100)
);

INSERT INTO mqtt_acl (id, allow, ipaddr, username, clientid, access, topic)
VALUES
    (1,1,NULL,'$all',NULL,2,'#'),
    (2,0,NULL,'$all',NULL,1,'$SYS/#'),
    (3,0,NULL,'$all',NULL,1,'eq #'),
    (5,1,'127.0.0.1',NULL,NULL,2,'$SYS/#'),
    (6,1,'127.0.0.1',NULL,NULL,2,'#'),
    (7,1,NULL,'dashboard',NULL,1,'$SYS/#');

Configure Postgre Auth/ACL Plugin

Plugin Config: etc/plugins/emq_auth_pgsql.conf.

Configure host, username, password and database of PostgreSQL:

## Postgre Server
auth.pgsql.server = 127.0.0.1:5432

auth.pgsql.pool = 8

auth.pgsql.username = root

#auth.pgsql.password =

auth.pgsql.database = mqtt

auth.pgsql.encoding = utf8

auth.pgsql.ssl = false

## Variables: %u = username, %c = clientid, %a = ipaddress

## Authentication Query: select password only
auth.pgsql.auth_query = select password from mqtt_user where username = '%u' limit 1

## Password hash: plain, md5, sha, sha256, pbkdf2
auth.pgsql.password_hash = sha256

## sha256 with salt prefix
## auth.pgsql.password_hash = salt sha256

## sha256 with salt suffix
## auth.pgsql.password_hash = sha256 salt

## Superuser Query
auth.pgsql.super_query = select is_superuser from mqtt_user where username = '%u' limit 1

## ACL Query. Comment this query, the acl will be disabled.
auth.pgsql.acl_query = select allow, ipaddr, username, clientid, access, topic from mqtt_acl where ipaddr = '%a' or username = '%u' or username = '$all' or clientid = '%c'

Load Postgre Auth/ACL Plugin

./bin/emqttd_ctl plugins load emq_auth_pgsql

emq_auth_redis - Redis Auth/ACL Plugin

MQTT Authentication, ACL with Redis: https://github.com/emqtt/emq_auth_redis

Configure Redis Auth/ACL Plugin

etc/plugins/emq_auth_redis.conf:

## Redis Server
auth.redis.server = 127.0.0.1:6379

## Redis Pool Size
auth.redis.pool = 8

## Redis Database
auth.redis.database = 0

## Redis Password
## auth.redis.password =

## Variables: %u = username, %c = clientid

## Authentication Query Command
auth.redis.auth_cmd = HGET mqtt_user:%u password

## Password hash: plain, md5, sha, sha256, pbkdf2
auth.redis.password_hash = sha256

## Superuser Query Command
auth.redis.super_cmd = HGET mqtt_user:%u is_superuser

## ACL Query Command
auth.redis.acl_cmd = HGETALL mqtt_acl:%u

Redis User Hash

Set a ‘user’ hash with ‘password’ field, for example:

HSET mqtt_user:<username> is_superuser 1
HSET mqtt_user:<username> password "passwd"

Redis ACL Rule Hash

The plugin uses a redis Hash to store ACL rules:

HSET mqtt_acl:<username> topic1 1
HSET mqtt_acl:<username> topic2 2
HSET mqtt_acl:<username> topic3 3

Note

1: subscribe, 2: publish, 3: pubsub

Redis Subscription Hash

The plugin can store static subscriptions in a redis Hash:

HSET mqtt_subs:<username> topic1 0
HSET mqtt_subs:<username> topic2 1
HSET mqtt_subs:<username> topic3 2

Load Redis Auth/ACL Plugin

./bin/emqttd_ctl plugins load emq_auth_redis

emq_auth_mongo - MongoDB Auth/ACL Plugin

MQTT Authentication/ACL with MongoDB: https://github.com/emqtt/emq_auth_mongo

Configure MongoDB Auth/ACL Plugin

etc/plugins/emq_auth_mongo.conf:

## Mongo Server
auth.mongo.server = 127.0.0.1:27017

## Mongo Pool Size
auth.mongo.pool = 8

## Mongo User
## auth.mongo.user =

## Mongo Password
## auth.mongo.password =

## Mongo Database
auth.mongo.database = mqtt

## auth_query
auth.mongo.auth_query.collection = mqtt_user

auth.mongo.auth_query.password_field = password

auth.mongo.auth_query.password_hash = sha256

auth.mongo.auth_query.selector = username=%u

## super_query
auth.mongo.super_query.collection = mqtt_user

auth.mongo.super_query.super_field = is_superuser

auth.mongo.super_query.selector = username=%u

## acl_query
auth.mongo.acl_query.collection = mqtt_user

auth.mongo.acl_query.selector = username=%u

MongoDB Database

use mqtt
db.createCollection("mqtt_user")
db.createCollection("mqtt_acl")
db.mqtt_user.ensureIndex({"username":1})

MongoDB User Collection

{
    username: "user",
    password: "password hash",
    is_superuser: boolean (true, false),
    created: "datetime"
}

For example:

db.mqtt_user.insert({username: "test", password: "password hash", is_superuser: false})
db.mqtt_user:insert({username: "root", is_superuser: true})

MongoDB ACL Collection

{
    username: "username",
    clientid: "clientid",
    publish: ["topic1", "topic2", ...],
    subscribe: ["subtop1", "subtop2", ...],
    pubsub: ["topic/#", "topic1", ...]
}

For example:

db.mqtt_acl.insert({username: "test", publish: ["t/1", "t/2"], subscribe: ["user/%u", "client/%c"]})
db.mqtt_acl.insert({username: "admin", pubsub: ["#"]})

Load MongoDB Auth/ACL Plugin

./bin/emqttd_ctl plugins load emq_auth_mongo

emq_modules - Modules Plugin

Merged the emq_mod_presence, emq_mod_subscription, emq_mod_rewrite into one emq_modules project.

Configure Modules Plugin

##--------------------------------------------------------------------
## Presence Module
##--------------------------------------------------------------------

## Enable Presence, Values: on | off
module.presence = on

module.presence.qos = 1

##--------------------------------------------------------------------
## Subscription Module
##--------------------------------------------------------------------

## Enable Subscription, Values: on | off
module.subscription = on

## Subscribe the Topics automatically when client connected
module.subscription.1.topic = $client/%c
## Qos of the subscription: 0 | 1 | 2
module.subscription.1.qos = 1

## module.subscription.2.topic = $user/%u
## module.subscription.2.qos = 1

##--------------------------------------------------------------------
## Rewrite Module
##--------------------------------------------------------------------

## Enable Rewrite, Values: on | off
module.rewrite = off

## {rewrite, Topic, Re, Dest}
## module.rewrite.rule.1 = x/# ^x/y/(.+)$ z/y/$1
## module.rewrite.rule.2 = y/+/z/# ^y/(.+)/z/(.+)$ y/z/$2

emq_mod_presence - Presence Module

Presence module will publish presence message to $SYS topic when a client connected or disconnected:

Note

This project has been deprecated in 2.1-beta release.

Configure Presence Module

etc/plugins/emq_mod_presence.conf:

## Enable presence module
## Values: on | off
module.presence = on

module.presence.qos = 0

Load Presence Module

Note

This module will be loaded by default.

./bin/emqttd_ctl plugins load emq_mod_presence

emq_mod_retainer - Retainer Module

Retainer module is responsible for storing MQTT retained messages.

Note

This project has been deprecated in 2.1-beta release.

Configure Retainer Module

etc/plugins/emq_mod_retainer.conf:

## disc: disc_copies, ram: ram_copies
module.retainer.storage_type = ram

## Max number of retained messages
module.retainer.max_message_num = 100000

## Max Payload Size of retained message
module.retainer.max_payload_size = 64KB

## Expired after seconds, never expired if 0
module.retainer.expired_after = 0

Load Retainer Module

Note

This module will be loaded by default.

./bin/emqttd_ctl plugins load emq_mod_retainer

emq_mod_subscription - Subscription Module

Subscription module forces the client to subscribe some topics when connected to the broker:

Note

This project has been deprecated in 2.1-beta release.

Configure Subscription Module

etc/plugins/emq_mod_subscription.conf:

## Subscribe the Topics automatically when client connected
module.subscription.1.topic = $client/%c
## Qos of the subscription: 0 | 1 | 2
module.subscription.1.qos = 1

##module.subscription.2.topic = $user/%u
##module.subscription.2.qos = 1

Load Subscription Module

Note

This module will be loaded by default.

./bin/emqttd_ctl plugins load emq_mod_subscription

emq_mod_rewrite - Topic Rewrite Module

Released in 2.0-rc.2: https://github.com/emqtt/emq_mod_rewrite

Note

This project has been deprecated in 2.1-beta release.

Configure Rewrite Module

etc/plugins/emq_mod_rewrite.config:

[
  {emq_mod_rewrite, [
    {rules, [
      %% {rewrite, Topic, Re, Dest}

      %% Example: x/y/ -> z/y/
      %% {rewrite, "x/#", "^x/y/(.+)$", "z/y/$1"},

      %% {rewrite, "y/+/z/#", "^y/(.+)/z/(.+)$", "y/z/$2"}
    ]}
  ]}
].

Load Rewrite Module

./bin/emqttd_ctl plugins load emq_mod_rewrite

emq_coap: CoAP Protocol Plugin

CoAP Protocol Plugin: https://github.com/emqtt/emqttd_coap

Configure CoAP Plugin

coap.server = 5683

coap.prefix.mqtt = mqtt

coap.handler.mqtt = emq_coap_gateway

Load CoAP Protocol Plugin

./bin/emqttd_ctl plugins load emq_coap

libcoap Client

yum install libcoap

% coap client publish message
coap-client -m post -e "qos=0&retain=0&message=payload&topic=hello" coap://localhost/mqtt

emq_sn: MQTT-SN Protocol

MQTT-SN Protocol/Gateway Plugin.

Configure MQTT-SN Plugin

Note

UDP Port for MQTT-SN: 1884

etc/plugins/emq_sn.conf:

mqtt.sn.port = 1884

Load MQTT-SN Plugin

./bin/emqttd_ctl plugins load emq_sn

emq_stomp - STOMP Protocol

Support STOMP 1.0/1.1/1.2 clients to connect to emqttd broker and communicate with MQTT Clients.

Configure Stomp Plugin

etc/plugins/emq_stomp.conf:

Note

Default Port for STOMP Protocol: 61613

stomp.default_user.login = guest

stomp.default_user.passcode = guest

stomp.allow_anonymous = true

stomp.frame.max_headers = 10

stomp.frame.max_header_length = 1024

stomp.frame.max_body_length = 8192

stomp.listener = 61613

stomp.listener.acceptors = 4

stomp.listener.max_clients = 512

Load Stomp Plugin

./bin/emqttd_ctl plugins load emq_stomp

emq_sockjs - STOMP/SockJS Plugin

emq_sockjs plugin enables web browser to connect to emqttd broker and communicate with MQTT clients.

Warning

The plugin is deprecated in 2.0

Configure SockJS Plugin

Note

Default TCP Port: 61616

[
  {emq_sockjs, [

    {sockjs, []},

    {cowboy_listener, {stomp_sockjs, 61616, 4}},

    %% TODO: unused...
    {stomp, [
      {frame, [
        {max_headers,       10},
        {max_header_length, 1024},
        {max_body_length,   8192}
      ]}
    ]}
  ]}
].

Load SockJS Plugin

./bin/emqttd_ctl plugins load emqttd_sockjs

emq_recon - Recon Plugin

The plugin loads recon library on a running EMQ broker. Recon libray helps debug and optimize an Erlang application.

Load Recon Plugin

./bin/emqttd_ctl plugins load emq_recon

Recon CLI

./bin/emqttd_ctl recon

recon memory                 #recon_alloc:memory/2
recon allocated              #recon_alloc:memory(allocated_types, current|max)
recon bin_leak               #recon:bin_leak(100)
recon node_stats             #recon:node_stats(10, 1000)
recon remote_load Mod        #recon:remote_load(Mod)

emq_reloader - Reloader Plugin

Erlang Module Reloader for Development

Note

Don’t load the plugin in production!

Load Reloader Plugin

./bin/emqttd_ctl plugins load emq_reloader

reload CLI

./bin/emqttd_ctl reload

reload <Module>             # Reload a Module

Plugin Development Guide

Create a Plugin Project

Clone emq_plugin_template source from github.com:

git clone https://github.com/emqtt/emq_plugin_template.git

Create a plugin project with erlang.mk and depends on ‘emqttd’ application, the ‘Makefile’:

PROJECT = emq_plugin_abc
PROJECT_DESCRIPTION = emqttd abc plugin
PROJECT_VERSION = 1.0

BUILD_DEPS = emqttd
dep_emqttd = git https://github.com/emqtt/emqttd master

COVER = true

include erlang.mk

Template Plugin: https://github.com/emqtt/emq_plugin_template

Register Auth/ACL Modules

emq_auth_demo.erl - demo authentication module:

-module(emq_auth_demo).

-behaviour(emqttd_auth_mod).

-include_lib("emqttd/include/emqttd.hrl").

-export([init/1, check/3, description/0]).

init(Opts) -> {ok, Opts}.

check(#mqtt_client{client_id = ClientId, username = Username}, Password, _Opts) ->
    io:format("Auth Demo: clientId=~p, username=~p, password=~p~n",
              [ClientId, Username, Password]),
    ok.

description() -> "Demo Auth Module".

emq_acl_demo.erl - demo ACL module:

-module(emq_acl_demo).

-include_lib("emqttd/include/emqttd.hrl").

%% ACL callbacks
-export([init/1, check_acl/2, reload_acl/1, description/0]).

init(Opts) ->
    {ok, Opts}.

check_acl({Client, PubSub, Topic}, Opts) ->
    io:format("ACL Demo: ~p ~p ~p~n", [Client, PubSub, Topic]),
    allow.

reload_acl(_Opts) ->
    ok.

description() -> "ACL Module Demo".

emq_plugin_template_app.erl - Register the auth/ACL modules:

ok = emqttd_access_control:register_mod(auth, emq_auth_demo, []),
ok = emqttd_access_control:register_mod(acl, emq_acl_demo, []),

Register Callbacks for Hooks

The plugin could register callbacks for hooks. The hooks will be run by the broker when a client connected/disconnected, a topic subscribed/unsubscribed or a message published/delivered:

Name Description
client.connected Run when a client connected to the broker successfully
client.subscribe Run before a client subscribes topics
client.unsubscribe Run when a client unsubscribes topics
session.subscribed Run after a client subscribed a topic
session.unsubscribed Run after a client unsubscribed a topic
message.publish Run when a message is published
message.delivered Run when a message is delivered
message.acked Run when a message(qos1/2) is acked
client.disconnected Run when a client is disconnnected

emq_plugin_template.erl for example:

%% Called when the plugin application start
load(Env) ->
    emqttd:hook('client.connected', fun ?MODULE:on_client_connected/3, [Env]),
    emqttd:hook('client.disconnected', fun ?MODULE:on_client_disconnected/3, [Env]),
    emqttd:hook('client.subscribe', fun ?MODULE:on_client_subscribe/4, [Env]),
    emqttd:hook('session.subscribed', fun ?MODULE:on_session_subscribed/4, [Env]),
    emqttd:hook('client.unsubscribe', fun ?MODULE:on_client_unsubscribe/4, [Env]),
    emqttd:hook('session.unsubscribed', fun ?MODULE:on_session_unsubscribed/4, [Env]),
    emqttd:hook('message.publish', fun ?MODULE:on_message_publish/2, [Env]),
    emqttd:hook('message.delivered', fun ?MODULE:on_message_delivered/4, [Env]),
    emqttd:hook('message.acked', fun ?MODULE:on_message_acked/4, [Env]).

Register CLI Modules

emq_cli_demo.erl:

-module(emqttd_cli_demo).

-include_lib("emqttd/include/emqttd_cli.hrl").

-export([cmd/1]).

cmd(["arg1", "arg2"]) ->
    ?PRINT_MSG("ok");

cmd(_) ->
    ?USAGE([{"cmd arg1 arg2", "cmd demo"}]).

emq_plugin_template_app.erl - register the CLI module to EMQ broker:

emqttd_ctl:register_cmd(cmd, {emq_cli_demo, cmd}, []).

There will be a new CLI after the plugin loaded:

./bin/emqttd_ctl cmd arg1 arg2

Create Configuration File

Create etc/${plugin_name}.conf|config file for the plugin. The EMQ broker supports two type of config syntax:

  1. ${plugin_name}.config with erlang syntax:
[
  {plugin_name, [
    {key, value}
  ]}
].
  1. ${plugin_name}.conf with a general k = v syntax:
plugin_name.key = value

Build and Release the Plugin

  1. clone emq-relx project:
git clone https://github.com/emqtt/emq-relx.git
  1. Add DEPS in Makefile:
DEPS += plugin_name
dep_plugin_name = git url_of_plugin
  1. Add the plugin in relx.config:
{plugin_name, load},