diff --git a/docs/drivers/Mqtt.md b/docs/drivers/Mqtt.md index d13bbf16..f8a6f5a4 100644 --- a/docs/drivers/Mqtt.md +++ b/docs/drivers/Mqtt.md @@ -325,6 +325,9 @@ and this driver ships all three or none of it 2. a matching **retained `OFFLINE` armed as the MQTT Last Will**, so a crash still reports the truth; 3. an explicit `OFFLINE` on a clean stand-down, so edge nodes do not wait out a keep-alive timeout. +A claim the broker keeps refusing backs off (STAB-27): 2 s, 4 s, 8 s … capped at 30 s between attempts, one +Warning per attempt, reset by a successful claim or a stand-down. The role is still sampled every second. + Set `Sparkplug.HostId` and `Sparkplug.ActAsPrimaryHost: true`. A blank host id is **refused** with a warning — the driver does not publish to `spBv1.0/STATE/`, which is a legal topic reaching nobody. @@ -460,8 +463,12 @@ every MQTT node materialized read-only regardless of what the operator authored. | **Plain** | the tag's `writeTopic` | the value as UTF-8 text (`Raw`/`Scalar`) or a bare JSON literal (`Json`) | | **Sparkplug B** | `spBv1.0/{group}/DCMD/{node}/{device}` for a device metric, `spBv1.0/{group}/NCMD/{node}` for an edge-node metric | a Sparkplug `Payload` carrying the metric **by name**, with an explicit `Datatype` and no `seq` | -Both publish at **QoS 0, `retain: false`**. Retain is the one that matters and is deliberately not -configurable: a retained command would be replayed by the broker to the device on *every* future +Both publish with `retain: false`. A **Plain** write goes at **QoS 1** (STAB-26): the publish completes only +once the broker PUBACKs it, so `Good` means "the broker accepted it", and a broker refusal or a connection +that died under the send is `Bad`. **Sparkplug commands stay at QoS 0** — a redelivered NCMD/DCMD would be +re-applied. A Sparkplug metric the tag did not give a `dataType` is commanded with the type its live birth +certificate declared (STAB-25), the same authored-beats-birth precedence reads use. Retain is the one that +matters and is deliberately not configurable: a retained command would be replayed by the broker to the device on *every* future subscribe — including the device's own reconnect — so one stale write would be re-applied forever. ### `writeTopic` is separate from `topic`, and its absence is meaningful diff --git a/docs/drivers/Sql.md b/docs/drivers/Sql.md index 37803f39..a31617ca 100644 --- a/docs/drivers/Sql.md +++ b/docs/drivers/Sql.md @@ -92,7 +92,10 @@ Both value-bearing fields — `whereValue` here and `keyValue` on the `KeyValue` authored as a JSON string, number or boolean, and are captured as text. They are **bound as parameters** either way, and matched back as text (the key cell is stringified), so a key column that holds an INTEGER station id can be authored either `"7"` or `7`. A JSON object, array or `null` -is not a value and is refused. (They were read asymmetrically until 2026-08-10, so a numerically +is not a value and is refused. On **PostgreSQL** a string bind is sent untyped (`NpgsqlDbType.Unknown`, +`PostgresDialect.ConfigureParameter`, STAB-24) so the server infers its type from the compared column; +bound as `text`, a `"7"` against an `integer` key fails with `operator does not exist: integer = text`. +(They were read asymmetrically until 2026-08-10, so a numerically authored `keyValue` refused the tag while the identically-bound `whereValue` was served — and the deploy gate reported the key as *missing* when it was right there in the blob.)