Overview
Lua devices communicate with agents over MQTT (recommended) or Socket.IO. This page documents the complete wire protocol so you can build a client in any language — Go, Rust, C#, Swift, or anything with an MQTT library.If you’re using Node.js, Python, or MicroPython, use the official SDKs instead.
This page is for building clients in languages without an official SDK.
Authentication
Devices authenticate using three credentials passed as MQTT connection parameters:
MQTT username format:
{agentId}:{deviceName}
Connection settings:
- Broker:
wss://mqtt.heylua.ai/mqtt(TLS required in production) - Client ID:
lua-{agentId}-{deviceName} - Clean session:
false(enables persistent session for QoS 1 message queueing) - Keep-alive:
60seconds
MQTT Topics
All topics use the prefixlua/devices/{agentId}/{deviceName}/.
Device Subscribes To (Server -> Device)
Device Publishes To (Device -> Server)
Socket.IO Events
If you prefer WebSocket transport, connect to{serverUrl}/devices with Socket.IO.
Socket.IO auth is passed in the
auth option at connection time:
Message Schemas
CommandMessage
Received on thecommand topic when the agent invokes a device command.
ResponseMessage
Published to theresponse topic after executing a command.
TriggerMessage
Published to thetrigger topic to fire an event to the agent.
TriggerAckMessage
Received on thetrigger_ack topic after the server processes a trigger.
StatusMessage (retained)
Published to thestatus topic with retain: true. This message must NOT contain secrets (the API key) because retained messages are stored by the broker and delivered to any future subscriber.
AuthStatusMessage (non-retained)
Published to thestatus topic with retain: false immediately after the retained status. Contains the API key and command manifest for server-side authentication.
Heartbeat
Published to theheartbeat topic every 30 seconds with an empty payload. QoS 0 (fire-and-forget).
Last Will and Testament (LWT)
Set the MQTT LWT to publish an offline status if the device disconnects unexpectedly:- Topic:
lua/devices/{agentId}/{deviceName}/status - Payload:
{"status": "offline", "timestamp": "..."} - QoS: 1
- Retain: true
Self-Describing Commands
Commands are sent at connect time in the AuthStatusMessage. The server registers them as agent tools automatically — no compile/push cycle needed.Command Lifecycle
Idempotency
Commands include acommandId that must be used for idempotency. Your client should:
- Maintain an LRU cache of recently seen
commandIdvalues (recommended: 1000 entries, 5-minute TTL) - On receiving a command, check if the
commandIdhas been seen before - If seen, re-publish the cached response without re-executing the handler
- If new, execute the handler, cache the response, then publish it
Rate Limits and Constraints
Example Implementations
- Go
- Rust
- C#
- Swift
These examples show the minimal connect-and-handle pattern. Production clients should add:
idempotency dedup, heartbeat loop, LWT, graceful shutdown, error handling, and auto-reconnect
with exponential backoff.

