Skip to content

Configuration

Configuration

The complete MQTT platform in one small binary: broker, SQL rules, data connectors and multi-tenancy in a single process.

Validate, reload and explain settings. Use it to change files safely. You get diffs, versions and source layers.

flowchart LR
Op["operator"] -- "POST /api/v1/config/reload" --> API["management API"]
API -- "apply indra.toml" --> C["broker-config"]

Before you start

  • Start the broker from First start.
  • Set INDRA_API_KEY to an operator key. The examples read it.

Note: Calls except /healthz need an operator API key. Pass it as Authorization: Bearer $INDRA_API_KEY.

Configuration validation, reload, history and setting lookup

POST /api/v1/config/diff

Show the diff a reload would apply

Operation id: diffConfig.

Parameters:

No parameters.

Request body:

Media type application/json:

Required: snapshot.

FieldTypeDescription
snapshotobject

Responses:

StatusDescription
200Setting diff with redacted secrets
400Candidate names each offending setting

Response 200 body:

Media type application/json:

{
"type": "object",
"properties": {
"diff": {
"type": "array",
"items": {
"type": "object"
}
}
}
}

Notes:

  • Send snapshot. An empty required string fails with 400.

  • Related calls sit on the same tag page.

Not verified: see ../_verification/failures.md.

Example:

# Illustrative request. Values come from the schema above.
# The example flow below runs verified calls on a live broker.
curl -s -X POST 'http://127.0.0.1:18083/api/v1/config/diff' \
-H "Authorization: Bearer $INDRA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"snapshot": {}
}'

GET /api/v1/config/explain

Explain one setting value and its source layer

Operation id: explainConfig.

Parameters:

NameInRequiredTypeDescription
keyquerytruestringSetting key such as session.max_qos0_backlog

Request body:

No request body.

Responses:

StatusDescription
200Effective value with source layer
400Query key is missing
404Unknown setting key

Response 200 body:

Media type application/json:

{
"type": "object"
}

Notes:

  • An unknown id fails with 404.

  • Related calls sit on the same tag page.

Not verified: see ../_verification/failures.md.

Example:

# Illustrative request. Values come from the schema above.
# The example flow below runs verified calls on a live broker.
curl -s -X GET 'http://127.0.0.1:18083/api/v1/config/explain?key=session.max_qos0_backlog' \
-H "Authorization: Bearer $INDRA_API_KEY"

GET /api/v1/config/history

List configuration versions newest first

Operation id: listConfigHistory.

Parameters:

No parameters.

Request body:

No request body.

Responses:

StatusDescription
200Version list without snapshots

Response 200 body:

Media type application/json:

{
"type": "object",
"properties": {
"versions": {
"type": "array",
"items": {
"type": "object"
}
}
}
}

Notes:

  • Related calls sit on the same tag page.

Verified: the example flow below runs this call.

Example:

# Illustrative request. Values come from the schema above.
# The example flow below runs verified calls on a live broker.
curl -s -X GET 'http://127.0.0.1:18083/api/v1/config/history' \
-H "Authorization: Bearer $INDRA_API_KEY"

GET /api/v1/config/history/{id}

Get one configuration version

Operation id: getConfigHistoryVersion.

Parameters:

NameInRequiredTypeDescription
idpathtrueintegerVersion id
secretsqueryfalsestringSet to raw to get the snapshot with its secrets as stored. This needs a full administrator: the administrator role with no scope list. Backup export uses it, so the export file holds secrets.

Request body:

No request body.

Responses:

StatusDescription
200Version with redacted snapshot
403The query secrets=raw needs a full administrator
404Version does not exist

Response 200 body:

Media type application/json:

{
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"actor": {
"type": "string"
},
"timestamp_secs": {
"type": "integer"
},
"summary": {
"type": "string"
},
"changes": {
"type": "array",
"items": {
"type": "string"
}
},
"snapshot": {
"type": "object"
}
}
}

Notes:

  • An unknown id fails with 404.

  • Related calls sit on the same tag page.

Not verified: see ../_verification/failures.md.

Example:

# Illustrative request. Values come from the schema above.
# The example flow below runs verified calls on a live broker.
curl -s -X GET 'http://127.0.0.1:18083/api/v1/config/history/1' \
-H "Authorization: Bearer $INDRA_API_KEY"

POST /api/v1/config/history/{id}/restore

Restore one configuration version

Operation id: restoreConfigHistoryVersion.

Parameters:

NameInRequiredTypeDescription
idpathtrueintegerVersion id

Request body:

Media type application/json:

FieldTypeDescription
actorstring

Responses:

StatusDescription
200Restored version with applied diff
404Version does not exist

