This is a development version of the documentation. Content may change without notice.
Voke Documentation
PLC / Devices

Topics

Full MQTT topic reference — topic tree, QoS, ACL scoping, and connection behaviour for PLC devices.

Every plant communicates with Voke over four MQTT topics. All topics sit under the cpi/{plantId}/ prefix, where {plantId} is the plant's UUID as provisioned in Voke admin.


Topic tree

TopicDirectionQoSPublished by
cpi/{plantId}/telemetryplant → Voke1plant
cpi/{plantId}/commandVoke → plant1Voke
cpi/{plantId}/ackplant → Voke1plant
cpi/{plantId}/alarmplant → Voke1plant

Additional topics used by the protocol but not covered on this page:

TopicDirectionNotes
cpi/{plantId}/statusplant → VokePlant connectivity / heartbeat
cpi/{plantId}/infoplant → VokeDevice capabilities, published retained
cpi/{plantId}/configVoke → plantConfiguration push from Voke

QoS 1 guarantees at-least-once delivery. PLCs must handle duplicate messages gracefully — Voke uses the nonce (n) field in each envelope for deduplication.


Message shapes

Telemetry (cpi/{plantId}/telemetry) carries either the PLC telemetry payload (flat power/SOC/grid fields) or a sub-device snapshot envelope (the devices array). Voke distinguishes between the two by checking for a top-level devices key. See Snapshot envelope for the full sub-device wire shape.

Command (cpi/{plantId}/command) carries a PlcCommand envelope from Voke. Fields: cmdId, ts, type, p (parameters), sig. The plant must subscribe to this topic at connect time — Voke does not publish commands as retained messages, so a plant that is offline when a command is sent will miss it.

Ack (cpi/{plantId}/ack) is the plant's reply to a command. Fields: cmdId, st (status), ts, n, err?, msg?, sig?. See Commands (receipt) for the full ack contract.

Alarm (cpi/{plantId}/alarm) carries a PlcAlarm envelope. Fields: ts, n, ev (RAISE | RESOLVE), alarmId, code, sev, msg?, detail?, sig?. See Alarms upstream.

Commands are not retained. A plant that reconnects after a command was dispatched will not automatically receive it. If your use case requires command delivery to transiently offline devices, poll GET /api/v1/plants/{plantId}/commands over REST instead.


ACL scoping per listener

Each plant credential is tightly scoped to its own plant topics. No plant can read or write another plant's data regardless of auth method.

mTLS and JWT listeners (ports 8883 / 8884 local, 8886 / 8887 prod)

ACL file: docker/mosquitto/config/acl

# Pattern rules — %u is replaced with the plant UUID at auth time
pattern write cpi/%u/telemetry
pattern write cpi/%u/status
pattern write cpi/%u/ack
pattern read  cpi/%u/commands

The ACL for mTLS/JWT uses commands (plural) for backward compatibility with older firmware. New firmware should use the singular command topic — both are accepted by the broker for now, but new Voke API features (sub-device commands, template-based actions) are published on cpi/{plantId}/command. If your plant connects via mTLS or JWT, subscribe to both command and commands during the migration window.

PASSWORD listener (port 8885)

ACL file: docker/mosquitto/config/acl-devices (dynamically managed)

Per-plant entries are written programmatically by MqttPasswordService during provisioning. Each entry grants:

  • write cpi/{plantId}/telemetry
  • write cpi/{plantId}/status
  • write cpi/{plantId}/ack
  • write cpi/{plantId}/alarm
  • read cpi/{plantId}/command

No wildcards. No cross-plant access.

API internal listener (port 1883)

The Voke API connects here with the cpi_api credential, which has readwrite cpi/# — full access to all plant topics. This is the listener used for publishing commands and subscribing to all plant telemetry streams.


Connection and keepalive

  • TLS: All listeners enforce TLS. PLCs must trust the Voke CA root (ca.crt).
  • Keepalive: PLCs should use a keepalive of 60 seconds (shown in all example clients). The broker enforces 1.5× keepalive as the session timeout; a 60-second keepalive gives a 90-second disconnect detection window.
  • Clean session: Use clean = true. Voke does not rely on persistent sessions.
  • Reconnect: Implement exponential backoff starting at ~5 seconds. The broker is configured for up to 500 simultaneous connections.
  • LWT (Last Will and Testament): Voke does not configure a server-side will on the plant connection. Plants may optionally publish their own LWT to cpi/{plantId}/status to signal a clean or ungraceful disconnect, but Voke detects disconnects via broker-side CONNACK monitoring rather than relying on a will.

On this page