> ## Documentation Index
> Fetch the complete documentation index at: https://docs.heylua.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Device reliability and limits

> Reconnection, heartbeats, offline semantics, command and trigger limits, payload caps, and credential checks for device connections

The numbers that govern a [device](/concepts/devices) connection, in one place. Client rows come from the shipped clients (`@lua-ai-global/device-client` 1.1.0, `lua-device-client` 1.3.0); the rest come from the platform.

*Verified against lua-cli 3.33.0.*

## Reconnection

| Client          | Retry delay                                                                          | Stops retrying                                        |
| --------------- | ------------------------------------------------------------------------------------ | ----------------------------------------------------- |
| Node, Socket.IO | 1 s doubling to 30 s with up to 50 % jitter; 1 s after a server-initiated disconnect | After `AUTH_FAILED` or `MISSING_AUTH`; `disconnect()` |
| Node, MQTT      | Every 5 s                                                                            | `disconnect()`                                        |
| Python          | paho-mqtt: 1 s doubling to 120 s                                                     | `disconnect()`                                        |
| MicroPython     | 2 s doubling to 30 s; board reset after 10 failures                                  | Never                                                 |

A `RATE_LIMITED` handshake response tells the Node Socket.IO client to wait `retryAfterMs`, clamped to 1 s–15 min, or 30 s when absent. A connection the platform closes itself, for example with `AGENT_FORBIDDEN` or `DEVICE_DISABLED`, is retried every second without limit. Every reconnection repeats the handshake, so the command list is re-sent and the tools return.

## Presence

| Item                        | Value                                                                                                                                                            |
| --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Heartbeat                   | Every 30 s from every client; QoS 0 over MQTT                                                                                                                    |
| Offline on connection close | Immediate: Socket.IO disconnect, MQTT will, or a published `offline` status                                                                                      |
| Offline without a close     | A sweep every 5 min marks devices whose last heartbeat is older than 5 min                                                                                       |
| What offline changes        | Removed from the online set, stored command list cleared, tools gone on the next turn; a `defineDevice` device keeps its tools and calls return `DEVICE_OFFLINE` |
| `device_disconnected`       | Fires on close only, not on the sweep                                                                                                                            |
| Stored command list         | Kept 24 h from connect; heartbeats don't extend it (self-describing devices only)                                                                                |
| Connected over 24 h         | Command tools gone until reconnect, `is_online` stays; no client reconnects on a timer, so schedule `disconnect()` then `connect()` yourself                     |
| `lua devices status`        | `online`, otherwise the stored `offline`, `registered`, or `disabled`                                                                                            |

## Commands

| Item                  | Value                                                                                                                                                           |
| --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Timeout               | `timeoutMs`, 30,000 ms default, plus 2 s grace                                                                                                                  |
| In flight per agent   | 100; more fail with `TOO_MANY_REQUESTS`                                                                                                                         |
| Retries               | None unless `retry` is set; then after `TIMEOUT` or `DEVICE_ERROR` only, waiting `backoffMs × attempt`                                                          |
| Not retried           | `DEVICE_OFFLINE`, `TOO_MANY_REQUESTS`, a handler that throws                                                                                                    |
| Delivery              | Socket.IO event with acknowledgment; MQTT QoS 1 with a persistent session                                                                                       |
| Duplicate suppression | Clients cache command IDs for 5 min; Node and Python keep 1,000 and re-send the cached response, MicroPython prunes above 100 entries and ignores the duplicate |
| Group fan-out         | `device__<group>__<command>__all`, `defineDevice` devices only, one attempt per device                                                                          |

## Triggers

| Item                | Value                                                                                                                          |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| Rate                | 10 per second per agent; excess answered with `RATE_LIMITED` on `trigger_error`, which only the Node Socket.IO client surfaces |
| Acknowledgment wait | 10 s in the Node and Python clients; none in MicroPython                                                                       |
| Name matching       | Exact string; device `defineDevice` triggers first, then standalone triggers                                                   |
| Handler time limit  | 10 min, the platform's default cap for event handlers                                                                          |
| Handler context     | `{ device: { name }, trigger: { name, triggerId } }`                                                                           |
| Delivery            | At least once from a durable queue; a handler that throws or times out is run again, 3 attempts 60 s apart                     |
| `trigger_result`    | Never sent by the platform                                                                                                     |
| System triggers     | `device_connected`, `device_disconnected`                                                                                      |

## Payloads and declarations

| Item                                              | Value                                                                                                      |
| ------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
| Command payload, response `data`, trigger payload | 1 MB (1,048,576 bytes)                                                                                     |
| Response `error` string                           | 4 KB                                                                                                       |
| Command name                                      | `^[a-z][a-z0-9_]{0,63}$`; others dropped                                                                   |
| Commands per device                               | 128; the rest dropped                                                                                      |
| `inputSchema` per command                         | 4 KB serialized; larger drops the command                                                                  |
| Command description                               | HTML stripped, cut at 500 characters                                                                       |
| Device tools per agent                            | 128, counting each device's `is_online` tool                                                               |
| Device name (`defineDevice`)                      | `^[a-z][a-z0-9-]*$`, compile warning otherwise; 1–200 characters, no whitespace, `/`, `+`, `#` on the wire |
| Device trigger name                               | `^[a-z][a-z0-9_-]*$`, compile warning otherwise                                                            |
| CDN upload                                        | 100 MB per file, one file per request                                                                      |

## Credentials and authentication

| Item                                       | Value                                                                                                                                                                                   |
| ------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Device credential re-check while connected | Every 60 s plus up to 30 s jitter (Socket.IO); at the next publish or subscribe after 60 s (MQTT)                                                                                       |
| Legacy API key re-check                    | Every 15 min plus up to 5 min jitter                                                                                                                                                    |
| Authorization outage                       | Connections kept open up to 15 min past the due re-check, then closed with `CONNECT_ERROR`                                                                                              |
| Revocation, rotation, suspension           | Socket.IO connections closed at once with `AUTH_FAILED`; MQTT denied at the next publish or subscribe; the re-check is the fallback                                                     |
| Failed handshakes                          | 5 rejected credentials per 60 s per device and credential; the next attempt gets `RATE_LIMITED` with `retryAfterMs` of 10 min, and every handshake from that pair is refused for 10 min |
| MQTT connection record                     | 24 h, extended on every authorized publish or subscribe                                                                                                                                 |
| Operations                                 | `commands`, `triggers`, `assets.upload`; enforced per message                                                                                                                           |

## See also

* [How devices work](/devices/how-it-works) — the same facts as a narrative
* [MQTT protocol](/devices/mqtt-protocol) — error codes and per-topic authorization
* [Create a device credential](/devices/credentials)
* [Node device client](/devices/node-client) and [Python device client](/devices/python-client)
