Skip to content

Verified: Consuming a Sparkplug fleet

Each claim the Consuming a Sparkplug fleet page makes, with the tests that prove it in the last green CI run: 40 of 40 verified. The claims live in docs/claims/sparkplug-host.yaml.

Last green CI run 16 Oct 2026, 14:48 UTC on c7618d3 · all runtime claims

SPH-001 verified

`broker`, `host-id`, `group-id` (or `group-ids`) and `manifest` are required for a sparkplug-host driver; a project missing one fails `naut check` (at load), not `naut run`.

where the docs say so

SPH-002 verified

`New` never dials: it builds the whole driver (manifest, indexes, companion tags) offline, so a complete sparkplug-host project passes `naut check` with no broker in sight.

where the docs say so

SPH-003 verified

The broker password is never read from the manifest: it comes from `NAUTILUS_MQTT_PASSWORD`.

where the docs say so

  • pass TestSparkplugHostPasswordComesFromTheEnvironment in internal/project (3 of 3 passed)

SPH-004 verified

`primary` defaults to true: a host project that does not set it publishes STATE.

where the docs say so

SPH-005 verified

The primary host publishes STATE on `spBv1.0/STATE/<host-id>`, `state-form: both` also publishes the legacy `STATE/<host-id>` form, and a passive consumer (`primary: false`) publishes no STATE.

where the docs say so

SPH-006 verified

`Stop` publishes the STATE death certificate before disconnecting.

where the docs say so

SPH-007 verified

`naut sparkplug import --sites` generates the types, manifest and tag file offline from a committed site list, identical for the metrics it shares with the live (from-births) path.

where the docs say so

SPH-008 verified

Live import asks every node heard from to rebirth (births are not retained) and generates from what birthed.

where the docs say so

  • pass TestDiscoverAsksForRebirth in sparkplug/host
  • pass TestSparkplugImportFromBrokerAsksForRebirth in cmd/naut

SPH-010 verified

`naut sparkplug import` writes Sparkplug Templates as ST `TYPE`s in `sparkplug_types.st`, composed automatically as a library so a program can use them with no entry in `nautilus.yaml`.

where the docs say so

SPH-011 verified

`naut sparkplug tags` re-derives the tag file from a committed manifest with no broker.

where the docs say so

SPH-012 verified

`naut sparkplug browse` prints what is on the wire without generating anything.

where the docs say so

  • pass TestSparkplugBrowsePrintsWithoutGenerating in cmd/naut

SPH-013 verified

The driver subscribes to the group and presents every edge node's (and device's) data as `role: input` tags.

where the docs say so

SPH-014 verified

The companions exist per site and device: `<site>__Online` is true from NBIRTH to the matching NDEATH, `<site>_<device>__Online` does the same per device, and `<site>__LastBirthMs` is the epoch ms of the last NBIRTH.

where the docs say so

SPH-015 verified

The driver never invents or zeroes a value on death: an NDEATH keeps every value (and takes the node and its devices offline), and a DDEATH keeps the device's values.

where the docs say so

SPH-016 verified

A rising edge on the `<site>__Rebirth` output sends NCMD Node Control/Rebirth to that site; a false value sends nothing.

where the docs say so · The NCMD is decoded (Node Control/Rebirth, Boolean true); held true and a falling edge send nothing, and a second rising edge sends again.

SPH-017 verified

A data tag stays absent from the driver's snapshot until its site has birthed (so reading it faults the scan), while `__Online` is present from t=0 as false.

where the docs say so

SPH-018 verified

`Quality()` reports non-Good entries only: a binding never delivered (site never birthed, or the birth omits that metric) is NotConnected, and a binding whose site is not online (NDEATH, silence past `stale-after`, or DDEATH for a device-scoped metric) is Stale, scoped to the node or device concerned.

where the docs say so

SPH-019 verified

Writable bindings and the companions are host-authored and get no quality verdict.

where the docs say so

SPH-020 verified

Silence longer than `stale-after` marks a node stale without disturbing its values.

where the docs say so

SPH-021 verified

Output writes leave as NCMD (node metrics) or DCMD (device metrics), QoS 0, not retained, with full metric names (never aliases).

where the docs say so

SPH-022 verified

A command to a dark site is held (the latest value per tag) and delivered once on the site's next NBIRTH/DBIRTH, after the birth has settled what the site holds.

where the docs say so

SPH-023 verified

If the site comes back already holding the commanded value, nothing goes on the wire; otherwise one command goes out, only for the members the operator touched.

where the docs say so

SPH-024 verified

