Skip to main content

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: 60 seconds

MQTT Topics

All topics use the prefix lua/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 the command topic when the agent invokes a device command.

ResponseMessage

Published to the response topic after executing a command.

TriggerMessage

Published to the trigger topic to fire an event to the agent.

TriggerAckMessage

Received on the trigger_ack topic after the server processes a trigger.

StatusMessage (retained)

Published to the status 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 the status topic with retain: false immediately after the retained status. Contains the API key and command manifest for server-side authentication.

Heartbeat

Published to the heartbeat 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 a commandId that must be used for idempotency. Your client should:
  1. Maintain an LRU cache of recently seen commandId values (recommended: 1000 entries, 5-minute TTL)
  2. On receiving a command, check if the commandId has been seen before
  3. If seen, re-publish the cached response without re-executing the handler
  4. If new, execute the handler, cache the response, then publish it
This ensures that retried messages (common with QoS 1) do not cause duplicate side effects.
Without idempotency handling, MQTT QoS 1 redelivery can cause commands to execute multiple times. Always implement the dedup cache.

Rate Limits and Constraints

Example Implementations

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.