Notes:

  • An unknown id fails with 404.

  • Related calls sit on the same tag page.

Not verified: see ../_verification/failures.md.

Example:

# Illustrative request. Values come from the schema above.
# The example flow below runs verified calls on a live broker.
curl -s -X POST 'http://127.0.0.1:18083/api/v1/config/history/1/restore' \
-H "Authorization: Bearer $INDRA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"actor": ""
}'

POST /api/v1/config/reload

Apply a candidate configuration snapshot

Operation id: reloadConfig.

Parameters:

No parameters.

Request body:

Media type application/json:

Required: snapshot.

FieldTypeDescription
snapshotobject
actorstring
summarystring

Responses:

StatusDescription
200Reloaded version with applied diff
400Candidate names each offending setting
500Reload failed after validation

Response 200 body:

Media type application/json:

{
"type": "object",
"properties": {
"version": {
"type": "integer"
},
"diff": {
"type": "array",
"items": {
"type": "object"
}
},
"pending_live": {
"type": "array",
"items": {
"type": "string"
}
}
}
}

Notes:

  • Send snapshot. An empty required string fails with 400.

  • Related calls sit on the same tag page.

Not verified: see ../_verification/failures.md.

Example:

# Illustrative request. Values come from the schema above.
# The example flow below runs verified calls on a live broker.
curl -s -X POST 'http://127.0.0.1:18083/api/v1/config/reload' \
-H "Authorization: Bearer $INDRA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"snapshot": {},
"actor": "",
"summary": ""
}'

POST /api/v1/config/validate

Validate a candidate configuration snapshot

Operation id: validateConfig.

Parameters:

No parameters.

Request body:

Media type application/json:

Required: snapshot.

FieldTypeDescription
snapshotobject

Responses:

StatusDescription
200Candidate applies cleanly
400Candidate names each offending setting

Response 200 body:

Media type application/json:

{
"type": "object",
"properties": {
"ok": {
"type": "boolean",
"example": true
}
}
}

Notes:

  • Send snapshot. An empty required string fails with 400.

  • Related calls sit on the same tag page.

Not verified: see ../_verification/failures.md.

Example:

# Illustrative request. Values come from the schema above.
# The example flow below runs verified calls on a live broker.
curl -s -X POST 'http://127.0.0.1:18083/api/v1/config/validate' \
-H "Authorization: Bearer $INDRA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"snapshot": {}
}'

Example flow

Read real rows through the management API, then read them back. The docs harness runs this flow against a started broker.

Terminal window
python3 - <<'PY'
import os, urllib.request
port = os.environ.get("API_PORT", "18083")
key = os.environ.get("API_KEY", "")
base = f"http://127.0.0.1:{port}"
def get(path):
req = urllib.request.Request(base + path, method="GET")
req.add_header("Authorization", f"Bearer {key}")
with urllib.request.urlopen(req, timeout=5) as r:
return r.status, r.read().decode()
s1, b1 = get("/api/v1/config/history")
assert s1 == 200, b1
assert "versions" in b1, b1
s2, b2 = get("/api/v1/config/history")
assert s2 == 200, b2
assert b1 == b2, "read back the same version list"
print("OK")
PY
OK

Check that it works

Start the broker with this file. The health endpoint returns OK.

[node]
id = "doc-ref"
[listeners.tcp]
bind = "0.0.0.0:1883"
[listeners.tls]
bind = "0.0.0.0:8883"
[listeners.ws]
bind = "0.0.0.0:8083"
[listeners.wss]
bind = "0.0.0.0:8084"
[listeners.api]
bind = "0.0.0.0:18083"
[logging]
level = "warn"
Terminal window
python3 - <<'PY'
import os, socket
port = int(os.environ.get("API_PORT", "18083"))
sock = socket.create_connection(("127.0.0.1", port), timeout=5)
sock.sendall(b"GET /healthz HTTP/1.0\r\nHost: docs\r\n\r\n")
print(sock.makefile().read().strip().splitlines()[-1])
PY
OK

Check one documented call. GET /api/v1/config/history answers 200 with its rows. It prints OK.

Terminal window
python3 - <<'PY'
import os, urllib.request
port = os.environ.get("API_PORT", "18083")
key = os.environ.get("API_KEY", "")
base = f"http://127.0.0.1:{port}"
req = urllib.request.Request(base + "/api/v1/config/history", method="GET")
req.add_header("Authorization", f"Bearer {key}")
with urllib.request.urlopen(req, timeout=5) as r:
body = r.read().decode()
assert r.status == 200, body
assert "versions" in body, body
print("OK")
PY
OK

Next steps