The offline queue holds at most 256 tags per site, and a write past a full queue is counted as `WriteDrops`.

where the docs say so

SPH-025 verified

`Status` counts `WriteQueued`, `QueuedWrites` per site, and a "queued writes" metric on `/api/drivers`.

where the docs say so

SPH-026 verified

A UDT is never writable as a whole: a `--writable` glob that matches a Template metric is a hard error, and controls are bound per member.

where the docs say so

SPH-027 verified

A member binding is a scalar tag carrying the leaf member's type (named `<prefix>_<metric>_<member path>`), is output-only (`writable: true` required), with `member:` a dotted path into the type shape; reads keep coming from the parent struct tag.

where the docs say so

SPH-028 verified

Writing a member publishes a partial template naming the parent metric and carrying only that member; two members of one UDT in one coalesce window go out as one partial template, and nested paths build intermediate templates with their TemplateRef.

where the docs say so

SPH-029 verified

A partial template written by the host merges member by member into nautilus's own edge, so members the host did not write are kept.

where the docs say so

  • pass TestCommandPartialTemplateKeepsOtherMembers in sparkplug
  • pass TestCommandNestedPartialTemplate in sparkplug
  • pass TestEdgeToHostQueuedWriteReachesAWokenSite in sparkplug/host

SPH-030 verified

The first output snapshot after `Start` is a baseline that is recorded and published to nobody, so a host connecting to an online fleet never zeroes an edge's setpoints.

where the docs say so

SPH-031 verified

After the baseline only a value that actually moved is a command, published once however many scans hand it back.

where the docs say so

SPH-032 verified

A member output adopts the site's live value when its parent template arrives (NBIRTH, DBIRTH, NDATA or rebirth): writing the value the panel already reports sends nothing, anything else sends one partial template; before the first birth an operator write still publishes.

where the docs say so

SPH-033 verified

A reconnect replays nothing: change detection survives a dropped connection, while commands raised during the outage are queued and go out on reconnect.

where the docs say so

SPH-034 verified

`__Rebirth` follows the baseline rule: only a rising edge is a command, and a `__Rebirth` already TRUE on the first scan is baseline, so a value held TRUE across a restart does not rebirth every site.

where the docs say so

SPH-035 verified

`/api/drivers` reports Kind `sparkplug-host`, Name the host-id, state error (broker down), connecting (no births yet), degraded (any node offline or strict-mode unknowns) or connected, with one device row per edge node and device sub-rows flattened as `W6/plc1`.

where the docs say so

SPH-036 verified

With a `redundancy:` section only the leader may claim to be online: the STATE publish and every outbound NCMD/DCMD are gated on leadership, not just on `primary: true`. On promotion the new leader publishes STATE online; on demotion the old one stops commanding and publishes its STATE death. Without `redundancy:` the host is always the leader.

where the docs say so

  • pass TestMqttStandbyPublishesNoStateAndNoCommandsUntilLeader in sparkplug/host
  • pass TestWriteGateDrivesStateAndCommands in sparkplug/host
  • pass TestWriteGateLeaderReassertsStateOverAStaleDeath in sparkplug/host
  • pass TestNoWriteGateSingleHostIsAlwaysPrimary in sparkplug/host
  • pass TestRedundantSparkplugHostOnlyTheLeaderIsPrimary in cmd/naut
  • pass TestRedundantSparkplugHostOnABenchIsPrimary in cmd/naut
  • pass TestSingleSparkplugHostWithoutRedundancyIsAlwaysPrimary in cmd/naut

SPH-037 verified

The host passes the Sparkplug TCK host-application profile (NBIRTH, DBIRTH, NDATA, a deliberate sequence gap, DDEATH, NDEATH) with a non-zero PASS count and zero FAIL.

where the docs say so · Runs only with NAUTILUS_TCK=1 in the sparkplug-tck CI job. The reorder-success scenario is deliberately not scored (harness limitation); the reorder-timeout rebirth path is.

SPH-038 verified

A sequence gap left unfilled for `reorder-timeout` sends NCMD Rebirth to the node; a gap filled inside the window drains without one.

where the docs say so

SPH-041 verified

Metrics the manifest never bound are handled by `on-unknown` (`log`, `ignore` or `strict`); under `strict` the driver reports degraded.

where the docs say so

SPH-042 verified

Only the configured group(s) are consumed from the broker.

where the docs say so · A foreign group is refused, the configured group is consumed, and with two groups configured the second is consumed too.

SPH-043 verified

`Start` owns the connection and its reconnect loop; the driver reconnects after a broker restart.

where the docs say so