Password onboarding
Provision USERNAME + PASSWORD credentials for your PLC via the Voke REST API or admin SPA.
The PASSWORD listener is the simplest path to a working MQTT connection. Credentials are managed entirely by Voke — no certificates or JWT keys are required on the PLC.
Listener port: 8885 (same in production and local dev)
When to use
- Local dev and CI smoke tests.
- Industrial controllers where firmware cannot implement TLS client certificates or RS256 JWT signing.
- Low-trust networks where the management overhead of mTLS is not justified.
How it works
- You call
POST /api/v1/plants/{plantId}/provisionwith the plant inPENDINGstatus andauthMethod = PASSWORD. - Voke's
MqttPasswordServicegenerates a short username (plc-<first 8 hex chars of plantId>) and a random 32-character alphanumeric password. - The password is hashed with PBKDF2-SHA512 and written into
/mosquitto/config/passwd-devices. - An ACL block scoped to
cpi/{plantId}/#is appended to/mosquitto/config/acl-devices. - Mosquitto receives SIGHUP via
docker kill --signal=SIGHUP cpi-mosquittoand reloads both files without dropping existing connections. - The provisioning package (including the plaintext password — shown only once) is returned in the response body.
Provisioning credentials
Via REST API
The plant must exist and be in PENDING lifecycle status. The authMethod is set when
creating the plant record.
POST /api/v1/plants/{plantId}/provision
Authorization: Cookie / Bearer token with ORG_ADMIN role
X-Org-Id: {organizationId}No request body is needed — the provisioning type is determined by the plant's stored
authMethod field.
Response (PASSWORD variant):
{
"plantId": "a1b2c3d4-0000-0000-0000-000000000001",
"authMethod": "PASSWORD",
"connection": {
"broker": "mqtts://mqtt.voke.lovinka.com",
"port": 8885,
"useTls": true,
"clientId": "plc-a1b2c3d4"
},
"auth": {
"username": "plc-a1b2c3d4",
"password": "Xy7Kq..."
},
"tls": {
"caCertPem": "-----BEGIN CERTIFICATE-----\n..."
},
"topics": {
"telemetry": "cpi/a1b2c3d4-0000-0000-0000-000000000001/telemetry",
"command": "cpi/a1b2c3d4-0000-0000-0000-000000000001/command",
"ack": "cpi/a1b2c3d4-0000-0000-0000-000000000001/ack",
"status": "cpi/a1b2c3d4-0000-0000-0000-000000000001/status",
"alarm": "cpi/a1b2c3d4-0000-0000-0000-000000000001/alarm"
},
"recommended": {
"qos": 1,
"keepAlive": 60
}
}The plaintext password is returned once in this response. Store it securely; the API only stores the bcrypt hash and cannot recover the plaintext.
Also available as a downloadable JSON file:
GET /api/v1/plants/{plantId}/provision/downloadReturns the same body with a Content-Disposition: attachment header.
Via Admin SPA
- Open Voke admin → Plants → select your plant.
- Go to the Security tab.
- Click Provision (requires the plant to be in PENDING status with
authMethod = PASSWORD). - Copy the displayed username and password — the dialog closes after one view.
Username format
Mosquitto passwords are limited to 65,535 bytes but Voke shortens the username to avoid embedded null issues with some MQTT clients:
plc-<first 8 hex chars of plantId (UUID, dashes removed)>For plant a1b2c3d4-0000-0000-0000-000000000001 the username is plc-a1b2c3d4.
Use this same string as both the MQTT client_id and username.
Connecting
import paho.mqtt.client as mqtt
HOST = "mqtt.voke.lovinka.com" # or "localhost" for local dev
PORT = 8885 # same port in prod and dev
PLANT_ID = "a1b2c3d4-0000-0000-0000-000000000001"
USERNAME = "plc-a1b2c3d4" # from provisioning package
PASSWORD = "Xy7Kq..." # from provisioning package
CA_CERT = None # set to "/path/to/ca.crt" for local dev without system trust
client = mqtt.Client(client_id=USERNAME, protocol=mqtt.MQTTv5)
client.username_pw_set(USERNAME, PASSWORD)
if CA_CERT:
client.tls_set(ca_certs=CA_CERT)
else:
client.tls_set() # use system trust store for production
client.connect(HOST, PORT, keepalive=60)
client.loop_forever()The PASSWORD listener uses server-side TLS (not mutual TLS). The PLC must trust the
Voke CA root or a system-trusted CA that signed the broker certificate. For local dev
pass the CA certificate from docker/mosquitto/certs/ca/ca.crt explicitly.
ACL scope
Every PASSWORD plant gets its own explicit ACL block written by MqttPasswordService:
user plc-a1b2c3d4
topic write cpi/a1b2c3d4-0000-0000-0000-000000000001/telemetry
topic write cpi/a1b2c3d4-0000-0000-0000-000000000001/status
topic write cpi/a1b2c3d4-0000-0000-0000-000000000001/ack
topic read cpi/a1b2c3d4-0000-0000-0000-000000000001/commandsNote that the ACL topics use the full plant UUID (not the short username). The short username is only the MQTT identity; Voke maps it to the plant UUID internally.
No cross-plant access is possible. Attempting to publish to another plant's topic results in an immediate disconnect.
Rotating credentials
Call the provision endpoint again. Voke will:
- Generate a new password.
- Atomically replace the existing entry in
/mosquitto/config/passwd-devices. - Send SIGHUP to Mosquitto.
The old password stops working as soon as Mosquitto reloads (within seconds of the SIGHUP). The ACL block is not changed — only the password hash is updated.
# Re-provision to rotate the password
POST /api/v1/plants/{plantId}/provisionThe plant must be in PENDING status to provision. If the plant is already ACTIVE,
revoke it first (POST /api/v1/plants/{plantId}/revoke) and then re-provision. Revocation
removes the old ACL block and password entry before the new credentials are written.
Troubleshooting
| Symptom | Likely cause |
|---|---|
CONNACK rc=5 — bad credentials | Wrong username or password; re-provision and copy the new values |
CONNACK rc=5 — connection refused | Plant not provisioned yet, or provisioning failed |
SSL certificate verify failed | Missing or wrong CA cert; pass ca_certs in tls_set() |
| Password reloaded but PLC still rejected | Mosquitto SIGHUP may have failed; check docker logs cpi-mosquitto |
| Can publish to telemetry but not read commands | ACL entry missing; re-provision to regenerate the ACL block |
Next steps
- Auth methods — compare all three listeners.
- Topics — full topic reference.
- HMAC signing — sign telemetry payloads.
- mTLS onboarding — upgrade to certificate-based auth for production.