Skip to content

MQTT transport ​

The MQTT transport makes Janus a client of an MQTT broker. Janus subscribes to one topic for requests and publishes replies and events on another. Use it when the system that controls Janus already talks through a broker (IoT platforms, event-driven back ends) and you do not want to open a port on Janus.

Janus does not know who sent a request: every reply and every event goes to the one publish topic (or, with MQTT 5, to the Response Topic the request names). Your application reads that topic and matches replies by transaction.

Enable and configure ​

The transport reads janus.transport.mqtt.jcfg. It is off unless general.enabled (Janus API) or admin.admin_enabled (Admin API) is true.

ini
general: {
	enabled = true
	json = "indented"                    # indented, plain or compact (the default)
	url = "tcp://localhost:1883"         # ssl:// for TLS
	client_id = "janus-docs"             # must be unique on the broker
	#username = "guest"
	#password = "guest"
	subscribe_topic = "to-janus"         # Janus reads requests here
	publish_topic = "from-janus"         # Janus writes replies and events here
}
admin: {
	admin_enabled = true
	subscribe_topic = "to-janus-admin"
	publish_topic = "from-janus-admin"
}
status: {
	enabled = true
	connect_message = "{\"online\": true}"
	disconnect_message = "{\"online\": false}"
	topic = "status"
}

The full table (QoS, keep_alive_interval, cleansession, TLS files, MQTT 5 options) is in the transport files reference. A broker that is down does not stop Janus: with the URL pointing at a closed port, the HTTP listener answered normally while the transport kept retrying (the delay starts at 1 second and doubles to 60, per parity/status.json).

Try it ​

The broker is eclipse-mosquitto:2 in a Docker container. The client side is mosquitto_pub (to send a request) and mosquitto_sub -v -t '#' (to see everything the broker carries). The transcript is what the broker saw. The requests are sent one at a time, so replies are easy to pair up.

jsonc
// Broker: mosquitto 2 (docker). The requests are published with mosquitto_pub. The replies are what mosquitto_sub, subscribed to #, saw on the broker.

// ⇠ status topic: published when Janus connects
{"online": true}

// → PUBLISH to-janus
{
  "janus": "ping",
  "transaction": "m1"
}

// ← from-janus
{
  "janus": "pong",
  "transaction": "m1"
}

// → PUBLISH to-janus
{
  "janus": "create",
  "transaction": "m2"
}

// ← from-janus
{
  "janus": "success",
  "transaction": "m2",
  "data": {
    "id": 3281821553895050
  }
}

// → PUBLISH to-janus
{
  "janus": "attach",
  "session_id": 3281821553895050,
  "plugin": "janus.plugin.echotest",
  "transaction": "m3"
}

// ← from-janus
{
  "janus": "success",
  "session_id": 3281821553895050,
  "transaction": "m3",
  "data": {
    "id": 438949328643112
  }
}

// A plugin message gets an ack, and the plugin's event follows as its own message, on the same publish topic.

// → PUBLISH to-janus
{
  "janus": "message",
  "session_id": 3281821553895050,
  "handle_id": 438949328643112,
  "transaction": "m4",
  "body": {
    "audio": true
  }
}

// ← from-janus
{
  "janus": "ack",
  "session_id": 3281821553895050,
  "transaction": "m4",
  "hint": "I'm taking my time!"
}

// ⇠ from-janus
{
  "janus": "event",
  "session_id": 3281821553895050,
  "transaction": "m4",
  "sender": 438949328643112,
  "plugindata": {
    "plugin": "janus.plugin.echotest",
    "data": {
      "echotest": "event",
      "result": "ok"
    }
  }
}

// The Admin API uses its own pair of topics.

// → PUBLISH to-janus-admin
{
  "janus": "list_sessions",
  "transaction": "m5",
  "admin_secret": "janusoverlord"
}

// ← from-janus-admin
{
  "janus": "success",
  "transaction": "m5",
  "sessions": [
    3281821553895050
  ]
}

