Edge clients
Devices or small services need a broker-mediated path.
Use MQTT when a broker is a better fit than a direct WSS or HTTPS connection.
MQTT is still a secure public protocol. Clients connect to the broker with TLS, authenticate with per-client credentials, and publish only to the topics allowed for that client.
Edge clients
Devices or small services need a broker-mediated path.
Network boundaries
The client can reach a broker more reliably than a direct hub route.
Fan-in traffic
Many clients need isolated topic access through one public broker endpoint.
Not the default
Use WSS first unless MQTT solves a real deployment problem.
mqtts://.topic_prefix the identity returns.The client identity carries a top-level crypto_key and, when MQTT is enabled, an mqtt block:
{ "crypto_key": "shared-encryption-key", "mqtt": { "endpoint": "mqtts://mqtt.thalovant.com:8883", "username": "client-access-key", "password": "client-broker-password", "topic_prefix": "<namespace>/<hub-id>/<client-id>", "tls": true }}| Field | Meaning |
|---|---|
crypto_key |
Top-level identity key for the encrypted message frames. It sits on the identity, not inside the mqtt block. |
endpoint |
Broker URL. Public clients should use mqtts://. |
username |
Broker username for this client. It is the client access key. |
password |
Broker password for this client. |
topic_prefix |
Full topic base for this client. Append a channel suffix instead of hardcoding a path. |
tls |
Whether the client should connect over TLS. |
The identity’s topic_prefix is the full base for every topic this client uses. It looks like <namespace>/<hub-id>/<client-id>, where <client-id> is the client access key. Treat it as opaque: read topic_prefix from the identity and append the channel suffix instead of assembling the path yourself.
| Channel | Topic | Purpose |
|---|---|---|
| Inbound | <topic_prefix>/in |
The client publishes requests here. |
| Outbound | <topic_prefix>/out |
The client subscribes here for replies. |
| Status | <topic_prefix>/status |
Retained presence and status for this client. |
A client’s MQTT credentials come from its identity. To read them for a custom client:
mqtts://mqtt.thalovant.com:8883), username (the access key), password, crypto key, and topic prefix. You can also download _identity.json.If two-step sign-in blocks the panel, set it up first on Profile and Security.
You can connect any MQTT client to the broker with the credentials above, without Thalovant Voice or a managed runtime. Reaching the broker is the easy part.
The payload is not plaintext. Every message on <topic_prefix>/in and <topic_prefix>/out is the same encrypted message frame the WSS transport uses, negotiated with an AES-GCM handshake. The identity crypto_key is the shared key for those frames, so a raw MQTT publish of JSON connects but is never understood.
A custom client therefore has to speak the encrypted message protocol, not just authenticate to the broker. In practice, use a Thalovant SDK or a library that already implements the frame and handshake, then select the MQTT transport. The broker moves bytes; the encrypted protocol gives them meaning.
from thalovant import ThalovantClient, ThalovantControlPlane
api = ThalovantControlPlane()
result = api.create_client_identity( "hub-id", name="python-mqtt-client", preferred_protocols=("mqtt", "wss"),)
identity = result.identityif identity.mqtt is None: raise RuntimeError("MQTT is not available for this identity.")
with ThalovantClient(identity, protocol="mqtt") as client: print(client.ask("Reply over MQTT.").text)import { ThalovantClient, ThalovantControlPlane } from "@thalovant/sdk";
const api = new ThalovantControlPlane();
const result = await api.createClientIdentity("hub-id", { name: "node-mqtt-client", preferredProtocols: ["mqtt", "wss"],});
if (!result.identity.mqtt) { throw new Error("MQTT is not available for this identity.");}
const client = new ThalovantClient(result.identity, { protocol: "mqtt" });try { const reply = await client.ask("Reply over MQTT."); console.log(reply.text);} finally { await client.close();}result, err := control.CreateClientIdentityForHubID(ctx, "hub-id", thalovant.BootstrapIdentityOptions{ Name: "go-mqtt-client", PreferredProtocols: []thalovant.HubProtocol{ thalovant.ProtocolMQTT, thalovant.ProtocolWSS, },})if err != nil { log.Fatal(err)}if result.Identity.MQTT == nil { log.Fatal("MQTT is not available for this identity.")}
client, err := thalovant.NewClientWithOptions(result.Identity, thalovant.ClientOptions{ Protocol: thalovant.ProtocolMQTT,})if err != nil { log.Fatal(err)}defer client.Close(ctx)let result = control .create_client_identity_for_hub_id( "hub-id", BootstrapIdentityOptions { name: "rust-mqtt-client".into(), preferred_protocols: vec![HubProtocol::Mqtt, HubProtocol::Wss], ..Default::default() }, ) .await?;
if result.identity.mqtt.is_none() { panic!("MQTT is not available for this identity.");}
let client = Client::with_protocol(result.identity.clone(), HubProtocol::Mqtt)?;| Symptom | Check |
|---|---|
| The SDK says MQTT is unsupported | Enable MQTT on the hub and create a fresh identity. |
The identity has no mqtt block |
Enable MQTT on the hub, then open Connection details or download a fresh identity to pick up broker credentials. |
| Broker auth fails | Confirm the username is the access key and the client uses the latest broker password. Rotate the identity if needed. |
| TLS fails | Use the mqtts:// endpoint and confirm the broker certificate is valid. |
| A publish succeeds but nothing replies | The payload must be an encrypted message frame, not plain JSON. Use a library that implements the protocol. |
| Messages are ignored | Publish to <topic_prefix>/in and subscribe to <topic_prefix>/out. Do not invent topic paths. |