// A message that is not JSON:

// → PUBLISH to-janus
not json

// ← from-janus
{
  "janus": "error",
  "error": {
    "code": 454,
    "reason": "Invalid Janus API request - <buffer>(1,3): '[' or '{' expected near 'not'"
  }
}

// Janus stops (SIGTERM): it publishes the disconnect message on the status topic.

// ⇠ status topic
{"online": false}

The status topic shows the connect message when Janus connects and the disconnect message when it stops.

MQTT 5: Response Topic, correlation data and user properties ​

With mqtt_version = "5" a request can say where the reply should go. Janus copies the request's Correlation Data to the reply and, for each name in proxy_transaction_user_properties, the request's user property of that name. add_transaction_user_properties adds fixed user properties to every reply.

jsonc
// Config: mqtt_version = "5", proxy_transaction_user_properties = ["trace"], add_transaction_user_properties = (["via", "janus"]). The client asks for the answer on its own topic (Response Topic) and sends a user property.

// → PUBLISH to-janus
// MQTT 5 properties: response-topic = reply/me, user-property trace = abc, correlation-data = corr-42
{
  "janus": "ping",
  "transaction": "v5"
}

// ← reply/me
// user properties: trace:abc via:janus
{"janus":"pong","transaction":"v5"}

mosquitto_sub cannot print Correlation Data, so the transcript shows the user properties only. Janus copies the correlation data in the C source; I did not see it come back here.

Reference ​

Topics ​

TopicDirectionCarries
general.subscribe_topicbroker to JanusJanus API requests, one JSON per message
general.publish_topicJanus to brokerreplies and events of the Janus API
admin.subscribe_topicbroker to JanusAdmin API requests
admin.publish_topicJanus to brokerAdmin API replies
status.topicJanus to brokerthe connect and disconnect messages, when status.enabled

subscribe_qos and publish_qos (and status.qos, status.retain) set the quality of service. In the C server the disconnect message also serves as the broker's last will. I did not test an unclean exit here.

Versions ​

mqtt_version is "3.1", "3.1.1" (the default) or "5". The examples here used 3.1.1 and 5 against mosquitto 2.1. Version 3.1 runs on a codec written for the port, and its live runs against mosquitto are in the crate's ignored tests, not in this page.

What is the same as other transports ​

Requests need session_id and handle_id in the JSON, there is no path. The session is tied to this transport: events for it go to publish_topic. A message that is not valid JSON gets error 454.

Behaviour you may not expect ​

  • verified The reply to a plugin message is an ack; the plugin's own answer is a second message on the same topic.
  • verified The disconnect message is published on a normal stop (SIGTERM).
  • verified The error for a message that is not JSON names <buffer> as the source (the JSON library reads the message from a buffer): Invalid Janus API request - <buffer>(1,3): ....
  • Janus answers on publish_topic for every client, so two applications that share the broker share the replies. Give each Janus instance its own topics and each application its own transaction values. With MQTT 5, use Response Topic.
  • vacuum_interval (MQTT 5 only) is how often Janus forgets the transaction state of requests that never got a final reply.

Known gaps ​

  • gap ws:// and wss:// broker URLs are refused. tcp:// and ssl:// work. TLS (ssl_enabled, cacertfile, certfile, keyfile, verify_peer) is ported and compared with C in the harness; I did not run it.
  • gap The disconnect event for a broker that closes the socket straight after its own DISCONNECT was not seen live (parity/status.json).
  • gap The log lines that the Paho library writes (callback names and return codes) are not reproduced, because the Rust client (rumqttc) does not expose them.
  • gap A bad topic filter on MQTT 3.1.1 and 5 is blocked by rumqttc before it reaches the broker. The broker would close the connection instead.
  • gap query_transport answers error 490.
  • gap session created events from this transport carry no transport object. See event types.

Test it in Janus Bench ​

Janus Bench talks HTTP and WebSockets only. There is no check for this transport.

GPL-3.0-or-later, like the Janus gateway it ports.